1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum NetworkCommissioningStatus {
19 Success = 0,
21 Outofrange = 1,
23 Boundsexceeded = 2,
25 Networkidnotfound = 3,
27 Duplicatenetworkid = 4,
29 Networknotfound = 5,
31 Regulatoryerror = 6,
33 Authfailure = 7,
35 Unsupportedsecurity = 8,
37 Otherconnectionfailure = 9,
39 Ipv6failed = 10,
41 Ipbindfailed = 11,
43 Unknownerror = 12,
45}
46
47impl NetworkCommissioningStatus {
48 pub fn from_u8(value: u8) -> Option<Self> {
50 match value {
51 0 => Some(NetworkCommissioningStatus::Success),
52 1 => Some(NetworkCommissioningStatus::Outofrange),
53 2 => Some(NetworkCommissioningStatus::Boundsexceeded),
54 3 => Some(NetworkCommissioningStatus::Networkidnotfound),
55 4 => Some(NetworkCommissioningStatus::Duplicatenetworkid),
56 5 => Some(NetworkCommissioningStatus::Networknotfound),
57 6 => Some(NetworkCommissioningStatus::Regulatoryerror),
58 7 => Some(NetworkCommissioningStatus::Authfailure),
59 8 => Some(NetworkCommissioningStatus::Unsupportedsecurity),
60 9 => Some(NetworkCommissioningStatus::Otherconnectionfailure),
61 10 => Some(NetworkCommissioningStatus::Ipv6failed),
62 11 => Some(NetworkCommissioningStatus::Ipbindfailed),
63 12 => Some(NetworkCommissioningStatus::Unknownerror),
64 _ => None,
65 }
66 }
67
68 pub fn to_u8(self) -> u8 {
70 self as u8
71 }
72}
73
74impl From<NetworkCommissioningStatus> for u8 {
75 fn from(val: NetworkCommissioningStatus) -> Self {
76 val as u8
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
81#[repr(u8)]
82pub enum WiFiBand {
83 _2g4 = 0,
85 _3g65 = 1,
87 _5g = 2,
89 _6g = 3,
91 _60g = 4,
93 _1g = 5,
95}
96
97impl WiFiBand {
98 pub fn from_u8(value: u8) -> Option<Self> {
100 match value {
101 0 => Some(WiFiBand::_2g4),
102 1 => Some(WiFiBand::_3g65),
103 2 => Some(WiFiBand::_5g),
104 3 => Some(WiFiBand::_6g),
105 4 => Some(WiFiBand::_60g),
106 5 => Some(WiFiBand::_1g),
107 _ => None,
108 }
109 }
110
111 pub fn to_u8(self) -> u8 {
113 self as u8
114 }
115}
116
117impl From<WiFiBand> for u8 {
118 fn from(val: WiFiBand) -> Self {
119 val as u8
120 }
121}
122
123pub type ThreadCapabilities = u8;
127
128pub mod threadcapabilities {
130 pub const IS_BORDER_ROUTER_CAPABLE: u8 = 0x01;
132 pub const IS_ROUTER_CAPABLE: u8 = 0x02;
134 pub const IS_SLEEPY_END_DEVICE_CAPABLE: u8 = 0x04;
136 pub const IS_FULL_THREAD_DEVICE: u8 = 0x08;
138 pub const IS_SYNCHRONIZED_SLEEPY_END_DEVICE_CAPABLE: u8 = 0x10;
140}
141
142pub type WiFiSecurity = u8;
144
145pub mod wifisecurity {
147 pub const UNENCRYPTED: u8 = 0x01;
149 pub const WEP: u8 = 0x02;
151 pub const WPA_PERSONAL: u8 = 0x04;
153 pub const WPA2_PERSONAL: u8 = 0x08;
155 pub const WPA3_PERSONAL: u8 = 0x10;
157}
158
159#[derive(Debug, serde::Serialize)]
162pub struct NetworkInfo {
163 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
164 pub network_id: Option<Vec<u8>>,
165 pub connected: Option<bool>,
166}
167
168#[derive(Debug, serde::Serialize)]
169pub struct ThreadInterfaceScanResult {
170 pub pan_id: Option<u16>,
171 pub extended_pan_id: Option<u64>,
172 pub network_name: Option<String>,
173 pub channel: Option<u16>,
174 pub version: Option<u8>,
175 pub extended_address: Option<u8>,
176 pub rssi: Option<i8>,
177 pub lqi: Option<u8>,
178}
179
180#[derive(Debug, serde::Serialize)]
181pub struct WiFiInterfaceScanResult {
182 pub security: Option<WiFiSecurity>,
183 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
184 pub ssid: Option<Vec<u8>>,
185 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
186 pub bssid: Option<Vec<u8>>,
187 pub channel: Option<u16>,
188 pub wifi_band: Option<WiFiBand>,
189 pub rssi: Option<i8>,
190}
191
192pub fn encode_scan_networks(ssid: Option<Vec<u8>>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
196 let tlv = tlv::TlvItemEnc {
197 tag: 0,
198 value: tlv::TlvItemValueEnc::StructInvisible(vec![
199 (0, tlv::TlvItemValueEnc::OctetString(ssid.unwrap_or_default())).into(),
200 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
201 ]),
202 };
203 Ok(tlv.encode()?)
204}
205
206pub fn encode_add_or_update_wifi_network(ssid: Vec<u8>, credentials: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
208 let tlv = tlv::TlvItemEnc {
209 tag: 0,
210 value: tlv::TlvItemValueEnc::StructInvisible(vec![
211 (0, tlv::TlvItemValueEnc::OctetString(ssid)).into(),
212 (1, tlv::TlvItemValueEnc::OctetString(credentials)).into(),
213 (2, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
214 ]),
215 };
216 Ok(tlv.encode()?)
217}
218
219pub fn encode_add_or_update_thread_network(operational_dataset: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
221 let tlv = tlv::TlvItemEnc {
222 tag: 0,
223 value: tlv::TlvItemValueEnc::StructInvisible(vec![
224 (0, tlv::TlvItemValueEnc::OctetString(operational_dataset)).into(),
225 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
226 ]),
227 };
228 Ok(tlv.encode()?)
229}
230
231pub fn encode_remove_network(network_id: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
233 let tlv = tlv::TlvItemEnc {
234 tag: 0,
235 value: tlv::TlvItemValueEnc::StructInvisible(vec![
236 (0, tlv::TlvItemValueEnc::OctetString(network_id)).into(),
237 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
238 ]),
239 };
240 Ok(tlv.encode()?)
241}
242
243pub fn encode_connect_network(network_id: Vec<u8>, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
245 let tlv = tlv::TlvItemEnc {
246 tag: 0,
247 value: tlv::TlvItemValueEnc::StructInvisible(vec![
248 (0, tlv::TlvItemValueEnc::OctetString(network_id)).into(),
249 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
250 ]),
251 };
252 Ok(tlv.encode()?)
253}
254
255pub fn encode_reorder_network(network_id: Vec<u8>, network_index: u8, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
257 let tlv = tlv::TlvItemEnc {
258 tag: 0,
259 value: tlv::TlvItemValueEnc::StructInvisible(vec![
260 (0, tlv::TlvItemValueEnc::OctetString(network_id)).into(),
261 (1, tlv::TlvItemValueEnc::UInt8(network_index)).into(),
262 (2, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
263 ]),
264 };
265 Ok(tlv.encode()?)
266}
267
268pub fn decode_max_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
272 if let tlv::TlvItemValue::Int(v) = inp {
273 Ok(*v as u8)
274 } else {
275 Err(anyhow::anyhow!("Expected UInt8"))
276 }
277}
278
279pub fn decode_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkInfo>> {
281 let mut res = Vec::new();
282 if let tlv::TlvItemValue::List(v) = inp {
283 for item in v {
284 res.push(NetworkInfo {
285 network_id: item.get_octet_string_owned(&[0]),
286 connected: item.get_bool(&[1]),
287 });
288 }
289 }
290 Ok(res)
291}
292
293pub fn decode_scan_max_time_seconds(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_connect_max_time_seconds(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
304 if let tlv::TlvItemValue::Int(v) = inp {
305 Ok(*v as u8)
306 } else {
307 Err(anyhow::anyhow!("Expected UInt8"))
308 }
309}
310
311pub fn decode_interface_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
313 if let tlv::TlvItemValue::Bool(v) = inp {
314 Ok(*v)
315 } else {
316 Err(anyhow::anyhow!("Expected Bool"))
317 }
318}
319
320pub fn decode_last_networking_status(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<NetworkCommissioningStatus>> {
322 if let tlv::TlvItemValue::Int(v) = inp {
323 Ok(NetworkCommissioningStatus::from_u8(*v as u8))
324 } else {
325 Ok(None)
326 }
327}
328
329pub fn decode_last_network_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
331 if let tlv::TlvItemValue::OctetString(v) = inp {
332 Ok(Some(v.clone()))
333 } else {
334 Ok(None)
335 }
336}
337
338pub fn decode_last_connect_error_value(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i32>> {
340 if let tlv::TlvItemValue::Int(v) = inp {
341 Ok(Some(*v as i32))
342 } else {
343 Ok(None)
344 }
345}
346
347pub fn decode_supported_wifi_bands(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<WiFiBand>> {
349 let mut res = Vec::new();
350 if let tlv::TlvItemValue::List(v) = inp {
351 for item in v {
352 if let tlv::TlvItemValue::Int(i) = &item.value {
353 if let Some(enum_val) = WiFiBand::from_u8(*i as u8) {
354 res.push(enum_val);
355 }
356 }
357 }
358 }
359 Ok(res)
360}
361
362pub fn decode_supported_thread_features(inp: &tlv::TlvItemValue) -> anyhow::Result<ThreadCapabilities> {
364 if let tlv::TlvItemValue::Int(v) = inp {
365 Ok(*v as u8)
366 } else {
367 Err(anyhow::anyhow!("Expected Integer"))
368 }
369}
370
371pub fn decode_thread_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
373 if let tlv::TlvItemValue::Int(v) = inp {
374 Ok(*v as u16)
375 } else {
376 Err(anyhow::anyhow!("Expected UInt16"))
377 }
378}
379
380
381pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
393 if cluster_id != 0x0031 {
395 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0031, got {}\"}}", cluster_id);
396 }
397
398 match attribute_id {
399 0x0000 => {
400 match decode_max_networks(tlv_value) {
401 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
402 Err(e) => format!("{{\"error\": \"{}\"}}", e),
403 }
404 }
405 0x0001 => {
406 match decode_networks(tlv_value) {
407 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
408 Err(e) => format!("{{\"error\": \"{}\"}}", e),
409 }
410 }
411 0x0002 => {
412 match decode_scan_max_time_seconds(tlv_value) {
413 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
414 Err(e) => format!("{{\"error\": \"{}\"}}", e),
415 }
416 }
417 0x0003 => {
418 match decode_connect_max_time_seconds(tlv_value) {
419 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
420 Err(e) => format!("{{\"error\": \"{}\"}}", e),
421 }
422 }
423 0x0004 => {
424 match decode_interface_enabled(tlv_value) {
425 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
426 Err(e) => format!("{{\"error\": \"{}\"}}", e),
427 }
428 }
429 0x0005 => {
430 match decode_last_networking_status(tlv_value) {
431 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
432 Err(e) => format!("{{\"error\": \"{}\"}}", e),
433 }
434 }
435 0x0006 => {
436 match decode_last_network_id(tlv_value) {
437 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
438 Err(e) => format!("{{\"error\": \"{}\"}}", e),
439 }
440 }
441 0x0007 => {
442 match decode_last_connect_error_value(tlv_value) {
443 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
444 Err(e) => format!("{{\"error\": \"{}\"}}", e),
445 }
446 }
447 0x0008 => {
448 match decode_supported_wifi_bands(tlv_value) {
449 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
450 Err(e) => format!("{{\"error\": \"{}\"}}", e),
451 }
452 }
453 0x0009 => {
454 match decode_supported_thread_features(tlv_value) {
455 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
456 Err(e) => format!("{{\"error\": \"{}\"}}", e),
457 }
458 }
459 0x000A => {
460 match decode_thread_version(tlv_value) {
461 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
462 Err(e) => format!("{{\"error\": \"{}\"}}", e),
463 }
464 }
465 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
466 }
467}
468
469pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
474 vec![
475 (0x0000, "MaxNetworks"),
476 (0x0001, "Networks"),
477 (0x0002, "ScanMaxTimeSeconds"),
478 (0x0003, "ConnectMaxTimeSeconds"),
479 (0x0004, "InterfaceEnabled"),
480 (0x0005, "LastNetworkingStatus"),
481 (0x0006, "LastNetworkID"),
482 (0x0007, "LastConnectErrorValue"),
483 (0x0008, "SupportedWiFiBands"),
484 (0x0009, "SupportedThreadFeatures"),
485 (0x000A, "ThreadVersion"),
486 ]
487}
488
489#[derive(Debug, serde::Serialize)]
490pub struct ScanNetworksResponse {
491 pub networking_status: Option<NetworkCommissioningStatus>,
492 pub debug_text: Option<String>,
493 pub wifi_scan_results: Option<Vec<WiFiInterfaceScanResult>>,
494 pub thread_scan_results: Option<Vec<ThreadInterfaceScanResult>>,
495}
496
497#[derive(Debug, serde::Serialize)]
498pub struct NetworkConfigResponse {
499 pub networking_status: Option<NetworkCommissioningStatus>,
500 pub debug_text: Option<String>,
501 pub network_index: Option<u8>,
502}
503
504#[derive(Debug, serde::Serialize)]
505pub struct ConnectNetworkResponse {
506 pub networking_status: Option<NetworkCommissioningStatus>,
507 pub debug_text: Option<String>,
508 pub error_value: Option<i32>,
509}
510
511pub fn decode_scan_networks_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ScanNetworksResponse> {
515 if let tlv::TlvItemValue::List(_fields) = inp {
516 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
517 Ok(ScanNetworksResponse {
518 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
519 debug_text: item.get_string_owned(&[1]),
520 wifi_scan_results: {
521 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
522 let mut items = Vec::new();
523 for list_item in l {
524 items.push(WiFiInterfaceScanResult {
525 security: list_item.get_int(&[0]).map(|v| v as u8),
526 ssid: list_item.get_octet_string_owned(&[1]),
527 bssid: list_item.get_octet_string_owned(&[2]),
528 channel: list_item.get_int(&[3]).map(|v| v as u16),
529 wifi_band: list_item.get_int(&[4]).and_then(|v| WiFiBand::from_u8(v as u8)),
530 rssi: list_item.get_int(&[5]).map(|v| v as i8),
531 });
532 }
533 Some(items)
534 } else {
535 None
536 }
537 },
538 thread_scan_results: {
539 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
540 let mut items = Vec::new();
541 for list_item in l {
542 items.push(ThreadInterfaceScanResult {
543 pan_id: list_item.get_int(&[0]).map(|v| v as u16),
544 extended_pan_id: list_item.get_int(&[1]),
545 network_name: list_item.get_string_owned(&[2]),
546 channel: list_item.get_int(&[3]).map(|v| v as u16),
547 version: list_item.get_int(&[4]).map(|v| v as u8),
548 extended_address: list_item.get_int(&[5]).map(|v| v as u8),
549 rssi: list_item.get_int(&[6]).map(|v| v as i8),
550 lqi: list_item.get_int(&[7]).map(|v| v as u8),
551 });
552 }
553 Some(items)
554 } else {
555 None
556 }
557 },
558 })
559 } else {
560 Err(anyhow::anyhow!("Expected struct fields"))
561 }
562}
563
564pub fn decode_network_config_response(inp: &tlv::TlvItemValue) -> anyhow::Result<NetworkConfigResponse> {
566 if let tlv::TlvItemValue::List(_fields) = inp {
567 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
568 Ok(NetworkConfigResponse {
569 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
570 debug_text: item.get_string_owned(&[1]),
571 network_index: item.get_int(&[2]).map(|v| v as u8),
572 })
573 } else {
574 Err(anyhow::anyhow!("Expected struct fields"))
575 }
576}
577
578pub fn decode_connect_network_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ConnectNetworkResponse> {
580 if let tlv::TlvItemValue::List(_fields) = inp {
581 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
582 Ok(ConnectNetworkResponse {
583 networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
584 debug_text: item.get_string_owned(&[1]),
585 error_value: item.get_int(&[2]).map(|v| v as i32),
586 })
587 } else {
588 Err(anyhow::anyhow!("Expected struct fields"))
589 }
590}
591