1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum CommissioningError {
18 Ok = 0,
20 Valueoutsiderange = 1,
22 Invalidauthentication = 2,
24 Nofailsafe = 3,
26 Busywithotheradmin = 4,
28 Requiredtcnotaccepted = 5,
30 Tcacknowledgementsnotreceived = 6,
32 Tcminversionnotmet = 7,
34}
35
36impl CommissioningError {
37 pub fn from_u8(value: u8) -> Option<Self> {
39 match value {
40 0 => Some(CommissioningError::Ok),
41 1 => Some(CommissioningError::Valueoutsiderange),
42 2 => Some(CommissioningError::Invalidauthentication),
43 3 => Some(CommissioningError::Nofailsafe),
44 4 => Some(CommissioningError::Busywithotheradmin),
45 5 => Some(CommissioningError::Requiredtcnotaccepted),
46 6 => Some(CommissioningError::Tcacknowledgementsnotreceived),
47 7 => Some(CommissioningError::Tcminversionnotmet),
48 _ => None,
49 }
50 }
51
52 pub fn to_u8(self) -> u8 {
54 self as u8
55 }
56}
57
58impl From<CommissioningError> for u8 {
59 fn from(val: CommissioningError) -> Self {
60 val as u8
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
65#[repr(u8)]
66pub enum NetworkRecoveryReason {
67 Unspecified = 0,
69 Auth = 1,
71 Visibility = 2,
73}
74
75impl NetworkRecoveryReason {
76 pub fn from_u8(value: u8) -> Option<Self> {
78 match value {
79 0 => Some(NetworkRecoveryReason::Unspecified),
80 1 => Some(NetworkRecoveryReason::Auth),
81 2 => Some(NetworkRecoveryReason::Visibility),
82 _ => None,
83 }
84 }
85
86 pub fn to_u8(self) -> u8 {
88 self as u8
89 }
90}
91
92impl From<NetworkRecoveryReason> for u8 {
93 fn from(val: NetworkRecoveryReason) -> Self {
94 val as u8
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
99#[repr(u8)]
100pub enum RegulatoryLocationType {
101 Indoor = 0,
103 Outdoor = 1,
105 Indooroutdoor = 2,
107}
108
109impl RegulatoryLocationType {
110 pub fn from_u8(value: u8) -> Option<Self> {
112 match value {
113 0 => Some(RegulatoryLocationType::Indoor),
114 1 => Some(RegulatoryLocationType::Outdoor),
115 2 => Some(RegulatoryLocationType::Indooroutdoor),
116 _ => None,
117 }
118 }
119
120 pub fn to_u8(self) -> u8 {
122 self as u8
123 }
124}
125
126impl From<RegulatoryLocationType> for u8 {
127 fn from(val: RegulatoryLocationType) -> Self {
128 val as u8
129 }
130}
131
132#[derive(Debug, serde::Serialize)]
135pub struct BasicCommissioningInfo {
136 pub fail_safe_expiry_length_seconds: Option<u16>,
137 pub max_cumulative_failsafe_seconds: Option<u16>,
138}
139
140pub fn encode_arm_fail_safe(expiry_length_seconds: u16, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
144 let tlv = tlv::TlvItemEnc {
145 tag: 0,
146 value: tlv::TlvItemValueEnc::StructInvisible(vec![
147 (0, tlv::TlvItemValueEnc::UInt16(expiry_length_seconds)).into(),
148 (1, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
149 ]),
150 };
151 Ok(tlv.encode()?)
152}
153
154pub fn encode_set_regulatory_config(new_regulatory_config: RegulatoryLocationType, country_code: String, breadcrumb: u64) -> anyhow::Result<Vec<u8>> {
156 let tlv = tlv::TlvItemEnc {
157 tag: 0,
158 value: tlv::TlvItemValueEnc::StructInvisible(vec![
159 (0, tlv::TlvItemValueEnc::UInt8(new_regulatory_config.to_u8())).into(),
160 (1, tlv::TlvItemValueEnc::String(country_code)).into(),
161 (2, tlv::TlvItemValueEnc::UInt64(breadcrumb)).into(),
162 ]),
163 };
164 Ok(tlv.encode()?)
165}
166
167pub fn encode_set_tc_acknowledgements(tc_version: u16, tc_user_response: u8) -> anyhow::Result<Vec<u8>> {
169 let tlv = tlv::TlvItemEnc {
170 tag: 0,
171 value: tlv::TlvItemValueEnc::StructInvisible(vec![
172 (0, tlv::TlvItemValueEnc::UInt16(tc_version)).into(),
173 (1, tlv::TlvItemValueEnc::UInt8(tc_user_response)).into(),
174 ]),
175 };
176 Ok(tlv.encode()?)
177}
178
179pub fn decode_breadcrumb(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
183 if let tlv::TlvItemValue::Int(v) = inp {
184 Ok(*v)
185 } else {
186 Err(anyhow::anyhow!("Expected UInt64"))
187 }
188}
189
190pub fn decode_basic_commissioning_info(inp: &tlv::TlvItemValue) -> anyhow::Result<BasicCommissioningInfo> {
192 if let tlv::TlvItemValue::List(_fields) = inp {
193 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
195 Ok(BasicCommissioningInfo {
196 fail_safe_expiry_length_seconds: item.get_int(&[0]).map(|v| v as u16),
197 max_cumulative_failsafe_seconds: item.get_int(&[1]).map(|v| v as u16),
198 })
199 } else {
200 Err(anyhow::anyhow!("Expected struct fields"))
201 }
202}
203
204pub fn decode_regulatory_config(inp: &tlv::TlvItemValue) -> anyhow::Result<RegulatoryLocationType> {
206 if let tlv::TlvItemValue::Int(v) = inp {
207 RegulatoryLocationType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
208 } else {
209 Err(anyhow::anyhow!("Expected Integer"))
210 }
211}
212
213pub fn decode_location_capability(inp: &tlv::TlvItemValue) -> anyhow::Result<RegulatoryLocationType> {
215 if let tlv::TlvItemValue::Int(v) = inp {
216 RegulatoryLocationType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
217 } else {
218 Err(anyhow::anyhow!("Expected Integer"))
219 }
220}
221
222pub fn decode_supports_concurrent_connection(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
224 if let tlv::TlvItemValue::Bool(v) = inp {
225 Ok(*v)
226 } else {
227 Err(anyhow::anyhow!("Expected Bool"))
228 }
229}
230
231pub fn decode_tc_accepted_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
233 if let tlv::TlvItemValue::Int(v) = inp {
234 Ok(*v as u16)
235 } else {
236 Err(anyhow::anyhow!("Expected UInt16"))
237 }
238}
239
240pub fn decode_tc_min_required_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
242 if let tlv::TlvItemValue::Int(v) = inp {
243 Ok(*v as u16)
244 } else {
245 Err(anyhow::anyhow!("Expected UInt16"))
246 }
247}
248
249pub fn decode_tc_acknowledgements(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
251 if let tlv::TlvItemValue::Int(v) = inp {
252 Ok(*v as u8)
253 } else {
254 Err(anyhow::anyhow!("Expected UInt8"))
255 }
256}
257
258pub fn decode_tc_acknowledgements_required(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
260 if let tlv::TlvItemValue::Bool(v) = inp {
261 Ok(*v)
262 } else {
263 Err(anyhow::anyhow!("Expected Bool"))
264 }
265}
266
267pub fn decode_tc_update_deadline(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
269 if let tlv::TlvItemValue::Int(v) = inp {
270 Ok(Some(*v as u32))
271 } else {
272 Ok(None)
273 }
274}
275
276pub fn decode_recovery_identifier(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
278 if let tlv::TlvItemValue::OctetString(v) = inp {
279 Ok(v.clone())
280 } else {
281 Err(anyhow::anyhow!("Expected OctetString"))
282 }
283}
284
285pub fn decode_network_recovery_reason(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<NetworkRecoveryReason>> {
287 if let tlv::TlvItemValue::Int(v) = inp {
288 Ok(NetworkRecoveryReason::from_u8(*v as u8))
289 } else {
290 Ok(None)
291 }
292}
293
294pub fn decode_is_commissioning_without_power(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
296 if let tlv::TlvItemValue::Bool(v) = inp {
297 Ok(*v)
298 } else {
299 Err(anyhow::anyhow!("Expected Bool"))
300 }
301}
302
303
304pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
316 if cluster_id != 0x0030 {
318 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0030, got {}\"}}", cluster_id);
319 }
320
321 match attribute_id {
322 0x0000 => {
323 match decode_breadcrumb(tlv_value) {
324 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
325 Err(e) => format!("{{\"error\": \"{}\"}}", e),
326 }
327 }
328 0x0001 => {
329 match decode_basic_commissioning_info(tlv_value) {
330 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
331 Err(e) => format!("{{\"error\": \"{}\"}}", e),
332 }
333 }
334 0x0002 => {
335 match decode_regulatory_config(tlv_value) {
336 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
337 Err(e) => format!("{{\"error\": \"{}\"}}", e),
338 }
339 }
340 0x0003 => {
341 match decode_location_capability(tlv_value) {
342 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
343 Err(e) => format!("{{\"error\": \"{}\"}}", e),
344 }
345 }
346 0x0004 => {
347 match decode_supports_concurrent_connection(tlv_value) {
348 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
349 Err(e) => format!("{{\"error\": \"{}\"}}", e),
350 }
351 }
352 0x0005 => {
353 match decode_tc_accepted_version(tlv_value) {
354 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
355 Err(e) => format!("{{\"error\": \"{}\"}}", e),
356 }
357 }
358 0x0006 => {
359 match decode_tc_min_required_version(tlv_value) {
360 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
361 Err(e) => format!("{{\"error\": \"{}\"}}", e),
362 }
363 }
364 0x0007 => {
365 match decode_tc_acknowledgements(tlv_value) {
366 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
367 Err(e) => format!("{{\"error\": \"{}\"}}", e),
368 }
369 }
370 0x0008 => {
371 match decode_tc_acknowledgements_required(tlv_value) {
372 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
373 Err(e) => format!("{{\"error\": \"{}\"}}", e),
374 }
375 }
376 0x0009 => {
377 match decode_tc_update_deadline(tlv_value) {
378 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
379 Err(e) => format!("{{\"error\": \"{}\"}}", e),
380 }
381 }
382 0x000A => {
383 match decode_recovery_identifier(tlv_value) {
384 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
385 Err(e) => format!("{{\"error\": \"{}\"}}", e),
386 }
387 }
388 0x000B => {
389 match decode_network_recovery_reason(tlv_value) {
390 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
391 Err(e) => format!("{{\"error\": \"{}\"}}", e),
392 }
393 }
394 0x000C => {
395 match decode_is_commissioning_without_power(tlv_value) {
396 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
397 Err(e) => format!("{{\"error\": \"{}\"}}", e),
398 }
399 }
400 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
401 }
402}
403
404pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
409 vec![
410 (0x0000, "Breadcrumb"),
411 (0x0001, "BasicCommissioningInfo"),
412 (0x0002, "RegulatoryConfig"),
413 (0x0003, "LocationCapability"),
414 (0x0004, "SupportsConcurrentConnection"),
415 (0x0005, "TCAcceptedVersion"),
416 (0x0006, "TCMinRequiredVersion"),
417 (0x0007, "TCAcknowledgements"),
418 (0x0008, "TCAcknowledgementsRequired"),
419 (0x0009, "TCUpdateDeadline"),
420 (0x000A, "RecoveryIdentifier"),
421 (0x000B, "NetworkRecoveryReason"),
422 (0x000C, "IsCommissioningWithoutPower"),
423 ]
424}
425
426pub fn get_command_list() -> Vec<(u32, &'static str)> {
429 vec![
430 (0x00, "ArmFailSafe"),
431 (0x02, "SetRegulatoryConfig"),
432 (0x04, "CommissioningComplete"),
433 (0x06, "SetTCAcknowledgements"),
434 ]
435}
436
437pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
438 match cmd_id {
439 0x00 => Some("ArmFailSafe"),
440 0x02 => Some("SetRegulatoryConfig"),
441 0x04 => Some("CommissioningComplete"),
442 0x06 => Some("SetTCAcknowledgements"),
443 _ => None,
444 }
445}
446
447pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
448 match cmd_id {
449 0x00 => Some(vec![
450 crate::clusters::codec::CommandField { tag: 0, name: "expiry_length_seconds", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
451 crate::clusters::codec::CommandField { tag: 1, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
452 ]),
453 0x02 => Some(vec![
454 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 },
455 crate::clusters::codec::CommandField { tag: 1, name: "country_code", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
456 crate::clusters::codec::CommandField { tag: 2, name: "breadcrumb", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
457 ]),
458 0x04 => Some(vec![]),
459 0x06 => Some(vec![
460 crate::clusters::codec::CommandField { tag: 0, name: "tc_version", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
461 crate::clusters::codec::CommandField { tag: 1, name: "tc_user_response", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
462 ]),
463 _ => None,
464 }
465}
466
467pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
468 match cmd_id {
469 0x00 => {
470 let expiry_length_seconds = crate::clusters::codec::json_util::get_u16(args, "expiry_length_seconds")?;
471 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
472 encode_arm_fail_safe(expiry_length_seconds, breadcrumb)
473 }
474 0x02 => {
475 let new_regulatory_config = {
476 let n = crate::clusters::codec::json_util::get_u64(args, "new_regulatory_config")?;
477 RegulatoryLocationType::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid RegulatoryLocationType: {}", n))?
478 };
479 let country_code = crate::clusters::codec::json_util::get_string(args, "country_code")?;
480 let breadcrumb = crate::clusters::codec::json_util::get_u64(args, "breadcrumb")?;
481 encode_set_regulatory_config(new_regulatory_config, country_code, breadcrumb)
482 }
483 0x04 => Ok(vec![]),
484 0x06 => {
485 let tc_version = crate::clusters::codec::json_util::get_u16(args, "tc_version")?;
486 let tc_user_response = crate::clusters::codec::json_util::get_u8(args, "tc_user_response")?;
487 encode_set_tc_acknowledgements(tc_version, tc_user_response)
488 }
489 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
490 }
491}
492
493#[derive(Debug, serde::Serialize)]
494pub struct ArmFailSafeResponse {
495 pub error_code: Option<CommissioningError>,
496 pub debug_text: Option<String>,
497}
498
499#[derive(Debug, serde::Serialize)]
500pub struct SetRegulatoryConfigResponse {
501 pub error_code: Option<CommissioningError>,
502 pub debug_text: Option<String>,
503}
504
505#[derive(Debug, serde::Serialize)]
506pub struct CommissioningCompleteResponse {
507 pub error_code: Option<CommissioningError>,
508 pub debug_text: Option<String>,
509}
510
511#[derive(Debug, serde::Serialize)]
512pub struct SetTCAcknowledgementsResponse {
513 pub error_code: Option<CommissioningError>,
514}
515
516pub fn decode_arm_fail_safe_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ArmFailSafeResponse> {
520 if let tlv::TlvItemValue::List(_fields) = inp {
521 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
522 Ok(ArmFailSafeResponse {
523 error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
524 debug_text: item.get_string_owned(&[1]),
525 })
526 } else {
527 Err(anyhow::anyhow!("Expected struct fields"))
528 }
529}
530
531pub fn decode_set_regulatory_config_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SetRegulatoryConfigResponse> {
533 if let tlv::TlvItemValue::List(_fields) = inp {
534 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
535 Ok(SetRegulatoryConfigResponse {
536 error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
537 debug_text: item.get_string_owned(&[1]),
538 })
539 } else {
540 Err(anyhow::anyhow!("Expected struct fields"))
541 }
542}
543
544pub fn decode_commissioning_complete_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CommissioningCompleteResponse> {
546 if let tlv::TlvItemValue::List(_fields) = inp {
547 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
548 Ok(CommissioningCompleteResponse {
549 error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
550 debug_text: item.get_string_owned(&[1]),
551 })
552 } else {
553 Err(anyhow::anyhow!("Expected struct fields"))
554 }
555}
556
557pub fn decode_set_tc_acknowledgements_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SetTCAcknowledgementsResponse> {
559 if let tlv::TlvItemValue::List(_fields) = inp {
560 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
561 Ok(SetTCAcknowledgementsResponse {
562 error_code: item.get_int(&[0]).and_then(|v| CommissioningError::from_u8(v as u8)),
563 })
564 } else {
565 Err(anyhow::anyhow!("Expected struct fields"))
566 }
567}
568
569pub async fn arm_fail_safe(conn: &crate::controller::Connection, endpoint: u16, expiry_length_seconds: u16, breadcrumb: u64) -> anyhow::Result<ArmFailSafeResponse> {
573 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?;
574 decode_arm_fail_safe_response(&tlv)
575}
576
577pub async fn set_regulatory_config(conn: &crate::controller::Connection, endpoint: u16, new_regulatory_config: RegulatoryLocationType, country_code: String, breadcrumb: u64) -> anyhow::Result<SetRegulatoryConfigResponse> {
579 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?;
580 decode_set_regulatory_config_response(&tlv)
581}
582
583pub async fn commissioning_complete(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<CommissioningCompleteResponse> {
585 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_CMD_ID_COMMISSIONINGCOMPLETE, &[]).await?;
586 decode_commissioning_complete_response(&tlv)
587}
588
589pub async fn set_tc_acknowledgements(conn: &crate::controller::Connection, endpoint: u16, tc_version: u16, tc_user_response: u8) -> anyhow::Result<SetTCAcknowledgementsResponse> {
591 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?;
592 decode_set_tc_acknowledgements_response(&tlv)
593}
594
595pub async fn read_breadcrumb(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u64> {
597 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BREADCRUMB).await?;
598 decode_breadcrumb(&tlv)
599}
600
601pub async fn read_basic_commissioning_info(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<BasicCommissioningInfo> {
603 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BASICCOMMISSIONINGINFO).await?;
604 decode_basic_commissioning_info(&tlv)
605}
606
607pub async fn read_regulatory_config(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RegulatoryLocationType> {
609 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_REGULATORYCONFIG).await?;
610 decode_regulatory_config(&tlv)
611}
612
613pub async fn read_location_capability(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RegulatoryLocationType> {
615 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_LOCATIONCAPABILITY).await?;
616 decode_location_capability(&tlv)
617}
618
619pub async fn read_supports_concurrent_connection(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
621 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_SUPPORTSCONCURRENTCONNECTION).await?;
622 decode_supports_concurrent_connection(&tlv)
623}
624
625pub async fn read_tc_accepted_version(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
627 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCACCEPTEDVERSION).await?;
628 decode_tc_accepted_version(&tlv)
629}
630
631pub async fn read_tc_min_required_version(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
633 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCMINREQUIREDVERSION).await?;
634 decode_tc_min_required_version(&tlv)
635}
636
637pub async fn read_tc_acknowledgements(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
639 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCACKNOWLEDGEMENTS).await?;
640 decode_tc_acknowledgements(&tlv)
641}
642
643pub async fn read_tc_acknowledgements_required(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
645 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCACKNOWLEDGEMENTSREQUIRED).await?;
646 decode_tc_acknowledgements_required(&tlv)
647}
648
649pub async fn read_tc_update_deadline(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
651 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_TCUPDATEDEADLINE).await?;
652 decode_tc_update_deadline(&tlv)
653}
654
655pub async fn read_recovery_identifier(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<u8>> {
657 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_RECOVERYIDENTIFIER).await?;
658 decode_recovery_identifier(&tlv)
659}
660
661pub async fn read_network_recovery_reason(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<NetworkRecoveryReason>> {
663 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_NETWORKRECOVERYREASON).await?;
664 decode_network_recovery_reason(&tlv)
665}
666
667pub async fn read_is_commissioning_without_power(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
669 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GENERAL_COMMISSIONING, crate::clusters::defs::CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_ISCOMMISSIONINGWITHOUTPOWER).await?;
670 decode_is_commissioning_without_power(&tlv)
671}
672