use std::{fs, time::Duration}; use serde::Deserialize; use chrono::{Local, Timelike, Datelike}; use mqtt_client::{mqtt_client, MqttMessage, MqttEvent, Sender, Receiver, QoS, Client, MqttTool}; fn get_u16_from_i32(value: i32) -> Option { match u16::try_from(value) { Ok(n) => Some(n), Err(_) => None } } fn get_u16_from_u32(value: u32) -> Option { match u16::try_from(value) { Ok(n) => Some(n), Err(_) => None } } struct Clock { tx: Sender, // client: Client, connected: bool, last_year: Option, last_month: Option, last_dom: Option, last_dow: Option, last_iso_week: Option, last_iso_year: Option, last_hour: Option, last_minute: Option, last_second: Option } impl Clock { fn tx(&self, topic: String, value: Option, retain: bool) { match value { None => {}, Some(v) => { let message = MqttMessage { topic, payload: v.to_string(), retain: retain, qos: QoS::AtMostOnce, }; match self.tx.send(message) { Err(n) => println!("ERROR: faild to send publish ({:?})", n), Ok(_) => {} } } } } fn tx_time(&self) { let second = self.last_second; let minute = self.last_minute; let hour = self.last_hour; if let (Some(second), Some(minute), Some(hour)) = (second, minute, hour) { let payload = format!("{{\"second\":{},\"minute\":{},\"hour\":{},\"time\":\"{}:{:02}:{:02}\"}}", second, minute, hour, hour, minute, second); let message = MqttMessage { topic: String::from("clock/time/time"), payload, retain: false, qos: QoS::AtMostOnce, }; match self.tx.send(message) { Err(n) => println!("ERROR: faild to send publish ({:?})", n), Ok(_) => {} } } } fn time(&mut self) { let datetime = Local::now(); if self.last_second != get_u16_from_u32(datetime.second()) { self.last_second = get_u16_from_u32(datetime.second()); if self.last_minute != get_u16_from_u32(datetime.minute()) { self.last_minute = get_u16_from_u32(datetime.minute()); if self.last_hour != get_u16_from_u32(datetime.hour()) { self.last_hour = get_u16_from_u32(datetime.hour()); self.tx(String::from("clock/time/hour"), self.last_hour, true); self.date(datetime); } self.tx(String::from("clock/time/minute"), self.last_minute, false); } self.tx(String::from("clock/time/second"), self.last_second, false); self.tx_time(); } } fn date(&mut self, datetime: chrono::DateTime) { if self.last_dom != get_u16_from_u32(datetime.day()) { self.last_dom = get_u16_from_u32(datetime.day()); if self.last_iso_week != get_u16_from_u32(datetime.iso_week().week()) { self.last_iso_week = get_u16_from_u32(datetime.iso_week().week()); if self.last_iso_year != get_u16_from_i32(datetime.iso_week().year()) { self.last_iso_year = get_u16_from_i32(datetime.iso_week().year()); self.tx(String::from("clock/date/isoYear"), self.last_iso_year, true); } self.tx(String::from("clock/date/isoWeek"), self.last_iso_week, true); } self.tx(String::from("clock/date/dom"), self.last_dom, true); if self.last_month != get_u16_from_u32(datetime.month()) { self.last_month = get_u16_from_u32(datetime.month()); if self.last_year != get_u16_from_i32(datetime.year()) { self.last_year = get_u16_from_i32(datetime.year()); self.tx(String::from("clock/date/year"), self.last_year, true); } self.tx(String::from("clock/date/month"), self.last_month, true); } self.last_dow = Some(datetime.weekday() as u16); self.tx(String::from("clock/date/dow"), self.last_dow, true); } } fn init(&mut self) { self.last_year = None; self.last_month = None; self.last_dom = None; self.last_dow = None; self.last_iso_week = None; self.last_iso_year = None; self.last_hour = None; } } impl MqttTool for Clock { fn new(tx: Sender, _config: i8, _client: Client) -> Clock { Clock { tx, // client, connected: false, last_year: None, last_month: None, last_dom: None, last_dow: None, last_iso_week: None, last_iso_year: None, last_hour: None, last_minute: None, last_second: None } } fn run(&mut self, event: Receiver) { loop { match event.recv_timeout(Duration::from_millis(500)) { Ok(e) => { match e { MqttEvent::Connected => { self.init(); self.connected = true; }, MqttEvent::Disconnected => { self.connected = false; }, MqttEvent::Message(_) => {/* nothing to do, no subscriptions */} } }, Err(_) => {}, } if self.connected { self.time(); } } } } #[derive(Deserialize)] struct SettingsMQTT { host: String, port: u16, client: String, user: String, pass: String } #[derive(Deserialize)] struct Settings { mqtt: SettingsMQTT } enum SettingsError { ReadError, SyntaxError } fn read_config() -> Result { let mut config_str: String = String::from("error"); match fs::read_to_string("./mqttClock.yml") { Err(_) => { println!("INFO : read_config: could not find './mqttClock.yml'. try '~/.config/mqttClock.yml"); match fs::read_to_string("~/.config/mqttClock.yml") { Err(_) => { println!("INFO : read_config: could not find '~/.config/mqttClock.yml'. try '/etc/mqttClock.yml"); match fs::read_to_string("/etc/mqttClock.yml") { Err(_) => println!("ERROR: read_config: could not find any config file"), Ok(str) => config_str = str } }, Ok(str) => config_str = str } }, Ok(str) => config_str = str } if config_str.eq("error") { Err::(SettingsError::ReadError) } else { match serde_yaml::from_str::(&config_str) { Ok(n) => Ok(n), Err(e) => { println!("ERROR: read_config: syntax error: {:?}", e); Err(SettingsError::SyntaxError) } } } } fn main() { // get setting match read_config() { Ok(conf) => mqtt_client::run::( conf.mqtt.host, conf.mqtt.port, conf.mqtt.client, conf.mqtt.user, conf.mqtt.pass, 0 ), Err(_) => {} } println!("INFO : main: exit"); }