matc/clusters/codec/
diagnostics_wifi.rs

1//! Matter TLV encoders and decoders for Wi-Fi Network Diagnostics Cluster
2//! Cluster ID: 0x0036
3//!
4//! This file is automatically generated from DiagnosticsWiFi.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Enum definitions
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum AssociationFailureCause {
16    /// The reason for the failure is unknown.
17    Unknown = 0,
18    /// An error occurred during association.
19    Associationfailed = 1,
20    /// An error occurred during authentication.
21    Authenticationfailed = 2,
22    /// The specified SSID could not be found.
23    Ssidnotfound = 3,
24}
25
26impl AssociationFailureCause {
27    /// Convert from u8 value
28    pub fn from_u8(value: u8) -> Option<Self> {
29        match value {
30            0 => Some(AssociationFailureCause::Unknown),
31            1 => Some(AssociationFailureCause::Associationfailed),
32            2 => Some(AssociationFailureCause::Authenticationfailed),
33            3 => Some(AssociationFailureCause::Ssidnotfound),
34            _ => None,
35        }
36    }
37
38    /// Convert to u8 value
39    pub fn to_u8(self) -> u8 {
40        self as u8
41    }
42}
43
44impl From<AssociationFailureCause> for u8 {
45    fn from(val: AssociationFailureCause) -> Self {
46        val as u8
47    }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
51#[repr(u8)]
52pub enum ConnectionStatus {
53    /// Indicate the node is connected
54    Connected = 0,
55    /// Indicate the node is not connected
56    Notconnected = 1,
57}
58
59impl ConnectionStatus {
60    /// Convert from u8 value
61    pub fn from_u8(value: u8) -> Option<Self> {
62        match value {
63            0 => Some(ConnectionStatus::Connected),
64            1 => Some(ConnectionStatus::Notconnected),
65            _ => None,
66        }
67    }
68
69    /// Convert to u8 value
70    pub fn to_u8(self) -> u8 {
71        self as u8
72    }
73}
74
75impl From<ConnectionStatus> for u8 {
76    fn from(val: ConnectionStatus) -> Self {
77        val as u8
78    }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
82#[repr(u8)]
83pub enum SecurityType {
84    /// Indicate the usage of an unspecified Wi-Fi security type
85    Unspecified = 0,
86    /// Indicate the usage of no Wi-Fi security
87    None = 1,
88    /// Indicate the usage of WEP Wi-Fi security
89    Wep = 2,
90    /// Indicate the usage of WPA Wi-Fi security
91    Wpa = 3,
92    /// Indicate the usage of WPA2 Wi-Fi security
93    Wpa2 = 4,
94    /// Indicate the usage of WPA3 Wi-Fi security
95    Wpa3 = 5,
96}
97
98impl SecurityType {
99    /// Convert from u8 value
100    pub fn from_u8(value: u8) -> Option<Self> {
101        match value {
102            0 => Some(SecurityType::Unspecified),
103            1 => Some(SecurityType::None),
104            2 => Some(SecurityType::Wep),
105            3 => Some(SecurityType::Wpa),
106            4 => Some(SecurityType::Wpa2),
107            5 => Some(SecurityType::Wpa3),
108            _ => None,
109        }
110    }
111
112    /// Convert to u8 value
113    pub fn to_u8(self) -> u8 {
114        self as u8
115    }
116}
117
118impl From<SecurityType> for u8 {
119    fn from(val: SecurityType) -> Self {
120        val as u8
121    }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
125#[repr(u8)]
126pub enum WiFiVersion {
127    /// Indicate the network interface is currently using IEEE 802.11a against the wireless access point.
128    A = 0,
129    /// Indicate the network interface is currently using IEEE 802.11b against the wireless access point.
130    B = 1,
131    /// Indicate the network interface is currently using IEEE 802.11g against the wireless access point.
132    G = 2,
133    /// Indicate the network interface is currently using IEEE 802.11n against the wireless access point.
134    N = 3,
135    /// Indicate the network interface is currently using IEEE 802.11ac against the wireless access point.
136    Ac = 4,
137    /// Indicate the network interface is currently using IEEE 802.11ax against the wireless access point.
138    Ax = 5,
139    /// Indicate the network interface is currently using IEEE 802.11ah against the wireless access point.
140    Ah = 6,
141}
142
143impl WiFiVersion {
144    /// Convert from u8 value
145    pub fn from_u8(value: u8) -> Option<Self> {
146        match value {
147            0 => Some(WiFiVersion::A),
148            1 => Some(WiFiVersion::B),
149            2 => Some(WiFiVersion::G),
150            3 => Some(WiFiVersion::N),
151            4 => Some(WiFiVersion::Ac),
152            5 => Some(WiFiVersion::Ax),
153            6 => Some(WiFiVersion::Ah),
154            _ => None,
155        }
156    }
157
158    /// Convert to u8 value
159    pub fn to_u8(self) -> u8 {
160        self as u8
161    }
162}
163
164impl From<WiFiVersion> for u8 {
165    fn from(val: WiFiVersion) -> Self {
166        val as u8
167    }
168}
169
170// Command encoders
171
172// Attribute decoders
173
174/// Decode BSSID attribute (0x0000)
175pub fn decode_bssid(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
176    if let tlv::TlvItemValue::OctetString(v) = inp {
177        Ok(Some(v.clone()))
178    } else {
179        Ok(None)
180    }
181}
182
183/// Decode SecurityType attribute (0x0001)
184pub fn decode_security_type(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<SecurityType>> {
185    if let tlv::TlvItemValue::Int(v) = inp {
186        Ok(SecurityType::from_u8(*v as u8))
187    } else {
188        Ok(None)
189    }
190}
191
192/// Decode WiFiVersion attribute (0x0002)
193pub fn decode_wifi_version(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<WiFiVersion>> {
194    if let tlv::TlvItemValue::Int(v) = inp {
195        Ok(WiFiVersion::from_u8(*v as u8))
196    } else {
197        Ok(None)
198    }
199}
200
201/// Decode ChannelNumber attribute (0x0003)
202pub fn decode_channel_number(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
203    if let tlv::TlvItemValue::Int(v) = inp {
204        Ok(Some(*v as u16))
205    } else {
206        Ok(None)
207    }
208}
209
210/// Decode RSSI attribute (0x0004)
211pub fn decode_rssi(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i8>> {
212    if let tlv::TlvItemValue::Int(v) = inp {
213        Ok(Some(*v as i8))
214    } else {
215        Ok(None)
216    }
217}
218
219/// Decode BeaconLostCount attribute (0x0005)
220pub fn decode_beacon_lost_count(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
221    if let tlv::TlvItemValue::Int(v) = inp {
222        Ok(Some(*v as u32))
223    } else {
224        Ok(None)
225    }
226}
227
228/// Decode BeaconRxCount attribute (0x0006)
229pub fn decode_beacon_rx_count(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
230    if let tlv::TlvItemValue::Int(v) = inp {
231        Ok(Some(*v as u32))
232    } else {
233        Ok(None)
234    }
235}
236
237/// Decode PacketMulticastRxCount attribute (0x0007)
238pub fn decode_packet_multicast_rx_count(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
239    if let tlv::TlvItemValue::Int(v) = inp {
240        Ok(Some(*v as u32))
241    } else {
242        Ok(None)
243    }
244}
245
246/// Decode PacketMulticastTxCount attribute (0x0008)
247pub fn decode_packet_multicast_tx_count(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
248    if let tlv::TlvItemValue::Int(v) = inp {
249        Ok(Some(*v as u32))
250    } else {
251        Ok(None)
252    }
253}
254
255/// Decode PacketUnicastRxCount attribute (0x0009)
256pub fn decode_packet_unicast_rx_count(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
257    if let tlv::TlvItemValue::Int(v) = inp {
258        Ok(Some(*v as u32))
259    } else {
260        Ok(None)
261    }
262}
263
264/// Decode PacketUnicastTxCount attribute (0x000A)
265pub fn decode_packet_unicast_tx_count(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
266    if let tlv::TlvItemValue::Int(v) = inp {
267        Ok(Some(*v as u32))
268    } else {
269        Ok(None)
270    }
271}
272
273/// Decode CurrentMaxRate attribute (0x000B)
274pub fn decode_current_max_rate(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
275    if let tlv::TlvItemValue::Int(v) = inp {
276        Ok(Some(*v))
277    } else {
278        Ok(None)
279    }
280}
281
282/// Decode OverrunCount attribute (0x000C)
283pub fn decode_overrun_count(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
284    if let tlv::TlvItemValue::Int(v) = inp {
285        Ok(Some(*v))
286    } else {
287        Ok(None)
288    }
289}
290
291
292// JSON dispatcher function
293
294/// Decode attribute value and return as JSON string
295///
296/// # Parameters
297/// * `cluster_id` - The cluster identifier
298/// * `attribute_id` - The attribute identifier
299/// * `tlv_value` - The TLV value to decode
300///
301/// # Returns
302/// JSON string representation of the decoded value or error
303pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
304    // Verify this is the correct cluster
305    if cluster_id != 0x0036 {
306        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0036, got {}\"}}", cluster_id);
307    }
308
309    match attribute_id {
310        0x0000 => {
311            match decode_bssid(tlv_value) {
312                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
313                Err(e) => format!("{{\"error\": \"{}\"}}", e),
314            }
315        }
316        0x0001 => {
317            match decode_security_type(tlv_value) {
318                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
319                Err(e) => format!("{{\"error\": \"{}\"}}", e),
320            }
321        }
322        0x0002 => {
323            match decode_wifi_version(tlv_value) {
324                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
325                Err(e) => format!("{{\"error\": \"{}\"}}", e),
326            }
327        }
328        0x0003 => {
329            match decode_channel_number(tlv_value) {
330                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
331                Err(e) => format!("{{\"error\": \"{}\"}}", e),
332            }
333        }
334        0x0004 => {
335            match decode_rssi(tlv_value) {
336                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
337                Err(e) => format!("{{\"error\": \"{}\"}}", e),
338            }
339        }
340        0x0005 => {
341            match decode_beacon_lost_count(tlv_value) {
342                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
343                Err(e) => format!("{{\"error\": \"{}\"}}", e),
344            }
345        }
346        0x0006 => {
347            match decode_beacon_rx_count(tlv_value) {
348                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
349                Err(e) => format!("{{\"error\": \"{}\"}}", e),
350            }
351        }
352        0x0007 => {
353            match decode_packet_multicast_rx_count(tlv_value) {
354                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
355                Err(e) => format!("{{\"error\": \"{}\"}}", e),
356            }
357        }
358        0x0008 => {
359            match decode_packet_multicast_tx_count(tlv_value) {
360                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
361                Err(e) => format!("{{\"error\": \"{}\"}}", e),
362            }
363        }
364        0x0009 => {
365            match decode_packet_unicast_rx_count(tlv_value) {
366                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
367                Err(e) => format!("{{\"error\": \"{}\"}}", e),
368            }
369        }
370        0x000A => {
371            match decode_packet_unicast_tx_count(tlv_value) {
372                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
373                Err(e) => format!("{{\"error\": \"{}\"}}", e),
374            }
375        }
376        0x000B => {
377            match decode_current_max_rate(tlv_value) {
378                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
379                Err(e) => format!("{{\"error\": \"{}\"}}", e),
380            }
381        }
382        0x000C => {
383            match decode_overrun_count(tlv_value) {
384                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
385                Err(e) => format!("{{\"error\": \"{}\"}}", e),
386            }
387        }
388        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
389    }
390}
391
392/// Get list of all attributes supported by this cluster
393///
394/// # Returns
395/// Vector of tuples containing (attribute_id, attribute_name)
396pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
397    vec![
398        (0x0000, "BSSID"),
399        (0x0001, "SecurityType"),
400        (0x0002, "WiFiVersion"),
401        (0x0003, "ChannelNumber"),
402        (0x0004, "RSSI"),
403        (0x0005, "BeaconLostCount"),
404        (0x0006, "BeaconRxCount"),
405        (0x0007, "PacketMulticastRxCount"),
406        (0x0008, "PacketMulticastTxCount"),
407        (0x0009, "PacketUnicastRxCount"),
408        (0x000A, "PacketUnicastTxCount"),
409        (0x000B, "CurrentMaxRate"),
410        (0x000C, "OverrunCount"),
411    ]
412}
413
414#[derive(Debug, serde::Serialize)]
415pub struct DisconnectionEvent {
416    pub reason_code: Option<u16>,
417}
418
419#[derive(Debug, serde::Serialize)]
420pub struct AssociationFailureEvent {
421    pub association_failure_cause: Option<AssociationFailureCause>,
422    pub status: Option<u16>,
423}
424
425#[derive(Debug, serde::Serialize)]
426pub struct ConnectionStatusEvent {
427    pub connection_status: Option<ConnectionStatus>,
428}
429
430// Event decoders
431
432/// Decode Disconnection event (0x00, priority: info)
433pub fn decode_disconnection_event(inp: &tlv::TlvItemValue) -> anyhow::Result<DisconnectionEvent> {
434    if let tlv::TlvItemValue::List(_fields) = inp {
435        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
436        Ok(DisconnectionEvent {
437                                reason_code: item.get_int(&[0]).map(|v| v as u16),
438        })
439    } else {
440        Err(anyhow::anyhow!("Expected struct fields"))
441    }
442}
443
444/// Decode AssociationFailure event (0x01, priority: info)
445pub fn decode_association_failure_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AssociationFailureEvent> {
446    if let tlv::TlvItemValue::List(_fields) = inp {
447        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
448        Ok(AssociationFailureEvent {
449                                association_failure_cause: item.get_int(&[0]).and_then(|v| AssociationFailureCause::from_u8(v as u8)),
450                                status: item.get_int(&[1]).map(|v| v as u16),
451        })
452    } else {
453        Err(anyhow::anyhow!("Expected struct fields"))
454    }
455}
456
457/// Decode ConnectionStatus event (0x02, priority: info)
458pub fn decode_connection_status_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ConnectionStatusEvent> {
459    if let tlv::TlvItemValue::List(_fields) = inp {
460        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
461        Ok(ConnectionStatusEvent {
462                                connection_status: item.get_int(&[0]).and_then(|v| ConnectionStatus::from_u8(v as u8)),
463        })
464    } else {
465        Err(anyhow::anyhow!("Expected struct fields"))
466    }
467}
468