1use std::{
2 collections::HashMap,
3 fmt::Display,
4 io,
5 sync::{Arc, RwLock, RwLockReadGuard, Weak},
6};
7
8use super::*;
9
10#[derive(Debug, Default, serde::Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct Device {
19 device_id: String,
20 #[serde(default)] device_name: String,
22 #[serde(default)]
23 device_type: String,
24 #[serde(default)]
25 remote_type: String,
26 hub_device_id: String,
27
28 #[serde(flatten)]
29 extra: HashMap<String, serde_json::Value>,
30
31 #[serde(skip)]
32 status: RwLock<HashMap<String, serde_json::Value>>,
33
34 #[serde(skip)]
35 service: Weak<SwitchBotService>,
36}
37
38impl Device {
39 pub(crate) fn new_for_test(index: usize) -> Self {
40 Self {
41 device_id: format!("device{}", index),
42 device_name: format!("Device {}", index),
43 device_type: "test".into(),
44 ..Default::default()
45 }
46 }
47
48 pub fn device_id(&self) -> &str {
50 &self.device_id
51 }
52
53 pub fn device_name(&self) -> &str {
56 &self.device_name
57 }
58
59 pub fn is_remote(&self) -> bool {
61 !self.remote_type.is_empty()
62 }
63
64 pub fn device_type(&self) -> &str {
67 &self.device_type
68 }
69
70 pub fn remote_type(&self) -> &str {
72 &self.remote_type
73 }
74
75 pub fn hub_device_id(&self) -> &str {
77 &self.hub_device_id
78 }
79
80 fn service(&self) -> anyhow::Result<Arc<SwitchBotService>> {
81 self.service
82 .upgrade()
83 .ok_or_else(|| anyhow::anyhow!("The service is dropped"))
84 }
85
86 pub(crate) fn set_service(&mut self, service: &Arc<SwitchBotService>) {
87 self.service = Arc::downgrade(service);
88 }
89
90 pub async fn command(&self, command: &CommandRequest) -> anyhow::Result<()> {
96 self.service()?.command(self.device_id(), command).await
97 }
98
99 pub async fn update_status(&self) -> anyhow::Result<()> {
107 let status = self.service()?.status(self.device_id()).await?;
108 assert_eq!(self.device_id, status.device_id);
109 let mut writer = self.status.write().unwrap();
110 *writer = status.extra;
111 Ok(())
112 }
113
114 fn status(&self) -> RwLockReadGuard<'_, HashMap<String, serde_json::Value>> {
115 self.status.read().unwrap()
116 }
117
118 pub fn status_by_key(&self, key: &str) -> Option<serde_json::Value> {
133 self.status().get(key).cloned()
134 }
135
136 pub fn write_status_to(&self, mut writer: impl io::Write) -> io::Result<()> {
151 let status = self.status();
152 for (key, value) in status.iter() {
153 writeln!(writer, "{key}: {value}")?;
154 }
155 Ok(())
156 }
157
158 fn fmt_multi_line(&self, buf: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 writeln!(buf, "Name: {}", self.device_name())?;
160 writeln!(buf, "ID: {}", self.device_id())?;
161 if self.is_remote() {
162 writeln!(buf, "Remote Type: {}", self.remote_type())?;
163 } else {
164 writeln!(buf, "Type: {}", self.device_type())?;
165 }
166 let status = self.status();
167 if !status.is_empty() {
168 writeln!(buf, "Status:")?;
169 for (key, value) in status.iter() {
170 writeln!(buf, " {key}: {value}")?;
171 }
172 }
173 Ok(())
174 }
175}
176
177impl Display for Device {
178 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179 if f.alternate() {
180 return self.fmt_multi_line(f);
181 }
182 write!(
183 f,
184 "{} ({}, ID:{})",
185 self.device_name,
186 if self.is_remote() {
187 self.remote_type()
188 } else {
189 self.device_type()
190 },
191 self.device_id
192 )
193 }
194}