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 NetworkCommissioningStatus {
21 Success = 0,
23 Outofrange = 1,
25 Boundsexceeded = 2,
27 Networkidnotfound = 3,
29 Duplicatenetworkid = 4,
31 Networknotfound = 5,
33 Regulatoryerror = 6,
35 Authfailure = 7,
37 Unsupportedsecurity = 8,
39 Otherconnectionfailure = 9,
41 Ipv6failed = 10,
43 Ipbindfailed = 11,
45 Unknownerror = 12,
47}
48
49impl NetworkCommissioningStatus {
50 pub fn from_u8(value: u8) -> Option<Self> {
52 match value {
53 0 => Some(NetworkCommissioningStatus::Success),
54 1 => Some(NetworkCommissioningStatus::Outofrange),
55 2 => Some(NetworkCommissioningStatus::Boundsexceeded),
56 3 => Some(NetworkCommissioningStatus::Networkidnotfound),
57 4 => Some(NetworkCommissioningStatus::Duplicatenetworkid),
58 5 => Some(NetworkCommissioningStatus::Networknotfound),
59 6 => Some(NetworkCommissioningStatus::Regulatoryerror),
60 7 => Some(NetworkCommissioningStatus::Authfailure),
61 8 => Some(NetworkCommissioningStatus::Unsupportedsecurity),
62 9 => Some(NetworkCommissioningStatus::Otherconnectionfailure),
63 10 => Some(NetworkCommissioningStatus::Ipv6failed),
64 11 => Some(NetworkCommissioningStatus::Ipbindfailed),
65 12 => Some(NetworkCommissioningStatus::Unknownerror),
66 _ => None,
67 }
68 }
69
70 pub fn to_u8(self) -> u8 {
72 self as u8
73 }
74}
75
76impl From<NetworkCommissioningStatus> for u8 {
77 fn from(val: NetworkCommissioningStatus) -> Self {
78 val as u8
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
83#[repr(u8)]
84pub enum WiFiBand {
85 _2g4 = 0,
87 _3g65 = 1,
89 _5g = 2,
91 _6g = 3,
93 _60g = 4,
95 _1g = 5,
97}
98
99impl WiFiBand {
100 pub fn from_u8(value: u8) -> Option<Self> {
102 match value {
103 0 => Some(WiFiBand::_2g4),
104 1 => Some(WiFiBand::_3g65),
105 2 => Some(WiFiBand::_5g),
106 3 => Some(WiFiBand::_6g),
107 4 => Some(WiFiBand::_60g),
108 5 => Some(WiFiBand::_1g),
109 _ => None,
110 }
111 }
112
113 pub fn to_u8(self) -> u8 {
115 self as u8
116 }
117}
118
119impl From<WiFiBand> for u8 {
120 fn from(val: WiFiBand) -> Self {
121 val as u8
122 }
123}
124
125pub type ThreadCapabilities = u8;
129
130pub mod threadcapabilities {
132 pub const IS_BORDER_ROUTER_CAPABLE: u8 = 0x01;
134 pub const IS_ROUTER_CAPABLE: u8 = 0x02;
136 pub const IS_SLEEPY_END_DEVICE_CAPABLE: u8 = 0x04;
138 pub const IS_FULL_THREAD_DEVICE: u8 = 0x08;
140 pub const IS_SYNCHRONIZED_SLEEPY_END_DEVICE_CAPABLE: u8 = 0x10;
142}
143
144pub type WiFiSecurity = u8;
146
147pub mod wifisecurity {
149 pub const UNENCRYPTED: u8 = 0x01;
151 pub const WEP: u8 = 0x02;
153 pub const WPA_PERSONAL: u8 = 0x04;
155 pub const WPA2_PERSONAL: u8 = 0x08;
157 pub const WPA3_PERSONAL: u8 = 0x10;
159 pub const WPA3_MATTER_PDC: u8 = 0x20;
161}
162
163#[derive(Debug, serde::Serialize)]
166pub struct NetworkInfo {
167 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
168 pub network_id: Option<Vec<u8>>,
169 pub connected: Option<bool>,
170 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
171 pub network_identifier: Option<Vec<u8>>,
172 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
173 pub client_identifier: Option<Vec<u8>>,
174}
175
176#[derive(Debug, serde::Serialize)]
177pub struct ThreadInterfaceScanResult {
178 pub pan_id: Option<u16>,
179 pub extended_pan_id: Option<u64>,
180 pub network_name: Option<String>,
181 pub channel: Option<u16>,
182 pub version: Option<u8>,
183 pub extended_address: Option<u8>,
184 pub rssi: Option<i8>,
185 pub lqi: Option<u8>,
186}
187
188#[derive(Debug, serde::Serialize)]
189pub struct WiFiInterfaceScanResult {
190 pub security: Option<WiFiSecurity>,
191 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
192 pub ssid: Option<Vec<u8>>,
193 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
194 pub bssid: Option<Vec<u8>>,
195 pub channel: Option<u16>,
196 pub wifi_band: Option<WiFiBand>,
197 pub rssi: Option<i8>,
198}
199
200pub fn encode_scan_networks(ssid: Option<Vec<u8>>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
204 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
205 tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(ssid.unwrap_or_default())).into());
206 if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
207 let tlv = tlv::TlvItemEnc {
208 tag: 0,
209 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
210 };
211 Ok(tlv.encode()?)
212}
213
214pub fn encode_add_or_update_wifi_network(ssid: Vec<u8>, credentials: Vec<u8>, breadcrumb: Option<u64>, network_identity: Option<Vec<u8>>, client_identifier: Option<Vec<u8>>, possession_nonce: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
216 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
217 tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(ssid)).into());
218 tlv_fields.push((1, tlv::TlvItemValueEnc::OctetString(credentials)).into());
219 if let Some(x) = breadcrumb { tlv_fields.push((2, tlv::TlvItemValueEnc::UInt64(x)).into()); }
220 if let Some(x) = network_identity { tlv_fields.push((3, tlv::TlvItemValueEnc::OctetString(x)).into()); }
221 if let Some(x) = client_identifier { tlv_fields.push((4, tlv::TlvItemValueEnc::OctetString(x)).into()); }
222 if let Some(x) = possession_nonce { tlv_fields.push((5, tlv::TlvItemValueEnc::OctetString(x)).into()); }
223 let tlv = tlv::TlvItemEnc {
224 tag: 0,
225 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
226 };
227 Ok(tlv.encode()?)
228}
229
230pub fn encode_add_or_update_thread_network(operational_dataset: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
232 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
233 tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(operational_dataset)).into());
234 if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
235 let tlv = tlv::TlvItemEnc {
236 tag: 0,
237 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
238 };
239 Ok(tlv.encode()?)
240}
241
242pub fn encode_remove_network(network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
244 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
245 tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(network_id)).into());
246 if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
247 let tlv = tlv::TlvItemEnc {
248 tag: 0,
249 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
250 };
251 Ok(tlv.encode()?)
252}
253
254pub fn encode_connect_network(network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
256 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
257 tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(network_id)).into());
258 if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
259 let tlv = tlv::TlvItemEnc {
260 tag: 0,
261 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
262 };
263 Ok(tlv.encode()?)
264}
265
266pub fn encode_reorder_network(network_id: Vec<u8>, network_index: u8, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
268 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
269 tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(network_id)).into());
270 tlv_fields.push((1, tlv::TlvItemValueEnc::UInt8(network_index)).into());
271 if let Some(x) = breadcrumb { tlv_fields.push((2, tlv::TlvItemValueEnc::UInt64(x)).into()); }
272 let tlv = tlv::TlvItemEnc {
273 tag: 0,
274 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
275 };
276 Ok(tlv.encode()?)
277}
278
279pub fn encode_query_identity(key_identifier: Vec<u8>, possession_nonce: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
281 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
282 tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(key_identifier)).into());
283 if let Some(x) = possession_nonce { tlv_fields.push((1, tlv::TlvItemValueEnc::OctetString(x)).into()); }
284 let tlv = tlv::TlvItemEnc {
285 tag: 0,
286 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
287 };
288 Ok(tlv.encode()?)
289}
290
291pub fn decode_max_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
295 if let tlv::TlvItemValue::Int(v) = inp {
296 Ok(*v as u8)
297 } else {
298 Err(anyhow::anyhow!("Expected UInt8"))
299 }
300}
301
302pub fn decode_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkInfo>> {
304 let mut res = Vec::new();
305 if let tlv::TlvItemValue::List(v) = inp {
306 for item in v {
307 res.push(NetworkInfo {
308 network_id: item.get_octet_string_owned(&[0]),
309 connected: item.get_bool(&[1]),
310 network_identifier: item.get_octet_string_owned(&[2]),
311 client_identifier: item.get_octet_string_owned(&[3]),
312 });
313 }
314 }
315 Ok(res)
316}
317
318pub fn decode_scan_max_time_seconds(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
320 if let tlv::TlvItemValue::Int(v) = inp {
321 Ok(*v as u8)
322 } else {
323 Err(anyhow::anyhow!("Expected UInt8"))
324 }
325}
326
327pub fn decode_connect_max_time_seconds(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
329 if let tlv::TlvItemValue::Int(v) = inp {
330 Ok(*v as u8)
331 } else {
332 Err(anyhow::anyhow!("Expected UInt8"))
333 }
334}
335
336pub fn decode_interface_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
338 if let tlv::TlvItemValue::Bool(v) = inp {
339 Ok(*v)
340 } else {
341 Err(anyhow::anyhow!("Expected Bool"))
342 }
343}
344
345pub fn decode_last_networking_status(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<NetworkCommissioningStatus>> {
347 if let tlv::TlvItemValue::Int(v) = inp {
348 Ok(NetworkCommissioningStatus::from_u8(*v as u8))
349 } else {
350 Ok(None)
351 }
352}
353
354pub fn decode_last_network_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
356 if let tlv::TlvItemValue::OctetString(v) = inp {
357 Ok(Some(v.clone()))
358 } else {
359 Ok(None)
360 }
361}
362
363pub fn decode_last_connect_error_value(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i32>> {
365 if let tlv::TlvItemValue::Int(v) = inp {
366 Ok(Some(*v as i32))
367 } else {
368 Ok(None)
369 }
370}
371
372pub fn decode_supported_wifi_bands(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<WiFiBand>> {
374 let mut res = Vec::new();
375 if let tlv::TlvItemValue::List(v) = inp {
376 for item in v {
377 if let tlv::TlvItemValue::Int(i) = &item.value {
378 if let Some(enum_val) = WiFiBand::from_u8(*i as u8) {
379 res.push(enum_val);
380 }
381 }
382 }
383 }
384 Ok(res)
385}
386
387pub fn decode_supported_thread_features(inp: &tlv::TlvItemValue) -> anyhow::Result<ThreadCapabilities> {
389 if let tlv::TlvItemValue::Int(v) = inp {
390 Ok(*v as u8)
391 } else {
392 Err(anyhow::anyhow!("Expected Integer"))
393 }
394}
395
396pub fn decode_thread_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
398 if let tlv::TlvItemValue::Int(v) = inp {
399 Ok(*v as u16)
400 } else {
401 Err(anyhow::anyhow!("Expected UInt16"))
402 }
403}
404
405
406pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
418 if cluster_id != 0x0031 {
420 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0031, got {}\"}}", cluster_id);
421 }
422
423 match attribute_id {
424 0x0000 => {
425 match decode_max_networks(tlv_value) {
426 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
427 Err(e) => format!("{{\"error\": \"{}\"}}", e),
428 }
429 }
430 0x0001 => {
431 match decode_networks(tlv_value) {
432 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
433 Err(e) => format!("{{\"error\": \"{}\"}}", e),
434 }
435 }
436 0x0002 => {
437 match decode_scan_max_time_seconds(tlv_value) {
438 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
439 Err(e) => format!("{{\"error\": \"{}\"}}", e),
440 }
441 }
442 0x0003 => {
443 match decode_connect_max_time_seconds(tlv_value) {
444 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
445 Err(e) => format!("{{\"error\": \"{}\"}}", e),
446 }
447 }
448 0x0004 => {
449 match decode_interface_enabled(tlv_value) {
450 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
451 Err(e) => format!("{{\"error\": \"{}\"}}", e),
452 }
453 }
454 0x0005 => {
455 match decode_last_networking_status(tlv_value) {
456 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
457 Err(e) => format!("{{\"error\": \"{}\"}}", e),
458 }
459 }
460 0x0006 => {
461 match decode_last_network_id(tlv_value) {
462 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
463 Err(e) => format!("{{\"error\": \"{}\"}}", e),
464 }
465 }
466 0x0007 => {
467 match decode_last_connect_error_value(tlv_value) {
468 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
469 Err(e) => format!("{{\"error\": \"{}\"}}", e),
470 }
471 }
472 0x0008 => {
473 match decode_supported_wifi_bands(tlv_value) {
474 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
475 Err(e) => format!("{{\"error\": \"{}\"}}", e),
476 }
477 }
478 0x0009 => {
479 match decode_supported_thread_features(tlv_value) {
480 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
481 Err(e) => format!("{{\"error\": \"{}\"}}", e),
482 }
483 }
484 0x000A => {
485 match decode_thread_version(tlv_value) {
486 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
487 Err(e) => format!("{{\"error\": \"{}\"}}", e),
488 }
489 }
490 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
491 }
492}
493
494pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
499 vec![
500 (0x0000, "MaxNetworks"),
501 (0x0001, "Networks"),
502 (0x0002, "ScanMaxTimeSeconds"),
503 (0x0003, "ConnectMaxTimeSeconds"),
504 (0x0004, "InterfaceEnabled"),
505 (0x0005, "LastNetworkingStatus"),
506 (0x0006, "LastNetworkID"),
507 (0x0007, "LastConnectErrorValue"),
508 (0x0008, "SupportedWiFiBands"),
509 (0x0009, "SupportedThreadFeatures"),
510 (0x000A, "ThreadVersion"),
511 ]
512}
513
514pub fn get_command_list() -> Vec<(u32, &'static str)> {
517 vec![
518 (0x00, "ScanNetworks"),
519 (0x02, "AddOrUpdateWiFiNetwork"),
520 (0x03, "AddOrUpdateThreadNetwork"),
521 (0x04, "RemoveNetwork"),
522 (0x06, "ConnectNetwork"),
523 (0x08, "ReorderNetwork"),
524 (0x09, "QueryIdentity"),
525 ]
526}
527
528pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
529 match cmd_id {
530 0x00 => Some("ScanNetworks"),
531 0x02 => Some("AddOrUpdateWiFiNetwork"),
532 0x03 => Some("AddOrUpdateThreadNetwork"),
533 0x04 => Some("RemoveNetwork"),
534 0x06 => Some("ConnectNetwork"),
535 0x08 => Some("ReorderNetwork"),
536 0x09 => Some("QueryIdentity"),
537 _ => None,
538 }
539}
540
541pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
542 match cmd_id {
543 0x00 => Some(vec![
544 crate::clusters::codec::CommandField { tag: 0, name: "ssid", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: true },
545 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
546 ]),
547 0x02 => Some(vec![
548 crate::clusters::codec::CommandField { tag: 0, name: "ssid", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
549 crate::clusters::codec::CommandField { tag: 1, name: "credentials", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
550 crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
551 crate::clusters::codec::CommandField { tag: 3, name: "network_identity", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
552 crate::clusters::codec::CommandField { tag: 4, name: "client_identifier", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
553 crate::clusters::codec::CommandField { tag: 5, name: "possession_nonce", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
554 ]),
555 0x03 => Some(vec![
556 crate::clusters::codec::CommandField { tag: 0, name: "operational_dataset", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
557 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
558 ]),
559 0x04 => Some(vec![
560 crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
561 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
562 ]),
563 0x06 => Some(vec![
564 crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
565 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
566 ]),
567 0x08 => Some(vec![
568 crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
569 crate::clusters::codec::CommandField { tag: 1, name: "network_index", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
570 crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
571 ]),
572 0x09 => Some(vec![
573 crate::clusters::codec::CommandField { tag: 0, name: "key_identifier", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
574 crate::clusters::codec::CommandField { tag: 1, name: "possession_nonce", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
575 ]),
576 _ => None,
577 }
578}
579
580pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
581 match cmd_id {
582 0x00 => {
583 let ssid = crate::clusters::codec::json_util::get_opt_octstr(args, "ssid")?;
584 let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
585 encode_scan_networks(ssid, breadcrumb)
586 }
587 0x02 => {
588 let ssid = crate::clusters::codec::json_util::get_octstr(args, "ssid")?;
589 let credentials = crate::clusters::codec::json_util::get_octstr(args, "credentials")?;
590 let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
591 let network_identity = crate::clusters::codec::json_util::get_opt_octstr(args, "network_identity")?;
592 let client_identifier = crate::clusters::codec::json_util::get_opt_octstr(args, "client_identifier")?;
593 let possession_nonce = crate::clusters::codec::json_util::get_opt_octstr(args, "possession_nonce")?;
594 encode_add_or_update_wifi_network(ssid, credentials, breadcrumb, network_identity, client_identifier, possession_nonce)
595 }
596 0x03 => {
597 let operational_dataset = crate::clusters::codec::json_util::get_octstr(args, "operational_dataset")?;
598 let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
599 encode_add_or_update_thread_network(operational_dataset, breadcrumb)
600 }
601 0x04 => {
602 let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
603 let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
604 encode_remove_network(network_id, breadcrumb)
605 }
606 0x06 => {
607 let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
608 let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
609 encode_connect_network(network_id, breadcrumb)
610 }
611 0x08 => {
612 let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
613 let network_index = crate::clusters::codec::json_util::get_u8(args, "network_index")?;
614 let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
615 encode_reorder_network(network_id, network_index, breadcrumb)
616 }
617 0x09 => {
618 let key_identifier = crate::clusters::codec::json_util::get_octstr(args, "key_identifier")?;
619 let possession_nonce = crate::clusters::codec::json_util::get_opt_octstr(args, "possession_nonce")?;
620 encode_query_identity(key_identifier, possession_nonce)
621 }
622 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
623 }
624}
625
626#[derive(Debug, serde::Serialize)]
627pub struct ScanNetworksResponse {
628 pub networking_status: Option<NetworkCommissioningStatus>,
629 pub debug_text: Option<String>,
630 pub wifi_scan_results: Option<Vec<WiFiInterfaceScanResult>>,
631 pub thread_scan_results: Option<Vec<ThreadInterfaceScanResult>>,
632}
633
634#[derive(Debug, serde::Serialize)]
635pub struct NetworkConfigResponse {
636 pub networking_status: Option<NetworkCommissioningStatus>,
637 pub debug_text: Option<String>,
638 pub network_index: Option<u8>,
639 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
640 pub client_identity: Option<Vec<u8>>,
641 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
642 pub possession_signature: Option<Vec<u8>>,
643}
644
645#[derive(Debug, serde::Serialize)]
646pub struct ConnectNetworkResponse {
647 pub networking_status: Option<NetworkCommissioningStatus>,
648 pub debug_text: Option<String>,
649 pub error_value: Option<i32>,
650}
651
652#[derive(Debug, serde::Serialize)]
653pub struct QueryIdentityResponse {
654 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
655 pub identity: Option<Vec<u8>>,
656 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
657 pub possession_signature: Option<Vec<u8>>,
658}
659
660pub fn decode_scan_networks_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ScanNetworksResponse> {
664 if let tlv::TlvItemValue::List(_fields) = inp {
665 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
666 Ok(ScanNetworksResponse {
667 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
668 debug_text: item.get_string_owned(&[1]),
669 wifi_scan_results: {
670 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
671 let mut items = Vec::new();
672 for list_item in l {
673 items.push(WiFiInterfaceScanResult {
674 security: list_item.get_int(&[0]).map(|v| v as u8),
675 ssid: list_item.get_octet_string_owned(&[1]),
676 bssid: list_item.get_octet_string_owned(&[2]),
677 channel: list_item.get_int(&[3]).map(|v| v as u16),
678 wifi_band: list_item.get_int(&[4]).and_then(|v| WiFiBand::from_u8(v as u8)),
679 rssi: list_item.get_int(&[5]).map(|v| v as i8),
680 });
681 }
682 Some(items)
683 } else {
684 None
685 }
686 },
687 thread_scan_results: {
688 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
689 let mut items = Vec::new();
690 for list_item in l {
691 items.push(ThreadInterfaceScanResult {
692 pan_id: list_item.get_int(&[0]).map(|v| v as u16),
693 extended_pan_id: list_item.get_int(&[1]),
694 network_name: list_item.get_string_owned(&[2]),
695 channel: list_item.get_int(&[3]).map(|v| v as u16),
696 version: list_item.get_int(&[4]).map(|v| v as u8),
697 extended_address: list_item.get_int(&[5]).map(|v| v as u8),
698 rssi: list_item.get_int(&[6]).map(|v| v as i8),
699 lqi: list_item.get_int(&[7]).map(|v| v as u8),
700 });
701 }
702 Some(items)
703 } else {
704 None
705 }
706 },
707 })
708 } else {
709 Err(anyhow::anyhow!("Expected struct fields"))
710 }
711}
712
713pub fn decode_network_config_response(inp: &tlv::TlvItemValue) -> anyhow::Result<NetworkConfigResponse> {
715 if let tlv::TlvItemValue::List(_fields) = inp {
716 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
717 Ok(NetworkConfigResponse {
718 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
719 debug_text: item.get_string_owned(&[1]),
720 network_index: item.get_int(&[2]).map(|v| v as u8),
721 client_identity: item.get_octet_string_owned(&[3]),
722 possession_signature: item.get_octet_string_owned(&[4]),
723 })
724 } else {
725 Err(anyhow::anyhow!("Expected struct fields"))
726 }
727}
728
729pub fn decode_connect_network_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ConnectNetworkResponse> {
731 if let tlv::TlvItemValue::List(_fields) = inp {
732 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
733 Ok(ConnectNetworkResponse {
734 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
735 debug_text: item.get_string_owned(&[1]),
736 error_value: item.get_int(&[2]).map(|v| v as i32),
737 })
738 } else {
739 Err(anyhow::anyhow!("Expected struct fields"))
740 }
741}
742
743pub fn decode_query_identity_response(inp: &tlv::TlvItemValue) -> anyhow::Result<QueryIdentityResponse> {
745 if let tlv::TlvItemValue::List(_fields) = inp {
746 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
747 Ok(QueryIdentityResponse {
748 identity: item.get_octet_string_owned(&[0]),
749 possession_signature: item.get_octet_string_owned(&[1]),
750 })
751 } else {
752 Err(anyhow::anyhow!("Expected struct fields"))
753 }
754}
755
756pub async fn scan_networks(conn: &crate::controller::Connection, endpoint: u16, ssid: Option<Vec<u8>>, breadcrumb: Option<u64>) -> anyhow::Result<ScanNetworksResponse> {
760 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_CMD_ID_SCANNETWORKS, &encode_scan_networks(ssid, breadcrumb)?).await?;
761 decode_scan_networks_response(&tlv)
762}
763
764pub async fn add_or_update_wifi_network(conn: &crate::controller::Connection, endpoint: u16, ssid: Vec<u8>, credentials: Vec<u8>, breadcrumb: Option<u64>, network_identity: Option<Vec<u8>>, client_identifier: Option<Vec<u8>>, possession_nonce: Option<Vec<u8>>) -> anyhow::Result<NetworkConfigResponse> {
766 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_CMD_ID_ADDORUPDATEWIFINETWORK, &encode_add_or_update_wifi_network(ssid, credentials, breadcrumb, network_identity, client_identifier, possession_nonce)?).await?;
767 decode_network_config_response(&tlv)
768}
769
770pub async fn add_or_update_thread_network(conn: &crate::controller::Connection, endpoint: u16, operational_dataset: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<NetworkConfigResponse> {
772 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_CMD_ID_ADDORUPDATETHREADNETWORK, &encode_add_or_update_thread_network(operational_dataset, breadcrumb)?).await?;
773 decode_network_config_response(&tlv)
774}
775
776pub async fn remove_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<NetworkConfigResponse> {
778 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_CMD_ID_REMOVENETWORK, &encode_remove_network(network_id, breadcrumb)?).await?;
779 decode_network_config_response(&tlv)
780}
781
782pub async fn connect_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<ConnectNetworkResponse> {
784 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_CMD_ID_CONNECTNETWORK, &encode_connect_network(network_id, breadcrumb)?).await?;
785 decode_connect_network_response(&tlv)
786}
787
788pub async fn reorder_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, network_index: u8, breadcrumb: Option<u64>) -> anyhow::Result<NetworkConfigResponse> {
790 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_CMD_ID_REORDERNETWORK, &encode_reorder_network(network_id, network_index, breadcrumb)?).await?;
791 decode_network_config_response(&tlv)
792}
793
794pub async fn query_identity(conn: &crate::controller::Connection, endpoint: u16, key_identifier: Vec<u8>, possession_nonce: Option<Vec<u8>>) -> anyhow::Result<QueryIdentityResponse> {
796 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_CMD_ID_QUERYIDENTITY, &encode_query_identity(key_identifier, possession_nonce)?).await?;
797 decode_query_identity_response(&tlv)
798}
799
800pub async fn read_max_networks(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
802 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_MAXNETWORKS).await?;
803 decode_max_networks(&tlv)
804}
805
806pub async fn read_networks(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NetworkInfo>> {
808 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_NETWORKS).await?;
809 decode_networks(&tlv)
810}
811
812pub async fn read_scan_max_time_seconds(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
814 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SCANMAXTIMESECONDS).await?;
815 decode_scan_max_time_seconds(&tlv)
816}
817
818pub async fn read_connect_max_time_seconds(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
820 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_CONNECTMAXTIMESECONDS).await?;
821 decode_connect_max_time_seconds(&tlv)
822}
823
824pub async fn read_interface_enabled(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
826 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_INTERFACEENABLED).await?;
827 decode_interface_enabled(&tlv)
828}
829
830pub async fn read_last_networking_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<NetworkCommissioningStatus>> {
832 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKINGSTATUS).await?;
833 decode_last_networking_status(&tlv)
834}
835
836pub async fn read_last_network_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Vec<u8>>> {
838 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKID).await?;
839 decode_last_network_id(&tlv)
840}
841
842pub async fn read_last_connect_error_value(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i32>> {
844 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTCONNECTERRORVALUE).await?;
845 decode_last_connect_error_value(&tlv)
846}
847
848pub async fn read_supported_wifi_bands(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<WiFiBand>> {
850 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDWIFIBANDS).await?;
851 decode_supported_wifi_bands(&tlv)
852}
853
854pub async fn read_supported_thread_features(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ThreadCapabilities> {
856 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDTHREADFEATURES).await?;
857 decode_supported_thread_features(&tlv)
858}
859
860pub async fn read_thread_version(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
862 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_THREADVERSION).await?;
863 decode_thread_version(&tlv)
864}
865