diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b20ce2..a3603a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ file. This change log follows the conventions of ## [Rust Unreleased][Unreleased] +### Changed + +- The implementation of `ApiClient::new` has been improved to allow for the return of `ApiClient` instead of `Result`. +- The default timeout for all requests has been reduced to 30 seconds from 300 seconds. + ## [Python Unreleased][Unreleased] ## [Rust v0.7.9][v0.7.9] - 2024-01-27 diff --git a/tapo-py/src/api_client.rs b/tapo-py/src/api_client.rs index 6bad616..f6dcad4 100644 --- a/tapo-py/src/api_client.rs +++ b/tapo-py/src/api_client.rs @@ -16,7 +16,7 @@ pub struct PyApiClient { impl PyApiClient { #[new] pub fn new(tapo_username: String, tapo_password: String) -> Result { - let client = ApiClient::new(tapo_username, tapo_password)?; + let client = ApiClient::new(tapo_username, tapo_password); Ok(Self { client }) } diff --git a/tapo/examples/tapo_generic_device.rs b/tapo/examples/tapo_generic_device.rs index a3b1dd7..19b585a 100644 --- a/tapo/examples/tapo_generic_device.rs +++ b/tapo/examples/tapo_generic_device.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let device = ApiClient::new(tapo_username, tapo_password)? + let device = ApiClient::new(tapo_username, tapo_password) .generic_device(ip_address) .await?; diff --git a/tapo/examples/tapo_generic_device_toggle.rs b/tapo/examples/tapo_generic_device_toggle.rs index e64e6fd..bb45850 100644 --- a/tapo/examples/tapo_generic_device_toggle.rs +++ b/tapo/examples/tapo_generic_device_toggle.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let device = ApiClient::new(tapo_username, tapo_password)? + let device = ApiClient::new(tapo_username, tapo_password) .generic_device(ip_address) .await?; diff --git a/tapo/examples/tapo_h100.rs b/tapo/examples/tapo_h100.rs index 47fe529..49f9321 100644 --- a/tapo/examples/tapo_h100.rs +++ b/tapo/examples/tapo_h100.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let hub = ApiClient::new(tapo_username, tapo_password)? + let hub = ApiClient::new(tapo_username, tapo_password) .h100(ip_address) .await?; diff --git a/tapo/examples/tapo_ke100.rs b/tapo/examples/tapo_ke100.rs index edeba1b..f9306a3 100644 --- a/tapo/examples/tapo_ke100.rs +++ b/tapo/examples/tapo_ke100.rs @@ -24,7 +24,7 @@ async fn main() -> Result<(), Box> { let device_id = env::var("DEVICE_ID")?; let target_temperature: u8 = env::var("TARGET_TEMPERATURE")?.parse()?; - let hub = ApiClient::new(tapo_username, tapo_password)? + let hub = ApiClient::new(tapo_username, tapo_password) .h100(ip_address) .await?; diff --git a/tapo/examples/tapo_l510.rs b/tapo/examples/tapo_l510.rs index 8fdb2da..30e2529 100644 --- a/tapo/examples/tapo_l510.rs +++ b/tapo/examples/tapo_l510.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let device = ApiClient::new(tapo_username, tapo_password)? + let device = ApiClient::new(tapo_username, tapo_password) .l510(ip_address) .await?; diff --git a/tapo/examples/tapo_l530.rs b/tapo/examples/tapo_l530.rs index c6e52ed..338ac70 100644 --- a/tapo/examples/tapo_l530.rs +++ b/tapo/examples/tapo_l530.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let device = ApiClient::new(tapo_username, tapo_password)? + let device = ApiClient::new(tapo_username, tapo_password) .l530(ip_address) .await?; diff --git a/tapo/examples/tapo_l930.rs b/tapo/examples/tapo_l930.rs index 4e77c6e..96d201b 100644 --- a/tapo/examples/tapo_l930.rs +++ b/tapo/examples/tapo_l930.rs @@ -22,7 +22,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let device = ApiClient::new(tapo_username, tapo_password)? + let device = ApiClient::new(tapo_username, tapo_password) .l930(ip_address) .await?; diff --git a/tapo/examples/tapo_p100.rs b/tapo/examples/tapo_p100.rs index e331597..fc6b7a3 100644 --- a/tapo/examples/tapo_p100.rs +++ b/tapo/examples/tapo_p100.rs @@ -19,7 +19,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let device = ApiClient::new(tapo_username, tapo_password)? + let device = ApiClient::new(tapo_username, tapo_password) .p100(ip_address) .await?; diff --git a/tapo/examples/tapo_p110.rs b/tapo/examples/tapo_p110.rs index 85faf50..cc35451 100644 --- a/tapo/examples/tapo_p110.rs +++ b/tapo/examples/tapo_p110.rs @@ -20,7 +20,7 @@ async fn main() -> Result<(), Box> { let tapo_password = env::var("TAPO_PASSWORD")?; let ip_address = env::var("IP_ADDRESS")?; - let device = ApiClient::new(tapo_username, tapo_password)? + let device = ApiClient::new(tapo_username, tapo_password) .p110(ip_address) .await?; diff --git a/tapo/src/api/api_client.rs b/tapo/src/api/api_client.rs index 63017e5..387bd36 100644 --- a/tapo/src/api/api_client.rs +++ b/tapo/src/api/api_client.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::time::Duration; use async_trait::async_trait; use isahc::prelude::Configurable; @@ -39,7 +40,7 @@ pub trait ApiClientExt: std::fmt::Debug + Send + Sync { /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { -/// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? +/// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l530("192.168.1.100") /// .await?; /// @@ -50,7 +51,10 @@ pub trait ApiClientExt: std::fmt::Debug + Send + Sync { /// ``` #[derive(Debug, Clone)] pub struct ApiClient { - protocol: TapoProtocol, + tapo_username: String, + tapo_password: String, + timeout: Option, + protocol: Option, } /// Tapo API Client constructor. @@ -63,14 +67,13 @@ impl ApiClient { /// /// * `tapo_username` - the Tapo username /// * `tapo_password` - the Tapo password - pub fn new( - tapo_username: impl Into, - tapo_password: impl Into, - ) -> Result { - let client = HttpClient::builder().title_case_headers(true).build()?; - Ok(Self { - protocol: TapoProtocol::new(client, tapo_username.into(), tapo_password.into()), - }) + pub fn new(tapo_username: impl Into, tapo_password: impl Into) -> ApiClient { + Self { + tapo_username: tapo_username.into(), + tapo_password: tapo_password.into(), + timeout: None, + protocol: None, + } } } @@ -88,7 +91,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .generic_device("192.168.1.100") /// .await?; /// device.on().await?; @@ -116,7 +119,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l510("192.168.1.100") /// .await?; /// device.on().await?; @@ -141,7 +144,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l520("192.168.1.100") /// .await?; /// device.on().await?; @@ -166,7 +169,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l530("192.168.1.100") /// .await?; /// device.on().await?; @@ -191,7 +194,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l610("192.168.1.100") /// .await?; /// device.on().await?; @@ -216,7 +219,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l630("192.168.1.100") /// .await?; /// device.on().await?; @@ -241,7 +244,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l900("192.168.1.100") /// .await?; /// device.on().await?; @@ -266,7 +269,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l920("192.168.1.100") /// .await?; /// device.on().await?; @@ -294,7 +297,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .l930("192.168.1.100") /// .await?; /// device.on().await?; @@ -322,7 +325,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .p100("192.168.1.100") /// .await?; /// device.on().await?; @@ -347,7 +350,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .p105("192.168.1.100") /// .await?; /// device.on().await?; @@ -372,7 +375,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .p110("192.168.1.100") /// .await?; /// device.on().await?; @@ -400,7 +403,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .p115("192.168.1.100") /// .await?; /// device.on().await?; @@ -428,7 +431,7 @@ impl ApiClient { /// # use tapo::ApiClient; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// @@ -450,18 +453,28 @@ impl ApiClient { let url = format!("http://{}/app", ip_address.into()); debug!("Device url: {url}"); - self.protocol.login(url).await + let tapo_username = self.tapo_username.clone(); + let tapo_password = self.tapo_password.clone(); + + self.get_protocol_mut()? + .login(url, tapo_username, tapo_password) + .await } pub(crate) async fn refresh_session(&mut self) -> Result<(), Error> { - self.protocol.refresh_session().await + let tapo_username = self.tapo_username.clone(); + let tapo_password = self.tapo_password.clone(); + + self.get_protocol_mut()? + .refresh_session(tapo_username, tapo_password) + .await } pub(crate) async fn device_reset(&self) -> Result<(), Error> { debug!("Device reset..."); let request = TapoRequest::DeviceReset(TapoParams::new(EmptyParams)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await?; @@ -475,7 +488,7 @@ impl ApiClient { debug!("Get Device info..."); let request = TapoRequest::GetDeviceInfo(TapoParams::new(EmptyParams)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await? .map(|result| result.decode()) @@ -489,7 +502,7 @@ impl ApiClient { debug!("Get Device usage..."); let request = TapoRequest::GetDeviceUsage(TapoParams::new(EmptyParams)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await? .ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult)) @@ -507,7 +520,7 @@ impl ApiClient { .set_terminal_uuid(TERMINAL_UUID), )); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await?; @@ -518,7 +531,7 @@ impl ApiClient { debug!("Get Energy usage..."); let request = TapoRequest::GetEnergyUsage(TapoParams::new(EmptyParams)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await? .ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult)) @@ -532,7 +545,7 @@ impl ApiClient { let params = GetEnergyDataParams::new(interval); let request = TapoRequest::GetEnergyData(TapoParams::new(params)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await? .ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult)) @@ -542,7 +555,7 @@ impl ApiClient { debug!("Get Current power..."); let request = TapoRequest::GetCurrentPower(TapoParams::new(EmptyParams)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await? .ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult)) @@ -555,7 +568,7 @@ impl ApiClient { debug!("Get Child device list..."); let request = TapoRequest::GetChildDeviceList(TapoParams::new(EmptyParams)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await? .map(|result| result.decode()) @@ -569,7 +582,7 @@ impl ApiClient { debug!("Get Child device component list..."); let request = TapoRequest::GetChildDeviceComponentList(TapoParams::new(EmptyParams)); - self.protocol + self.get_protocol()? .execute_request::(request, true) .await? .map(|result| result.decode()) @@ -592,7 +605,7 @@ impl ApiClient { let request = TapoRequest::ControlChild(Box::new(TapoParams::new(params))); let responses = self - .protocol + .get_protocol()? .execute_request::>>(request, true) .await? .ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))? @@ -609,6 +622,33 @@ impl ApiClient { Ok(response.result) } + + fn get_protocol_mut(&mut self) -> Result<&mut TapoProtocol, Error> { + if self.protocol.is_none() { + let timeout = self.timeout.unwrap_or_else(|| Duration::from_secs(30)); + + let client = HttpClient::builder() + .title_case_headers(true) + .timeout(timeout) + .build()?; + let protocol = TapoProtocol::new(client); + self.protocol.replace(protocol); + } + + self.protocol.as_mut().ok_or_else(|| { + Error::Other(anyhow::anyhow!( + "The protocol should have been initialized already." + )) + }) + } + + fn get_protocol(&self) -> Result<&TapoProtocol, Error> { + self.protocol.as_ref().ok_or_else(|| { + Error::Other(anyhow::anyhow!( + "The protocol should have been initialized already." + )) + }) + } } #[async_trait] @@ -622,7 +662,7 @@ impl ApiClientExt for ApiClient { .set_terminal_uuid(TERMINAL_UUID), )); - self.protocol + self.get_protocol()? .execute_request::(set_device_info_request, true) .await?; diff --git a/tapo/src/api/color_light_handler.rs b/tapo/src/api/color_light_handler.rs index dceba7f..299a2b0 100644 --- a/tapo/src/api/color_light_handler.rs +++ b/tapo/src/api/color_light_handler.rs @@ -70,7 +70,7 @@ impl ColorLightHandler { /// # use tapo::requests::Color; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// # let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// # let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// # .l530("192.168.1.100") /// # .await?; /// device diff --git a/tapo/src/api/color_light_strip_handler.rs b/tapo/src/api/color_light_strip_handler.rs index 647a0d4..1272390 100644 --- a/tapo/src/api/color_light_strip_handler.rs +++ b/tapo/src/api/color_light_strip_handler.rs @@ -71,7 +71,7 @@ impl ColorLightStripHandler { /// # use tapo::requests::Color; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// # let device = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// # let device = ApiClient::new("tapo-username@example.com", "tapo-password") /// # .l930("192.168.1.100") /// # .await?; /// device diff --git a/tapo/src/api/hub_handler.rs b/tapo/src/api/hub_handler.rs index 1536dc9..57903b7 100644 --- a/tapo/src/api/hub_handler.rs +++ b/tapo/src/api/hub_handler.rs @@ -90,7 +90,7 @@ impl HubHandler { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// // Connect to the hub - /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// // Get a handler for the child device @@ -117,7 +117,7 @@ impl HubHandler { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// // Connect to the hub - /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// // Get a handler for the child device @@ -144,7 +144,7 @@ impl HubHandler { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// // Connect to the hub - /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// // Get a handler for the child device @@ -171,7 +171,7 @@ impl HubHandler { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// // Connect to the hub - /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// // Get a handler for the child device @@ -198,7 +198,7 @@ impl HubHandler { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// // Connect to the hub - /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// // Get a handler for the child device @@ -225,7 +225,7 @@ impl HubHandler { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// // Connect to the hub - /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// // Get a handler for the child device @@ -252,7 +252,7 @@ impl HubHandler { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// // Connect to the hub - /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password")? + /// let hub = ApiClient::new("tapo-username@example.com", "tapo-password") /// .h100("192.168.1.100") /// .await?; /// // Get a handler for the child device diff --git a/tapo/src/api/protocol/discovery_protocol.rs b/tapo/src/api/protocol/discovery_protocol.rs index 08276fa..02e622e 100644 --- a/tapo/src/api/protocol/discovery_protocol.rs +++ b/tapo/src/api/protocol/discovery_protocol.rs @@ -11,17 +11,11 @@ use super::{passthrough_protocol::PassthroughProtocol, TapoProtocolType}; #[derive(Debug, Clone)] pub(crate) struct DiscoveryProtocol { client: HttpClient, - username: String, - password: String, } impl DiscoveryProtocol { - pub fn new(client: HttpClient, username: String, password: String) -> Self { - Self { - client, - username, - password, - } + pub fn new(client: HttpClient) -> Self { + Self { client } } pub async fn discover(&mut self, url: &str) -> Result { @@ -30,15 +24,11 @@ impl DiscoveryProtocol { debug!("Supported. Setting up the Passthrough protocol..."); Ok(TapoProtocolType::Passthrough(PassthroughProtocol::new( self.client.clone(), - self.username.clone(), - self.password.clone(), )?)) } else { debug!("Not supported. Setting up the Klap protocol..."); Ok(TapoProtocolType::Klap(KlapProtocol::new( self.client.clone(), - self.username.clone(), - self.password.clone(), ))) } } diff --git a/tapo/src/api/protocol/klap_protocol.rs b/tapo/src/api/protocol/klap_protocol.rs index d8771aa..3073ce9 100644 --- a/tapo/src/api/protocol/klap_protocol.rs +++ b/tapo/src/api/protocol/klap_protocol.rs @@ -21,8 +21,6 @@ use super::TapoProtocolExt; pub(crate) struct KlapProtocol { client: HttpClient, cookie_jar: CookieJar, - username: String, - password: String, rng: StdRng, url: Option, cipher: Option, @@ -30,14 +28,19 @@ pub(crate) struct KlapProtocol { #[async_trait] impl TapoProtocolExt for KlapProtocol { - async fn login(&mut self, url: String) -> Result<(), Error> { - self.handshake(url).await?; + async fn login( + &mut self, + url: String, + username: String, + password: String, + ) -> Result<(), Error> { + self.handshake(url, username, password).await?; Ok(()) } - async fn refresh_session(&mut self) -> Result<(), Error> { + async fn refresh_session(&mut self, username: String, password: String) -> Result<(), Error> { let url = self.url.as_ref().expect("This should never happen").clone(); - self.handshake(url).await?; + self.handshake(url, username, password).await?; Ok(()) } @@ -92,34 +95,33 @@ impl TapoProtocolExt for KlapProtocol { } fn clone_as_discovery(&self) -> DiscoveryProtocol { - DiscoveryProtocol::new( - self.client.clone(), - self.username.clone(), - self.password.clone(), - ) + DiscoveryProtocol::new(self.client.clone()) } } impl KlapProtocol { - pub fn new(client: HttpClient, username: String, password: String) -> Self { + pub fn new(client: HttpClient) -> Self { Self { client, cookie_jar: CookieJar::new(), - username, - password, rng: StdRng::from_entropy(), url: None, cipher: None, } } - async fn handshake(&mut self, url: String) -> Result<(), Error> { + async fn handshake( + &mut self, + url: String, + username: String, + password: String, + ) -> Result<(), Error> { self.cookie_jar.clear(); let auth_hash = KlapCipher::sha256( &[ - KlapCipher::sha1(self.username.as_bytes()), - KlapCipher::sha1(self.password.as_bytes()), + KlapCipher::sha1(username.as_bytes()), + KlapCipher::sha1(password.as_bytes()), ] .concat(), ) diff --git a/tapo/src/api/protocol/passthrough_protocol.rs b/tapo/src/api/protocol/passthrough_protocol.rs index 1969a95..660f5e2 100644 --- a/tapo/src/api/protocol/passthrough_protocol.rs +++ b/tapo/src/api/protocol/passthrough_protocol.rs @@ -24,8 +24,6 @@ use super::tapo_protocol::TapoProtocolExt; #[derive(Debug)] pub(crate) struct PassthroughProtocol { client: HttpClient, - username: String, - password: String, key_pair: PassthroughKeyPair, session: Option, } @@ -40,16 +38,21 @@ struct Session { #[async_trait] impl TapoProtocolExt for PassthroughProtocol { - async fn login(&mut self, url: String) -> Result<(), Error> { + async fn login( + &mut self, + url: String, + username: String, + password: String, + ) -> Result<(), Error> { self.handshake(url).await?; - self.login_request().await?; + self.login_request(username, password).await?; Ok(()) } - async fn refresh_session(&mut self) -> Result<(), Error> { + async fn refresh_session(&mut self, username: String, password: String) -> Result<(), Error> { let url = self.get_session_ref().url.clone(); - self.login(url).await + self.login(url, username, password).await } async fn execute_request( @@ -117,23 +120,14 @@ impl TapoProtocolExt for PassthroughProtocol { } fn clone_as_discovery(&self) -> DiscoveryProtocol { - DiscoveryProtocol::new( - self.client.clone(), - self.username.clone(), - self.password.clone(), - ) + DiscoveryProtocol::new(self.client.clone()) } } impl PassthroughProtocol { - pub fn new(client: HttpClient, username: String, password: String) -> Result { - let username_digest = PassthroughCipher::sha1_digest_username(username); - debug!("Username digest: {username_digest}"); - + pub fn new(client: HttpClient) -> Result { Ok(Self { client, - username: general_purpose::STANDARD.encode(username_digest), - password: general_purpose::STANDARD.encode(password), key_pair: PassthroughKeyPair::new()?, session: None, }) @@ -177,10 +171,16 @@ impl PassthroughProtocol { Ok(()) } - async fn login_request(&mut self) -> Result<(), Error> { - debug!("Will login with username '{}'...", self.username); + async fn login_request(&mut self, username: String, password: String) -> Result<(), Error> { + let username_digest = PassthroughCipher::sha1_digest_username(username); + debug!("Username digest: {username_digest}"); + + let username = general_purpose::STANDARD.encode(username_digest); + let password = general_purpose::STANDARD.encode(password); + + debug!("Will login with username '{}'...", username); - let params = TapoParams::new(LoginDeviceParams::new(&self.username, &self.password)) + let params = TapoParams::new(LoginDeviceParams::new(&username, &password)) .set_request_time_mils()?; let request = TapoRequest::LoginDevice(params); diff --git a/tapo/src/api/protocol/tapo_protocol.rs b/tapo/src/api/protocol/tapo_protocol.rs index 25e7e7c..4cbe1d3 100644 --- a/tapo/src/api/protocol/tapo_protocol.rs +++ b/tapo/src/api/protocol/tapo_protocol.rs @@ -20,8 +20,9 @@ pub(crate) struct TapoProtocol { #[async_trait] pub(crate) trait TapoProtocolExt { - async fn login(&mut self, url: String) -> Result<(), Error>; - async fn refresh_session(&mut self) -> Result<(), Error>; + async fn login(&mut self, url: String, username: String, password: String) + -> Result<(), Error>; + async fn refresh_session(&mut self, username: String, password: String) -> Result<(), Error>; async fn execute_request( &self, request: TapoRequest, @@ -51,22 +52,31 @@ impl Clone for TapoProtocolType { #[async_trait] impl TapoProtocolExt for TapoProtocol { - async fn login(&mut self, url: String) -> Result<(), Error> { + async fn login( + &mut self, + url: String, + username: String, + password: String, + ) -> Result<(), Error> { if let TapoProtocolType::Discovery(protocol) = &mut self.protocol { self.protocol = protocol.discover(&url).await?; } match &mut self.protocol { - TapoProtocolType::Passthrough(protocol) => protocol.login(url).await, - TapoProtocolType::Klap(protocol) => protocol.login(url).await, + TapoProtocolType::Passthrough(protocol) => { + protocol.login(url, username, password).await + } + TapoProtocolType::Klap(protocol) => protocol.login(url, username, password).await, _ => Err(anyhow::anyhow!("The protocol discovery should have happened already").into()), } } - async fn refresh_session(&mut self) -> Result<(), Error> { + async fn refresh_session(&mut self, username: String, password: String) -> Result<(), Error> { match &mut self.protocol { - TapoProtocolType::Passthrough(protocol) => protocol.refresh_session().await, - TapoProtocolType::Klap(protocol) => protocol.refresh_session().await, + TapoProtocolType::Passthrough(protocol) => { + protocol.refresh_session(username, password).await + } + TapoProtocolType::Klap(protocol) => protocol.refresh_session(username, password).await, _ => Err(anyhow::anyhow!("The protocol discovery should have happened already").into()), } } @@ -98,11 +108,9 @@ impl TapoProtocolExt for TapoProtocol { } impl TapoProtocol { - pub fn new(client: HttpClient, username: String, password: String) -> Self { + pub fn new(client: HttpClient) -> Self { Self { - protocol: TapoProtocolType::Discovery(DiscoveryProtocol::new( - client, username, password, - )), + protocol: TapoProtocolType::Discovery(DiscoveryProtocol::new(client)), } } } diff --git a/tapo/src/lib.rs b/tapo/src/lib.rs index 5732bfd..9099ffe 100644 --- a/tapo/src/lib.rs +++ b/tapo/src/lib.rs @@ -17,7 +17,7 @@ //! let tapo_password = env::var("TAPO_PASSWORD")?; //! let ip_address = env::var("IP_ADDRESS")?; //! -//! let device = ApiClient::new(tapo_username, tapo_password)? +//! let device = ApiClient::new(tapo_username, tapo_password) //! .l530(ip_address) //! .await?; //!