From dc60244834a3748639ba17d9a311e5eef704948d Mon Sep 17 00:00:00 2001 From: LailaTheElf Date: Mon, 14 Apr 2025 12:36:02 +0200 Subject: [PATCH] add pc switch automation --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/automation/mod.rs | 139 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 123 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13a9ae3..03c47d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,7 +289,7 @@ dependencies = [ [[package]] name = "mqttAutomation" -version = "1.2.0" +version = "1.3.0" dependencies = [ "crossbeam", "json", diff --git a/Cargo.toml b/Cargo.toml index 081df73..dc69212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mqttAutomation" -version = "1.2.0" +version = "1.3.0" edition = "2021" [dependencies] diff --git a/src/automation/mod.rs b/src/automation/mod.rs index 578ac18..bdc0aa8 100644 --- a/src/automation/mod.rs +++ b/src/automation/mod.rs @@ -11,15 +11,46 @@ use crate::automation::json::json_parser; #[derive(Deserialize)] pub struct SettingsConf { base_topic: String, + alarm_hour: u8, alarm_minute: u8 } +struct PingStats { + total: u16, + fails: u16, + avg: f32 +} + +fn ping_stat_update(stats: &mut PingStats, payload: String) -> Option { + let mut new_value: Option = None; + if !payload.eq("false") { + match payload.parse::() { + Ok(value) => new_value = Some(value), + Err(_) => {} + } + } + + stats.total += 1; + match new_value { + Some(value) => stats.avg += (value - stats.avg) / f32::from(stats.total - stats.fails), + None => stats.fails += 1 + } + new_value +} + pub struct Automation { tx: Sender, + clock_dow: u8, clock_hour: u8, clock_min: u8, + clock_sec: u8, + + elfdesktop_last_on: u32, + elfdesktop_state: bool, + + ping_elfdesktop: PingStats, config: SettingsConf } @@ -33,7 +64,7 @@ impl Automation { } } - fn lamp01_set(&self, state: bool) { + fn tx_set(&self, topic: String, state: bool) { let payload: String; if state { payload = String::from("ON"); @@ -41,12 +72,22 @@ impl Automation { payload = String::from("OFF"); } self.tx({ mqtt_client::MqttMessage { - topic: String::from("/cool/devices/lamp-01/set"), + topic: topic, payload: payload, retain: false, qos: mqtt_client::QoS::AtMostOnce, }}); } + fn lamp01_set(&self, state: bool) { + self.tx_set(String::from("/cool/devices/lamp-01/set"), state); + } + fn pc_sw_set(&self, state: bool) { + self.tx_set(String::from("/cool/devices/sw-01/set"), state); + } + + fn get_current_time(&self) -> u32 { + u32::from(self.clock_dow) * 24 + u32::from(self.clock_hour) * 60 + u32::from(self.clock_min) + } fn alarm(&self) { self.lamp01_set(true); @@ -74,16 +115,29 @@ impl Automation { } fn clock_message_in(&mut self, message: mqtt_client::MqttMessage) { - if message.topic.eq("clock/time/hour") { + if message.topic.eq("clock/time/second") { match message.payload.parse::() { Err(e) => - println!("ERROR: clock_message_in: clock/time/hour has invalid payload ({:?})", e), - Ok(n) => { - self.clock_hour = n; - - if self.config.alarm_minute == 0 && n == self.config.alarm_hour && self.clock_dow < 5 { - self.alarm(); + println!("ERROR: clock_message_in: clock/time/second has invalid payload ({:?})", e), + Ok(n) => { + self.clock_sec = n; + + if n % 20 == 0 { + let json = format!( + "{{ \"deviceID\": 0, \"ping\": {}, \"pong\": {} }}", + self.ping_elfdesktop.avg, + 1.0 - (f32::from(self.ping_elfdesktop.fails) / f32::from(self.ping_elfdesktop.total)) + ); + self.tx({ mqtt_client::MqttMessage { + topic: String::from("/kees/db/insert/ping"), + payload: json, + retain: false, + qos: mqtt_client::QoS::AtMostOnce, + }}); + self.ping_elfdesktop.total = 0; + self.ping_elfdesktop.fails = 0; + self.ping_elfdesktop.avg = 0.0; } } } @@ -97,7 +151,24 @@ impl Automation { Ok(n) => { self.clock_min = n; - if n == self.config.alarm_minute && self.clock_hour == self.config.alarm_hour && self.clock_dow < 5 { + if n == self.config.alarm_minute + && self.clock_hour == self.config.alarm_hour + && self.clock_dow < 5 { + self.alarm(); + } + } + } + + } + else if message.topic.eq("clock/time/hour") { + + match message.payload.parse::() { + Err(e) => + println!("ERROR: clock_message_in: clock/time/hour has invalid payload ({:?})", e), + Ok(n) => { + self.clock_hour = n; + + if self.config.alarm_minute == 0 && n == self.config.alarm_hour && self.clock_dow < 5 { self.alarm(); } } @@ -113,7 +184,7 @@ impl Automation { } } - // println!("DEBUG: clock_message_in: current time: {}:{}", self.clock_hour, self.clock_min); + // 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) { @@ -140,6 +211,30 @@ impl Automation { } } + else if message.topic.eq("/kees/ping/elfdesktop") { + + let now: u32 = self.get_current_time(); + let ping = ping_stat_update(&mut self.ping_elfdesktop, message.payload); + + match ping { + None => { + if self.elfdesktop_last_on < now - 3 { + if self.elfdesktop_state { + self.pc_sw_set(false); + self.elfdesktop_state = false; + } + } + }, + Some(_) => { + self.elfdesktop_last_on = now; + if !self.elfdesktop_state { + self.pc_sw_set(true); + self.elfdesktop_state = true; + } + } + } + + } } } @@ -147,16 +242,11 @@ impl Automation { impl mqtt_client::MqttTool for Automation { fn new(client: rumqttc::Client, tx: Sender, config: SettingsConf) -> Automation { - match client.subscribe("clock/time/hour", QoS::AtMostOnce) { + match client.subscribe("clock/time/#", QoS::AtMostOnce) { Err(e) => println!("ERROR: main: faild to subscribe to clock/time/hour ({})", e), Ok(_) => {} } - match client.subscribe("clock/time/minute", QoS::AtMostOnce) { - Err(e) => - println!("ERROR: main: faild to subscribe to clock/time/minute ({})", e), - Ok(_) => {} - } match client.subscribe("clock/date/dow", QoS::AtMostOnce) { Err(e) => println!("ERROR: main: faild to subscribe to clock/date/dow ({})", e), @@ -172,13 +262,26 @@ impl mqtt_client::MqttTool for Automation { println!("ERROR: main: faild to subscribe to KNMITemp/values ({})", e), Ok(_) => {} } + match client.subscribe("/kees/ping/#", QoS::AtMostOnce) { + Err(e) => + println!("ERROR: main: faild to subscribe to KNMITemp/values ({})", e), + Ok(_) => {} + } Automation { tx, + clock_dow: u8::MAX, clock_hour: u8::MAX, clock_min: u8::MAX, - config: config + clock_sec: u8::MAX, + + elfdesktop_last_on: u32::MAX, + elfdesktop_state: false, + + ping_elfdesktop: { PingStats { total: 0, fails: 0, avg: 0.0 } }, + + config: config, } }