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 NetworkInterface {
249    pub name: Option<String>,
250    pub is_operational: Option<bool>,
251    pub off_premise_services_reachable_i_pv4: Option<bool>,
252    pub off_premise_services_reachable_i_pv6: Option<bool>,
253    pub hardware_address: Option<u8>,
254    pub i_pv4_addresses: Option<Vec<u8>>,
255    pub i_pv6_addresses: Option<Vec<u8>>,
256    pub type_: Option<InterfaceType>,
257}
258
259// Command encoders
260
261/// Encode TestEventTrigger command (0x00)
262pub fn encode_test_event_trigger(enable_key: Vec<u8>, event_trigger: u64) -> anyhow::Result<Vec<u8>> {
263    let tlv = tlv::TlvItemEnc {
264        tag: 0,
265        value: tlv::TlvItemValueEnc::StructInvisible(vec![
266        (0, tlv::TlvItemValueEnc::OctetString(enable_key)).into(),
267        (1, tlv::TlvItemValueEnc::UInt64(event_trigger)).into(),
268        ]),
269    };
270    Ok(tlv.encode()?)
271}
272
273/// Encode PayloadTestRequest command (0x03)
274pub fn encode_payload_test_request(enable_key: Vec<u8>, value: u8, count: u16) -> anyhow::Result<Vec<u8>> {
275    let tlv = tlv::TlvItemEnc {
276        tag: 0,
277        value: tlv::TlvItemValueEnc::StructInvisible(vec![
278        (0, tlv::TlvItemValueEnc::OctetString(enable_key)).into(),
279        (1, tlv::TlvItemValueEnc::UInt8(value)).into(),
280        (2, tlv::TlvItemValueEnc::UInt16(count)).into(),
281        ]),
282    };
283    Ok(tlv.encode()?)
284}
285
286// Attribute decoders
287
288/// Decode NetworkInterfaces attribute (0x0000)
289pub fn decode_network_interfaces(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkInterface>> {
290    let mut res = Vec::new();
291    if let tlv::TlvItemValue::List(v) = inp {
292        for item in v {
293            res.push(NetworkInterface {
294                name: item.get_string_owned(&[0]),
295                is_operational: item.get_bool(&[1]),
296                off_premise_services_reachable_i_pv4: item.get_bool(&[2]),
297                off_premise_services_reachable_i_pv6: item.get_bool(&[3]),
298                hardware_address: item.get_int(&[4]).map(|v| v as u8),
299                i_pv4_addresses: {
300                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[5]) {
301                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
302                        Some(items)
303                    } else {
304                        None
305                    }
306                },
307                i_pv6_addresses: {
308                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[6]) {
309                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
310                        Some(items)
311                    } else {
312                        None
313                    }
314                },
315                type_: item.get_int(&[7]).and_then(|v| InterfaceType::from_u8(v as u8)),
316            });
317        }
318    }
319    Ok(res)
320}
321
322/// Decode RebootCount attribute (0x0001)
323pub fn decode_reboot_count(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
324    if let tlv::TlvItemValue::Int(v) = inp {
325        Ok(*v as u16)
326    } else {
327        Err(anyhow::anyhow!("Expected UInt16"))
328    }
329}
330
331/// Decode UpTime attribute (0x0002)
332pub fn decode_up_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
333    if let tlv::TlvItemValue::Int(v) = inp {
334        Ok(*v)
335    } else {
336        Err(anyhow::anyhow!("Expected UInt64"))
337    }
338}
339
340/// Decode TotalOperationalHours attribute (0x0003)
341pub fn decode_total_operational_hours(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
342    if let tlv::TlvItemValue::Int(v) = inp {
343        Ok(*v as u32)
344    } else {
345        Err(anyhow::anyhow!("Expected UInt32"))
346    }
347}
348
349/// Decode BootReason attribute (0x0004)
350pub fn decode_boot_reason(inp: &tlv::TlvItemValue) -> anyhow::Result<BootReason> {
351    if let tlv::TlvItemValue::Int(v) = inp {
352        BootReason::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
353    } else {
354        Err(anyhow::anyhow!("Expected Integer"))
355    }
356}
357
358/// Decode ActiveHardwareFaults attribute (0x0005)
359pub fn decode_active_hardware_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<HardwareFault>> {
360    let mut res = Vec::new();
361    if let tlv::TlvItemValue::List(v) = inp {
362        for item in v {
363            if let tlv::TlvItemValue::Int(i) = &item.value {
364                if let Some(enum_val) = HardwareFault::from_u8(*i as u8) {
365                    res.push(enum_val);
366                }
367            }
368        }
369    }
370    Ok(res)
371}
372
373/// Decode ActiveRadioFaults attribute (0x0006)
374pub fn decode_active_radio_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RadioFault>> {
375    let mut res = Vec::new();
376    if let tlv::TlvItemValue::List(v) = inp {
377        for item in v {
378            if let tlv::TlvItemValue::Int(i) = &item.value {
379                if let Some(enum_val) = RadioFault::from_u8(*i as u8) {
380                    res.push(enum_val);
381                }
382            }
383        }
384    }
385    Ok(res)
386}
387
388/// Decode ActiveNetworkFaults attribute (0x0007)
389pub fn decode_active_network_faults(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkFault>> {
390    let mut res = Vec::new();
391    if let tlv::TlvItemValue::List(v) = inp {
392        for item in v {
393            if let tlv::TlvItemValue::Int(i) = &item.value {
394                if let Some(enum_val) = NetworkFault::from_u8(*i as u8) {
395                    res.push(enum_val);
396                }
397            }
398        }
399    }
400    Ok(res)
401}
402
403/// Decode TestEventTriggersEnabled attribute (0x0008)
404pub fn decode_test_event_triggers_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
405    if let tlv::TlvItemValue::Bool(v) = inp {
406        Ok(*v)
407    } else {
408        Err(anyhow::anyhow!("Expected Bool"))
409    }
410}
411
412/// Decode DoNotUse attribute (0x0009)
413pub fn decode_do_not_use(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
414    if let tlv::TlvItemValue::Int(v) = inp {
415        Ok(*v as u8)
416    } else {
417        Err(anyhow::anyhow!("Expected UInt8"))
418    }
419}
420
421
422// JSON dispatcher function
423
424/// Decode attribute value and return as JSON string
425///
426/// # Parameters
427/// * `cluster_id` - The cluster identifier
428/// * `attribute_id` - The attribute identifier
429/// * `tlv_value` - The TLV value to decode
430///
431/// # Returns
432/// JSON string representation of the decoded value or error
433pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
434    // Verify this is the correct cluster
435    if cluster_id != 0x0033 {
436        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0033, got {}\"}}", cluster_id);
437    }
438
439    match attribute_id {
440        0x0000 => {
441            match decode_network_interfaces(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        0x0001 => {
447            match decode_reboot_count(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        0x0002 => {
453            match decode_up_time(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        0x0003 => {
459            match decode_total_operational_hours(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        0x0004 => {
465            match decode_boot_reason(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        0x0005 => {
471            match decode_active_hardware_faults(tlv_value) {
472                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
473                Err(e) => format!("{{\"error\": \"{}\"}}", e),
474            }
475        }
476        0x0006 => {
477            match decode_active_radio_faults(tlv_value) {
478                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
479                Err(e) => format!("{{\"error\": \"{}\"}}", e),
480            }
481        }
482        0x0007 => {
483            match decode_active_network_faults(tlv_value) {
484                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
485                Err(e) => format!("{{\"error\": \"{}\"}}", e),
486            }
487        }
488        0x0008 => {
489            match decode_test_event_triggers_enabled(tlv_value) {
490                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
491                Err(e) => format!("{{\"error\": \"{}\"}}", e),
492            }
493        }
494        0x0009 => {
495            match decode_do_not_use(tlv_value) {
496                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
497                Err(e) => format!("{{\"error\": \"{}\"}}", e),
498            }
499        }
500        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
501    }
502}
503
504/// Get list of all attributes supported by this cluster
505///
506/// # Returns
507/// Vector of tuples containing (attribute_id, attribute_name)
508pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
509    vec![
510        (0x0000, "NetworkInterfaces"),
511        (0x0001, "RebootCount"),
512        (0x0002, "UpTime"),
513        (0x0003, "TotalOperationalHours"),
514        (0x0004, "BootReason"),
515        (0x0005, "ActiveHardwareFaults"),
516        (0x0006, "ActiveRadioFaults"),
517        (0x0007, "ActiveNetworkFaults"),
518        (0x0008, "TestEventTriggersEnabled"),
519        (0x0009, "DoNotUse"),
520    ]
521}
522
523// Command listing
524
525pub fn get_command_list() -> Vec<(u32, &'static str)> {
526    vec![
527        (0x00, "TestEventTrigger"),
528        (0x01, "TimeSnapshot"),
529        (0x03, "PayloadTestRequest"),
530    ]
531}
532
533pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
534    match cmd_id {
535        0x00 => Some("TestEventTrigger"),
536        0x01 => Some("TimeSnapshot"),
537        0x03 => Some("PayloadTestRequest"),
538        _ => None,
539    }
540}
541
542pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
543    match cmd_id {
544        0x00 => Some(vec![
545            crate::clusters::codec::CommandField { tag: 0, name: "enable_key", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
546            crate::clusters::codec::CommandField { tag: 1, name: "event_trigger", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
547        ]),
548        0x01 => Some(vec![]),
549        0x03 => Some(vec![
550            crate::clusters::codec::CommandField { tag: 0, name: "enable_key", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
551            crate::clusters::codec::CommandField { tag: 1, name: "value", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
552            crate::clusters::codec::CommandField { tag: 2, name: "count", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
553        ]),
554        _ => None,
555    }
556}
557
558pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
559    match cmd_id {
560        0x00 => {
561        let enable_key = crate::clusters::codec::json_util::get_octstr(args, "enable_key")?;
562        let event_trigger = crate::clusters::codec::json_util::get_u64(args, "event_trigger")?;
563        encode_test_event_trigger(enable_key, event_trigger)
564        }
565        0x01 => Ok(vec![]),
566        0x03 => {
567        let enable_key = crate::clusters::codec::json_util::get_octstr(args, "enable_key")?;
568        let value = crate::clusters::codec::json_util::get_u8(args, "value")?;
569        let count = crate::clusters::codec::json_util::get_u16(args, "count")?;
570        encode_payload_test_request(enable_key, value, count)
571        }
572        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
573    }
574}
575
576#[derive(Debug, serde::Serialize)]
577pub struct TimeSnapshotResponse {
578    pub system_time_ms: Option<u8>,
579    pub posix_time_ms: Option<u8>,
580}
581
582#[derive(Debug, serde::Serialize)]
583pub struct PayloadTestResponse {
584    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
585    pub payload: Option<Vec<u8>>,
586}
587
588// Command response decoders
589
590/// Decode TimeSnapshotResponse command response (02)
591pub fn decode_time_snapshot_response(inp: &tlv::TlvItemValue) -> anyhow::Result<TimeSnapshotResponse> {
592    if let tlv::TlvItemValue::List(_fields) = inp {
593        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
594        Ok(TimeSnapshotResponse {
595                system_time_ms: item.get_int(&[0]).map(|v| v as u8),
596                posix_time_ms: item.get_int(&[1]).map(|v| v as u8),
597        })
598    } else {
599        Err(anyhow::anyhow!("Expected struct fields"))
600    }
601}
602
603/// Decode PayloadTestResponse command response (04)
604pub fn decode_payload_test_response(inp: &tlv::TlvItemValue) -> anyhow::Result<PayloadTestResponse> {
605    if let tlv::TlvItemValue::List(_fields) = inp {
606        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
607        Ok(PayloadTestResponse {
608                payload: item.get_octet_string_owned(&[0]),
609        })
610    } else {
611        Err(anyhow::anyhow!("Expected struct fields"))
612    }
613}
614
615// Typed facade (invokes + reads)
616
617/// Invoke `TestEventTrigger` command on cluster `General Diagnostics`.
618pub async fn test_event_trigger(conn: &crate::controller::Connection, endpoint: u16, enable_key: Vec<u8>, event_trigger: u64) -> anyhow::Result<()> {
619    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?;
620    Ok(())
621}
622
623/// Invoke `TimeSnapshot` command on cluster `General Diagnostics`.
624pub async fn time_snapshot(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TimeSnapshotResponse> {
625    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_CMD_ID_TIMESNAPSHOT, &[]).await?;
626    decode_time_snapshot_response(&tlv)
627}
628
629/// Invoke `PayloadTestRequest` command on cluster `General Diagnostics`.
630pub async fn payload_test_request(conn: &crate::controller::Connection, endpoint: u16, enable_key: Vec<u8>, value: u8, count: u16) -> anyhow::Result<PayloadTestResponse> {
631    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?;
632    decode_payload_test_response(&tlv)
633}
634
635/// Read `NetworkInterfaces` attribute from cluster `General Diagnostics`.
636pub async fn read_network_interfaces(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NetworkInterface>> {
637    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_NETWORKINTERFACES).await?;
638    decode_network_interfaces(&tlv)
639}
640
641/// Read `RebootCount` attribute from cluster `General Diagnostics`.
642pub async fn read_reboot_count(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
643    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_REBOOTCOUNT).await?;
644    decode_reboot_count(&tlv)
645}
646
647/// Read `UpTime` attribute from cluster `General Diagnostics`.
648pub async fn read_up_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u64> {
649    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_UPTIME).await?;
650    decode_up_time(&tlv)
651}
652
653/// Read `TotalOperationalHours` attribute from cluster `General Diagnostics`.
654pub async fn read_total_operational_hours(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
655    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_TOTALOPERATIONALHOURS).await?;
656    decode_total_operational_hours(&tlv)
657}
658
659/// Read `BootReason` attribute from cluster `General Diagnostics`.
660pub async fn read_boot_reason(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<BootReason> {
661    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_BOOTREASON).await?;
662    decode_boot_reason(&tlv)
663}
664
665/// Read `ActiveHardwareFaults` attribute from cluster `General Diagnostics`.
666pub async fn read_active_hardware_faults(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<HardwareFault>> {
667    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_ACTIVEHARDWAREFAULTS).await?;
668    decode_active_hardware_faults(&tlv)
669}
670
671/// Read `ActiveRadioFaults` attribute from cluster `General Diagnostics`.
672pub async fn read_active_radio_faults(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<RadioFault>> {
673    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_ACTIVERADIOFAULTS).await?;
674    decode_active_radio_faults(&tlv)
675}
676
677/// Read `ActiveNetworkFaults` attribute from cluster `General Diagnostics`.
678pub async fn read_active_network_faults(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NetworkFault>> {
679    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_ACTIVENETWORKFAULTS).await?;
680    decode_active_network_faults(&tlv)
681}
682
683/// Read `TestEventTriggersEnabled` attribute from cluster `General Diagnostics`.
684pub async fn read_test_event_triggers_enabled(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
685    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_TESTEVENTTRIGGERSENABLED).await?;
686    decode_test_event_triggers_enabled(&tlv)
687}
688
689/// Read `DoNotUse` attribute from cluster `General Diagnostics`.
690pub async fn read_do_not_use(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
691    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_DIAGNOSTICS, crate::clusters::defs::CLUSTER_GENERAL_DIAGNOSTICS_ATTR_ID_DONOTUSE).await?;
692    decode_do_not_use(&tlv)
693}
694
695#[derive(Debug, serde::Serialize)]
696pub struct HardwareFaultChangeEvent {
697    pub current: Option<Vec<HardwareFault>>,
698    pub previous: Option<Vec<HardwareFault>>,
699}
700
701#[derive(Debug, serde::Serialize)]
702pub struct RadioFaultChangeEvent {
703    pub current: Option<Vec<RadioFault>>,
704    pub previous: Option<Vec<RadioFault>>,
705}
706
707#[derive(Debug, serde::Serialize)]
708pub struct NetworkFaultChangeEvent {
709    pub current: Option<Vec<NetworkFault>>,
710    pub previous: Option<Vec<NetworkFault>>,
711}
712
713#[derive(Debug, serde::Serialize)]
714pub struct BootReasonEvent {
715    pub boot_reason: Option<BootReason>,
716}
717
718// Event decoders
719
720/// Decode HardwareFaultChange event (0x00, priority: critical)
721pub fn decode_hardware_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<HardwareFaultChangeEvent> {
722    if let tlv::TlvItemValue::List(_fields) = inp {
723        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
724        Ok(HardwareFaultChangeEvent {
725                                current: {
726                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
727                        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();
728                        Some(items)
729                    } else {
730                        None
731                    }
732                },
733                                previous: {
734                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
735                        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();
736                        Some(items)
737                    } else {
738                        None
739                    }
740                },
741        })
742    } else {
743        Err(anyhow::anyhow!("Expected struct fields"))
744    }
745}
746
747/// Decode RadioFaultChange event (0x01, priority: critical)
748pub fn decode_radio_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<RadioFaultChangeEvent> {
749    if let tlv::TlvItemValue::List(_fields) = inp {
750        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
751        Ok(RadioFaultChangeEvent {
752                                current: {
753                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
754                        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();
755                        Some(items)
756                    } else {
757                        None
758                    }
759                },
760                                previous: {
761                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
762                        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();
763                        Some(items)
764                    } else {
765                        None
766                    }
767                },
768        })
769    } else {
770        Err(anyhow::anyhow!("Expected struct fields"))
771    }
772}
773
774/// Decode NetworkFaultChange event (0x02, priority: critical)
775pub fn decode_network_fault_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<NetworkFaultChangeEvent> {
776    if let tlv::TlvItemValue::List(_fields) = inp {
777        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
778        Ok(NetworkFaultChangeEvent {
779                                current: {
780                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
781                        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();
782                        Some(items)
783                    } else {
784                        None
785                    }
786                },
787                                previous: {
788                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
789                        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();
790                        Some(items)
791                    } else {
792                        None
793                    }
794                },
795        })
796    } else {
797        Err(anyhow::anyhow!("Expected struct fields"))
798    }
799}
800
801/// Decode BootReason event (0x03, priority: critical)
802pub fn decode_boot_reason_event(inp: &tlv::TlvItemValue) -> anyhow::Result<BootReasonEvent> {
803    if let tlv::TlvItemValue::List(_fields) = inp {
804        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
805        Ok(BootReasonEvent {
806                                boot_reason: item.get_int(&[0]).and_then(|v| BootReason::from_u8(v as u8)),
807        })
808    } else {
809        Err(anyhow::anyhow!("Expected struct fields"))
810    }
811}
812