1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum ICACResponseStatus {
21 Ok = 0,
23 Invalidpublickey = 1,
25 Invalidicac = 2,
27}
28
29impl ICACResponseStatus {
30 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 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 Busy = 2,
57 Pakeparametererror = 3,
59 Windownotopen = 4,
61 Vidnotverified = 5,
63 Invalidadministratorfabricindex = 6,
65}
66
67impl StatusCode {
68 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 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 Ok = 0,
97 Transferanchorstatusdatastorebusy = 1,
99 Transferanchorstatusnouserconsent = 2,
101}
102
103impl TransferAnchorResponseStatus {
104 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 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
126pub 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
139pub 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
154pub 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
165pub 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
177pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
189 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
205pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
210 vec![
211 (0x0000, "AdministratorFabricIndex"),
212 ]
213}
214
215pub 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
303pub 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
317pub 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
329pub 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
341pub 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
349pub 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
355pub 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
361pub 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
367pub 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
373pub 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
379pub 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