Skip to main content

matter_clusters/gen/
window_covering.rs

1//! WindowCovering cluster (0x0102).
2//! @generated by `cargo xtask codegen` — do not edit.
3
4#![allow(
5    clippy::all,
6    clippy::pedantic,
7    dead_code,
8    unreachable_pub,
9    unused_imports
10)]
11
12use crate::datatypes::SemanticTagStruct;
13use crate::error::ClusterError;
14use crate::types::Nullable;
15use matter_codec::{ContainerKind, Element, Tag, TlvReader, TlvWriter, Value};
16
17/// Cluster ID.
18pub const CLUSTER_ID: u32 = 0x0102;
19/// Cluster revision.
20pub const CLUSTER_REVISION: u16 = 8;
21
22/// Command IDs (requests and responses).
23pub mod command_id {
24    /// `UpOrOpen` (request).
25    pub const UP_OR_OPEN: u32 = 0x00;
26    /// `DownOrClose` (request).
27    pub const DOWN_OR_CLOSE: u32 = 0x01;
28    /// `StopMotion` (request).
29    pub const STOP_MOTION: u32 = 0x02;
30    /// `GoToLiftPercentage` (request).
31    pub const GO_TO_LIFT_PERCENTAGE: u32 = 0x05;
32    /// `GoToTiltPercentage` (request).
33    pub const GO_TO_TILT_PERCENTAGE: u32 = 0x08;
34}
35
36/// Attribute IDs (cluster-specific).
37pub mod attribute_id {
38    /// `Type`.
39    pub const TYPE: u32 = 0x0000;
40    /// `NumberOfActuationsLift`.
41    pub const NUMBER_OF_ACTUATIONS_LIFT: u32 = 0x0005;
42    /// `NumberOfActuationsTilt`.
43    pub const NUMBER_OF_ACTUATIONS_TILT: u32 = 0x0006;
44    /// `ConfigStatus`.
45    pub const CONFIG_STATUS: u32 = 0x0007;
46    /// `CurrentPositionLiftPercentage`.
47    pub const CURRENT_POSITION_LIFT_PERCENTAGE: u32 = 0x0008;
48    /// `CurrentPositionTiltPercentage`.
49    pub const CURRENT_POSITION_TILT_PERCENTAGE: u32 = 0x0009;
50    /// `OperationalStatus`.
51    pub const OPERATIONAL_STATUS: u32 = 0x000A;
52    /// `TargetPositionLiftPercent100ths`.
53    pub const TARGET_POSITION_LIFT_PERCENT100THS: u32 = 0x000B;
54    /// `TargetPositionTiltPercent100ths`.
55    pub const TARGET_POSITION_TILT_PERCENT100THS: u32 = 0x000C;
56    /// `EndProductType`.
57    pub const END_PRODUCT_TYPE: u32 = 0x000D;
58    /// `CurrentPositionLiftPercent100ths`.
59    pub const CURRENT_POSITION_LIFT_PERCENT100THS: u32 = 0x000E;
60    /// `CurrentPositionTiltPercent100ths`.
61    pub const CURRENT_POSITION_TILT_PERCENT100THS: u32 = 0x000F;
62    /// `Mode`.
63    pub const MODE: u32 = 0x0017;
64    /// `SafetyStatus`.
65    pub const SAFETY_STATUS: u32 = 0x001A;
66}
67
68bitflags::bitflags! {
69    /// `WindowCovering` feature bits (FeatureMap).
70    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
71    pub struct Feature: u32 {
72        /// Lift (LF).
73        const LF = 1 << 0;
74        /// Tilt (TL).
75        const TL = 1 << 1;
76        /// PositionAwareLift (PA_LF).
77        const PA_LF = 1 << 2;
78        /// PositionAwareTilt (PA_TL).
79        const PA_TL = 1 << 4;
80    }
81}
82
83bitflags::bitflags! {
84    /// `ConfigStatusBitmap` (map8).
85    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
86    pub struct ConfigStatusBitmap: u8 {
87        /// Operational.
88        const OPERATIONAL = 1 << 0;
89        /// OnlineReserved.
90        const ONLINE_RESERVED = 1 << 1;
91        /// LiftMovementReversed.
92        const LIFT_MOVEMENT_REVERSED = 1 << 2;
93        /// LiftPositionAware.
94        const LIFT_POSITION_AWARE = 1 << 3;
95        /// TiltPositionAware.
96        const TILT_POSITION_AWARE = 1 << 4;
97        /// LiftEncoderControlled.
98        const LIFT_ENCODER_CONTROLLED = 1 << 5;
99        /// TiltEncoderControlled.
100        const TILT_ENCODER_CONTROLLED = 1 << 6;
101    }
102}
103
104/// `EndProductTypeEnum` (enum8).
105#[derive(Copy, Clone, Debug, PartialEq, Eq)]
106pub enum EndProductTypeEnum {
107    /// RollerShade = 0.
108    RollerShade,
109    /// RomanShade = 1.
110    RomanShade,
111    /// BalloonShade = 2.
112    BalloonShade,
113    /// WovenWood = 3.
114    WovenWood,
115    /// PleatedShade = 4.
116    PleatedShade,
117    /// CellularShade = 5.
118    CellularShade,
119    /// LayeredShade = 6.
120    LayeredShade,
121    /// LayeredShade2D = 7.
122    LayeredShade2D,
123    /// SheerShade = 8.
124    SheerShade,
125    /// TiltOnlyInteriorBlind = 9.
126    TiltOnlyInteriorBlind,
127    /// InteriorBlind = 10.
128    InteriorBlind,
129    /// VerticalBlindStripCurtain = 11.
130    VerticalBlindStripCurtain,
131    /// InteriorVenetianBlind = 12.
132    InteriorVenetianBlind,
133    /// ExteriorVenetianBlind = 13.
134    ExteriorVenetianBlind,
135    /// LateralLeftCurtain = 14.
136    LateralLeftCurtain,
137    /// LateralRightCurtain = 15.
138    LateralRightCurtain,
139    /// CentralCurtain = 16.
140    CentralCurtain,
141    /// RollerShutter = 17.
142    RollerShutter,
143    /// ExteriorVerticalScreen = 18.
144    ExteriorVerticalScreen,
145    /// AwningTerracePatio = 19.
146    AwningTerracePatio,
147    /// AwningVerticalScreen = 20.
148    AwningVerticalScreen,
149    /// TiltOnlyPergola = 21.
150    TiltOnlyPergola,
151    /// SwingingShutter = 22.
152    SwingingShutter,
153    /// SlidingShutter = 23.
154    SlidingShutter,
155    /// Unknown = 255.
156    Unknown,
157    /// A value not known to this codegen revision.
158    Unrecognized(u8),
159}
160
161impl EndProductTypeEnum {
162    /// Decode from its raw discriminant (unknown → `Unrecognized`).
163    #[must_use]
164    pub fn from_raw(v: u8) -> Self {
165        match v {
166            0 => Self::RollerShade,
167            1 => Self::RomanShade,
168            2 => Self::BalloonShade,
169            3 => Self::WovenWood,
170            4 => Self::PleatedShade,
171            5 => Self::CellularShade,
172            6 => Self::LayeredShade,
173            7 => Self::LayeredShade2D,
174            8 => Self::SheerShade,
175            9 => Self::TiltOnlyInteriorBlind,
176            10 => Self::InteriorBlind,
177            11 => Self::VerticalBlindStripCurtain,
178            12 => Self::InteriorVenetianBlind,
179            13 => Self::ExteriorVenetianBlind,
180            14 => Self::LateralLeftCurtain,
181            15 => Self::LateralRightCurtain,
182            16 => Self::CentralCurtain,
183            17 => Self::RollerShutter,
184            18 => Self::ExteriorVerticalScreen,
185            19 => Self::AwningTerracePatio,
186            20 => Self::AwningVerticalScreen,
187            21 => Self::TiltOnlyPergola,
188            22 => Self::SwingingShutter,
189            23 => Self::SlidingShutter,
190            255 => Self::Unknown,
191            other => Self::Unrecognized(other),
192        }
193    }
194    /// The raw discriminant.
195    #[must_use]
196    pub fn to_raw(self) -> u8 {
197        match self {
198            Self::RollerShade => 0,
199            Self::RomanShade => 1,
200            Self::BalloonShade => 2,
201            Self::WovenWood => 3,
202            Self::PleatedShade => 4,
203            Self::CellularShade => 5,
204            Self::LayeredShade => 6,
205            Self::LayeredShade2D => 7,
206            Self::SheerShade => 8,
207            Self::TiltOnlyInteriorBlind => 9,
208            Self::InteriorBlind => 10,
209            Self::VerticalBlindStripCurtain => 11,
210            Self::InteriorVenetianBlind => 12,
211            Self::ExteriorVenetianBlind => 13,
212            Self::LateralLeftCurtain => 14,
213            Self::LateralRightCurtain => 15,
214            Self::CentralCurtain => 16,
215            Self::RollerShutter => 17,
216            Self::ExteriorVerticalScreen => 18,
217            Self::AwningTerracePatio => 19,
218            Self::AwningVerticalScreen => 20,
219            Self::TiltOnlyPergola => 21,
220            Self::SwingingShutter => 22,
221            Self::SlidingShutter => 23,
222            Self::Unknown => 255,
223            Self::Unrecognized(v) => v,
224        }
225    }
226}
227
228bitflags::bitflags! {
229    /// `ModeBitmap` (map8).
230    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
231    pub struct ModeBitmap: u8 {
232        /// MotorDirectionReversed.
233        const MOTOR_DIRECTION_REVERSED = 1 << 0;
234        /// CalibrationMode.
235        const CALIBRATION_MODE = 1 << 1;
236        /// MaintenanceMode.
237        const MAINTENANCE_MODE = 1 << 2;
238        /// LedFeedback.
239        const LED_FEEDBACK = 1 << 3;
240    }
241}
242
243/// `MovementStatus` (enum8).
244#[derive(Copy, Clone, Debug, PartialEq, Eq)]
245pub enum MovementStatus {
246    /// Stopped = 0.
247    Stopped,
248    /// Opening = 1.
249    Opening,
250    /// Closing = 2.
251    Closing,
252    /// A value not known to this codegen revision.
253    Unknown(u8),
254}
255
256impl MovementStatus {
257    /// Decode from its raw discriminant (unknown → `Unknown`).
258    #[must_use]
259    pub fn from_raw(v: u8) -> Self {
260        match v {
261            0 => Self::Stopped,
262            1 => Self::Opening,
263            2 => Self::Closing,
264            other => Self::Unknown(other),
265        }
266    }
267    /// The raw discriminant.
268    #[must_use]
269    pub fn to_raw(self) -> u8 {
270        match self {
271            Self::Stopped => 0,
272            Self::Opening => 1,
273            Self::Closing => 2,
274            Self::Unknown(v) => v,
275        }
276    }
277}
278
279bitflags::bitflags! {
280    /// `OperationalStatusBitmap` (map8).
281    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
282    pub struct OperationalStatusBitmap: u8 {
283    }
284}
285
286bitflags::bitflags! {
287    /// `SafetyStatusBitmap` (map16).
288    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
289    pub struct SafetyStatusBitmap: u16 {
290        /// RemoteLockout.
291        const REMOTE_LOCKOUT = 1 << 0;
292        /// TamperDetection.
293        const TAMPER_DETECTION = 1 << 1;
294        /// FailedCommunication.
295        const FAILED_COMMUNICATION = 1 << 2;
296        /// PositionFailure.
297        const POSITION_FAILURE = 1 << 3;
298        /// ThermalProtection.
299        const THERMAL_PROTECTION = 1 << 4;
300        /// ObstacleDetected.
301        const OBSTACLE_DETECTED = 1 << 5;
302        /// Power.
303        const POWER = 1 << 6;
304        /// StopInput.
305        const STOP_INPUT = 1 << 7;
306        /// MotorJammed.
307        const MOTOR_JAMMED = 1 << 8;
308        /// HardwareFailure.
309        const HARDWARE_FAILURE = 1 << 9;
310        /// ManualOperation.
311        const MANUAL_OPERATION = 1 << 10;
312        /// Protection.
313        const PROTECTION = 1 << 11;
314    }
315}
316
317/// `TypeEnum` (enum8).
318#[derive(Copy, Clone, Debug, PartialEq, Eq)]
319pub enum TypeEnum {
320    /// Rollershade = 0.
321    Rollershade,
322    /// Rollershade2Motor = 1.
323    Rollershade2Motor,
324    /// RollershadeExterior = 2.
325    RollershadeExterior,
326    /// RollershadeExterior2Motor = 3.
327    RollershadeExterior2Motor,
328    /// Drapery = 4.
329    Drapery,
330    /// Awning = 5.
331    Awning,
332    /// Shutter = 6.
333    Shutter,
334    /// TiltBlindTiltOnly = 7.
335    TiltBlindTiltOnly,
336    /// TiltBlindLift = 8.
337    TiltBlindLift,
338    /// ProjectorScreen = 9.
339    ProjectorScreen,
340    /// Unknown = 255.
341    Unknown,
342    /// A value not known to this codegen revision.
343    Unrecognized(u8),
344}
345
346impl TypeEnum {
347    /// Decode from its raw discriminant (unknown → `Unrecognized`).
348    #[must_use]
349    pub fn from_raw(v: u8) -> Self {
350        match v {
351            0 => Self::Rollershade,
352            1 => Self::Rollershade2Motor,
353            2 => Self::RollershadeExterior,
354            3 => Self::RollershadeExterior2Motor,
355            4 => Self::Drapery,
356            5 => Self::Awning,
357            6 => Self::Shutter,
358            7 => Self::TiltBlindTiltOnly,
359            8 => Self::TiltBlindLift,
360            9 => Self::ProjectorScreen,
361            255 => Self::Unknown,
362            other => Self::Unrecognized(other),
363        }
364    }
365    /// The raw discriminant.
366    #[must_use]
367    pub fn to_raw(self) -> u8 {
368        match self {
369            Self::Rollershade => 0,
370            Self::Rollershade2Motor => 1,
371            Self::RollershadeExterior => 2,
372            Self::RollershadeExterior2Motor => 3,
373            Self::Drapery => 4,
374            Self::Awning => 5,
375            Self::Shutter => 6,
376            Self::TiltBlindTiltOnly => 7,
377            Self::TiltBlindLift => 8,
378            Self::ProjectorScreen => 9,
379            Self::Unknown => 255,
380            Self::Unrecognized(v) => v,
381        }
382    }
383}
384
385/// Decode the `Type` attribute value.
386///
387/// # Errors
388/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
389pub fn decode_type(tlv: &[u8]) -> Result<TypeEnum, ClusterError> {
390    let mut r = TlvReader::new(tlv);
391    match r.next()? {
392        Some(Element::Scalar {
393            value: Value::Uint(v),
394            ..
395        }) => Ok(TypeEnum::from_raw(
396            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("Type"))?,
397        )),
398        _ => Err(ClusterError::UnexpectedType { context: "Type" }),
399    }
400}
401
402/// Decode the `NumberOfActuationsLift` attribute value.
403///
404/// # Errors
405/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
406pub fn decode_number_of_actuations_lift(tlv: &[u8]) -> Result<u16, ClusterError> {
407    let mut r = TlvReader::new(tlv);
408    match r.next()? {
409        Some(Element::Scalar {
410            value: Value::Uint(v),
411            ..
412        }) => {
413            Ok(u16::try_from(v)
414                .map_err(|_| ClusterError::InvalidLength("NumberOfActuationsLift"))?)
415        }
416        _ => Err(ClusterError::UnexpectedType {
417            context: "NumberOfActuationsLift",
418        }),
419    }
420}
421
422/// Decode the `NumberOfActuationsTilt` attribute value.
423///
424/// # Errors
425/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
426pub fn decode_number_of_actuations_tilt(tlv: &[u8]) -> Result<u16, ClusterError> {
427    let mut r = TlvReader::new(tlv);
428    match r.next()? {
429        Some(Element::Scalar {
430            value: Value::Uint(v),
431            ..
432        }) => {
433            Ok(u16::try_from(v)
434                .map_err(|_| ClusterError::InvalidLength("NumberOfActuationsTilt"))?)
435        }
436        _ => Err(ClusterError::UnexpectedType {
437            context: "NumberOfActuationsTilt",
438        }),
439    }
440}
441
442/// Decode the `ConfigStatus` attribute value.
443///
444/// # Errors
445/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
446pub fn decode_config_status(tlv: &[u8]) -> Result<ConfigStatusBitmap, ClusterError> {
447    let mut r = TlvReader::new(tlv);
448    match r.next()? {
449        Some(Element::Scalar {
450            value: Value::Uint(v),
451            ..
452        }) => Ok(ConfigStatusBitmap::from_bits_retain(
453            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("ConfigStatus"))?,
454        )),
455        _ => Err(ClusterError::UnexpectedType {
456            context: "ConfigStatus",
457        }),
458    }
459}
460
461/// Decode the `CurrentPositionLiftPercentage` attribute value.
462///
463/// # Errors
464/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
465pub fn decode_current_position_lift_percentage(tlv: &[u8]) -> Result<Nullable<u8>, ClusterError> {
466    let mut r = TlvReader::new(tlv);
467    match r.next()? {
468        Some(Element::Scalar {
469            value: Value::Null, ..
470        }) => Ok(Nullable::Null),
471        Some(Element::Scalar {
472            value: Value::Uint(v),
473            ..
474        }) => Ok(Nullable::Value(u8::try_from(v).map_err(|_| {
475            ClusterError::InvalidLength("CurrentPositionLiftPercentage")
476        })?)),
477        _ => Err(ClusterError::UnexpectedType {
478            context: "CurrentPositionLiftPercentage",
479        }),
480    }
481}
482
483/// Decode the `CurrentPositionTiltPercentage` attribute value.
484///
485/// # Errors
486/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
487pub fn decode_current_position_tilt_percentage(tlv: &[u8]) -> Result<Nullable<u8>, ClusterError> {
488    let mut r = TlvReader::new(tlv);
489    match r.next()? {
490        Some(Element::Scalar {
491            value: Value::Null, ..
492        }) => Ok(Nullable::Null),
493        Some(Element::Scalar {
494            value: Value::Uint(v),
495            ..
496        }) => Ok(Nullable::Value(u8::try_from(v).map_err(|_| {
497            ClusterError::InvalidLength("CurrentPositionTiltPercentage")
498        })?)),
499        _ => Err(ClusterError::UnexpectedType {
500            context: "CurrentPositionTiltPercentage",
501        }),
502    }
503}
504
505/// Decode the `OperationalStatus` attribute value.
506///
507/// # Errors
508/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
509pub fn decode_operational_status(tlv: &[u8]) -> Result<OperationalStatusBitmap, ClusterError> {
510    let mut r = TlvReader::new(tlv);
511    match r.next()? {
512        Some(Element::Scalar {
513            value: Value::Uint(v),
514            ..
515        }) => Ok(OperationalStatusBitmap::from_bits_retain(
516            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("OperationalStatus"))?,
517        )),
518        _ => Err(ClusterError::UnexpectedType {
519            context: "OperationalStatus",
520        }),
521    }
522}
523
524/// Decode the `TargetPositionLiftPercent100ths` attribute value.
525///
526/// # Errors
527/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
528pub fn decode_target_position_lift_percent100ths(
529    tlv: &[u8],
530) -> Result<Nullable<u16>, ClusterError> {
531    let mut r = TlvReader::new(tlv);
532    match r.next()? {
533        Some(Element::Scalar {
534            value: Value::Null, ..
535        }) => Ok(Nullable::Null),
536        Some(Element::Scalar {
537            value: Value::Uint(v),
538            ..
539        }) => Ok(Nullable::Value(u16::try_from(v).map_err(|_| {
540            ClusterError::InvalidLength("TargetPositionLiftPercent100ths")
541        })?)),
542        _ => Err(ClusterError::UnexpectedType {
543            context: "TargetPositionLiftPercent100ths",
544        }),
545    }
546}
547
548/// Decode the `TargetPositionTiltPercent100ths` attribute value.
549///
550/// # Errors
551/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
552pub fn decode_target_position_tilt_percent100ths(
553    tlv: &[u8],
554) -> Result<Nullable<u16>, ClusterError> {
555    let mut r = TlvReader::new(tlv);
556    match r.next()? {
557        Some(Element::Scalar {
558            value: Value::Null, ..
559        }) => Ok(Nullable::Null),
560        Some(Element::Scalar {
561            value: Value::Uint(v),
562            ..
563        }) => Ok(Nullable::Value(u16::try_from(v).map_err(|_| {
564            ClusterError::InvalidLength("TargetPositionTiltPercent100ths")
565        })?)),
566        _ => Err(ClusterError::UnexpectedType {
567            context: "TargetPositionTiltPercent100ths",
568        }),
569    }
570}
571
572/// Decode the `EndProductType` attribute value.
573///
574/// # Errors
575/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
576pub fn decode_end_product_type(tlv: &[u8]) -> Result<EndProductTypeEnum, ClusterError> {
577    let mut r = TlvReader::new(tlv);
578    match r.next()? {
579        Some(Element::Scalar {
580            value: Value::Uint(v),
581            ..
582        }) => Ok(EndProductTypeEnum::from_raw(
583            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("EndProductType"))?,
584        )),
585        _ => Err(ClusterError::UnexpectedType {
586            context: "EndProductType",
587        }),
588    }
589}
590
591/// Decode the `CurrentPositionLiftPercent100ths` attribute value.
592///
593/// # Errors
594/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
595pub fn decode_current_position_lift_percent100ths(
596    tlv: &[u8],
597) -> Result<Nullable<u16>, ClusterError> {
598    let mut r = TlvReader::new(tlv);
599    match r.next()? {
600        Some(Element::Scalar {
601            value: Value::Null, ..
602        }) => Ok(Nullable::Null),
603        Some(Element::Scalar {
604            value: Value::Uint(v),
605            ..
606        }) => Ok(Nullable::Value(u16::try_from(v).map_err(|_| {
607            ClusterError::InvalidLength("CurrentPositionLiftPercent100ths")
608        })?)),
609        _ => Err(ClusterError::UnexpectedType {
610            context: "CurrentPositionLiftPercent100ths",
611        }),
612    }
613}
614
615/// Decode the `CurrentPositionTiltPercent100ths` attribute value.
616///
617/// # Errors
618/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
619pub fn decode_current_position_tilt_percent100ths(
620    tlv: &[u8],
621) -> Result<Nullable<u16>, ClusterError> {
622    let mut r = TlvReader::new(tlv);
623    match r.next()? {
624        Some(Element::Scalar {
625            value: Value::Null, ..
626        }) => Ok(Nullable::Null),
627        Some(Element::Scalar {
628            value: Value::Uint(v),
629            ..
630        }) => Ok(Nullable::Value(u16::try_from(v).map_err(|_| {
631            ClusterError::InvalidLength("CurrentPositionTiltPercent100ths")
632        })?)),
633        _ => Err(ClusterError::UnexpectedType {
634            context: "CurrentPositionTiltPercent100ths",
635        }),
636    }
637}
638
639/// Decode the `Mode` attribute value.
640///
641/// # Errors
642/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
643pub fn decode_mode(tlv: &[u8]) -> Result<ModeBitmap, ClusterError> {
644    let mut r = TlvReader::new(tlv);
645    match r.next()? {
646        Some(Element::Scalar {
647            value: Value::Uint(v),
648            ..
649        }) => Ok(ModeBitmap::from_bits_retain(
650            u8::try_from(v).map_err(|_| ClusterError::InvalidLength("Mode"))?,
651        )),
652        _ => Err(ClusterError::UnexpectedType { context: "Mode" }),
653    }
654}
655
656/// Encode the `Mode` attribute value as a standalone TLV element.
657#[must_use]
658#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
659pub fn encode_mode(value: ModeBitmap) -> Vec<u8> {
660    let mut buf = Vec::new();
661    let mut w = TlvWriter::new(&mut buf);
662    w.put_uint(Tag::Anonymous, u64::from(value.bits()))
663        .expect("infallible: vec writer");
664    buf
665}
666
667/// Decode the `SafetyStatus` attribute value.
668///
669/// # Errors
670/// Returns [`ClusterError`] on a type mismatch or out-of-range value.
671pub fn decode_safety_status(tlv: &[u8]) -> Result<SafetyStatusBitmap, ClusterError> {
672    let mut r = TlvReader::new(tlv);
673    match r.next()? {
674        Some(Element::Scalar {
675            value: Value::Uint(v),
676            ..
677        }) => Ok(SafetyStatusBitmap::from_bits_retain(
678            u16::try_from(v).map_err(|_| ClusterError::InvalidLength("SafetyStatus"))?,
679        )),
680        _ => Err(ClusterError::UnexpectedType {
681            context: "SafetyStatus",
682        }),
683    }
684}
685
686/// Encode the `UpOrOpen` command request payload.
687#[must_use]
688#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
689pub fn encode_up_or_open() -> Vec<u8> {
690    let mut buf = Vec::new();
691    let mut w = TlvWriter::new(&mut buf);
692    w.start_structure(Tag::Anonymous)
693        .expect("infallible: vec writer");
694    w.end_container().expect("infallible: vec writer");
695    buf
696}
697
698/// Encode the `DownOrClose` command request payload.
699#[must_use]
700#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
701pub fn encode_down_or_close() -> Vec<u8> {
702    let mut buf = Vec::new();
703    let mut w = TlvWriter::new(&mut buf);
704    w.start_structure(Tag::Anonymous)
705        .expect("infallible: vec writer");
706    w.end_container().expect("infallible: vec writer");
707    buf
708}
709
710/// Encode the `StopMotion` command request payload.
711#[must_use]
712#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
713pub fn encode_stop_motion() -> Vec<u8> {
714    let mut buf = Vec::new();
715    let mut w = TlvWriter::new(&mut buf);
716    w.start_structure(Tag::Anonymous)
717        .expect("infallible: vec writer");
718    w.end_container().expect("infallible: vec writer");
719    buf
720}
721
722/// Encode the `GoToLiftPercentage` command request payload.
723#[must_use]
724#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
725pub fn encode_go_to_lift_percentage(lift_percent100ths_value: u16) -> Vec<u8> {
726    let mut buf = Vec::new();
727    let mut w = TlvWriter::new(&mut buf);
728    w.start_structure(Tag::Anonymous)
729        .expect("infallible: vec writer");
730    w.put_uint(Tag::Context(0), u64::from(lift_percent100ths_value))
731        .expect("infallible: vec writer");
732    w.end_container().expect("infallible: vec writer");
733    buf
734}
735
736/// Encode the `GoToTiltPercentage` command request payload.
737#[must_use]
738#[allow(clippy::expect_used, clippy::missing_panics_doc)] // Vec-backed TlvWriter is infallible.
739pub fn encode_go_to_tilt_percentage(tilt_percent100ths_value: u16) -> Vec<u8> {
740    let mut buf = Vec::new();
741    let mut w = TlvWriter::new(&mut buf);
742    w.start_structure(Tag::Anonymous)
743        .expect("infallible: vec writer");
744    w.put_uint(Tag::Context(0), u64::from(tilt_percent100ths_value))
745        .expect("infallible: vec writer");
746    w.end_container().expect("infallible: vec writer");
747    buf
748}