matc/clusters/codec/
thread_network_directory.rs

1//! Matter TLV encoders and decoders for Thread Network Directory Cluster
2//! Cluster ID: 0x0453
3//!
4//! This file is automatically generated from ThreadNetworkDirectory.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// Struct definitions
15
16#[derive(Debug, serde::Serialize)]
17pub struct ThreadNetwork {
18    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
19    pub extended_pan_id: Option<Vec<u8>>,
20    pub network_name: Option<String>,
21    pub channel: Option<u16>,
22    pub active_timestamp: Option<u64>,
23}
24
25// Command encoders
26
27/// Encode AddNetwork command (0x00)
28pub fn encode_add_network(operational_dataset: Vec<u8>) -> anyhow::Result<Vec<u8>> {
29    let tlv = tlv::TlvItemEnc {
30        tag: 0,
31        value: tlv::TlvItemValueEnc::StructInvisible(vec![
32        (0, tlv::TlvItemValueEnc::OctetString(operational_dataset)).into(),
33        ]),
34    };
35    Ok(tlv.encode()?)
36}
37
38/// Encode RemoveNetwork command (0x01)
39pub fn encode_remove_network(extended_pan_id: Vec<u8>) -> anyhow::Result<Vec<u8>> {
40    let tlv = tlv::TlvItemEnc {
41        tag: 0,
42        value: tlv::TlvItemValueEnc::StructInvisible(vec![
43        (0, tlv::TlvItemValueEnc::OctetString(extended_pan_id)).into(),
44        ]),
45    };
46    Ok(tlv.encode()?)
47}
48
49/// Encode GetOperationalDataset command (0x02)
50pub fn encode_get_operational_dataset(extended_pan_id: Vec<u8>) -> anyhow::Result<Vec<u8>> {
51    let tlv = tlv::TlvItemEnc {
52        tag: 0,
53        value: tlv::TlvItemValueEnc::StructInvisible(vec![
54        (0, tlv::TlvItemValueEnc::OctetString(extended_pan_id)).into(),
55        ]),
56    };
57    Ok(tlv.encode()?)
58}
59
60// Attribute decoders
61
62/// Decode PreferredExtendedPanID attribute (0x0000)
63pub fn decode_preferred_extended_pan_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
64    if let tlv::TlvItemValue::OctetString(v) = inp {
65        Ok(Some(v.clone()))
66    } else {
67        Ok(None)
68    }
69}
70
71/// Decode ThreadNetworks attribute (0x0001)
72pub fn decode_thread_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ThreadNetwork>> {
73    let mut res = Vec::new();
74    if let tlv::TlvItemValue::List(v) = inp {
75        for item in v {
76            res.push(ThreadNetwork {
77                extended_pan_id: item.get_octet_string_owned(&[0]),
78                network_name: item.get_string_owned(&[1]),
79                channel: item.get_int(&[2]).map(|v| v as u16),
80                active_timestamp: item.get_int(&[3]),
81            });
82        }
83    }
84    Ok(res)
85}
86
87/// Decode ThreadNetworkTableSize attribute (0x0002)
88pub fn decode_thread_network_table_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
89    if let tlv::TlvItemValue::Int(v) = inp {
90        Ok(*v as u8)
91    } else {
92        Err(anyhow::anyhow!("Expected UInt8"))
93    }
94}
95
96
97// JSON dispatcher function
98
99/// Decode attribute value and return as JSON string
100///
101/// # Parameters
102/// * `cluster_id` - The cluster identifier
103/// * `attribute_id` - The attribute identifier
104/// * `tlv_value` - The TLV value to decode
105///
106/// # Returns
107/// JSON string representation of the decoded value or error
108pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
109    // Verify this is the correct cluster
110    if cluster_id != 0x0453 {
111        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0453, got {}\"}}", cluster_id);
112    }
113
114    match attribute_id {
115        0x0000 => {
116            match decode_preferred_extended_pan_id(tlv_value) {
117                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
118                Err(e) => format!("{{\"error\": \"{}\"}}", e),
119            }
120        }
121        0x0001 => {
122            match decode_thread_networks(tlv_value) {
123                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
124                Err(e) => format!("{{\"error\": \"{}\"}}", e),
125            }
126        }
127        0x0002 => {
128            match decode_thread_network_table_size(tlv_value) {
129                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
130                Err(e) => format!("{{\"error\": \"{}\"}}", e),
131            }
132        }
133        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
134    }
135}
136
137/// Get list of all attributes supported by this cluster
138///
139/// # Returns
140/// Vector of tuples containing (attribute_id, attribute_name)
141pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
142    vec![
143        (0x0000, "PreferredExtendedPanID"),
144        (0x0001, "ThreadNetworks"),
145        (0x0002, "ThreadNetworkTableSize"),
146    ]
147}
148
149#[derive(Debug, serde::Serialize)]
150pub struct OperationalDatasetResponse {
151    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
152    pub operational_dataset: Option<Vec<u8>>,
153}
154
155// Command response decoders
156
157/// Decode OperationalDatasetResponse command response (03)
158pub fn decode_operational_dataset_response(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationalDatasetResponse> {
159    if let tlv::TlvItemValue::List(_fields) = inp {
160        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
161        Ok(OperationalDatasetResponse {
162                operational_dataset: item.get_octet_string_owned(&[0]),
163        })
164    } else {
165        Err(anyhow::anyhow!("Expected struct fields"))
166    }
167}
168