1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum CommissioningWindowStatus {
18 Windownotopen = 0,
20 Enhancedwindowopen = 1,
22 Basicwindowopen = 2,
24}
25
26impl CommissioningWindowStatus {
27 pub fn from_u8(value: u8) -> Option<Self> {
29 match value {
30 0 => Some(CommissioningWindowStatus::Windownotopen),
31 1 => Some(CommissioningWindowStatus::Enhancedwindowopen),
32 2 => Some(CommissioningWindowStatus::Basicwindowopen),
33 _ => None,
34 }
35 }
36
37 pub fn to_u8(self) -> u8 {
39 self as u8
40 }
41}
42
43impl From<CommissioningWindowStatus> for u8 {
44 fn from(val: CommissioningWindowStatus) -> Self {
45 val as u8
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50#[repr(u8)]
51pub enum StatusCode {
52 Busy = 2,
54 Pakeparametererror = 3,
56 Windownotopen = 4,
58}
59
60impl StatusCode {
61 pub fn from_u8(value: u8) -> Option<Self> {
63 match value {
64 2 => Some(StatusCode::Busy),
65 3 => Some(StatusCode::Pakeparametererror),
66 4 => Some(StatusCode::Windownotopen),
67 _ => None,
68 }
69 }
70
71 pub fn to_u8(self) -> u8 {
73 self as u8
74 }
75}
76
77impl From<StatusCode> for u8 {
78 fn from(val: StatusCode) -> Self {
79 val as u8
80 }
81}
82
83pub fn encode_open_commissioning_window(commissioning_timeout: u16, pake_passcode_verifier: Vec<u8>, discriminator: u16, iterations: u32, salt: Vec<u8>) -> anyhow::Result<Vec<u8>> {
87 let tlv = tlv::TlvItemEnc {
88 tag: 0,
89 value: tlv::TlvItemValueEnc::StructInvisible(vec![
90 (0, tlv::TlvItemValueEnc::UInt16(commissioning_timeout)).into(),
91 (1, tlv::TlvItemValueEnc::OctetString(pake_passcode_verifier)).into(),
92 (2, tlv::TlvItemValueEnc::UInt16(discriminator)).into(),
93 (3, tlv::TlvItemValueEnc::UInt32(iterations)).into(),
94 (4, tlv::TlvItemValueEnc::OctetString(salt)).into(),
95 ]),
96 };
97 Ok(tlv.encode()?)
98}
99
100pub fn encode_open_basic_commissioning_window(commissioning_timeout: u16) -> anyhow::Result<Vec<u8>> {
102 let tlv = tlv::TlvItemEnc {
103 tag: 0,
104 value: tlv::TlvItemValueEnc::StructInvisible(vec![
105 (0, tlv::TlvItemValueEnc::UInt16(commissioning_timeout)).into(),
106 ]),
107 };
108 Ok(tlv.encode()?)
109}
110
111pub fn decode_window_status(inp: &tlv::TlvItemValue) -> anyhow::Result<CommissioningWindowStatus> {
115 if let tlv::TlvItemValue::Int(v) = inp {
116 CommissioningWindowStatus::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
117 } else {
118 Err(anyhow::anyhow!("Expected Integer"))
119 }
120}
121
122pub fn decode_admin_fabric_index(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
124 if let tlv::TlvItemValue::Int(v) = inp {
125 Ok(Some(*v as u8))
126 } else {
127 Ok(None)
128 }
129}
130
131pub fn decode_admin_vendor_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
133 if let tlv::TlvItemValue::Int(v) = inp {
134 Ok(Some(*v as u16))
135 } else {
136 Ok(None)
137 }
138}
139
140
141pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
153 if cluster_id != 0x003C {
155 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x003C, got {}\"}}", cluster_id);
156 }
157
158 match attribute_id {
159 0x0000 => {
160 match decode_window_status(tlv_value) {
161 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
162 Err(e) => format!("{{\"error\": \"{}\"}}", e),
163 }
164 }
165 0x0001 => {
166 match decode_admin_fabric_index(tlv_value) {
167 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
168 Err(e) => format!("{{\"error\": \"{}\"}}", e),
169 }
170 }
171 0x0002 => {
172 match decode_admin_vendor_id(tlv_value) {
173 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
174 Err(e) => format!("{{\"error\": \"{}\"}}", e),
175 }
176 }
177 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
178 }
179}
180
181pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
186 vec![
187 (0x0000, "WindowStatus"),
188 (0x0001, "AdminFabricIndex"),
189 (0x0002, "AdminVendorId"),
190 ]
191}
192
193pub fn get_command_list() -> Vec<(u32, &'static str)> {
196 vec![
197 (0x00, "OpenCommissioningWindow"),
198 (0x01, "OpenBasicCommissioningWindow"),
199 (0x02, "RevokeCommissioning"),
200 ]
201}
202
203pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
204 match cmd_id {
205 0x00 => Some("OpenCommissioningWindow"),
206 0x01 => Some("OpenBasicCommissioningWindow"),
207 0x02 => Some("RevokeCommissioning"),
208 _ => None,
209 }
210}
211
212pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
213 match cmd_id {
214 0x00 => Some(vec![
215 crate::clusters::codec::CommandField { tag: 0, name: "commissioning_timeout", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
216 crate::clusters::codec::CommandField { tag: 1, name: "pake_passcode_verifier", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
217 crate::clusters::codec::CommandField { tag: 2, name: "discriminator", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
218 crate::clusters::codec::CommandField { tag: 3, name: "iterations", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
219 crate::clusters::codec::CommandField { tag: 4, name: "salt", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
220 ]),
221 0x01 => Some(vec![
222 crate::clusters::codec::CommandField { tag: 0, name: "commissioning_timeout", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
223 ]),
224 0x02 => Some(vec![]),
225 _ => None,
226 }
227}
228
229pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
230 match cmd_id {
231 0x00 => {
232 let commissioning_timeout = crate::clusters::codec::json_util::get_u16(args, "commissioning_timeout")?;
233 let pake_passcode_verifier = crate::clusters::codec::json_util::get_octstr(args, "pake_passcode_verifier")?;
234 let discriminator = crate::clusters::codec::json_util::get_u16(args, "discriminator")?;
235 let iterations = crate::clusters::codec::json_util::get_u32(args, "iterations")?;
236 let salt = crate::clusters::codec::json_util::get_octstr(args, "salt")?;
237 encode_open_commissioning_window(commissioning_timeout, pake_passcode_verifier, discriminator, iterations, salt)
238 }
239 0x01 => {
240 let commissioning_timeout = crate::clusters::codec::json_util::get_u16(args, "commissioning_timeout")?;
241 encode_open_basic_commissioning_window(commissioning_timeout)
242 }
243 0x02 => Ok(vec![]),
244 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
245 }
246}
247
248pub async fn open_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<()> {
252 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ADMINISTRATOR_COMMISSIONING, crate::clusters::defs::CLUSTER_ADMINISTRATOR_COMMISSIONING_CMD_ID_OPENCOMMISSIONINGWINDOW, &encode_open_commissioning_window(commissioning_timeout, pake_passcode_verifier, discriminator, iterations, salt)?).await?;
253 Ok(())
254}
255
256pub async fn open_basic_commissioning_window(conn: &crate::controller::Connection, endpoint: u16, commissioning_timeout: u16) -> anyhow::Result<()> {
258 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ADMINISTRATOR_COMMISSIONING, crate::clusters::defs::CLUSTER_ADMINISTRATOR_COMMISSIONING_CMD_ID_OPENBASICCOMMISSIONINGWINDOW, &encode_open_basic_commissioning_window(commissioning_timeout)?).await?;
259 Ok(())
260}
261
262pub async fn revoke_commissioning(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
264 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ADMINISTRATOR_COMMISSIONING, crate::clusters::defs::CLUSTER_ADMINISTRATOR_COMMISSIONING_CMD_ID_REVOKECOMMISSIONING, &[]).await?;
265 Ok(())
266}
267
268pub async fn read_window_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<CommissioningWindowStatus> {
270 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ADMINISTRATOR_COMMISSIONING, crate::clusters::defs::CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_WINDOWSTATUS).await?;
271 decode_window_status(&tlv)
272}
273
274pub async fn read_admin_fabric_index(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
276 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ADMINISTRATOR_COMMISSIONING, crate::clusters::defs::CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_ADMINFABRICINDEX).await?;
277 decode_admin_fabric_index(&tlv)
278}
279
280pub async fn read_admin_vendor_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u16>> {
282 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ADMINISTRATOR_COMMISSIONING, crate::clusters::defs::CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_ADMINVENDORID).await?;
283 decode_admin_vendor_id(&tlv)
284}
285