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 ZoneEventStoppedReason {
18 Actionstopped = 0,
20 Timeout = 1,
22}
23
24impl ZoneEventStoppedReason {
25 pub fn from_u8(value: u8) -> Option<Self> {
27 match value {
28 0 => Some(ZoneEventStoppedReason::Actionstopped),
29 1 => Some(ZoneEventStoppedReason::Timeout),
30 _ => None,
31 }
32 }
33
34 pub fn to_u8(self) -> u8 {
36 self as u8
37 }
38}
39
40impl From<ZoneEventStoppedReason> for u8 {
41 fn from(val: ZoneEventStoppedReason) -> Self {
42 val as u8
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
47#[repr(u8)]
48pub enum ZoneEventTriggeredReason {
49 Motion = 0,
51}
52
53impl ZoneEventTriggeredReason {
54 pub fn from_u8(value: u8) -> Option<Self> {
56 match value {
57 0 => Some(ZoneEventTriggeredReason::Motion),
58 _ => None,
59 }
60 }
61
62 pub fn to_u8(self) -> u8 {
64 self as u8
65 }
66}
67
68impl From<ZoneEventTriggeredReason> for u8 {
69 fn from(val: ZoneEventTriggeredReason) -> Self {
70 val as u8
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
75#[repr(u8)]
76pub enum ZoneSource {
77 Mfg = 0,
79 User = 1,
81}
82
83impl ZoneSource {
84 pub fn from_u8(value: u8) -> Option<Self> {
86 match value {
87 0 => Some(ZoneSource::Mfg),
88 1 => Some(ZoneSource::User),
89 _ => None,
90 }
91 }
92
93 pub fn to_u8(self) -> u8 {
95 self as u8
96 }
97}
98
99impl From<ZoneSource> for u8 {
100 fn from(val: ZoneSource) -> Self {
101 val as u8
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
106#[repr(u8)]
107pub enum ZoneType {
108 Twodcartzone = 0,
110}
111
112impl ZoneType {
113 pub fn from_u8(value: u8) -> Option<Self> {
115 match value {
116 0 => Some(ZoneType::Twodcartzone),
117 _ => None,
118 }
119 }
120
121 pub fn to_u8(self) -> u8 {
123 self as u8
124 }
125}
126
127impl From<ZoneType> for u8 {
128 fn from(val: ZoneType) -> Self {
129 val as u8
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
134#[repr(u8)]
135pub enum ZoneUse {
136 Motion = 0,
138 Privacy = 1,
140 Focus = 2,
142}
143
144impl ZoneUse {
145 pub fn from_u8(value: u8) -> Option<Self> {
147 match value {
148 0 => Some(ZoneUse::Motion),
149 1 => Some(ZoneUse::Privacy),
150 2 => Some(ZoneUse::Focus),
151 _ => None,
152 }
153 }
154
155 pub fn to_u8(self) -> u8 {
157 self as u8
158 }
159}
160
161impl From<ZoneUse> for u8 {
162 fn from(val: ZoneUse) -> Self {
163 val as u8
164 }
165}
166
167#[derive(Debug, serde::Serialize)]
170pub struct TwoDCartesianVertex {
171 pub x: Option<u16>,
172 pub y: Option<u16>,
173}
174
175#[derive(Debug, serde::Serialize)]
176pub struct TwoDCartesianZone {
177 pub name: Option<String>,
178 pub use_: Option<ZoneUse>,
179 pub vertices: Option<Vec<TwoDCartesianVertex>>,
180 pub color: Option<String>,
181}
182
183#[derive(Debug, serde::Serialize)]
184pub struct ZoneInformation {
185 pub zone_id: Option<u8>,
186 pub zone_type: Option<ZoneType>,
187 pub zone_source: Option<ZoneSource>,
188 pub two_d_cartesian_zone: Option<TwoDCartesianZone>,
189}
190
191#[derive(Debug, serde::Serialize)]
192pub struct ZoneTriggerControl {
193 pub zone_id: Option<u8>,
194 pub initial_duration: Option<u32>,
195 pub augmentation_duration: Option<u32>,
196 pub max_duration: Option<u32>,
197 pub blind_duration: Option<u32>,
198 pub sensitivity: Option<u8>,
199}
200
201pub fn encode_create_two_d_cartesian_zone(zone: TwoDCartesianZone) -> anyhow::Result<Vec<u8>> {
205 let mut zone_fields = Vec::new();
207 if let Some(x) = zone.name { zone_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
208 if let Some(x) = zone.use_ { zone_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
209 if let Some(listv) = zone.vertices {
210 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
211 let mut nested_fields = Vec::new();
212 if let Some(x) = inner.x { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
213 if let Some(x) = inner.y { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
214 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
215 }).collect();
216 zone_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
217 }
218 if let Some(x) = zone.color { zone_fields.push((3, tlv::TlvItemValueEnc::String(x.clone())).into()); }
219 let tlv = tlv::TlvItemEnc {
220 tag: 0,
221 value: tlv::TlvItemValueEnc::StructInvisible(vec![
222 (0, tlv::TlvItemValueEnc::StructInvisible(zone_fields)).into(),
223 ]),
224 };
225 Ok(tlv.encode()?)
226}
227
228pub fn encode_update_two_d_cartesian_zone(zone_id: u8, zone: TwoDCartesianZone) -> anyhow::Result<Vec<u8>> {
230 let mut zone_fields = Vec::new();
232 if let Some(x) = zone.name { zone_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
233 if let Some(x) = zone.use_ { zone_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
234 if let Some(listv) = zone.vertices {
235 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
236 let mut nested_fields = Vec::new();
237 if let Some(x) = inner.x { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
238 if let Some(x) = inner.y { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
239 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
240 }).collect();
241 zone_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
242 }
243 if let Some(x) = zone.color { zone_fields.push((3, tlv::TlvItemValueEnc::String(x.clone())).into()); }
244 let tlv = tlv::TlvItemEnc {
245 tag: 0,
246 value: tlv::TlvItemValueEnc::StructInvisible(vec![
247 (0, tlv::TlvItemValueEnc::UInt8(zone_id)).into(),
248 (1, tlv::TlvItemValueEnc::StructInvisible(zone_fields)).into(),
249 ]),
250 };
251 Ok(tlv.encode()?)
252}
253
254pub fn encode_remove_zone(zone_id: u8) -> anyhow::Result<Vec<u8>> {
256 let tlv = tlv::TlvItemEnc {
257 tag: 0,
258 value: tlv::TlvItemValueEnc::StructInvisible(vec![
259 (0, tlv::TlvItemValueEnc::UInt8(zone_id)).into(),
260 ]),
261 };
262 Ok(tlv.encode()?)
263}
264
265pub fn encode_create_or_update_trigger(trigger: ZoneTriggerControl) -> anyhow::Result<Vec<u8>> {
267 let mut trigger_fields = Vec::new();
269 if let Some(x) = trigger.initial_duration { trigger_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
271 if let Some(x) = trigger.augmentation_duration { trigger_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
272 if let Some(x) = trigger.max_duration { trigger_fields.push((3, tlv::TlvItemValueEnc::UInt32(x)).into()); }
273 if let Some(x) = trigger.blind_duration { trigger_fields.push((4, tlv::TlvItemValueEnc::UInt32(x)).into()); }
274 if let Some(x) = trigger.sensitivity { trigger_fields.push((5, tlv::TlvItemValueEnc::UInt8(x)).into()); }
275 let tlv = tlv::TlvItemEnc {
276 tag: 0,
277 value: tlv::TlvItemValueEnc::StructInvisible(vec![
278 (0, tlv::TlvItemValueEnc::StructInvisible(trigger_fields)).into(),
279 ]),
280 };
281 Ok(tlv.encode()?)
282}
283
284pub fn encode_remove_trigger(zone_id: u8) -> anyhow::Result<Vec<u8>> {
286 let tlv = tlv::TlvItemEnc {
287 tag: 0,
288 value: tlv::TlvItemValueEnc::StructInvisible(vec![
289 (0, tlv::TlvItemValueEnc::UInt8(zone_id)).into(),
290 ]),
291 };
292 Ok(tlv.encode()?)
293}
294
295pub fn decode_max_user_defined_zones(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
299 if let tlv::TlvItemValue::Int(v) = inp {
300 Ok(*v as u8)
301 } else {
302 Err(anyhow::anyhow!("Expected UInt8"))
303 }
304}
305
306pub fn decode_max_zones(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
308 if let tlv::TlvItemValue::Int(v) = inp {
309 Ok(*v as u8)
310 } else {
311 Err(anyhow::anyhow!("Expected UInt8"))
312 }
313}
314
315pub fn decode_zones(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ZoneInformation>> {
317 let mut res = Vec::new();
318 if let tlv::TlvItemValue::List(v) = inp {
319 for item in v {
320 res.push(ZoneInformation {
321 zone_id: item.get_int(&[0]).map(|v| v as u8),
322 zone_type: item.get_int(&[1]).and_then(|v| ZoneType::from_u8(v as u8)),
323 zone_source: item.get_int(&[2]).and_then(|v| ZoneSource::from_u8(v as u8)),
324 two_d_cartesian_zone: {
325 if let Some(nested_tlv) = item.get(&[3]) {
326 if let tlv::TlvItemValue::List(_) = nested_tlv {
327 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
328 Some(TwoDCartesianZone {
329 name: nested_item.get_string_owned(&[0]),
330 use_: nested_item.get_int(&[1]).and_then(|v| ZoneUse::from_u8(v as u8)),
331 vertices: {
332 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[2]) {
333 let mut items = Vec::new();
334 for list_item in l {
335 items.push(TwoDCartesianVertex {
336 x: list_item.get_int(&[0]).map(|v| v as u16),
337 y: list_item.get_int(&[1]).map(|v| v as u16),
338 });
339 }
340 Some(items)
341 } else {
342 None
343 }
344 },
345 color: nested_item.get_string_owned(&[3]),
346 })
347 } else {
348 None
349 }
350 } else {
351 None
352 }
353 },
354 });
355 }
356 }
357 Ok(res)
358}
359
360pub fn decode_triggers(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ZoneTriggerControl>> {
362 let mut res = Vec::new();
363 if let tlv::TlvItemValue::List(v) = inp {
364 for item in v {
365 res.push(ZoneTriggerControl {
366 zone_id: item.get_int(&[0]).map(|v| v as u8),
367 initial_duration: item.get_int(&[1]).map(|v| v as u32),
368 augmentation_duration: item.get_int(&[2]).map(|v| v as u32),
369 max_duration: item.get_int(&[3]).map(|v| v as u32),
370 blind_duration: item.get_int(&[4]).map(|v| v as u32),
371 sensitivity: item.get_int(&[5]).map(|v| v as u8),
372 });
373 }
374 }
375 Ok(res)
376}
377
378pub fn decode_sensitivity_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
380 if let tlv::TlvItemValue::Int(v) = inp {
381 Ok(*v as u8)
382 } else {
383 Err(anyhow::anyhow!("Expected UInt8"))
384 }
385}
386
387pub fn decode_sensitivity(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
389 if let tlv::TlvItemValue::Int(v) = inp {
390 Ok(*v as u8)
391 } else {
392 Err(anyhow::anyhow!("Expected UInt8"))
393 }
394}
395
396pub fn decode_two_d_cartesian_max(inp: &tlv::TlvItemValue) -> anyhow::Result<TwoDCartesianVertex> {
398 if let tlv::TlvItemValue::List(_fields) = inp {
399 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
401 Ok(TwoDCartesianVertex {
402 x: item.get_int(&[0]).map(|v| v as u16),
403 y: item.get_int(&[1]).map(|v| v as u16),
404 })
405 } else {
406 Err(anyhow::anyhow!("Expected struct fields"))
407 }
408}
409
410
411pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
423 if cluster_id != 0x0550 {
425 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0550, got {}\"}}", cluster_id);
426 }
427
428 match attribute_id {
429 0x0000 => {
430 match decode_max_user_defined_zones(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 0x0001 => {
436 match decode_max_zones(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 0x0002 => {
442 match decode_zones(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 0x0003 => {
448 match decode_triggers(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 0x0004 => {
454 match decode_sensitivity_max(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 0x0005 => {
460 match decode_sensitivity(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 0x0006 => {
466 match decode_two_d_cartesian_max(tlv_value) {
467 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
468 Err(e) => format!("{{\"error\": \"{}\"}}", e),
469 }
470 }
471 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
472 }
473}
474
475pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
480 vec![
481 (0x0000, "MaxUserDefinedZones"),
482 (0x0001, "MaxZones"),
483 (0x0002, "Zones"),
484 (0x0003, "Triggers"),
485 (0x0004, "SensitivityMax"),
486 (0x0005, "Sensitivity"),
487 (0x0006, "TwoDCartesianMax"),
488 ]
489}
490
491pub fn get_command_list() -> Vec<(u32, &'static str)> {
494 vec![
495 (0x00, "CreateTwoDCartesianZone"),
496 (0x02, "UpdateTwoDCartesianZone"),
497 (0x03, "RemoveZone"),
498 (0x04, "CreateOrUpdateTrigger"),
499 (0x05, "RemoveTrigger"),
500 ]
501}
502
503pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
504 match cmd_id {
505 0x00 => Some("CreateTwoDCartesianZone"),
506 0x02 => Some("UpdateTwoDCartesianZone"),
507 0x03 => Some("RemoveZone"),
508 0x04 => Some("CreateOrUpdateTrigger"),
509 0x05 => Some("RemoveTrigger"),
510 _ => None,
511 }
512}
513
514pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
515 match cmd_id {
516 0x00 => Some(vec![
517 crate::clusters::codec::CommandField { tag: 0, name: "zone", kind: crate::clusters::codec::FieldKind::Struct { name: "TwoDCartesianZoneStruct" }, optional: false, nullable: false },
518 ]),
519 0x02 => Some(vec![
520 crate::clusters::codec::CommandField { tag: 0, name: "zone_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
521 crate::clusters::codec::CommandField { tag: 1, name: "zone", kind: crate::clusters::codec::FieldKind::Struct { name: "TwoDCartesianZoneStruct" }, optional: false, nullable: false },
522 ]),
523 0x03 => Some(vec![
524 crate::clusters::codec::CommandField { tag: 0, name: "zone_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
525 ]),
526 0x04 => Some(vec![
527 crate::clusters::codec::CommandField { tag: 0, name: "trigger", kind: crate::clusters::codec::FieldKind::Struct { name: "ZoneTriggerControlStruct" }, optional: false, nullable: false },
528 ]),
529 0x05 => Some(vec![
530 crate::clusters::codec::CommandField { tag: 0, name: "zone_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
531 ]),
532 _ => None,
533 }
534}
535
536pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
537 match cmd_id {
538 0x00 => Err(anyhow::anyhow!("command \"CreateTwoDCartesianZone\" has complex args: use raw mode")),
539 0x02 => Err(anyhow::anyhow!("command \"UpdateTwoDCartesianZone\" has complex args: use raw mode")),
540 0x03 => {
541 let zone_id = crate::clusters::codec::json_util::get_u8(args, "zone_id")?;
542 encode_remove_zone(zone_id)
543 }
544 0x04 => Err(anyhow::anyhow!("command \"CreateOrUpdateTrigger\" has complex args: use raw mode")),
545 0x05 => {
546 let zone_id = crate::clusters::codec::json_util::get_u8(args, "zone_id")?;
547 encode_remove_trigger(zone_id)
548 }
549 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
550 }
551}
552
553#[derive(Debug, serde::Serialize)]
554pub struct CreateTwoDCartesianZoneResponse {
555 pub zone_id: Option<u8>,
556}
557
558pub fn decode_create_two_d_cartesian_zone_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CreateTwoDCartesianZoneResponse> {
562 if let tlv::TlvItemValue::List(_fields) = inp {
563 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
564 Ok(CreateTwoDCartesianZoneResponse {
565 zone_id: item.get_int(&[0]).map(|v| v as u8),
566 })
567 } else {
568 Err(anyhow::anyhow!("Expected struct fields"))
569 }
570}
571
572pub async fn create_two_d_cartesian_zone(conn: &crate::controller::Connection, endpoint: u16, zone: TwoDCartesianZone) -> anyhow::Result<CreateTwoDCartesianZoneResponse> {
576 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_CMD_ID_CREATETWODCARTESIANZONE, &encode_create_two_d_cartesian_zone(zone)?).await?;
577 decode_create_two_d_cartesian_zone_response(&tlv)
578}
579
580pub async fn update_two_d_cartesian_zone(conn: &crate::controller::Connection, endpoint: u16, zone_id: u8, zone: TwoDCartesianZone) -> anyhow::Result<()> {
582 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_CMD_ID_UPDATETWODCARTESIANZONE, &encode_update_two_d_cartesian_zone(zone_id, zone)?).await?;
583 Ok(())
584}
585
586pub async fn remove_zone(conn: &crate::controller::Connection, endpoint: u16, zone_id: u8) -> anyhow::Result<()> {
588 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_CMD_ID_REMOVEZONE, &encode_remove_zone(zone_id)?).await?;
589 Ok(())
590}
591
592pub async fn create_or_update_trigger(conn: &crate::controller::Connection, endpoint: u16, trigger: ZoneTriggerControl) -> anyhow::Result<()> {
594 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_CMD_ID_CREATEORUPDATETRIGGER, &encode_create_or_update_trigger(trigger)?).await?;
595 Ok(())
596}
597
598pub async fn remove_trigger(conn: &crate::controller::Connection, endpoint: u16, zone_id: u8) -> anyhow::Result<()> {
600 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_CMD_ID_REMOVETRIGGER, &encode_remove_trigger(zone_id)?).await?;
601 Ok(())
602}
603
604pub async fn read_max_user_defined_zones(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
606 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_ATTR_ID_MAXUSERDEFINEDZONES).await?;
607 decode_max_user_defined_zones(&tlv)
608}
609
610pub async fn read_max_zones(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
612 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_ATTR_ID_MAXZONES).await?;
613 decode_max_zones(&tlv)
614}
615
616pub async fn read_zones(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<ZoneInformation>> {
618 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_ATTR_ID_ZONES).await?;
619 decode_zones(&tlv)
620}
621
622pub async fn read_triggers(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<ZoneTriggerControl>> {
624 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_ATTR_ID_TRIGGERS).await?;
625 decode_triggers(&tlv)
626}
627
628pub async fn read_sensitivity_max(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
630 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_ATTR_ID_SENSITIVITYMAX).await?;
631 decode_sensitivity_max(&tlv)
632}
633
634pub async fn read_sensitivity(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
636 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_ATTR_ID_SENSITIVITY).await?;
637 decode_sensitivity(&tlv)
638}
639
640pub async fn read_two_d_cartesian_max(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TwoDCartesianVertex> {
642 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ZONE_MANAGEMENT, crate::clusters::defs::CLUSTER_ZONE_MANAGEMENT_ATTR_ID_TWODCARTESIANMAX).await?;
643 decode_two_d_cartesian_max(&tlv)
644}
645
646#[derive(Debug, serde::Serialize)]
647pub struct ZoneTriggeredEvent {
648 pub zone: Option<u8>,
649 pub reason: Option<ZoneEventTriggeredReason>,
650}
651
652#[derive(Debug, serde::Serialize)]
653pub struct ZoneStoppedEvent {
654 pub zone: Option<u8>,
655 pub reason: Option<ZoneEventStoppedReason>,
656}
657
658pub fn decode_zone_triggered_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ZoneTriggeredEvent> {
662 if let tlv::TlvItemValue::List(_fields) = inp {
663 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
664 Ok(ZoneTriggeredEvent {
665 zone: item.get_int(&[0]).map(|v| v as u8),
666 reason: item.get_int(&[1]).and_then(|v| ZoneEventTriggeredReason::from_u8(v as u8)),
667 })
668 } else {
669 Err(anyhow::anyhow!("Expected struct fields"))
670 }
671}
672
673pub fn decode_zone_stopped_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ZoneStoppedEvent> {
675 if let tlv::TlvItemValue::List(_fields) = inp {
676 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
677 Ok(ZoneStoppedEvent {
678 zone: item.get_int(&[0]).map(|v| v as u8),
679 reason: item.get_int(&[1]).and_then(|v| ZoneEventStoppedReason::from_u8(v as u8)),
680 })
681 } else {
682 Err(anyhow::anyhow!("Expected struct fields"))
683 }
684}
685