matc/clusters/codec/
diagnostics_general.rs

1//! Matter TLV encoders and decoders for General Diagnostics Cluster
2//! Cluster ID: 0x0033
3//!
4//! This file is automatically generated from DiagnosticsGeneral.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Import serialization helpers for octet strings
12use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14// Enum definitions
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum BootReason {
19    /// The Node is unable to identify the Power-On reason as one of the other provided enumeration values.
20    Unspecified = 0,
21    /// The Node has booted as the result of physical interaction with the device resulting in a reboot.
22    Poweronreboot = 1,
23    /// The Node has rebooted as the result of a brown-out of the Node's power supply.
24    Brownoutreset = 2,
25    /// The Node has rebooted as the result of a software watchdog timer.
26    Softwarewatchdogreset = 3,
27    /// The Node has rebooted as the result of a hardware watchdog timer.
28    Hardwarewatchdogreset = 4,
29    /// The Node has rebooted as the result of a completed software update.
30    Softwareupdatecompleted = 5,
31    /// The Node has rebooted as the result of a software initiated reboot.
32    Softwarereset = 6,
33}
34
35impl BootReason {
36    /// Convert from u8 value
37    pub fn from_u8(value: u8) -> Option<Self> {
38        match value {
39            0 => Some(BootReason::Unspecified),
40            1 => Some(BootReason::Poweronreboot),
41            2 => Some(BootReason::Brownoutreset),
42            3 => Some(BootReason::Softwarewatchdogreset),
43            4 => Some(BootReason::Hardwarewatchdogreset),
44            5 => Some(BootReason::Softwareupdatecompleted),
45            6 => Some(BootReason::Softwarereset),
46            _ => None,
47        }
48    }
49
50    /// Convert to u8 value
51    pub fn to_u8(self) -> u8 {
52        self as u8
53    }
54}
55
56impl From<BootReason> for u8 {
57    fn from(val: BootReason) -> Self {
58        val as u8
59    }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
63#[repr(u8)]
64pub enum HardwareFault {
65    /// The Node has encountered an unspecified fault.
66    Unspecified = 0,
67    /// The Node has encountered a fault with at least one of its radios.
68    Radio = 1,
69    /// The Node has encountered a fault with at least one of its sensors.
70    Sensor = 2,
71    /// The Node has encountered an over-temperature fault that is resettable.
72    Resettableovertemp = 3,
73    /// The Node has encountered an over-temperature fault that is not resettable.
74    Nonresettableovertemp = 4,
75    /// The Node has encountered a fault with at least one of its power sources.
76    Powersource = 5,
77    /// The Node has encountered a fault with at least one of its visual displays.
78    Visualdisplayfault = 6,
79    /// The Node has encountered a fault with at least one of its audio outputs.
80    Audiooutputfault = 7,
81    /// The Node has encountered a fault with at least one of its user interfaces.
82    Userinterfacefault = 8,
83    /// The Node has encountered a fault with its non-volatile memory.
84    Nonvolatilememoryerror = 9,
85    /// The Node has encountered disallowed physical tampering.
86    Tamperdetected = 10,
87}
88
89impl HardwareFault {
90    /// Convert from u8 value
91    pub fn from_u8(value: u8) -> Option<Self> {
92        match value {
93            0 => Some(HardwareFault::Unspecified),
94            1 => Some(HardwareFault::Radio),
95            2 => Some(HardwareFault::Sensor),
96            3 => Some(HardwareFault::Resettableovertemp),
97            4 => Some(HardwareFault::Nonresettableovertemp),
98            5 => Some(HardwareFault::Powersource),
99            6 => Some(HardwareFault::Visualdisplayfault),
100            7 => Some(HardwareFault::Audiooutputfault),
101            8 => Some(HardwareFault::Userinterfacefault),
102            9 => Some(HardwareFault::Nonvolatilememoryerror),
103            10 => Some(HardwareFault::Tamperdetected),
104            _ => None,
105        }
106    }
107
108    /// Convert to u8 value
109    pub fn to_u8(self) -> u8 {
110        self as u8
111    }
112}
113
114impl From<HardwareFault> for u8 {
115    fn from(val: HardwareFault) -> Self {
116        val as u8
117    }
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
121#[repr(u8)]
122pub enum InterfaceType {
123    /// Indicates an interface of an unspecified type.
124    Unspecified = 0,
125    /// Indicates a Wi-Fi interface.
126    Wifi = 1,
127    /// Indicates a Ethernet interface.
128    Ethernet = 2,
129    /// Indicates a Cellular interface.
130    Cellular = 3,
131    /// Indicates a Thread interface.
132    Thread = 4,
133}
134
135impl InterfaceType {
136    /// Convert from u8 value
137    pub fn from_u8(value: u8) -> Option<Self> {
138        match value {
139            0 => Some(InterfaceType::Unspecified),
140            1 => Some(InterfaceType::Wifi),
141            2 => Some(InterfaceType::Ethernet),
142            3 => Some(InterfaceType::Cellular),
143            4 => Some(InterfaceType::Thread),
144            _ => None,
145        }
146    }
147
148    /// Convert to u8 value
149    pub fn to_u8(self) -> u8 {
150        self as u8
151    }
152}
153
154impl From<InterfaceType> for u8 {
155    fn from(val: InterfaceType) -> Self {
156        val as u8
157    }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
161#[repr(u8)]
162pub enum NetworkFault {
163    /// The Node has encountered an unspecified fault.
164    Unspecified = 0,
165    /// The Node has encountered a network fault as a result of a hardware failure.
166    Hardwarefailure = 1,
167    /// The Node has encountered a network fault as a result of a jammed network.
168    Networkjammed = 2,
169    /// The Node has encountered a network fault as a result of a failure to establish a connection.
170    Connectionfailed = 3,
171}
172
173impl NetworkFault {
174    /// Convert from u8 value
175    pub fn from_u8(value: u8) -> Option<Self> {
176        match value {
177            0 => Some(NetworkFault::Unspecified),
178            1 => Some(NetworkFault::Hardwarefailure),
179            2 => Some(NetworkFault::Networkjammed),
180            3 => Some(NetworkFault::Connectionfailed),
181            _ => None,
182        }
183    }
184
185    /// Convert to u8 value
186    pub fn to_u8(self) -> u8 {
187        self as u8
188    }
189}
190
191impl From<NetworkFault> for u8 {
192    fn from(val: NetworkFault) -> Self {
193        val as u8
194    }
195}
196
197#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
198#[repr(u8)]
199pub enum RadioFault {
200    /// The Node has encountered an unspecified radio fault.
201    Unspecified = 0,
202    /// The Node has encountered a fault with its Wi-Fi radio.
203    Wififault = 1,
204    /// The Node has encountered a fault with its cellular radio.
205    Cellularfault = 2,
206    /// The Node has encountered a fault with its 802.15.4 radio.
207    Threadfault = 3,
208    /// The Node has encountered a fault with its NFC radio.
209    Nfcfault = 4,
210    /// The Node has encountered a fault with its BLE radio.
211    Blefault = 5,
212    /// The Node has encountered a fault with its Ethernet controller.
213    Ethernetfault = 6,
214}
215
216impl RadioFault {
217    /// Convert from u8 value
218    pub fn from_u8(value: u8) -> Option<Self> {
219        match value {
220            0 => Some(RadioFault::Unspecified),
221            1 => Some(RadioFault::Wififault),
222            2 => Some(RadioFault::Cellularfault),
223            3 => Some(RadioFault::Threadfault),
224            4 => Some(RadioFault::Nfcfault),
225            5 => Some(RadioFault::Blefault),
226            6 => Some(RadioFault::Ethernetfault),
227            _ => None,
228        }
229    }
230
231    /// Convert to u8 value
232    pub fn to_u8(self) -> u8 {
233        self as u8
234    }
235}
236
237impl From<RadioFault> for u8 {
238    fn from(val: RadioFault) -> Self {
239        val as u8
240    }
241}
242
243// Struct definitions
244
245#[derive(Debug, serde::Serialize)]
246pub struct NetworkInterface {
247    pub name: Option<String>,
248    pub is_operational: Option<bool>,
249    pub off_premise_services_reachable_i_pv4: Option<bool>,
250    pub off_premise_services_reachable_i_pv6: Option<bool>,
251    pub hardware_address: Option<u8>,
252    pub i_pv4_addresses: Option<Vec<u8>>,
253    pub i_pv6_addresses: Option<Vec<u8>>,
254    pub type_: Option<InterfaceType>,
255}
256
257// Command encoders
258
259/// Encode TestEventTrigger command (0x00)
260pub fn encode_test_event_trigger(enable_key: Vec<u8>, event_trigger: u64) -> anyhow::Result<Vec<u8>> {
261    let tlv = tlv::TlvItemEnc {
262        tag: 0,
263        value: tlv::TlvItemValueEnc::StructInvisible(vec![
264        (0, tlv::TlvItemValueEnc::OctetString(enable_key)).into(),
265        (1, tlv::TlvItemValueEnc::UInt64(event_trigger)).into(),
266        ]),
267    };
268    Ok(tlv.encode()?)
269}
270
271/// Encode PayloadTestRequest command (0x03)
272pub fn encode_payload_test_request(enable_key: Vec<u8>, value: u8, count: u16) -> anyhow::Result<Vec<u8>> {
273    let tlv = tlv::TlvItemEnc {
274        tag: 0,
275        value: tlv::TlvItemValueEnc::StructInvisible(vec![
276        (0, tlv::TlvItemValueEnc::OctetString(enable_key)).into(),
277        (1, tlv::TlvItemValueEnc::UInt8(value)).into(),
278        (2, tlv::TlvItemValueEnc::UInt16(count)).into(),
279        ]),
280    };
281    Ok(tlv.encode()?)
282}
283
284// Attribute decoders
285
286/// Decode NetworkInterfaces attribute (0x0000)
287pub fn decode_network_interfaces(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkInterface>> {
288    let mut res = Vec::new();
289    if let tlv::TlvItemValue::List(v) = inp {
290        for item in v {
291            res.push(NetworkInterface {
292                name: item.get_string_owned(&[0]),
293                is_operational: item.get_bool(&[1]),
294                off_premise_services_reachable_i_pv4: item.get_bool(&[2]),
295                off_premise_services_reachable_i_pv6: item.get_bool(&[3]),
296                hardware_address: item.get_int(&[4]).map(|v| v as u8),
297                i_pv4_addresses: {
298                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[5]) {
299                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
300                        Some(items)
301                    } else {
302                        None
303                    }
304                },
305                i_pv6_addresses: {
306                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[6]) {
307                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
308                        Some(items)
309                    } else {
310                        None
311                    }
312                },
313                type_: item.get_int(&[7]).and_then(|v| InterfaceType::from_u8(v as u8)),
314            });
315        }
316    }
317    Ok(res)
318}
319
320/// Decode RebootCount attribute (0x0001)
321pub fn decode_reboot_count(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
322    if let tlv::TlvItemValue::Int(v) = inp {
323        Ok(*v as u16)
324    } else {
325        Err(anyhow::anyhow!("Expected UInt16"))
326    }
327}
328
329/// Decode UpTime attribute (0x0002)
330pub fn decode_up_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
331    if let tlv::TlvItemValue::Int(v) = inp {
332        Ok(*v)
333    } else {
334        Err(anyhow::anyhow!("Expected UInt64"))
335    }
336}
337
338/// Decode TotalOperationalHours attribute (0x0003)
339pub fn decode_total_operational_hours(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
340    if let tlv::TlvItemValue::Int(v) = inp {
341        Ok(*v as u32)
342    } else {
343        Err(anyhow::anyhow!("Expected UInt32"))
344    }
345}
346
347/// Decode BootReason attribute (0x0004)
348pub fn decode_boot_reason(inp: &tlv::TlvItemValue) -> anyhow::Result<BootReason> {
349    if let tlv::TlvItemValue::Int(v) = inp {
350        BootReason::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
351    } else {
352        Err(anyhow::anyhow!("Expected Integer"))
353    }
354}
355
356/// Decode ActiveHardwareFaults attribute (0x0005)
357pub fn decode_active_hardware_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<HardwareFault>> {
358    let mut res = Vec::new();
359    if let tlv::TlvItemValue::List(v) = inp {
360        for item in v {
361            if let tlv::TlvItemValue::Int(i) = &item.value {
362                if let Some(enum_val) = HardwareFault::from_u8(*i as u8) {
363                    res.push(enum_val);
364                }
365            }
366        }
367    }
368    Ok(res)
369}
370
371/// Decode ActiveRadioFaults attribute (0x0006)
372pub fn decode_active_radio_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RadioFault>> {
373    let mut res = Vec::new();
374    if let tlv::TlvItemValue::List(v) = inp {
375        for item in v {
376            if let tlv::TlvItemValue::Int(i) = &item.value {
377                if let Some(enum_val) = RadioFault::from_u8(*i as u8) {
378                    res.push(enum_val);
379                }
380            }
381        }
382    }
383    Ok(res)
384}
385
386/// Decode ActiveNetworkFaults attribute (0x0007)
387pub fn decode_active_network_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkFault>> {
388    let mut res = Vec::new();
389    if let tlv::TlvItemValue::List(v) = inp {
390        for item in v {
391            if let tlv::TlvItemValue::Int(i) = &item.value {
392                if let Some(enum_val) = NetworkFault::from_u8(*i as u8) {
393                    res.push(enum_val);
394                }
395            }
396        }
397    }
398    Ok(res)
399}
400
401/// Decode TestEventTriggersEnabled attribute (0x0008)
402pub fn decode_test_event_triggers_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
403    if let tlv::TlvItemValue::Bool(v) = inp {
404        Ok(*v)
405    } else {
406        Err(anyhow::anyhow!("Expected Bool"))
407    }
408}
409
410/// Decode DoNotUse attribute (0x0009)
411pub fn decode_do_not_use(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
412    if let tlv::TlvItemValue::Int(v) = inp {
413        Ok(*v as u8)
414    } else {
415        Err(anyhow::anyhow!("Expected UInt8"))
416    }
417}
418
419
420// JSON dispatcher function
421
422/// Decode attribute value and return as JSON string
423///
424/// # Parameters
425/// * `cluster_id` - The cluster identifier
426/// * `attribute_id` - The attribute identifier
427/// * `tlv_value` - The TLV value to decode
428///
429/// # Returns
430/// JSON string representation of the decoded value or error
431pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
432    // Verify this is the correct cluster
433    if cluster_id != 0x0033 {
434        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0033, got {}\"}}", cluster_id);
435    }
436
437    match attribute_id {
438        0x0000 => {
439            match decode_network_interfaces(tlv_value) {
440                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
441                Err(e) => format!("{{\"error\": \"{}\"}}", e),
442            }
443        }
444        0x0001 => {
445            match decode_reboot_count(tlv_value) {
446                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
447                Err(e) => format!("{{\"error\": \"{}\"}}", e),
448            }
449        }
450        0x0002 => {
451            match decode_up_time(tlv_value) {
452                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
453                Err(e) => format!("{{\"error\": \"{}\"}}", e),
454            }
455        }
456        0x0003 => {
457            match decode_total_operational_hours(tlv_value) {
458                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
459                Err(e) => format!("{{\"error\": \"{}\"}}", e),
460            }
461        }
462        0x0004 => {
463            match decode_boot_reason(tlv_value) {
464                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
465                Err(e) => format!("{{\"error\": \"{}\"}}", e),
466            }
467        }
468        0x0005 => {
469            match decode_active_hardware_faults(tlv_value) {
470                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
471                Err(e) => format!("{{\"error\": \"{}\"}}", e),
472            }
473        }
474        0x0006 => {
475            match decode_active_radio_faults(tlv_value) {
476                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
477                Err(e) => format!("{{\"error\": \"{}\"}}", e),
478            }
479        }
480        0x0007 => {
481            match decode_active_network_faults(tlv_value) {
482                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
483                Err(e) => format!("{{\"error\": \"{}\"}}", e),
484            }
485        }
486        0x0008 => {
487            match decode_test_event_triggers_enabled(tlv_value) {
488                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
489                Err(e) => format!("{{\"error\": \"{}\"}}", e),
490            }
491        }
492        0x0009 => {
493            match decode_do_not_use(tlv_value) {
494                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
495                Err(e) => format!("{{\"error\": \"{}\"}}", e),
496            }
497        }
498        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
499    }
500}
501
502/// Get list of all attributes supported by this cluster
503///
504/// # Returns
505/// Vector of tuples containing (attribute_id, attribute_name)
506pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
507    vec![
508        (0x0000, "NetworkInterfaces"),
509        (0x0001, "RebootCount"),
510        (0x0002, "UpTime"),
511        (0x0003, "TotalOperationalHours"),
512        (0x0004, "BootReason"),
513        (0x0005, "ActiveHardwareFaults"),
514        (0x0006, "ActiveRadioFaults"),
515        (0x0007, "ActiveNetworkFaults"),
516        (0x0008, "TestEventTriggersEnabled"),
517        (0x0009, "DoNotUse"),
518    ]
519}
520
521#[derive(Debug, serde::Serialize)]
522pub struct TimeSnapshotResponse {
523    pub system_time_ms: Option<u8>,
524    pub posix_time_ms: Option<u8>,
525}
526
527#[derive(Debug, serde::Serialize)]
528pub struct PayloadTestResponse {
529    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
530    pub payload: Option<Vec<u8>>,
531}
532
533// Command response decoders
534
535/// Decode TimeSnapshotResponse command response (02)
536pub fn decode_time_snapshot_response(inp: &tlv::TlvItemValue) -> anyhow::Result<TimeSnapshotResponse> {
537    if let tlv::TlvItemValue::List(_fields) = inp {
538        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
539        Ok(TimeSnapshotResponse {
540                system_time_ms: item.get_int(&[0]).map(|v| v as u8),
541                posix_time_ms: item.get_int(&[1]).map(|v| v as u8),
542        })
543    } else {
544        Err(anyhow::anyhow!("Expected struct fields"))
545    }
546}
547
548/// Decode PayloadTestResponse command response (04)
549pub fn decode_payload_test_response(inp: &tlv::TlvItemValue) -> anyhow::Result<PayloadTestResponse> {
550    if let tlv::TlvItemValue::List(_fields) = inp {
551        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
552        Ok(PayloadTestResponse {
553                payload: item.get_octet_string_owned(&[0]),
554        })
555    } else {
556        Err(anyhow::anyhow!("Expected struct fields"))
557    }
558}
559
560#[derive(Debug, serde::Serialize)]
561pub struct HardwareFaultChangeEvent {
562    pub current: Option<Vec<HardwareFault>>,
563    pub previous: Option<Vec<HardwareFault>>,
564}
565
566#[derive(Debug, serde::Serialize)]
567pub struct RadioFaultChangeEvent {
568    pub current: Option<Vec<RadioFault>>,
569    pub previous: Option<Vec<RadioFault>>,
570}
571
572#[derive(Debug, serde::Serialize)]
573pub struct NetworkFaultChangeEvent {
574    pub current: Option<Vec<NetworkFault>>,
575    pub previous: Option<Vec<NetworkFault>>,
576}
577
578#[derive(Debug, serde::Serialize)]
579pub struct BootReasonEvent {
580    pub boot_reason: Option<BootReason>,
581}
582
583// Event decoders
584
585/// Decode HardwareFaultChange event (0x00, priority: critical)
586pub fn decode_hardware_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<HardwareFaultChangeEvent> {
587    if let tlv::TlvItemValue::List(_fields) = inp {
588        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
589        Ok(HardwareFaultChangeEvent {
590                                current: {
591                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
592                        let items: Vec<HardwareFault> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { HardwareFault::from_u8(*v as u8) } else { None } }).collect();
593                        Some(items)
594                    } else {
595                        None
596                    }
597                },
598                                previous: {
599                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
600                        let items: Vec<HardwareFault> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { HardwareFault::from_u8(*v as u8) } else { None } }).collect();
601                        Some(items)
602                    } else {
603                        None
604                    }
605                },
606        })
607    } else {
608        Err(anyhow::anyhow!("Expected struct fields"))
609    }
610}
611
612/// Decode RadioFaultChange event (0x01, priority: critical)
613pub fn decode_radio_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<RadioFaultChangeEvent> {
614    if let tlv::TlvItemValue::List(_fields) = inp {
615        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
616        Ok(RadioFaultChangeEvent {
617                                current: {
618                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
619                        let items: Vec<RadioFault> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { RadioFault::from_u8(*v as u8) } else { None } }).collect();
620                        Some(items)
621                    } else {
622                        None
623                    }
624                },
625                                previous: {
626                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
627                        let items: Vec<RadioFault> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { RadioFault::from_u8(*v as u8) } else { None } }).collect();
628                        Some(items)
629                    } else {
630                        None
631                    }
632                },
633        })
634    } else {
635        Err(anyhow::anyhow!("Expected struct fields"))
636    }
637}
638
639/// Decode NetworkFaultChange event (0x02, priority: critical)
640pub fn decode_network_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<NetworkFaultChangeEvent> {
641    if let tlv::TlvItemValue::List(_fields) = inp {
642        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
643        Ok(NetworkFaultChangeEvent {
644                                current: {
645                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
646                        let items: Vec<NetworkFault> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { NetworkFault::from_u8(*v as u8) } else { None } }).collect();
647                        Some(items)
648                    } else {
649                        None
650                    }
651                },
652                                previous: {
653                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
654                        let items: Vec<NetworkFault> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { NetworkFault::from_u8(*v as u8) } else { None } }).collect();
655                        Some(items)
656                    } else {
657                        None
658                    }
659                },
660        })
661    } else {
662        Err(anyhow::anyhow!("Expected struct fields"))
663    }
664}
665
666/// Decode BootReason event (0x03, priority: critical)
667pub fn decode_boot_reason_event(inp: &tlv::TlvItemValue) -> anyhow::Result<BootReasonEvent> {
668    if let tlv::TlvItemValue::List(_fields) = inp {
669        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
670        Ok(BootReasonEvent {
671                                boot_reason: item.get_int(&[0]).and_then(|v| BootReason::from_u8(v as u8)),
672        })
673    } else {
674        Err(anyhow::anyhow!("Expected struct fields"))
675    }
676}
677