Skip to content

Commit

Permalink
Separate hub and power strip child device list result
Browse files Browse the repository at this point in the history
  • Loading branch information
mihai-dinculescu committed Apr 7, 2024
1 parent 4c92ab7 commit c0bd473
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 120 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ file. This change log follows the conventions of

## [Rust Unreleased][Unreleased]

### Changed

- `ChildDeviceResult` has been renamed to `ChildDeviceHubResult` to facilitate adding support for other devices with children.

## [Python Unreleased][Unreleased]

## [Rust v0.7.10][v0.7.10] - 2024-04-05
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![PyPI][pypi_badge]][pypi]
[![Python][pypi_versions_badge]][pypi]
[![PyPI][pypi_downloads_badge]][pypi]\
Unofficial Tapo API Client. Works with TP-Link Tapo smart devices. Tested with light bulbs (L510, L520, L530, L610, L630), light strips (L900, L920, L930), plugs (P100, P105, P110, P115), hubs (H100), switches (S200B) and sensors (KE100, T100, T110, T300, T310, T315).
Unofficial Tapo API Client. Works with TP-Link Tapo smart devices. Tested with light bulbs (L510, L520, L530, L610, L630), light strips (L900, L920, L930), plugs (P100, P105, P110, P115, P300), hubs (H100), switches (S200B) and sensors (KE100, T100, T110, T300, T310, T315).

[license_badge]: https://img.shields.io/crates/l/tapo.svg
[license]: https://github.com/mihai-dinculescu/tapo/blob/main/LICENSE
Expand Down
14 changes: 7 additions & 7 deletions tapo/examples/tapo_h100.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use std::env;

