matc/clusters/codec/
group_key_management_cluster.rs

1//! Matter TLV encoders and decoders for Group Key Management Cluster
2//! Cluster ID: 0x003F
3//!
4//! This file is automatically generated from Group-Key-Management-Cluster.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 GroupKeyMulticastPolicy {
21    /// Indicates filtering of multicast messages for a specific Group ID
22    Pergroupid = 0,
23    /// Indicates not filtering of multicast messages
24    Allnodes = 1,
25}
26
27impl GroupKeyMulticastPolicy {
28    /// Convert from u8 value
29    pub fn from_u8(value: u8) -> Option<Self> {
30        match value {
31            0 => Some(GroupKeyMulticastPolicy::Pergroupid),
32            1 => Some(GroupKeyMulticastPolicy::Allnodes),
33            _ => None,
34        }
35    }
36
37    /// Convert to u8 value
38    pub fn to_u8(self) -> u8 {
39        self as u8
40    }
41}
42
43impl From<GroupKeyMulticastPolicy> for u8 {
44    fn from(val: GroupKeyMulticastPolicy) -> Self {
45        val as u8
46    }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50#[repr(u8)]
51pub enum GroupKeySecurityPolicy {
52    /// Message counter synchronization using trust-first
53    Trustfirst = 0,
54    /// Message counter synchronization using cache-and-sync
55    Cacheandsync = 1,
56}
57
58impl GroupKeySecurityPolicy {
59    /// Convert from u8 value
60    pub fn from_u8(value: u8) -> Option<Self> {
61        match value {
62            0 => Some(GroupKeySecurityPolicy::Trustfirst),
63            1 => Some(GroupKeySecurityPolicy::Cacheandsync),
64            _ => None,
65        }
66    }
67
68    /// Convert to u8 value
69    pub fn to_u8(self) -> u8 {
70        self as u8
71    }
72}
73
74impl From<GroupKeySecurityPolicy> for u8 {
75    fn from(val: GroupKeySecurityPolicy) -> Self {
76        val as u8
77    }
78}
79
80// Struct definitions
81
82#[derive(Debug, serde::Serialize)]
83pub struct GroupInfoMap {
84    pub group_id: Option<u8>,
85    pub endpoints: Option<Vec<u16>>,
86    pub group_name: Option<String>,
87}
88
89#[derive(Debug, serde::Serialize)]
90pub struct GroupKeyMap {
91    pub group_id: Option<u8>,
92    pub group_key_set_id: Option<u16>,
93}
94
95#[derive(Debug, serde::Serialize)]
96pub struct GroupKeySet {
97    pub group_key_set_id: Option<u16>,
98    pub group_key_security_policy: Option<GroupKeySecurityPolicy>,
99    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
100    pub epoch_key0: Option<Vec<u8>>,
101    pub epoch_start_time0: Option<u64>,
102    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
103    pub epoch_key1: Option<Vec<u8>>,
104    pub epoch_start_time1: Option<u64>,
105    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
106    pub epoch_key2: Option<Vec<u8>>,
107    pub epoch_start_time2: Option<u64>,
108    pub group_key_multicast_policy: Option<GroupKeyMulticastPolicy>,
109}
110
111// Command encoders
112
113/// Encode KeySetWrite command (0x00)
114pub fn encode_key_set_write(group_key_set: GroupKeySet) -> anyhow::Result<Vec<u8>> {
115            // Encode struct GroupKeySetStruct
116            let mut group_key_set_fields = Vec::new();
117            if let Some(x) = group_key_set.group_key_set_id { group_key_set_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
118            if let Some(x) = group_key_set.group_key_security_policy { group_key_set_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
119            if let Some(x) = group_key_set.epoch_key0 { group_key_set_fields.push((2, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
120            if let Some(x) = group_key_set.epoch_start_time0 { group_key_set_fields.push((3, tlv::TlvItemValueEnc::UInt64(x)).into()); }
121            if let Some(x) = group_key_set.epoch_key1 { group_key_set_fields.push((4, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
122            if let Some(x) = group_key_set.epoch_start_time1 { group_key_set_fields.push((5, tlv::TlvItemValueEnc::UInt64(x)).into()); }
123            if let Some(x) = group_key_set.epoch_key2 { group_key_set_fields.push((6, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
124            if let Some(x) = group_key_set.epoch_start_time2 { group_key_set_fields.push((7, tlv::TlvItemValueEnc::UInt64(x)).into()); }
125            if let Some(x) = group_key_set.group_key_multicast_policy { group_key_set_fields.push((8, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
126    let tlv = tlv::TlvItemEnc {
127        tag: 0,
128        value: tlv::TlvItemValueEnc::StructInvisible(vec![
129        (0, tlv::TlvItemValueEnc::StructInvisible(group_key_set_fields)).into(),
130        ]),
131    };
132    Ok(tlv.encode()?)
133}
134
135/// Encode KeySetRead command (0x01)
136pub fn encode_key_set_read(group_key_set_id: u16) -> anyhow::Result<Vec<u8>> {
137    let tlv = tlv::TlvItemEnc {
138        tag: 0,
139        value: tlv::TlvItemValueEnc::StructInvisible(vec![
140        (0, tlv::TlvItemValueEnc::UInt16(group_key_set_id)).into(),
141        ]),
142    };
143    Ok(tlv.encode()?)
144}
145
146/// Encode KeySetRemove command (0x03)
147pub fn encode_key_set_remove(group_key_set_id: u16) -> anyhow::Result<Vec<u8>> {
148    let tlv = tlv::TlvItemEnc {
149        tag: 0,
150        value: tlv::TlvItemValueEnc::StructInvisible(vec![
151        (0, tlv::TlvItemValueEnc::UInt16(group_key_set_id)).into(),
152        ]),
153    };
154    Ok(tlv.encode()?)
155}
156
157/// Encode KeySetReadAllIndices command (0x04)
158pub fn encode_key_set_read_all_indices(do_not_use: u8) -> anyhow::Result<Vec<u8>> {
159    let tlv = tlv::TlvItemEnc {
160        tag: 0,
161        value: tlv::TlvItemValueEnc::StructInvisible(vec![
162        (0, tlv::TlvItemValueEnc::UInt8(do_not_use)).into(),
163        ]),
164    };
165    Ok(tlv.encode()?)
166}
167
168// Attribute decoders
169
170/// Decode GroupKeyMap attribute (0x0000)
171pub fn decode_group_key_map(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<GroupKeyMap>> {
172    let mut res = Vec::new();
173    if let tlv::TlvItemValue::List(v) = inp {
174        for item in v {
175            res.push(GroupKeyMap {
176                group_id: item.get_int(&[1]).map(|v| v as u8),
177                group_key_set_id: item.get_int(&[2]).map(|v| v as u16),
178            });
179        }
180    }
181    Ok(res)
182}
183
184/// Decode GroupTable attribute (0x0001)
185pub fn decode_group_table(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<GroupInfoMap>> {
186    let mut res = Vec::new();
187    if let tlv::TlvItemValue::List(v) = inp {
188        for item in v {
189            res.push(GroupInfoMap {
190                group_id: item.get_int(&[1]).map(|v| v as u8),
191                endpoints: {
192                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
193                        let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
194                        Some(items)
195                    } else {
196                        None
197                    }
198                },
199                group_name: item.get_string_owned(&[3]),
200            });
201        }
202    }
203    Ok(res)
204}
205
206/// Decode MaxGroupsPerFabric attribute (0x0002)
207pub fn decode_max_groups_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
208    if let tlv::TlvItemValue::Int(v) = inp {
209        Ok(*v as u16)
210    } else {
211        Err(anyhow::anyhow!("Expected UInt16"))
212    }
213}
214
215/// Decode MaxGroupKeysPerFabric attribute (0x0003)
216pub fn decode_max_group_keys_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
217    if let tlv::TlvItemValue::Int(v) = inp {
218        Ok(*v as u16)
219    } else {
220        Err(anyhow::anyhow!("Expected UInt16"))
221    }
222}
223
224
225// JSON dispatcher function
226
227/// Decode attribute value and return as JSON string
228///
229/// # Parameters
230/// * `cluster_id` - The cluster identifier
231/// * `attribute_id` - The attribute identifier
232/// * `tlv_value` - The TLV value to decode
233///
234/// # Returns
235/// JSON string representation of the decoded value or error
236pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
237    // Verify this is the correct cluster
238    if cluster_id != 0x003F {
239        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x003F, got {}\"}}", cluster_id);
240    }
241
242    match attribute_id {
243        0x0000 => {
244            match decode_group_key_map(tlv_value) {
245                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
246                Err(e) => format!("{{\"error\": \"{}\"}}", e),
247            }
248        }
249        0x0001 => {
250            match decode_group_table(tlv_value) {
251                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
252                Err(e) => format!("{{\"error\": \"{}\"}}", e),
253            }
254        }
255        0x0002 => {
256            match decode_max_groups_per_fabric(tlv_value) {
257                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
258                Err(e) => format!("{{\"error\": \"{}\"}}", e),
259            }
260        }
261        0x0003 => {
262            match decode_max_group_keys_per_fabric(tlv_value) {
263                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
264                Err(e) => format!("{{\"error\": \"{}\"}}", e),
265            }
266        }
267        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
268    }
269}
270
271/// Get list of all attributes supported by this cluster
272///
273/// # Returns
274/// Vector of tuples containing (attribute_id, attribute_name)
275pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
276    vec![
277        (0x0000, "GroupKeyMap"),
278        (0x0001, "GroupTable"),
279        (0x0002, "MaxGroupsPerFabric"),
280        (0x0003, "MaxGroupKeysPerFabric"),
281    ]
282}
283
284// Command listing
285
286pub fn get_command_list() -> Vec<(u32, &'static str)> {
287    vec![
288        (0x00, "KeySetWrite"),
289        (0x01, "KeySetRead"),
290        (0x03, "KeySetRemove"),
291        (0x04, "KeySetReadAllIndices"),
292    ]
293}
294
295pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
296    match cmd_id {
297        0x00 => Some("KeySetWrite"),
298        0x01 => Some("KeySetRead"),
299        0x03 => Some("KeySetRemove"),
300        0x04 => Some("KeySetReadAllIndices"),
301        _ => None,
302    }
303}
304
305pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
306    match cmd_id {
307        0x00 => Some(vec![
308            crate::clusters::codec::CommandField { tag: 0, name: "group_key_set", kind: crate::clusters::codec::FieldKind::Struct { name: "GroupKeySetStruct" }, optional: false, nullable: false },
309        ]),
310        0x01 => Some(vec![
311            crate::clusters::codec::CommandField { tag: 0, name: "group_key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
312        ]),
313        0x03 => Some(vec![
314            crate::clusters::codec::CommandField { tag: 0, name: "group_key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
315        ]),
316        0x04 => Some(vec![
317            crate::clusters::codec::CommandField { tag: 0, name: "do_not_use", kind: crate::clusters::codec::FieldKind::U8, optional: true, nullable: false },
318        ]),
319        _ => None,
320    }
321}
322
323pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
324    match cmd_id {
325        0x00 => Err(anyhow::anyhow!("command \"KeySetWrite\" has complex args: use raw mode")),
326        0x01 => {
327        let group_key_set_id = crate::clusters::codec::json_util::get_u16(args, "group_key_set_id")?;
328        encode_key_set_read(group_key_set_id)
329        }
330        0x03 => {
331        let group_key_set_id = crate::clusters::codec::json_util::get_u16(args, "group_key_set_id")?;
332        encode_key_set_remove(group_key_set_id)
333        }
334        0x04 => {
335        let do_not_use = crate::clusters::codec::json_util::get_u8(args, "do_not_use")?;
336        encode_key_set_read_all_indices(do_not_use)
337        }
338        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
339    }
340}
341
342#[derive(Debug, serde::Serialize)]
343pub struct KeySetReadResponse {
344    pub group_key_set: Option<GroupKeySet>,
345}
346
347#[derive(Debug, serde::Serialize)]
348pub struct KeySetReadAllIndicesResponse {
349    pub group_key_set_i_ds: Option<Vec<u16>>,
350}
351
352// Command response decoders
353
354/// Decode KeySetReadResponse command response (02)
355pub fn decode_key_set_read_response(inp: &tlv::TlvItemValue) -> anyhow::Result<KeySetReadResponse> {
356    if let tlv::TlvItemValue::List(_fields) = inp {
357        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
358        Ok(KeySetReadResponse {
359                group_key_set: {
360                    if let Some(nested_tlv) = item.get(&[0]) {
361                        if let tlv::TlvItemValue::List(_) = nested_tlv {
362                            let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
363                            Some(GroupKeySet {
364                group_key_set_id: nested_item.get_int(&[0]).map(|v| v as u16),
365                group_key_security_policy: nested_item.get_int(&[1]).and_then(|v| GroupKeySecurityPolicy::from_u8(v as u8)),
366                epoch_key0: nested_item.get_octet_string_owned(&[2]),
367                epoch_start_time0: nested_item.get_int(&[3]),
368                epoch_key1: nested_item.get_octet_string_owned(&[4]),
369                epoch_start_time1: nested_item.get_int(&[5]),
370                epoch_key2: nested_item.get_octet_string_owned(&[6]),
371                epoch_start_time2: nested_item.get_int(&[7]),
372                group_key_multicast_policy: nested_item.get_int(&[8]).and_then(|v| GroupKeyMulticastPolicy::from_u8(v as u8)),
373                            })
374                        } else {
375                            None
376                        }
377                    } else {
378                        None
379                    }
380                },
381        })
382    } else {
383        Err(anyhow::anyhow!("Expected struct fields"))
384    }
385}
386
387/// Decode KeySetReadAllIndicesResponse command response (05)
388pub fn decode_key_set_read_all_indices_response(inp: &tlv::TlvItemValue) -> anyhow::Result<KeySetReadAllIndicesResponse> {
389    if let tlv::TlvItemValue::List(_fields) = inp {
390        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
391        Ok(KeySetReadAllIndicesResponse {
392                group_key_set_i_ds: {
393                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
394                        let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
395                        Some(items)
396                    } else {
397                        None
398                    }
399                },
400        })
401    } else {
402        Err(anyhow::anyhow!("Expected struct fields"))
403    }
404}
405
406// Typed facade (invokes + reads)
407
408/// Invoke `KeySetWrite` command on cluster `Group Key Management`.
409pub async fn key_set_write(conn: &crate::controller::Connection, endpoint: u16, group_key_set: GroupKeySet) -> anyhow::Result<()> {
410    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETWRITE, &encode_key_set_write(group_key_set)?).await?;
411    Ok(())
412}
413
414/// Invoke `KeySetRead` command on cluster `Group Key Management`.
415pub async fn key_set_read(conn: &crate::controller::Connection, endpoint: u16, group_key_set_id: u16) -> anyhow::Result<KeySetReadResponse> {
416    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETREAD, &encode_key_set_read(group_key_set_id)?).await?;
417    decode_key_set_read_response(&tlv)
418}
419
420/// Invoke `KeySetRemove` command on cluster `Group Key Management`.
421pub async fn key_set_remove(conn: &crate::controller::Connection, endpoint: u16, group_key_set_id: u16) -> anyhow::Result<()> {
422    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETREMOVE, &encode_key_set_remove(group_key_set_id)?).await?;
423    Ok(())
424}
425
426/// Invoke `KeySetReadAllIndices` command on cluster `Group Key Management`.
427pub async fn key_set_read_all_indices(conn: &crate::controller::Connection, endpoint: u16, do_not_use: u8) -> anyhow::Result<KeySetReadAllIndicesResponse> {
428    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETREADALLINDICES, &encode_key_set_read_all_indices(do_not_use)?).await?;
429    decode_key_set_read_all_indices_response(&tlv)
430}
431
432/// Read `GroupKeyMap` attribute from cluster `Group Key Management`.
433pub async fn read_group_key_map(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<GroupKeyMap>> {
434    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_GROUPKEYMAP).await?;
435    decode_group_key_map(&tlv)
436}
437
438/// Read `GroupTable` attribute from cluster `Group Key Management`.
439pub async fn read_group_table(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<GroupInfoMap>> {
440    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_GROUPTABLE).await?;
441    decode_group_table(&tlv)
442}
443
444/// Read `MaxGroupsPerFabric` attribute from cluster `Group Key Management`.
445pub async fn read_max_groups_per_fabric(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
446    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_MAXGROUPSPERFABRIC).await?;
447    decode_max_groups_per_fabric(&tlv)
448}
449
450/// Read `MaxGroupKeysPerFabric` attribute from cluster `Group Key Management`.
451pub async fn read_max_group_keys_per_fabric(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
452    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_MAXGROUPKEYSPERFABRIC).await?;
453    decode_max_group_keys_per_fabric(&tlv)
454}
455