diff --git a/.gitignore b/.gitignore index ea8c4bf..0a28673 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +dev-config.toml diff --git a/Cargo.lock b/Cargo.lock index e789fb0..6f6ed33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,6 +674,16 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.27" @@ -788,6 +798,29 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -819,7 +852,9 @@ dependencies = [ "clap", "reqwest", "serde", + "serde_json", "thiserror", + "tokio", "toml", ] @@ -847,6 +882,15 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + [[package]] name = "reqwest" version = "0.12.15" @@ -984,6 +1028,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" version = "2.11.1" @@ -1066,6 +1116,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -1214,11 +1273,25 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 607ae1a..bf23c38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,7 @@ edition = "2024" clap = { version = "4.5.35", features = ["derive"] } reqwest = "0.12.15" serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" thiserror = "2.0.12" +tokio = { version = "1.44.1", features = ["full"] } toml = "0.8.20" diff --git a/config.toml b/config.toml index 9ac6053..43620fe 100644 --- a/config.toml +++ b/config.toml @@ -1,5 +1,3 @@ target = [ - "target 1", - "target 2", - "target 3", + "YOUR IP HERE" ] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 49d5c1d..73f1b28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use clap::{Parser, Subcommand}; use monitor::Monitor; mod monitor; +mod tasmota; #[derive(Subcommand)] pub enum Commands { @@ -9,14 +10,17 @@ pub enum Commands { } impl Commands { - pub fn execute(self, config_file: &str) { - match self { + pub async fn execute(self, config_file: &str) { + let handle = match self { Self::Monitor => { - let _m = Monitor::new_from_file(config_file).unwrap(); - - println!("[TODO] Power: ----W") + let m = Monitor::new_from_file(config_file).unwrap(); + tokio::spawn(async move { + m.get_power().await.unwrap(); + }) + // println!("[TODO] Power: ----W") } - } + }; + handle.await.unwrap(); } } @@ -29,12 +33,13 @@ pub struct Cli { } impl Cli { - pub fn execute(self) { - self.command.execute(&self.config_file); + pub async fn execute(self) { + self.command.execute(&self.config_file).await; } } -fn main() { +#[tokio::main] +async fn main() { let cli = Cli::parse(); - cli.execute(); + cli.execute().await; } diff --git a/src/monitor.rs b/src/monitor.rs index 203fc20..068bdc3 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -1,3 +1,5 @@ +use crate::tasmota::{PowerStatusData, StatusResponse}; +use reqwest::Client; use serde::Deserialize; use std::fs; use thiserror::Error; @@ -8,6 +10,10 @@ pub enum 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(Deserialize)] @@ -22,13 +28,35 @@ impl MonitorConfig { } } -pub struct Monitor {} +pub struct Monitor { + config: MonitorConfig, + 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)?; - config.print(); - Ok(Self {}) + // config.print(); + Ok(Self { + config, + client: Client::new(), + }) + } + + pub async fn get_power(&self) -> Result<(), Error> { + let ip = &self.config.target[0]; + let res = self + .client + .get(format!("http://{ip}/cm?cmnd=Status%208")) + .send() + .await? + .text() + .await?; + + // println!("body = {res:?}"); + let data: StatusResponse = serde_json::from_str(&res)?; + println!("POWER: {}W", data.status.energy.power); + Ok(()) } } diff --git a/src/tasmota.rs b/src/tasmota.rs new file mode 100644 index 0000000..6cd1f7b --- /dev/null +++ b/src/tasmota.rs @@ -0,0 +1,21 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct EnergyData { + #[serde(rename = "Power")] + pub power: isize, +} + +#[derive(Deserialize)] +pub struct PowerStatusData { + // #[serde(rename = "Time")] + // pub time: String, + #[serde(rename = "ENERGY")] + pub energy: EnergyData, +} + +#[derive(Deserialize)] +pub struct StatusResponse { + #[serde(rename = "StatusSNS")] + pub status: PowerStatusData, +}