From 987150c9b630eb2b1540e4044db2f3b010b18c2b Mon Sep 17 00:00:00 2001 From: sigil-03 Date: Sun, 6 Apr 2025 14:34:15 -0600 Subject: [PATCH] make SET stub + move elements into system wrapper --- src/control.rs | 5 +++++ src/main.rs | 16 ++++++++++--- src/monitor.rs | 61 +------------------------------------------------- src/system.rs | 51 +++++++++++++++++++++++++++++++++++++++++ src/tasmota.rs | 20 +++++++++++++++-- src/types.rs | 20 +++++++++++++++++ 6 files changed, 108 insertions(+), 65 deletions(-) create mode 100644 src/control.rs create mode 100644 src/system.rs create mode 100644 src/types.rs diff --git a/src/control.rs b/src/control.rs new file mode 100644 index 0000000..2ecde08 --- /dev/null +++ b/src/control.rs @@ -0,0 +1,5 @@ +use crate::types::Error; + +pub trait Control { + async fn set_power(&self) -> Result<(), Error>; +} diff --git a/src/main.rs b/src/main.rs index 129371c..42e68a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,31 @@ use clap::{Parser, Subcommand}; -use monitor::Monitor; +mod control; mod monitor; +mod system; mod tasmota; +mod types; #[derive(Subcommand)] pub enum Commands { Monitor, + #[command(subcommand)] + Set(types::PowerState), } impl Commands { pub async fn execute(self, config_file: &str) { let handle = match self { Self::Monitor => { - let m = Monitor::new_from_file(config_file).unwrap(); + let s = system::System::new_from_file(config_file).unwrap(); tokio::spawn(async move { - m.get_power().await.unwrap(); + s.get_power().await.unwrap(); + }) + } + Self::Set(state) => { + // let c = Controller::new_from_file(config_file).unwrap(); + tokio::spawn(async move { + println!("SET"); }) } }; diff --git a/src/monitor.rs b/src/monitor.rs index c43974a..6d121d9 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,68 +1,9 @@ use crate::tasmota::{PowerStatusData, StatusResponse, TasmotaInterface, TasmotaInterfaceConfig}; +use crate::types::Error; use reqwest::Client; use serde::Deserialize; use std::fs; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("io error")] - IoError(#[from] std::io::Error), - #[error("toml parsing error")] - ParseError(#[from] toml::de::Error), - #[error("request error")] - RequestError(#[from] reqwest::Error), - #[error("JSON Parse error")] - JsonParseError(#[from] serde_json::Error), -} pub trait Monitoring { async fn get_power(&self) -> Result; } - -#[derive(Deserialize)] -pub struct MonitorConfig { - targets: Vec, -} -impl MonitorConfig { - fn print(&self) { - for t in &self.targets { - t.print(); - } - } -} - -pub struct Monitor { - targets: Vec, - client: Client, -} - -impl Monitor { - pub fn new_from_file(config_file: &str) -> Result { - let config_str = fs::read_to_string(config_file)?; - let config: MonitorConfig = toml::from_str(&config_str)?; - Ok(Self { - targets: Monitor::load_targets(&config.targets), - client: Client::new(), - }) - } - - pub fn load_targets(targets: &Vec) -> Vec { - let mut v = Vec::new(); - for target in targets { - v.push(TasmotaInterface::new(target.clone())); - } - v - } - - pub async fn get_power(&self) -> Result<(), Error> { - for target in &self.targets { - if let Ok(res) = target.get_power().await { - target.print(); - println!("* POWER: {}W", res); - println!("------------------") - } - } - Ok(()) - } -} diff --git a/src/system.rs b/src/system.rs new file mode 100644 index 0000000..61b7791 --- /dev/null +++ b/src/system.rs @@ -0,0 +1,51 @@ +use crate::monitor::Monitoring; +use crate::tasmota::{TasmotaInterface, TasmotaInterfaceConfig}; +use crate::types::Error; +use reqwest::Client; +use serde::Deserialize; +use std::fs; + +#[derive(Deserialize)] +pub struct SystemConfig { + components: Vec, +} +impl SystemConfig { + fn print(&self) { + for t in &self.components { + t.print(); + } + } +} + +pub struct System { + components: Vec, +} + +impl System { + pub fn new_from_file(config_file: &str) -> Result { + let config_str = fs::read_to_string(config_file)?; + let config: SystemConfig = toml::from_str(&config_str)?; + Ok(Self { + components: System::load_targets(&config.components), + }) + } + + pub fn load_targets(targets: &Vec) -> Vec { + let mut v = Vec::new(); + for target in targets { + v.push(TasmotaInterface::new(target.clone())); + } + v + } + + pub async fn get_power(&self) -> Result<(), Error> { + for component in &self.components { + if let Ok(res) = component.get_power().await { + component.print(); + println!("* POWER: {}W", res); + println!("------------------") + } + } + Ok(()) + } +} diff --git a/src/tasmota.rs b/src/tasmota.rs index 1ad6f5b..769eaf2 100644 --- a/src/tasmota.rs +++ b/src/tasmota.rs @@ -1,8 +1,7 @@ use reqwest::Client; use serde::Deserialize; -use crate::monitor::Error; -use crate::monitor::Monitoring; +use crate::{control::Control, monitor::Monitoring}; #[derive(Deserialize)] pub struct EnergyData { @@ -69,3 +68,20 @@ impl Monitoring for TasmotaInterface { Ok(data.status.energy.power) } } + +impl Control for TasmotaInterface { + async fn set_power(&self) -> Result<(), Error> { + let res = self + .client + .get(format!( + "http://{}/cm?cmnd=Power%20TOGGLE", + &self.config.target + )) + .send() + .await? + .text() + .await?; + + Ok(()) + } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..889bcb6 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,20 @@ +use clap::Subcommand; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("io error")] + IoError(#[from] std::io::Error), + #[error("toml parsing error")] + ParseError(#[from] toml::de::Error), + #[error("request error")] + RequestError(#[from] reqwest::Error), + #[error("JSON Parse error")] + JsonParseError(#[from] serde_json::Error), +} + +#[derive(Subcommand, Clone)] +pub enum PowerState { + Off, + On, +}