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}
160
161#[derive(Debug, serde::Serialize)]
164pub struct NetworkInfo {
165 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
166 pub network_id: Option<Vec<u8>>,
167 pub connected: Option<bool>,
168}
169
170#[derive(Debug, serde::Serialize)]
171pub struct ThreadInterfaceScanResult {
172 pub pan_id: Option<u16>,
173 pub extended_pan_id: Option<u64>,
174 pub network_name: Option<String>,
175 pub channel: Option<u16>,
176 pub version: Option<u8>,
177 pub extended_address: Option<u8>,
178 pub rssi: Option<i8>,
179 pub lqi: Option<u8>,
180}
181
182#[derive(Debug, serde::Serialize)]
183pub struct WiFiInterfaceScanResult {
184 pub security: Option<WiFiSecurity>,
185 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
186 pub ssid: Option<Vec<u8>>,
187 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
188 pub bssid: Option<Vec<u8>>,
189 pub channel: Option<u16>,
190 pub wifi_band: Option<WiFiBand>,
191 pub rssi: Option<i8>,
192}
193
194pub fn encode_scan_networks(ssid: Option<Vec<u8>>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
198 let tlv = tlv::TlvItemEnc {
199 tag: 0,
200 value: tlv::TlvItemValueEnc::StructInvisible(vec![
201 (0, tlv::TlvItemValueEnc::OctetString(ssid.unwrap_or_default())).into(),
202 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
203 ]),
204 };
205 Ok(tlv.encode()?)
206}
207
208pub fn encode_add_or_update_wifi_network(ssid: Vec<u8>, credentials: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
210 let tlv = tlv::TlvItemEnc {
211 tag: 0,
212 value: tlv::TlvItemValueEnc::StructInvisible(vec![
213 (0, tlv::TlvItemValueEnc::OctetString(ssid)).into(),
214 (1, tlv::TlvItemValueEnc::OctetString(credentials)).into(),
215 (2, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
216 ]),
217 };
218 Ok(tlv.encode()?)
219}
220
221pub fn encode_add_or_update_thread_network(operational_dataset: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
223 let tlv = tlv::TlvItemEnc {
224 tag: 0,
225 value: tlv::TlvItemValueEnc::StructInvisible(vec![
226 (0, tlv::TlvItemValueEnc::OctetString(operational_dataset)).into(),
227 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
228 ]),
229 };
230 Ok(tlv.encode()?)
231}
232
233pub fn encode_remove_network(network_id: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
235 let tlv = tlv::TlvItemEnc {
236 tag: 0,
237 value: tlv::TlvItemValueEnc::StructInvisible(vec![
238 (0, tlv::TlvItemValueEnc::OctetString(network_id)).into(),
239 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
240 ]),
241 };
242 Ok(tlv.encode()?)
243}
244
245pub fn encode_connect_network(network_id: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
247 let tlv = tlv::TlvItemEnc {
248 tag: 0,
249 value: tlv::TlvItemValueEnc::StructInvisible(vec![
250 (0, tlv::TlvItemValueEnc::OctetString(network_id)).into(),
251 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
252 ]),
253 };
254 Ok(tlv.encode()?)
255}
256
257pub fn encode_reorder_network(network_id: Vec<u8>, network_index: u8, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
259 let tlv = tlv::TlvItemEnc {
260 tag: 0,
261 value: tlv::TlvItemValueEnc::StructInvisible(vec![
262 (0, tlv::TlvItemValueEnc::OctetString(network_id)).into(),
263 (1, tlv::TlvItemValueEnc::UInt8(network_index)).into(),
264 (2, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
265 ]),
266 };
267 Ok(tlv.encode()?)
268}
269
270pub fn decode_max_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
274 if let tlv::TlvItemValue::Int(v) = inp {
275 Ok(*v as u8)
276 } else {
277 Err(anyhow::anyhow!("Expected UInt8"))
278 }
279}
280
281pub fn decode_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkInfo>> {
283 let mut res = Vec::new();
284 if let tlv::TlvItemValue::List(v) = inp {
285 for item in v {
286 res.push(NetworkInfo {
287 network_id: item.get_octet_string_owned(&[0]),
288 connected: item.get_bool(&[1]),
289 });
290 }
291 }
292 Ok(res)
293}
294
295pub fn decode_scan_max_time_seconds(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
297 if let tlv::TlvItemValue::Int(v) = inp {
298 Ok(*v as u8)
299 } else {
300 Err(anyhow::anyhow!("Expected UInt8"))
301 }
302}
303
304pub fn decode_connect_max_time_seconds(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
306 if let tlv::TlvItemValue::Int(v) = inp {
307 Ok(*v as u8)
308 } else {
309 Err(anyhow::anyhow!("Expected UInt8"))
310 }
311}
312
313pub fn decode_interface_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
315 if let tlv::TlvItemValue::Bool(v) = inp {
316 Ok(*v)
317 } else {
318 Err(anyhow::anyhow!("Expected Bool"))
319 }
320}
321
322pub fn decode_last_networking_status(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<NetworkCommissioningStatus>> {
324 if let tlv::TlvItemValue::Int(v) = inp {
325 Ok(NetworkCommissioningStatus::from_u8(*v as u8))
326 } else {
327 Ok(None)
328 }
329}
330
331pub fn decode_last_network_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
333 if let tlv::TlvItemValue::OctetString(v) = inp {
334 Ok(Some(v.clone()))
335 } else {
336 Ok(None)
337 }
338}
339
340pub fn decode_last_connect_error_value(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i32>> {
342 if let tlv::TlvItemValue::Int(v) = inp {
343 Ok(Some(*v as i32))
344 } else {
345 Ok(None)
346 }
347}
348
349pub fn decode_supported_wifi_bands(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<WiFiBand>> {
351 let mut res = Vec::new();
352 if let tlv::TlvItemValue::List(v) = inp {
353 for item in v {
354 if let tlv::TlvItemValue::Int(i) = &item.value {
355 if let Some(enum_val) = WiFiBand::from_u8(*i as u8) {
356 res.push(enum_val);
357 }
358 }
359 }
360 }
361 Ok(res)
362}
363
364pub fn decode_supported_thread_features(inp: &tlv::TlvItemValue) -> anyhow::Result<ThreadCapabilities> {
366 if let tlv::TlvItemValue::Int(v) = inp {
367 Ok(*v as u8)
368 } else {
369 Err(anyhow::anyhow!("Expected Integer"))
370 }
371}
372
373pub fn decode_thread_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
375 if let tlv::TlvItemValue::Int(v) = inp {
376 Ok(*v as u16)
377 } else {
378 Err(anyhow::anyhow!("Expected UInt16"))
379 }
380}
381
382
383pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
395 if cluster_id != 0x0031 {
397 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0031, got {}\"}}", cluster_id);
398 }
399
400 match attribute_id {
401 0x0000 => {
402 match decode_max_networks(tlv_value) {
403 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
404 Err(e) => format!("{{\"error\": \"{}\"}}", e),
405 }
406 }
407 0x0001 => {
408 match decode_networks(tlv_value) {
409 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
410 Err(e) => format!("{{\"error\": \"{}\"}}", e),
411 }
412 }
413 0x0002 => {
414 match decode_scan_max_time_seconds(tlv_value) {
415 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
416 Err(e) => format!("{{\"error\": \"{}\"}}", e),
417 }
418 }
419 0x0003 => {
420 match decode_connect_max_time_seconds(tlv_value) {
421 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
422 Err(e) => format!("{{\"error\": \"{}\"}}", e),
423 }
424 }
425 0x0004 => {
426 match decode_interface_enabled(tlv_value) {
427 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
428 Err(e) => format!("{{\"error\": \"{}\"}}", e),
429 }
430 }
431 0x0005 => {
432 match decode_last_networking_status(tlv_value) {
433 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
434 Err(e) => format!("{{\"error\": \"{}\"}}", e),
435 }
436 }
437 0x0006 => {
438 match decode_last_network_id(tlv_value) {
439 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
440 Err(e) => format!("{{\"error\": \"{}\"}}", e),
441 }
442 }
443 0x0007 => {
444 match decode_last_connect_error_value(tlv_value) {
445 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
446 Err(e) => format!("{{\"error\": \"{}\"}}", e),
447 }
448 }
449 0x0008 => {
450 match decode_supported_wifi_bands(tlv_value) {
451 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
452 Err(e) => format!("{{\"error\": \"{}\"}}", e),
453 }
454 }
455 0x0009 => {
456 match decode_supported_thread_features(tlv_value) {
457 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
458 Err(e) => format!("{{\"error\": \"{}\"}}", e),
459 }
460 }
461 0x000A => {
462 match decode_thread_version(tlv_value) {
463 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
464 Err(e) => format!("{{\"error\": \"{}\"}}", e),
465 }
466 }
467 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
468 }
469}
470
471pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
476 vec![
477 (0x0000, "MaxNetworks"),
478 (0x0001, "Networks"),
479 (0x0002, "ScanMaxTimeSeconds"),
480 (0x0003, "ConnectMaxTimeSeconds"),
481 (0x0004, "InterfaceEnabled"),
482 (0x0005, "LastNetworkingStatus"),
483 (0x0006, "LastNetworkID"),
484 (0x0007, "LastConnectErrorValue"),
485 (0x0008, "SupportedWiFiBands"),
486 (0x0009, "SupportedThreadFeatures"),
487 (0x000A, "ThreadVersion"),
488 ]
489}
490
491pub fn get_command_list() -> Vec<(u32, &'static str)> {
494 vec![
495 (0x00, "ScanNetworks"),
496 (0x02, "AddOrUpdateWiFiNetwork"),
497 (0x03, "AddOrUpdateThreadNetwork"),
498 (0x04, "RemoveNetwork"),
499 (0x06, "ConnectNetwork"),
500 (0x08, "ReorderNetwork"),
501 ]
502}
503
504pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
505 match cmd_id {
506 0x00 => Some("ScanNetworks"),
507 0x02 => Some("AddOrUpdateWiFiNetwork"),
508 0x03 => Some("AddOrUpdateThreadNetwork"),
509 0x04 => Some("RemoveNetwork"),
510 0x06 => Some("ConnectNetwork"),
511 0x08 => Some("ReorderNetwork"),
512 _ => None,
513 }
514}
515
516pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
517 match cmd_id {
518 0x00 => Some(vec![
519 crate::clusters::codec::CommandField { tag: 0, name: "ssid", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: true },
520 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
521 ]),
522 0x02 => Some(vec![
523 crate::clusters::codec::CommandField { tag: 0, name: "ssid", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
524 crate::clusters::codec::CommandField { tag: 1, name: "credentials", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
525 crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
526 ]),
527 0x03 => Some(vec![
528 crate::clusters::codec::CommandField { tag: 0, name: "operational_dataset", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
529 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
530 ]),
531 0x04 => Some(vec![
532 crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
533 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
534 ]),
535 0x06 => Some(vec![
536 crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
537 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
538 ]),
539 0x08 => Some(vec![
540 crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
541 crate::clusters::codec::CommandField { tag: 1, name: "network_index", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
542 crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
543 ]),
544 _ => None,
545 }
546}
547
548pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
549 match cmd_id {
550 0x00 => {
551 let ssid = crate::clusters::codec::json_util::get_opt_octstr(args, "ssid")?;
552 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
553 encode_scan_networks(ssid, breadcrumb)
554 }
555 0x02 => {
556 let ssid = crate::clusters::codec::json_util::get_octstr(args, "ssid")?;
557 let credentials = crate::clusters::codec::json_util::get_octstr(args, "credentials")?;
558 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
559 encode_add_or_update_wifi_network(ssid, credentials, breadcrumb)
560 }
561 0x03 => {
562 let operational_dataset = crate::clusters::codec::json_util::get_octstr(args, "operational_dataset")?;
563 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
564 encode_add_or_update_thread_network(operational_dataset, breadcrumb)
565 }
566 0x04 => {
567 let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
568 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
569 encode_remove_network(network_id, breadcrumb)
570 }
571 0x06 => {
572 let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
573 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
574 encode_connect_network(network_id, breadcrumb)
575 }
576 0x08 => {
577 let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
578 let network_index = crate::clusters::codec::json_util::get_u8(args, "network_index")?;
579 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
580 encode_reorder_network(network_id, network_index, breadcrumb)
581 }
582 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
583 }
584}
585
586#[derive(Debug, serde::Serialize)]
587pub struct ScanNetworksResponse {
588 pub networking_status: Option<NetworkCommissioningStatus>,
589 pub debug_text: Option<String>,
590 pub wifi_scan_results: Option<Vec<WiFiInterfaceScanResult>>,
591 pub thread_scan_results: Option<Vec<ThreadInterfaceScanResult>>,
592}
593
594#[derive(Debug, serde::Serialize)]
595pub struct NetworkConfigResponse {
596 pub networking_status: Option<NetworkCommissioningStatus>,
597 pub debug_text: Option<String>,
598 pub network_index: Option<u8>,
599}
600
601#[derive(Debug, serde::Serialize)]
602pub struct ConnectNetworkResponse {
603 pub networking_status: Option<NetworkCommissioningStatus>,
604 pub debug_text: Option<String>,
605 pub error_value: Option<i32>,
606}
607
608pub fn decode_scan_networks_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ScanNetworksResponse> {
612 if let tlv::TlvItemValue::List(_fields) = inp {
613 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
614 Ok(ScanNetworksResponse {
615 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
616 debug_text: item.get_string_owned(&[1]),
617 wifi_scan_results: {
618 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
619 let mut items = Vec::new();
620 for list_item in l {
621 items.push(WiFiInterfaceScanResult {
622 security: list_item.get_int(&[0]).map(|v| v as u8),
623 ssid: list_item.get_octet_string_owned(&[1]),
624 bssid: list_item.get_octet_string_owned(&[2]),
625 channel: list_item.get_int(&[3]).map(|v| v as u16),
626 wifi_band: list_item.get_int(&[4]).and_then(|v| WiFiBand::from_u8(v as u8)),
627 rssi: list_item.get_int(&[5]).map(|v| v as i8),
628 });
629 }
630 Some(items)
631 } else {
632 None
633 }
634 },
635 thread_scan_results: {
636 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
637 let mut items = Vec::new();
638 for list_item in l {
639 items.push(ThreadInterfaceScanResult {
640 pan_id: list_item.get_int(&[0]).map(|v| v as u16),
641 extended_pan_id: list_item.get_int(&[1]),
642 network_name: list_item.get_string_owned(&[2]),
643 channel: list_item.get_int(&[3]).map(|v| v as u16),
644 version: list_item.get_int(&[4]).map(|v| v as u8),
645 extended_address: list_item.get_int(&[5]).map(|v| v as u8),
646 rssi: list_item.get_int(&[6]).map(|v| v as i8),
647 lqi: list_item.get_int(&[7]).map(|v| v as u8),
648 });
649 }
650 Some(items)
651 } else {
652 None
653 }
654 },
655 })
656 } else {
657 Err(anyhow::anyhow!("Expected struct fields"))
658 }
659}
660
661pub fn decode_network_config_response(inp: &tlv::TlvItemValue) -> anyhow::Result<NetworkConfigResponse> {
663 if let tlv::TlvItemValue::List(_fields) = inp {
664 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
665 Ok(NetworkConfigResponse {
666 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
667 debug_text: item.get_string_owned(&[1]),
668 network_index: item.get_int(&[2]).map(|v| v as u8),
669 })
670 } else {
671 Err(anyhow::anyhow!("Expected struct fields"))
672 }
673}
674
675pub fn decode_connect_network_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ConnectNetworkResponse> {
677 if let tlv::TlvItemValue::List(_fields) = inp {
678 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
679 Ok(ConnectNetworkResponse {
680 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
681 debug_text: item.get_string_owned(&[1]),
682 error_value: item.get_int(&[2]).map(|v| v as i32),
683 })
684 } else {
685 Err(anyhow::anyhow!("Expected struct fields"))
686 }
687}
688
689pub async fn scan_networks(conn: &crate::controller::Connection, endpoint: u16, ssid: Option<Vec<u8>>, breadcrumb: u64) -> anyhow::Result<ScanNetworksResponse> {
693 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?;
694 decode_scan_networks_response(&tlv)
695}
696
697pub async fn add_or_update_wifi_network(conn: &crate::controller::Connection, endpoint: u16, ssid: Vec<u8>, credentials: Vec<u8>, breadcrumb: u64) -> anyhow::Result<NetworkConfigResponse> {
699 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)?).await?;
700 decode_network_config_response(&tlv)
701}
702
703pub async fn add_or_update_thread_network(conn: &crate::controller::Connection, endpoint: u16, operational_dataset: Vec<u8>, breadcrumb: u64) -> anyhow::Result<NetworkConfigResponse> {
705 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?;
706 decode_network_config_response(&tlv)
707}
708
709pub async fn remove_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, breadcrumb: u64) -> anyhow::Result<NetworkConfigResponse> {
711 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?;
712 decode_network_config_response(&tlv)
713}
714
715pub async fn connect_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, breadcrumb: u64) -> anyhow::Result<ConnectNetworkResponse> {
717 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?;
718 decode_connect_network_response(&tlv)
719}
720
721pub async fn reorder_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, network_index: u8, breadcrumb: u64) -> anyhow::Result<NetworkConfigResponse> {
723 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?;
724 decode_network_config_response(&tlv)
725}
726
727pub async fn read_max_networks(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
729 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_MAXNETWORKS).await?;
730 decode_max_networks(&tlv)
731}
732
733pub async fn read_networks(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NetworkInfo>> {
735 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_NETWORKS).await?;
736 decode_networks(&tlv)
737}
738
739pub async fn read_scan_max_time_seconds(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
741 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SCANMAXTIMESECONDS).await?;
742 decode_scan_max_time_seconds(&tlv)
743}
744
745pub async fn read_connect_max_time_seconds(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
747 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_CONNECTMAXTIMESECONDS).await?;
748 decode_connect_max_time_seconds(&tlv)
749}
750
751pub async fn read_interface_enabled(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
753 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_INTERFACEENABLED).await?;
754 decode_interface_enabled(&tlv)
755}
756
757pub async fn read_last_networking_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<NetworkCommissioningStatus>> {
759 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKINGSTATUS).await?;
760 decode_last_networking_status(&tlv)
761}
762
763pub async fn read_last_network_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Vec<u8>>> {
765 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKID).await?;
766 decode_last_network_id(&tlv)
767}
768
769pub async fn read_last_connect_error_value(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i32>> {
771 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTCONNECTERRORVALUE).await?;
772 decode_last_connect_error_value(&tlv)
773}
774
775pub async fn read_supported_wifi_bands(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<WiFiBand>> {
777 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDWIFIBANDS).await?;
778 decode_supported_wifi_bands(&tlv)
779}
780
781pub async fn read_supported_thread_features(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ThreadCapabilities> {
783 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDTHREADFEATURES).await?;
784 decode_supported_thread_features(&tlv)
785}
786
787pub async fn read_thread_version(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
789 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_THREADVERSION).await?;
790 decode_thread_version(&tlv)
791}
792