1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6use super::device::{Device, ResourceType};
7use super::jsonapi::Resource;
8use crate::error::{AlarmError, Result};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum SensorState {
13 Unknown,
14 Closed,
15 Open,
16 Idle,
17 Active,
18 Dry,
19 Wet,
20}
21
22impl SensorState {
23 pub fn from_code(code: u32) -> Self {
24 match code {
25 0 => SensorState::Unknown,
26 1 => SensorState::Closed,
27 2 => SensorState::Open,
28 3 => SensorState::Idle,
29 4 => SensorState::Active,
30 5 => SensorState::Dry,
31 6 => SensorState::Wet,
32 _ => SensorState::Unknown,
33 }
34 }
35}
36
37impl fmt::Display for SensorState {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 SensorState::Unknown => write!(f, "Unknown"),
41 SensorState::Closed => write!(f, "Closed"),
42 SensorState::Open => write!(f, "Open"),
43 SensorState::Idle => write!(f, "Idle"),
44 SensorState::Active => write!(f, "Active"),
45 SensorState::Dry => write!(f, "Dry"),
46 SensorState::Wet => write!(f, "Wet"),
47 }
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
53pub enum SensorType {
54 DoorWindow,
55 Motion,
56 GlassBreak,
57 Smoke,
58 CarbonMonoxide,
59 Flood,
60 Temperature,
61 Freeze,
62 Shock,
63 Tilt,
64 Other,
65}
66
67impl fmt::Display for SensorType {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 match self {
70 SensorType::DoorWindow => write!(f, "Door/Window"),
71 SensorType::Motion => write!(f, "Motion"),
72 SensorType::GlassBreak => write!(f, "Glass Break"),
73 SensorType::Smoke => write!(f, "Smoke"),
74 SensorType::CarbonMonoxide => write!(f, "CO"),
75 SensorType::Flood => write!(f, "Flood"),
76 SensorType::Temperature => write!(f, "Temperature"),
77 SensorType::Freeze => write!(f, "Freeze"),
78 SensorType::Shock => write!(f, "Shock"),
79 SensorType::Tilt => write!(f, "Tilt"),
80 SensorType::Other => write!(f, "Other"),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Deserialize, Serialize)]
87#[serde(rename_all = "camelCase")]
88pub struct SensorAttributes {
89 #[serde(default)]
90 pub state: u32,
91 #[serde(default)]
92 pub description: Option<String>,
93 #[serde(default)]
94 pub device_type: Option<u32>,
95 #[serde(default)]
96 pub has_permission: Option<bool>,
97 #[serde(default)]
98 pub can_be_bypassed: Option<bool>,
99 #[serde(default)]
100 pub is_bypassed: Option<bool>,
101 #[serde(default)]
102 pub low_battery: Option<bool>,
103 #[serde(default)]
104 pub critical_battery: Option<bool>,
105 #[serde(default)]
106 pub malfunction: Option<bool>,
107}
108
109#[derive(Debug, Clone)]
111pub struct Sensor {
112 pub id: String,
113 pub name: String,
114 pub state: SensorState,
115 pub sensor_type: SensorType,
116 pub is_bypassed: bool,
117 pub low_battery: bool,
118 pub malfunction: bool,
119 pub attributes: SensorAttributes,
120}
121
122impl Sensor {
123 pub fn from_resource(resource: &Resource) -> Result<Self> {
124 let attrs: SensorAttributes =
125 serde_json::from_value(resource.attributes.clone()).map_err(|e| {
126 AlarmError::UnexpectedResponse(format!("failed to parse sensor attributes: {e}"))
127 })?;
128
129 let name = attrs
130 .description
131 .clone()
132 .unwrap_or_else(|| format!("Sensor {}", resource.id));
133
134 let sensor_type = match attrs.device_type {
135 Some(1) => SensorType::DoorWindow,
136 Some(2) => SensorType::Motion,
137 Some(5) => SensorType::GlassBreak,
138 Some(6) => SensorType::Smoke,
139 Some(8) => SensorType::CarbonMonoxide,
140 Some(9) => SensorType::Flood,
141 Some(10) => SensorType::Temperature,
142 Some(14) => SensorType::Tilt,
143 Some(16) => SensorType::Shock,
144 Some(19) => SensorType::Freeze,
145 _ => SensorType::Other,
146 };
147
148 Ok(Sensor {
149 id: resource.id.clone(),
150 name,
151 state: SensorState::from_code(attrs.state),
152 sensor_type,
153 is_bypassed: attrs.is_bypassed.unwrap_or(false),
154 low_battery: attrs.low_battery.unwrap_or(false),
155 malfunction: attrs.malfunction.unwrap_or(false),
156 attributes: attrs,
157 })
158 }
159}
160
161impl Device for Sensor {
162 fn id(&self) -> &str {
163 &self.id
164 }
165
166 fn name(&self) -> &str {
167 &self.name
168 }
169
170 fn resource_type() -> ResourceType {
171 ResourceType::Sensor
172 }
173}