matc/clusters/codec/
closure_dimension.rs

1//! Matter TLV encoders and decoders for Closure Dimension Cluster
2//! Cluster ID: 0x0105
3//!
4//! This file is automatically generated from ClosureDimension.xml
5
6#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13// Enum definitions
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum ClosureUnit {
18    /// Millimeter used as unit
19    Millimeter = 0,
20    /// Degree used as unit
21    Degree = 1,
22}
23
24impl ClosureUnit {
25    /// Convert from u8 value
26    pub fn from_u8(value: u8) -> Option<Self> {
27        match value {
28            0 => Some(ClosureUnit::Millimeter),
29            1 => Some(ClosureUnit::Degree),
30            _ => None,
31        }
32    }
33
34    /// Convert to u8 value
35    pub fn to_u8(self) -> u8 {
36        self as u8
37    }
38}
39
40impl From<ClosureUnit> for u8 {
41    fn from(val: ClosureUnit) -> Self {
42        val as u8
43    }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
47#[repr(u8)]
48pub enum ModulationType {
49    /// Orientation of the slats
50    Slatsorientation = 0,
51    /// Aperture of the slats
52    Slatsopenwork = 1,
53    /// Alignment of blind stripes (Zebra)
54    Stripesalignment = 2,
55    /// Opacity of a surface
56    Opacity = 3,
57    /// Ventilation control
58    Ventilation = 4,
59}
60
61impl ModulationType {
62    /// Convert from u8 value
63    pub fn from_u8(value: u8) -> Option<Self> {
64        match value {
65            0 => Some(ModulationType::Slatsorientation),
66            1 => Some(ModulationType::Slatsopenwork),
67            2 => Some(ModulationType::Stripesalignment),
68            3 => Some(ModulationType::Opacity),
69            4 => Some(ModulationType::Ventilation),
70            _ => None,
71        }
72    }
73
74    /// Convert to u8 value
75    pub fn to_u8(self) -> u8 {
76        self as u8
77    }
78}
79
80impl From<ModulationType> for u8 {
81    fn from(val: ModulationType) -> Self {
82        val as u8
83    }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
87#[repr(u8)]
88pub enum Overflow {
89    /// No overflow
90    Nooverflow = 0,
91    /// Inside overflow
92    Inside = 1,
93    /// Outside overflow
94    Outside = 2,
95    /// Top inside overflow
96    Topinside = 3,
97    /// Top outside overflow
98    Topoutside = 4,
99    /// Bottom inside overflow
100    Bottominside = 5,
101    /// Bottom outside overflow
102    Bottomoutside = 6,
103    /// Left inside overflow
104    Leftinside = 7,
105    /// Left outside overflow
106    Leftoutside = 8,
107    /// Right inside overflow
108    Rightinside = 9,
109    /// Right outside overflow
110    Rightoutside = 10,
111}
112
113impl Overflow {
114    /// Convert from u8 value
115    pub fn from_u8(value: u8) -> Option<Self> {
116        match value {
117            0 => Some(Overflow::Nooverflow),
118            1 => Some(Overflow::Inside),
119            2 => Some(Overflow::Outside),
120            3 => Some(Overflow::Topinside),
121            4 => Some(Overflow::Topoutside),
122            5 => Some(Overflow::Bottominside),
123            6 => Some(Overflow::Bottomoutside),
124            7 => Some(Overflow::Leftinside),
125            8 => Some(Overflow::Leftoutside),
126            9 => Some(Overflow::Rightinside),
127            10 => Some(Overflow::Rightoutside),
128            _ => None,
129        }
130    }
131
132    /// Convert to u8 value
133    pub fn to_u8(self) -> u8 {
134        self as u8
135    }
136}
137
138impl From<Overflow> for u8 {
139    fn from(val: Overflow) -> Self {
140        val as u8
141    }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
145#[repr(u8)]
146pub enum RotationAxis {
147    /// The panel rotates around a vertical axis located on the left side of the panel
148    Left = 0,
149    /// The panel rotates around a vertical axis located in the center of the panel
150    Centeredvertical = 1,
151    /// The panels rotates around vertical axes located on the left and right sides of the panel
152    Leftandright = 2,
153    /// The panel rotates around a vertical axis located on the right side of the panel
154    Right = 3,
155    /// The panel rotates around a horizontal axis located on the top of the panel
156    Top = 4,
157    /// The panel rotates around a horizontal axis located in the center of the panel
158    Centeredhorizontal = 5,
159    /// The panels rotates around horizontal axes located on the top and bottom of the panel
160    Topandbottom = 6,
161    /// The panel rotates around a horizontal axis located on the bottom of the panel
162    Bottom = 7,
163    /// The barrier tilts around an axis located at the left end of the barrier
164    Leftbarrier = 8,
165    /// The dual barriers tilt around axes located at each side of the composite barrier
166    Leftandrightbarriers = 9,
167    /// The barrier tilts around an axis located at the right end of the barrier
168    Rightbarrier = 10,
169}
170
171impl RotationAxis {
172    /// Convert from u8 value
173    pub fn from_u8(value: u8) -> Option<Self> {
174        match value {
175            0 => Some(RotationAxis::Left),
176            1 => Some(RotationAxis::Centeredvertical),
177            2 => Some(RotationAxis::Leftandright),
178            3 => Some(RotationAxis::Right),
179            4 => Some(RotationAxis::Top),
180            5 => Some(RotationAxis::Centeredhorizontal),
181            6 => Some(RotationAxis::Topandbottom),
182            7 => Some(RotationAxis::Bottom),
183            8 => Some(RotationAxis::Leftbarrier),
184            9 => Some(RotationAxis::Leftandrightbarriers),
185            10 => Some(RotationAxis::Rightbarrier),
186            _ => None,
187        }
188    }
189
190    /// Convert to u8 value
191    pub fn to_u8(self) -> u8 {
192        self as u8
193    }
194}
195
196impl From<RotationAxis> for u8 {
197    fn from(val: RotationAxis) -> Self {
198        val as u8
199    }
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
203#[repr(u8)]
204pub enum StepDirection {
205    /// Decrease towards 0.00%
206    Decrease = 0,
207    /// Increase towards 100.00%
208    Increase = 1,
209}
210
211impl StepDirection {
212    /// Convert from u8 value
213    pub fn from_u8(value: u8) -> Option<Self> {
214        match value {
215            0 => Some(StepDirection::Decrease),
216            1 => Some(StepDirection::Increase),
217            _ => None,
218        }
219    }
220
221    /// Convert to u8 value
222    pub fn to_u8(self) -> u8 {
223        self as u8
224    }
225}
226
227impl From<StepDirection> for u8 {
228    fn from(val: StepDirection) -> Self {
229        val as u8
230    }
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
234#[repr(u8)]
235pub enum TranslationDirection {
236    /// Downward translation
237    Downward = 0,
238    /// Upward translation
239    Upward = 1,
240    /// Vertical mask translation
241    Verticalmask = 2,
242    /// Vertical symmetry translation
243    Verticalsymmetry = 3,
244    /// Leftward translation
245    Leftward = 4,
246    /// Rightward translation
247    Rightward = 5,
248    /// Horizontal mask translation
249    Horizontalmask = 6,
250    /// Horizontal symmetry translation
251    Horizontalsymmetry = 7,
252    /// Forward translation
253    Forward = 8,
254    /// Backward translation
255    Backward = 9,
256    /// Depth mask translation
257    Depthmask = 10,
258    /// Depth symmetry translation
259    Depthsymmetry = 11,
260}
261
262impl TranslationDirection {
263    /// Convert from u8 value
264    pub fn from_u8(value: u8) -> Option<Self> {
265        match value {
266            0 => Some(TranslationDirection::Downward),
267            1 => Some(TranslationDirection::Upward),
268            2 => Some(TranslationDirection::Verticalmask),
269            3 => Some(TranslationDirection::Verticalsymmetry),
270            4 => Some(TranslationDirection::Leftward),
271            5 => Some(TranslationDirection::Rightward),
272            6 => Some(TranslationDirection::Horizontalmask),
273            7 => Some(TranslationDirection::Horizontalsymmetry),
274            8 => Some(TranslationDirection::Forward),
275            9 => Some(TranslationDirection::Backward),
276            10 => Some(TranslationDirection::Depthmask),
277            11 => Some(TranslationDirection::Depthsymmetry),
278            _ => None,
279        }
280    }
281
282    /// Convert to u8 value
283    pub fn to_u8(self) -> u8 {
284        self as u8
285    }
286}
287
288impl From<TranslationDirection> for u8 {
289    fn from(val: TranslationDirection) -> Self {
290        val as u8
291    }
292}
293
294// Bitmap definitions
295
296/// LatchControlModes bitmap type
297pub type LatchControlModes = u8;
298
299/// Constants for LatchControlModes
300pub mod latchcontrolmodes {
301    /// Remote latching capability
302    pub const REMOTE_LATCHING: u8 = 0x01;
303    /// Remote unlatching capability
304    pub const REMOTE_UNLATCHING: u8 = 0x02;
305}
306
307// Struct definitions
308
309#[derive(Debug, serde::Serialize)]
310pub struct DimensionState {
311    pub position: Option<u8>,
312    pub latch: Option<bool>,
313    pub speed: Option<u8>,
314}
315
316#[derive(Debug, serde::Serialize)]
317pub struct RangePercent {
318    pub min: Option<u8>,
319    pub max: Option<u8>,
320}
321
322#[derive(Debug, serde::Serialize)]
323pub struct UnitRange {
324    pub min: Option<i16>,
325    pub max: Option<i16>,
326}
327
328// Command encoders
329
330/// Encode SetTarget command (0x00)
331pub fn encode_set_target(position: u8, latch: bool, speed: u8) -> anyhow::Result<Vec<u8>> {
332    let tlv = tlv::TlvItemEnc {
333        tag: 0,
334        value: tlv::TlvItemValueEnc::StructInvisible(vec![
335        (0, tlv::TlvItemValueEnc::UInt8(position)).into(),
336        (1, tlv::TlvItemValueEnc::Bool(latch)).into(),
337        (2, tlv::TlvItemValueEnc::UInt8(speed)).into(),
338        ]),
339    };
340    Ok(tlv.encode()?)
341}
342
343/// Encode Step command (0x01)
344pub fn encode_step(direction: StepDirection, number_of_steps: u16, speed: u8) -> anyhow::Result<Vec<u8>> {
345    let tlv = tlv::TlvItemEnc {
346        tag: 0,
347        value: tlv::TlvItemValueEnc::StructInvisible(vec![
348        (0, tlv::TlvItemValueEnc::UInt8(direction.to_u8())).into(),
349        (1, tlv::TlvItemValueEnc::UInt16(number_of_steps)).into(),
350        (2, tlv::TlvItemValueEnc::UInt8(speed)).into(),
351        ]),
352    };
353    Ok(tlv.encode()?)
354}
355
356// Attribute decoders
357
358/// Decode CurrentState attribute (0x0000)
359pub fn decode_current_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DimensionState>> {
360    if let tlv::TlvItemValue::List(_fields) = inp {
361        // Struct with fields
362        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
363        Ok(Some(DimensionState {
364                position: item.get_int(&[0]).map(|v| v as u8),
365                latch: item.get_bool(&[1]),
366                speed: item.get_int(&[2]).map(|v| v as u8),
367        }))
368    //} else if let tlv::TlvItemValue::Null = inp {
369    //    // Null value for nullable struct
370    //    Ok(None)
371    } else {
372    Ok(None)
373    //    Err(anyhow::anyhow!("Expected struct fields or null"))
374    }
375}
376
377/// Decode TargetState attribute (0x0001)
378pub fn decode_target_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DimensionState>> {
379    if let tlv::TlvItemValue::List(_fields) = inp {
380        // Struct with fields
381        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
382        Ok(Some(DimensionState {
383                position: item.get_int(&[0]).map(|v| v as u8),
384                latch: item.get_bool(&[1]),
385                speed: item.get_int(&[2]).map(|v| v as u8),
386        }))
387    //} else if let tlv::TlvItemValue::Null = inp {
388    //    // Null value for nullable struct
389    //    Ok(None)
390    } else {
391    Ok(None)
392    //    Err(anyhow::anyhow!("Expected struct fields or null"))
393    }
394}
395
396/// Decode Resolution attribute (0x0002)
397pub fn decode_resolution(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
398    if let tlv::TlvItemValue::Int(v) = inp {
399        Ok(*v as u8)
400    } else {
401        Err(anyhow::anyhow!("Expected UInt8"))
402    }
403}
404
405/// Decode StepValue attribute (0x0003)
406pub fn decode_step_value(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
407    if let tlv::TlvItemValue::Int(v) = inp {
408        Ok(*v as u8)
409    } else {
410        Err(anyhow::anyhow!("Expected UInt8"))
411    }
412}
413
414/// Decode Unit attribute (0x0004)
415pub fn decode_unit(inp: &tlv::TlvItemValue) -> anyhow::Result<ClosureUnit> {
416    if let tlv::TlvItemValue::Int(v) = inp {
417        ClosureUnit::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
418    } else {
419        Err(anyhow::anyhow!("Expected Integer"))
420    }
421}
422
423/// Decode UnitRange attribute (0x0005)
424pub fn decode_unit_range(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<UnitRange>> {
425    if let tlv::TlvItemValue::List(_fields) = inp {
426        // Struct with fields
427        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
428        Ok(Some(UnitRange {
429                min: item.get_int(&[0]).map(|v| v as i16),
430                max: item.get_int(&[1]).map(|v| v as i16),
431        }))
432    //} else if let tlv::TlvItemValue::Null = inp {
433    //    // Null value for nullable struct
434    //    Ok(None)
435    } else {
436    Ok(None)
437    //    Err(anyhow::anyhow!("Expected struct fields or null"))
438    }
439}
440
441/// Decode LimitRange attribute (0x0006)
442pub fn decode_limit_range(inp: &tlv::TlvItemValue) -> anyhow::Result<RangePercent> {
443    if let tlv::TlvItemValue::List(_fields) = inp {
444        // Struct with fields
445        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
446        Ok(RangePercent {
447                min: item.get_int(&[0]).map(|v| v as u8),
448                max: item.get_int(&[1]).map(|v| v as u8),
449        })
450    } else {
451        Err(anyhow::anyhow!("Expected struct fields"))
452    }
453}
454
455/// Decode TranslationDirection attribute (0x0007)
456pub fn decode_translation_direction(inp: &tlv::TlvItemValue) -> anyhow::Result<TranslationDirection> {
457    if let tlv::TlvItemValue::Int(v) = inp {
458        TranslationDirection::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
459    } else {
460        Err(anyhow::anyhow!("Expected Integer"))
461    }
462}
463
464/// Decode RotationAxis attribute (0x0008)
465pub fn decode_rotation_axis(inp: &tlv::TlvItemValue) -> anyhow::Result<RotationAxis> {
466    if let tlv::TlvItemValue::Int(v) = inp {
467        RotationAxis::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
468    } else {
469        Err(anyhow::anyhow!("Expected Integer"))
470    }
471}
472
473/// Decode Overflow attribute (0x0009)
474pub fn decode_overflow(inp: &tlv::TlvItemValue) -> anyhow::Result<Overflow> {
475    if let tlv::TlvItemValue::Int(v) = inp {
476        Overflow::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
477    } else {
478        Err(anyhow::anyhow!("Expected Integer"))
479    }
480}
481
482/// Decode ModulationType attribute (0x000A)
483pub fn decode_modulation_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ModulationType> {
484    if let tlv::TlvItemValue::Int(v) = inp {
485        ModulationType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
486    } else {
487        Err(anyhow::anyhow!("Expected Integer"))
488    }
489}
490
491/// Decode LatchControlModes attribute (0x000B)
492pub fn decode_latch_control_modes(inp: &tlv::TlvItemValue) -> anyhow::Result<LatchControlModes> {
493    if let tlv::TlvItemValue::Int(v) = inp {
494        Ok(*v as u8)
495    } else {
496        Err(anyhow::anyhow!("Expected Integer"))
497    }
498}
499
500
501// JSON dispatcher function
502
503/// Decode attribute value and return as JSON string
504///
505/// # Parameters
506/// * `cluster_id` - The cluster identifier
507/// * `attribute_id` - The attribute identifier
508/// * `tlv_value` - The TLV value to decode
509///
510/// # Returns
511/// JSON string representation of the decoded value or error
512pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
513    // Verify this is the correct cluster
514    if cluster_id != 0x0105 {
515        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0105, got {}\"}}", cluster_id);
516    }
517
518    match attribute_id {
519        0x0000 => {
520            match decode_current_state(tlv_value) {
521                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
522                Err(e) => format!("{{\"error\": \"{}\"}}", e),
523            }
524        }
525        0x0001 => {
526            match decode_target_state(tlv_value) {
527                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
528                Err(e) => format!("{{\"error\": \"{}\"}}", e),
529            }
530        }
531        0x0002 => {
532            match decode_resolution(tlv_value) {
533                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
534                Err(e) => format!("{{\"error\": \"{}\"}}", e),
535            }
536        }
537        0x0003 => {
538            match decode_step_value(tlv_value) {
539                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
540                Err(e) => format!("{{\"error\": \"{}\"}}", e),
541            }
542        }
543        0x0004 => {
544            match decode_unit(tlv_value) {
545                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
546                Err(e) => format!("{{\"error\": \"{}\"}}", e),
547            }
548        }
549        0x0005 => {
550            match decode_unit_range(tlv_value) {
551                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
552                Err(e) => format!("{{\"error\": \"{}\"}}", e),
553            }
554        }
555        0x0006 => {
556            match decode_limit_range(tlv_value) {
557                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
558                Err(e) => format!("{{\"error\": \"{}\"}}", e),
559            }
560        }
561        0x0007 => {
562            match decode_translation_direction(tlv_value) {
563                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
564                Err(e) => format!("{{\"error\": \"{}\"}}", e),
565            }
566        }
567        0x0008 => {
568            match decode_rotation_axis(tlv_value) {
569                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
570                Err(e) => format!("{{\"error\": \"{}\"}}", e),
571            }
572        }
573        0x0009 => {
574            match decode_overflow(tlv_value) {
575                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
576                Err(e) => format!("{{\"error\": \"{}\"}}", e),
577            }
578        }
579        0x000A => {
580            match decode_modulation_type(tlv_value) {
581                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
582                Err(e) => format!("{{\"error\": \"{}\"}}", e),
583            }
584        }
585        0x000B => {
586            match decode_latch_control_modes(tlv_value) {
587                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
588                Err(e) => format!("{{\"error\": \"{}\"}}", e),
589            }
590        }
591        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
592    }
593}
594
595/// Get list of all attributes supported by this cluster
596///
597/// # Returns
598/// Vector of tuples containing (attribute_id, attribute_name)
599pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
600    vec![
601        (0x0000, "CurrentState"),
602        (0x0001, "TargetState"),
603        (0x0002, "Resolution"),
604        (0x0003, "StepValue"),
605        (0x0004, "Unit"),
606        (0x0005, "UnitRange"),
607        (0x0006, "LimitRange"),
608        (0x0007, "TranslationDirection"),
609        (0x0008, "RotationAxis"),
610        (0x0009, "Overflow"),
611        (0x000A, "ModulationType"),
612        (0x000B, "LatchControlModes"),
613    ]
614}
615
616// Command listing
617
618pub fn get_command_list() -> Vec<(u32, &'static str)> {
619    vec![
620        (0x00, "SetTarget"),
621        (0x01, "Step"),
622    ]
623}
624
625pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
626    match cmd_id {
627        0x00 => Some("SetTarget"),
628        0x01 => Some("Step"),
629        _ => None,
630    }
631}
632
633pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
634    match cmd_id {
635        0x00 => Some(vec![
636            crate::clusters::codec::CommandField { tag: 0, name: "position", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
637            crate::clusters::codec::CommandField { tag: 1, name: "latch", kind: crate::clusters::codec::FieldKind::Bool, optional: true, nullable: false },
638            crate::clusters::codec::CommandField { tag: 2, name: "speed", kind: crate::clusters::codec::FieldKind::U8, optional: true, nullable: false },
639        ]),
640        0x01 => Some(vec![
641            crate::clusters::codec::CommandField { tag: 0, name: "direction", kind: crate::clusters::codec::FieldKind::Enum { name: "StepDirection", variants: &[(0, "Decrease"), (1, "Increase")] }, optional: false, nullable: false },
642            crate::clusters::codec::CommandField { tag: 1, name: "number_of_steps", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
643            crate::clusters::codec::CommandField { tag: 2, name: "speed", kind: crate::clusters::codec::FieldKind::U8, optional: true, nullable: false },
644        ]),
645        _ => None,
646    }
647}
648
649pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
650    match cmd_id {
651        0x00 => {
652        let position = crate::clusters::codec::json_util::get_u8(args, "position")?;
653        let latch = crate::clusters::codec::json_util::get_bool(args, "latch")?;
654        let speed = crate::clusters::codec::json_util::get_u8(args, "speed")?;
655        encode_set_target(position, latch, speed)
656        }
657        0x01 => {
658        let direction = {
659            let n = crate::clusters::codec::json_util::get_u64(args, "direction")?;
660            StepDirection::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid StepDirection: {}", n))?
661        };
662        let number_of_steps = crate::clusters::codec::json_util::get_u16(args, "number_of_steps")?;
663        let speed = crate::clusters::codec::json_util::get_u8(args, "speed")?;
664        encode_step(direction, number_of_steps, speed)
665        }
666        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
667    }
668}
669
670// Typed facade (invokes + reads)
671
672/// Invoke `SetTarget` command on cluster `Closure Dimension`.
673pub async fn set_target(conn: &crate::controller::Connection, endpoint: u16, position: u8, latch: bool, speed: u8) -> anyhow::Result<()> {
674    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_CMD_ID_SETTARGET, &encode_set_target(position, latch, speed)?).await?;
675    Ok(())
676}
677
678/// Invoke `Step` command on cluster `Closure Dimension`.
679pub async fn step(conn: &crate::controller::Connection, endpoint: u16, direction: StepDirection, number_of_steps: u16, speed: u8) -> anyhow::Result<()> {
680    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_CMD_ID_STEP, &encode_step(direction, number_of_steps, speed)?).await?;
681    Ok(())
682}
683
684/// Read `CurrentState` attribute from cluster `Closure Dimension`.
685pub async fn read_current_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DimensionState>> {
686    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_CURRENTSTATE).await?;
687    decode_current_state(&tlv)
688}
689
690/// Read `TargetState` attribute from cluster `Closure Dimension`.
691pub async fn read_target_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DimensionState>> {
692    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_TARGETSTATE).await?;
693    decode_target_state(&tlv)
694}
695
696/// Read `Resolution` attribute from cluster `Closure Dimension`.
697pub async fn read_resolution(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
698    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_RESOLUTION).await?;
699    decode_resolution(&tlv)
700}
701
702/// Read `StepValue` attribute from cluster `Closure Dimension`.
703pub async fn read_step_value(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
704    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_STEPVALUE).await?;
705    decode_step_value(&tlv)
706}
707
708/// Read `Unit` attribute from cluster `Closure Dimension`.
709pub async fn read_unit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ClosureUnit> {
710    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_UNIT).await?;
711    decode_unit(&tlv)
712}
713
714/// Read `UnitRange` attribute from cluster `Closure Dimension`.
715pub async fn read_unit_range(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<UnitRange>> {
716    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_UNITRANGE).await?;
717    decode_unit_range(&tlv)
718}
719
720/// Read `LimitRange` attribute from cluster `Closure Dimension`.
721pub async fn read_limit_range(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RangePercent> {
722    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_LIMITRANGE).await?;
723    decode_limit_range(&tlv)
724}
725
726/// Read `TranslationDirection` attribute from cluster `Closure Dimension`.
727pub async fn read_translation_direction(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TranslationDirection> {
728    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_TRANSLATIONDIRECTION).await?;
729    decode_translation_direction(&tlv)
730}
731
732/// Read `RotationAxis` attribute from cluster `Closure Dimension`.
733pub async fn read_rotation_axis(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RotationAxis> {
734    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_ROTATIONAXIS).await?;
735    decode_rotation_axis(&tlv)
736}
737
738/// Read `Overflow` attribute from cluster `Closure Dimension`.
739pub async fn read_overflow(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Overflow> {
740    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_OVERFLOW).await?;
741    decode_overflow(&tlv)
742}
743
744/// Read `ModulationType` attribute from cluster `Closure Dimension`.
745pub async fn read_modulation_type(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ModulationType> {
746    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_MODULATIONTYPE).await?;
747    decode_modulation_type(&tlv)
748}
749
750/// Read `LatchControlModes` attribute from cluster `Closure Dimension`.
751pub async fn read_latch_control_modes(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<LatchControlModes> {
752    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_CLOSURE_DIMENSION, crate::clusters::defs::CLUSTER_CLOSURE_DIMENSION_ATTR_ID_LATCHCONTROLMODES).await?;
753    decode_latch_control_modes(&tlv)
754}
755