use log::{info, LevelFilter};
use tapo::{responses::ChildDeviceResult, ApiClient};
use tapo::{responses::ChildDeviceHubResult, ApiClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -31,7 +31,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

for child in child_device_list {
match child {
ChildDeviceResult::KE100(device) => {
ChildDeviceHubResult::KE100(device) => {
info!(
"Found KE100 child device with nickname: {}, id: {}, current temperature: {} {:?} and target temperature: {} {:?}.",
device.nickname,
Expand All @@ -42,7 +42,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
device.temperature_unit,
);
}
ChildDeviceResult::S200B(device) => {
ChildDeviceHubResult::S200B(device) => {
let s200b = hub.s200b(&device.device_id);
let trigger_logs = s200b.get_trigger_logs(5, 0).await?;

Expand All @@ -51,7 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
device.nickname, device.device_id, trigger_logs
);
}
ChildDeviceResult::T100(device) => {
ChildDeviceHubResult::T100(device) => {
let t100 = hub.t100(&device.device_id);
let trigger_logs = t100.get_trigger_logs(5, 0).await?;

Expand All @@ -60,7 +60,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
device.nickname, device.device_id, device.detected, trigger_logs
);
}
ChildDeviceResult::T110(device) => {
ChildDeviceHubResult::T110(device) => {
let t110 = hub.t110(&device.device_id);
let trigger_logs = t110.get_trigger_logs(5, 0).await?;

Expand All @@ -69,7 +69,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
device.nickname, device.device_id, device.open, trigger_logs
);
}
ChildDeviceResult::T300(device) => {
ChildDeviceHubResult::T300(device) => {
let t300 = hub.t300(&device.device_id);
let trigger_logs = t300.get_trigger_logs(5, 0).await?;

Expand All @@ -82,7 +82,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
trigger_logs
);
}
ChildDeviceResult::T310(device) | ChildDeviceResult::T315(device) => {
ChildDeviceHubResult::T310(device) | ChildDeviceHubResult::T315(device) => {
let t31x = hub.t315(&device.device_id);
let temperature_humidity_records = t31x.get_temperature_humidity_records().await?;

Expand Down
44 changes: 16 additions & 28 deletions tapo/examples/tapo_p300.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use std::{env, thread, time::Duration};

use log::{info, LevelFilter};
use tapo::{responses::ChildDeviceResult, ApiClient};
use tapo::ApiClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -19,46 +19,34 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let tapo_password = env::var("TAPO_PASSWORD")?;
let ip_address = env::var("IP_ADDRESS")?;

info!("connecting...");

let strip = ApiClient::new(tapo_username, tapo_password)
let power_strip = ApiClient::new(tapo_username, tapo_password)
.p300(ip_address)
.await?;

let device_info = strip.get_device_info().await?;
let device_info = power_strip.get_device_info().await?;
info!("Device info: {device_info:?}");

info!("Getting child devices...");
let child_device_list = strip.get_child_device_list().await?;
let child_device_list = power_strip.get_child_device_list().await?;

for child in child_device_list {
match child {
ChildDeviceResult::P300(child) => {
info!(
"Found P300 child device with nickname: {}, id: {}, state: {}.",
child.nickname,
child.device_id,
child.device_on,
);

let device = strip.p300(child.device_id);
info!(
"Found plug with nickname: {}, id: {}, state: {}.",
child.nickname, child.device_id, child.device_on,
);

let plug = power_strip.plug(child.device_id);

info!("Turning device on...");
device.on().await?;
info!("Turning device on...");
plug.on().await?;

info!("Waiting 2 seconds...");
thread::sleep(Duration::from_secs(2));
info!("Waiting 2 seconds...");
thread::sleep(Duration::from_secs(2));

info!("Turning device off...");
device.off().await?;
info!("Turning device off...");
plug.off().await?;

info!("Waiting 2 seconds...");
}
_ => {
info!("Found unsupported device.")
}
}
info!("Waiting 2 seconds...");
}

Ok(())
Expand Down
9 changes: 3 additions & 6 deletions tapo/src/api/api_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use serde::de::DeserializeOwned;
use crate::api::protocol::{TapoProtocol, TapoProtocolExt};
use crate::api::{
ColorLightHandler, ColorLightStripHandler, GenericDeviceHandler, HubHandler, LightHandler,
PlugEnergyMonitoringHandler, PlugHandler, PowerStripHandler
PlugEnergyMonitoringHandler, PlugHandler, PowerStripHandler,
};
use crate::error::{Error, TapoResponseError};
use crate::requests::{
Expand Down Expand Up @@ -404,7 +404,7 @@ impl ApiClient {
Ok(PlugEnergyMonitoringHandler::new(self))
}

/// Specializes the given [`ApiClient`] into an authenticated [`PlugEnergyMonitoringHandler`].
/// Specializes the given [`ApiClient`] into an authenticated [`PowerStripHandler`].
///
/// # Arguments
///
Expand All @@ -424,10 +424,7 @@ impl ApiClient {
/// # Ok(())
/// # }
/// ```
pub async fn p300(
mut self,
ip_address: impl Into<String>,
) -> Result<PowerStripHandler, Error> {
pub async fn p300(mut self, ip_address: impl Into<String>) -> Result<PowerStripHandler, Error> {
self.login(ip_address).await?;

Ok(PowerStripHandler::new(self))
Expand Down
4 changes: 2 additions & 2 deletions tapo/src/api/child_devices.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
mod ke100_handler;
mod plug_power_strip_handler;
mod s200b_handler;
mod t100_handler;
mod t110_handler;
mod t300_handler;
mod t31x_handler;
mod p300_child_handler;

pub use ke100_handler::*;
pub use plug_power_strip_handler::*;
pub use s200b_handler::*;
pub use t100_handler::*;
pub use t110_handler::*;
pub use t300_handler::*;
pub use t31x_handler::*;
pub use p300_child_handler::*;
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
use crate::api::PowerStripHandler;
use crate::error::{Error, TapoResponseError};
use crate::requests::{EmptyParams, GenericSetDeviceInfoParams, TapoParams, TapoRequest};
use crate::responses::{DecodableResultExt, P300ChildResult};
use crate::responses::{DecodableResultExt, PlugPowerStripResult};


/// Handler for the [P300](https://www.tapo.com/en/search/?q=T100) child devices.
pub struct P300ChildHandler<'h> {
/// Handler for the [P300](https://www.tapo.com/en/search/?q=P300) child plugs.
pub struct PlugPowerStripHandler<'h> {
power_strip_handler: &'h PowerStripHandler,
device_id: String,
}

impl<'h> P300ChildHandler<'h> {
impl<'h> PlugPowerStripHandler<'h> {
pub(crate) fn new(power_strip_handler: &'h PowerStripHandler, device_id: String) -> Self {
Self {
power_strip_handler,
device_id,
}
}

/// Returns *device info* as [`P300ChildResult`].
/// Returns *device info* as [`PlugPowerStripResult`].
/// It is not guaranteed to contain all the properties returned from the Tapo API.
pub async fn get_device_info(&self) -> Result<P300ChildResult, Error> {
pub async fn get_device_info(&self) -> Result<PlugPowerStripResult, Error> {
let request = TapoRequest::GetDeviceInfo(TapoParams::new(EmptyParams));

self.power_strip_handler
.control_child::<P300ChildResult>(self.device_id.clone(), request)
.control_child::<PlugPowerStripResult>(self.device_id.clone(), request)
.await?
.ok_or_else(|| Error::Tapo(TapoResponseError::EmptyResult))
.map(|result| result.decode())?
Expand All @@ -46,7 +45,6 @@ impl<'h> P300ChildHandler<'h> {
let json = serde_json::to_value(GenericSetDeviceInfoParams::device_on(true)?)?;
let request = TapoRequest::SetDeviceInfo(Box::new(TapoParams::new(json)));


self.power_strip_handler
.control_child::<serde_json::Value>(self.device_id.clone(), request)
.await?;
Expand All @@ -59,7 +57,6 @@ impl<'h> P300ChildHandler<'h> {
let json = serde_json::to_value(GenericSetDeviceInfoParams::device_on(false)?)?;
let request = TapoRequest::SetDeviceInfo(Box::new(TapoParams::new(json)));


self.power_strip_handler
.control_child::<serde_json::Value>(self.device_id.clone(), request)
.await?;
Expand Down
8 changes: 4 additions & 4 deletions tapo/src/api/hub_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::api::{KE100Handler, S200BHandler, T100Handler, T110Handler, T300Handl
use crate::error::Error;
use crate::requests::TapoRequest;
use crate::responses::{
ChildDeviceListResult, ChildDeviceResult, DeviceInfoHubResult, TapoResponseExt,
ChildDeviceHubResult, ChildDeviceListHubResult, DeviceInfoHubResult, TapoResponseExt,
};

/// Handler for the [H100](https://www.tapo.com/en/search/?q=H100) hubs.
Expand Down Expand Up @@ -40,12 +40,12 @@ impl HubHandler {
self.client.get_device_info().await
}

/// Returns *child device list* as [`ChildDeviceListResult`].
/// Returns *child device list* as [`ChildDeviceHubResult`].
/// It is not guaranteed to contain all the properties returned from the Tapo API
/// or to support all the possible devices connected to the hub.
pub async fn get_child_device_list(&self) -> Result<Vec<ChildDeviceResult>, Error> {
pub async fn get_child_device_list(&self) -> Result<Vec<ChildDeviceHubResult>, Error> {
self.client
.get_child_device_list::<ChildDeviceListResult>()
.get_child_device_list::<ChildDeviceListHubResult>()
.await
.map(|r| r.devices)
}
Expand Down
29 changes: 16 additions & 13 deletions tapo/src/api/power_strip_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ use std::fmt;

use serde::de::DeserializeOwned;

use crate::api::{ApiClient, ApiClientExt};
use crate::api::P300ChildHandler;
use crate::api::ApiClient;
use crate::api::PlugPowerStripHandler;
use crate::error::Error;
use crate::requests::TapoRequest;
use crate::responses::{ChildDeviceListResult, ChildDeviceResult, DeviceInfoPowerStripResult, TapoResponseExt};
use crate::responses::{
ChildDeviceListPowerStripResult, DeviceInfoPowerStripResult, PlugPowerStripResult,
TapoResponseExt,
};

/// Handler for the [P300](https://www.tapo.com/en/search/?q=P300) devices.
pub struct PowerStripHandler {
Expand All @@ -26,7 +29,7 @@ impl PowerStripHandler {

/// Returns *device info* as [`DeviceInfoPowerStripResult`].
/// It is not guaranteed to contain all the properties returned from the Tapo API.
/// If the deserialization fails, or if a property that you care about it's not present, try [`HubHandler::get_device_info_json`].
/// If the deserialization fails, or if a property that you care about it's not present, try [`PowerStripHandler::get_device_info_json`].
pub async fn get_device_info(&self) -> Result<DeviceInfoPowerStripResult, Error> {
self.client.get_device_info().await
}
Expand All @@ -37,14 +40,14 @@ impl PowerStripHandler {
self.client.get_device_info().await
}

/// Returns *child device list* as [`ChildDeviceListResult`].
/// Returns *child device list* as [`PlugPowerStripResult`].
/// It is not guaranteed to contain all the properties returned from the Tapo API
/// or to support all the possible devices connected to the hub.
pub async fn get_child_device_list(&self) -> Result<Vec<ChildDeviceResult>, Error> {
pub async fn get_child_device_list(&self) -> Result<Vec<PlugPowerStripResult>, Error> {
self.client
.get_child_device_list::<ChildDeviceListResult>()
.get_child_device_list::<ChildDeviceListPowerStripResult>()
.await
.map(|r| r.devices)
.map(|r| r.sub_plugs)
}

/// Returns *child device list* as [`serde_json::Value`].
Expand Down Expand Up @@ -74,7 +77,7 @@ impl PowerStripHandler {

/// Child device handler builders.
impl PowerStripHandler {
/// Returns a [`P300ChildHandler`] for the given `device_id`.
/// Returns a [`PlugPowerStripHandler`] for the given `device_id`.
///
/// # Arguments
///
Expand All @@ -87,17 +90,17 @@ impl PowerStripHandler {
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // Connect to the hub
/// let hub = ApiClient::new("[email protected]", "tapo-password")
/// let power_strip = ApiClient::new("[email protected]", "tapo-password")
/// .p300("192.168.1.100")
/// .await?;
/// // Get a handler for the child device
/// let device = hub.p300("0000000000000000000000000000000000000000");
/// let device = power_strip.plug("0000000000000000000000000000000000000000");
/// // Get the device info of the child device
/// let device_info = device.get_device_info().await?;
/// # Ok(())
/// # }
/// ```
pub fn p300(&self, device_id: impl Into<String>) -> P300ChildHandler {
P300ChildHandler::new(self, device_id.into())
pub fn plug(&self, device_id: impl Into<String>) -> PlugPowerStripHandler {
PlugPowerStripHandler::new(self, device_id.into())
}
}
2 changes: 1 addition & 1 deletion tapo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Tapo API Client.
//!
//! Tested with light bulbs (L510, L520, L530, L610, L630), light strips (L900, L920, L930),
//! plugs (P100, P105, P110, P115), hubs (H100), switches (S200B) and sensors (KE100, T100, T110, T300, T310, T315).
//! plugs (P100, P105, P110, P115, P300), hubs (H100), switches (S200B) and sensors (KE100, T100, T110, T300, T310, T315).
//!
//! # Example with L530
//! ```rust,no_run
Expand Down
6 changes: 4 additions & 2 deletions tapo/src/responses.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Tapo response objects.
mod child_device_list_result;
mod child_device_list_hub_result;
mod child_device_list_power_strip_result;
mod control_child_result;
mod current_power_result;
mod decodable_result_ext;
Expand All @@ -15,7 +16,8 @@ mod tapo_result;
mod token_result;
mod trigger_logs_result;

pub use child_device_list_result::*;
pub use child_device_list_hub_result::*;
pub use child_device_list_power_strip_result::*;
pub use current_power_result::*;
pub use device_info_result::*;
pub use device_usage_energy_monitoring_result::*;
Expand Down
Loading

0 comments on commit c0bd473

Please sign in to comment.