use anyhow::Context;
use crate::responses::{
DecodableResultExt, DeviceInfoBasicResult, DeviceInfoCameraResult, DeviceInfoColorLightResult,
DeviceInfoHubResult, DeviceInfoLightResult, DeviceInfoPlugEnergyMonitoringResult,
DeviceInfoPlugResult, DeviceInfoPowerStripResult, DeviceInfoRgbLightStripResult,
DeviceInfoRgbicLightStripResult,
};
use crate::{
ApiClient, CameraPtzHandler, ColorLightHandler, Error, HubHandler, LightHandler,
PlugEnergyMonitoringHandler, PlugHandler, PowerStripEnergyMonitoringHandler, PowerStripHandler,
RgbLightStripHandler, RgbicLightStripHandler,
};
use crate::api::protocol::DeviceFamily;
use super::DeviceType;
use super::discovery_raw_result::DiscoveryRawResult;
#[derive(Debug)]
pub enum DiscoveryResult {
Light {
device_info: Box<DeviceInfoLightResult>,
handler: LightHandler,
},
ColorLight {
device_info: Box<DeviceInfoColorLightResult>,
handler: ColorLightHandler,
},
RgbLightStrip {
device_info: Box<DeviceInfoRgbLightStripResult>,
handler: RgbLightStripHandler,
},
RgbicLightStrip {
device_info: Box<DeviceInfoRgbicLightStripResult>,
handler: RgbicLightStripHandler,
},
Plug {
device_info: Box<DeviceInfoPlugResult>,
handler: PlugHandler,
},
PlugEnergyMonitoring {
device_info: Box<DeviceInfoPlugEnergyMonitoringResult>,
handler: PlugEnergyMonitoringHandler,
},
PowerStrip {
device_info: Box<DeviceInfoPowerStripResult>,
handler: PowerStripHandler,
},
PowerStripEnergyMonitoring {
device_info: Box<DeviceInfoPowerStripResult>,
handler: PowerStripEnergyMonitoringHandler,
},
Hub {
device_info: Box<DeviceInfoHubResult>,
handler: HubHandler,
},
CameraPtz {
device_info: Box<DeviceInfoCameraResult>,
handler: CameraPtzHandler,
ip: String,
},
Other {
device_info: Box<DeviceInfoBasicResult>,
ip: String,
},
}
macro_rules! map_device_model {
($discovery_result_type:ident, $device_info_type:ident, $handler_type:ident, $device_info:expr, $client:expr) => {{
DiscoveryResult::$discovery_result_type {
device_info: Box::new(
serde_json::from_value::<$device_info_type>($device_info)?.decode()?,
),
handler: $handler_type::new($client.clone()),
}
}};
}
impl DiscoveryResult {
pub(crate) async fn new(
mut client: ApiClient,
raw_result: DiscoveryRawResult,
) -> Result<Self, Error> {
let device_family = raw_result.device_family();
let auth_protocol = raw_result.auth_protocol();
client
.login(raw_result.ip.to_string(), device_family, auth_protocol)
.await?;
let device_info: serde_json::Value = client.get_device_info().await?;
let client = std::sync::Arc::new(tokio::sync::RwLock::new(client));
let obj = device_info
.as_object()
.context("Expected device_info result to be an object")?;
let model = obj
.get("model")
.or_else(|| obj.get("device_model"))
.and_then(|v| v.as_str())
.context("Expected device_info to contain the model field")?;
let device_type = DeviceType::from_model(model);
let result = match device_type {
DeviceType::Light => {
map_device_model!(
Light,
DeviceInfoLightResult,
LightHandler,
device_info,
client
)
}
DeviceType::ColorLight => {
map_device_model!(
ColorLight,
DeviceInfoColorLightResult,
ColorLightHandler,
device_info,
client
)
}
DeviceType::RgbLightStrip => {
map_device_model!(
RgbLightStrip,
DeviceInfoRgbLightStripResult,
RgbLightStripHandler,
device_info,
client
)
}
DeviceType::RgbicLightStrip => {
map_device_model!(
RgbicLightStrip,
DeviceInfoRgbicLightStripResult,
RgbicLightStripHandler,
device_info,
client
)
}
DeviceType::Plug => {
map_device_model!(Plug, DeviceInfoPlugResult, PlugHandler, device_info, client)
}
DeviceType::PlugEnergyMonitoring => {
map_device_model!(
PlugEnergyMonitoring,
DeviceInfoPlugEnergyMonitoringResult,
PlugEnergyMonitoringHandler,
device_info,
client
)
}
DeviceType::PowerStrip => {
map_device_model!(
PowerStrip,
DeviceInfoPowerStripResult,
PowerStripHandler,
device_info,
client
)
}
DeviceType::PowerStripEnergyMonitoring => {
map_device_model!(
PowerStripEnergyMonitoring,
DeviceInfoPowerStripResult,
PowerStripEnergyMonitoringHandler,
device_info,
client
)
}
DeviceType::Hub => {
map_device_model!(Hub, DeviceInfoHubResult, HubHandler, device_info, client)
}
DeviceType::CameraPtz => DiscoveryResult::CameraPtz {
device_info: Box::new(serde_json::from_value::<DeviceInfoCameraResult>(
device_info,
)?),
handler: CameraPtzHandler::new(client.clone(), raw_result.ip.to_string()),
ip: raw_result.ip.to_string(),
},
DeviceType::Other => {
let info: DeviceInfoBasicResult = serde_json::from_value(device_info)?;
let info = match device_family {
DeviceFamily::SmartCam => info,
_ => info.decode()?,
};
DiscoveryResult::Other {
device_info: Box::new(info),
ip: raw_result.ip.to_string(),
}
}
};
Ok(result)
}
pub fn device_type(&self) -> DeviceType {
match self {
DiscoveryResult::Light { .. } => DeviceType::Light,
DiscoveryResult::ColorLight { .. } => DeviceType::ColorLight,
DiscoveryResult::RgbLightStrip { .. } => DeviceType::RgbLightStrip,
DiscoveryResult::RgbicLightStrip { .. } => DeviceType::RgbicLightStrip,
DiscoveryResult::Plug { .. } => DeviceType::Plug,
DiscoveryResult::PlugEnergyMonitoring { .. } => DeviceType::PlugEnergyMonitoring,
DiscoveryResult::PowerStrip { .. } => DeviceType::PowerStrip,
DiscoveryResult::PowerStripEnergyMonitoring { .. } => {
DeviceType::PowerStripEnergyMonitoring
}
DiscoveryResult::Hub { .. } => DeviceType::Hub,
DiscoveryResult::CameraPtz { .. } => DeviceType::CameraPtz,
DiscoveryResult::Other { .. } => DeviceType::Other,
}
}
pub fn model(&self) -> &str {
match self {
DiscoveryResult::Light { device_info, .. } => &device_info.model,
DiscoveryResult::ColorLight { device_info, .. } => &device_info.model,
DiscoveryResult::RgbLightStrip { device_info, .. } => &device_info.model,
DiscoveryResult::RgbicLightStrip { device_info, .. } => &device_info.model,
DiscoveryResult::Plug { device_info, .. } => &device_info.model,
DiscoveryResult::PlugEnergyMonitoring { device_info, .. } => &device_info.model,
DiscoveryResult::PowerStrip { device_info, .. } => &device_info.model,
DiscoveryResult::PowerStripEnergyMonitoring { device_info, .. } => &device_info.model,
DiscoveryResult::Hub { device_info, .. } => &device_info.model,
DiscoveryResult::CameraPtz { device_info, .. } => &device_info.model,
DiscoveryResult::Other { device_info, .. } => &device_info.model,
}
}
pub fn ip(&self) -> &str {
match self {
DiscoveryResult::Light { device_info, .. } => &device_info.ip,
DiscoveryResult::ColorLight { device_info, .. } => &device_info.ip,
DiscoveryResult::RgbLightStrip { device_info, .. } => &device_info.ip,
DiscoveryResult::RgbicLightStrip { device_info, .. } => &device_info.ip,
DiscoveryResult::Plug { device_info, .. } => &device_info.ip,
DiscoveryResult::PlugEnergyMonitoring { device_info, .. } => &device_info.ip,
DiscoveryResult::PowerStrip { device_info, .. } => &device_info.ip,
DiscoveryResult::PowerStripEnergyMonitoring { device_info, .. } => &device_info.ip,
DiscoveryResult::Hub { device_info, .. } => &device_info.ip,
DiscoveryResult::CameraPtz { ip, .. } => ip,
DiscoveryResult::Other { ip, .. } => ip,
}
}
pub fn device_id(&self) -> &str {
match self {
DiscoveryResult::Light { device_info, .. } => &device_info.device_id,
DiscoveryResult::ColorLight { device_info, .. } => &device_info.device_id,
DiscoveryResult::RgbLightStrip { device_info, .. } => &device_info.device_id,
DiscoveryResult::RgbicLightStrip { device_info, .. } => &device_info.device_id,
DiscoveryResult::Plug { device_info, .. } => &device_info.device_id,
DiscoveryResult::PlugEnergyMonitoring { device_info, .. } => &device_info.device_id,
DiscoveryResult::PowerStrip { device_info, .. } => &device_info.device_id,
DiscoveryResult::PowerStripEnergyMonitoring { device_info, .. } => {
&device_info.device_id
}
DiscoveryResult::Hub { device_info, .. } => &device_info.device_id,
DiscoveryResult::CameraPtz { device_info, .. } => &device_info.device_id,
DiscoveryResult::Other { device_info, .. } => &device_info.device_id,
}
}
pub fn nickname(&self) -> &str {
match self {
DiscoveryResult::Light { device_info, .. } => &device_info.nickname,
DiscoveryResult::ColorLight { device_info, .. } => &device_info.nickname,
DiscoveryResult::RgbLightStrip { device_info, .. } => &device_info.nickname,
DiscoveryResult::RgbicLightStrip { device_info, .. } => &device_info.nickname,
DiscoveryResult::Plug { device_info, .. } => &device_info.nickname,
DiscoveryResult::PlugEnergyMonitoring { device_info, .. } => &device_info.nickname,
DiscoveryResult::PowerStrip { .. } => DeviceType::PowerStrip.as_str(),
DiscoveryResult::PowerStripEnergyMonitoring { .. } => {
DeviceType::PowerStripEnergyMonitoring.as_str()
}
DiscoveryResult::Hub { device_info, .. } => &device_info.nickname,
DiscoveryResult::CameraPtz { device_info, .. } => &device_info.nickname,
DiscoveryResult::Other { device_info, .. } => device_info
.nickname
.as_deref()
.unwrap_or(DeviceType::Other.as_str()),
}
}
}