1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum ZoneEventStoppedReason {
16 Actionstopped = 0,
18 Timeout = 1,
20}
21
22impl ZoneEventStoppedReason {
23 pub fn from_u8(value: u8) -> Option<Self> {
25 match value {
26 0 => Some(ZoneEventStoppedReason::Actionstopped),
27 1 => Some(ZoneEventStoppedReason::Timeout),
28 _ => None,
29 }
30 }
31
32 pub fn to_u8(self) -> u8 {
34 self as u8
35 }
36}
37
38impl From<ZoneEventStoppedReason> for u8 {
39 fn from(val: ZoneEventStoppedReason) -> Self {
40 val as u8
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45#[repr(u8)]
46pub enum ZoneEventTriggeredReason {
47 Motion = 0,
49}
50
51impl ZoneEventTriggeredReason {
52 pub fn from_u8(value: u8) -> Option<Self> {
54 match value {
55 0 => Some(ZoneEventTriggeredReason::Motion),
56 _ => None,
57 }
58 }
59
60 pub fn to_u8(self) -> u8 {
62 self as u8
63 }
64}
65
66impl From<ZoneEventTriggeredReason> for u8 {
67 fn from(val: ZoneEventTriggeredReason) -> Self {
68 val as u8
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
73#[repr(u8)]
74pub enum ZoneSource {
75 Mfg = 0,
77 User = 1,
79}
80
81impl ZoneSource {
82 pub fn from_u8(value: u8) -> Option<Self> {
84 match value {
85 0 => Some(ZoneSource::Mfg),
86 1 => Some(ZoneSource::User),
87 _ => None,
88 }
89 }
90
91 pub fn to_u8(self) -> u8 {
93 self as u8
94 }
95}
96
97impl From<ZoneSource> for u8 {
98 fn from(val: ZoneSource) -> Self {
99 val as u8
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
104#[repr(u8)]
105pub enum ZoneType {
106 Twodcartzone = 0,
108}
109
110impl ZoneType {
111 pub fn from_u8(value: u8) -> Option<Self> {
113 match value {
114 0 => Some(ZoneType::Twodcartzone),
115 _ => None,
116 }
117 }
118
119 pub fn to_u8(self) -> u8 {
121 self as u8
122 }
123}
124
125impl From<ZoneType> for u8 {
126 fn from(val: ZoneType) -> Self {
127 val as u8
128 }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
132#[repr(u8)]
133pub enum ZoneUse {
134 Motion = 0,
136 Privacy = 1,
138 Focus = 2,
140}
141
142impl ZoneUse {
143 pub fn from_u8(value: u8) -> Option<Self> {
145 match value {
146 0 => Some(ZoneUse::Motion),
147 1 => Some(ZoneUse::Privacy),
148 2 => Some(ZoneUse::Focus),
149 _ => None,
150 }
151 }
152
153 pub fn to_u8(self) -> u8 {
155 self as u8
156 }
157}
158
159impl From<ZoneUse> for u8 {
160 fn from(val: ZoneUse) -> Self {
161 val as u8
162 }
163}
164
165#[derive(Debug, serde::Serialize)]
168pub struct TwoDCartesianVertex {
169 pub x: Option<u16>,
170 pub y: Option<u16>,
171}
172
173#[derive(Debug, serde::Serialize)]
174pub struct TwoDCartesianZone {
175 pub name: Option<String>,
176 pub use_: Option<ZoneUse>,
177 pub vertices: Option<Vec<TwoDCartesianVertex>>,
178 pub color: Option<String>,
179}
180
181#[derive(Debug, serde::Serialize)]
182pub struct ZoneInformation {
183 pub zone_id: Option<u8>,
184 pub zone_type: Option<ZoneType>,
185 pub zone_source: Option<ZoneSource>,
186 pub two_d_cartesian_zone: Option<TwoDCartesianZone>,
187}
188
189#[derive(Debug, serde::Serialize)]
190pub struct ZoneTriggerControl {
191 pub zone_id: Option<u8>,
192 pub initial_duration: Option<u32>,
193 pub augmentation_duration: Option<u32>,
194 pub max_duration: Option<u32>,
195 pub blind_duration: Option<u32>,
196 pub sensitivity: Option<u8>,
197}
198
199pub fn encode_create_two_d_cartesian_zone(zone: TwoDCartesianZone) -> anyhow::Result<Vec<u8>> {
203 let mut zone_fields = Vec::new();
205 if let Some(x) = zone.name { zone_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
206 if let Some(x) = zone.use_ { zone_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
207 if let Some(listv) = zone.vertices {
208 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
209 let mut nested_fields = Vec::new();
210 if let Some(x) = inner.x { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
211 if let Some(x) = inner.y { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
212 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
213 }).collect();
214 zone_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
215 }
216 if let Some(x) = zone.color { zone_fields.push((3, tlv::TlvItemValueEnc::String(x.clone())).into()); }
217 let tlv = tlv::TlvItemEnc {
218 tag: 0,
219 value: tlv::TlvItemValueEnc::StructInvisible(vec![
220 (0, tlv::TlvItemValueEnc::StructInvisible(zone_fields)).into(),
221 ]),
222 };
223 Ok(tlv.encode()?)
224}
225
226pub fn encode_update_two_d_cartesian_zone(zone_id: u8, zone: TwoDCartesianZone) -> anyhow::Result<Vec<u8>> {
228 let mut zone_fields = Vec::new();
230 if let Some(x) = zone.name { zone_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
231 if let Some(x) = zone.use_ { zone_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
232 if let Some(listv) = zone.vertices {
233 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
234 let mut nested_fields = Vec::new();
235 if let Some(x) = inner.x { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
236 if let Some(x) = inner.y { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
237 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
238 }).collect();
239 zone_fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
240 }
241 if let Some(x) = zone.color { zone_fields.push((3, tlv::TlvItemValueEnc::String(x.clone())).into()); }
242 let tlv = tlv::TlvItemEnc {
243 tag: 0,
244 value: tlv::TlvItemValueEnc::StructInvisible(vec![
245 (0, tlv::TlvItemValueEnc::UInt8(zone_id)).into(),
246 (1, tlv::TlvItemValueEnc::StructInvisible(zone_fields)).into(),
247 ]),
248 };
249 Ok(tlv.encode()?)
250}
251
252pub fn encode_remove_zone(zone_id: u8) -> anyhow::Result<Vec<u8>> {
254 let tlv = tlv::TlvItemEnc {
255 tag: 0,
256 value: tlv::TlvItemValueEnc::StructInvisible(vec![
257 (0, tlv::TlvItemValueEnc::UInt8(zone_id)).into(),
258 ]),
259 };
260 Ok(tlv.encode()?)
261}
262
263pub fn encode_create_or_update_trigger(trigger: ZoneTriggerControl) -> anyhow::Result<Vec<u8>> {
265 let mut trigger_fields = Vec::new();
267 if let Some(x) = trigger.initial_duration { trigger_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
269 if let Some(x) = trigger.augmentation_duration { trigger_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
270 if let Some(x) = trigger.max_duration { trigger_fields.push((3, tlv::TlvItemValueEnc::UInt32(x)).into()); }
271 if let Some(x) = trigger.blind_duration { trigger_fields.push((4, tlv::TlvItemValueEnc::UInt32(x)).into()); }
272 if let Some(x) = trigger.sensitivity { trigger_fields.push((5, tlv::TlvItemValueEnc::UInt8(x)).into()); }
273 let tlv = tlv::TlvItemEnc {
274 tag: 0,
275 value: tlv::TlvItemValueEnc::StructInvisible(vec![
276 (0, tlv::TlvItemValueEnc::StructInvisible(trigger_fields)).into(),
277 ]),
278 };
279 Ok(tlv.encode()?)
280}
281
282pub fn encode_remove_trigger(zone_id: u8) -> anyhow::Result<Vec<u8>> {
284 let tlv = tlv::TlvItemEnc {
285 tag: 0,
286 value: tlv::TlvItemValueEnc::StructInvisible(vec![
287 (0, tlv::TlvItemValueEnc::UInt8(zone_id)).into(),
288 ]),
289 };
290 Ok(tlv.encode()?)
291}
292
293pub fn decode_max_user_defined_zones(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
297 if let tlv::TlvItemValue::Int(v) = inp {
298 Ok(*v as u8)
299 } else {
300 Err(anyhow::anyhow!("Expected UInt8"))
301 }
302}
303
304pub fn decode_max_zones(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
306 if let tlv::TlvItemValue::Int(v) = inp {
307 Ok(*v as u8)
308 } else {
309 Err(anyhow::anyhow!("Expected UInt8"))
310 }
311}
312
313pub fn decode_zones(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ZoneInformation>> {
315 let mut res = Vec::new();
316 if let tlv::TlvItemValue::List(v) = inp {
317 for item in v {
318 res.push(ZoneInformation {
319 zone_id: item.get_int(&[0]).map(|v| v as u8),
320 zone_type: item.get_int(&[1]).and_then(|v| ZoneType::from_u8(v as u8)),
321 zone_source: item.get_int(&[2]).and_then(|v| ZoneSource::from_u8(v as u8)),
322 two_d_cartesian_zone: {
323 if let Some(nested_tlv) = item.get(&[3]) {
324 if let tlv::TlvItemValue::List(_) = nested_tlv {
325 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
326 Some(TwoDCartesianZone {
327 name: nested_item.get_string_owned(&[0]),
328 use_: nested_item.get_int(&[1]).and_then(|v| ZoneUse::from_u8(v as u8)),
329 vertices: {
330 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[2]) {
331 let mut items = Vec::new();
332 for list_item in l {
333 items.push(TwoDCartesianVertex {
334 x: list_item.get_int(&[0]).map(|v| v as u16),
335 y: list_item.get_int(&[1]).map(|v| v as u16),
336 });
337 }
338 Some(items)
339 } else {
340 None
341 }
342 },
343 color: nested_item.get_string_owned(&[3]),
344 })
345 } else {
346 None
347 }
348 } else {
349 None
350 }
351 },
352 });
353 }
354 }
355 Ok(res)
356}
357
358pub fn decode_triggers(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ZoneTriggerControl>> {
360 let mut res = Vec::new();
361 if let tlv::TlvItemValue::List(v) = inp {
362 for item in v {
363 res.push(ZoneTriggerControl {
364 zone_id: item.get_int(&[0]).map(|v| v as u8),
365 initial_duration: item.get_int(&[1]).map(|v| v as u32),
366 augmentation_duration: item.get_int(&[2]).map(|v| v as u32),
367 max_duration: item.get_int(&[3]).map(|v| v as u32),
368 blind_duration: item.get_int(&[4]).map(|v| v as u32),
369 sensitivity: item.get_int(&[5]).map(|v| v as u8),
370 });
371 }
372 }
373 Ok(res)
374}
375
376pub fn decode_sensitivity_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
378 if let tlv::TlvItemValue::Int(v) = inp {
379 Ok(*v as u8)
380 } else {
381 Err(anyhow::anyhow!("Expected UInt8"))
382 }
383}
384
385pub fn decode_sensitivity(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
387 if let tlv::TlvItemValue::Int(v) = inp {
388 Ok(*v as u8)
389 } else {
390 Err(anyhow::anyhow!("Expected UInt8"))
391 }
392}
393
394pub fn decode_two_d_cartesian_max(inp: &tlv::TlvItemValue) -> anyhow::Result<TwoDCartesianVertex> {
396 if let tlv::TlvItemValue::List(_fields) = inp {
397 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
399 Ok(TwoDCartesianVertex {
400 x: item.get_int(&[0]).map(|v| v as u16),
401 y: item.get_int(&[1]).map(|v| v as u16),
402 })
403 } else {
404 Err(anyhow::anyhow!("Expected struct fields"))
405 }
406}
407
408
409pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
421 if cluster_id != 0x0550 {
423 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0550, got {}\"}}", cluster_id);
424 }
425
426 match attribute_id {
427 0x0000 => {
428 match decode_max_user_defined_zones(tlv_value) {
429 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
430 Err(e) => format!("{{\"error\": \"{}\"}}", e),
431 }
432 }
433 0x0001 => {
434 match decode_max_zones(tlv_value) {
435 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
436 Err(e) => format!("{{\"error\": \"{}\"}}", e),
437 }
438 }
439 0x0002 => {
440 match decode_zones(tlv_value) {
441 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
442 Err(e) => format!("{{\"error\": \"{}\"}}", e),
443 }
444 }
445 0x0003 => {
446 match decode_triggers(tlv_value) {
447 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
448 Err(e) => format!("{{\"error\": \"{}\"}}", e),
449 }
450 }
451 0x0004 => {
452 match decode_sensitivity_max(tlv_value) {
453 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
454 Err(e) => format!("{{\"error\": \"{}\"}}", e),
455 }
456 }
457 0x0005 => {
458 match decode_sensitivity(tlv_value) {
459 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
460 Err(e) => format!("{{\"error\": \"{}\"}}", e),
461 }
462 }
463 0x0006 => {
464 match decode_two_d_cartesian_max(tlv_value) {
465 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
466 Err(e) => format!("{{\"error\": \"{}\"}}", e),
467 }
468 }
469 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
470 }
471}
472
473pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
478 vec![
479 (0x0000, "MaxUserDefinedZones"),
480 (0x0001, "MaxZones"),
481 (0x0002, "Zones"),
482 (0x0003, "Triggers"),
483 (0x0004, "SensitivityMax"),
484 (0x0005, "Sensitivity"),
485 (0x0006, "TwoDCartesianMax"),
486 ]
487}
488
489#[derive(Debug, serde::Serialize)]
490pub struct CreateTwoDCartesianZoneResponse {
491 pub zone_id: Option<u8>,
492}
493
494pub fn decode_create_two_d_cartesian_zone_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CreateTwoDCartesianZoneResponse> {
498 if let tlv::TlvItemValue::List(_fields) = inp {
499 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
500 Ok(CreateTwoDCartesianZoneResponse {
501 zone_id: item.get_int(&[0]).map(|v| v as u8),
502 })
503 } else {
504 Err(anyhow::anyhow!("Expected struct fields"))
505 }
506}
507
508#[derive(Debug, serde::Serialize)]
509pub struct ZoneTriggeredEvent {
510 pub zone: Option<u8>,
511 pub reason: Option<ZoneEventTriggeredReason>,
512}
513
514#[derive(Debug, serde::Serialize)]
515pub struct ZoneStoppedEvent {
516 pub zone: Option<u8>,
517 pub reason: Option<ZoneEventStoppedReason>,
518}
519
520pub fn decode_zone_triggered_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ZoneTriggeredEvent> {
524 if let tlv::TlvItemValue::List(_fields) = inp {
525 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
526 Ok(ZoneTriggeredEvent {
527 zone: item.get_int(&[0]).map(|v| v as u8),
528 reason: item.get_int(&[1]).and_then(|v| ZoneEventTriggeredReason::from_u8(v as u8)),
529 })
530 } else {
531 Err(anyhow::anyhow!("Expected struct fields"))
532 }
533}
534
535pub fn decode_zone_stopped_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ZoneStoppedEvent> {
537 if let tlv::TlvItemValue::List(_fields) = inp {
538 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
539 Ok(ZoneStoppedEvent {
540 zone: item.get_int(&[0]).map(|v| v as u8),
541 reason: item.get_int(&[1]).and_then(|v| ZoneEventStoppedReason::from_u8(v as u8)),
542 })
543 } else {
544 Err(anyhow::anyhow!("Expected struct fields"))
545 }
546}
547