1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum AlarmState {
18 Normal = 0,
20 Warning = 1,
22 Critical = 2,
24}
25
26impl AlarmState {
27 pub fn from_u8(value: u8) -> Option<Self> {
29 match value {
30 0 => Some(AlarmState::Normal),
31 1 => Some(AlarmState::Warning),
32 2 => Some(AlarmState::Critical),
33 _ => None,
34 }
35 }
36
37 pub fn to_u8(self) -> u8 {
39 self as u8
40 }
41}
42
43impl From<AlarmState> for u8 {
44 fn from(val: AlarmState) -> Self {
45 val as u8
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50#[repr(u8)]
51pub enum ContaminationState {
52 Normal = 0,
54 Low = 1,
56 Warning = 2,
58 Critical = 3,
60}
61
62impl ContaminationState {
63 pub fn from_u8(value: u8) -> Option<Self> {
65 match value {
66 0 => Some(ContaminationState::Normal),
67 1 => Some(ContaminationState::Low),
68 2 => Some(ContaminationState::Warning),
69 3 => Some(ContaminationState::Critical),
70 _ => None,
71 }
72 }
73
74 pub fn to_u8(self) -> u8 {
76 self as u8
77 }
78}
79
80impl From<ContaminationState> for u8 {
81 fn from(val: ContaminationState) -> Self {
82 val as u8
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
87#[repr(u8)]
88pub enum EndOfService {
89 Normal = 0,
91 Expired = 1,
93}
94
95impl EndOfService {
96 pub fn from_u8(value: u8) -> Option<Self> {
98 match value {
99 0 => Some(EndOfService::Normal),
100 1 => Some(EndOfService::Expired),
101 _ => None,
102 }
103 }
104
105 pub fn to_u8(self) -> u8 {
107 self as u8
108 }
109}
110
111impl From<EndOfService> for u8 {
112 fn from(val: EndOfService) -> Self {
113 val as u8
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
118#[repr(u8)]
119pub enum ExpressedState {
120 Normal = 0,
122 Smokealarm = 1,
124 Coalarm = 2,
126 Batteryalert = 3,
128 Testing = 4,
130 Hardwarefault = 5,
132 Endofservice = 6,
134 Interconnectsmoke = 7,
136 Interconnectco = 8,
138 Inoperative = 9,
140}
141
142impl ExpressedState {
143 pub fn from_u8(value: u8) -> Option<Self> {
145 match value {
146 0 => Some(ExpressedState::Normal),
147 1 => Some(ExpressedState::Smokealarm),
148 2 => Some(ExpressedState::Coalarm),
149 3 => Some(ExpressedState::Batteryalert),
150 4 => Some(ExpressedState::Testing),
151 5 => Some(ExpressedState::Hardwarefault),
152 6 => Some(ExpressedState::Endofservice),
153 7 => Some(ExpressedState::Interconnectsmoke),
154 8 => Some(ExpressedState::Interconnectco),
155 9 => Some(ExpressedState::Inoperative),
156 _ => None,
157 }
158 }
159
160 pub fn to_u8(self) -> u8 {
162 self as u8
163 }
164}
165
166impl From<ExpressedState> for u8 {
167 fn from(val: ExpressedState) -> Self {
168 val as u8
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
173#[repr(u8)]
174pub enum MuteState {
175 Notmuted = 0,
177 Muted = 1,
179}
180
181impl MuteState {
182 pub fn from_u8(value: u8) -> Option<Self> {
184 match value {
185 0 => Some(MuteState::Notmuted),
186 1 => Some(MuteState::Muted),
187 _ => None,
188 }
189 }
190
191 pub fn to_u8(self) -> u8 {
193 self as u8
194 }
195}
196
197impl From<MuteState> for u8 {
198 fn from(val: MuteState) -> Self {
199 val as u8
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
204#[repr(u8)]
205pub enum Sensitivity {
206 High = 0,
208 Standard = 1,
210 Low = 2,
212}
213
214impl Sensitivity {
215 pub fn from_u8(value: u8) -> Option<Self> {
217 match value {
218 0 => Some(Sensitivity::High),
219 1 => Some(Sensitivity::Standard),
220 2 => Some(Sensitivity::Low),
221 _ => None,
222 }
223 }
224
225 pub fn to_u8(self) -> u8 {
227 self as u8
228 }
229}
230
231impl From<Sensitivity> for u8 {
232 fn from(val: Sensitivity) -> Self {
233 val as u8
234 }
235}
236
237pub fn decode_expressed_state(inp: &tlv::TlvItemValue) -> anyhow::Result<ExpressedState> {
243 if let tlv::TlvItemValue::Int(v) = inp {
244 ExpressedState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
245 } else {
246 Err(anyhow::anyhow!("Expected Integer"))
247 }
248}
249
250pub fn decode_smoke_state(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmState> {
252 if let tlv::TlvItemValue::Int(v) = inp {
253 AlarmState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
254 } else {
255 Err(anyhow::anyhow!("Expected Integer"))
256 }
257}
258
259pub fn decode_co_state(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmState> {
261 if let tlv::TlvItemValue::Int(v) = inp {
262 AlarmState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
263 } else {
264 Err(anyhow::anyhow!("Expected Integer"))
265 }
266}
267
268pub fn decode_battery_alert(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmState> {
270 if let tlv::TlvItemValue::Int(v) = inp {
271 AlarmState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
272 } else {
273 Err(anyhow::anyhow!("Expected Integer"))
274 }
275}
276
277pub fn decode_device_muted(inp: &tlv::TlvItemValue) -> anyhow::Result<MuteState> {
279 if let tlv::TlvItemValue::Int(v) = inp {
280 MuteState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
281 } else {
282 Err(anyhow::anyhow!("Expected Integer"))
283 }
284}
285
286pub fn decode_test_in_progress(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
288 if let tlv::TlvItemValue::Bool(v) = inp {
289 Ok(*v)
290 } else {
291 Err(anyhow::anyhow!("Expected Bool"))
292 }
293}
294
295pub fn decode_hardware_fault_alert(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
297 if let tlv::TlvItemValue::Bool(v) = inp {
298 Ok(*v)
299 } else {
300 Err(anyhow::anyhow!("Expected Bool"))
301 }
302}
303
304pub fn decode_end_of_service_alert(inp: &tlv::TlvItemValue) -> anyhow::Result<EndOfService> {
306 if let tlv::TlvItemValue::Int(v) = inp {
307 EndOfService::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
308 } else {
309 Err(anyhow::anyhow!("Expected Integer"))
310 }
311}
312
313pub fn decode_interconnect_smoke_alarm(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmState> {
315 if let tlv::TlvItemValue::Int(v) = inp {
316 AlarmState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
317 } else {
318 Err(anyhow::anyhow!("Expected Integer"))
319 }
320}
321
322pub fn decode_interconnect_co_alarm(inp: &tlv::TlvItemValue) -> anyhow::Result<AlarmState> {
324 if let tlv::TlvItemValue::Int(v) = inp {
325 AlarmState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
326 } else {
327 Err(anyhow::anyhow!("Expected Integer"))
328 }
329}
330
331pub fn decode_contamination_state(inp: &tlv::TlvItemValue) -> anyhow::Result<ContaminationState> {
333 if let tlv::TlvItemValue::Int(v) = inp {
334 ContaminationState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
335 } else {
336 Err(anyhow::anyhow!("Expected Integer"))
337 }
338}
339
340pub fn decode_smoke_sensitivity_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Sensitivity> {
342 if let tlv::TlvItemValue::Int(v) = inp {
343 Sensitivity::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
344 } else {
345 Err(anyhow::anyhow!("Expected Integer"))
346 }
347}
348
349pub fn decode_expiry_date(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
351 if let tlv::TlvItemValue::Int(v) = inp {
352 Ok(*v)
353 } else {
354 Err(anyhow::anyhow!("Expected UInt64"))
355 }
356}
357
358pub fn decode_unmounted(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
360 if let tlv::TlvItemValue::Bool(v) = inp {
361 Ok(*v)
362 } else {
363 Err(anyhow::anyhow!("Expected Bool"))
364 }
365}
366
367
368pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
380 if cluster_id != 0x005C {
382 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x005C, got {}\"}}", cluster_id);
383 }
384
385 match attribute_id {
386 0x0000 => {
387 match decode_expressed_state(tlv_value) {
388 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
389 Err(e) => format!("{{\"error\": \"{}\"}}", e),
390 }
391 }
392 0x0001 => {
393 match decode_smoke_state(tlv_value) {
394 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
395 Err(e) => format!("{{\"error\": \"{}\"}}", e),
396 }
397 }
398 0x0002 => {
399 match decode_co_state(tlv_value) {
400 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
401 Err(e) => format!("{{\"error\": \"{}\"}}", e),
402 }
403 }
404 0x0003 => {
405 match decode_battery_alert(tlv_value) {
406 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
407 Err(e) => format!("{{\"error\": \"{}\"}}", e),
408 }
409 }
410 0x0004 => {
411 match decode_device_muted(tlv_value) {
412 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
413 Err(e) => format!("{{\"error\": \"{}\"}}", e),
414 }
415 }
416 0x0005 => {
417 match decode_test_in_progress(tlv_value) {
418 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
419 Err(e) => format!("{{\"error\": \"{}\"}}", e),
420 }
421 }
422 0x0006 => {
423 match decode_hardware_fault_alert(tlv_value) {
424 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
425 Err(e) => format!("{{\"error\": \"{}\"}}", e),
426 }
427 }
428 0x0007 => {
429 match decode_end_of_service_alert(tlv_value) {
430 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
431 Err(e) => format!("{{\"error\": \"{}\"}}", e),
432 }
433 }
434 0x0008 => {
435 match decode_interconnect_smoke_alarm(tlv_value) {
436 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
437 Err(e) => format!("{{\"error\": \"{}\"}}", e),
438 }
439 }
440 0x0009 => {
441 match decode_interconnect_co_alarm(tlv_value) {
442 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
443 Err(e) => format!("{{\"error\": \"{}\"}}", e),
444 }
445 }
446 0x000A => {
447 match decode_contamination_state(tlv_value) {
448 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
449 Err(e) => format!("{{\"error\": \"{}\"}}", e),
450 }
451 }
452 0x000B => {
453 match decode_smoke_sensitivity_level(tlv_value) {
454 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
455 Err(e) => format!("{{\"error\": \"{}\"}}", e),
456 }
457 }
458 0x000C => {
459 match decode_expiry_date(tlv_value) {
460 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
461 Err(e) => format!("{{\"error\": \"{}\"}}", e),
462 }
463 }
464 0x000D => {
465 match decode_unmounted(tlv_value) {
466 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
467 Err(e) => format!("{{\"error\": \"{}\"}}", e),
468 }
469 }
470 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
471 }
472}
473
474pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
479 vec![
480 (0x0000, "ExpressedState"),
481 (0x0001, "SmokeState"),
482 (0x0002, "COState"),
483 (0x0003, "BatteryAlert"),
484 (0x0004, "DeviceMuted"),
485 (0x0005, "TestInProgress"),
486 (0x0006, "HardwareFaultAlert"),
487 (0x0007, "EndOfServiceAlert"),
488 (0x0008, "InterconnectSmokeAlarm"),
489 (0x0009, "InterconnectCOAlarm"),
490 (0x000A, "ContaminationState"),
491 (0x000B, "SmokeSensitivityLevel"),
492 (0x000C, "ExpiryDate"),
493 (0x000D, "Unmounted"),
494 ]
495}
496
497pub fn get_command_list() -> Vec<(u32, &'static str)> {
500 vec![
501 (0x00, "SelfTestRequest"),
502 ]
503}
504
505pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
506 match cmd_id {
507 0x00 => Some("SelfTestRequest"),
508 _ => None,
509 }
510}
511
512pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
513 match cmd_id {
514 0x00 => Some(vec![]),
515 _ => None,
516 }
517}
518
519pub fn encode_command_json(cmd_id: u32, _args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
520 match cmd_id {
521 0x00 => Ok(vec![]),
522 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
523 }
524}
525
526pub async fn self_test_request(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
530 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_CMD_ID_SELFTESTREQUEST, &[]).await?;
531 Ok(())
532}
533
534pub async fn read_expressed_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ExpressedState> {
536 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_EXPRESSEDSTATE).await?;
537 decode_expressed_state(&tlv)
538}
539
540pub async fn read_smoke_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmState> {
542 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_SMOKESTATE).await?;
543 decode_smoke_state(&tlv)
544}
545
546pub async fn read_co_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmState> {
548 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_COSTATE).await?;
549 decode_co_state(&tlv)
550}
551
552pub async fn read_battery_alert(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmState> {
554 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_BATTERYALERT).await?;
555 decode_battery_alert(&tlv)
556}
557
558pub async fn read_device_muted(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<MuteState> {
560 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_DEVICEMUTED).await?;
561 decode_device_muted(&tlv)
562}
563
564pub async fn read_test_in_progress(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
566 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_TESTINPROGRESS).await?;
567 decode_test_in_progress(&tlv)
568}
569
570pub async fn read_hardware_fault_alert(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
572 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_HARDWAREFAULTALERT).await?;
573 decode_hardware_fault_alert(&tlv)
574}
575
576pub async fn read_end_of_service_alert(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<EndOfService> {
578 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_ENDOFSERVICEALERT).await?;
579 decode_end_of_service_alert(&tlv)
580}
581
582pub async fn read_interconnect_smoke_alarm(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmState> {
584 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_INTERCONNECTSMOKEALARM).await?;
585 decode_interconnect_smoke_alarm(&tlv)
586}
587
588pub async fn read_interconnect_co_alarm(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<AlarmState> {
590 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_INTERCONNECTCOALARM).await?;
591 decode_interconnect_co_alarm(&tlv)
592}
593
594pub async fn read_contamination_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ContaminationState> {
596 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_CONTAMINATIONSTATE).await?;
597 decode_contamination_state(&tlv)
598}
599
600pub async fn read_smoke_sensitivity_level(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Sensitivity> {
602 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_SMOKESENSITIVITYLEVEL).await?;
603 decode_smoke_sensitivity_level(&tlv)
604}
605
606pub async fn read_expiry_date(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u64> {
608 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_EXPIRYDATE).await?;
609 decode_expiry_date(&tlv)
610}
611
612pub async fn read_unmounted(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
614 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_SMOKE_CO_ALARM, crate::clusters::defs::CLUSTER_SMOKE_CO_ALARM_ATTR_ID_UNMOUNTED).await?;
615 decode_unmounted(&tlv)
616}
617
618#[derive(Debug, serde::Serialize)]
619pub struct SmokeAlarmEvent {
620 pub alarm_severity_level: Option<AlarmState>,
621}
622
623#[derive(Debug, serde::Serialize)]
624pub struct COAlarmEvent {
625 pub alarm_severity_level: Option<AlarmState>,
626}
627
628#[derive(Debug, serde::Serialize)]
629pub struct LowBatteryEvent {
630 pub alarm_severity_level: Option<AlarmState>,
631}
632
633#[derive(Debug, serde::Serialize)]
634pub struct InterconnectSmokeAlarmEvent {
635 pub alarm_severity_level: Option<AlarmState>,
636}
637
638#[derive(Debug, serde::Serialize)]
639pub struct InterconnectCOAlarmEvent {
640 pub alarm_severity_level: Option<AlarmState>,
641}
642
643pub fn decode_smoke_alarm_event(inp: &tlv::TlvItemValue) -> anyhow::Result<SmokeAlarmEvent> {
647 if let tlv::TlvItemValue::List(_fields) = inp {
648 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
649 Ok(SmokeAlarmEvent {
650 alarm_severity_level: item.get_int(&[0]).and_then(|v| AlarmState::from_u8(v as u8)),
651 })
652 } else {
653 Err(anyhow::anyhow!("Expected struct fields"))
654 }
655}
656
657pub fn decode_co_alarm_event(inp: &tlv::TlvItemValue) -> anyhow::Result<COAlarmEvent> {
659 if let tlv::TlvItemValue::List(_fields) = inp {
660 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
661 Ok(COAlarmEvent {
662 alarm_severity_level: item.get_int(&[0]).and_then(|v| AlarmState::from_u8(v as u8)),
663 })
664 } else {
665 Err(anyhow::anyhow!("Expected struct fields"))
666 }
667}
668
669pub fn decode_low_battery_event(inp: &tlv::TlvItemValue) -> anyhow::Result<LowBatteryEvent> {
671 if let tlv::TlvItemValue::List(_fields) = inp {
672 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
673 Ok(LowBatteryEvent {
674 alarm_severity_level: item.get_int(&[0]).and_then(|v| AlarmState::from_u8(v as u8)),
675 })
676 } else {
677 Err(anyhow::anyhow!("Expected struct fields"))
678 }
679}
680
681pub fn decode_interconnect_smoke_alarm_event(inp: &tlv::TlvItemValue) -> anyhow::Result<InterconnectSmokeAlarmEvent> {
683 if let tlv::TlvItemValue::List(_fields) = inp {
684 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
685 Ok(InterconnectSmokeAlarmEvent {
686 alarm_severity_level: item.get_int(&[0]).and_then(|v| AlarmState::from_u8(v as u8)),
687 })
688 } else {
689 Err(anyhow::anyhow!("Expected struct fields"))
690 }
691}
692
693pub fn decode_interconnect_co_alarm_event(inp: &tlv::TlvItemValue) -> anyhow::Result<InterconnectCOAlarmEvent> {
695 if let tlv::TlvItemValue::List(_fields) = inp {
696 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
697 Ok(InterconnectCOAlarmEvent {
698 alarm_severity_level: item.get_int(&[0]).and_then(|v| AlarmState::from_u8(v as u8)),
699 })
700 } else {
701 Err(anyhow::anyhow!("Expected struct fields"))
702 }
703}
704
705
706pub fn decode_event_json(cluster_id: u32, event_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
710 if cluster_id != 0x005C {
711 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x005C, got {}\"}}", cluster_id);
712 }
713
714 match event_id {
715 0x00 => {
716 match decode_smoke_alarm_event(tlv_value) {
717 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
718 Err(e) => format!("{{\"error\": \"{}\"}}", e),
719 }
720 }
721 0x01 => {
722 match decode_co_alarm_event(tlv_value) {
723 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
724 Err(e) => format!("{{\"error\": \"{}\"}}", e),
725 }
726 }
727 0x02 => {
728 match decode_low_battery_event(tlv_value) {
729 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
730 Err(e) => format!("{{\"error\": \"{}\"}}", e),
731 }
732 }
733 0x03 => "{}".to_string(),
734 0x04 => "{}".to_string(),
735 0x05 => "{}".to_string(),
736 0x06 => "{}".to_string(),
737 0x07 => "{}".to_string(),
738 0x08 => {
739 match decode_interconnect_smoke_alarm_event(tlv_value) {
740 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
741 Err(e) => format!("{{\"error\": \"{}\"}}", e),
742 }
743 }
744 0x09 => {
745 match decode_interconnect_co_alarm_event(tlv_value) {
746 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
747 Err(e) => format!("{{\"error\": \"{}\"}}", e),
748 }
749 }
750 0x0A => "{}".to_string(),
751 _ => format!("{{\"error\": \"Unknown event ID: {}\"}}", event_id),
752 }
753}
754
755pub fn get_event_list() -> Vec<(u32, &'static str)> {
760 vec![
761 (0x00, "SmokeAlarm"),
762 (0x01, "COAlarm"),
763 (0x02, "LowBattery"),
764 (0x03, "HardwareFault"),
765 (0x04, "EndOfService"),
766 (0x05, "SelfTestComplete"),
767 (0x06, "AlarmMuted"),
768 (0x07, "MuteEnded"),
769 (0x08, "InterconnectSmokeAlarm"),
770 (0x09, "InterconnectCOAlarm"),
771 (0x0A, "AllClear"),
772 ]
773}
774