Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
0009efc322 | |||
b5484c2bcd | |||
473883f6dc | |||
400aaf667d | |||
2dd5020963 |
102
.drone.yml
102
.drone.yml
@ -1,37 +1,101 @@
|
|||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: default
|
name: cleanup-before
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: test
|
- name: clean
|
||||||
image: rust:1
|
image: alpine
|
||||||
commands:
|
commands:
|
||||||
- apt-get update -y
|
- rm -rf /build/*
|
||||||
- apt-get install libasound2-dev -y
|
volumes:
|
||||||
- cargo build --verbose --all
|
- name: build
|
||||||
- cargo test --verbose --all
|
path: /build
|
||||||
- name: release
|
|
||||||
image: rust:1
|
|
||||||
commands:
|
|
||||||
- apt-get update -y
|
|
||||||
- apt-get install libasound2-dev -y
|
|
||||||
- cargo build --release --verbose --all
|
|
||||||
- cd target/release
|
|
||||||
- tar -czvf zabbixlaunch-${DRONE_TAG}.tar.gz zabbixlaunch
|
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
- name: publish
|
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
host:
|
||||||
|
path: /tmp/ipbl/build
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default-linux-amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Build
|
||||||
|
image: rust:latest
|
||||||
|
commands:
|
||||||
|
- cargo build --verbose --all
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: gitea-release
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: move
|
||||||
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- mv target/release/* ./
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
path: /drone/src/build
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
- name: release
|
||||||
image: plugins/gitea-release
|
image: plugins/gitea-release
|
||||||
settings:
|
settings:
|
||||||
base_url: https://git.paulbsd.com
|
base_url: https://git.paulbsd.com
|
||||||
api_key:
|
api_key:
|
||||||
from_secret: gitea_token
|
from_secret: gitea_token
|
||||||
files: "target/release/*.tar.gz"
|
files: "*.tar.gz"
|
||||||
checksum:
|
checksum:
|
||||||
- sha256
|
- sha256
|
||||||
- sha512
|
- sha512
|
||||||
environment:
|
title: VERSION
|
||||||
PLUGIN_TITLE: ""
|
volumes:
|
||||||
|
- name: build
|
||||||
|
path: /drone/src/build
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
|
- name: ls
|
||||||
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- find .
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
path: /drone/src/build
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
host:
|
||||||
|
path: /tmp/ipbl/build
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- default-linux-amd64
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: cleanup-after
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clean
|
||||||
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- rm -rf /build/*
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
path: /build
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: build
|
||||||
|
host:
|
||||||
|
path: /tmp/ipbl/build
|
||||||
|
1130
Cargo.lock
generated
1130
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
19
Cargo.toml
19
Cargo.toml
@ -1,18 +1,17 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "zabbixlaunch"
|
name = "zabbixlaunch"
|
||||||
version = "1.0.1"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
authors = ["PaulBSD <paul@paulbsd.com>"]
|
|
||||||
description = "Lights up Launchpad mini using Zabbix data"
|
|
||||||
repository = "https://git.paulbsd.com/paulbsd/zabbixlaunch"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "4.3"
|
clap = { version = "2", features = ["yaml"] }
|
||||||
embedded-graphics = { version = "0.8", optional = true }
|
embedded-graphics = { version = "0.7", optional = true }
|
||||||
launchy = { git = "https://github.com/paulbsd/launchy", branch = "develop-launchpad-mini-mk3" }
|
futures = "*"
|
||||||
nix = "0.26"
|
launchy = { git = "https://github.com/kangalioo/launchy", branch = "master" }
|
||||||
reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] }
|
nix = "0.23"
|
||||||
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
# zabbixlaunch
|
# zabbixlaunch
|
||||||
|
|
||||||
[![Build Status](https://drone.paulbsd.com/api/badges/paulbsd/zabbixlaunch/status.svg)](https://drone.paulbsd.com/paulbsd/zabbixlaunch)
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
zabbixlaunch is a application that take control over a launchpad mini, and draw problems handled by Zabbix
|
zabbixlaunch is a application that take control over a launchpad mini, and draw problems handled by Zabbix
|
||||||
@ -16,9 +14,8 @@ zabbixlaunch is a application that take control over a launchpad mini, and draw
|
|||||||
"server": "https://zabbix.acme.com/api_jsonrpc.php",
|
"server": "https://zabbix.acme.com/api_jsonrpc.php",
|
||||||
"username": "bob",
|
"username": "bob",
|
||||||
"password": "password",
|
"password": "password",
|
||||||
"sloweffect": true,
|
"authtoken": "token",
|
||||||
"refresh": 2,
|
"limit": 20
|
||||||
"limit": 100
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -51,7 +48,7 @@ cargo build --release
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Copyright (c) 2021, 2022 PaulBSD
|
Copyright (c) 2021 PaulBSD
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"server": "https://zabbix.acme.com/api_jsonrpc.php",
|
|
||||||
"username": "bob",
|
|
||||||
"password": "password",
|
|
||||||
"sloweffect": true,
|
|
||||||
"refresh": 2,
|
|
||||||
"limit": 100
|
|
||||||
}
|
|
17
src/config/cli.yml
Normal file
17
src/config/cli.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: zabbixlaunch
|
||||||
|
version: "1.0.0"
|
||||||
|
author: PaulBSD <paul@paulbsd.com>
|
||||||
|
about: Lights up Launchpad mini using Zabbix data
|
||||||
|
args:
|
||||||
|
- config:
|
||||||
|
short: c
|
||||||
|
long: config
|
||||||
|
value_name: FILE
|
||||||
|
help: Sets a custom config file
|
||||||
|
takes_value: true
|
||||||
|
- mode:
|
||||||
|
short: m
|
||||||
|
value_name: MODE
|
||||||
|
help: Handles mode
|
||||||
|
takes_value: true
|
@ -1,6 +1,6 @@
|
|||||||
use crate::zabbix::api::get_zabbix_authtoken;
|
use crate::zabbix::api::get_zabbix_authtoken;
|
||||||
use crate::zabbix::problems::DataMatrix;
|
use crate::zabbix::problems::DataMatrix;
|
||||||
use clap::{Arg, ArgMatches, Command};
|
use clap::App;
|
||||||
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
|
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Error as JsonError;
|
use serde_json::Error as JsonError;
|
||||||
@ -8,60 +8,42 @@ use serde_json::Value as JsonValue;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ReloadFrequency {
|
pub enum ReloadFrequency {
|
||||||
High = 50,
|
High = 50,
|
||||||
Medium = 20,
|
Medium = 20,
|
||||||
Low = 10,
|
Low = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Mode {
|
|
||||||
Draw,
|
|
||||||
Test,
|
|
||||||
Input,
|
|
||||||
Effect,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub cfg: Config,
|
pub cfg: Config,
|
||||||
configfile: String,
|
pub configfile: String,
|
||||||
inotify: Inotify,
|
pub inotify: Inotify,
|
||||||
pub datamatrix: DataMatrix,
|
pub datamatrix: DataMatrix,
|
||||||
pub mode: Option<Mode>,
|
pub mode: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let argp: ArgMatches = Context::argparse();
|
let yaml = load_yaml!("cli.yml");
|
||||||
|
let cli = App::from_yaml(yaml).get_matches();
|
||||||
|
|
||||||
let configfile = argp
|
let configfile = cli.value_of("config").unwrap_or("config.json").to_string();
|
||||||
.get_one::<String>("config")
|
|
||||||
.unwrap_or(&"config.json".to_string())
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let mut ctx = Context {
|
let mut ctx = Context {
|
||||||
cfg: Config::new(),
|
cfg: Config::new(),
|
||||||
configfile: configfile,
|
configfile: configfile,
|
||||||
inotify: Inotify::init(InitFlags::IN_NONBLOCK).unwrap(),
|
inotify: Inotify::init(InitFlags::IN_NONBLOCK).unwrap(),
|
||||||
datamatrix: DataMatrix::new(),
|
datamatrix: DataMatrix::new(),
|
||||||
mode: match argp.get_one::<String>("mode").unwrap().as_str() {
|
mode: cli.value_of("mode").unwrap_or("draw").to_string(),
|
||||||
"draw" => Some(Mode::Draw),
|
|
||||||
"test" => Some(Mode::Test),
|
|
||||||
"input" => Some(Mode::Input),
|
|
||||||
"effect" => Some(Mode::Effect),
|
|
||||||
_ => {
|
|
||||||
println!("No valid mode choosen");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Loading {configfile} file ...", configfile = ctx.configfile);
|
println!("Loading {configfile} file ...", configfile = ctx.configfile);
|
||||||
ctx.cfg.load(&ctx.configfile);
|
ctx.cfg.load(&ctx.configfile.to_string()).await;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Adding inotify watch on {configfile} file ...",
|
"Adding inotify watch on {configfile} file ...",
|
||||||
@ -70,57 +52,38 @@ impl Context {
|
|||||||
ctx.inotify
|
ctx.inotify
|
||||||
.add_watch(ctx.configfile.as_str(), AddWatchFlags::IN_MODIFY)
|
.add_watch(ctx.configfile.as_str(), AddWatchFlags::IN_MODIFY)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
get_zabbix_authtoken(&mut ctx.cfg);
|
get_zabbix_authtoken(&mut ctx.cfg).await;
|
||||||
|
|
||||||
ctx
|
ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn argparse() -> ArgMatches {
|
/*pub async fn hotreload(&mut self) {
|
||||||
Command::new(env!("CARGO_PKG_NAME"))
|
let events = match self.inotify.read_events() {
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
Ok(ev) => ev,
|
||||||
.author(env!("CARGO_PKG_AUTHORS"))
|
Err(_) => vec![],
|
||||||
.about(env!("CARGO_PKG_DESCRIPTION"))
|
};
|
||||||
.arg(
|
if events.len() > 0 {
|
||||||
Arg::new("config")
|
println!("Reloading {cfg}", cfg = self.configfile);
|
||||||
.short('c')
|
get_zabbix_authtoken(&mut self.cfg).await;
|
||||||
.long("config")
|
self.cfg.load(self.configfile.as_str());
|
||||||
.value_name("FILE")
|
|
||||||
.default_value("/etc/zabbixlaunch/config.json")
|
|
||||||
.help("Sets a custom config file"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("mode")
|
|
||||||
.short('m')
|
|
||||||
.long("mode")
|
|
||||||
.value_name("mode")
|
|
||||||
.default_value("draw")
|
|
||||||
.value_parser(["draw", "test", "input", "effect"])
|
|
||||||
.help("Sets a when running"),
|
|
||||||
)
|
|
||||||
.arg(Arg::new("debug").short('d').help("Enable debugging"))
|
|
||||||
.get_matches()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hotreload(&mut self) {
|
|
||||||
let mut i = 0;
|
|
||||||
let waitmilli = self.cfg.refresh.unwrap_or(5) * 1000;
|
|
||||||
|
|
||||||
while i < waitmilli {
|
|
||||||
let waitinc = waitmilli / ReloadFrequency::Medium as u64;
|
|
||||||
let events = match self.inotify.read_events() {
|
|
||||||
Ok(ev) => ev,
|
|
||||||
Err(_) => vec![],
|
|
||||||
};
|
|
||||||
if events.len() > 0 {
|
|
||||||
println!("Reloading {cfg}", cfg = self.configfile);
|
|
||||||
self.cfg.load(self.configfile.as_str());
|
|
||||||
get_zabbix_authtoken(&mut self.cfg);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
sleep(Duration::from_millis(waitinc));
|
|
||||||
}
|
|
||||||
i += waitinc;
|
|
||||||
}
|
}
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn hotreload(ctx: &mut Context, ctxsender: &tokio::sync::mpsc::Sender<Context>) {
|
||||||
|
println!("function hotreload, {:?}", ctxsender);
|
||||||
|
loop {
|
||||||
|
let events = match ctx.to_owned().inotify.read_events() {
|
||||||
|
Ok(ev) => ev,
|
||||||
|
Err(_) => vec![],
|
||||||
|
};
|
||||||
|
if events.len() > 0 {
|
||||||
|
ctx.cfg.load(&ctx.configfile).await;
|
||||||
|
println!("{cfg:?}", cfg = ctx.cfg);
|
||||||
|
}
|
||||||
|
println!("Sending {:?}", ctx);
|
||||||
|
ctxsender.send(ctx.to_owned()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,21 +112,8 @@ impl<'a> Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&'a mut self, configfile: &str) {
|
async fn load(&mut self, configfile: &String) {
|
||||||
let fileopen: Result<File, std::io::Error>;
|
let mut file = match File::open(configfile) {
|
||||||
let filemeta = std::fs::metadata(configfile);
|
|
||||||
let mut error_reading = false;
|
|
||||||
let fileexists = match filemeta {
|
|
||||||
Ok(_) => true,
|
|
||||||
Err(_) => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !fileexists {
|
|
||||||
File::create(configfile).unwrap();
|
|
||||||
}
|
|
||||||
fileopen = File::open(configfile);
|
|
||||||
|
|
||||||
let mut file = match fileopen {
|
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
panic!("{err}", err = err);
|
panic!("{err}", err = err);
|
||||||
@ -172,51 +122,56 @@ impl<'a> Config {
|
|||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents).unwrap();
|
file.read_to_string(&mut contents).unwrap();
|
||||||
let parse: Result<JsonValue, JsonError> = serde_json::from_str(contents.as_str());
|
let parse: Result<JsonValue, JsonError> = serde_json::from_str(contents.as_str());
|
||||||
let ncfg = match parse {
|
*self = match parse {
|
||||||
Ok(ncfg) => {
|
Ok(cfg) => {
|
||||||
let tmpcfg = Config::new();
|
let tmpcfg = Config::new();
|
||||||
Config {
|
Config {
|
||||||
server: ncfg["server"]
|
server: cfg["server"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap_or(tmpcfg.server.as_str())
|
.unwrap_or(tmpcfg.server.as_str())
|
||||||
.to_string(),
|
.to_string(),
|
||||||
username: ncfg["username"]
|
username: cfg["username"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap_or(tmpcfg.username.as_str())
|
.unwrap_or(tmpcfg.username.as_str())
|
||||||
.to_string(),
|
.to_string(),
|
||||||
password: ncfg["password"]
|
password: cfg["password"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.unwrap_or(tmpcfg.password.as_str())
|
.unwrap_or(tmpcfg.password.as_str())
|
||||||
.to_string(),
|
.to_string(),
|
||||||
authtoken: Some(
|
authtoken: Some(
|
||||||
ncfg["authtoken"]
|
self.authtoken
|
||||||
.as_str()
|
.clone()
|
||||||
.unwrap_or(self.authtoken.clone().unwrap().as_str())
|
.unwrap_or(tmpcfg.authtoken.unwrap().to_string())
|
||||||
.to_string(),
|
.to_string(),
|
||||||
),
|
),
|
||||||
sloweffect: Some(
|
sloweffect: Some(
|
||||||
ncfg["sloweffect"]
|
cfg["sloweffect"]
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(tmpcfg.sloweffect.unwrap()),
|
.unwrap_or(tmpcfg.sloweffect.unwrap()),
|
||||||
),
|
),
|
||||||
refresh: Some(ncfg["refresh"].as_u64().unwrap_or(tmpcfg.refresh.unwrap())),
|
refresh: Some(cfg["refresh"].as_u64().unwrap_or(tmpcfg.refresh.unwrap())),
|
||||||
limit: Some(ncfg["limit"].as_u64().unwrap_or(tmpcfg.limit.unwrap())),
|
limit: Some(cfg["limit"].as_u64().unwrap_or(tmpcfg.limit.unwrap())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error_reading = true;
|
|
||||||
println!("error occured: '{}'", err);
|
println!("error occured: '{}'", err);
|
||||||
Config::new()
|
Config::new()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
*self = ncfg;
|
self.save(&configfile).await;
|
||||||
if !fileexists || error_reading {
|
|
||||||
self.save(&configfile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&self, configfile: &str) {
|
async fn save(&self, configfile: &String) {
|
||||||
let file = File::create(configfile).unwrap();
|
let file: File;
|
||||||
serde_json::to_writer_pretty(file, &self).unwrap();
|
let filemeta = std::fs::metadata(configfile);
|
||||||
|
let fileexists = match filemeta {
|
||||||
|
Ok(_) => true,
|
||||||
|
Err(_) => false,
|
||||||
|
};
|
||||||
|
if !fileexists {
|
||||||
|
file = File::create(configfile).unwrap();
|
||||||
|
serde_json::to_writer_pretty(file, &self).unwrap();
|
||||||
|
sleep(Duration::from_millis(1)).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -1,10 +1,11 @@
|
|||||||
mod config;
|
pub mod config;
|
||||||
mod padcontrol;
|
mod padcontrol;
|
||||||
mod zabbix;
|
mod zabbix;
|
||||||
|
|
||||||
use config::Context;
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
fn main() {
|
#[tokio::main]
|
||||||
let mut context = Context::new();
|
async fn main() {
|
||||||
padcontrol::run(&mut context);
|
padcontrol::run().await;
|
||||||
}
|
}
|
||||||
|
@ -1,163 +1,177 @@
|
|||||||
use crate::config::{Context, Mode};
|
use crate::config::{hotreload, Context};
|
||||||
use crate::zabbix::api::get_zabbix_problems;
|
use crate::zabbix::api::*;
|
||||||
use launchy::Color;
|
use launchy::Color;
|
||||||
use launchy::{self, Canvas, CanvasLayout, CanvasLayoutPoller, MsgPollingWrapper, Pad};
|
use launchy::{self, Canvas, CanvasLayout, CanvasLayoutPoller, MsgPollingWrapper, Pad};
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
const MATRIX_WIDTH: i32 = 8;
|
const MATRIX_WIDTH: i32 = 8;
|
||||||
const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH;
|
const MATRIX_SIZE: i32 = MATRIX_WIDTH * MATRIX_WIDTH;
|
||||||
const REQUEST_TIMEOUT: i32 = 5;
|
const REQUEST_WAITFOR: i32 = 5;
|
||||||
const ZERO_COLOR: f32 = 0.;
|
const ZERO_COLOR: f32 = 0.;
|
||||||
const ONE_COLOR: f32 = 1.;
|
const ONE_COLOR: f32 = 1.;
|
||||||
const SLOWEFFECT_FACTOR: f32 = 800.0;
|
|
||||||
|
|
||||||
pub fn run(ctx: &mut Context) {
|
const POLLER_ITER: u64 = 17;
|
||||||
match ctx.mode {
|
|
||||||
Some(Mode::Draw) => {
|
fn log(num: i64) {
|
||||||
let (mut canvas, mut _poller) = initpad();
|
return println!("test{} 🦀", num);
|
||||||
draw(&mut canvas, ctx);
|
}
|
||||||
}
|
|
||||||
Some(Mode::Input) => {
|
pub async fn run<'a>() {
|
||||||
let (mut canvas, mut poller) = initpad();
|
log(1);
|
||||||
input(&mut canvas, &mut poller);
|
let mut ctx = Box::new(Context::new().await);
|
||||||
}
|
log(2);
|
||||||
Some(Mode::Effect) => {
|
let (tx1, mut rx1) = mpsc::channel(1);
|
||||||
let (mut canvas, mut poller) = initpad();
|
log(3);
|
||||||
effect(&mut canvas, &mut poller)
|
let t1 = tokio::task::spawn(async move { test(&mut rx1).await });
|
||||||
}
|
let t2 = tokio::task::spawn(async move { hotreload(&mut ctx, &tx1).await });
|
||||||
Some(Mode::Test) => test(ctx),
|
tokio::join!(t1, t2);
|
||||||
None => {
|
log(4);
|
||||||
println!("No valid option choosen for mode");
|
}
|
||||||
}
|
|
||||||
|
async fn test<'a>(ctxreceiver: &mut tokio::sync::mpsc::Receiver<Context>) {
|
||||||
|
while let Some(val) = ctxreceiver.recv().await {
|
||||||
|
println!("Fetching zabbix problems, {:?}", val.cfg);
|
||||||
|
let zabbix_data = get_zabbix_problems(&val.cfg).await.unwrap();
|
||||||
|
println!("{}", zabbix_data["result"][0]);
|
||||||
|
println!("sleeping during {}", val.cfg.refresh.unwrap());
|
||||||
|
sleep(Duration::from_secs(val.cfg.refresh.unwrap())).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) {
|
fn initpad() -> (CanvasLayout<'static>, CanvasLayoutPoller) {
|
||||||
let (mut canvas, poller) = launchy::CanvasLayout::new_polling();
|
let (mut canvas, poller) = launchy::CanvasLayout::new_polling();
|
||||||
match canvas.add_by_guess_rotated::<launchy::mini::Canvas>(0, 0, launchy::Rotation::None) {
|
match canvas.add_by_guess_rotated::<launchy::mini::Canvas>(0, 0, launchy::Rotation::None) {
|
||||||
Ok(_) => {
|
Ok(o) => o,
|
||||||
println!("Connected to device using USB");
|
|
||||||
clear(&mut canvas);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Failed to connect to device, check USB connection {err}",);
|
eprintln!(
|
||||||
|
"Failed to connect to device, check USB connection {err}",
|
||||||
|
err = err
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
println!("Connected to device using USB");
|
||||||
|
clear(&mut canvas);
|
||||||
(canvas, poller)
|
(canvas, poller)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
|
fn input(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
|
||||||
for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
|
for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
|
||||||
for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
|
for msg in poller
|
||||||
|
.iter_for_millis(POLLER_ITER)
|
||||||
|
.filter(|msg| msg.is_press())
|
||||||
|
{
|
||||||
canvas[msg.pad()] = color;
|
canvas[msg.pad()] = color;
|
||||||
println!("{msg:?}", msg = msg.pad())
|
println!("{msg:?}", msg = msg.pad())
|
||||||
}
|
}
|
||||||
canvas.flush().unwrap();
|
let _res = canvas.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test(ctx: &mut Context) {
|
/*
|
||||||
println!(
|
async fn draw(canvas: &mut CanvasLayout<'_>, ctx: &mut Context) {
|
||||||
"Refresh rate is {sec} seconds",
|
|
||||||
sec = ctx.cfg.refresh.unwrap()
|
|
||||||
);
|
|
||||||
loop {
|
|
||||||
let zabbix_data = match get_zabbix_problems(&ctx.cfg) {
|
|
||||||
Ok(z) => z,
|
|
||||||
Err(_) => {
|
|
||||||
println!(
|
|
||||||
"Error requesting zabbix service, retrying in {} seconds",
|
|
||||||
REQUEST_TIMEOUT
|
|
||||||
);
|
|
||||||
sleep(Duration::from_secs(REQUEST_TIMEOUT as u64));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
println!("{}", zabbix_data);
|
|
||||||
ctx.hotreload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(canvas: &mut CanvasLayout, ctx: &mut Context) {
|
|
||||||
println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap());
|
println!("Refresh rate is {} seconds", ctx.cfg.refresh.unwrap());
|
||||||
loop {
|
loop {
|
||||||
let mut max = 0;
|
let zabbix_data_result = get_zabbix_problems(&ctx.cfg);
|
||||||
let zabbix_data = match get_zabbix_problems(&ctx.cfg) {
|
let zabbix_data = match zabbix_data_result {
|
||||||
Ok(zabbix_data) => zabbix_data,
|
Ok(zabbix_data) => zabbix_data,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!(
|
println!(
|
||||||
"Error requesting zabbix service, retrying in {}",
|
"Error requesting zabbix service, retrying in {}",
|
||||||
REQUEST_TIMEOUT
|
REQUEST_WAITFOR
|
||||||
);
|
);
|
||||||
sleep(Duration::from_secs(REQUEST_TIMEOUT as u64));
|
sleep(Duration::from_secs(REQUEST_WAITFOR as u64));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ctx.datamatrix.compute(&zabbix_data);
|
ctx.datamatrix.compute(&zabbix_data);
|
||||||
clear(canvas);
|
clear(canvas);
|
||||||
|
let mut max = 0;
|
||||||
for (i, j) in ctx.datamatrix.layout.iter().enumerate() {
|
for (i, j) in ctx.datamatrix.layout.iter().enumerate() {
|
||||||
let p = Pad {
|
let p = Pad {
|
||||||
x: ((i as i32) % MATRIX_WIDTH),
|
x: ((i as i32) % MATRIX_WIDTH),
|
||||||
y: ((i as i32) / MATRIX_WIDTH) + 1,
|
y: ((i as i32) / MATRIX_WIDTH) + 1,
|
||||||
};
|
};
|
||||||
canvas[p] = match j.status {
|
let c = match j.status {
|
||||||
5 => Color::RED,
|
5 => Color::RED,
|
||||||
4 => Color::from_hue(0.05),
|
4 => Color::from_hue(0.05),
|
||||||
3 => Color::from_hue(0.1),
|
3 => Color::from_hue(0.1),
|
||||||
2 => Color::from_hue(1.22),
|
2 => Color::from_hue(1.22),
|
||||||
_ => Color::GREEN,
|
_ => Color::GREEN,
|
||||||
};
|
};
|
||||||
if ctx.cfg.sloweffect.unwrap() {
|
canvas[p] = c;
|
||||||
sleep(Duration::from_millis(
|
|
||||||
(SLOWEFFECT_FACTOR * (1.0 / ctx.datamatrix.layout.len() as f32)) as u64,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
canvas.flush().unwrap();
|
|
||||||
max += 1;
|
max += 1;
|
||||||
if max >= MATRIX_SIZE {
|
if max >= MATRIX_SIZE {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ctx.cfg.sloweffect.unwrap() {
|
||||||
|
sleep(Duration::from_millis(15));
|
||||||
|
}
|
||||||
|
canvas.flush().unwrap();
|
||||||
}
|
}
|
||||||
ctx.hotreload();
|
ctx.hotreload();
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
fn effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
|
fn effect(canvas: &mut CanvasLayout, poller: &mut CanvasLayoutPoller) {
|
||||||
loop {
|
for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
|
||||||
for color in (0u64..).map(|f| Color::red_green_color(f as f32 / 60.0 / 2.5)) {
|
for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
|
||||||
for msg in poller.iter_for_millis(17).filter(|msg| msg.is_press()) {
|
canvas[msg.pad()] = color;
|
||||||
canvas[msg.pad()] = color;
|
println!("{msg:?}", msg = msg.pad())
|
||||||
println!("{msg:?}", msg = msg.pad())
|
|
||||||
}
|
|
||||||
canvas.flush().unwrap();
|
|
||||||
|
|
||||||
canvas.iter().for_each(|pad| {
|
|
||||||
let surrounding_color = pad
|
|
||||||
.neighbors_5()
|
|
||||||
.iter()
|
|
||||||
.map(|&p| canvas.get(p).unwrap_or(Color::GREEN))
|
|
||||||
.sum::<Color>()
|
|
||||||
/ 5.0
|
|
||||||
/ 1.05;
|
|
||||||
|
|
||||||
canvas[pad] = canvas[pad].mix(surrounding_color, 0.4);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
clear(canvas);
|
let _res = canvas.flush();
|
||||||
|
|
||||||
|
canvas.iter().for_each(|pad| {
|
||||||
|
let surrounding_color = pad
|
||||||
|
.neighbors_5()
|
||||||
|
.iter()
|
||||||
|
.map(|&p| canvas.get(p).unwrap_or(Color::GREEN))
|
||||||
|
.sum::<Color>()
|
||||||
|
/ 5.0
|
||||||
|
/ 1.05;
|
||||||
|
|
||||||
|
canvas[pad] = canvas[pad].mix(surrounding_color, 0.4);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(canvas: &mut CanvasLayout) {
|
fn clear(canvas: &mut CanvasLayout) {
|
||||||
for a in 0..MATRIX_WIDTH {
|
for a in 0..8 {
|
||||||
for b in 0..MATRIX_WIDTH + 1 {
|
for b in 0..9 {
|
||||||
canvas[Pad { x: a, y: b }] = Color {
|
canvas[Pad { x: a, y: b }] = Color {
|
||||||
r: ZERO_COLOR,
|
r: 0f32,
|
||||||
g: ZERO_COLOR,
|
g: 0f32,
|
||||||
b: ONE_COLOR,
|
b: 1f32,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
canvas.flush().unwrap();
|
let _res = canvas.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
let (send, recv): (Sender<Context>, Receiver<Context>) = channel();
|
||||||
|
match ctx.mode.as_str() {
|
||||||
|
/*"draw" => {
|
||||||
|
let (mut canvas, mut _poller) = initpad();
|
||||||
|
draw(&mut canvas, c);
|
||||||
|
}
|
||||||
|
"input" => {
|
||||||
|
let (mut canvas, mut poller) = initpad();
|
||||||
|
input(&mut canvas, &mut poller);
|
||||||
|
}
|
||||||
|
"effect" => {
|
||||||
|
let (mut canvas, mut poller) = initpad();
|
||||||
|
effect(&mut canvas, &mut poller);
|
||||||
|
}*/ "test" => loop {
|
||||||
|
let mut data = cm.lock().unwrap();
|
||||||
|
let future1 = test(&mut data);
|
||||||
|
let mut data = cm.lock().unwrap();
|
||||||
|
let future2 = data.hotreload();
|
||||||
|
//futures::join!(future1, future2);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
println!("No valid option choosen for mode");
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
@ -1,64 +1,54 @@
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
//use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
//use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub const ZABBIX_API_VERSION: &'static str = "2.0";
|
pub const ZABBIX_API_VERSION: &'static str = "2.0";
|
||||||
pub const ZABBIX_API_ID: i32 = 1;
|
pub const ZABBIX_API_ID: i32 = 1;
|
||||||
|
|
||||||
/// Refresh the user token
|
/// Refresh the user token
|
||||||
pub fn get_zabbix_authtoken(cfg: &mut Config) {
|
pub async fn get_zabbix_authtoken(cfg: &mut Config) -> Result<(), reqwest::Error> {
|
||||||
let body = build_query_auth_token(&cfg.username, &cfg.password);
|
let body = build_query_auth_token(&cfg.username, &cfg.password);
|
||||||
let resp = reqwest::blocking::Client::new()
|
let resp = reqwest::Client::new()
|
||||||
.post(&cfg.server)
|
.post(&cfg.server)
|
||||||
.json(&body)
|
.json(&body)
|
||||||
.send();
|
.send()
|
||||||
match resp {
|
.await?;
|
||||||
Ok(v) => {
|
let values: JsonValue = resp.json().await?;
|
||||||
let values: JsonValue = v.json().unwrap();
|
cfg.authtoken = Some(values["result"].as_str().unwrap().to_string());
|
||||||
cfg.authtoken = Some(values["result"].as_str().unwrap().to_string());
|
Ok(())
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("{}", e);
|
|
||||||
cfg.authtoken = Some("".to_string());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch Zabbix problems
|
/// Fetch Zabbix problems
|
||||||
pub fn get_zabbix_problems(cfg: &Config) -> Result<JsonValue, reqwest::Error> {
|
pub async fn get_zabbix_problems(cfg: &Config) -> Result<JsonValue, reqwest::Error> {
|
||||||
let body = build_query_triggers(&cfg.authtoken.as_ref().unwrap_or(&String::from("")));
|
let body = build_query_triggers(&cfg.authtoken.as_ref().unwrap_or(&String::from("")));
|
||||||
let resp = reqwest::blocking::Client::new()
|
let resp = reqwest::Client::new()
|
||||||
.post(&cfg.server)
|
.post(&cfg.server)
|
||||||
.json(&body)
|
.json(&body)
|
||||||
.send();
|
.send()
|
||||||
|
.await?;
|
||||||
match resp {
|
Ok(resp.json().await?)
|
||||||
Ok(v) => v.json(),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if Zabbix is operational
|
/// Check if Zabbix is operational
|
||||||
/// Undefinitely check that Zabbix works
|
/// Undefinitely check that Zabbix works
|
||||||
/*fn check_zabbix_connection(cfg: &Config) -> bool {
|
async fn _async_check_zabbix_connection(cfg: &Config) -> Result<bool, reqwest::Error> {
|
||||||
let mut res: bool = false;
|
let mut res: bool = false;
|
||||||
let delay = 5;
|
let delay = 5;
|
||||||
let timeout = 10;
|
let timeout = 10;
|
||||||
while !res {
|
while !res {
|
||||||
let req = reqwest::blocking::Client::builder().timeout(Duration::from_secs(timeout)).build().unwrap();
|
let req = reqwest::Client::builder()
|
||||||
let resp = req.get(&cfg.server)
|
.timeout(Duration::from_secs(timeout))
|
||||||
.send();
|
.build()
|
||||||
match resp {
|
.unwrap();
|
||||||
Ok(_) => res = true,
|
req.get(&cfg.server).send().await?;
|
||||||
Err(_) => res = false
|
res = true;
|
||||||
}
|
println!("Waiting for {delay}", delay = delay);
|
||||||
println!("Waiting for {delay}", delay=delay);
|
|
||||||
sleep(Duration::from_secs(delay));
|
sleep(Duration::from_secs(delay));
|
||||||
}
|
}
|
||||||
res
|
Ok(res)
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/// Build the query that fetchs the token
|
/// Build the query that fetchs the token
|
||||||
fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) -> JsonValue {
|
fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) -> JsonValue {
|
||||||
@ -67,7 +57,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
|
|||||||
"jsonrpc": ZABBIX_API_VERSION,
|
"jsonrpc": ZABBIX_API_VERSION,
|
||||||
"method": zabbix_api_function,
|
"method": zabbix_api_function,
|
||||||
"params": {
|
"params": {
|
||||||
"username": zabbix_username,
|
"user": zabbix_username,
|
||||||
"password": zabbix_password
|
"password": zabbix_password
|
||||||
},
|
},
|
||||||
"id": ZABBIX_API_ID
|
"id": ZABBIX_API_ID
|
||||||
@ -75,7 +65,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build the query that fetchs problems
|
/// Build the query that fetchs problems
|
||||||
/*fn build_query_problems(zabbix_token: &String, zabbix_limit: i64) -> JsonValue {
|
fn _build_query_problems(zabbix_token: &String, zabbix_limit: i64) -> JsonValue {
|
||||||
let zabbix_api_function = "problem.get";
|
let zabbix_api_function = "problem.get";
|
||||||
json!({
|
json!({
|
||||||
"jsonrpc": ZABBIX_API_VERSION,
|
"jsonrpc": ZABBIX_API_VERSION,
|
||||||
@ -95,7 +85,7 @@ fn build_query_auth_token(zabbix_username: &String, zabbix_password: &String) ->
|
|||||||
"auth": zabbix_token,
|
"auth": zabbix_token,
|
||||||
"id": ZABBIX_API_ID
|
"id": ZABBIX_API_ID
|
||||||
})
|
})
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/// Build the query that fetchs triggers
|
/// Build the query that fetchs triggers
|
||||||
fn build_query_triggers(zabbix_token: &String) -> JsonValue {
|
fn build_query_triggers(zabbix_token: &String) -> JsonValue {
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::fmt::{Display, Formatter, Result};
|
use std::fmt::{Display, Formatter, Result};
|
||||||
|
|
||||||
/*#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ZabbixStatus {
|
pub enum _ZabbixStatus {
|
||||||
Disaster = 5,
|
_Disaster = 5,
|
||||||
High = 4,
|
_High = 4,
|
||||||
Average = 3,
|
_Average = 3,
|
||||||
Warning = 2,
|
_Warning = 2,
|
||||||
Info = 1,
|
_Info = 1,
|
||||||
}*/
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DataMatrix {
|
pub struct DataMatrix {
|
||||||
@ -26,10 +26,6 @@ impl DataMatrix {
|
|||||||
let severity = res["priority"].as_str().unwrap().parse::<i64>().unwrap();
|
let severity = res["priority"].as_str().unwrap().parse::<i64>().unwrap();
|
||||||
self.layout.push(ZabbixProblems { status: severity });
|
self.layout.push(ZabbixProblems { status: severity });
|
||||||
}
|
}
|
||||||
// test
|
|
||||||
/*for i in 0..1000 {
|
|
||||||
self.layout.push(ZabbixProblems { status: 5 });
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user