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 pub id: DeviceID,
17
18 pub room_id: RoomID,
20
21 pub password_hash: String,
23
24 pub device_type: DeviceType,
26
27 pub traits: Vec<DeviceTrait>,
29
30 pub name: String,
32
33 pub will_push_state: bool,
35
36 pub model: String,
38
39 pub hw_version: Version,
41
42 pub sw_version: Version,
44
45 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#[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#[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 None = 0x0000,
140
141 FunctionNotSupported = 0x0001,
143
144 InvalidParameters = 0x0002,
146}
147
148#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, EnumIter, strum::Display)]
149#[repr(u8)]
150pub enum DeviceStatus {
151 Success,
153
154 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}