houseflow_types/
device.rs

1use crate::common::Credential;
2use semver::Version;
3use std::collections::HashMap;
4use strum::{EnumIter, IntoEnumIterator};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9pub type DeviceID = Credential<16>;
10pub type DevicePassword = String;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14pub struct Device {
15    /// Identifier of the device
16    pub id: DeviceID,
17
18    /// Name of room(if available)
19    pub room_id: RoomID,
20
21    /// Hashed password for device
22    pub password_hash: String,
23
24    /// Type of the device
25    pub device_type: DeviceType,
26
27    /// Functionatily that the device has
28    pub traits: Vec<DeviceTrait>,
29
30    /// Name of the device
31    pub name: String,
32
33    /// True if device will push state by itself, otherwise will use polling
34    pub will_push_state: bool,
35
36    /// The model or SKU identifier of the device
37    pub model: String,
38
39    /// Specific version number of hardware of the device
40    pub hw_version: Version,
41
42    /// Specific version number of software of the device
43    pub sw_version: Version,
44
45    /// Aligned with per-trait attributes described in each trait schema reference.
46    pub attributes: HashMap<String, Option<String>>,
47}
48
49pub type StructureID = Credential<16>;
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
53pub struct Structure {
54    pub id: StructureID,
55    pub name: String,
56}
57
58pub type RoomID = Credential<16>;
59
60#[derive(Debug, Clone, PartialEq, Eq)]
61#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
62pub struct Room {
63    pub id: RoomID,
64    pub structure_id: StructureID,
65    pub name: String,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
70pub struct UserStructure {
71    pub structure_id: StructureID,
72    pub user_id: crate::UserID,
73    pub is_manager: bool,
74}
75
76use strum::EnumString;
77
78/// Traits defines what functionality device supports
79#[derive(Debug, Clone, Hash, Eq, PartialEq, strum::Display, EnumString)]
80#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
81#[strum(serialize_all = "snake_case")]
82#[repr(u16)]
83#[non_exhaustive]
84pub enum DeviceTrait {
85    OnOff,
86    OpenClose,
87}
88
89impl DeviceTrait {
90    pub fn commands(&self) -> Vec<DeviceCommand> {
91        match *self {
92            Self::OnOff => vec![DeviceCommand::NoOperation, DeviceCommand::OnOff],
93            Self::OpenClose => vec![DeviceCommand::NoOperation, DeviceCommand::OpenClose],
94        }
95    }
96}
97
98/// Type of the device
99#[derive(Debug, Clone, PartialEq, Eq, strum::Display, EnumString)]
100#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
101#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
102#[strum(serialize_all = "snake_case")]
103#[repr(u16)]
104#[non_exhaustive]
105pub enum DeviceType {
106    Gate,
107    Garage,
108    Light,
109}
110
111impl DeviceType {
112    pub fn required_traits(&self) -> Vec<DeviceTrait> {
113        match *self {
114            Self::Gate => vec![DeviceTrait::OpenClose],
115            Self::Garage => vec![DeviceTrait::OpenClose],
116            Self::Light => vec![DeviceTrait::OnOff],
117        }
118    }
119}
120
121#[derive(Debug, Clone, Eq, PartialEq, EnumIter, strum::Display, EnumString)]
122#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
123#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
124#[strum(serialize_all = "snake_case")]
125#[repr(u16)]
126#[non_exhaustive]
127pub enum DeviceCommand {
128    NoOperation = 0x0000,
129    OnOff = 0x0001,
130    OpenClose = 0x0002,
131}
132
133#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, strum::Display, EnumIter)]
134#[strum(serialize_all = "snake_case")]
135#[repr(u16)]
136#[non_exhaustive]
137pub enum DeviceError {
138    /// No error occurred
139    None = 0x0000,
140
141    /// Actually, <device(s)> <doesn't/don't> support that functionality.
142    FunctionNotSupported = 0x0001,
143
144    /// Device does not support sent parameters
145    InvalidParameters = 0x0002,
146}
147
148#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, EnumIter, strum::Display)]
149#[repr(u8)]
150pub enum DeviceStatus {
151    /// Confirm that the command succeeded.
152    Success,
153
154    /// Target device is unable to perform the command.
155    Error,
156}
157
158impl std::convert::TryFrom<u8> for DeviceStatus {
159    type Error = ();
160
161    fn try_from(v: u8) -> Result<Self, ()> {
162        Self::iter().find(|e| e.clone() as u8 == v).ok_or(())
163    }
164}
165
166impl rand::distributions::Distribution<DeviceStatus> for rand::distributions::Standard {
167    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> DeviceStatus {
168        DeviceStatus::iter()
169            .nth(rng.gen_range(0..DeviceStatus::iter().len()))
170            .unwrap()
171    }
172}
173
174impl std::convert::TryFrom<u16> for DeviceError {
175    type Error = ();
176
177    fn try_from(v: u16) -> Result<Self, Self::Error> {
178        Self::iter().find(|e| e.clone() as u16 == v).ok_or(())
179    }
180}
181
182impl rand::distributions::Distribution<DeviceError> for rand::distributions::Standard {
183    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> DeviceError {
184        DeviceError::iter()
185            .nth(rng.gen_range(0..DeviceError::iter().len()))
186            .unwrap()
187    }
188}
189
190impl std::convert::TryFrom<u16> for DeviceCommand {
191    type Error = ();
192
193    fn try_from(v: u16) -> Result<Self, Self::Error> {
194        Self::iter().find(|e| e.clone() as u16 == v).ok_or(())
195    }
196}
197
198impl rand::distributions::Distribution<DeviceCommand> for rand::distributions::Standard {
199    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> DeviceCommand {
200        DeviceCommand::iter()
201            .nth(rng.gen_range(0..DeviceCommand::iter().len()))
202            .unwrap()
203    }
204}
205
206#[cfg(feature = "postgres-types")]
207impl<'a> postgres_types::FromSql<'a> for DeviceType {
208    fn from_sql(
209        _ty: &postgres_types::Type,
210        raw: &'a [u8],
211    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
212        use std::str::FromStr;
213
214        let str = std::str::from_utf8(raw)?;
215        Ok(Self::from_str(str)?)
216    }
217
218    fn accepts(ty: &postgres_types::Type) -> bool {
219        *ty == postgres_types::Type::TEXT
220    }
221}
222
223#[cfg(feature = "postgres-types")]
224impl<'a> postgres_types::FromSql<'a> for DeviceTrait {
225    fn from_sql(
226        _ty: &postgres_types::Type,
227        raw: &'a [u8],
228    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
229        use std::str::FromStr;
230
231        let str = std::str::from_utf8(raw)?;
232        Ok(Self::from_str(str)?)
233    }
234
235    fn accepts(ty: &postgres_types::Type) -> bool {
236        *ty == postgres_types::Type::TEXT
237    }
238}