switchbot_api/
device.rs

1use std::{
2    fmt::Display,
3    sync::{Arc, Weak},
4};
5
6use super::*;
7
8/// Represents a device.
9///
10/// For the details of fields, please refer to the [devices] section
11/// of the API documentation.
12///
13/// [devices]: https://github.com/OpenWonderLabs/SwitchBotAPI?tab=readme-ov-file#devices
14#[derive(Debug, Default, serde::Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct Device {
17    device_id: String,
18    device_name: String,
19    #[serde(default)]
20    device_type: String,
21    #[serde(default)]
22    remote_type: String,
23    hub_device_id: String,
24
25    #[serde(skip)]
26    service: Weak<SwitchBotService>,
27}
28
29impl Device {
30    pub(crate) fn new_for_test(index: usize) -> Self {
31        Self {
32            device_id: format!("device{}", index),
33            device_name: format!("Device {}", index),
34            device_type: "test".into(),
35            ..Default::default()
36        }
37    }
38
39    /// The device ID.
40    pub fn device_id(&self) -> &str {
41        &self.device_id
42    }
43
44    /// The device name.
45    /// This is the name configured in the SwitchBot app.
46    pub fn device_name(&self) -> &str {
47        &self.device_name
48    }
49
50    /// True if this device is an infrared remote device.
51    pub fn is_remote(&self) -> bool {
52        !self.remote_type.is_empty()
53    }
54
55    /// The device type.
56    /// This is empty if this is an infrared remote device.
57    pub fn device_type(&self) -> &str {
58        &self.device_type
59    }
60
61    /// The device type for an infrared remote device.
62    pub fn remote_type(&self) -> &str {
63        &self.remote_type
64    }
65
66    /// The parent Hub ID.
67    pub fn hub_device_id(&self) -> &str {
68        &self.hub_device_id
69    }
70
71    fn service(&self) -> anyhow::Result<Arc<SwitchBotService>> {
72        self.service
73            .upgrade()
74            .ok_or_else(|| anyhow::anyhow!("The service is dropped"))
75    }
76
77    pub(crate) fn set_service(&mut self, service: &Arc<SwitchBotService>) {
78        self.service = Arc::downgrade(service);
79    }
80
81    /// Send the `command` to the [SwitchBot API].
82    ///
83    /// Please also see the [`CommandRequest`].
84    ///
85    /// [SwitchBot API]: https://github.com/OpenWonderLabs/SwitchBotAPI
86    pub async fn command(&self, command: &CommandRequest) -> anyhow::Result<()> {
87        self.service()?.command(self.device_id(), command).await
88    }
89}
90
91impl Display for Device {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        if f.alternate() {
94            writeln!(f, "Name: {}", self.device_name())?;
95            writeln!(f, "ID: {}", self.device_id())?;
96            if self.is_remote() {
97                write!(f, "Remote Type: {}", self.remote_type())?;
98            } else {
99                write!(f, "Type: {}", self.device_type())?;
100            }
101            return Ok(());
102        }
103        write!(
104            f,
105            "{} ({}, ID:{})",
106            self.device_name,
107            if self.is_remote() {
108                self.remote_type()
109            } else {
110                self.device_type()
111            },
112            self.device_id
113        )
114    }
115}