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 ClosureUnit {
16 Millimeter = 0,
18 Degree = 1,
20}
21
22impl ClosureUnit {
23 pub fn from_u8(value: u8) -> Option<Self> {
25 match value {
26 0 => Some(ClosureUnit::Millimeter),
27 1 => Some(ClosureUnit::Degree),
28 _ => None,
29 }
30 }
31
32 pub fn to_u8(self) -> u8 {
34 self as u8
35 }
36}
37
38impl From<ClosureUnit> for u8 {
39 fn from(val: ClosureUnit) -> Self {
40 val as u8
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45#[repr(u8)]
46pub enum ModulationType {
47 Slatsorientation = 0,
49 Slatsopenwork = 1,
51 Stripesalignment = 2,
53 Opacity = 3,
55 Ventilation = 4,
57}
58
59impl ModulationType {
60 pub fn from_u8(value: u8) -> Option<Self> {
62 match value {
63 0 => Some(ModulationType::Slatsorientation),
64 1 => Some(ModulationType::Slatsopenwork),
65 2 => Some(ModulationType::Stripesalignment),
66 3 => Some(ModulationType::Opacity),
67 4 => Some(ModulationType::Ventilation),
68 _ => None,
69 }
70 }
71
72 pub fn to_u8(self) -> u8 {
74 self as u8
75 }
76}
77
78impl From<ModulationType> for u8 {
79 fn from(val: ModulationType) -> Self {
80 val as u8
81 }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
85#[repr(u8)]
86pub enum Overflow {
87 Nooverflow = 0,
89 Inside = 1,
91 Outside = 2,
93 Topinside = 3,
95 Topoutside = 4,
97 Bottominside = 5,
99 Bottomoutside = 6,
101 Leftinside = 7,
103 Leftoutside = 8,
105 Rightinside = 9,
107 Rightoutside = 10,
109}
110
111impl Overflow {
112 pub fn from_u8(value: u8) -> Option<Self> {
114 match value {
115 0 => Some(Overflow::Nooverflow),
116 1 => Some(Overflow::Inside),
117 2 => Some(Overflow::Outside),
118 3 => Some(Overflow::Topinside),
119 4 => Some(Overflow::Topoutside),
120 5 => Some(Overflow::Bottominside),
121 6 => Some(Overflow::Bottomoutside),
122 7 => Some(Overflow::Leftinside),
123 8 => Some(Overflow::Leftoutside),
124 9 => Some(Overflow::Rightinside),
125 10 => Some(Overflow::Rightoutside),
126 _ => None,
127 }
128 }
129
130 pub fn to_u8(self) -> u8 {
132 self as u8
133 }
134}
135
136impl From<Overflow> for u8 {
137 fn from(val: Overflow) -> Self {
138 val as u8
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
143#[repr(u8)]
144pub enum RotationAxis {
145 Left = 0,
147 Centeredvertical = 1,
149 Leftandright = 2,
151 Right = 3,
153 Top = 4,
155 Centeredhorizontal = 5,
157 Topandbottom = 6,
159 Bottom = 7,
161 Leftbarrier = 8,
163 Leftandrightbarriers = 9,
165 Rightbarrier = 10,
167}
168
169impl RotationAxis {
170 pub fn from_u8(value: u8) -> Option<Self> {
172 match value {
173 0 => Some(RotationAxis::Left),
174 1 => Some(RotationAxis::Centeredvertical),
175 2 => Some(RotationAxis::Leftandright),
176 3 => Some(RotationAxis::Right),
177 4 => Some(RotationAxis::Top),
178 5 => Some(RotationAxis::Centeredhorizontal),
179 6 => Some(RotationAxis::Topandbottom),
180 7 => Some(RotationAxis::Bottom),
181 8 => Some(RotationAxis::Leftbarrier),
182 9 => Some(RotationAxis::Leftandrightbarriers),
183 10 => Some(RotationAxis::Rightbarrier),
184 _ => None,
185 }
186 }
187
188 pub fn to_u8(self) -> u8 {
190 self as u8
191 }
192}
193
194impl From<RotationAxis> for u8 {
195 fn from(val: RotationAxis) -> Self {
196 val as u8
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
201#[repr(u8)]
202pub enum StepDirection {
203 Decrease = 0,
205 Increase = 1,
207}
208
209impl StepDirection {
210 pub fn from_u8(value: u8) -> Option<Self> {
212 match value {
213 0 => Some(StepDirection::Decrease),
214 1 => Some(StepDirection::Increase),
215 _ => None,
216 }
217 }
218
219 pub fn to_u8(self) -> u8 {
221 self as u8
222 }
223}
224
225impl From<StepDirection> for u8 {
226 fn from(val: StepDirection) -> Self {
227 val as u8
228 }
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
232#[repr(u8)]
233pub enum TranslationDirection {
234 Downward = 0,
236 Upward = 1,
238 Verticalmask = 2,
240 Verticalsymmetry = 3,
242 Leftward = 4,
244 Rightward = 5,
246 Horizontalmask = 6,
248 Horizontalsymmetry = 7,
250 Forward = 8,
252 Backward = 9,
254 Depthmask = 10,
256 Depthsymmetry = 11,
258}
259
260impl TranslationDirection {
261 pub fn from_u8(value: u8) -> Option<Self> {
263 match value {
264 0 => Some(TranslationDirection::Downward),
265 1 => Some(TranslationDirection::Upward),
266 2 => Some(TranslationDirection::Verticalmask),
267 3 => Some(TranslationDirection::Verticalsymmetry),
268 4 => Some(TranslationDirection::Leftward),
269 5 => Some(TranslationDirection::Rightward),
270 6 => Some(TranslationDirection::Horizontalmask),
271 7 => Some(TranslationDirection::Horizontalsymmetry),
272 8 => Some(TranslationDirection::Forward),
273 9 => Some(TranslationDirection::Backward),
274 10 => Some(TranslationDirection::Depthmask),
275 11 => Some(TranslationDirection::Depthsymmetry),
276 _ => None,
277 }
278 }
279
280 pub fn to_u8(self) -> u8 {
282 self as u8
283 }
284}
285
286impl From<TranslationDirection> for u8 {
287 fn from(val: TranslationDirection) -> Self {
288 val as u8
289 }
290}
291
292pub type LatchControlModes = u8;
296
297pub mod latchcontrolmodes {
299 pub const REMOTE_LATCHING: u8 = 0x01;
301 pub const REMOTE_UNLATCHING: u8 = 0x02;
303}
304
305#[derive(Debug, serde::Serialize)]
308pub struct DimensionState {
309 pub position: Option<u8>,
310 pub latch: Option<bool>,
311 pub speed: Option<u8>,
312}
313
314#[derive(Debug, serde::Serialize)]
315pub struct RangePercent {
316 pub min: Option<u8>,
317 pub max: Option<u8>,
318}
319
320#[derive(Debug, serde::Serialize)]
321pub struct UnitRange {
322 pub min: Option<i16>,
323 pub max: Option<i16>,
324}
325
326pub fn encode_set_target(position: u8, latch: bool, speed: u8) -> anyhow::Result<Vec<u8>> {
330 let tlv = tlv::TlvItemEnc {
331 tag: 0,
332 value: tlv::TlvItemValueEnc::StructInvisible(vec![
333 (0, tlv::TlvItemValueEnc::UInt8(position)).into(),
334 (1, tlv::TlvItemValueEnc::Bool(latch)).into(),
335 (2, tlv::TlvItemValueEnc::UInt8(speed)).into(),
336 ]),
337 };
338 Ok(tlv.encode()?)
339}
340
341pub fn encode_step(direction: StepDirection, number_of_steps: u16, speed: u8) -> anyhow::Result<Vec<u8>> {
343 let tlv = tlv::TlvItemEnc {
344 tag: 0,
345 value: tlv::TlvItemValueEnc::StructInvisible(vec![
346 (0, tlv::TlvItemValueEnc::UInt8(direction.to_u8())).into(),
347 (1, tlv::TlvItemValueEnc::UInt16(number_of_steps)).into(),
348 (2, tlv::TlvItemValueEnc::UInt8(speed)).into(),
349 ]),
350 };
351 Ok(tlv.encode()?)
352}
353
354pub fn decode_current_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DimensionState>> {
358 if let tlv::TlvItemValue::List(_fields) = inp {
359 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
361 Ok(Some(DimensionState {
362 position: item.get_int(&[0]).map(|v| v as u8),
363 latch: item.get_bool(&[1]),
364 speed: item.get_int(&[2]).map(|v| v as u8),
365 }))
366 } else {
370 Ok(None)
371 }
373}
374
375pub fn decode_target_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DimensionState>> {
377 if let tlv::TlvItemValue::List(_fields) = inp {
378 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
380 Ok(Some(DimensionState {
381 position: item.get_int(&[0]).map(|v| v as u8),
382 latch: item.get_bool(&[1]),
383 speed: item.get_int(&[2]).map(|v| v as u8),
384 }))
385 } else {
389 Ok(None)
390 }
392}
393
394pub fn decode_resolution(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
396 if let tlv::TlvItemValue::Int(v) = inp {
397 Ok(*v as u8)
398 } else {
399 Err(anyhow::anyhow!("Expected UInt8"))
400 }
401}
402
403pub fn decode_step_value(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
405 if let tlv::TlvItemValue::Int(v) = inp {
406 Ok(*v as u8)
407 } else {
408 Err(anyhow::anyhow!("Expected UInt8"))
409 }
410}
411
412pub fn decode_unit(inp: &tlv::TlvItemValue) -> anyhow::Result<ClosureUnit> {
414 if let tlv::TlvItemValue::Int(v) = inp {
415 ClosureUnit::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
416 } else {
417 Err(anyhow::anyhow!("Expected Integer"))
418 }
419}
420
421pub fn decode_unit_range(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<UnitRange>> {
423 if let tlv::TlvItemValue::List(_fields) = inp {
424 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
426 Ok(Some(UnitRange {
427 min: item.get_int(&[0]).map(|v| v as i16),
428 max: item.get_int(&[1]).map(|v| v as i16),
429 }))
430 } else {
434 Ok(None)
435 }
437}
438
439pub fn decode_limit_range(inp: &tlv::TlvItemValue) -> anyhow::Result<RangePercent> {
441 if let tlv::TlvItemValue::List(_fields) = inp {
442 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
444 Ok(RangePercent {
445 min: item.get_int(&[0]).map(|v| v as u8),
446 max: item.get_int(&[1]).map(|v| v as u8),
447 })
448 } else {
449 Err(anyhow::anyhow!("Expected struct fields"))
450 }
451}
452
453pub fn decode_translation_direction(inp: &tlv::TlvItemValue) -> anyhow::Result<TranslationDirection> {
455 if let tlv::TlvItemValue::Int(v) = inp {
456 TranslationDirection::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
457 } else {
458 Err(anyhow::anyhow!("Expected Integer"))
459 }
460}
461
462pub fn decode_rotation_axis(inp: &tlv::TlvItemValue) -> anyhow::Result<RotationAxis> {
464 if let tlv::TlvItemValue::Int(v) = inp {
465 RotationAxis::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
466 } else {
467 Err(anyhow::anyhow!("Expected Integer"))
468 }
469}
470
471pub fn decode_overflow(inp: &tlv::TlvItemValue) -> anyhow::Result<Overflow> {
473 if let tlv::TlvItemValue::Int(v) = inp {
474 Overflow::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
475 } else {
476 Err(anyhow::anyhow!("Expected Integer"))
477 }
478}
479
480pub fn decode_modulation_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ModulationType> {
482 if let tlv::TlvItemValue::Int(v) = inp {
483 ModulationType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
484 } else {
485 Err(anyhow::anyhow!("Expected Integer"))
486 }
487}
488
489pub fn decode_latch_control_modes(inp: &tlv::TlvItemValue) -> anyhow::Result<LatchControlModes> {
491 if let tlv::TlvItemValue::Int(v) = inp {
492 Ok(*v as u8)
493 } else {
494 Err(anyhow::anyhow!("Expected Integer"))
495 }
496}
497
498
499pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
511 if cluster_id != 0x0105 {
513 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0105, got {}\"}}", cluster_id);
514 }
515
516 match attribute_id {
517 0x0000 => {
518 match decode_current_state(tlv_value) {
519 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
520 Err(e) => format!("{{\"error\": \"{}\"}}", e),
521 }
522 }
523 0x0001 => {
524 match decode_target_state(tlv_value) {
525 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
526 Err(e) => format!("{{\"error\": \"{}\"}}", e),
527 }
528 }
529 0x0002 => {
530 match decode_resolution(tlv_value) {
531 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
532 Err(e) => format!("{{\"error\": \"{}\"}}", e),
533 }
534 }
535 0x0003 => {
536 match decode_step_value(tlv_value) {
537 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
538 Err(e) => format!("{{\"error\": \"{}\"}}", e),
539 }
540 }
541 0x0004 => {
542 match decode_unit(tlv_value) {
543 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
544 Err(e) => format!("{{\"error\": \"{}\"}}", e),
545 }
546 }
547 0x0005 => {
548 match decode_unit_range(tlv_value) {
549 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
550 Err(e) => format!("{{\"error\": \"{}\"}}", e),
551 }
552 }
553 0x0006 => {
554 match decode_limit_range(tlv_value) {
555 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
556 Err(e) => format!("{{\"error\": \"{}\"}}", e),
557 }
558 }
559 0x0007 => {
560 match decode_translation_direction(tlv_value) {
561 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
562 Err(e) => format!("{{\"error\": \"{}\"}}", e),
563 }
564 }
565 0x0008 => {
566 match decode_rotation_axis(tlv_value) {
567 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
568 Err(e) => format!("{{\"error\": \"{}\"}}", e),
569 }
570 }
571 0x0009 => {
572 match decode_overflow(tlv_value) {
573 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
574 Err(e) => format!("{{\"error\": \"{}\"}}", e),
575 }
576 }
577 0x000A => {
578 match decode_modulation_type(tlv_value) {
579 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
580 Err(e) => format!("{{\"error\": \"{}\"}}", e),
581 }
582 }
583 0x000B => {
584 match decode_latch_control_modes(tlv_value) {
585 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
586 Err(e) => format!("{{\"error\": \"{}\"}}", e),
587 }
588 }
589 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
590 }
591}
592
593pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
598 vec![
599 (0x0000, "CurrentState"),
600 (0x0001, "TargetState"),
601 (0x0002, "Resolution"),
602 (0x0003, "StepValue"),
603 (0x0004, "Unit"),
604 (0x0005, "UnitRange"),
605 (0x0006, "LimitRange"),
606 (0x0007, "TranslationDirection"),
607 (0x0008, "RotationAxis"),
608 (0x0009, "Overflow"),
609 (0x000A, "ModulationType"),
610 (0x000B, "LatchControlModes"),
611 ]
612}
613