feat(tapo-agent): add schedule/countdown timer API support
- Fork tapo crate to add missing schedule/timer APIs - Add get_countdown_rules, get_schedule_rules, get_next_event methods - New readings: countdown_active, countdown_remain, schedule_count, schedule_active_count, next_event_time - Add local compilation to build script alongside cross-compilation - Implement offline polling - device collection continues when server disconnects - Add more device readings: on_time, signal_level, rssi, runtime_today/month, energy_month Vendored tapo fork in tapo-fork/ with minimal changes to add schedule APIs.
This commit is contained in:
29
agents/tapo/tapo-fork/tapo-py/src/api.rs
Normal file
29
agents/tapo/tapo-fork/tapo-py/src/api.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
mod api_client;
|
||||
mod child_devices;
|
||||
mod color_light_handler;
|
||||
mod discovery;
|
||||
mod generic_device_handler;
|
||||
mod hub_handler;
|
||||
mod light_handler;
|
||||
mod plug_energy_monitoring_handler;
|
||||
mod plug_handler;
|
||||
mod power_strip_energy_monitoring_handler;
|
||||
mod power_strip_handler;
|
||||
mod py_handler_ext;
|
||||
mod rgb_light_strip_handler;
|
||||
mod rgbic_light_strip_handler;
|
||||
|
||||
pub use api_client::*;
|
||||
pub use child_devices::*;
|
||||
pub use color_light_handler::*;
|
||||
pub use discovery::*;
|
||||
pub use generic_device_handler::*;
|
||||
pub use hub_handler::*;
|
||||
pub use light_handler::*;
|
||||
pub use plug_energy_monitoring_handler::*;
|
||||
pub use plug_handler::*;
|
||||
pub use power_strip_energy_monitoring_handler::*;
|
||||
pub use power_strip_handler::*;
|
||||
pub use py_handler_ext::*;
|
||||
pub use rgb_light_strip_handler::*;
|
||||
pub use rgbic_light_strip_handler::*;
|
||||
165
agents/tapo/tapo-fork/tapo-py/src/api/api_client.rs
Normal file
165
agents/tapo/tapo-fork/tapo-py/src/api/api_client.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use tapo::{
|
||||
ApiClient, ColorLightHandler, DeviceDiscovery, GenericDeviceHandler, HubHandler, LightHandler,
|
||||
PlugEnergyMonitoringHandler, PlugHandler, PowerStripEnergyMonitoringHandler, PowerStripHandler,
|
||||
RgbLightStripHandler, RgbicLightStripHandler,
|
||||
};
|
||||
|
||||
use crate::call_handler_constructor;
|
||||
use crate::errors::ErrorWrapper;
|
||||
|
||||
use super::{
|
||||
PyColorLightHandler, PyDeviceDiscovery, PyGenericDeviceHandler, PyHubHandler, PyLightHandler,
|
||||
PyPlugEnergyMonitoringHandler, PyPlugHandler, PyPowerStripEnergyMonitoringHandler,
|
||||
PyPowerStripHandler, PyRgbLightStripHandler, PyRgbicLightStripHandler,
|
||||
};
|
||||
|
||||
#[pyclass(name = "ApiClient")]
|
||||
pub struct PyApiClient {
|
||||
client: ApiClient,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyApiClient {
|
||||
#[new]
|
||||
#[pyo3(signature = (tapo_username, tapo_password, timeout_s=None))]
|
||||
pub fn new(
|
||||
tapo_username: String,
|
||||
tapo_password: String,
|
||||
timeout_s: Option<u64>,
|
||||
) -> Result<Self, ErrorWrapper> {
|
||||
let client = match timeout_s {
|
||||
Some(timeout_s) => ApiClient::new(tapo_username, tapo_password)
|
||||
.with_timeout(Duration::from_secs(timeout_s)),
|
||||
None => ApiClient::new(tapo_username, tapo_password),
|
||||
};
|
||||
|
||||
Ok(Self { client })
|
||||
}
|
||||
|
||||
pub async fn discover_devices(
|
||||
&self,
|
||||
target: String,
|
||||
timeout_s: u64,
|
||||
) -> Result<PyDeviceDiscovery, ErrorWrapper> {
|
||||
let discovery: DeviceDiscovery =
|
||||
call_handler_constructor!(self, tapo::ApiClient::discover_devices, target, timeout_s);
|
||||
Ok(PyDeviceDiscovery::new(discovery))
|
||||
}
|
||||
|
||||
pub async fn generic_device(&self, ip_address: String) -> PyResult<PyGenericDeviceHandler> {
|
||||
let handler: GenericDeviceHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::generic_device, ip_address);
|
||||
Ok(PyGenericDeviceHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l510(&self, ip_address: String) -> PyResult<PyLightHandler> {
|
||||
let handler: LightHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l510, ip_address);
|
||||
Ok(PyLightHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l520(&self, ip_address: String) -> PyResult<PyLightHandler> {
|
||||
let handler: LightHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l520, ip_address);
|
||||
Ok(PyLightHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l530(&self, ip_address: String) -> PyResult<PyColorLightHandler> {
|
||||
let handler: ColorLightHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l530, ip_address);
|
||||
Ok(PyColorLightHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l535(&self, ip_address: String) -> PyResult<PyColorLightHandler> {
|
||||
let handler: ColorLightHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l535, ip_address);
|
||||
Ok(PyColorLightHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l610(&self, ip_address: String) -> PyResult<PyLightHandler> {
|
||||
let handler: LightHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l610, ip_address);
|
||||
Ok(PyLightHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l630(&self, ip_address: String) -> PyResult<PyColorLightHandler> {
|
||||
let handler: ColorLightHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l630, ip_address);
|
||||
Ok(PyColorLightHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l900(&self, ip_address: String) -> PyResult<PyRgbLightStripHandler> {
|
||||
let handler: RgbLightStripHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l900, ip_address);
|
||||
Ok(PyRgbLightStripHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l920(&self, ip_address: String) -> PyResult<PyRgbicLightStripHandler> {
|
||||
let handler: RgbicLightStripHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l920, ip_address);
|
||||
Ok(PyRgbicLightStripHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn l930(&self, ip_address: String) -> PyResult<PyRgbicLightStripHandler> {
|
||||
let handler: RgbicLightStripHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::l930, ip_address);
|
||||
Ok(PyRgbicLightStripHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p100(&self, ip_address: String) -> PyResult<PyPlugHandler> {
|
||||
let handler: PlugHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p100, ip_address);
|
||||
Ok(PyPlugHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p105(&self, ip_address: String) -> PyResult<PyPlugHandler> {
|
||||
let handler: PlugHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p105, ip_address);
|
||||
Ok(PyPlugHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p110(&self, ip_address: String) -> PyResult<PyPlugEnergyMonitoringHandler> {
|
||||
let handler: PlugEnergyMonitoringHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p110, ip_address);
|
||||
Ok(PyPlugEnergyMonitoringHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p115(&self, ip_address: String) -> PyResult<PyPlugEnergyMonitoringHandler> {
|
||||
let handler: PlugEnergyMonitoringHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p115, ip_address);
|
||||
Ok(PyPlugEnergyMonitoringHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p300(&self, ip_address: String) -> PyResult<PyPowerStripHandler> {
|
||||
let handler: PowerStripHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p300, ip_address);
|
||||
Ok(PyPowerStripHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p304(&self, ip_address: String) -> PyResult<PyPowerStripEnergyMonitoringHandler> {
|
||||
let handler: PowerStripEnergyMonitoringHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p304, ip_address);
|
||||
Ok(PyPowerStripEnergyMonitoringHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p306(&self, ip_address: String) -> PyResult<PyPowerStripHandler> {
|
||||
let handler: PowerStripHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p306, ip_address);
|
||||
Ok(PyPowerStripHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn p316(&self, ip_address: String) -> PyResult<PyPowerStripEnergyMonitoringHandler> {
|
||||
let handler: PowerStripEnergyMonitoringHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::p316, ip_address);
|
||||
Ok(PyPowerStripEnergyMonitoringHandler::new(handler))
|
||||
}
|
||||
|
||||
pub async fn h100(&self, ip_address: String) -> PyResult<PyHubHandler> {
|
||||
let handler: HubHandler =
|
||||
call_handler_constructor!(self, tapo::ApiClient::h100, ip_address);
|
||||
Ok(PyHubHandler::new(handler))
|
||||
}
|
||||
}
|
||||
17
agents/tapo/tapo-fork/tapo-py/src/api/child_devices.rs
Normal file
17
agents/tapo/tapo-fork/tapo-py/src/api/child_devices.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
mod ke100_handler;
|
||||
mod power_strip_plug_energy_monitoring_handler;
|
||||
mod power_strip_plug_handler;
|
||||
mod s200b_handler;
|
||||
mod t100_handler;
|
||||
mod t110_handler;
|
||||
mod t300_handler;
|
||||
mod t31x_handler;
|
||||
|
||||
pub use ke100_handler::*;
|
||||
pub use power_strip_plug_energy_monitoring_handler::*;
|
||||
pub use power_strip_plug_handler::*;
|
||||
pub use s200b_handler::*;
|
||||
pub use t31x_handler::*;
|
||||
pub use t100_handler::*;
|
||||
pub use t110_handler::*;
|
||||
pub use t300_handler::*;
|
||||
@@ -0,0 +1,101 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::KE100Handler;
|
||||
use tapo::responses::{KE100Result, TemperatureUnitKE100};
|
||||
|
||||
use crate::call_handler_method;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "KE100Handler")]
|
||||
pub struct PyKE100Handler {
|
||||
inner: Arc<KE100Handler>,
|
||||
}
|
||||
|
||||
impl PyKE100Handler {
|
||||
pub fn new(handler: KE100Handler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyKE100Handler {
|
||||
pub async fn get_device_info(&self) -> PyResult<KE100Result> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), KE100Handler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(handler.deref(), KE100Handler::get_device_info_json)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn set_child_protection(&self, on: bool) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), KE100Handler::set_child_protection, on)
|
||||
}
|
||||
|
||||
pub async fn set_frost_protection(&self, on: bool) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), KE100Handler::set_frost_protection, on)
|
||||
}
|
||||
|
||||
pub async fn set_max_control_temperature(
|
||||
&self,
|
||||
value: u8,
|
||||
unit: TemperatureUnitKE100,
|
||||
) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
KE100Handler::set_max_control_temperature,
|
||||
value,
|
||||
unit
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_min_control_temperature(
|
||||
&self,
|
||||
value: u8,
|
||||
unit: TemperatureUnitKE100,
|
||||
) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
KE100Handler::set_min_control_temperature,
|
||||
value,
|
||||
unit
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_target_temperature(
|
||||
&self,
|
||||
value: u8,
|
||||
unit: TemperatureUnitKE100,
|
||||
) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
KE100Handler::set_target_temperature,
|
||||
value,
|
||||
unit
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_temperature_offset(
|
||||
&self,
|
||||
value: i8,
|
||||
unit: TemperatureUnitKE100,
|
||||
) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
KE100Handler::set_temperature_offset,
|
||||
value,
|
||||
unit
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use chrono::{DateTime, NaiveDate, Utc};
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::PowerStripPlugEnergyMonitoringHandler;
|
||||
use tapo::requests::{EnergyDataInterval, PowerDataInterval};
|
||||
use tapo::responses::{
|
||||
CurrentPowerResult, DeviceUsageEnergyMonitoringResult, EnergyDataResult, EnergyUsageResult,
|
||||
PowerDataResult, PowerStripPlugEnergyMonitoringResult,
|
||||
};
|
||||
|
||||
use crate::call_handler_method;
|
||||
use crate::requests::{PyEnergyDataInterval, PyPowerDataInterval};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "PowerStripPlugEnergyMonitoringHandler")]
|
||||
pub struct PyPowerStripPlugEnergyMonitoringHandler {
|
||||
inner: Arc<PowerStripPlugEnergyMonitoringHandler>,
|
||||
}
|
||||
|
||||
impl PyPowerStripPlugEnergyMonitoringHandler {
|
||||
pub fn new(handler: PowerStripPlugEnergyMonitoringHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyPowerStripPlugEnergyMonitoringHandler {
|
||||
pub async fn get_device_info(&self) -> PyResult<PowerStripPlugEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
PowerStripPlugEnergyMonitoringHandler::get_device_info
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.deref(),
|
||||
PowerStripPlugEnergyMonitoringHandler::get_device_info_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), PowerStripPlugEnergyMonitoringHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), PowerStripPlugEnergyMonitoringHandler::off)
|
||||
}
|
||||
|
||||
pub async fn get_current_power(&self) -> PyResult<CurrentPowerResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
PowerStripPlugEnergyMonitoringHandler::get_current_power,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_usage(&self) -> PyResult<DeviceUsageEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
PowerStripPlugEnergyMonitoringHandler::get_device_usage,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_energy_usage(&self) -> PyResult<EnergyUsageResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
PowerStripPlugEnergyMonitoringHandler::get_energy_usage,
|
||||
)
|
||||
}
|
||||
|
||||
#[pyo3(signature = (interval, start_date, end_date=None))]
|
||||
pub async fn get_energy_data(
|
||||
&self,
|
||||
interval: PyEnergyDataInterval,
|
||||
start_date: NaiveDate,
|
||||
end_date: Option<NaiveDate>,
|
||||
) -> PyResult<EnergyDataResult> {
|
||||
let interval = match interval {
|
||||
PyEnergyDataInterval::Hourly => EnergyDataInterval::Hourly {
|
||||
start_date,
|
||||
end_date: end_date.unwrap_or(start_date),
|
||||
},
|
||||
PyEnergyDataInterval::Daily => EnergyDataInterval::Daily { start_date },
|
||||
PyEnergyDataInterval::Monthly => EnergyDataInterval::Monthly { start_date },
|
||||
};
|
||||
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.deref(),
|
||||
PowerStripPlugEnergyMonitoringHandler::get_energy_data,
|
||||
interval
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn get_power_data(
|
||||
&self,
|
||||
interval: PyPowerDataInterval,
|
||||
start_date_time: DateTime<Utc>,
|
||||
end_date_time: DateTime<Utc>,
|
||||
) -> PyResult<PowerDataResult> {
|
||||
let interval = match interval {
|
||||
PyPowerDataInterval::Every5Minutes => PowerDataInterval::Every5Minutes {
|
||||
start_date_time,
|
||||
end_date_time,
|
||||
},
|
||||
PyPowerDataInterval::Hourly => PowerDataInterval::Hourly {
|
||||
start_date_time,
|
||||
end_date_time,
|
||||
},
|
||||
};
|
||||
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.deref(),
|
||||
PowerStripPlugEnergyMonitoringHandler::get_power_data,
|
||||
interval
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::PowerStripPlugHandler;
|
||||
use tapo::responses::PowerStripPlugResult;
|
||||
|
||||
use crate::call_handler_method;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "PowerStripPlugHandler")]
|
||||
pub struct PyPowerStripPlugHandler {
|
||||
inner: Arc<PowerStripPlugHandler>,
|
||||
}
|
||||
|
||||
impl PyPowerStripPlugHandler {
|
||||
pub fn new(handler: PowerStripPlugHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyPowerStripPlugHandler {
|
||||
pub async fn get_device_info(&self) -> PyResult<PowerStripPlugResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), PowerStripPlugHandler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result =
|
||||
call_handler_method!(handler.deref(), PowerStripPlugHandler::get_device_info_json)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), PowerStripPlugHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), PowerStripPlugHandler::off)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::S200BHandler;
|
||||
use tapo::responses::S200BResult;
|
||||
|
||||
use crate::call_handler_method;
|
||||
use crate::responses::TriggerLogsS200BResult;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "S200BHandler")]
|
||||
pub struct PyS200BHandler {
|
||||
inner: Arc<S200BHandler>,
|
||||
}
|
||||
|
||||
impl PyS200BHandler {
|
||||
pub fn new(handler: S200BHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyS200BHandler {
|
||||
pub async fn get_device_info(&self) -> PyResult<S200BResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), S200BHandler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(handler.deref(), S200BHandler::get_device_info_json)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_trigger_logs(
|
||||
&self,
|
||||
page_size: u64,
|
||||
start_id: u64,
|
||||
) -> PyResult<TriggerLogsS200BResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
S200BHandler::get_trigger_logs,
|
||||
page_size,
|
||||
start_id
|
||||
)
|
||||
.map(|result| result.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::{T100Handler, responses::T100Result};
|
||||
|
||||
use crate::call_handler_method;
|
||||
use crate::responses::TriggerLogsT100Result;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "T100Handler")]
|
||||
pub struct PyT100Handler {
|
||||
inner: Arc<T100Handler>,
|
||||
}
|
||||
|
||||
impl PyT100Handler {
|
||||
pub fn new(handler: T100Handler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyT100Handler {
|
||||
pub async fn get_device_info(&self) -> PyResult<T100Result> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), T100Handler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(handler.deref(), T100Handler::get_device_info_json)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_trigger_logs(
|
||||
&self,
|
||||
page_size: u64,
|
||||
start_id: u64,
|
||||
) -> PyResult<TriggerLogsT100Result> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
T100Handler::get_trigger_logs,
|
||||
page_size,
|
||||
start_id
|
||||
)
|
||||
.map(|result| result.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::T110Handler;
|
||||
use tapo::responses::T110Result;
|
||||
|
||||
use crate::call_handler_method;
|
||||
use crate::responses::TriggerLogsT110Result;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "T110Handler")]
|
||||
pub struct PyT110Handler {
|
||||
inner: Arc<T110Handler>,
|
||||
}
|
||||
|
||||
impl PyT110Handler {
|
||||
pub fn new(handler: T110Handler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyT110Handler {
|
||||
pub async fn get_device_info(&self) -> PyResult<T110Result> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), T110Handler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(handler.deref(), T110Handler::get_device_info_json)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_trigger_logs(
|
||||
&self,
|
||||
page_size: u64,
|
||||
start_id: u64,
|
||||
) -> PyResult<TriggerLogsT110Result> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
T110Handler::get_trigger_logs,
|
||||
page_size,
|
||||
start_id
|
||||
)
|
||||
.map(|result| result.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::T300Handler;
|
||||
use tapo::responses::T300Result;
|
||||
|
||||
use crate::call_handler_method;
|
||||
use crate::responses::TriggerLogsT300Result;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "T300Handler")]
|
||||
pub struct PyT300Handler {
|
||||
inner: Arc<T300Handler>,
|
||||
}
|
||||
|
||||
impl PyT300Handler {
|
||||
pub fn new(handler: T300Handler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyT300Handler {
|
||||
pub async fn get_device_info(&self) -> PyResult<T300Result> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), T300Handler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(handler.deref(), T300Handler::get_device_info_json)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_trigger_logs(
|
||||
&self,
|
||||
page_size: u64,
|
||||
start_id: u64,
|
||||
) -> PyResult<TriggerLogsT300Result> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
T300Handler::get_trigger_logs,
|
||||
page_size,
|
||||
start_id
|
||||
)
|
||||
.map(|result| result.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use pyo3::{prelude::*, types::PyDict};
|
||||
use tapo::T31XHandler;
|
||||
use tapo::responses::{T31XResult, TemperatureHumidityRecords};
|
||||
|
||||
use crate::call_handler_method;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "T31XHandler")]
|
||||
pub struct PyT31XHandler {
|
||||
inner: Arc<T31XHandler>,
|
||||
}
|
||||
|
||||
impl PyT31XHandler {
|
||||
pub fn new(handler: T31XHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyT31XHandler {
|
||||
pub async fn get_device_info(&self) -> PyResult<T31XResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.deref(), T31XHandler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(handler.deref(), T31XHandler::get_device_info_json)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_temperature_humidity_records(&self) -> PyResult<TemperatureHumidityRecords> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.deref(),
|
||||
T31XHandler::get_temperature_humidity_records
|
||||
)
|
||||
}
|
||||
}
|
||||
138
agents/tapo/tapo-fork/tapo-py/src/api/color_light_handler.rs
Normal file
138
agents/tapo/tapo-fork/tapo-py/src/api/color_light_handler.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use tapo::requests::Color;
|
||||
use tapo::responses::{DeviceInfoColorLightResult, DeviceUsageEnergyMonitoringResult};
|
||||
use tapo::{ColorLightHandler, DeviceManagementExt as _, HandlerExt};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::api::PyHandlerExt;
|
||||
use crate::call_handler_method;
|
||||
use crate::requests::PyColorLightSetDeviceInfoParams;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "ColorLightHandler")]
|
||||
pub struct PyColorLightHandler {
|
||||
inner: Arc<RwLock<ColorLightHandler>>,
|
||||
}
|
||||
|
||||
impl PyColorLightHandler {
|
||||
pub fn new(handler: ColorLightHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PyHandlerExt for PyColorLightHandler {
|
||||
fn get_inner_handler(&self) -> Arc<RwLock<impl HandlerExt + 'static>> {
|
||||
Arc::clone(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyColorLightHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
ColorLightHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), ColorLightHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), ColorLightHandler::off)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::device_reset,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoColorLightResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::get_device_info
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::get_device_info_json,
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_device_usage(&self) -> PyResult<DeviceUsageEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::get_device_usage
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set(&self) -> PyColorLightSetDeviceInfoParams {
|
||||
PyColorLightSetDeviceInfoParams::new()
|
||||
}
|
||||
|
||||
pub async fn set_brightness(&self, brightness: u8) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::set_brightness,
|
||||
brightness
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_color(&self, color: Color) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::set_color,
|
||||
color
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_hue_saturation(&self, hue: u16, saturation: u8) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::set_hue_saturation,
|
||||
hue,
|
||||
saturation
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_color_temperature(&self, color_temperature: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
ColorLightHandler::set_color_temperature,
|
||||
color_temperature
|
||||
)
|
||||
}
|
||||
}
|
||||
5
agents/tapo/tapo-fork/tapo-py/src/api/discovery.rs
Normal file
5
agents/tapo/tapo-fork/tapo-py/src/api/discovery.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod device_discovery;
|
||||
mod discovery_result;
|
||||
|
||||
pub use device_discovery::*;
|
||||
pub use discovery_result::*;
|
||||
@@ -0,0 +1,87 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use tapo::{DeviceDiscovery, StreamExt as _};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::{PyMaybeDiscoveryResult, convert_result_to_maybe_py};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "DeviceDiscovery")]
|
||||
pub struct PyDeviceDiscovery {
|
||||
pub inner: Arc<Mutex<DeviceDiscovery>>,
|
||||
}
|
||||
|
||||
impl PyDeviceDiscovery {
|
||||
pub fn new(inner: DeviceDiscovery) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(inner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyDeviceDiscovery {
|
||||
fn __iter__(slf: PyRef<'_, Self>) -> PyResult<PyDeviceDiscoveryIter> {
|
||||
Ok(PyDeviceDiscoveryIter {
|
||||
inner: (*slf).inner.clone(),
|
||||
})
|
||||
}
|
||||
fn __aiter__(slf: PyRef<'_, Self>) -> PyResult<PyDeviceDiscoveryIter> {
|
||||
Ok(PyDeviceDiscoveryIter {
|
||||
inner: (*slf).inner.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "DeviceDiscoveryIter")]
|
||||
pub struct PyDeviceDiscoveryIter {
|
||||
pub inner: Arc<Mutex<DeviceDiscovery>>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyDeviceDiscoveryIter {
|
||||
fn __iter__(slf: Py<Self>) -> Py<Self> {
|
||||
slf
|
||||
}
|
||||
|
||||
fn __aiter__(slf: Py<Self>) -> Py<Self> {
|
||||
slf
|
||||
}
|
||||
|
||||
fn __next__(slf: PyRefMut<'_, Self>) -> PyResult<Option<PyMaybeDiscoveryResult>> {
|
||||
let inner = (*slf).inner.clone();
|
||||
|
||||
let result = Python::attach(|py| {
|
||||
py.detach(|| {
|
||||
crate::runtime::tokio().block_on(async {
|
||||
let mut guard = inner.lock().await;
|
||||
guard.next().await
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(result) = result {
|
||||
let result_maybe_py = convert_result_to_maybe_py(result)?;
|
||||
Ok(Some(result_maybe_py))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn __anext__<'py>(slf: PyRefMut<'_, Self>, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
|
||||
let inner = (*slf).inner.clone();
|
||||
|
||||
pyo3_async_runtimes::tokio::future_into_py(py, async move {
|
||||
let mut guard = inner.lock().await;
|
||||
let result = guard.next().await;
|
||||
|
||||
match result {
|
||||
Some(result) => convert_result_to_maybe_py(result),
|
||||
None => Err(PyErr::new::<pyo3::exceptions::PyStopAsyncIteration, _>(
|
||||
"No more devices found",
|
||||
)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
use pyo3::prelude::*;
|
||||
use tapo::responses::{
|
||||
DeviceInfoColorLightResult, DeviceInfoGenericResult, DeviceInfoHubResult,
|
||||
DeviceInfoLightResult, DeviceInfoPlugEnergyMonitoringResult, DeviceInfoPlugResult,
|
||||
DeviceInfoPowerStripResult, DeviceInfoRgbLightStripResult, DeviceInfoRgbicLightStripResult,
|
||||
};
|
||||
use tapo::{DiscoveryResult, Error};
|
||||
|
||||
use crate::api::{
|
||||
PyColorLightHandler, PyGenericDeviceHandler, PyHubHandler, PyLightHandler,
|
||||
PyPlugEnergyMonitoringHandler, PyPlugHandler, PyPowerStripEnergyMonitoringHandler,
|
||||
PyPowerStripHandler, PyRgbLightStripHandler, PyRgbicLightStripHandler,
|
||||
};
|
||||
use crate::errors::ErrorWrapper;
|
||||
|
||||
#[pyclass(name = "DiscoveryResult")]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum PyDiscoveryResult {
|
||||
GenericDevice {
|
||||
device_info: DeviceInfoGenericResult,
|
||||
handler: PyGenericDeviceHandler,
|
||||
},
|
||||
Light {
|
||||
device_info: DeviceInfoLightResult,
|
||||
handler: PyLightHandler,
|
||||
},
|
||||
ColorLight {
|
||||
device_info: DeviceInfoColorLightResult,
|
||||
handler: PyColorLightHandler,
|
||||
},
|
||||
RgbLightStrip {
|
||||
device_info: DeviceInfoRgbLightStripResult,
|
||||
handler: PyRgbLightStripHandler,
|
||||
},
|
||||
RgbicLightStrip {
|
||||
device_info: DeviceInfoRgbicLightStripResult,
|
||||
handler: PyRgbicLightStripHandler,
|
||||
},
|
||||
Plug {
|
||||
device_info: DeviceInfoPlugResult,
|
||||
handler: PyPlugHandler,
|
||||
},
|
||||
PlugEnergyMonitoring {
|
||||
device_info: DeviceInfoPlugEnergyMonitoringResult,
|
||||
handler: PyPlugEnergyMonitoringHandler,
|
||||
},
|
||||
PowerStrip {
|
||||
device_info: DeviceInfoPowerStripResult,
|
||||
handler: PyPowerStripHandler,
|
||||
},
|
||||
PowerStripEnergyMonitoring {
|
||||
device_info: DeviceInfoPowerStripResult,
|
||||
handler: PyPowerStripEnergyMonitoringHandler,
|
||||
},
|
||||
Hub {
|
||||
device_info: DeviceInfoHubResult,
|
||||
handler: PyHubHandler,
|
||||
},
|
||||
}
|
||||
|
||||
#[pyclass(name = "MaybeDiscoveryResult")]
|
||||
pub struct PyMaybeDiscoveryResult {
|
||||
result: Option<PyDiscoveryResult>,
|
||||
exception: Option<ErrorWrapper>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyMaybeDiscoveryResult {
|
||||
pub fn get(mut slf: PyRefMut<'_, Self>) -> PyResult<PyDiscoveryResult> {
|
||||
if let Some(result) = slf.result.take() {
|
||||
Ok(result)
|
||||
} else if let Some(exception) = slf.exception.take() {
|
||||
Err(exception.into())
|
||||
} else {
|
||||
Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
|
||||
"No result or exception available. `get` can only be called once.",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_result_to_maybe_py(
|
||||
result: Result<DiscoveryResult, Error>,
|
||||
) -> PyResult<PyMaybeDiscoveryResult> {
|
||||
match result {
|
||||
Ok(result) => Ok(PyMaybeDiscoveryResult {
|
||||
result: Some(convert_result_to_py(result)),
|
||||
exception: None,
|
||||
}),
|
||||
Err(e) => Ok(PyMaybeDiscoveryResult {
|
||||
result: None,
|
||||
exception: Some(ErrorWrapper::from(e)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_result_to_py(result: DiscoveryResult) -> PyDiscoveryResult {
|
||||
match result {
|
||||
DiscoveryResult::GenericDevice {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::GenericDevice {
|
||||
device_info: *device_info,
|
||||
handler: PyGenericDeviceHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::Light {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::Light {
|
||||
device_info: *device_info,
|
||||
handler: PyLightHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::ColorLight {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::ColorLight {
|
||||
device_info: *device_info,
|
||||
handler: PyColorLightHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::RgbLightStrip {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::RgbLightStrip {
|
||||
device_info: *device_info,
|
||||
handler: PyRgbLightStripHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::RgbicLightStrip {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::RgbicLightStrip {
|
||||
device_info: *device_info,
|
||||
handler: PyRgbicLightStripHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::Plug {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::Plug {
|
||||
device_info: *device_info,
|
||||
handler: PyPlugHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::PlugEnergyMonitoring {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::PlugEnergyMonitoring {
|
||||
device_info: *device_info,
|
||||
handler: PyPlugEnergyMonitoringHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::PowerStrip {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::PowerStrip {
|
||||
device_info: *device_info,
|
||||
handler: PyPowerStripHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::PowerStripEnergyMonitoring {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::PowerStripEnergyMonitoring {
|
||||
device_info: *device_info,
|
||||
handler: PyPowerStripEnergyMonitoringHandler::new(handler),
|
||||
},
|
||||
DiscoveryResult::Hub {
|
||||
device_info,
|
||||
handler,
|
||||
} => PyDiscoveryResult::Hub {
|
||||
device_info: *device_info,
|
||||
handler: PyHubHandler::new(handler),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use tapo::GenericDeviceHandler;
|
||||
use tapo::responses::DeviceInfoGenericResult;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::call_handler_method;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "GenericDeviceHandler")]
|
||||
pub struct PyGenericDeviceHandler {
|
||||
inner: Arc<RwLock<GenericDeviceHandler>>,
|
||||
}
|
||||
|
||||
impl PyGenericDeviceHandler {
|
||||
pub fn new(handler: GenericDeviceHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyGenericDeviceHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
GenericDeviceHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), GenericDeviceHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), GenericDeviceHandler::off)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoGenericResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
GenericDeviceHandler::get_device_info
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
GenericDeviceHandler::get_device_info_json,
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
}
|
||||
291
agents/tapo/tapo-fork/tapo-py/src/api/hub_handler.rs
Normal file
291
agents/tapo/tapo-fork/tapo-py/src/api/hub_handler.rs
Normal file
@@ -0,0 +1,291 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyList};
|
||||
use tapo::requests::{AlarmDuration, AlarmRingtone, AlarmVolume};
|
||||
use tapo::responses::{ChildDeviceHubResult, DeviceInfoHubResult};
|
||||
use tapo::{DeviceManagementExt as _, Error, HubDevice, HubHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::api::{
|
||||
PyKE100Handler, PyS200BHandler, PyT31XHandler, PyT100Handler, PyT110Handler, PyT300Handler,
|
||||
};
|
||||
use crate::call_handler_method;
|
||||
use crate::errors::ErrorWrapper;
|
||||
use crate::requests::PyAlarmDuration;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "HubHandler")]
|
||||
pub struct PyHubHandler {
|
||||
inner: Arc<RwLock<HubHandler>>,
|
||||
}
|
||||
|
||||
impl PyHubHandler {
|
||||
pub fn new(handler: HubHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_identifier(
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<HubDevice> {
|
||||
match (device_id, nickname) {
|
||||
(Some(device_id), _) => Ok(HubDevice::ByDeviceId(device_id)),
|
||||
(None, Some(nickname)) => Ok(HubDevice::ByNickname(nickname)),
|
||||
_ => Err(Into::<ErrorWrapper>::into(Error::Validation {
|
||||
field: "identifier".to_string(),
|
||||
message: "Either a device_id or nickname must be provided".to_string(),
|
||||
})
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyHubHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
HubHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
HubHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::device_reset)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoHubResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
HubHandler::get_device_info_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_child_device_list(&self) -> PyResult<Py<PyList>> {
|
||||
let handler = self.inner.clone();
|
||||
let children = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
HubHandler::get_child_device_list
|
||||
)?;
|
||||
|
||||
Python::attach(|py| {
|
||||
let results = PyList::empty(py);
|
||||
|
||||
for child in children {
|
||||
match child {
|
||||
ChildDeviceHubResult::KE100(device) => {
|
||||
results.append(device.into_pyobject(py)?)?;
|
||||
}
|
||||
ChildDeviceHubResult::S200B(device) => {
|
||||
results.append(device.into_pyobject(py)?)?;
|
||||
}
|
||||
ChildDeviceHubResult::T100(device) => {
|
||||
results.append(device.into_pyobject(py)?)?;
|
||||
}
|
||||
ChildDeviceHubResult::T110(device) => {
|
||||
results.append(device.into_pyobject(py)?)?;
|
||||
}
|
||||
ChildDeviceHubResult::T300(device) => {
|
||||
results.append(device.into_pyobject(py)?)?;
|
||||
}
|
||||
ChildDeviceHubResult::T310(device) => {
|
||||
results.append(device.into_pyobject(py)?)?;
|
||||
}
|
||||
ChildDeviceHubResult::T315(device) => {
|
||||
results.append(device.into_pyobject(py)?)?;
|
||||
}
|
||||
_ => {
|
||||
results.append(py.None())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results.into())
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_child_device_list_json(&self, start_index: u64) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
HubHandler::get_child_device_list_json,
|
||||
start_index
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_child_device_component_list_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
HubHandler::get_child_device_component_list_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_supported_ringtone_list(&self) -> PyResult<Vec<String>> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
HubHandler::get_supported_ringtone_list
|
||||
)
|
||||
}
|
||||
|
||||
#[pyo3(signature = (ringtone, volume, duration, seconds=None))]
|
||||
pub async fn play_alarm(
|
||||
&self,
|
||||
ringtone: AlarmRingtone,
|
||||
volume: AlarmVolume,
|
||||
duration: PyAlarmDuration,
|
||||
seconds: Option<u32>,
|
||||
) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
|
||||
let duration = match duration {
|
||||
PyAlarmDuration::Continuous => AlarmDuration::Continuous,
|
||||
PyAlarmDuration::Once => AlarmDuration::Once,
|
||||
PyAlarmDuration::Seconds => {
|
||||
if let Some(seconds) = seconds {
|
||||
AlarmDuration::Seconds(seconds)
|
||||
} else {
|
||||
return Err(Into::<ErrorWrapper>::into(Error::Validation {
|
||||
field: "seconds".to_string(),
|
||||
message:
|
||||
"A value must be provided for seconds when duration = AlarmDuration.Seconds"
|
||||
.to_string(),
|
||||
})
|
||||
.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
HubHandler::play_alarm,
|
||||
ringtone,
|
||||
volume,
|
||||
duration
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn stop_alarm(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::stop_alarm)
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None))]
|
||||
pub async fn ke100(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<PyKE100Handler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier = PyHubHandler::parse_identifier(device_id, nickname)?;
|
||||
|
||||
let child_handler =
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::ke100, identifier)?;
|
||||
Ok(PyKE100Handler::new(child_handler))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None))]
|
||||
pub async fn s200b(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<PyS200BHandler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier = PyHubHandler::parse_identifier(device_id, nickname)?;
|
||||
|
||||
let child_handler =
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::s200b, identifier)?;
|
||||
Ok(PyS200BHandler::new(child_handler))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None))]
|
||||
pub async fn t100(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<PyT100Handler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier = PyHubHandler::parse_identifier(device_id, nickname)?;
|
||||
|
||||
let child_handler =
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::t100, identifier)?;
|
||||
Ok(PyT100Handler::new(child_handler))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None))]
|
||||
pub async fn t110(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<PyT110Handler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier = PyHubHandler::parse_identifier(device_id, nickname)?;
|
||||
|
||||
let child_handler =
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::t110, identifier)?;
|
||||
Ok(PyT110Handler::new(child_handler))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None))]
|
||||
pub async fn t300(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<PyT300Handler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier = PyHubHandler::parse_identifier(device_id, nickname)?;
|
||||
|
||||
let child_handler =
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::t300, identifier)?;
|
||||
Ok(PyT300Handler::new(child_handler))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None))]
|
||||
pub async fn t310(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<PyT31XHandler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier = PyHubHandler::parse_identifier(device_id, nickname)?;
|
||||
|
||||
let child_handler =
|
||||
call_handler_method!(handler.read().await.deref(), HubHandler::t310, identifier)?;
|
||||
Ok(PyT31XHandler::new(child_handler))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None))]
|
||||
pub async fn t315(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
) -> PyResult<PyT31XHandler> {
|
||||
self.t310(device_id, nickname).await
|
||||
}
|
||||
}
|
||||
88
agents/tapo/tapo-fork/tapo-py/src/api/light_handler.rs
Normal file
88
agents/tapo/tapo-fork/tapo-py/src/api/light_handler.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use tapo::responses::{DeviceInfoLightResult, DeviceUsageEnergyMonitoringResult};
|
||||
use tapo::{DeviceManagementExt as _, LightHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::call_handler_method;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "LightHandler")]
|
||||
pub struct PyLightHandler {
|
||||
inner: Arc<RwLock<LightHandler>>,
|
||||
}
|
||||
|
||||
impl PyLightHandler {
|
||||
pub fn new(handler: LightHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyLightHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
LightHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), LightHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), LightHandler::off)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
LightHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), LightHandler::device_reset)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoLightResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), LightHandler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
LightHandler::get_device_info_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_device_usage(&self) -> PyResult<DeviceUsageEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), LightHandler::get_device_usage)
|
||||
}
|
||||
|
||||
pub async fn set_brightness(&self, brightness: u8) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
LightHandler::set_brightness,
|
||||
brightness
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::{DateTime, NaiveDate, Utc};
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use tapo::requests::{EnergyDataInterval, PowerDataInterval};
|
||||
use tapo::responses::{
|
||||
CurrentPowerResult, DeviceInfoPlugEnergyMonitoringResult, DeviceUsageEnergyMonitoringResult,
|
||||
EnergyDataResult, EnergyUsageResult, PowerDataResult,
|
||||
};
|
||||
use tapo::{DeviceManagementExt as _, PlugEnergyMonitoringHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::call_handler_method;
|
||||
use crate::requests::{PyEnergyDataInterval, PyPowerDataInterval};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "PlugEnergyMonitoringHandler")]
|
||||
pub struct PyPlugEnergyMonitoringHandler {
|
||||
inner: Arc<RwLock<PlugEnergyMonitoringHandler>>,
|
||||
}
|
||||
|
||||
impl PyPlugEnergyMonitoringHandler {
|
||||
pub fn new(handler: PlugEnergyMonitoringHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyPlugEnergyMonitoringHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
PlugEnergyMonitoringHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::on
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::off
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::device_reset,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoPlugEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::get_device_info,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::get_device_info_json,
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_current_power(&self) -> PyResult<CurrentPowerResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::get_current_power,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_usage(&self) -> PyResult<DeviceUsageEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::get_device_usage,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_energy_usage(&self) -> PyResult<EnergyUsageResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::get_energy_usage,
|
||||
)
|
||||
}
|
||||
|
||||
#[pyo3(signature = (interval, start_date, end_date=None))]
|
||||
pub async fn get_energy_data(
|
||||
&self,
|
||||
interval: PyEnergyDataInterval,
|
||||
start_date: NaiveDate,
|
||||
end_date: Option<NaiveDate>,
|
||||
) -> PyResult<EnergyDataResult> {
|
||||
let interval = match interval {
|
||||
PyEnergyDataInterval::Hourly => EnergyDataInterval::Hourly {
|
||||
start_date,
|
||||
end_date: end_date.unwrap_or(start_date),
|
||||
},
|
||||
PyEnergyDataInterval::Daily => EnergyDataInterval::Daily { start_date },
|
||||
PyEnergyDataInterval::Monthly => EnergyDataInterval::Monthly { start_date },
|
||||
};
|
||||
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::get_energy_data,
|
||||
interval
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn get_power_data(
|
||||
&self,
|
||||
interval: PyPowerDataInterval,
|
||||
start_date_time: DateTime<Utc>,
|
||||
end_date_time: DateTime<Utc>,
|
||||
) -> PyResult<PowerDataResult> {
|
||||
let interval = match interval {
|
||||
PyPowerDataInterval::Every5Minutes => PowerDataInterval::Every5Minutes {
|
||||
start_date_time,
|
||||
end_date_time,
|
||||
},
|
||||
PyPowerDataInterval::Hourly => PowerDataInterval::Hourly {
|
||||
start_date_time,
|
||||
end_date_time,
|
||||
},
|
||||
};
|
||||
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugEnergyMonitoringHandler::get_power_data,
|
||||
interval
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
79
agents/tapo/tapo-fork/tapo-py/src/api/plug_handler.rs
Normal file
79
agents/tapo/tapo-fork/tapo-py/src/api/plug_handler.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use tapo::responses::{DeviceInfoPlugResult, DeviceUsageResult};
|
||||
use tapo::{DeviceManagementExt as _, PlugHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::call_handler_method;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "PlugHandler")]
|
||||
pub struct PyPlugHandler {
|
||||
inner: Arc<RwLock<PlugHandler>>,
|
||||
}
|
||||
|
||||
impl PyPlugHandler {
|
||||
pub fn new(handler: PlugHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyPlugHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
PlugHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), PlugHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), PlugHandler::off)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), PlugHandler::device_reset)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoPlugResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), PlugHandler::get_device_info)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PlugHandler::get_device_info_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_device_usage(&self) -> PyResult<DeviceUsageResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), PlugHandler::get_device_usage)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyList};
|
||||
use tapo::responses::DeviceInfoPowerStripResult;
|
||||
use tapo::{DeviceManagementExt as _, Error, Plug, PowerStripEnergyMonitoringHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::api::PyPowerStripPlugEnergyMonitoringHandler;
|
||||
use crate::call_handler_method;
|
||||
use crate::errors::ErrorWrapper;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "PowerStripEnergyMonitoringHandler")]
|
||||
pub struct PyPowerStripEnergyMonitoringHandler {
|
||||
inner: Arc<RwLock<PowerStripEnergyMonitoringHandler>>,
|
||||
}
|
||||
|
||||
impl PyPowerStripEnergyMonitoringHandler {
|
||||
pub fn new(handler: PowerStripEnergyMonitoringHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_identifier(
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
position: Option<u8>,
|
||||
) -> PyResult<Plug> {
|
||||
match (device_id, nickname, position) {
|
||||
(Some(device_id), _, _) => Ok(Plug::ByDeviceId(device_id)),
|
||||
(None, Some(nickname), _) => Ok(Plug::ByNickname(nickname)),
|
||||
(None, None, Some(position)) => Ok(Plug::ByPosition(position)),
|
||||
_ => Err(Into::<ErrorWrapper>::into(Error::Validation {
|
||||
field: "identifier".to_string(),
|
||||
message: "Either a device_id, nickname, or position must be provided".to_string(),
|
||||
})
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyPowerStripEnergyMonitoringHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
PowerStripEnergyMonitoringHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::device_reset,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoPowerStripResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::get_device_info
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::get_device_info_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_child_device_list(&self) -> PyResult<Py<PyList>> {
|
||||
let handler = self.inner.clone();
|
||||
let children = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::get_child_device_list
|
||||
)?;
|
||||
|
||||
Python::attach(|py| {
|
||||
let results = PyList::empty(py);
|
||||
|
||||
for child in children {
|
||||
results.append(child.into_pyobject(py)?)?;
|
||||
}
|
||||
|
||||
Ok(results.into())
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_child_device_list_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::get_child_device_list_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_child_device_component_list_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::get_child_device_component_list_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None, position=None))]
|
||||
pub async fn plug(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
position: Option<u8>,
|
||||
) -> PyResult<PyPowerStripPlugEnergyMonitoringHandler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier =
|
||||
PyPowerStripEnergyMonitoringHandler::parse_identifier(device_id, nickname, position)?;
|
||||
|
||||
let child_handler = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripEnergyMonitoringHandler::plug,
|
||||
identifier
|
||||
)?;
|
||||
Ok(PyPowerStripPlugEnergyMonitoringHandler::new(child_handler))
|
||||
}
|
||||
}
|
||||
143
agents/tapo/tapo-fork/tapo-py/src/api/power_strip_handler.rs
Normal file
143
agents/tapo/tapo-fork/tapo-py/src/api/power_strip_handler.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyList};
|
||||
use tapo::responses::DeviceInfoPowerStripResult;
|
||||
use tapo::{DeviceManagementExt as _, Error, Plug, PowerStripHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::api::PyPowerStripPlugHandler;
|
||||
use crate::call_handler_method;
|
||||
use crate::errors::ErrorWrapper;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "PowerStripHandler")]
|
||||
pub struct PyPowerStripHandler {
|
||||
inner: Arc<RwLock<PowerStripHandler>>,
|
||||
}
|
||||
|
||||
impl PyPowerStripHandler {
|
||||
pub fn new(handler: PowerStripHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_identifier(
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
position: Option<u8>,
|
||||
) -> PyResult<Plug> {
|
||||
match (device_id, nickname, position) {
|
||||
(Some(device_id), _, _) => Ok(Plug::ByDeviceId(device_id)),
|
||||
(None, Some(nickname), _) => Ok(Plug::ByNickname(nickname)),
|
||||
(None, None, Some(position)) => Ok(Plug::ByPosition(position)),
|
||||
_ => Err(Into::<ErrorWrapper>::into(Error::Validation {
|
||||
field: "identifier".to_string(),
|
||||
message: "Either a device_id, nickname, or position must be provided".to_string(),
|
||||
})
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyPowerStripHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
PowerStripHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::device_reset,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoPowerStripResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::get_device_info
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::get_device_info_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_child_device_list(&self) -> PyResult<Py<PyList>> {
|
||||
let handler = self.inner.clone();
|
||||
let children = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::get_child_device_list
|
||||
)?;
|
||||
|
||||
Python::attach(|py| {
|
||||
let results = PyList::empty(py);
|
||||
|
||||
for child in children {
|
||||
results.append(child.into_pyobject(py)?)?;
|
||||
}
|
||||
|
||||
Ok(results.into())
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_child_device_list_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::get_child_device_list_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_child_device_component_list_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::get_child_device_component_list_json
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
#[pyo3(signature = (device_id=None, nickname=None, position=None))]
|
||||
pub async fn plug(
|
||||
&self,
|
||||
device_id: Option<String>,
|
||||
nickname: Option<String>,
|
||||
position: Option<u8>,
|
||||
) -> PyResult<PyPowerStripPlugHandler> {
|
||||
let handler = self.inner.clone();
|
||||
let identifier = PyPowerStripHandler::parse_identifier(device_id, nickname, position)?;
|
||||
|
||||
let child_handler = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
PowerStripHandler::plug,
|
||||
identifier
|
||||
)?;
|
||||
Ok(PyPowerStripPlugHandler::new(child_handler))
|
||||
}
|
||||
}
|
||||
8
agents/tapo/tapo-fork/tapo-py/src/api/py_handler_ext.rs
Normal file
8
agents/tapo/tapo-fork/tapo-py/src/api/py_handler_ext.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use tapo::HandlerExt;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub trait PyHandlerExt: Send + Sync + Sized {
|
||||
fn get_inner_handler(&self) -> Arc<RwLock<impl HandlerExt + 'static>>;
|
||||
}
|
||||
138
agents/tapo/tapo-fork/tapo-py/src/api/rgb_light_strip_handler.rs
Normal file
138
agents/tapo/tapo-fork/tapo-py/src/api/rgb_light_strip_handler.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use tapo::requests::Color;
|
||||
use tapo::responses::{DeviceInfoRgbLightStripResult, DeviceUsageEnergyMonitoringResult};
|
||||
use tapo::{DeviceManagementExt as _, HandlerExt, RgbLightStripHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::api::PyHandlerExt;
|
||||
use crate::call_handler_method;
|
||||
use crate::requests::PyColorLightSetDeviceInfoParams;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "RgbLightStripHandler")]
|
||||
pub struct PyRgbLightStripHandler {
|
||||
pub inner: Arc<RwLock<RgbLightStripHandler>>,
|
||||
}
|
||||
|
||||
impl PyRgbLightStripHandler {
|
||||
pub fn new(handler: RgbLightStripHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PyHandlerExt for PyRgbLightStripHandler {
|
||||
fn get_inner_handler(&self) -> Arc<RwLock<impl HandlerExt + 'static>> {
|
||||
Arc::clone(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyRgbLightStripHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
RgbLightStripHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), RgbLightStripHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), RgbLightStripHandler::off)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::device_reset,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoRgbLightStripResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::get_device_info
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::get_device_info_json,
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_device_usage(&self) -> PyResult<DeviceUsageEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::get_device_usage
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set(&self) -> PyColorLightSetDeviceInfoParams {
|
||||
PyColorLightSetDeviceInfoParams::new()
|
||||
}
|
||||
|
||||
pub async fn set_brightness(&self, brightness: u8) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::set_brightness,
|
||||
brightness
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_color(&self, color: Color) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::set_color,
|
||||
color
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_hue_saturation(&self, hue: u16, saturation: u8) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::set_hue_saturation,
|
||||
hue,
|
||||
saturation
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_color_temperature(&self, color_temperature: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbLightStripHandler::set_color_temperature,
|
||||
color_temperature
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::exceptions::PyTypeError;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use tapo::requests::{Color, LightingEffect, LightingEffectPreset};
|
||||
use tapo::responses::{DeviceInfoRgbicLightStripResult, DeviceUsageEnergyMonitoringResult};
|
||||
use tapo::{DeviceManagementExt as _, HandlerExt, RgbicLightStripHandler};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::api::PyHandlerExt;
|
||||
use crate::call_handler_method;
|
||||
use crate::requests::{PyColorLightSetDeviceInfoParams, PyLightingEffect};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "RgbicLightStripHandler")]
|
||||
pub struct PyRgbicLightStripHandler {
|
||||
pub inner: Arc<RwLock<RgbicLightStripHandler>>,
|
||||
}
|
||||
|
||||
impl PyRgbicLightStripHandler {
|
||||
pub fn new(handler: RgbicLightStripHandler) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new(handler)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PyHandlerExt for PyRgbicLightStripHandler {
|
||||
fn get_inner_handler(&self) -> Arc<RwLock<impl HandlerExt + 'static>> {
|
||||
Arc::clone(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyRgbicLightStripHandler {
|
||||
pub async fn refresh_session(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.write().await.deref_mut(),
|
||||
RgbicLightStripHandler::refresh_session,
|
||||
discard_result
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn on(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), RgbicLightStripHandler::on)
|
||||
}
|
||||
|
||||
pub async fn off(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(handler.read().await.deref(), RgbicLightStripHandler::off)
|
||||
}
|
||||
|
||||
pub async fn device_reboot(&self, delay_s: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::device_reboot,
|
||||
delay_s
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn device_reset(&self) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::device_reset,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info(&self) -> PyResult<DeviceInfoRgbicLightStripResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::get_device_info
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn get_device_info_json(&self) -> PyResult<Py<PyDict>> {
|
||||
let handler = self.inner.clone();
|
||||
let result = call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::get_device_info_json,
|
||||
)?;
|
||||
Python::attach(|py| tapo::python::serde_object_to_py_dict(py, &result))
|
||||
}
|
||||
|
||||
pub async fn get_device_usage(&self) -> PyResult<DeviceUsageEnergyMonitoringResult> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::get_device_usage
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set(&self) -> PyColorLightSetDeviceInfoParams {
|
||||
PyColorLightSetDeviceInfoParams::new()
|
||||
}
|
||||
|
||||
pub async fn set_brightness(&self, brightness: u8) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::set_brightness,
|
||||
brightness
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_color(&self, color: Color) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::set_color,
|
||||
color
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_hue_saturation(&self, hue: u16, saturation: u8) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::set_hue_saturation,
|
||||
hue,
|
||||
saturation
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_color_temperature(&self, color_temperature: u16) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::set_color_temperature,
|
||||
color_temperature
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn set_lighting_effect(&self, lighting_effect: Py<PyAny>) -> PyResult<()> {
|
||||
let handler = self.inner.clone();
|
||||
let lighting_effect = map_lighting_effect(lighting_effect)?;
|
||||
call_handler_method!(
|
||||
handler.read().await.deref(),
|
||||
RgbicLightStripHandler::set_lighting_effect,
|
||||
lighting_effect
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn map_lighting_effect(lighting_effect: Py<PyAny>) -> PyResult<LightingEffect> {
|
||||
if let Some(lighting_effect) =
|
||||
Python::attach(|py| lighting_effect.extract::<LightingEffectPreset>(py).ok())
|
||||
{
|
||||
return Ok(lighting_effect.into());
|
||||
}
|
||||
|
||||
if let Some(lighting_effect) =
|
||||
Python::attach(|py| lighting_effect.extract::<PyLightingEffect>(py).ok())
|
||||
{
|
||||
return Ok(lighting_effect.into());
|
||||
}
|
||||
|
||||
Err(PyErr::new::<PyTypeError, _>(
|
||||
"Invalid lighting effect type. Must be one of `LightingEffect` or `LightingEffectPreset`",
|
||||
))
|
||||
}
|
||||
23
agents/tapo/tapo-fork/tapo-py/src/errors.rs
Normal file
23
agents/tapo/tapo-fork/tapo-py/src/errors.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use pyo3::PyErr;
|
||||
use pyo3::exceptions::PyException;
|
||||
use tapo::Error;
|
||||
|
||||
pub struct ErrorWrapper(pub Error);
|
||||
|
||||
impl From<Error> for ErrorWrapper {
|
||||
fn from(err: Error) -> Self {
|
||||
Self(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for ErrorWrapper {
|
||||
fn from(err: anyhow::Error) -> Self {
|
||||
Self(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ErrorWrapper> for PyErr {
|
||||
fn from(err: ErrorWrapper) -> PyErr {
|
||||
PyException::new_err(format!("{:?}", err.0))
|
||||
}
|
||||
}
|
||||
209
agents/tapo/tapo-fork/tapo-py/src/lib.rs
Normal file
209
agents/tapo/tapo-fork/tapo-py/src/lib.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
mod api;
|
||||
mod errors;
|
||||
mod requests;
|
||||
mod responses;
|
||||
mod runtime;
|
||||
|
||||
use log::LevelFilter;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3_log::{Caching, Logger};
|
||||
|
||||
use tapo::requests::{AlarmRingtone, AlarmVolume, Color, LightingEffectPreset, LightingEffectType};
|
||||
use tapo::responses::{
|
||||
AutoOffStatus, ColorLightState, CurrentPowerResult, DefaultBrightnessState,
|
||||
DefaultColorLightState, DefaultLightState, DefaultPlugState, DefaultPowerType,
|
||||
DefaultRgbLightStripState, DefaultRgbicLightStripState, DefaultStateType,
|
||||
DeviceInfoColorLightResult, DeviceInfoGenericResult, DeviceInfoHubResult,
|
||||
DeviceInfoLightResult, DeviceInfoPlugEnergyMonitoringResult, DeviceInfoPlugResult,
|
||||
DeviceInfoPowerStripResult, DeviceInfoRgbLightStripResult, DeviceInfoRgbicLightStripResult,
|
||||
DeviceUsageEnergyMonitoringResult, DeviceUsageResult, EnergyDataIntervalResult,
|
||||
EnergyDataResult, EnergyUsageResult, KE100Result, OvercurrentStatus, OverheatStatus, PlugState,
|
||||
PowerDataIntervalResult, PowerDataResult, PowerProtectionStatus,
|
||||
PowerStripPlugEnergyMonitoringResult, PowerStripPlugResult, RgbLightStripState,
|
||||
RgbicLightStripState, S200BLog, S200BResult, S200BRotationParams, Status, T31XResult, T100Log,
|
||||
T100Result, T110Log, T110Result, T300Log, T300Result, TemperatureHumidityRecord,
|
||||
TemperatureHumidityRecords, TemperatureUnit, TemperatureUnitKE100, UsageByPeriodResult,
|
||||
WaterLeakStatus,
|
||||
};
|
||||
|
||||
use api::{
|
||||
PyApiClient, PyColorLightHandler, PyDeviceDiscovery, PyDeviceDiscoveryIter, PyDiscoveryResult,
|
||||
PyGenericDeviceHandler, PyHubHandler, PyKE100Handler, PyLightHandler, PyMaybeDiscoveryResult,
|
||||
PyPlugEnergyMonitoringHandler, PyPlugHandler, PyPowerStripEnergyMonitoringHandler,
|
||||
PyPowerStripHandler, PyPowerStripPlugEnergyMonitoringHandler, PyPowerStripPlugHandler,
|
||||
PyRgbLightStripHandler, PyRgbicLightStripHandler, PyT31XHandler, PyT100Handler, PyT110Handler,
|
||||
PyT300Handler,
|
||||
};
|
||||
use requests::{
|
||||
PyAlarmDuration, PyColorLightSetDeviceInfoParams, PyEnergyDataInterval, PyLightingEffect,
|
||||
PyPowerDataInterval,
|
||||
};
|
||||
use responses::{
|
||||
TriggerLogsS200BResult, TriggerLogsT100Result, TriggerLogsT110Result, TriggerLogsT300Result,
|
||||
};
|
||||
|
||||
#[pymodule]
|
||||
#[pyo3(name = "tapo")]
|
||||
fn tapo_py(py: Python, module: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
Logger::new(py, Caching::LoggersAndLevels)?
|
||||
.filter(LevelFilter::Trace)
|
||||
.install()
|
||||
.expect("Failed to install the logger");
|
||||
|
||||
let requests = PyModule::new(py, "tapo.requests")?;
|
||||
let responses = PyModule::new(py, "tapo.responses")?;
|
||||
|
||||
register_handlers(module)?;
|
||||
register_requests(&requests)?;
|
||||
register_responses(&responses)?;
|
||||
register_responses_hub(&responses)?;
|
||||
register_responses_power_strip(&responses)?;
|
||||
|
||||
module.add_submodule(&requests)?;
|
||||
module.add_submodule(&responses)?;
|
||||
|
||||
let sys = py.import("sys")?;
|
||||
let modules = sys.getattr("modules")?;
|
||||
modules.set_item("tapo.requests", requests)?;
|
||||
modules.set_item("tapo.responses", responses)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_requests(module: &Bound<'_, PyModule>) -> Result<(), PyErr> {
|
||||
module.add_class::<Color>()?;
|
||||
module.add_class::<PyLightingEffect>()?;
|
||||
module.add_class::<LightingEffectPreset>()?;
|
||||
module.add_class::<LightingEffectType>()?;
|
||||
module.add_class::<PyColorLightSetDeviceInfoParams>()?;
|
||||
module.add_class::<PyEnergyDataInterval>()?;
|
||||
module.add_class::<PyPowerDataInterval>()?;
|
||||
|
||||
// hub requests
|
||||
module.add_class::<AlarmRingtone>()?;
|
||||
module.add_class::<AlarmVolume>()?;
|
||||
module.add_class::<PyAlarmDuration>()?;
|
||||
module.add_class::<TemperatureUnitKE100>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_handlers(module: &Bound<'_, PyModule>) -> Result<(), PyErr> {
|
||||
module.add_class::<PyApiClient>()?;
|
||||
module.add_class::<PyColorLightHandler>()?;
|
||||
module.add_class::<PyGenericDeviceHandler>()?;
|
||||
module.add_class::<PyLightHandler>()?;
|
||||
module.add_class::<PyPlugEnergyMonitoringHandler>()?;
|
||||
module.add_class::<PyPlugHandler>()?;
|
||||
module.add_class::<PyRgbLightStripHandler>()?;
|
||||
module.add_class::<PyRgbicLightStripHandler>()?;
|
||||
|
||||
module.add_class::<PyHubHandler>()?;
|
||||
module.add_class::<PyKE100Handler>()?;
|
||||
module.add_class::<PyT100Handler>()?;
|
||||
module.add_class::<PyT110Handler>()?;
|
||||
module.add_class::<PyT300Handler>()?;
|
||||
module.add_class::<PyT31XHandler>()?;
|
||||
|
||||
module.add_class::<PyPowerStripHandler>()?;
|
||||
module.add_class::<PyPowerStripEnergyMonitoringHandler>()?;
|
||||
module.add_class::<PyPowerStripPlugHandler>()?;
|
||||
module.add_class::<PyPowerStripPlugEnergyMonitoringHandler>()?;
|
||||
|
||||
module.add_class::<PyDeviceDiscovery>()?;
|
||||
module.add_class::<PyDeviceDiscoveryIter>()?;
|
||||
module.add_class::<PyDiscoveryResult>()?;
|
||||
module.add_class::<PyMaybeDiscoveryResult>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_responses(module: &Bound<'_, PyModule>) -> Result<(), PyErr> {
|
||||
module.add_class::<CurrentPowerResult>()?;
|
||||
module.add_class::<DefaultBrightnessState>()?;
|
||||
module.add_class::<DefaultPowerType>()?;
|
||||
module.add_class::<DefaultStateType>()?;
|
||||
module.add_class::<DeviceUsageEnergyMonitoringResult>()?;
|
||||
module.add_class::<DeviceUsageResult>()?;
|
||||
module.add_class::<EnergyDataIntervalResult>()?;
|
||||
module.add_class::<EnergyDataResult>()?;
|
||||
module.add_class::<EnergyUsageResult>()?;
|
||||
module.add_class::<OvercurrentStatus>()?;
|
||||
module.add_class::<OverheatStatus>()?;
|
||||
module.add_class::<PowerDataIntervalResult>()?;
|
||||
module.add_class::<PowerDataResult>()?;
|
||||
module.add_class::<PowerProtectionStatus>()?;
|
||||
module.add_class::<UsageByPeriodResult>()?;
|
||||
|
||||
// device info: generic
|
||||
module.add_class::<DeviceInfoGenericResult>()?;
|
||||
|
||||
// device info: light
|
||||
module.add_class::<DeviceInfoLightResult>()?;
|
||||
module.add_class::<DefaultLightState>()?;
|
||||
|
||||
// device info: color light
|
||||
module.add_class::<DeviceInfoColorLightResult>()?;
|
||||
module.add_class::<DefaultColorLightState>()?;
|
||||
module.add_class::<ColorLightState>()?;
|
||||
|
||||
// device info: rgb light strip
|
||||
module.add_class::<DeviceInfoRgbLightStripResult>()?;
|
||||
module.add_class::<DefaultRgbLightStripState>()?;
|
||||
module.add_class::<RgbLightStripState>()?;
|
||||
|
||||
// device info: rgbic light strip
|
||||
module.add_class::<DeviceInfoRgbicLightStripResult>()?;
|
||||
module.add_class::<DefaultRgbicLightStripState>()?;
|
||||
module.add_class::<RgbicLightStripState>()?;
|
||||
module.add_class::<PyLightingEffect>()?;
|
||||
module.add_class::<LightingEffectType>()?;
|
||||
|
||||
// device info: plugs
|
||||
module.add_class::<DefaultPlugState>()?;
|
||||
module.add_class::<DeviceInfoPlugEnergyMonitoringResult>()?;
|
||||
module.add_class::<DeviceInfoPlugResult>()?;
|
||||
module.add_class::<PlugState>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_responses_hub(module: &Bound<'_, PyModule>) -> Result<(), PyErr> {
|
||||
module.add_class::<DeviceInfoHubResult>()?;
|
||||
module.add_class::<KE100Result>()?;
|
||||
module.add_class::<S200BResult>()?;
|
||||
module.add_class::<T100Result>()?;
|
||||
module.add_class::<T110Result>()?;
|
||||
module.add_class::<T300Result>()?;
|
||||
module.add_class::<T31XResult>()?;
|
||||
|
||||
// child devices
|
||||
module.add_class::<S200BLog>()?;
|
||||
module.add_class::<S200BRotationParams>()?;
|
||||
module.add_class::<Status>()?;
|
||||
module.add_class::<T100Log>()?;
|
||||
module.add_class::<T110Log>()?;
|
||||
module.add_class::<T300Log>()?;
|
||||
module.add_class::<TemperatureHumidityRecord>()?;
|
||||
module.add_class::<TemperatureHumidityRecords>()?;
|
||||
module.add_class::<TemperatureUnit>()?;
|
||||
module.add_class::<TemperatureUnitKE100>()?;
|
||||
module.add_class::<TriggerLogsS200BResult>()?;
|
||||
module.add_class::<TriggerLogsT100Result>()?;
|
||||
module.add_class::<TriggerLogsT110Result>()?;
|
||||
module.add_class::<TriggerLogsT300Result>()?;
|
||||
module.add_class::<WaterLeakStatus>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_responses_power_strip(module: &Bound<'_, PyModule>) -> Result<(), PyErr> {
|
||||
module.add_class::<DeviceInfoPowerStripResult>()?;
|
||||
|
||||
// child devices
|
||||
module.add_class::<AutoOffStatus>()?;
|
||||
module.add_class::<PowerStripPlugResult>()?;
|
||||
module.add_class::<PowerStripPlugEnergyMonitoringResult>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
9
agents/tapo/tapo-fork/tapo-py/src/requests.rs
Normal file
9
agents/tapo/tapo-fork/tapo-py/src/requests.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
mod energy_data_interval;
|
||||
mod play_alarm;
|
||||
mod power_data_interval;
|
||||
mod set_device_info;
|
||||
|
||||
pub use energy_data_interval::*;
|
||||
pub use play_alarm::*;
|
||||
pub use power_data_interval::*;
|
||||
pub use set_device_info::*;
|
||||
@@ -0,0 +1,9 @@
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[pyclass(name = "EnergyDataInterval", eq, eq_int)]
|
||||
pub enum PyEnergyDataInterval {
|
||||
Hourly,
|
||||
Daily,
|
||||
Monthly,
|
||||
}
|
||||
9
agents/tapo/tapo-fork/tapo-py/src/requests/play_alarm.rs
Normal file
9
agents/tapo/tapo-fork/tapo-py/src/requests/play_alarm.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[pyclass(name = "AlarmDuration", eq)]
|
||||
pub enum PyAlarmDuration {
|
||||
Continuous,
|
||||
Once,
|
||||
Seconds,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[pyclass(name = "PowerDataInterval", eq, eq_int)]
|
||||
pub enum PyPowerDataInterval {
|
||||
Every5Minutes,
|
||||
Hourly,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod color_light;
|
||||
mod lighting_effect;
|
||||
|
||||
pub use color_light::*;
|
||||
pub use lighting_effect::*;
|
||||
@@ -0,0 +1,108 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use tapo::requests::{Color, ColorLightSetDeviceInfoParams};
|
||||
|
||||
use crate::api::{
|
||||
PyColorLightHandler, PyHandlerExt, PyRgbLightStripHandler, PyRgbicLightStripHandler,
|
||||
};
|
||||
use crate::errors::ErrorWrapper;
|
||||
use crate::runtime::tokio;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "LightSetDeviceInfoParams")]
|
||||
pub struct PyColorLightSetDeviceInfoParams {
|
||||
params: ColorLightSetDeviceInfoParams,
|
||||
}
|
||||
|
||||
impl PyColorLightSetDeviceInfoParams {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
params: ColorLightSetDeviceInfoParams::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn _send_to_inner_handler(&self, handler: impl PyHandlerExt) -> PyResult<()> {
|
||||
let params = self.params.clone();
|
||||
let handler = handler.get_inner_handler();
|
||||
|
||||
tokio()
|
||||
.spawn(async move {
|
||||
let handler_lock = handler.read().await;
|
||||
|
||||
params
|
||||
.send(handler_lock.deref())
|
||||
.await
|
||||
.map_err(ErrorWrapper)?;
|
||||
|
||||
Ok::<_, ErrorWrapper>(())
|
||||
})
|
||||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err(ErrorWrapper::from)??;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
}
|
||||
}
|
||||
|
||||
async fn send(&self, handler: Py<PyAny>) -> PyResult<()> {
|
||||
if let Some(handler) = Python::attach(|py| handler.extract::<PyColorLightHandler>(py).ok())
|
||||
{
|
||||
return self._send_to_inner_handler(handler).await;
|
||||
}
|
||||
|
||||
if let Some(handler) =
|
||||
Python::attach(|py| handler.extract::<PyRgbLightStripHandler>(py).ok())
|
||||
{
|
||||
return self._send_to_inner_handler(handler).await;
|
||||
}
|
||||
|
||||
if let Some(handler) =
|
||||
Python::attach(|py| handler.extract::<PyRgbicLightStripHandler>(py).ok())
|
||||
{
|
||||
return self._send_to_inner_handler(handler).await;
|
||||
}
|
||||
|
||||
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
|
||||
"Invalid handler type. Must be one of `PyColorLightHandler`, `PyRgbLightStripHandler` or `PyRgbicLightStripHandler`",
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
use pyo3::prelude::*;
|
||||
use tapo::requests::{LightingEffect, LightingEffectType};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[pyclass(name = "LightingEffect")]
|
||||
pub struct PyLightingEffect {
|
||||
inner: LightingEffect,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyLightingEffect {
|
||||
#[new]
|
||||
fn new(
|
||||
name: String,
|
||||
r#type: LightingEffectType,
|
||||
is_custom: bool,
|
||||
enabled: bool,
|
||||
brightness: u8,
|
||||
display_colors: Vec<[u16; 3]>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: LightingEffect::new(
|
||||
name,
|
||||
r#type,
|
||||
is_custom,
|
||||
enabled,
|
||||
brightness,
|
||||
display_colors,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_brightness(mut slf: PyRefMut<'_, Self>, brightness: u8) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.brightness = brightness;
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_is_custom(mut slf: PyRefMut<'_, Self>, is_custom: bool) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.is_custom = is_custom;
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_display_colors(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
display_colors: Vec<[u16; 3]>,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.display_colors = display_colors;
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_enabled(mut slf: PyRefMut<'_, Self>, enabled: bool) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.enabled = enabled;
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_id(mut slf: PyRefMut<'_, Self>, id: String) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.id = id;
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_name(mut slf: PyRefMut<'_, Self>, name: String) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.name = name;
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_type(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
r#type: LightingEffectType,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.r#type = r#type;
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_backgrounds(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
backgrounds: Vec<[u16; 3]>,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.backgrounds = Some(backgrounds);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_brightness_range(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
brightness_range: [u8; 2],
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.brightness_range = Some(brightness_range.to_vec());
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_direction(mut slf: PyRefMut<'_, Self>, direction: u8) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.direction = Some(direction);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_duration(mut slf: PyRefMut<'_, Self>, duration: u64) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.duration = Some(duration);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_expansion_strategy(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
expansion_strategy: u8,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.expansion_strategy = Some(expansion_strategy);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_fade_off(mut slf: PyRefMut<'_, Self>, fade_off: u16) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.fade_off = Some(fade_off);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_hue_range(mut slf: PyRefMut<'_, Self>, hue_range: [u16; 2]) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.hue_range = Some(hue_range);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_init_states(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
init_states: Vec<[u16; 3]>,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.init_states = Some(init_states);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_random_seed(mut slf: PyRefMut<'_, Self>, random_seed: u64) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.random_seed = Some(random_seed);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_repeat_times(mut slf: PyRefMut<'_, Self>, repeat_times: u8) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.repeat_times = Some(repeat_times);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_run_time(mut slf: PyRefMut<'_, Self>, run_time: u64) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.run_time = Some(run_time);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_saturation_range(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
saturation_range: [u8; 2],
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.saturation_range = Some(saturation_range);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_segment_length(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
segment_length: u8,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.segment_length = Some(segment_length);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_segments(mut slf: PyRefMut<'_, Self>, segments: Vec<u8>) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.segments = Some(segments);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_sequence(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
sequence: Vec<[u16; 3]>,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.sequence = Some(sequence);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_spread(mut slf: PyRefMut<'_, Self>, spread: u8) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.spread = Some(spread);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_transition(mut slf: PyRefMut<'_, Self>, transition: u16) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.transition = Some(transition);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_transition_range(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
transition_range: [u16; 2],
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.transition_range = Some(transition_range);
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn with_transition_sequence(
|
||||
mut slf: PyRefMut<'_, Self>,
|
||||
transition_sequence: Vec<u16>,
|
||||
) -> PyRefMut<'_, Self> {
|
||||
(*slf).inner.transition_sequence = Some(transition_sequence);
|
||||
slf
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PyLightingEffect> for LightingEffect {
|
||||
fn from(effect: PyLightingEffect) -> Self {
|
||||
effect.inner
|
||||
}
|
||||
}
|
||||
3
agents/tapo/tapo-fork/tapo-py/src/responses.rs
Normal file
3
agents/tapo/tapo-fork/tapo-py/src/responses.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod child_device_list_hub_result;
|
||||
|
||||
pub use child_device_list_hub_result::*;
|
||||
@@ -0,0 +1,9 @@
|
||||
mod s200b_result;
|
||||
mod t100_result;
|
||||
mod t110_result;
|
||||
mod t300_result;
|
||||
|
||||
pub use s200b_result::*;
|
||||
pub use t100_result::*;
|
||||
pub use t110_result::*;
|
||||
pub use t300_result::*;
|
||||
@@ -0,0 +1,32 @@
|
||||
use pyo3::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tapo::responses::{S200BLog, TriggerLogsResult};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[pyclass(get_all)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct TriggerLogsS200BResult {
|
||||
start_id: u64,
|
||||
sum: u64,
|
||||
logs: Vec<S200BLog>,
|
||||
}
|
||||
|
||||
impl From<TriggerLogsResult<S200BLog>> for TriggerLogsS200BResult {
|
||||
fn from(result: TriggerLogsResult<S200BLog>) -> Self {
|
||||
Self {
|
||||
start_id: result.start_id,
|
||||
sum: result.sum,
|
||||
logs: result.logs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyo3::pymethods]
|
||||
impl TriggerLogsS200BResult {
|
||||
pub fn to_dict(&self, py: pyo3::Python) -> pyo3::PyResult<pyo3::Py<pyo3::types::PyDict>> {
|
||||
let value = serde_json::to_value(self)
|
||||
.map_err(|e| pyo3::exceptions::PyException::new_err(e.to_string()))?;
|
||||
|
||||
tapo::python::serde_object_to_py_dict(py, &value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
use pyo3::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tapo::responses::{T100Log, TriggerLogsResult};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[pyclass(get_all)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct TriggerLogsT100Result {
|
||||
start_id: u64,
|
||||
sum: u64,
|
||||
logs: Vec<T100Log>,
|
||||
}
|
||||
|
||||
impl From<TriggerLogsResult<T100Log>> for TriggerLogsT100Result {
|
||||
fn from(result: TriggerLogsResult<T100Log>) -> Self {
|
||||
Self {
|
||||
start_id: result.start_id,
|
||||
sum: result.sum,
|
||||
logs: result.logs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyo3::pymethods]
|
||||
impl TriggerLogsT100Result {
|
||||
pub fn to_dict(&self, py: pyo3::Python) -> pyo3::PyResult<pyo3::Py<pyo3::types::PyDict>> {
|
||||
let value = serde_json::to_value(self)
|
||||
.map_err(|e| pyo3::exceptions::PyException::new_err(e.to_string()))?;
|
||||
|
||||
tapo::python::serde_object_to_py_dict(py, &value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
use pyo3::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tapo::responses::{T110Log, TriggerLogsResult};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[pyclass(get_all)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct TriggerLogsT110Result {
|
||||
start_id: u64,
|
||||
sum: u64,
|
||||
logs: Vec<T110Log>,
|
||||
}
|
||||
|
||||
impl From<TriggerLogsResult<T110Log>> for TriggerLogsT110Result {
|
||||
fn from(result: TriggerLogsResult<T110Log>) -> Self {
|
||||
Self {
|
||||
start_id: result.start_id,
|
||||
sum: result.sum,
|
||||
logs: result.logs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyo3::pymethods]
|
||||
impl TriggerLogsT110Result {
|
||||
pub fn to_dict(&self, py: pyo3::Python) -> pyo3::PyResult<pyo3::Py<pyo3::types::PyDict>> {
|
||||
let value = serde_json::to_value(self)
|
||||
.map_err(|e| pyo3::exceptions::PyException::new_err(e.to_string()))?;
|
||||
|
||||
tapo::python::serde_object_to_py_dict(py, &value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
use pyo3::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tapo::responses::{T300Log, TriggerLogsResult};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[pyclass(get_all)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct TriggerLogsT300Result {
|
||||
start_id: u64,
|
||||
sum: u64,
|
||||
logs: Vec<T300Log>,
|
||||
}
|
||||
|
||||
impl From<TriggerLogsResult<T300Log>> for TriggerLogsT300Result {
|
||||
fn from(result: TriggerLogsResult<T300Log>) -> Self {
|
||||
Self {
|
||||
start_id: result.start_id,
|
||||
sum: result.sum,
|
||||
logs: result.logs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyo3::pymethods]
|
||||
impl TriggerLogsT300Result {
|
||||
pub fn to_dict(&self, py: pyo3::Python) -> pyo3::PyResult<pyo3::Py<pyo3::types::PyDict>> {
|
||||
let value = serde_json::to_value(self)
|
||||
.map_err(|e| pyo3::exceptions::PyException::new_err(e.to_string()))?;
|
||||
|
||||
tapo::python::serde_object_to_py_dict(py, &value)
|
||||
}
|
||||
}
|
||||
60
agents/tapo/tapo-fork/tapo-py/src/runtime.rs
Normal file
60
agents/tapo/tapo-fork/tapo-py/src/runtime.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
pub fn tokio() -> &'static tokio::runtime::Runtime {
|
||||
use std::sync::OnceLock;
|
||||
use tokio::runtime::Runtime;
|
||||
static RT: std::sync::OnceLock<Runtime> = OnceLock::new();
|
||||
RT.get_or_init(|| Runtime::new().expect("Failed to create tokio runtime"))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! call_handler_constructor {
|
||||
($self:ident, $constructor:path, $($params:expr),*) => {{
|
||||
let client = $self.client.clone();
|
||||
let handler = $crate::runtime::tokio()
|
||||
.spawn(async move {
|
||||
$constructor(client, $($params),*)
|
||||
.await
|
||||
.map_err($crate::errors::ErrorWrapper)
|
||||
})
|
||||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err($crate::errors::ErrorWrapper::from)??;
|
||||
|
||||
handler
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! call_handler_method {
|
||||
($handler:expr, $method:path) => (call_handler_method!($handler, $method,));
|
||||
($handler:expr, $method:path, discard_result) => (call_handler_method!($handler, $method, discard_result,));
|
||||
($handler:expr, $method:path, $($param:expr),*) => {{
|
||||
let result = $crate::runtime::tokio()
|
||||
.spawn(async move {
|
||||
let result = $method($handler, $($param),*)
|
||||
.await
|
||||
.map_err($crate::errors::ErrorWrapper)?;
|
||||
|
||||
Ok::<_, $crate::errors::ErrorWrapper>(result)
|
||||
})
|
||||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err($crate::errors::ErrorWrapper::from)??;
|
||||
|
||||
Ok::<_, PyErr>(result)
|
||||
}};
|
||||
($handler:expr, $method:path, discard_result, $($param:expr),*) => {{
|
||||
let result = $crate::runtime::tokio()
|
||||
.spawn(async move {
|
||||
$method($handler, $($param),*)
|
||||
.await
|
||||
.map_err($crate::errors::ErrorWrapper)?;
|
||||
|
||||
Ok::<_, $crate::errors::ErrorWrapper>(())
|
||||
})
|
||||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err($crate::errors::ErrorWrapper::from)??;
|
||||
|
||||
Ok(result)
|
||||
}};
|
||||
}
|
||||
Reference in New Issue
Block a user