#![allow(clippy::too_many_arguments)]
use crate::tlv;
use anyhow;
use serde_json;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum CommissioningError {
Ok = 0,
Valueoutsiderange = 1,
Invalidauthentication = 2,
Nofailsafe = 3,
Busywithotheradmin = 4,
Requiredtcnotaccepted = 5,
Tcacknowledgementsnotreceived = 6,
Tcminversionnotmet = 7,
}
impl CommissioningError {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(CommissioningError::Ok),
1 => Some(CommissioningError::Valueoutsiderange),
2 => Some(CommissioningError::Invalidauthentication),
3 => Some(CommissioningError::Nofailsafe),
4 => Some(CommissioningError::Busywithotheradmin),
5 => Some(CommissioningError::Requiredtcnotaccepted),
6 => Some(CommissioningError::Tcacknowledgementsnotreceived),
7 => Some(CommissioningError::Tcminversionnotmet),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<CommissioningError> for u8 {
fn from(val: CommissioningError) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum NetworkRecoveryReason {
Unspecified = 0,
Auth = 1,
Visibility = 2,
}
impl NetworkRecoveryReason {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(NetworkRecoveryReason::Unspecified),
1 => Some(NetworkRecoveryReason::Auth),
2 => Some(NetworkRecoveryReason::Visibility),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<NetworkRecoveryReason> for u8 {
fn from(val: NetworkRecoveryReason) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum RegulatoryLocationType {
Indoor = 0,
Outdoor = 1,
Indooroutdoor = 2,
}
impl RegulatoryLocationType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(RegulatoryLocationType::Indoor),
1 => Some(RegulatoryLocationType::Outdoor),
2 => Some(RegulatoryLocationType::Indooroutdoor),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<RegulatoryLocationType> for u8 {
fn from(val: RegulatoryLocationType) -> Self {
val as u8
}
}
#[derive(Debug, serde::Serialize)]
pub struct BasicCommissioningInfo {
pub fail_safe_expiry_length_seconds: Option<u16>,
pub max_cumulative_failsafe_seconds: Option<u16>,
}
pub fn encode_arm_fail_safe(expiry_length_seconds: u16, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt16(expiry_length_seconds)).into(),
(1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_regulatory_config(new_regulatory_config: RegulatoryLocationType, country_code: String, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(new_regulatory_config.to_u8())).into(),
(1, tlv::TlvItemValueEnc::String(country_code)).into(),
(2, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_tc_acknowledgements(tc_version: u16, tc_user_response: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt16(tc_version)).into(),
(1, tlv::TlvItemValueEnc::UInt8(tc_user_response)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn decode_breadcrumb(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected UInt64"))
}
}
pub fn decode_basic_commissioning_info(inp: &tlv::TlvItemValue) -> anyhow::Result<BasicCommissioningInfo> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(BasicCommissioningInfo {
fail_safe_expiry_length_seconds: item.get_int(&[0]).map(|v| v as u16),
max_cumulative_failsafe_seconds: item.get_int(&[1]).map(|v| v as u16),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_regulatory_config(inp: &tlv::TlvItemValue) -> anyhow::Result<RegulatoryLocationType> {
if let tlv::TlvItemValue::Int(v) = inp {
RegulatoryLocationType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_location_capability(inp: &tlv::TlvItemValue) -> anyhow::Result<RegulatoryLocationType> {
if let tlv::TlvItemValue::Int(v) = inp {
RegulatoryLocationType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_supports_concurrent_connection(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_tc_accepted_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_tc_min_required_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_tc_acknowledgements(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_tc_acknowledgements_required(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_tc_update_deadline(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as u32))
} else {
Ok(None)
}
}
pub fn decode_recovery_identifier(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
if let tlv::TlvItemValue::OctetString(v) = inp {
Ok(v.clone())
} else {
Err(anyhow::anyhow!("Expected OctetString"))
}
}
pub fn decode_network_recovery_reason(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<NetworkRecoveryReason>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(NetworkRecoveryReason::from_u8(*v as u8))
} else {
Ok(None)
}
}
pub fn decode_is_commissioning_without_power(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
if let tlv::TlvItemValue::Bool(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected Bool"))
}
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x0030 {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0030, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_breadcrumb(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_basic_commissioning_info(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_regulatory_config(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_location_capability(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_supports_concurrent_connection(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_tc_accepted_version(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_tc_min_required_version(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0007 => {
match decode_tc_acknowledgements(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0008 => {
match decode_tc_acknowledgements_required(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0009 => {
match decode_tc_update_deadline(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000A => {
match decode_recovery_identifier(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000B => {
match decode_network_recovery_reason(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000C => {
match decode_is_commissioning_without_power(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, "Breadcrumb"),
(0x0001, "BasicCommissioningInfo"),
(0x0002, "RegulatoryConfig"),
(0x0003, "LocationCapability"),
(0x0004, "SupportsConcurrentConnection"),
(0x0005, "TCAcceptedVersion"),
(0x0006, "TCMinRequiredVersion"),
(0x0007, "TCAcknowledgements"),
(0x0008, "TCAcknowledgementsRequired"),
(0x0009, "TCUpdateDeadline"),
(0x000A, "RecoveryIdentifier"),
(0x000B, "NetworkRecoveryReason"),
(0x000C, "IsCommissioningWithoutPower"),
]
}
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "ArmFailSafe"),
(0x02, "SetRegulatoryConfig"),
(0x04, "CommissioningComplete"),
(0x06, "SetTCAcknowledgements"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("ArmFailSafe"),
0x02 => Some("SetRegulatoryConfig"),
0x04 => Some("CommissioningComplete"),
0x06 => Some("SetTCAcknowledgements"),
_ => 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: "expiry_length_seconds", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
]),
0x02 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "new_regulatory_config", kind: crate::clusters::codec::FieldKind::Enum { name: "RegulatoryLocationType", variants: &[(0, "Indoor"), (1, "Outdoor"), (2, "Indooroutdoor")] }, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "country_code", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
]),
0x04 => Some(vec![]),
0x06 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "tc_version", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "tc_user_response", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
_ => None,
}
}
pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
match cmd_id {
0x00 => {
let expiry_length_seconds = crate::clusters::codec::json_util::get_u16(args, "expiry_length_seconds")?;
let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
encode_arm_fail_safe(expiry_length_seconds, breadcrumb)
}
0x02 => {
let new_regulatory_config = {
let n = crate::clusters::codec::json_util::get_u64(args, "new_regulatory_config")?;
RegulatoryLocationType::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid RegulatoryLocationType: {}", n))?
};
let country_code = crate::clusters::codec::json_util::get_string(args, "country_code")?;
let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
encode_set_regulatory_config(new_regulatory_config, country_code, breadcrumb)
}
0x04 => Ok(vec![]),
0x06 => {
let tc_version = crate::clusters::codec::json_util::get_u16(args, "tc_version")?;
let tc_user_response = crate::clusters::codec::json_util::get_u8(args, "tc_user_response")?;
encode_set_tc_acknowledgements(tc_version, tc_user_response)
}
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
#[derive(Debug, serde::Serialize)]
pub struct ArmFailSafeResponse {
pub error_code: Option<CommissioningError>,
pub debug_text: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct SetRegulatoryConfigResponse {
pub error_code: Option<CommissioningError>,
pub debug_text: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct CommissioningCompleteResponse {
pub error_code: Option<CommissioningError>,
pub debug_text: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct SetTCAcknowledgementsResponse {
pub error_code: Option<CommissioningError>,
}
pub fn decode_arm_fail_safe_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ArmFailSafeResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(ArmFailSafeResponse {
error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
debug_text: item.get_string_owned(&[1]),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_set_regulatory_config_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SetRegulatoryConfigResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(SetRegulatoryConfigResponse {
error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
debug_text: item.get_string_owned(&[1]),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_commissioning_complete_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CommissioningCompleteResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(CommissioningCompleteResponse {
error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
debug_text: item.get_string_owned(&[1]),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_set_tc_acknowledgements_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SetTCAcknowledgementsResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(SetTCAcknowledgementsResponse {
error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub async fn arm_fail_safe(conn: &crate::controller::Connection, endpoint: u16, expiry_length_seconds: u16, breadcrumb: u64) -> anyhow::Result<ArmFailSafeResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_CMD_ID_ARMFAILSAFE, &encode_arm_fail_safe(expiry_length_seconds, breadcrumb)?).await?;
decode_arm_fail_safe_response(&tlv)
}
pub async fn set_regulatory_config(conn: &crate::controller::Connection, endpoint: u16, new_regulatory_config: RegulatoryLocationType, country_code: String, breadcrumb: u64) -> anyhow::Result<SetRegulatoryConfigResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_CMD_ID_SETREGULATORYCONFIG, &encode_set_regulatory_config(new_regulatory_config, country_code, breadcrumb)?).await?;
decode_set_regulatory_config_response(&tlv)
}
pub async fn commissioning_complete(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<CommissioningCompleteResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_CMD_ID_COMMISSIONINGCOMPLETE, &[]).await?;
decode_commissioning_complete_response(&tlv)
}
pub async fn set_tc_acknowledgements(conn: &crate::controller::Connection, endpoint: u16, tc_version: u16, tc_user_response: u8) -> anyhow::Result<SetTCAcknowledgementsResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_CMD_ID_SETTCACKNOWLEDGEMENTS, &encode_set_tc_acknowledgements(tc_version, tc_user_response)?).await?;
decode_set_tc_acknowledgements_response(&tlv)
}
pub async fn read_breadcrumb(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u64> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BREADCRUMB).await?;
decode_breadcrumb(&tlv)
}
pub async fn read_basic_commissioning_info(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<BasicCommissioningInfo> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BASICCOMMISSIONINGINFO).await?;
decode_basic_commissioning_info(&tlv)
}
pub async fn read_regulatory_config(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RegulatoryLocationType> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_REGULATORYCONFIG).await?;
decode_regulatory_config(&tlv)
}
pub async fn read_location_capability(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RegulatoryLocationType> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_LOCATIONCAPABILITY).await?;
decode_location_capability(&tlv)
}
pub async fn read_supports_concurrent_connection(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_SUPPORTSCONCURRENTCONNECTION).await?;
decode_supports_concurrent_connection(&tlv)
}
pub async fn read_tc_accepted_version(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCACCEPTEDVERSION).await?;
decode_tc_accepted_version(&tlv)
}
pub async fn read_tc_min_required_version(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCMINREQUIREDVERSION).await?;
decode_tc_min_required_version(&tlv)
}
pub async fn read_tc_acknowledgements(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCACKNOWLEDGEMENTS).await?;
decode_tc_acknowledgements(&tlv)
}
pub async fn read_tc_acknowledgements_required(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCACKNOWLEDGEMENTSREQUIRED).await?;
decode_tc_acknowledgements_required(&tlv)
}
pub async fn read_tc_update_deadline(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCUPDATEDEADLINE).await?;
decode_tc_update_deadline(&tlv)
}
pub async fn read_recovery_identifier(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<u8>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_RECOVERYIDENTIFIER).await?;
decode_recovery_identifier(&tlv)
}
pub async fn read_network_recovery_reason(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<NetworkRecoveryReason>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_NETWORKRECOVERYREASON).await?;
decode_network_recovery_reason(&tlv)
}
pub async fn read_is_commissioning_without_power(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_ISCOMMISSIONINGWITHOUTPOWER).await?;
decode_is_commissioning_without_power(&tlv)
}