matc/clusters/codec/
joint_fabric_administrator_cluster.rs

1//! Matter TLV encoders and decoders for Joint Fabric Administrator Cluster
2//! Cluster ID: 0x0753
3//!
4//! This file is automatically generated from JointFabricAdministratorCluster.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 ICACResponseStatus {
21    /// No error
22    Ok = 0,
23    /// Public Key in the ICAC is invalid
24    Invalidpublickey = 1,
25    /// ICAC chain validation failed / ICAC DN Encoding rules verification failed
26    Invalidicac = 2,
27}
28
29impl ICACResponseStatus {
30    /// Convert from u8 value
31    pub fn from_u8(value: u8) -> Option<Self> {
32        match value {
33            0 => Some(ICACResponseStatus::Ok),
34            1 => Some(ICACResponseStatus::Invalidpublickey),
35            2 => Some(ICACResponseStatus::Invalidicac),
36            _ => None,
37        }
38    }
39
40    /// Convert to u8 value
41    pub fn to_u8(self) -> u8 {
42        self as u8
43    }
44}
45
46impl From<ICACResponseStatus> for u8 {
47    fn from(val: ICACResponseStatus) -> Self {
48        val as u8
49    }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53#[repr(u8)]
54pub enum StatusCode {
55    /// Could not be completed because another commissioning is in progress
56    Busy = 2,
57    /// Provided PAKE parameters were incorrectly formatted or otherwise invalid
58    Pakeparametererror = 3,
59    /// No commissioning window was currently open
60    Windownotopen = 4,
61    /// ICACCSRRequest command has been invoked by a peer against which Fabric Table VID Verification hasn't been executed
62    Vidnotverified = 5,
63    /// OpenJointCommissioningWindow command has been invoked but the AdministratorFabricIndex field has the value of null
64    Invalidadministratorfabricindex = 6,
65}
66
67impl StatusCode {
68    /// Convert from u8 value
69    pub fn from_u8(value: u8) -> Option<Self> {
70        match value {
71            2 => Some(StatusCode::Busy),
72            3 => Some(StatusCode::Pakeparametererror),
73            4 => Some(StatusCode::Windownotopen),
74            5 => Some(StatusCode::Vidnotverified),
75            6 => Some(StatusCode::Invalidadministratorfabricindex),
76            _ => None,
77        }
78    }
79
80    /// Convert to u8 value
81    pub fn to_u8(self) -> u8 {
82        self as u8
83    }
84}
85
86impl From<StatusCode> for u8 {
87    fn from(val: StatusCode) -> Self {
88        val as u8
89    }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
93#[repr(u8)]
94pub enum TransferAnchorResponseStatus {
95    /// No error
96    Ok = 0,
97    /// Anchor Transfer was not started due to on-going Datastore operations
98    Transferanchorstatusdatastorebusy = 1,
99    /// User has not consented for Anchor Transfer
100    Transferanchorstatusnouserconsent = 2,
101}
102
103impl TransferAnchorResponseStatus {
104    /// Convert from u8 value
105    pub fn from_u8(value: u8) -> Option<Self> {
106        match value {
107            0 => Some(TransferAnchorResponseStatus::Ok),
108            1 => Some(TransferAnchorResponseStatus::Transferanchorstatusdatastorebusy),
109            2 => Some(TransferAnchorResponseStatus::Transferanchorstatusnouserconsent),
110            _ => None,
111        }
112    }
113
114    /// Convert to u8 value
115    pub fn to_u8(self) -> u8 {
116        self as u8
117    }
118}
119
120impl From<TransferAnchorResponseStatus> for u8 {
121    fn from(val: TransferAnchorResponseStatus) -> Self {
122        val as u8
123    }
124}
125
126// Command encoders
127
128/// Encode AddICAC command (0x02)
129pub fn encode_add_icac(icac_value: Vec<u8>) -> anyhow::Result<Vec<u8>> {
130    let tlv = tlv::TlvItemEnc {
131        tag: 0,
132        value: tlv::TlvItemValueEnc::StructInvisible(vec![
133        (1, tlv::TlvItemValueEnc::OctetString(icac_value)).into(),
134        ]),
135    };
136    Ok(tlv.encode()?)
137}
138
139/// Encode OpenJointCommissioningWindow command (0x04)
140pub fn encode_open_joint_commissioning_window(commissioning_timeout: u16, pake_passcode_verifier: Vec<u8>, discriminator: u16, iterations: u32, salt: Vec<u8>) -> anyhow::Result<Vec<u8>> {
141    let tlv = tlv::TlvItemEnc {
142        tag: 0,
143        value: tlv::TlvItemValueEnc::StructInvisible(vec![
144        (0, tlv::TlvItemValueEnc::UInt16(commissioning_timeout)).into(),
145        (1, tlv::TlvItemValueEnc::OctetString(pake_passcode_verifier)).into(),
146        (2, tlv::TlvItemValueEnc::UInt16(discriminator)).into(),
147        (3, tlv::TlvItemValueEnc::UInt32(iterations)).into(),
148        (4, tlv::TlvItemValueEnc::OctetString(salt)).into(),
149        ]),
150    };
151    Ok(tlv.encode()?)
152}
153
154/// Encode AnnounceJointFabricAdministrator command (0x08)
155pub fn encode_announce_joint_fabric_administrator(endpoint_id: u16) -> anyhow::Result<Vec<u8>> {
156    let tlv = tlv::TlvItemEnc {
157        tag: 0,
158        value: tlv::TlvItemValueEnc::StructInvisible(vec![
159        (0, tlv::TlvItemValueEnc::UInt16(endpoint_id)).into(),
160        ]),
161    };
162    Ok(tlv.encode()?)
163}
164
165// Attribute decoders
166
167/// Decode AdministratorFabricIndex attribute (0x0000)
168pub fn decode_administrator_fabric_index(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
169    if let tlv::TlvItemValue::Int(v) = inp {
170        Ok(Some(*v as u8))
171    } else {
172        Ok(None)
173    }
174}
175
176
177// JSON dispatcher function
178
179/// Decode attribute value and return as JSON string
180///
181/// # Parameters
182/// * `cluster_id` - The cluster identifier
183/// * `attribute_id` - The attribute identifier
184/// * `tlv_value` - The TLV value to decode
185///
186/// # Returns
187/// JSON string representation of the decoded value or error
188pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
189    // Verify this is the correct cluster
190    if cluster_id != 0x0753 {
191        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0753, got {}\"}}", cluster_id);
192    }
193
194    match attribute_id {
195        0x0000 => {
196            match decode_administrator_fabric_index(tlv_value) {
197                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
198                Err(e) => format!("{{\"error\": \"{}\"}}", e),
199            }
200        }
201        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
202    }
203}
204
205/// Get list of all attributes supported by this cluster
206///
207/// # Returns
208/// Vector of tuples containing (attribute_id, attribute_name)
209pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
210    vec![
211        (0x0000, "AdministratorFabricIndex"),
212    ]
213}
214
215// Command listing
216
217pub fn get_command_list() -> Vec<(u32, &'static str)> {
218    vec![
219        (0x00, "ICACCSRRequest"),
220        (0x02, "AddICAC"),
221        (0x04, "OpenJointCommissioningWindow"),
222        (0x05, "TransferAnchorRequest"),
223        (0x07, "TransferAnchorComplete"),
224        (0x08, "AnnounceJointFabricAdministrator"),
225    ]
226}
227
228pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
229    match cmd_id {
230        0x00 => Some("ICACCSRRequest"),
231        0x02 => Some("AddICAC"),
232        0x04 => Some("OpenJointCommissioningWindow"),
233        0x05 => Some("TransferAnchorRequest"),
234        0x07 => Some("TransferAnchorComplete"),
235        0x08 => Some("AnnounceJointFabricAdministrator"),
236        _ => None,
237    }
238}
239
240pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
241    match cmd_id {
242        0x00 => Some(vec![]),
243        0x02 => Some(vec![
244            crate::clusters::codec::CommandField { tag: 1, name: "icac_value", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
245        ]),
246        0x04 => Some(vec![
247            crate::clusters::codec::CommandField { tag: 0, name: "commissioning_timeout", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
248            crate::clusters::codec::CommandField { tag: 1, name: "pake_passcode_verifier", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
249            crate::clusters::codec::CommandField { tag: 2, name: "discriminator", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
250            crate::clusters::codec::CommandField { tag: 3, name: "iterations", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
251            crate::clusters::codec::CommandField { tag: 4, name: "salt", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
252        ]),
253        0x05 => Some(vec![]),
254        0x07 => Some(vec![]),
255        0x08 => Some(vec![
256            crate::clusters::codec::CommandField { tag: 0, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
257        ]),
258        _ => None,
259    }
260}
261
262pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
263    match cmd_id {
264        0x00 => Ok(vec![]),
265        0x02 => {
266        let icac_value = crate::clusters::codec::json_util::get_octstr(args, "icac_value")?;
267        encode_add_icac(icac_value)
268        }
269        0x04 => {
270        let commissioning_timeout = crate::clusters::codec::json_util::get_u16(args, "commissioning_timeout")?;
271        let pake_passcode_verifier = crate::clusters::codec::json_util::get_octstr(args, "pake_passcode_verifier")?;
272        let discriminator = crate::clusters::codec::json_util::get_u16(args, "discriminator")?;
273        let iterations = crate::clusters::codec::json_util::get_u32(args, "iterations")?;
274        let salt = crate::clusters::codec::json_util::get_octstr(args, "salt")?;
275        encode_open_joint_commissioning_window(commissioning_timeout, pake_passcode_verifier, discriminator, iterations, salt)
276        }
277        0x05 => Ok(vec![]),
278        0x07 => Ok(vec![]),
279        0x08 => {
280        let endpoint_id = crate::clusters::codec::json_util::get_u16(args, "endpoint_id")?;
281        encode_announce_joint_fabric_administrator(endpoint_id)
282        }
283        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
284    }
285}
286
287#[derive(Debug, serde::Serialize)]
288pub struct ICACCSRResponse {
289    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
290    pub icaccsr: Option<Vec<u8>>,
291}
292
293#[derive(Debug, serde::Serialize)]
294pub struct ICACResponse {
295    pub status_code: Option<ICACResponseStatus>,
296}
297
298#[derive(Debug, serde::Serialize)]
299pub struct TransferAnchorResponse {
300    pub status_code: Option<TransferAnchorResponseStatus>,
301}
302
303// Command response decoders
304
305/// Decode ICACCSRResponse command response (01)
306pub fn decode_icaccsr_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ICACCSRResponse> {
307    if let tlv::TlvItemValue::List(_fields) = inp {
308        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
309        Ok(ICACCSRResponse {
310                icaccsr: item.get_octet_string_owned(&[0]),
311        })
312    } else {
313        Err(anyhow::anyhow!("Expected struct fields"))
314    }
315}
316
317/// Decode ICACResponse command response (03)
318pub fn decode_icac_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ICACResponse> {
319    if let tlv::TlvItemValue::List(_fields) = inp {
320        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
321        Ok(ICACResponse {
322                status_code: item.get_int(&[0]).and_then(|v| ICACResponseStatus::from_u8(v as u8)),
323        })
324    } else {
325        Err(anyhow::anyhow!("Expected struct fields"))
326    }
327}
328
329/// Decode TransferAnchorResponse command response (06)
330pub fn decode_transfer_anchor_response(inp: &tlv::TlvItemValue) -> anyhow::Result<TransferAnchorResponse> {
331    if let tlv::TlvItemValue::List(_fields) = inp {
332        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
333        Ok(TransferAnchorResponse {
334                status_code: item.get_int(&[0]).and_then(|v| TransferAnchorResponseStatus::from_u8(v as u8)),
335        })
336    } else {
337        Err(anyhow::anyhow!("Expected struct fields"))
338    }
339}
340
341// Typed facade (invokes + reads)
342
343/// Invoke `ICACCSRRequest` command on cluster `Joint Fabric Administrator`.
344pub async fn icaccsr_request(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ICACCSRResponse> {
345    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_ADMINISTRATOR, crate::clusters::defs::CLUSTER_JOINT_FABRIC_ADMINISTRATOR_CMD_ID_ICACCSRREQUEST, &[]).await?;
346    decode_icaccsr_response(&tlv)
347}
348
349/// Invoke `AddICAC` command on cluster `Joint Fabric Administrator`.
350pub async fn add_icac(conn: &crate::controller::Connection, endpoint: u16, icac_value: Vec<u8>) -> anyhow::Result<ICACResponse> {
351    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_ADMINISTRATOR, crate::clusters::defs::CLUSTER_JOINT_FABRIC_ADMINISTRATOR_CMD_ID_ADDICAC, &encode_add_icac(icac_value)?).await?;
352    decode_icac_response(&tlv)
353}
354
355/// Invoke `OpenJointCommissioningWindow` command on cluster `Joint Fabric Administrator`.
356pub async fn open_joint_commissioning_window(conn: &crate::controller::Connection, endpoint: u16, commissioning_timeout: u16, pake_passcode_verifier: Vec<u8>, discriminator: u16, iterations: u32, salt: Vec<u8>) -> anyhow::Result<()> {
357    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_ADMINISTRATOR, crate::clusters::defs::CLUSTER_JOINT_FABRIC_ADMINISTRATOR_CMD_ID_OPENJOINTCOMMISSIONINGWINDOW, &encode_open_joint_commissioning_window(commissioning_timeout, pake_passcode_verifier, discriminator, iterations, salt)?).await?;
358    Ok(())
359}
360
361/// Invoke `TransferAnchorRequest` command on cluster `Joint Fabric Administrator`.
362pub async fn transfer_anchor_request(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TransferAnchorResponse> {
363    let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_ADMINISTRATOR, crate::clusters::defs::CLUSTER_JOINT_FABRIC_ADMINISTRATOR_CMD_ID_TRANSFERANCHORREQUEST, &[]).await?;
364    decode_transfer_anchor_response(&tlv)
365}
366
367/// Invoke `TransferAnchorComplete` command on cluster `Joint Fabric Administrator`.
368pub async fn transfer_anchor_complete(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
369    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_ADMINISTRATOR, crate::clusters::defs::CLUSTER_JOINT_FABRIC_ADMINISTRATOR_CMD_ID_TRANSFERANCHORCOMPLETE, &[]).await?;
370    Ok(())
371}
372
373/// Invoke `AnnounceJointFabricAdministrator` command on cluster `Joint Fabric Administrator`.
374pub async fn announce_joint_fabric_administrator(conn: &crate::controller::Connection, endpoint: u16, endpoint_id: u16) -> anyhow::Result<()> {
375    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_ADMINISTRATOR, crate::clusters::defs::CLUSTER_JOINT_FABRIC_ADMINISTRATOR_CMD_ID_ANNOUNCEJOINTFABRICADMINISTRATOR, &encode_announce_joint_fabric_administrator(endpoint_id)?).await?;
376    Ok(())
377}
378
379/// Read `AdministratorFabricIndex` attribute from cluster `Joint Fabric Administrator`.
380pub async fn read_administrator_fabric_index(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
381    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_ADMINISTRATOR, crate::clusters::defs::CLUSTER_JOINT_FABRIC_ADMINISTRATOR_ATTR_ID_ADMINISTRATORFABRICINDEX).await?;
382    decode_administrator_fabric_index(&tlv)
383}
384