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 ClientType {
18 Permanent = 0,
20 Ephemeral = 1,
22}
23
24impl ClientType {
25 pub fn from_u8(value: u8) -> Option<Self> {
27 match value {
28 0 => Some(ClientType::Permanent),
29 1 => Some(ClientType::Ephemeral),
30 _ => None,
31 }
32 }
33
34 pub fn to_u8(self) -> u8 {
36 self as u8
37 }
38}
39
40impl From<ClientType> for u8 {
41 fn from(val: ClientType) -> Self {
42 val as u8
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
47#[repr(u8)]
48pub enum OperatingMode {
49 Sit = 0,
51 Lit = 1,
53}
54
55impl OperatingMode {
56 pub fn from_u8(value: u8) -> Option<Self> {
58 match value {
59 0 => Some(OperatingMode::Sit),
60 1 => Some(OperatingMode::Lit),
61 _ => None,
62 }
63 }
64
65 pub fn to_u8(self) -> u8 {
67 self as u8
68 }
69}
70
71impl From<OperatingMode> for u8 {
72 fn from(val: OperatingMode) -> Self {
73 val as u8
74 }
75}
76
77pub type UserActiveModeTrigger = u32;
81
82pub mod useractivemodetrigger {
84 pub const POWER_CYCLE: u32 = 0x01;
86 pub const SETTINGS_MENU: u32 = 0x02;
88 pub const CUSTOM_INSTRUCTION: u32 = 0x04;
90 pub const DEVICE_MANUAL: u32 = 0x08;
92 pub const ACTUATE_SENSOR: u32 = 0x10;
94 pub const ACTUATE_SENSOR_SECONDS: u32 = 0x20;
96 pub const ACTUATE_SENSOR_TIMES: u32 = 0x40;
98 pub const ACTUATE_SENSOR_LIGHTS_BLINK: u32 = 0x80;
100 pub const RESET_BUTTON: u32 = 0x100;
102 pub const RESET_BUTTON_LIGHTS_BLINK: u32 = 0x200;
104 pub const RESET_BUTTON_SECONDS: u32 = 0x400;
106 pub const RESET_BUTTON_TIMES: u32 = 0x800;
108 pub const SETUP_BUTTON: u32 = 0x1000;
110 pub const SETUP_BUTTON_SECONDS: u32 = 0x2000;
112 pub const SETUP_BUTTON_LIGHTS_BLINK: u32 = 0x4000;
114 pub const SETUP_BUTTON_TIMES: u32 = 0x8000;
116 pub const APP_DEFINED_BUTTON: u32 = 0x10000;
118}
119
120#[derive(Debug, serde::Serialize)]
123pub struct MonitoringRegistration {
124 pub check_in_node_id: Option<u64>,
125 pub monitored_subject: Option<u64>,
126 pub key: Option<u8>,
127 pub client_type: Option<ClientType>,
128}
129
130pub fn encode_register_client(check_in_node_id: u64, monitored_subject: u64, key: Vec<u8>, verification_key: Vec<u8>, client_type: ClientType) -> anyhow::Result<Vec<u8>> {
134 let tlv = tlv::TlvItemEnc {
135 tag: 0,
136 value: tlv::TlvItemValueEnc::StructInvisible(vec![
137 (0, tlv::TlvItemValueEnc::UInt64(check_in_node_id)).into(),
138 (1, tlv::TlvItemValueEnc::UInt64(monitored_subject)).into(),
139 (2, tlv::TlvItemValueEnc::OctetString(key)).into(),
140 (3, tlv::TlvItemValueEnc::OctetString(verification_key)).into(),
141 (4, tlv::TlvItemValueEnc::UInt8(client_type.to_u8())).into(),
142 ]),
143 };
144 Ok(tlv.encode()?)
145}
146
147pub fn encode_unregister_client(check_in_node_id: u64, verification_key: Vec<u8>) -> anyhow::Result<Vec<u8>> {
149 let tlv = tlv::TlvItemEnc {
150 tag: 0,
151 value: tlv::TlvItemValueEnc::StructInvisible(vec![
152 (0, tlv::TlvItemValueEnc::UInt64(check_in_node_id)).into(),
153 (1, tlv::TlvItemValueEnc::OctetString(verification_key)).into(),
154 ]),
155 };
156 Ok(tlv.encode()?)
157}
158
159pub fn encode_stay_active_request(stay_active_duration: u32) -> anyhow::Result<Vec<u8>> {
161 let tlv = tlv::TlvItemEnc {
162 tag: 0,
163 value: tlv::TlvItemValueEnc::StructInvisible(vec![
164 (0, tlv::TlvItemValueEnc::UInt32(stay_active_duration)).into(),
165 ]),
166 };
167 Ok(tlv.encode()?)
168}
169
170pub fn decode_idle_mode_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
174 if let tlv::TlvItemValue::Int(v) = inp {
175 Ok(*v as u32)
176 } else {
177 Err(anyhow::anyhow!("Expected UInt32"))
178 }
179}
180
181pub fn decode_active_mode_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
183 if let tlv::TlvItemValue::Int(v) = inp {
184 Ok(*v as u32)
185 } else {
186 Err(anyhow::anyhow!("Expected UInt32"))
187 }
188}
189
190pub fn decode_active_mode_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
192 if let tlv::TlvItemValue::Int(v) = inp {
193 Ok(*v as u16)
194 } else {
195 Err(anyhow::anyhow!("Expected UInt16"))
196 }
197}
198
199pub fn decode_registered_clients(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<MonitoringRegistration>> {
201 let mut res = Vec::new();
202 if let tlv::TlvItemValue::List(v) = inp {
203 for item in v {
204 res.push(MonitoringRegistration {
205 check_in_node_id: item.get_int(&[1]),
206 monitored_subject: item.get_int(&[2]),
207 key: item.get_int(&[3]).map(|v| v as u8),
208 client_type: item.get_int(&[4]).and_then(|v| ClientType::from_u8(v as u8)),
209 });
210 }
211 }
212 Ok(res)
213}
214
215pub fn decode_icd_counter(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
217 if let tlv::TlvItemValue::Int(v) = inp {
218 Ok(*v as u32)
219 } else {
220 Err(anyhow::anyhow!("Expected UInt32"))
221 }
222}
223
224pub fn decode_clients_supported_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
226 if let tlv::TlvItemValue::Int(v) = inp {
227 Ok(*v as u16)
228 } else {
229 Err(anyhow::anyhow!("Expected UInt16"))
230 }
231}
232
233pub fn decode_user_active_mode_trigger_hint(inp: &tlv::TlvItemValue) -> anyhow::Result<UserActiveModeTrigger> {
235 if let tlv::TlvItemValue::Int(v) = inp {
236 Ok(*v as u32)
237 } else {
238 Err(anyhow::anyhow!("Expected Integer"))
239 }
240}
241
242pub fn decode_user_active_mode_trigger_instruction(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
244 if let tlv::TlvItemValue::String(v) = inp {
245 Ok(v.clone())
246 } else {
247 Err(anyhow::anyhow!("Expected String"))
248 }
249}
250
251pub fn decode_operating_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<OperatingMode> {
253 if let tlv::TlvItemValue::Int(v) = inp {
254 OperatingMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
255 } else {
256 Err(anyhow::anyhow!("Expected Integer"))
257 }
258}
259
260pub fn decode_maximum_check_in_backoff(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
262 if let tlv::TlvItemValue::Int(v) = inp {
263 Ok(*v as u32)
264 } else {
265 Err(anyhow::anyhow!("Expected UInt32"))
266 }
267}
268
269
270pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
282 if cluster_id != 0x0046 {
284 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0046, got {}\"}}", cluster_id);
285 }
286
287 match attribute_id {
288 0x0000 => {
289 match decode_idle_mode_duration(tlv_value) {
290 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
291 Err(e) => format!("{{\"error\": \"{}\"}}", e),
292 }
293 }
294 0x0001 => {
295 match decode_active_mode_duration(tlv_value) {
296 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
297 Err(e) => format!("{{\"error\": \"{}\"}}", e),
298 }
299 }
300 0x0002 => {
301 match decode_active_mode_threshold(tlv_value) {
302 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
303 Err(e) => format!("{{\"error\": \"{}\"}}", e),
304 }
305 }
306 0x0003 => {
307 match decode_registered_clients(tlv_value) {
308 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
309 Err(e) => format!("{{\"error\": \"{}\"}}", e),
310 }
311 }
312 0x0004 => {
313 match decode_icd_counter(tlv_value) {
314 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
315 Err(e) => format!("{{\"error\": \"{}\"}}", e),
316 }
317 }
318 0x0005 => {
319 match decode_clients_supported_per_fabric(tlv_value) {
320 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
321 Err(e) => format!("{{\"error\": \"{}\"}}", e),
322 }
323 }
324 0x0006 => {
325 match decode_user_active_mode_trigger_hint(tlv_value) {
326 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
327 Err(e) => format!("{{\"error\": \"{}\"}}", e),
328 }
329 }
330 0x0007 => {
331 match decode_user_active_mode_trigger_instruction(tlv_value) {
332 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
333 Err(e) => format!("{{\"error\": \"{}\"}}", e),
334 }
335 }
336 0x0008 => {
337 match decode_operating_mode(tlv_value) {
338 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
339 Err(e) => format!("{{\"error\": \"{}\"}}", e),
340 }
341 }
342 0x0009 => {
343 match decode_maximum_check_in_backoff(tlv_value) {
344 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
345 Err(e) => format!("{{\"error\": \"{}\"}}", e),
346 }
347 }
348 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
349 }
350}
351
352pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
357 vec![
358 (0x0000, "IdleModeDuration"),
359 (0x0001, "ActiveModeDuration"),
360 (0x0002, "ActiveModeThreshold"),
361 (0x0003, "RegisteredClients"),
362 (0x0004, "ICDCounter"),
363 (0x0005, "ClientsSupportedPerFabric"),
364 (0x0006, "UserActiveModeTriggerHint"),
365 (0x0007, "UserActiveModeTriggerInstruction"),
366 (0x0008, "OperatingMode"),
367 (0x0009, "MaximumCheckInBackoff"),
368 ]
369}
370
371pub fn get_command_list() -> Vec<(u32, &'static str)> {
374 vec![
375 (0x00, "RegisterClient"),
376 (0x02, "UnregisterClient"),
377 (0x03, "StayActiveRequest"),
378 ]
379}
380
381pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
382 match cmd_id {
383 0x00 => Some("RegisterClient"),
384 0x02 => Some("UnregisterClient"),
385 0x03 => Some("StayActiveRequest"),
386 _ => None,
387 }
388}
389
390pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
391 match cmd_id {
392 0x00 => Some(vec![
393 crate::clusters::codec::CommandField { tag: 0, name: "check_in_node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
394 crate::clusters::codec::CommandField { tag: 1, name: "monitored_subject", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
395 crate::clusters::codec::CommandField { tag: 2, name: "key", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
396 crate::clusters::codec::CommandField { tag: 3, name: "verification_key", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
397 crate::clusters::codec::CommandField { tag: 4, name: "client_type", kind: crate::clusters::codec::FieldKind::Enum { name: "ClientType", variants: &[(0, "Permanent"), (1, "Ephemeral")] }, optional: false, nullable: false },
398 ]),
399 0x02 => Some(vec![
400 crate::clusters::codec::CommandField { tag: 0, name: "check_in_node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
401 crate::clusters::codec::CommandField { tag: 1, name: "verification_key", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
402 ]),
403 0x03 => Some(vec![
404 crate::clusters::codec::CommandField { tag: 0, name: "stay_active_duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
405 ]),
406 _ => None,
407 }
408}
409
410pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
411 match cmd_id {
412 0x00 => {
413 let check_in_node_id = crate::clusters::codec::json_util::get_u64(args, "check_in_node_id")?;
414 let monitored_subject = crate::clusters::codec::json_util::get_u64(args, "monitored_subject")?;
415 let key = crate::clusters::codec::json_util::get_octstr(args, "key")?;
416 let verification_key = crate::clusters::codec::json_util::get_octstr(args, "verification_key")?;
417 let client_type = {
418 let n = crate::clusters::codec::json_util::get_u64(args, "client_type")?;
419 ClientType::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid ClientType: {}", n))?
420 };
421 encode_register_client(check_in_node_id, monitored_subject, key, verification_key, client_type)
422 }
423 0x02 => {
424 let check_in_node_id = crate::clusters::codec::json_util::get_u64(args, "check_in_node_id")?;
425 let verification_key = crate::clusters::codec::json_util::get_octstr(args, "verification_key")?;
426 encode_unregister_client(check_in_node_id, verification_key)
427 }
428 0x03 => {
429 let stay_active_duration = crate::clusters::codec::json_util::get_u32(args, "stay_active_duration")?;
430 encode_stay_active_request(stay_active_duration)
431 }
432 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
433 }
434}
435
436#[derive(Debug, serde::Serialize)]
437pub struct RegisterClientResponse {
438 pub icd_counter: Option<u32>,
439}
440
441#[derive(Debug, serde::Serialize)]
442pub struct StayActiveResponse {
443 pub promised_active_duration: Option<u32>,
444}
445
446pub fn decode_register_client_response(inp: &tlv::TlvItemValue) -> anyhow::Result<RegisterClientResponse> {
450 if let tlv::TlvItemValue::List(_fields) = inp {
451 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
452 Ok(RegisterClientResponse {
453 icd_counter: item.get_int(&[0]).map(|v| v as u32),
454 })
455 } else {
456 Err(anyhow::anyhow!("Expected struct fields"))
457 }
458}
459
460pub fn decode_stay_active_response(inp: &tlv::TlvItemValue) -> anyhow::Result<StayActiveResponse> {
462 if let tlv::TlvItemValue::List(_fields) = inp {
463 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
464 Ok(StayActiveResponse {
465 promised_active_duration: item.get_int(&[0]).map(|v| v as u32),
466 })
467 } else {
468 Err(anyhow::anyhow!("Expected struct fields"))
469 }
470}
471
472pub async fn register_client(conn: &crate::controller::Connection, endpoint: u16, check_in_node_id: u64, monitored_subject: u64, key: Vec<u8>, verification_key: Vec<u8>, client_type: ClientType) -> anyhow::Result<RegisterClientResponse> {
476 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_CMD_ID_REGISTERCLIENT, &encode_register_client(check_in_node_id, monitored_subject, key, verification_key, client_type)?).await?;
477 decode_register_client_response(&tlv)
478}
479
480pub async fn unregister_client(conn: &crate::controller::Connection, endpoint: u16, check_in_node_id: u64, verification_key: Vec<u8>) -> anyhow::Result<()> {
482 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_CMD_ID_UNREGISTERCLIENT, &encode_unregister_client(check_in_node_id, verification_key)?).await?;
483 Ok(())
484}
485
486pub async fn stay_active_request(conn: &crate::controller::Connection, endpoint: u16, stay_active_duration: u32) -> anyhow::Result<StayActiveResponse> {
488 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_CMD_ID_STAYACTIVEREQUEST, &encode_stay_active_request(stay_active_duration)?).await?;
489 decode_stay_active_response(&tlv)
490}
491
492pub async fn read_idle_mode_duration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
494 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_IDLEMODEDURATION).await?;
495 decode_idle_mode_duration(&tlv)
496}
497
498pub async fn read_active_mode_duration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
500 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_ACTIVEMODEDURATION).await?;
501 decode_active_mode_duration(&tlv)
502}
503
504pub async fn read_active_mode_threshold(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
506 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_ACTIVEMODETHRESHOLD).await?;
507 decode_active_mode_threshold(&tlv)
508}
509
510pub async fn read_registered_clients(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<MonitoringRegistration>> {
512 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_REGISTEREDCLIENTS).await?;
513 decode_registered_clients(&tlv)
514}
515
516pub async fn read_icd_counter(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
518 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_ICDCOUNTER).await?;
519 decode_icd_counter(&tlv)
520}
521
522pub async fn read_clients_supported_per_fabric(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
524 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_CLIENTSSUPPORTEDPERFABRIC).await?;
525 decode_clients_supported_per_fabric(&tlv)
526}
527
528pub async fn read_user_active_mode_trigger_hint(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<UserActiveModeTrigger> {
530 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_USERACTIVEMODETRIGGERHINT).await?;
531 decode_user_active_mode_trigger_hint(&tlv)
532}
533
534pub async fn read_user_active_mode_trigger_instruction(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<String> {
536 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_USERACTIVEMODETRIGGERINSTRUCTION).await?;
537 decode_user_active_mode_trigger_instruction(&tlv)
538}
539
540pub async fn read_operating_mode(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<OperatingMode> {
542 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_OPERATINGMODE).await?;
543 decode_operating_mode(&tlv)
544}
545
546pub async fn read_maximum_check_in_backoff(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
548 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ICD_MANAGEMENT, crate::clusters::defs::CLUSTER_ICD_MANAGEMENT_ATTR_ID_MAXIMUMCHECKINBACKOFF).await?;
549 decode_maximum_check_in_backoff(&tlv)
550}
551