Skip to main content

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