Skip to content

Commit

Permalink
tapo-py: Add support for the .set() API
Browse files Browse the repository at this point in the history
  • Loading branch information
mihai-dinculescu committed Jan 27, 2024
1 parent 482f8db commit b44065f
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Unofficial Tapo API Client. Works with TP-Link Tapo smart devices. Tested with l
| set_color_temperature | | | ✅ | ✓ | | |
| set_hue_saturation | | | ✅ | ✓ | | |
| set_lighting_effect | | | | ✓ | | |
| set() API \* | | | ✓ | ✓ | | |
| set() API \* | | | ✅ | ✓ | | |

\* The `set()` API allows multiple properties to be set in a single request.

Expand Down
6 changes: 6 additions & 0 deletions tapo-py/examples/tapo_l530.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ async def main():
print("Waiting 2 seconds...")
await asyncio.sleep(2)

print("Using the `set` API to set multiple properties in a single request...")
await device.set().brightness(50).color(Color.HotPink).send(device)

print("Waiting 2 seconds...")
await asyncio.sleep(2)

print("Turning device off...")
await device.off()

Expand Down
80 changes: 77 additions & 3 deletions tapo-py/src/handlers/color_light_handler.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use std::sync::Arc;
use std::{ops::Deref, sync::Arc};

use pyo3::prelude::*;
use tapo::{requests::Color, ColorLightHandler};
use tapo::{
requests::{Color, ColorLightSetDeviceInfoParams},
ColorLightHandler,
};
use tokio::sync::Mutex;

use crate::errors::ErrorWrapper;

#[derive(Clone)]
#[pyclass(name = "ColorColorLightHandler")]
#[pyclass(name = "ColorLightHandler")]
pub struct PyColorLightHandler {
handler: Arc<Mutex<ColorLightHandler>>,
}
Expand Down Expand Up @@ -105,6 +108,10 @@ impl PyColorLightHandler {
})
}

pub fn set(&self) -> PyColorLightSetDeviceInfoParams {
PyColorLightSetDeviceInfoParams::new()
}

pub fn set_brightness<'a>(&'a self, py: Python<'a>, brightness: u8) -> PyResult<&'a PyAny> {
let handler = self.handler.clone();
pyo3_asyncio::tokio::future_into_py(py, async move {
Expand Down Expand Up @@ -166,3 +173,70 @@ impl PyColorLightHandler {
})
}
}

#[derive(Clone)]
#[pyclass(name = "LightSetDeviceInfoParams")]
pub struct PyColorLightSetDeviceInfoParams {
params: ColorLightSetDeviceInfoParams,
}

impl PyColorLightSetDeviceInfoParams {
pub(crate) fn new() -> Self {
Self {
params: ColorLightSetDeviceInfoParams::new(),
}
}
}

#[pymethods]
impl PyColorLightSetDeviceInfoParams {
pub fn on(&self) -> Self {
Self {
params: self.params.clone().on(),
}
}

pub fn off(&self) -> Self {
Self {
params: self.params.clone().off(),
}
}

pub fn brightness(&self, brightness: u8) -> Self {
Self {
params: self.params.clone().brightness(brightness),
}
}

pub fn color(&self, color: Color) -> Self {
Self {
params: self.params.clone().color(color),
}
}

pub fn hue_saturation(&self, hue: u16, saturation: u8) -> Self {
Self {
params: self.params.clone().hue_saturation(hue, saturation),
}
}

pub fn color_temperature(&self, color_temperature: u16) -> Self {
Self {
params: self.params.clone().color_temperature(color_temperature),
}
}

pub fn send<'a>(&'a self, py: Python<'a>, handler: PyColorLightHandler) -> PyResult<&'a PyAny> {
let params = self.params.clone();

pyo3_asyncio::tokio::future_into_py(py, async move {
let handler_lock = handler.handler.lock().await;
params
.send(handler_lock.deref())
.await
.map_err(ErrorWrapper)?;

Ok(())
})
}
}
63 changes: 63 additions & 0 deletions tapo-py/tapo.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,15 @@ class ColorLightHandler:
Returns:
DeviceUsageResult: Contains the time usage.
"""
def set(self) -> ColorLightSetDeviceInfoParams:
"""Returns a `ColorLightSetDeviceInfoParams` builder that allows
multiple properties to be set in a single request.
`ColorLightSetDeviceInfoParams.send` must be called at the end to apply the changes.
Returns:
ColorLightSetDeviceInfoParams: Builder that is used by the
`ColorLightHandler.set` API to set multiple properties in a single request.
"""
async def set_brightness(self, brightness: int) -> None:
"""Sets the *brightness* and turns *on* the device.
Expand All @@ -425,6 +434,60 @@ class ColorLightHandler:
color_temperature (int): between 2500 and 6500
"""

class ColorLightSetDeviceInfoParams:
"""Builder that is used by the `ColorLightHandler.set` API to set
multiple properties in a single request.
"""

def on(self) -> ColorLightSetDeviceInfoParams:
"""Turns *on* the device.
`ColorLightSetDeviceInfoParams.send` must be called at the end to apply the changes.
"""
def off(self) -> ColorLightSetDeviceInfoParams:
"""Turns *off* the device.
`ColorLightSetDeviceInfoParams.send` must be called at the end to apply the changes.
"""
def brightness(self, brightness: int) -> ColorLightSetDeviceInfoParams:
"""Sets the *brightness*.
`ColorLightSetDeviceInfoParams.send` must be called at the end to apply the changes.
The device will also be turned *on*, unless `ColorLightSetDeviceInfoParams.off` is called.
Args:
brightness (int): between 1 and 100
"""
def color(self, color: Color) -> ColorLightSetDeviceInfoParams:
"""Sets the *color*.
`ColorLightSetDeviceInfoParams.send` must be called at the end to apply the changes.
The device will also be turned *on*, unless `ColorLightSetDeviceInfoParams.off` is called.
Args:
color (Color): one of `tapo.Color` as defined in the Google Home app.
"""
def hue_saturation(
self, hue: int, saturation: int
) -> ColorLightSetDeviceInfoParams:
"""Sets the *hue* and *saturation*.
`ColorLightSetDeviceInfoParams.send` must be called at the end to apply the changes.
The device will also be turned *on*, unless `ColorLightSetDeviceInfoParams.off` is called.
Args:
hue (int): between 1 and 360
saturation (int): between 1 and 100
"""
def color_temperature(
self, color_temperature: int
) -> ColorLightSetDeviceInfoParams:
"""
Sets the *color temperature*.
`ColorLightSetDeviceInfoParams.send` must be called at the end to apply the changes.
The device will also be turned *on*, unless `ColorLightSetDeviceInfoParams.off` is called.
Args:
color_temperature (int): between 2500 and 6500
"""
async def send(self) -> None:
"""Performs a request to apply the changes to the device."""

class PlugHandler:
"""Handler for the [P100](https://www.tapo.com/en/search/?q=P100) & [P105](https://www.tapo.com/en/search/?q=P105) devices."""

Expand Down

0 comments on commit b44065f

Please sign in to comment.