#![allow(clippy::too_many_arguments)]
use crate::tlv;
use anyhow;
use serde_json;
use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum NetworkCommissioningStatus {
Success = 0,
Outofrange = 1,
Boundsexceeded = 2,
Networkidnotfound = 3,
Duplicatenetworkid = 4,
Networknotfound = 5,
Regulatoryerror = 6,
Authfailure = 7,
Unsupportedsecurity = 8,
Otherconnectionfailure = 9,
Ipv6failed = 10,
Ipbindfailed = 11,
Unknownerror = 12,
}
impl NetworkCommissioningStatus {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(NetworkCommissioningStatus::Success),
1 => Some(NetworkCommissioningStatus::Outofrange),
2 => Some(NetworkCommissioningStatus::Boundsexceeded),
3 => Some(NetworkCommissioningStatus::Networkidnotfound),
4 => Some(NetworkCommissioningStatus::Duplicatenetworkid),
5 => Some(NetworkCommissioningStatus::Networknotfound),
6 => Some(NetworkCommissioningStatus::Regulatoryerror),
7 => Some(NetworkCommissioningStatus::Authfailure),
8 => Some(NetworkCommissioningStatus::Unsupportedsecurity),
9 => Some(NetworkCommissioningStatus::Otherconnectionfailure),
10 => Some(NetworkCommissioningStatus::Ipv6failed),
11 => Some(NetworkCommissioningStatus::Ipbindfailed),
12 => Some(NetworkCommissioningStatus::Unknownerror),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<NetworkCommissioningStatus> for u8 {
fn from(val: NetworkCommissioningStatus) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum WiFiBand {
_2g4 = 0,
_3g65 = 1,
_5g = 2,
_6g = 3,
_60g = 4,
_1g = 5,
}
impl WiFiBand {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(WiFiBand::_2g4),
1 => Some(WiFiBand::_3g65),
2 => Some(WiFiBand::_5g),
3 => Some(WiFiBand::_6g),
4 => Some(WiFiBand::_60g),
5 => Some(WiFiBand::_1g),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<WiFiBand> for u8 {
fn from(val: WiFiBand) -> Self {
val as u8
}
}
pub type ThreadCapabilities = u8;
pub mod threadcapabilities {
pub const IS_BORDER_ROUTER_CAPABLE: u8 = 0x01;
pub const IS_ROUTER_CAPABLE: u8 = 0x02;
pub const IS_SLEEPY_END_DEVICE_CAPABLE: u8 = 0x04;
pub const IS_FULL_THREAD_DEVICE: u8 = 0x08;
pub const IS_SYNCHRONIZED_SLEEPY_END_DEVICE_CAPABLE: u8 = 0x10;
}
pub type WiFiSecurity = u8;
pub mod wifisecurity {
pub const UNENCRYPTED: u8 = 0x01;
pub const WEP: u8 = 0x02;
pub const WPA_PERSONAL: u8 = 0x04;
pub const WPA2_PERSONAL: u8 = 0x08;
pub const WPA3_PERSONAL: u8 = 0x10;
}
#[derive(Debug, serde::Serialize)]
pub struct NetworkInfo {
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub network_id: Option<Vec<u8>>,
pub connected: Option<bool>,
}
#[derive(Debug, serde::Serialize)]
pub struct ThreadInterfaceScanResult {
pub pan_id: Option<u16>,
pub extended_pan_id: Option<u64>,
pub network_name: Option<String>,
pub channel: Option<u16>,
pub version: Option<u8>,
pub extended_address: Option<u8>,
pub rssi: Option<i8>,
pub lqi: Option<u8>,
}
#[derive(Debug, serde::Serialize)]
pub struct WiFiInterfaceScanResult {
pub security: Option<WiFiSecurity>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub ssid: Option<Vec<u8>>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub bssid: Option<Vec<u8>>,
pub channel: Option<u16>,
pub wifi_band: Option<WiFiBand>,
pub rssi: Option<i8>,
}
pub fn encode_scan_networks(ssid: Option<Vec<u8>>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(ssid.unwrap_or_default())).into());
if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_add_or_update_wifi_network(ssid: Vec<u8>, credentials: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(ssid)).into());
tlv_fields.push((1, tlv::TlvItemValueEnc::OctetString(credentials)).into());
if let Some(x) = breadcrumb { tlv_fields.push((2, tlv::TlvItemValueEnc::UInt64(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_add_or_update_thread_network(operational_dataset: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(operational_dataset)).into());
if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_network(network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(network_id)).into());
if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_connect_network(network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(network_id)).into());
if let Some(x) = breadcrumb { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_reorder_network(network_id: Vec<u8>, network_index: u8, breadcrumb: Option<u64>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::OctetString(network_id)).into());
tlv_fields.push((1, tlv::TlvItemValueEnc::UInt8(network_index)).into());
if let Some(x) = breadcrumb { tlv_fields.push((2, tlv::TlvItemValueEnc::UInt64(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn decode_max_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_networks(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<NetworkInfo>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(NetworkInfo {
network_id: item.get_octet_string_owned(&[0]),
connected: item.get_bool(&[1]),
});
}
}
Ok(res)
}
pub fn decode_scan_max_time_seconds(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_connect_max_time_seconds(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_interface_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_last_networking_status(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<NetworkCommissioningStatus>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(NetworkCommissioningStatus::from_u8(*v as u8))
} else {
Ok(None)
}
}
pub fn decode_last_network_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
if let tlv::TlvItemValue::OctetString(v) = inp {
Ok(Some(v.clone()))
} else {
Ok(None)
}
}
pub fn decode_last_connect_error_value(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i32>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as i32))
} else {
Ok(None)
}
}
pub fn decode_supported_wifi_bands(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<WiFiBand>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
if let tlv::TlvItemValue::Int(i) = &item.value {
if let Some(enum_val) = WiFiBand::from_u8(*i as u8) {
res.push(enum_val);
}
}
}
}
Ok(res)
}
pub fn decode_supported_thread_features(inp: &tlv::TlvItemValue) -> anyhow::Result<ThreadCapabilities> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_thread_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x0031 {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0031, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_max_networks(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_networks(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_scan_max_time_seconds(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_connect_max_time_seconds(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_interface_enabled(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_last_networking_status(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_last_network_id(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0007 => {
match decode_last_connect_error_value(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0008 => {
match decode_supported_wifi_bands(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0009 => {
match decode_supported_thread_features(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000A => {
match decode_thread_version(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
_ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
}
}
pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
vec![
(0x0000, "MaxNetworks"),
(0x0001, "Networks"),
(0x0002, "ScanMaxTimeSeconds"),
(0x0003, "ConnectMaxTimeSeconds"),
(0x0004, "InterfaceEnabled"),
(0x0005, "LastNetworkingStatus"),
(0x0006, "LastNetworkID"),
(0x0007, "LastConnectErrorValue"),
(0x0008, "SupportedWiFiBands"),
(0x0009, "SupportedThreadFeatures"),
(0x000A, "ThreadVersion"),
]
}
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "ScanNetworks"),
(0x02, "AddOrUpdateWiFiNetwork"),
(0x03, "AddOrUpdateThreadNetwork"),
(0x04, "RemoveNetwork"),
(0x06, "ConnectNetwork"),
(0x08, "ReorderNetwork"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("ScanNetworks"),
0x02 => Some("AddOrUpdateWiFiNetwork"),
0x03 => Some("AddOrUpdateThreadNetwork"),
0x04 => Some("RemoveNetwork"),
0x06 => Some("ConnectNetwork"),
0x08 => Some("ReorderNetwork"),
_ => None,
}
}
pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
match cmd_id {
0x00 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "ssid", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: true },
crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
]),
0x02 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "ssid", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "credentials", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
]),
0x03 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "operational_dataset", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
]),
0x04 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
]),
0x06 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
]),
0x08 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "network_id", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "network_index", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: true, nullable: false },
]),
_ => None,
}
}
pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
match cmd_id {
0x00 => {
let ssid = crate::clusters::codec::json_util::get_opt_octstr(args, "ssid")?;
let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
encode_scan_networks(ssid, breadcrumb)
}
0x02 => {
let ssid = crate::clusters::codec::json_util::get_octstr(args, "ssid")?;
let credentials = crate::clusters::codec::json_util::get_octstr(args, "credentials")?;
let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
encode_add_or_update_wifi_network(ssid, credentials, breadcrumb)
}
0x03 => {
let operational_dataset = crate::clusters::codec::json_util::get_octstr(args, "operational_dataset")?;
let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
encode_add_or_update_thread_network(operational_dataset, breadcrumb)
}
0x04 => {
let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
encode_remove_network(network_id, breadcrumb)
}
0x06 => {
let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
encode_connect_network(network_id, breadcrumb)
}
0x08 => {
let network_id = crate::clusters::codec::json_util::get_octstr(args, "network_id")?;
let network_index = crate::clusters::codec::json_util::get_u8(args, "network_index")?;
let breadcrumb = crate::clusters::codec::json_util::get_opt_u64(args, "breadcrumb")?;
encode_reorder_network(network_id, network_index, breadcrumb)
}
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
#[derive(Debug, serde::Serialize)]
pub struct ScanNetworksResponse {
pub networking_status: Option<NetworkCommissioningStatus>,
pub debug_text: Option<String>,
pub wifi_scan_results: Option<Vec<WiFiInterfaceScanResult>>,
pub thread_scan_results: Option<Vec<ThreadInterfaceScanResult>>,
}
#[derive(Debug, serde::Serialize)]
pub struct NetworkConfigResponse {
pub networking_status: Option<NetworkCommissioningStatus>,
pub debug_text: Option<String>,
pub network_index: Option<u8>,
}
#[derive(Debug, serde::Serialize)]
pub struct ConnectNetworkResponse {
pub networking_status: Option<NetworkCommissioningStatus>,
pub debug_text: Option<String>,
pub error_value: Option<i32>,
}
pub fn decode_scan_networks_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ScanNetworksResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(ScanNetworksResponse {
networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
debug_text: item.get_string_owned(&[1]),
wifi_scan_results: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let mut items = Vec::new();
for list_item in l {
items.push(WiFiInterfaceScanResult {
security: list_item.get_int(&[0]).map(|v| v as u8),
ssid: list_item.get_octet_string_owned(&[1]),
bssid: list_item.get_octet_string_owned(&[2]),
channel: list_item.get_int(&[3]).map(|v| v as u16),
wifi_band: list_item.get_int(&[4]).and_then(|v| WiFiBand::from_u8(v as u8)),
rssi: list_item.get_int(&[5]).map(|v| v as i8),
});
}
Some(items)
} else {
None
}
},
thread_scan_results: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
let mut items = Vec::new();
for list_item in l {
items.push(ThreadInterfaceScanResult {
pan_id: list_item.get_int(&[0]).map(|v| v as u16),
extended_pan_id: list_item.get_int(&[1]),
network_name: list_item.get_string_owned(&[2]),
channel: list_item.get_int(&[3]).map(|v| v as u16),
version: list_item.get_int(&[4]).map(|v| v as u8),
extended_address: list_item.get_int(&[5]).map(|v| v as u8),
rssi: list_item.get_int(&[6]).map(|v| v as i8),
lqi: list_item.get_int(&[7]).map(|v| v as u8),
});
}
Some(items)
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_network_config_response(inp: &tlv::TlvItemValue) -> anyhow::Result<NetworkConfigResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(NetworkConfigResponse {
networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
debug_text: item.get_string_owned(&[1]),
network_index: item.get_int(&[2]).map(|v| v as u8),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_connect_network_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ConnectNetworkResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(ConnectNetworkResponse {
networking_status: item.get_int(&[0]).and_then(|v| NetworkCommissioningStatus::from_u8(v as u8)),
debug_text: item.get_string_owned(&[1]),
error_value: item.get_int(&[2]).map(|v| v as i32),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub async fn scan_networks(conn: &crate::controller::Connection, endpoint: u16, ssid: Option<Vec<u8>>, breadcrumb: Option<u64>) -> anyhow::Result<ScanNetworksResponse> {
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?;
decode_scan_networks_response(&tlv)
}
pub async fn add_or_update_wifi_network(conn: &crate::controller::Connection, endpoint: u16, ssid: Vec<u8>, credentials: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<NetworkConfigResponse> {
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?;
decode_network_config_response(&tlv)
}
pub async fn add_or_update_thread_network(conn: &crate::controller::Connection, endpoint: u16, operational_dataset: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<NetworkConfigResponse> {
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?;
decode_network_config_response(&tlv)
}
pub async fn remove_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<NetworkConfigResponse> {
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?;
decode_network_config_response(&tlv)
}
pub async fn connect_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, breadcrumb: Option<u64>) -> anyhow::Result<ConnectNetworkResponse> {
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?;
decode_connect_network_response(&tlv)
}
pub async fn reorder_network(conn: &crate::controller::Connection, endpoint: u16, network_id: Vec<u8>, network_index: u8, breadcrumb: Option<u64>) -> anyhow::Result<NetworkConfigResponse> {
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?;
decode_network_config_response(&tlv)
}
pub async fn read_max_networks(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_MAXNETWORKS).await?;
decode_max_networks(&tlv)
}
pub async fn read_networks(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<NetworkInfo>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_NETWORKS).await?;
decode_networks(&tlv)
}
pub async fn read_scan_max_time_seconds(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SCANMAXTIMESECONDS).await?;
decode_scan_max_time_seconds(&tlv)
}
pub async fn read_connect_max_time_seconds(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_CONNECTMAXTIMESECONDS).await?;
decode_connect_max_time_seconds(&tlv)
}
pub async fn read_interface_enabled(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_INTERFACEENABLED).await?;
decode_interface_enabled(&tlv)
}
pub async fn read_last_networking_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<NetworkCommissioningStatus>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKINGSTATUS).await?;
decode_last_networking_status(&tlv)
}
pub async fn read_last_network_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Vec<u8>>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKID).await?;
decode_last_network_id(&tlv)
}
pub async fn read_last_connect_error_value(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i32>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTCONNECTERRORVALUE).await?;
decode_last_connect_error_value(&tlv)
}
pub async fn read_supported_wifi_bands(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<WiFiBand>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDWIFIBANDS).await?;
decode_supported_wifi_bands(&tlv)
}
pub async fn read_supported_thread_features(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ThreadCapabilities> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDTHREADFEATURES).await?;
decode_supported_thread_features(&tlv)
}
pub async fn read_thread_version(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_NETWORK_COMMISSIONING, crate::clusters::defs::CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_THREADVERSION).await?;
decode_thread_version(&tlv)
}