Skip to main content

alarm_dot_com/models/
sensor.rs

1//! Sensor model (door/window contacts, motion, glass break, smoke, CO, flood).
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6use super::device::{Device, ResourceType};
7use super::jsonapi::Resource;
8use crate::error::{AlarmError, Result};
9
10/// Sensor state.
11#[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/// Type of sensor device.
52#[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/// Sensor attributes from the API.
86#[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/// A sensor device.
110#[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}