macro_rules! tapo_handler {
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
on_off,
device_usage = $device_usage:ty,
device_management,
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
tapo_handler!(@on_off $name);
tapo_handler!(@device_usage $name, $device_usage);
tapo_handler!(@device_management $name);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
on_off,
device_usage = $device_usage:ty,
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
tapo_handler!(@on_off $name);
tapo_handler!(@device_usage $name, $device_usage);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
on_off,
device_management,
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
tapo_handler!(@on_off $name);
tapo_handler!(@device_management $name);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
on_off,
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
tapo_handler!(@on_off $name);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
device_usage = $device_usage:ty,
device_management,
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
tapo_handler!(@device_usage $name, $device_usage);
tapo_handler!(@device_management $name);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
device_usage = $device_usage:ty,
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
tapo_handler!(@device_usage $name, $device_usage);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
device_management,
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
tapo_handler!(@device_management $name);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
ip_address,
device_management,
) => {
tapo_handler!(@base_with_ip $(#[$meta])* $name($device_info));
tapo_handler!(@device_management $name);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
ip_address,
) => {
tapo_handler!(@base_with_ip $(#[$meta])* $name($device_info));
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
) => {
tapo_handler!(@base $(#[$meta])* $name($device_info));
};
(@base_with_ip $(#[$meta:meta])* $name:ident($device_info:ty)) => {
$(#[$meta])*
#[derive(Debug)]
pub struct $name {
client: std::sync::Arc<tokio::sync::RwLock<crate::api::ApiClient>>,
ip_address: String,
}
impl $name {
pub(crate) fn new(
client: std::sync::Arc<tokio::sync::RwLock<crate::api::ApiClient>>,
ip_address: String,
) -> Self {
Self { client, ip_address }
}
}
tapo_handler!(@methods $name($device_info));
tapo_handler!(@handler_ext $name);
};
(@base $(#[$meta:meta])* $name:ident($device_info:ty)) => {
$(#[$meta])*
#[derive(Debug)]
pub struct $name {
client: std::sync::Arc<tokio::sync::RwLock<crate::api::ApiClient>>,
}
impl $name {
pub(crate) fn new(
client: std::sync::Arc<tokio::sync::RwLock<crate::api::ApiClient>>,
) -> Self {
Self { client }
}
}
tapo_handler!(@methods $name($device_info));
tapo_handler!(@handler_ext $name);
};
(@methods $name:ident($device_info:ty)) => {
impl $name {
pub async fn refresh_session(&mut self) -> Result<&mut Self, crate::error::Error> {
self.client.write().await.refresh_session().await?;
Ok(self)
}
#[doc = concat!(
"Returns *device info* as [`", stringify!($device_info), "`].\n",
"It is not guaranteed to contain all the properties returned from the Tapo API.\n",
"If the deserialization fails, or if a property that you care about it's not present, ",
"try [`", stringify!($name), "::get_device_info_json`].",
)]
pub async fn get_device_info(&self) -> Result<$device_info, crate::error::Error> {
self.client.read().await.get_device_info().await
}
#[cfg(feature = "debug")]
pub async fn get_device_info_json(
&self,
) -> Result<serde_json::Value, crate::error::Error> {
self.client.read().await.get_device_info().await
}
#[cfg(feature = "debug")]
pub async fn get_component_list(
&self,
) -> Result<Vec<crate::responses::Component>, crate::error::Error> {
self.client.read().await.get_component_list().await
}
}
};
(@handler_ext $name:ident) => {
#[async_trait::async_trait]
impl crate::api::HandlerExt for $name {
async fn get_client(
&self,
) -> tokio::sync::RwLockReadGuard<'_, dyn crate::api::ApiClientExt> {
tokio::sync::RwLockReadGuard::map(
self.client.read().await,
|client: &crate::api::ApiClient| -> &dyn crate::api::ApiClientExt { client },
)
}
}
};
(@on_off $name:ident) => {
impl $name {
pub async fn on(&self) -> Result<(), crate::error::Error> {
let json = serde_json::to_value(
crate::requests::GenericSetDeviceInfoParams::device_on(true)?,
)?;
crate::api::ApiClientExt::set_device_info(&*self.client.read().await, json).await
}
pub async fn off(&self) -> Result<(), crate::error::Error> {
let json = serde_json::to_value(
crate::requests::GenericSetDeviceInfoParams::device_on(false)?,
)?;
crate::api::ApiClientExt::set_device_info(&*self.client.read().await, json).await
}
}
};
(@device_usage $name:ident, $device_usage:ty) => {
impl $name {
#[doc = concat!("Returns *device usage* as [`", stringify!($device_usage), "`].")]
pub async fn get_device_usage(&self) -> Result<$device_usage, crate::error::Error> {
self.client.read().await.get_device_usage().await
}
}
};
(@device_management $name:ident) => {
impl $name {
pub async fn device_reboot(&self, delay_s: u16) -> Result<(), crate::error::Error> {
crate::api::ApiClientExt::device_reboot(&*self.client.read().await, delay_s).await
}
pub async fn device_reset(&self) -> Result<(), crate::error::Error> {
crate::api::ApiClientExt::device_reset(&*self.client.read().await).await
}
}
};
}
macro_rules! tapo_child_handler {
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
on_off,
) => {
tapo_child_handler!(@base $(#[$meta])* $name($device_info));
tapo_child_handler!(@on_off $name);
};
(
$(#[$meta:meta])*
$name:ident($device_info:ty),
) => {
tapo_child_handler!(@base $(#[$meta])* $name($device_info));
};
(@base $(#[$meta:meta])* $name:ident($device_info:ty)) => {
$(#[$meta])*
pub struct $name {
client: std::sync::Arc<tokio::sync::RwLock<crate::api::ApiClient>>,
device_id: String,
}
impl $name {
pub(crate) fn new(
client: std::sync::Arc<tokio::sync::RwLock<crate::api::ApiClient>>,
device_id: String,
) -> Self {
Self { client, device_id }
}
#[doc = concat!(
"Returns *device info* as [`", stringify!($device_info), "`].\n",
"It is not guaranteed to contain all the properties returned from the Tapo API.\n",
"If the deserialization fails, or if a property that you care about it's not present, ",
"try [`", stringify!($name), "::get_device_info_json`].",
)]
pub async fn get_device_info(&self) -> Result<$device_info, crate::error::Error> {
let request = crate::requests::TapoRequest::GetDeviceInfo(
crate::requests::TapoParams::new(crate::requests::EmptyParams),
);
self.client
.read()
.await
.control_child::<$device_info>(self.device_id.clone(), request)
.await?
.ok_or_else(|| {
crate::error::Error::Tapo(crate::error::TapoResponseError::EmptyResult)
})
.map(|result| crate::responses::DecodableResultExt::decode(result))?
}
#[cfg(feature = "debug")]
pub async fn get_device_info_json(
&self,
) -> Result<serde_json::Value, crate::error::Error> {
let request = crate::requests::TapoRequest::GetDeviceInfo(
crate::requests::TapoParams::new(crate::requests::EmptyParams),
);
self.client
.read()
.await
.control_child::<serde_json::Value>(self.device_id.clone(), request)
.await?
.ok_or_else(|| {
crate::error::Error::Tapo(crate::error::TapoResponseError::EmptyResult)
})
}
#[cfg(feature = "debug")]
pub async fn get_component_list(
&self,
) -> Result<Vec<crate::responses::Component>, crate::error::Error> {
let request = crate::requests::TapoRequest::ComponentNegotiation(
crate::requests::TapoParams::new(crate::requests::EmptyParams),
);
let result: crate::responses::ComponentListResult = self
.client
.read()
.await
.control_child(self.device_id.clone(), request)
.await?
.ok_or_else(|| {
crate::error::Error::Tapo(crate::error::TapoResponseError::EmptyResult)
})?;
Ok(result.component_list)
}
}
};
(@on_off $name:ident) => {
impl $name {
pub async fn on(&self) -> Result<(), crate::error::Error> {
let json = serde_json::to_value(
crate::requests::GenericSetDeviceInfoParams::device_on(true)?,
)?;
let request = crate::requests::TapoRequest::SetDeviceInfo(
Box::new(crate::requests::TapoParams::new(json)),
);
self.client
.read()
.await
.control_child::<serde_json::Value>(self.device_id.clone(), request)
.await?;
Ok(())
}
pub async fn off(&self) -> Result<(), crate::error::Error> {
let json = serde_json::to_value(
crate::requests::GenericSetDeviceInfoParams::device_on(false)?,
)?;
let request = crate::requests::TapoRequest::SetDeviceInfo(
Box::new(crate::requests::TapoParams::new(json)),
);
self.client
.read()
.await
.control_child::<serde_json::Value>(self.device_id.clone(), request)
.await?;
Ok(())
}
}
};
}