Skip to main content

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
6#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13// Import serialization helpers for octet strings
14use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16// Enum definitions
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum BootReason {
21    /// The Node is unable to identify the Power-On reason as one of the other provided enumeration values.
22    Unspecified = 0,
23    /// The Node has booted as the result of physical interaction with the device resulting in a reboot.
24    Poweronreboot = 1,
25    /// The Node has rebooted as the result of a brown-out of the Node's power supply.
26    Brownoutreset = 2,
27    /// The Node has rebooted as the result of a software watchdog timer.
28    Softwarewatchdogreset = 3,
29    /// The Node has rebooted as the result of a hardware watchdog timer.
30    Hardwarewatchdogreset = 4,
31    /// The Node has rebooted as the result of a completed software update.
32    Softwareupdatecompleted = 5,
33    /// The Node has rebooted as the result of a software initiated reboot.
34    Softwarereset = 6,
35}
36
37impl BootReason {
38    /// Convert from u8 value
39    pub fn from_u8(value: u8) -> Option<Self> {
40        match value {
41            0 => Some(BootReason::Unspecified),
42            1 => Some(BootReason::Poweronreboot),
43            2 => Some(BootReason::Brownoutreset),
44            3 => Some(BootReason::Softwarewatchdogreset),
45            4 => Some(BootReason::Hardwarewatchdogreset),
46            5 => Some(BootReason::Softwareupdatecompleted),
47            6 => Some(BootReason::Softwarereset),
48            _ => None,
49        }
50    }
51
52    /// Convert to u8 value
53    pub fn to_u8(self) -> u8 {
54        self as u8
55    }
56}
57
58impl From<BootReason> for u8 {
59    fn from(val: BootReason) -> Self {
60        val as u8
61    }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
65#[repr(u8)]
66pub enum HardwareFault {
67    /// The Node has encountered an unspecified fault.
68    Unspecified = 0,
69    /// The Node has encountered a fault with at least one of its radios.
70    Radio = 1,
71    /// The Node has encountered a fault with at least one of its sensors.
72    Sensor = 2,
73    /// The Node has encountered an over-temperature fault that is resettable.
74    Resettableovertemp = 3,
75    /// The Node has encountered an over-temperature fault that is not resettable.
76    Nonresettableovertemp = 4,
77    /// The Node has encountered a fault with at least one of its power sources.
78    Powersource = 5,
79    /// The Node has encountered a fault with at least one of its visual displays.
80    Visualdisplayfault = 6,
81    /// The Node has encountered a fault with at least one of its audio outputs.
82    Audiooutputfault = 7,
83    /// The Node has encountered a fault with at least one of its user interfaces.
84    Userinterfacefault = 8,
85    /// The Node has encountered a fault with its non-volatile memory.
86    Nonvolatilememoryerror = 9,
87    /// The Node has encountered disallowed physical tampering.
88    Tamperdetected = 10,
89}
90
91impl HardwareFault {
92    /// Convert from u8 value
93    pub fn from_u8(value: u8) -> Option<Self> {
94        match value {
95            0 => Some(HardwareFault::Unspecified),
96            1 => Some(HardwareFault::Radio),
97            2 => Some(HardwareFault::Sensor),
98            3 => Some(HardwareFault::Resettableovertemp),
99            4 => Some(HardwareFault::Nonresettableovertemp),
100            5 => Some(HardwareFault::Powersource),
101            6 => Some(HardwareFault::Visualdisplayfault),
102            7 => Some(HardwareFault::Audiooutputfault),
103            8 => Some(HardwareFault::Userinterfacefault),
104            9 => Some(HardwareFault::Nonvolatilememoryerror),
105            10 => Some(HardwareFault::Tamperdetected),
106            _ => None,
107        }
108    }
109
110    /// Convert to u8 value
111    pub fn to_u8(self) -> u8 {
112        self as u8
113    }
114}
115
116impl From<HardwareFault> for u8 {
117    fn from(val: HardwareFault) -> Self {
118        val as u8
119    }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
123#[repr(u8)]
124pub enum InterfaceType {
125    /// Indicates an interface of an unspecified type.
126    Unspecified = 0,
127    /// Indicates a Wi-Fi interface.
128    Wifi = 1,
129    /// Indicates a Ethernet interface.
130    Ethernet = 2,
131    /// Indicates a Cellular interface.
132    Cellular = 3,
133    /// Indicates a Thread interface.
134    Thread = 4,
135}
136
137impl InterfaceType {
138    /// Convert from u8 value
139    pub fn from_u8(value: u8) -> Option<Self> {
140        match value {
141            0 => Some(InterfaceType::Unspecified),
142            1 => Some(InterfaceType::Wifi),
143            2 => Some(InterfaceType::Ethernet),
144            3 => Some(InterfaceType::Cellular),
145            4 => Some(InterfaceType::Thread),
146            _ => None,
147        }
148    }
149
150    /// Convert to u8 value
151    pub fn to_u8(self) -> u8 {
152        self as u8
153    }
154}
155
156impl From<InterfaceType> for u8 {
157    fn from(val: InterfaceType) -> Self {
158        val as u8
159    }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
163#[repr(u8)]
164pub enum NetworkFault {
165    /// The Node has encountered an unspecified fault.
166    Unspecified = 0,
167    /// The Node has encountered a network fault as a result of a hardware failure.
168    Hardwarefailure = 1,
169    /// The Node has encountered a network fault as a result of a jammed network.
170    Networkjammed = 2,
171    /// The Node has encountered a network fault as a result of a failure to establish a connection.
172    Connectionfailed = 3,
173}
174
175impl NetworkFault {
176    /// Convert from u8 value
177    pub fn from_u8(value: u8) -> Option<Self> {
178        match value {
179            0 => Some(NetworkFault::Unspecified),
180            1 => Some(NetworkFault::Hardwarefailure),
181            2 => Some(NetworkFault::Networkjammed),
182            3 => Some(NetworkFault::Connectionfailed),
183            _ => None,
184        }
185    }
186
187    /// Convert to u8 value
188    pub fn to_u8(self) -> u8 {
189        self as u8
190    }
191}
192
193impl From<NetworkFault> for u8 {
194    fn from(val: NetworkFault) -> Self {
195        val as u8
196    }
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
200#[repr(u8)]
201pub enum RadioFault {
202    /// The Node has encountered an unspecified radio fault.
203    Unspecified = 0,
204    /// The Node has encountered a fault with its Wi-Fi radio.
205    Wififault = 1,
206    /// The Node has encountered a fault with its cellular radio.
207    Cellularfault = 2,
208    /// The Node has encountered a fault with its 802.15.4 radio.
209    Threadfault = 3,
210    /// The Node has encountered a fault with its NFC radio.
211    Nfcfault = 4,
212    /// The Node has encountered a fault with its BLE radio.
213    Blefault = 5,
214    /// The Node has encountered a fault with its Ethernet controller.
215    Ethernetfault = 6,
216}
217
218impl RadioFault {
219    /// Convert from u8 value
220    pub fn from_u8(value: u8) -> Option<Self> {
221        match value {
222            0 => Some(RadioFault::Unspecified),
223            1 => Some(RadioFault::Wififault),
224            2 => Some(RadioFault::Cellularfault),
225            3 => Some(RadioFault::Threadfault),
226            4 => Some(RadioFault::Nfcfault),
227            5 => Some(RadioFault::Blefault),
228            6 => Some(RadioFault::Ethernetfault),
229            _ => None,
230        }
231    }
232
233    /// Convert to u8 value
234    pub fn to_u8(self) -> u8 {
235        self as u8
236    }
237}
238
239impl From<RadioFault> for u8 {
240    fn from(val: RadioFault) -> Self {
241        val as u8
242    }
243}
244
245// Struct definitions
246
247#[derive(Debug, serde::Serialize)]
248pub struct DeviceLoad {
249    pub current_subscriptions: Option<u16>,
250    pub current_subscriptions_for_fabric: Option<u16>,
251    pub total_subscriptions_established: Option<u32>,
252    pub total_interaction_model_messages_sent: Option<u32>,
253    pub total_interaction_model_messages_received: Option<u32>,
254}
255
256#[derive(Debug, serde::Serialize)]
257pub struct NetworkInterface {
258    pub name: Option<String>,
259    pub is_operational: Option<bool>,
260    pub off_premise_services_reachable_i_pv4: Option<bool>,
261    pub off_premise_services_reachable_i_pv6: Option<bool>,
262    pub hardware_address: Option<u8>,
263    pub i_pv4_addresses: Option<Vec<u8>>,
264    pub i_pv6_addresses: Option<Vec<u8>>,
265    pub type_: Option<InterfaceType>,
266}
267
268// Command encoders
269
270/// Encode TestEventTrigger command (0x00)
271pub fn encode_test_event_trigger(enable_key: Vec<u8>, event_trigger: u64) -> anyhow::Result<Vec<u8>> {
272    let tlv = tlv::TlvItemEnc {
273        tag: 0,
274        value: tlv::TlvItemValueEnc::StructInvisible(vec![
275        (0, tlv::TlvItemValueEnc::OctetString(enable_key)).into(),
276        (1, tlv::TlvItemValueEnc::UInt64(event_trigger)).into(),
277        ]),
278    };
279    Ok(tlv.encode()?)
280}
281
282/// Encode PayloadTestRequest command (0x03)
283pub fn encode_payload_test_request(enable_key: Vec<u8>, value: u8, count: u16) -> anyhow::Result<Vec<u8>> {
284    let tlv = tlv::TlvItemEnc {
285        tag: 0,
286        value: tlv::TlvItemValueEnc::StructInvisible(vec![
287        (0, tlv::TlvItemValueEnc::OctetString(enable_key)).into(),
288        (1, tlv::TlvItemValueEnc::UInt8(value)).into(),
289        (2, tlv::TlvItemValueEnc::UInt16(count)).into(),
290        ]),
291    };
292    Ok(tlv.encode()?)
293}
294
295// Attribute decoders
296
297/// Decode NetworkInterfaces attribute (0x0000)
298pub fn decode_network_interfaces(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkInterface>> {
299    let mut res = Vec::new();
300    if let tlv::TlvItemValue::List(v) = inp {
301        for item in v {
302            res.push(NetworkInterface {
303                name: item.get_string_owned(&[0]),
304                is_operational: item.get_bool(&[1]),
305                off_premise_services_reachable_i_pv4: item.get_bool(&[2]),
306                off_premise_services_reachable_i_pv6: item.get_bool(&[3]),
307                hardware_address: item.get_int(&[4]).map(|v| v as u8),
308                i_pv4_addresses: {
309                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[5]) {
310                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
311                        Some(items)
312                    } else {
313                        None
314                    }
315                },
316                i_pv6_addresses: {
317                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[6]) {
318                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
319                        Some(items)
320                    } else {
321                        None
322                    }
323                },
324                type_: item.get_int(&[7]).and_then(|v| InterfaceType::from_u8(v as u8)),
325            });
326        }
327    }
328    Ok(res)
329}
330
331/// Decode RebootCount attribute (0x0001)
332pub fn decode_reboot_count(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
333    if let tlv::TlvItemValue::Int(v) = inp {
334        Ok(*v as u16)
335    } else {
336        Err(anyhow::anyhow!("Expected UInt16"))
337    }
338}
339
340/// Decode UpTime attribute (0x0002)
341pub fn decode_up_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
342    if let tlv::TlvItemValue::Int(v) = inp {
343        Ok(*v)
344    } else {
345        Err(anyhow::anyhow!("Expected UInt64"))
346    }
347}
348
349/// Decode TotalOperationalHours attribute (0x0003)
350pub fn decode_total_operational_hours(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
351    if let tlv::TlvItemValue::Int(v) = inp {
352        Ok(*v as u32)
353    } else {
354        Err(anyhow::anyhow!("Expected UInt32"))
355    }
356}
357
358/// Decode BootReason attribute (0x0004)
359pub fn decode_boot_reason(inp: &tlv::TlvItemValue) -> anyhow::Result<BootReason> {
360    if let tlv::TlvItemValue::Int(v) = inp {
361        BootReason::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
362    } else {
363        Err(anyhow::anyhow!("Expected Integer"))
364    }
365}
366
367/// Decode ActiveHardwareFaults attribute (0x0005)
368pub fn decode_active_hardware_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<HardwareFault>> {
369    let mut res = Vec::new();
370    if let tlv::TlvItemValue::List(v) = inp {
371        for item in v {
372            if let tlv::TlvItemValue::Int(i) = &item.value {
373                if let Some(enum_val) = HardwareFault::from_u8(*i as u8) {
374                    res.push(enum_val);
375                }
376            }
377        }
378    }
379    Ok(res)
380}
381
382/// Decode ActiveRadioFaults attribute (0x0006)
383pub fn decode_active_radio_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RadioFault>> {
384    let mut res = Vec::new();
385    if let tlv::TlvItemValue::List(v) = inp {
386        for item in v {
387            if let tlv::TlvItemValue::Int(i) = &item.value {
388                if let Some(enum_val) = RadioFault::from_u8(*i as u8) {
389                    res.push(enum_val);
390                }
391            }
392        }
393    }
394    Ok(res)
395}
396
397/// Decode ActiveNetworkFaults attribute (0x0007)
398pub fn decode_active_network_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkFault>> {
399    let mut res = Vec::new();
400    if let tlv::TlvItemValue::List(v) = inp {
401        for item in v {
402            if let tlv::TlvItemValue::Int(i) = &item.value {
403                if let Some(enum_val) = NetworkFault::from_u8(*i as u8) {
404                    res.push(enum_val);
405                }
406            }
407        }
408    }
409    Ok(res)
410}
411
412/// Decode TestEventTriggersEnabled attribute (0x0008)
413pub fn decode_test_event_triggers_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
414    if let tlv::TlvItemValue::Bool(v) = inp {
415        Ok(*v)
416    } else {
417        Err(anyhow::anyhow!("Expected Bool"))
418    }
419}
420
421/// Decode DoNotUse attribute (0x0009)
422pub fn decode_do_not_use(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
423    if let tlv::TlvItemValue::Int(v) = inp {
424        Ok(*v as u8)
425    } else {
426        Err(anyhow::anyhow!("Expected UInt8"))
427    }
428}
429
430/// Decode DeviceLoadStatus attribute (0x000A)
431pub fn decode_device_load_status(inp: &tlv::TlvItemValue) -> anyhow::Result<DeviceLoad> {
432    if let tlv::TlvItemValue::List(_fields) = inp {
433        // Struct with fields
434        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
435        Ok(DeviceLoad {
436                current_subscriptions: item.get_int(&[0]).map(|v| v as u16),
437                current_subscriptions_for_fabric: item.get_int(&[1]).map(|v| v as u16),
438                total_subscriptions_established: item.get_int(&[2]).map(|v| v as u32),
439                total_interaction_model_messages_sent: item.get_int(&[3]).map(|v| v as u32),
440                total_interaction_model_messages_received: item.get_int(&[4]).map(|v| v as u32),
441        })
442    } else {
443        Err(anyhow::anyhow!("Expected struct fields"))
444    }
445}
446
447
448// JSON dispatcher function
449
450/// Decode attribute value and return as JSON string
451///
452/// # Parameters
453/// * `cluster_id` - The cluster identifier
454/// * `attribute_id` - The attribute identifier
455/// * `tlv_value` - The TLV value to decode
456///
457/// # Returns
458/// JSON string representation of the decoded value or error
459pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
460    // Verify this is the correct cluster
461    if cluster_id != 0x0033 {
462        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0033, got {}\"}}", cluster_id);
463    }
464
465    match attribute_id {
466        0x0000 => {
467            match decode_network_interfaces(tlv_value) {
468                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
469                Err(e) => format!("{{\"error\": \"{}\"}}", e),
470            }
471        }
472        0x0001 => {
473            match decode_reboot_count(tlv_value) {
474                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
475                Err(e) => format!("{{\"error\": \"{}\"}}", e),
476            }
477        }
478        0x0002 => {
479            match decode_up_time(tlv_value) {
480                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
481                Err(e) => format!("{{\"error\": \"{}\"}}", e),
482            }
483        }
484        0x0003 => {
485            match decode_total_operational_hours(tlv_value) {
486                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
487                Err(e) => format!("{{\"error\": \"{}\"}}", e),
488            }
489        }
490        0x0004 => {
491            match decode_boot_reason(tlv_value) {
492                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
493                Err(e) => format!("{{\"error\": \"{}\"}}", e),
494            }
495        }
496        0x0005 => {
497            match decode_active_hardware_faults(tlv_value) {
498                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
499                Err(e) => format!("{{\"error\": \"{}\"}}", e),
500            }
501        }
502        0x0006 => {
503            match decode_active_radio_faults(tlv_value) {
504                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
505                Err(e) => format!("{{\"error\": \"{}\"}}", e),
506            }
507        }
508        0x0007 => {
509            match decode_active_network_faults(tlv_value) {
510                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
511                Err(e) => format!("{{\"error\": \"{}\"}}", e),
512            }
513        }
514        0x0008 => {
515            match decode_test_event_triggers_enabled(tlv_value) {
516                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
517                Err(e) => format!("{{\"error\": \"{}\"}}", e),
518            }
519        }
520        0x0009 => {
521            match decode_do_not_use(tlv_value) {
522                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
523                Err(e) => format!("{{\"error\": \"{}\"}}", e),
524            }
525        }
526        0x000A => {
527            match decode_device_load_status(tlv_value) {
528                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
529                Err(e) => format!("{{\"error\": \"{}\"}}", e),
530            }
531        }
532        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
533    }
534}
535
536/// Get list of all attributes supported by this cluster
537///
538/// # Returns
539/// Vector of tuples containing (attribute_id, attribute_name)
540pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
541    vec![
542        (0x0000, "NetworkInterfaces"),
543        (0x0001, "RebootCount"),
544        (0x0002, "UpTime"),
545        (0x0003, "TotalOperationalHours"),
546        (0x0004, "BootReason"),
547        (0x0005, "ActiveHardwareFaults"),
548        (0x0006, "ActiveRadioFaults"),
549        (0x0007, "ActiveNetworkFaults"),
550        (0x0008, "TestEventTriggersEnabled"),
551        (0x0009, "DoNotUse"),
552        (0x000A, "DeviceLoadStatus"),
553    ]
554}
555
556// Command listing
557
558pub fn get_command_list() -> Vec<(u32, &'static str)> {
559    vec![
560        (0x00, "TestEventTrigger"),
561        (0x01, "TimeSnapshot"),
562        (0x03, "PayloadTestRequest"),
563    ]
564}
565
566pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
567    match cmd_id {
568        0x00 => Some("TestEventTrigger"),
569        0x01 => Some("TimeSnapshot"),
570        0x03 => Some("PayloadTestRequest"),
571        _ => None,
572    }
573}
574
575pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
576    match cmd_id {
577        0x00 => Some(vec![
578            crate::clusters::codec::CommandField { tag: 0, name: "enable_key", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
579            crate::clusters::codec::CommandField { tag: 1, name: "event_trigger", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
580        ]),
581        0x01 => Some(vec![]),
582        0x03 => Some(vec![
583            crate::clusters::codec::CommandField { tag: 0, name: "enable_key", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
584            crate::clusters::codec::CommandField { tag: 1, name: "value", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
585            crate::clusters::codec::CommandField { tag: 2, name: "count", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
586        ]),
587        _ => None,
588    }
589}
590
591pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
592    match cmd_id {
593        0x00 => {
594        let enable_key = crate::clusters::codec::json_util::get_octstr(args, "enable_key")?;
595        let event_trigger = crate::clusters::codec::json_util::get_u64(args, "event_trigger")?;
596        encode_test_event_trigger(enable_key, event_trigger)
597        }
598        0x01 => Ok(vec![]),
599        0x03 => {
600        let enable_key = crate::clusters::codec::json_util::get_octstr(args, "enable_key")?;
601        let value = crate::clusters::codec::json_util::get_u8(args, "value")?;
602        let count = crate::clusters::codec::json_util::get_u16(args, "count")?;
603        encode_payload_test_request(enable_key, value, count)
604        }
605        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
606    }
607}
608
609#[derive(Debug, serde::Serialize)]
610pub struct TimeSnapshotResponse {
611    pub system_time_ms: Option<u8>,
612    pub posix_time_ms: Option<u8>,
613}
614
615#[derive(Debug, serde::Serialize)]
616pub struct PayloadTestResponse {
617    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
618    pub payload: Option<Vec<u8>>,
619}
620
621// Command response decoders
622
623/// Decode TimeSnapshotResponse command response (02)
624pub fn decode_time_snapshot_response(inp: &tlv::TlvItemValue) -> anyhow::Result<TimeSnapshotResponse> {
625    if let tlv::TlvItemValue::List(_fields) = inp {
626        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
627        Ok(TimeSnapshotResponse {
628                system_time_ms: item.get_int(&[0]).map(|v| v as u8),
629                posix_time_ms: item.get_int(&[1]).map(|v| v as u8),
630        })
631    } else {
632        Err(anyhow::anyhow!("Expected struct fields"))
633    }
634}
635
636/// Decode PayloadTestResponse command response (04)
637pub fn decode_payload_test_response(inp: &tlv::TlvItemValue) -> anyhow::Result<PayloadTestResponse> {
638    if let tlv::TlvItemValue::List(_fields) = inp {
639        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
640        Ok(PayloadTestResponse {
641                payload: item.get_octet_string_owned(&[0]),
642        })
643    } else {
644        Err(anyhow::anyhow!("Expected struct fields"))
645    }
646}
647
648// Typed facade (invokes + reads)
649
650/// Invoke `TestEventTrigger` command on cluster `General Diagnostics`.
651pub async fn test_event_trigger(conn: &crate::controller::Connection, endpoint: u16, enable_key: Vec<u8>, event_trigger: u64) -> anyhow::Result<()> {
652    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_CMD_ID_TESTEVENTTRIGGER, &encode_test_event_trigger(enable_key, event_trigger)?).await?;
653    Ok(())
654}
655
656/// Invoke `TimeSnapshot` command on cluster `General Diagnostics`.
657pub async fn time_snapshot(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TimeSnapshotResponse> {
658    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_CMD_ID_TIMESNAPSHOT, &[]).await?;
659    decode_time_snapshot_response(&tlv)
660}
661
662/// Invoke `PayloadTestRequest` command on cluster `General Diagnostics`.
663pub async fn payload_test_request(conn: &crate::controller::Connection, endpoint: u16, enable_key: Vec<u8>, value: u8, count: u16) -> anyhow::Result<PayloadTestResponse> {
664    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_CMD_ID_PAYLOADTESTREQUEST, &encode_payload_test_request(enable_key, value, count)?).await?;
665    decode_payload_test_response(&tlv)
666}
667
668/// Read `NetworkInterfaces` attribute from cluster `General Diagnostics`.
669pub async fn read_network_interfaces(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NetworkInterface>> {
670    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_NETWORKINTERFACES).await?;
671    decode_network_interfaces(&tlv)
672}
673
674/// Read `RebootCount` attribute from cluster `General Diagnostics`.
675pub async fn read_reboot_count(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
676    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_REBOOTCOUNT).await?;
677    decode_reboot_count(&tlv)
678}
679
680/// Read `UpTime` attribute from cluster `General Diagnostics`.
681pub async fn read_up_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u64> {
682    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_UPTIME).await?;
683    decode_up_time(&tlv)
684}
685
686/// Read `TotalOperationalHours` attribute from cluster `General Diagnostics`.
687pub async fn read_total_operational_hours(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
688    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_TOTALOPERATIONALHOURS).await?;
689    decode_total_operational_hours(&tlv)
690}
691
692/// Read `BootReason` attribute from cluster `General Diagnostics`.
693pub async fn read_boot_reason(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<BootReason> {
694    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_BOOTREASON).await?;
695    decode_boot_reason(&tlv)
696}
697
698/// Read `ActiveHardwareFaults` attribute from cluster `General Diagnostics`.
699pub async fn read_active_hardware_faults(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<HardwareFault>> {
700    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_ACTIVEHARDWAREFAULTS).await?;
701    decode_active_hardware_faults(&tlv)
702}
703
704/// Read `ActiveRadioFaults` attribute from cluster `General Diagnostics`.
705pub async fn read_active_radio_faults(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<RadioFault>> {
706    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_ACTIVERADIOFAULTS).await?;
707    decode_active_radio_faults(&tlv)
708}
709
710/// Read `ActiveNetworkFaults` attribute from cluster `General Diagnostics`.
711pub async fn read_active_network_faults(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NetworkFault>> {
712    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_ACTIVENETWORKFAULTS).await?;
713    decode_active_network_faults(&tlv)
714}
715
716/// Read `TestEventTriggersEnabled` attribute from cluster `General Diagnostics`.
717pub async fn read_test_event_triggers_enabled(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
718    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_TESTEVENTTRIGGERSENABLED).await?;
719    decode_test_event_triggers_enabled(&tlv)
720}
721
722/// Read `DoNotUse` attribute from cluster `General Diagnostics`.
723pub async fn read_do_not_use(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
724    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_DONOTUSE).await?;
725    decode_do_not_use(&tlv)
726}
727
728/// Read `DeviceLoadStatus` attribute from cluster `General Diagnostics`.
729pub async fn read_device_load_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<DeviceLoad> {
730    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_DEVICELOADSTATUS).await?;
731    decode_device_load_status(&tlv)
732}
733
734#[derive(Debug, serde::Serialize)]
735pub struct HardwareFaultChangeEvent {
736    pub current: Option<Vec<HardwareFault>>,
737    pub previous: Option<Vec<HardwareFault>>,
738}
739
740#[derive(Debug, serde::Serialize)]
741pub struct RadioFaultChangeEvent {
742    pub current: Option<Vec<RadioFault>>,
743    pub previous: Option<Vec<RadioFault>>,
744}
745
746#[derive(Debug, serde::Serialize)]
747pub struct NetworkFaultChangeEvent {
748    pub current: Option<Vec<NetworkFault>>,
749    pub previous: Option<Vec<NetworkFault>>,
750}
751
752#[derive(Debug, serde::Serialize)]
753pub struct BootReasonEvent {
754    pub boot_reason: Option<BootReason>,
755}
756
757// Event decoders
758
759/// Decode HardwareFaultChange event (0x00, priority: critical)
760pub fn decode_hardware_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<HardwareFaultChangeEvent> {
761    if let tlv::TlvItemValue::List(_fields) = inp {
762        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
763        Ok(HardwareFaultChangeEvent {
764                                current: {
765                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
766                        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();
767                        Some(items)
768                    } else {
769                        None
770                    }
771                },
772                                previous: {
773                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
774                        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();
775                        Some(items)
776                    } else {
777                        None
778                    }
779                },
780        })
781    } else {
782        Err(anyhow::anyhow!("Expected struct fields"))
783    }
784}
785
786/// Decode RadioFaultChange event (0x01, priority: critical)
787pub fn decode_radio_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<RadioFaultChangeEvent> {
788    if let tlv::TlvItemValue::List(_fields) = inp {
789        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
790        Ok(RadioFaultChangeEvent {
791                                current: {
792                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
793                        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();
794                        Some(items)
795                    } else {
796                        None
797                    }
798                },
799                                previous: {
800                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
801                        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();
802                        Some(items)
803                    } else {
804                        None
805                    }
806                },
807        })
808    } else {
809        Err(anyhow::anyhow!("Expected struct fields"))
810    }
811}
812
813/// Decode NetworkFaultChange event (0x02, priority: critical)
814pub fn decode_network_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<NetworkFaultChangeEvent> {
815    if let tlv::TlvItemValue::List(_fields) = inp {
816        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
817        Ok(NetworkFaultChangeEvent {
818                                current: {
819                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
820                        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();
821                        Some(items)
822                    } else {
823                        None
824                    }
825                },
826                                previous: {
827                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
828                        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();
829                        Some(items)
830                    } else {
831                        None
832                    }
833                },
834        })
835    } else {
836        Err(anyhow::anyhow!("Expected struct fields"))
837    }
838}
839
840/// Decode BootReason event (0x03, priority: critical)
841pub fn decode_boot_reason_event(inp: &tlv::TlvItemValue) -> anyhow::Result<BootReasonEvent> {
842    if let tlv::TlvItemValue::List(_fields) = inp {
843        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
844        Ok(BootReasonEvent {
845                                boot_reason: item.get_int(&[0]).and_then(|v| BootReason::from_u8(v as u8)),
846        })
847    } else {
848        Err(anyhow::anyhow!("Expected struct fields"))
849    }
850}
851
852
853// Event JSON dispatcher
854
855/// Decode event value and return as JSON string
856pub fn decode_event_json(cluster_id: u32, event_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
857    if cluster_id != 0x0033 {
858        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0033, got {}\"}}", cluster_id);
859    }
860
861    match event_id {
862        0x00 => {
863            match decode_hardware_fault_change_event(tlv_value) {
864                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
865                Err(e) => format!("{{\"error\": \"{}\"}}", e),
866            }
867        }
868        0x01 => {
869            match decode_radio_fault_change_event(tlv_value) {
870                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
871                Err(e) => format!("{{\"error\": \"{}\"}}", e),
872            }
873        }
874        0x02 => {
875            match decode_network_fault_change_event(tlv_value) {
876                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
877                Err(e) => format!("{{\"error\": \"{}\"}}", e),
878            }
879        }
880        0x03 => {
881            match decode_boot_reason_event(tlv_value) {
882                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
883                Err(e) => format!("{{\"error\": \"{}\"}}", e),
884            }
885        }
886        _ => format!("{{\"error\": \"Unknown event ID: {}\"}}", event_id),
887    }
888}
889
890/// Get list of all events supported by this cluster
891///
892/// # Returns
893/// Vector of tuples containing (event_id, event_name)
894pub fn get_event_list() -> Vec<(u32, &'static str)> {
895    vec![
896        (0x00, "HardwareFaultChange"),
897        (0x01, "RadioFaultChange"),
898        (0x02, "NetworkFaultChange"),
899        (0x03, "BootReason"),
900    ]
901}
902