1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13pub type AlarmMode = u8;
17
18pub mod alarmmode {
20 pub const VISUAL: u8 = 0x01;
22 pub const AUDIBLE: u8 = 0x02;
24}
25
26pub type SensorFault = u8;
28
29pub mod sensorfault {
31 pub const GENERAL_FAULT: u8 = 0x01;
33}
34
35pub fn encode_suppress_alarm(alarms_to_suppress: AlarmMode) -> anyhow::Result<Vec<u8>> {
39 let tlv = tlv::TlvItemEnc {
40 tag: 0,
41 value: tlv::TlvItemValueEnc::StructInvisible(vec![
42 (0, tlv::TlvItemValueEnc::UInt8(alarms_to_suppress)).into(),
43 ]),
44 };
45 Ok(tlv.encode()?)
46}
47
48pub fn encode_enable_disable_alarm(alarms_to_enable_disable: AlarmMode) -> anyhow::Result<Vec<u8>> {
50 let tlv = tlv::TlvItemEnc {
51 tag: 0,
52 value: tlv::TlvItemValueEnc::StructInvisible(vec![
53 (0, tlv::TlvItemValueEnc::UInt8(alarms_to_enable_disable)).into(),
54 ]),
55 };
56 Ok(tlv.encode()?)
57}
58
59pub fn decode_current_sensitivity_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
63 if let tlv::TlvItemValue::Int(v) = inp {
64 Ok(*v as u8)
65 } else {
66 Err(anyhow::anyhow!("Expected UInt8"))
67 }
68}
69
70pub fn decode_supported_sensitivity_levels(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
72 if let tlv::TlvItemValue::Int(v) = inp {
73 Ok(*v as u8)
74 } else {
75 Err(anyhow::anyhow!("Expected UInt8"))
76 }
77}
78
79pub fn decode_default_sensitivity_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
81 if let tlv::TlvItemValue::Int(v) = inp {
82 Ok(*v as u8)
83 } else {
84 Err(anyhow::anyhow!("Expected UInt8"))
85 }
86}
87
88pub fn decode_alarms_active(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmMode> {
90 if let tlv::TlvItemValue::Int(v) = inp {
91 Ok(*v as u8)
92 } else {
93 Err(anyhow::anyhow!("Expected Integer"))
94 }
95}
96
97pub fn decode_alarms_suppressed(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmMode> {
99 if let tlv::TlvItemValue::Int(v) = inp {
100 Ok(*v as u8)
101 } else {
102 Err(anyhow::anyhow!("Expected Integer"))
103 }
104}
105
106pub fn decode_alarms_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmMode> {
108 if let tlv::TlvItemValue::Int(v) = inp {
109 Ok(*v as u8)
110 } else {
111 Err(anyhow::anyhow!("Expected Integer"))
112 }
113}
114
115pub fn decode_alarms_supported(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmMode> {
117 if let tlv::TlvItemValue::Int(v) = inp {
118 Ok(*v as u8)
119 } else {
120 Err(anyhow::anyhow!("Expected Integer"))
121 }
122}
123
124pub fn decode_sensor_fault(inp: &tlv::TlvItemValue) -> anyhow::Result<SensorFault> {
126 if let tlv::TlvItemValue::Int(v) = inp {
127 Ok(*v as u8)
128 } else {
129 Err(anyhow::anyhow!("Expected Integer"))
130 }
131}
132
133
134pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
146 if cluster_id != 0x0080 {
148 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0080, got {}\"}}", cluster_id);
149 }
150
151 match attribute_id {
152 0x0000 => {
153 match decode_current_sensitivity_level(tlv_value) {
154 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
155 Err(e) => format!("{{\"error\": \"{}\"}}", e),
156 }
157 }
158 0x0001 => {
159 match decode_supported_sensitivity_levels(tlv_value) {
160 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
161 Err(e) => format!("{{\"error\": \"{}\"}}", e),
162 }
163 }
164 0x0002 => {
165 match decode_default_sensitivity_level(tlv_value) {
166 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
167 Err(e) => format!("{{\"error\": \"{}\"}}", e),
168 }
169 }
170 0x0003 => {
171 match decode_alarms_active(tlv_value) {
172 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
173 Err(e) => format!("{{\"error\": \"{}\"}}", e),
174 }
175 }
176 0x0004 => {
177 match decode_alarms_suppressed(tlv_value) {
178 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
179 Err(e) => format!("{{\"error\": \"{}\"}}", e),
180 }
181 }
182 0x0005 => {
183 match decode_alarms_enabled(tlv_value) {
184 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
185 Err(e) => format!("{{\"error\": \"{}\"}}", e),
186 }
187 }
188 0x0006 => {
189 match decode_alarms_supported(tlv_value) {
190 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
191 Err(e) => format!("{{\"error\": \"{}\"}}", e),
192 }
193 }
194 0x0007 => {
195 match decode_sensor_fault(tlv_value) {
196 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
197 Err(e) => format!("{{\"error\": \"{}\"}}", e),
198 }
199 }
200 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
201 }
202}
203
204pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
209 vec![
210 (0x0000, "CurrentSensitivityLevel"),
211 (0x0001, "SupportedSensitivityLevels"),
212 (0x0002, "DefaultSensitivityLevel"),
213 (0x0003, "AlarmsActive"),
214 (0x0004, "AlarmsSuppressed"),
215 (0x0005, "AlarmsEnabled"),
216 (0x0006, "AlarmsSupported"),
217 (0x0007, "SensorFault"),
218 ]
219}
220
221pub fn get_command_list() -> Vec<(u32, &'static str)> {
224 vec![
225 (0x00, "SuppressAlarm"),
226 (0x01, "EnableDisableAlarm"),
227 ]
228}
229
230pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
231 match cmd_id {
232 0x00 => Some("SuppressAlarm"),
233 0x01 => Some("EnableDisableAlarm"),
234 _ => None,
235 }
236}
237
238pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
239 match cmd_id {
240 0x00 => Some(vec![
241 crate::clusters::codec::CommandField { tag: 0, name: "alarms_to_suppress", kind: crate::clusters::codec::FieldKind::Bitmap { name: "AlarmMode", bits: &[(1, "VISUAL"), (2, "AUDIBLE")] }, optional: false, nullable: false },
242 ]),
243 0x01 => Some(vec![
244 crate::clusters::codec::CommandField { tag: 0, name: "alarms_to_enable_disable", kind: crate::clusters::codec::FieldKind::Bitmap { name: "AlarmMode", bits: &[(1, "VISUAL"), (2, "AUDIBLE")] }, optional: false, nullable: false },
245 ]),
246 _ => None,
247 }
248}
249
250pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
251 match cmd_id {
252 0x00 => {
253 let alarms_to_suppress = crate::clusters::codec::json_util::get_u8(args, "alarms_to_suppress")?;
254 encode_suppress_alarm(alarms_to_suppress)
255 }
256 0x01 => {
257 let alarms_to_enable_disable = crate::clusters::codec::json_util::get_u8(args, "alarms_to_enable_disable")?;
258 encode_enable_disable_alarm(alarms_to_enable_disable)
259 }
260 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
261 }
262}
263
264pub async fn suppress_alarm(conn: &crate::controller::Connection, endpoint: u16, alarms_to_suppress: AlarmMode) -> anyhow::Result<()> {
268 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_CMD_ID_SUPPRESSALARM, &encode_suppress_alarm(alarms_to_suppress)?).await?;
269 Ok(())
270}
271
272pub async fn enable_disable_alarm(conn: &crate::controller::Connection, endpoint: u16, alarms_to_enable_disable: AlarmMode) -> anyhow::Result<()> {
274 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_CMD_ID_ENABLEDISABLEALARM, &encode_enable_disable_alarm(alarms_to_enable_disable)?).await?;
275 Ok(())
276}
277
278pub async fn read_current_sensitivity_level(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
280 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_CURRENTSENSITIVITYLEVEL).await?;
281 decode_current_sensitivity_level(&tlv)
282}
283
284pub async fn read_supported_sensitivity_levels(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
286 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_SUPPORTEDSENSITIVITYLEVELS).await?;
287 decode_supported_sensitivity_levels(&tlv)
288}
289
290pub async fn read_default_sensitivity_level(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
292 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_DEFAULTSENSITIVITYLEVEL).await?;
293 decode_default_sensitivity_level(&tlv)
294}
295
296pub async fn read_alarms_active(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmMode> {
298 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_ALARMSACTIVE).await?;
299 decode_alarms_active(&tlv)
300}
301
302pub async fn read_alarms_suppressed(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmMode> {
304 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_ALARMSSUPPRESSED).await?;
305 decode_alarms_suppressed(&tlv)
306}
307
308pub async fn read_alarms_enabled(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmMode> {
310 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_ALARMSENABLED).await?;
311 decode_alarms_enabled(&tlv)
312}
313
314pub async fn read_alarms_supported(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmMode> {
316 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_ALARMSSUPPORTED).await?;
317 decode_alarms_supported(&tlv)
318}
319
320pub async fn read_sensor_fault(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<SensorFault> {
322 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_BOOLEAN_STATE_CONFIGURATION, crate::clusters::defs::CLUSTER_BOOLEAN_STATE_CONFIGURATION_ATTR_ID_SENSORFAULT).await?;
323 decode_sensor_fault(&tlv)
324}
325
326#[derive(Debug, serde::Serialize)]
327pub struct AlarmsStateChangedEvent {
328 pub alarms_active: Option<AlarmMode>,
329 pub alarms_suppressed: Option<AlarmMode>,
330}
331
332#[derive(Debug, serde::Serialize)]
333pub struct SensorFaultEvent {
334 pub sensor_fault: Option<SensorFault>,
335}
336
337pub fn decode_alarms_state_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmsStateChangedEvent> {
341 if let tlv::TlvItemValue::List(_fields) = inp {
342 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
343 Ok(AlarmsStateChangedEvent {
344 alarms_active: item.get_int(&[0]).map(|v| v as u8),
345 alarms_suppressed: item.get_int(&[1]).map(|v| v as u8),
346 })
347 } else {
348 Err(anyhow::anyhow!("Expected struct fields"))
349 }
350}
351
352pub fn decode_sensor_fault_event(inp: &tlv::TlvItemValue) -> anyhow::Result<SensorFaultEvent> {
354 if let tlv::TlvItemValue::List(_fields) = inp {
355 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
356 Ok(SensorFaultEvent {
357 sensor_fault: item.get_int(&[0]).map(|v| v as u8),
358 })
359 } else {
360 Err(anyhow::anyhow!("Expected struct fields"))
361 }
362}
363