diff --git a/Cargo.lock b/Cargo.lock index 03c47d3..fd4d06f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,8 +280,8 @@ dependencies = [ [[package]] name = "mqtt-client" -version = "3.0.0" -source = "git+https://gitea.finnvanreenen.nl/LailaTheElf/mqttClient.git?tag=v3.0.0#f186e437331b0c8680ac9917d7b88f17bb5f176b" +version = "4.0.0" +source = "git+https://gitea.finnvanreenen.nl/LailaTheElf/mqttClient.git?tag=v4.0.0#49e8adf2eb768fcd147f0d6508a2f32eed86a641" dependencies = [ "crossbeam", "rumqttc", @@ -289,7 +289,7 @@ dependencies = [ [[package]] name = "mqttAutomation" -version = "1.3.0" +version = "1.3.1" dependencies = [ "crossbeam", "json", diff --git a/Cargo.toml b/Cargo.toml index dc69212..1c98056 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mqttAutomation" -version = "1.3.0" +version = "1.3.1" edition = "2021" [dependencies] @@ -9,4 +9,4 @@ json = "0.12.4" rumqttc = "0.24.0" serde = { version = "1.0.217", features = ["derive"] } serde_yaml = "0.9.34" -mqtt-client = { tag = "v3.0.0", git = "https://gitea.finnvanreenen.nl/LailaTheElf/mqttClient.git" } +mqtt-client = { tag = "v4.0.0", git = "https://gitea.finnvanreenen.nl/LailaTheElf/mqttClient.git" } diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..f16ac69 --- /dev/null +++ b/build.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +cross build --target aarch64-unknown-linux-gnu --release +cargo build --release + +cp target/aarch64-unknown-linux-gnu/release/mqttAutomation mqttAutomation-aarch64 +cp target/release/mqttAutomation mqttAutomation-x86_64 diff --git a/src/automation/mod.rs b/src/automation/mod.rs index bdc0aa8..d0134b8 100644 --- a/src/automation/mod.rs +++ b/src/automation/mod.rs @@ -3,7 +3,7 @@ mod json; use std::{thread, time::Duration}; use serde::Deserialize; -use mqtt_client::{MqttMessage, Sender, Receiver, QoS}; +use mqtt_client::{MqttMessage, MqttEvent, Sender, Receiver, QoS}; use mqtt_client::mqtt_client; use crate::automation::json::json_parser; @@ -40,7 +40,8 @@ fn ping_stat_update(stats: &mut PingStats, payload: String) -> Option { } pub struct Automation { - tx: Sender, + tx: Sender, + client: mqtt_client::Client, clock_dow: u8, clock_hour: u8, @@ -57,7 +58,7 @@ pub struct Automation { impl Automation { - fn tx(&self, message: mqtt_client::MqttMessage) { + fn tx(&self, message: MqttMessage) { match self.tx.send(message) { Err(n) => println!("ERROR: faild to send publish ({:?})", n), Ok(_n) => {} @@ -71,7 +72,7 @@ impl Automation { } else { payload = String::from("OFF"); } - self.tx({ mqtt_client::MqttMessage { + self.tx({ MqttMessage { topic: topic, payload: payload, retain: false, @@ -86,35 +87,76 @@ impl Automation { } fn get_current_time(&self) -> u32 { - u32::from(self.clock_dow) * 24 + u32::from(self.clock_hour) * 60 + u32::from(self.clock_min) + self.get_time(None, None, None, None) + } + fn get_time(&self, dow: Option, hour: Option, min: Option, sec: Option) -> u32 { + let d = match dow { + Some(n) => n, + None => self.clock_dow + }; + let h = match hour { + Some(n) => n, + None => self.clock_hour + }; + let m = match min { + Some(n) => n, + None => self.clock_min + }; + let s = match sec { + Some(n) => n, + None => self.clock_sec + }; + ((u32::from(d) * 24 + u32::from(h)) * 60 + u32::from(m)) * 60 + u32::from(s) } - fn alarm(&self) { + fn alarm_trigger(&self) { self.lamp01_set(true); } + fn alarm_send_config(&self) { + self.tx({ MqttMessage { + topic: format!("{}alarm", self.config.base_topic), + payload: format!("{}:{}", self.config.alarm_hour, self.config.alarm_minute), + retain: true, + qos: mqtt_client::QoS::AtMostOnce, + }}); + } - fn config_message_in(&mut self, message: mqtt_client::MqttMessage, topic: String) { - if topic.starts_with("alarm/hour") { + fn config_message_in(&mut self, message: MqttMessage, topic: String) { + if topic.starts_with("alarm/set") { - match message.payload.parse::() { - Err(e) => - println!("ERROR: config_message_in: alarm/hour has invalid payload ({:?})", e), - Ok(n) => self.config.alarm_hour = n + let mut time = message.payload.split(':'); + if time.clone().count() != 2 { + println!("ERROR: config_message_in: alarm/set has invalid payload. incorect number of slices ({})", message.payload) } + else { + let hour_str = time.next().unwrap(); + let min_str = time.next().unwrap(); + let mut hour: Option = None; + let mut min: Option = None; - } - else if topic.eq("alarm/minute") { + match hour_str.parse::() { + Err(_) => + println!("ERROR: config_message_in: alarm/set has invalid payload. hour is NaN ({})", message.payload), + Ok(n) => hour = Some(n) + } - match message.payload.parse::() { - Err(e) => - println!("ERROR: config_message_in: alarm/minute has invalid payload ({:?})", e), - Ok(n) => self.config.alarm_minute = n + match min_str.parse::() { + Err(_) => + println!("ERROR: config_message_in: alarm/set has invalid payload. min is NaN ({})", message.payload), + Ok(n) => min = Some(n) + } + + if hour != None && min != None { + self.config.alarm_hour = hour.unwrap(); + self.config.alarm_minute = min.unwrap(); + } } + self.alarm_send_config(); } } - fn clock_message_in(&mut self, message: mqtt_client::MqttMessage) { + fn clock_message_in(&mut self, message: MqttMessage) { if message.topic.eq("clock/time/second") { match message.payload.parse::() { @@ -129,7 +171,7 @@ impl Automation { self.ping_elfdesktop.avg, 1.0 - (f32::from(self.ping_elfdesktop.fails) / f32::from(self.ping_elfdesktop.total)) ); - self.tx({ mqtt_client::MqttMessage { + self.tx({ MqttMessage { topic: String::from("/kees/db/insert/ping"), payload: json, retain: false, @@ -146,15 +188,16 @@ impl Automation { else if message.topic.eq("clock/time/minute") { match message.payload.parse::() { - Err(e) => + Err(e) => println!("ERROR: clock_message_in: clock/time/minute has invalid payload ({:?})", e), - Ok(n) => { + Ok(n) => { self.clock_min = n; - if n == self.config.alarm_minute - && self.clock_hour == self.config.alarm_hour + if n == self.config.alarm_minute + && n != 0 + && self.clock_hour == self.config.alarm_hour && self.clock_dow < 5 { - self.alarm(); + self.alarm_trigger(); } } } @@ -163,13 +206,15 @@ impl Automation { else if message.topic.eq("clock/time/hour") { match message.payload.parse::() { - Err(e) => + Err(e) => println!("ERROR: clock_message_in: clock/time/hour has invalid payload ({:?})", e), Ok(n) => { self.clock_hour = n; + self.alarm_send_config(); + if self.config.alarm_minute == 0 && n == self.config.alarm_hour && self.clock_dow < 5 { - self.alarm(); + self.alarm_trigger(); } } } @@ -187,7 +232,7 @@ impl Automation { // println!("DEBUG: clock_message_in: current time: {}:{}:{}", self.clock_hour, self.clock_min, self.clock_sec); } - fn message_in(&mut self, message: mqtt_client::MqttMessage) { + fn message_in(&mut self, message: MqttMessage) { // println!("DEBUG : mqtt_automation: {}: {}", message.topic, message.payload); if message.topic.starts_with(&self.config.base_topic) { let topic = message.topic[self.config.base_topic.len()..].to_string(); @@ -203,7 +248,14 @@ impl Automation { match json_parser::get_u32(payload_json, path) { Ok(gr) => { if gr > 30 { - self.lamp01_set(false); + let alarm_time = + self.get_time(None, Some(self.config.alarm_hour), Some(self.config.alarm_minute), Some(0)); + let time_30min = + self.get_time(Some(0), Some(0), Some(29), Some(0)); + let time_diff: i64 = i64::from(self.get_current_time()) - i64::from(alarm_time); + if self.clock_dow >= 5 || time_diff < 0 || time_diff > i64::from(time_30min) { + self.lamp01_set(false); + } } }, Err(e) => @@ -236,40 +288,46 @@ impl Automation { } } -} -impl mqtt_client::MqttTool for Automation { - fn new(client: rumqttc::Client, tx: Sender, config: SettingsConf) -> Automation { - - match client.subscribe("clock/time/#", QoS::AtMostOnce) { + fn init(&self) { + println!("DEBUG: init"); + match self.client.subscribe("clock/time/#", QoS::AtMostOnce) { Err(e) => println!("ERROR: main: faild to subscribe to clock/time/hour ({})", e), Ok(_) => {} } - match client.subscribe("clock/date/dow", QoS::AtMostOnce) { + match self.client.subscribe("clock/date/dow", QoS::AtMostOnce) { Err(e) => println!("ERROR: main: faild to subscribe to clock/date/dow ({})", e), Ok(_) => {} } - match client.subscribe("/kees/automation/#", QoS::AtMostOnce) { + match self.client.subscribe("/kees/automation/#", QoS::AtMostOnce) { Err(e) => println!("ERROR: main: faild to subscribe to automation/alarm/# ({})", e), Ok(_) => {} } - match client.subscribe("/cool/devices/KNMITemp/values", QoS::AtMostOnce) { + match self.client.subscribe("/cool/devices/KNMITemp/values", QoS::AtMostOnce) { Err(e) => println!("ERROR: main: faild to subscribe to KNMITemp/values ({})", e), Ok(_) => {} } - match client.subscribe("/kees/ping/#", QoS::AtMostOnce) { + match self.client.subscribe("/kees/ping/#", QoS::AtMostOnce) { Err(e) => println!("ERROR: main: faild to subscribe to KNMITemp/values ({})", e), Ok(_) => {} } + self.alarm_send_config(); + } +} + + +impl mqtt_client::MqttTool for Automation { + fn new(tx: Sender, config: SettingsConf, client: mqtt_client::Client) -> Automation { Automation { tx, + client, clock_dow: u8::MAX, clock_hour: u8::MAX, @@ -285,7 +343,7 @@ impl mqtt_client::MqttTool for Automation { } } - fn run(&mut self, rx: Receiver) { + fn run(&mut self, rx: Receiver) { loop { let message = rx.recv(); match message { @@ -293,8 +351,16 @@ impl mqtt_client::MqttTool for Automation { println!("ERROR: mqttAutomation: failed to receve an message ({})", e); thread::sleep(Duration::from_millis(500)); }, - Ok(message) => { - self.message_in(message); + Ok(event) => { + match event { + MqttEvent::Connected => { + self.init(); + }, + MqttEvent::Disconnected => {}, + MqttEvent::Message(message) => { + self.message_in(message); + }, + } } } }