From 503b73c14c5204b136ddc251d8b8c822675aa01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 19:59:27 +0200 Subject: [PATCH 1/8] Bump edition, add Cargo.lock, bump dependencies --- .gitignore | 1 - Cargo.lock | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 +- src/lib.rs | 18 +++++------ 4 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 593c177..f074ecf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target/ /example/target **/*.rs.bk -Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..972cfbd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,94 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "hs100api" +version = "0.1.1" +dependencies = [ + "byteorder", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" diff --git a/Cargo.toml b/Cargo.toml index f004d3f..1edfc97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "hs100api" version = "0.1.1" +edition = "2021" authors = ["Alexandre Beslic "] description = "A library to manipulate and get data from a TP-Link HS-100/110 smart plugs" repository = "https://github.com/abronan/hs100-rust-api" @@ -11,7 +12,7 @@ exclude = [ ] [dependencies] -byteorder = "1.0" +byteorder = "1.4" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" diff --git a/src/lib.rs b/src/lib.rs index a50004f..c2f7f50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,12 +59,12 @@ impl SmartPlug { } fn submit_to_device(&self, msg: &str) -> Result { - let msg = try!(encrypt(msg)); - let mut resp = try!(send(self.ip, &msg)); + let msg = encrypt(msg)?; + let mut resp = send(self.ip, &msg)?; let data = decrypt(&mut resp.split_off(4)); // deserialize json - let resp = try!(serde_json::from_str(&data)); + let resp = serde_json::from_str(&data)?; Ok(resp) } @@ -76,7 +76,7 @@ fn encrypt(plain: &str) -> Result, Error> { let len = plain.len(); let msgbytes = plain.as_bytes(); let mut cipher = vec![]; - try!(cipher.write_u32::(len as u32)); + cipher.write_u32::(len as u32)?; let mut key = 0xAB; let mut payload: Vec = Vec::with_capacity(len); @@ -87,7 +87,7 @@ fn encrypt(plain: &str) -> Result, Error> { } for i in &payload { - try!(cipher.write_u8(*i)); + cipher.write_u8(*i)?; } Ok(cipher) @@ -112,13 +112,13 @@ fn decrypt(cipher: &mut [u8]) -> String { // Sends a message to the device and awaits a response synchronously fn send(ip: &str, payload: &[u8]) -> Result, Error> { - let mut stream = try!(TcpStream::connect(ip)); + let mut stream = TcpStream::connect(ip)?; - try!(stream.set_read_timeout(Some(Duration::new(5, 0)))); - try!(stream.write_all(payload)); + stream.set_read_timeout(Some(Duration::new(5, 0)))?; + stream.write_all(payload)?; let mut resp = vec![]; - try!(stream.read_to_end(&mut resp)); + stream.read_to_end(&mut resp)?; Ok(resp) } From 84556754437016df0a2c13355fdc68e4fc3754dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 20:01:30 +0200 Subject: [PATCH 2/8] Clippy lint fixes --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index c2f7f50..11c0424 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ pub struct SmartPlug { impl SmartPlug { pub fn new(ip: &'static str) -> SmartPlug { - SmartPlug { ip: ip } + SmartPlug { ip } } // Wakes up the device @@ -95,6 +95,7 @@ fn encrypt(plain: &str) -> Result, Error> { // Decrypt received string // see: https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/ +#[allow(clippy::needless_range_loop)] fn decrypt(cipher: &mut [u8]) -> String { let len = cipher.len(); From 7f527f4ee9176bef308bc8b1da6616a686bd0775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 20:03:24 +0200 Subject: [PATCH 3/8] Convert example into proper cargo example --- Cargo.toml | 3 +++ example/Cargo.toml | 7 ------- example/src/main.rs => examples/example.rs | 0 3 files changed, 3 insertions(+), 7 deletions(-) delete mode 100644 example/Cargo.toml rename example/src/main.rs => examples/example.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 1edfc97..274785b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,6 @@ byteorder = "1.4" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" + +[[example]] +name = "example" diff --git a/example/Cargo.toml b/example/Cargo.toml deleted file mode 100644 index d2d4ded..0000000 --- a/example/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "example" -version = "0.1.0" -authors = ["Alexandre Beslic "] - -[dependencies.hs100api] -path = ".." \ No newline at end of file diff --git a/example/src/main.rs b/examples/example.rs similarity index 100% rename from example/src/main.rs rename to examples/example.rs From 0ce8aa55af1d42f462c18b9580d58d140ebd289d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 20:03:52 +0200 Subject: [PATCH 4/8] Clippy lint fixes for example --- examples/example.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example.rs b/examples/example.rs index 8dfbab3..4d5653a 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -3,7 +3,7 @@ extern crate hs100api; use hs100api::SmartPlug; use hs100api::error::Error; -const HOST: &'static str = "192.168.0.37:9999"; +const HOST: &str = "192.168.0.37:9999"; fn main() { let api = SmartPlug::new(HOST); From c61c1307d2e681e2f58c8c39d6a8263b41e77cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 20:04:27 +0200 Subject: [PATCH 5/8] cargo fmt --- examples/example.rs | 14 ++++++-------- src/error.rs | 8 ++++---- src/lib.rs | 13 ++++++------- src/types.rs | 2 +- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index 4d5653a..a9eb0dc 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,7 +1,7 @@ extern crate hs100api; -use hs100api::SmartPlug; use hs100api::error::Error; +use hs100api::SmartPlug; const HOST: &str = "192.168.0.37:9999"; @@ -22,13 +22,11 @@ fn main() { // Handle specific error types match api.sysinfo() { Ok(info) => println!("[sysinfo]: {:?}\n", info), - Err(err) => { - match err { - Error::IoError(_) => println!("some io error occurred"), - Error::EncryptError => println!("error encrypting the message"), - Error::DeserializeError(_) => println!("couldn't deserialize the message"), - } - } + Err(err) => match err { + Error::IoError(_) => println!("some io error occurred"), + Error::EncryptError => println!("error encrypting the message"), + Error::DeserializeError(_) => println!("couldn't deserialize the message"), + }, } // Print specific property, it is "safe" to unwrap as long as: diff --git a/src/error.rs b/src/error.rs index 5250c68..11c5fb6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,8 @@ -use std::fmt; -use std::error::Error as StdError; -use std::io::Error as IoError; use serde_json::Error as JsonError; use std::convert::From; +use std::error::Error as StdError; +use std::fmt; +use std::io::Error as IoError; #[derive(Debug)] pub enum Error { @@ -43,4 +43,4 @@ impl From for Error { fn from(error: JsonError) -> Self { Error::DeserializeError(error) } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 11c0424..510fd30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,17 +3,17 @@ extern crate byteorder; #[macro_use] extern crate serde_derive; extern crate serde_json; +use byteorder::{BigEndian, WriteBytesExt}; use std::io::prelude::*; use std::net::TcpStream; -use byteorder::{BigEndian, WriteBytesExt}; -use std::time::Duration; use std::str; +use std::time::Duration; -pub mod types; pub mod error; +pub mod types; -use types::*; use error::*; +use types::*; pub struct SmartPlug { ip: &'static str, @@ -52,8 +52,7 @@ impl SmartPlug { pub fn dailystats(&self, month: i32, year: i32) -> Result { let json = format!( "{{\"emeter\":{{\"get_daystat\":{{\"month\":{},\"year\":{}}}}}}}", - month, - year + month, year ); self.submit_to_device(&json) } @@ -126,8 +125,8 @@ fn send(ip: &str, payload: &[u8]) -> Result, Error> { #[cfg(test)] mod tests { - use encrypt; use decrypt; + use encrypt; #[test] fn encrypt_decrypt() { diff --git a/src/types.rs b/src/types.rs index 4c00ea8..1476ad0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -75,4 +75,4 @@ pub struct EmeterGetDaystat { pub struct PlugInfo { pub system: Option, pub emeter: Option, -} \ No newline at end of file +} From 06e06e2462d9c69af552f6e3ddcdf4a5d5be9b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 20:08:15 +0200 Subject: [PATCH 6/8] remove unnessecary extern crates --- examples/example.rs | 2 -- src/lib.rs | 3 --- 2 files changed, 5 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index a9eb0dc..770343c 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,5 +1,3 @@ -extern crate hs100api; - use hs100api::error::Error; use hs100api::SmartPlug; diff --git a/src/lib.rs b/src/lib.rs index 510fd30..2175624 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,5 @@ -extern crate byteorder; - #[macro_use] extern crate serde_derive; -extern crate serde_json; use byteorder::{BigEndian, WriteBytesExt}; use std::io::prelude::*; use std::net::TcpStream; From 875d36d2e488ab53b48077c0c30ead60a91b78da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 20:09:11 +0200 Subject: [PATCH 7/8] fix tests --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2175624..a6d5671 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,14 +122,14 @@ fn send(ip: &str, payload: &[u8]) -> Result, Error> { #[cfg(test)] mod tests { - use decrypt; - use encrypt; + use super::decrypt; + use super::encrypt; #[test] fn encrypt_decrypt() { let json = "{\"system\":{\"get_sysinfo\":{}}}"; - let mut data = encrypt(json); + let mut data = encrypt(json).unwrap(); let resp = decrypt(&mut data.split_off(4)); assert_eq!(json, resp); From 929bb4feca1c026666bd84d1478c616f731c3f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Capypara=20K=C3=B6pcke?= Date: Fri, 23 Jun 2023 20:40:46 +0200 Subject: [PATCH 8/8] Support for async --- Cargo.lock | 600 +++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 7 + examples/example.rs | 57 +++++ src/lib.rs | 73 ++++-- 4 files changed, 712 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 972cfbd..8430163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,28 +2,406 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hs100api" version = "0.1.1" dependencies = [ + "async-std", "byteorder", + "maybe-async", "serde", "serde_derive", "serde_json", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + [[package]] name = "itoa" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +dependencies = [ + "value-bag", +] + +[[package]] +name = "maybe-async" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys", +] + [[package]] name = "proc-macro2" version = "1.0.60" @@ -42,6 +420,20 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.13" @@ -62,7 +454,7 @@ checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.18", ] [[package]] @@ -76,6 +468,36 @@ dependencies = [ "serde", ] +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.18" @@ -92,3 +514,179 @@ name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "value-bag" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4d330786735ea358f3bc09eea4caa098569c1c93f342d9aca0514915022fe7e" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml index 274785b..7f82fa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,17 @@ exclude = [ ] [dependencies] +maybe-async = "0.2" byteorder = "1.4" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" +async-std = { version = "1.12", optional = true } + +[features] +default = ["async"] +sync = ["maybe-async/is_sync"] +async = ["async-std"] [[example]] name = "example" diff --git a/examples/example.rs b/examples/example.rs index 770343c..23f4917 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -3,6 +3,8 @@ use hs100api::SmartPlug; const HOST: &str = "192.168.0.37:9999"; +// Sync example +#[cfg(feature = "sync")] fn main() { let api = SmartPlug::new(HOST); @@ -52,3 +54,58 @@ fn main() { // let resp = api.off(); // let resp = api.on(); } + +// Async example +#[cfg(feature = "async")] +fn main() { + async_std::task::block_on(async { + let api = SmartPlug::new(HOST); + + // Quick example: + println!("[sysinfo]: {:?}\n", api.sysinfo().await); + println!("[meterinfo]: {:?}\n", api.meterinfo().await); + println!("[dailystats]: {:?}\n", api.dailystats(7, 2017).await); + + // Using it properly + match api.sysinfo().await { + Ok(info) => println!("[sysinfo]: {:?}\n", info), + Err(err) => println!("{}\n", err), + } + + // Handle specific error types + match api.sysinfo().await { + Ok(info) => println!("[sysinfo]: {:?}\n", info), + Err(err) => match err { + Error::IoError(_) => println!("some io error occurred"), + Error::EncryptError => println!("error encrypting the message"), + Error::DeserializeError(_) => println!("couldn't deserialize the message"), + }, + } + + // Print specific property, it is "safe" to unwrap as long as: + // - for meterinfo() -> get_realtime + // - for dailystats() -> get_daystat + // - otherwise -> use match to handle if Option result is `Some` or `None` + // + // note: this is just an example, this will panic on unwrap() if meterinfo() + // returns an error, use proper error handling as shown above. + println!( + "[watt]: {}", + api.meterinfo() + .await + .unwrap() + .emeter + .unwrap() + .get_realtime + .unwrap() + .current + ); + + // + // Avoid these if the HS100 is plugged to your computer :) + // + + // let resp = api.off(); + // let resp = api.on(); + }); +} diff --git a/src/lib.rs b/src/lib.rs index a6d5671..e669c25 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,12 @@ #[macro_use] extern crate serde_derive; +#[cfg(feature = "async")] +use async_std::{net::TcpStream as AsyncTcpStream, prelude::*}; use byteorder::{BigEndian, WriteBytesExt}; -use std::io::prelude::*; -use std::net::TcpStream; use std::str; use std::time::Duration; +#[cfg(feature = "sync")] +use std::{io::prelude::*, net::TcpStream}; pub mod error; pub mod types; @@ -21,42 +23,48 @@ impl SmartPlug { SmartPlug { ip } } - // Wakes up the device - pub fn on(&self) -> Result { + /// Wakes up the device + #[maybe_async::maybe_async] + pub async fn on(&self) -> Result { let json = "{\"system\":{\"set_relay_state\":{\"state\":1}}}"; - self.submit_to_device(json) + self.submit_to_device(json).await } - // Turns off the device - pub fn off(&self) -> Result { + /// Turns off the device + #[maybe_async::maybe_async] + pub async fn off(&self) -> Result { let json = "{\"system\":{\"set_relay_state\":{\"state\":0}}}"; - self.submit_to_device(json) + self.submit_to_device(json).await } - // Gather system wide info such as model of the device, etc. - pub fn sysinfo(&self) -> Result { + /// Gather system wide info such as model of the device, etc. + #[maybe_async::maybe_async] + pub async fn sysinfo(&self) -> Result { let json = "{\"system\":{\"get_sysinfo\":{}}}"; - self.submit_to_device(json) + self.submit_to_device(json).await } - // Gather system information as well as watt meter information - pub fn meterinfo(&self) -> Result { + /// Gather system information as well as watt meter information + #[maybe_async::maybe_async] + pub async fn meterinfo(&self) -> Result { let json = "{\"system\":{\"get_sysinfo\":{}}, \"emeter\":{\"get_realtime\":{},\"get_vgain_igain\":{}}}"; - self.submit_to_device(json) + self.submit_to_device(json).await } - // Returns system information as well as daily statistics of power usage - pub fn dailystats(&self, month: i32, year: i32) -> Result { + /// Returns system information as well as daily statistics of power usage + #[maybe_async::maybe_async] + pub async fn dailystats(&self, month: i32, year: i32) -> Result { let json = format!( "{{\"emeter\":{{\"get_daystat\":{{\"month\":{},\"year\":{}}}}}}}", month, year ); - self.submit_to_device(&json) + self.submit_to_device(&json).await } - fn submit_to_device(&self, msg: &str) -> Result { + #[maybe_async::maybe_async] + async fn submit_to_device(&self, msg: &str) -> Result { let msg = encrypt(msg)?; - let mut resp = send(self.ip, &msg)?; + let mut resp = send(self.ip, &msg).await?; let data = decrypt(&mut resp.split_off(4)); // deserialize json @@ -66,8 +74,8 @@ impl SmartPlug { } } -// Prepare and encrypt message to send to the device -// see: https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/ +/// Prepare and encrypt message to send to the device +/// see: https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/ fn encrypt(plain: &str) -> Result, Error> { let len = plain.len(); let msgbytes = plain.as_bytes(); @@ -89,8 +97,8 @@ fn encrypt(plain: &str) -> Result, Error> { Ok(cipher) } -// Decrypt received string -// see: https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/ +/// Decrypt received string +/// see: https://www.softscheck.com/en/reverse-engineering-tp-link-hs110/ #[allow(clippy::needless_range_loop)] fn decrypt(cipher: &mut [u8]) -> String { let len = cipher.len(); @@ -107,7 +115,8 @@ fn decrypt(cipher: &mut [u8]) -> String { String::from_utf8_lossy(cipher).into_owned() } -// Sends a message to the device and awaits a response synchronously +/// Sends a message to the device and awaits a response synchronously +#[maybe_async::sync_impl] fn send(ip: &str, payload: &[u8]) -> Result, Error> { let mut stream = TcpStream::connect(ip)?; @@ -120,6 +129,22 @@ fn send(ip: &str, payload: &[u8]) -> Result, Error> { Ok(resp) } +/// Sends a message to the device and awaits a response asynchronously +#[maybe_async::async_impl] +async fn send(ip: &str, payload: &[u8]) -> Result, Error> { + let mut stream = AsyncTcpStream::connect(ip).await?; + + let mut resp = vec![]; + async_std::io::timeout(Duration::new(5, 0), async { + stream.write_all(payload).await?; + stream.read_to_end(&mut resp).await?; + Ok(()) + }) + .await?; + + Ok(resp) +} + #[cfg(test)] mod tests { use super::decrypt;