matc/clusters/codec/
window_covering.rs

1//! Matter TLV encoders and decoders for Window Covering Cluster
2//! Cluster ID: 0x0102
3//!
4//! This file is automatically generated from WindowCovering.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 EndProductType {
18    /// Simple Roller Shade
19    Rollershade = 0,
20    /// Roman Shade
21    Romanshade = 1,
22    /// Balloon Shade
23    Balloonshade = 2,
24    /// Woven Wood
25    Wovenwood = 3,
26    /// Pleated Shade
27    Pleatedshade = 4,
28    /// Cellular Shade
29    Cellularshade = 5,
30    /// Layered Shade
31    Layeredshade = 6,
32    /// Layered Shade 2D
33    Layeredshade2d = 7,
34    /// Sheer Shade
35    Sheershade = 8,
36    /// Tilt Only Interior Blind
37    Tiltonlyinteriorblind = 9,
38    /// Interior Blind
39    Interiorblind = 10,
40    /// Vertical Blind, Strip Curtain
41    Verticalblindstripcurtain = 11,
42    /// Interior Venetian Blind
43    Interiorvenetianblind = 12,
44    /// Exterior Venetian Blind
45    Exteriorvenetianblind = 13,
46    /// Lateral Left Curtain
47    Lateralleftcurtain = 14,
48    /// Lateral Right Curtain
49    Lateralrightcurtain = 15,
50    /// Central Curtain
51    Centralcurtain = 16,
52    /// Roller Shutter
53    Rollershutter = 17,
54    /// Exterior Vertical Screen
55    Exteriorverticalscreen = 18,
56    /// Awning Terrace (Patio)
57    Awningterracepatio = 19,
58    /// Awning Vertical Screen
59    Awningverticalscreen = 20,
60    /// Tilt Only Pergola
61    Tiltonlypergola = 21,
62    /// Swinging Shutter
63    Swingingshutter = 22,
64    /// Sliding Shutter
65    Slidingshutter = 23,
66    /// Unknown
67    Unknown = 255,
68}
69
70impl EndProductType {
71    /// Convert from u8 value
72    pub fn from_u8(value: u8) -> Option<Self> {
73        match value {
74            0 => Some(EndProductType::Rollershade),
75            1 => Some(EndProductType::Romanshade),
76            2 => Some(EndProductType::Balloonshade),
77            3 => Some(EndProductType::Wovenwood),
78            4 => Some(EndProductType::Pleatedshade),
79            5 => Some(EndProductType::Cellularshade),
80            6 => Some(EndProductType::Layeredshade),
81            7 => Some(EndProductType::Layeredshade2d),
82            8 => Some(EndProductType::Sheershade),
83            9 => Some(EndProductType::Tiltonlyinteriorblind),
84            10 => Some(EndProductType::Interiorblind),
85            11 => Some(EndProductType::Verticalblindstripcurtain),
86            12 => Some(EndProductType::Interiorvenetianblind),
87            13 => Some(EndProductType::Exteriorvenetianblind),
88            14 => Some(EndProductType::Lateralleftcurtain),
89            15 => Some(EndProductType::Lateralrightcurtain),
90            16 => Some(EndProductType::Centralcurtain),
91            17 => Some(EndProductType::Rollershutter),
92            18 => Some(EndProductType::Exteriorverticalscreen),
93            19 => Some(EndProductType::Awningterracepatio),
94            20 => Some(EndProductType::Awningverticalscreen),
95            21 => Some(EndProductType::Tiltonlypergola),
96            22 => Some(EndProductType::Swingingshutter),
97            23 => Some(EndProductType::Slidingshutter),
98            255 => Some(EndProductType::Unknown),
99            _ => None,
100        }
101    }
102
103    /// Convert to u8 value
104    pub fn to_u8(self) -> u8 {
105        self as u8
106    }
107}
108
109impl From<EndProductType> for u8 {
110    fn from(val: EndProductType) -> Self {
111        val as u8
112    }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
116#[repr(u8)]
117pub enum Type {
118    /// RollerShade
119    Rollershade = 0,
120    /// RollerShade - 2 Motor
121    Rollershade2motor = 1,
122    /// RollerShade - Exterior
123    Rollershadeexterior = 2,
124    /// RollerShade - Exterior - 2 Motor
125    Rollershadeexterior2motor = 3,
126    /// Drapery (curtain)
127    Drapery = 4,
128    /// Awning
129    Awning = 5,
130    /// Shutter
131    Shutter = 6,
132    /// Tilt Blind - Tilt Only
133    Tiltblindtiltonly = 7,
134    /// Tilt Blind - Lift & Tilt
135    Tiltblindliftandtilt = 8,
136    /// Projector Screen
137    Projectorscreen = 9,
138    /// Unknown
139    Unknown = 255,
140}
141
142impl Type {
143    /// Convert from u8 value
144    pub fn from_u8(value: u8) -> Option<Self> {
145        match value {
146            0 => Some(Type::Rollershade),
147            1 => Some(Type::Rollershade2motor),
148            2 => Some(Type::Rollershadeexterior),
149            3 => Some(Type::Rollershadeexterior2motor),
150            4 => Some(Type::Drapery),
151            5 => Some(Type::Awning),
152            6 => Some(Type::Shutter),
153            7 => Some(Type::Tiltblindtiltonly),
154            8 => Some(Type::Tiltblindliftandtilt),
155            9 => Some(Type::Projectorscreen),
156            255 => Some(Type::Unknown),
157            _ => None,
158        }
159    }
160
161    /// Convert to u8 value
162    pub fn to_u8(self) -> u8 {
163        self as u8
164    }
165}
166
167impl From<Type> for u8 {
168    fn from(val: Type) -> Self {
169        val as u8
170    }
171}
172
173// Bitmap definitions
174
175/// ConfigStatus bitmap type
176pub type ConfigStatus = u8;
177
178/// Constants for ConfigStatus
179pub mod configstatus {
180    /// Device is operational.
181    pub const OPERATIONAL: u8 = 0x01;
182    pub const ONLINE_RESERVED: u8 = 0x02;
183    /// The lift movement is reversed.
184    pub const LIFT_MOVEMENT_REVERSED: u8 = 0x04;
185    /// Supports the PositionAwareLift feature (PA_LF).
186    pub const LIFT_POSITION_AWARE: u8 = 0x08;
187    /// Supports the PositionAwareTilt feature (PA_TL).
188    pub const TILT_POSITION_AWARE: u8 = 0x10;
189    /// Uses an encoder for lift.
190    pub const LIFT_ENCODER_CONTROLLED: u8 = 0x20;
191    /// Uses an encoder for tilt.
192    pub const TILT_ENCODER_CONTROLLED: u8 = 0x40;
193}
194
195/// Mode bitmap type
196pub type Mode = u8;
197
198/// Constants for Mode
199pub mod mode {
200    /// Reverse the lift direction.
201    pub const MOTOR_DIRECTION_REVERSED: u8 = 0x01;
202    /// Perform a calibration.
203    pub const CALIBRATION_MODE: u8 = 0x02;
204    /// Freeze all motions for maintenance.
205    pub const MAINTENANCE_MODE: u8 = 0x04;
206    /// Control the LEDs feedback.
207    pub const LED_FEEDBACK: u8 = 0x08;
208}
209
210/// OperationalStatus bitmap type
211pub type OperationalStatus = u8;
212
213/// SafetyStatus bitmap type
214pub type SafetyStatus = u16;
215
216/// Constants for SafetyStatus
217pub mod safetystatus {
218    /// Movement commands are ignored (locked out). e.g. not granted authorization, outside some time/date range.
219    pub const REMOTE_LOCKOUT: u16 = 0x01;
220    /// Tampering detected on sensors or any other safety equipment. Ex: a device has been forcedly moved without its actuator(s).
221    pub const TAMPER_DETECTION: u16 = 0x02;
222    /// Communication failure to sensors or other safety equipment.
223    pub const FAILED_COMMUNICATION: u16 = 0x04;
224    /// Device has failed to reach the desired position. e.g. with position aware device, time expired before TargetPosition is reached.
225    pub const POSITION_FAILURE: u16 = 0x08;
226    /// Motor(s) and/or electric circuit thermal protection activated.
227    pub const THERMAL_PROTECTION: u16 = 0x10;
228    /// An obstacle is preventing actuator movement.
229    pub const OBSTACLE_DETECTED: u16 = 0x20;
230    /// Device has power related issue or limitation e.g. device is running w/ the help of a backup battery or power might not be fully available at the moment.
231    pub const POWER: u16 = 0x40;
232    /// Local safety sensor (not a direct obstacle) is preventing movements (e.g. Safety EU Standard EN60335).
233    pub const STOP_INPUT: u16 = 0x80;
234    /// Mechanical problem related to the motor(s) detected.
235    pub const MOTOR_JAMMED: u16 = 0x100;
236    /// PCB, fuse and other electrics problems.
237    pub const HARDWARE_FAILURE: u16 = 0x200;
238    /// Actuator is manually operated and is preventing actuator movement (e.g. actuator is disengaged/decoupled).
239    pub const MANUAL_OPERATION: u16 = 0x400;
240    /// Protection is activated.
241    pub const PROTECTION: u16 = 0x800;
242}
243
244// Command encoders
245
246/// Encode GoToLiftPercentage command (0x05)
247pub fn encode_go_to_lift_percentage(lift_percent100ths_value: u8) -> anyhow::Result<Vec<u8>> {
248    let tlv = tlv::TlvItemEnc {
249        tag: 0,
250        value: tlv::TlvItemValueEnc::StructInvisible(vec![
251        (0, tlv::TlvItemValueEnc::UInt8(lift_percent100ths_value)).into(),
252        ]),
253    };
254    Ok(tlv.encode()?)
255}
256
257/// Encode GoToTiltPercentage command (0x08)
258pub fn encode_go_to_tilt_percentage(tilt_percent100ths_value: u8) -> anyhow::Result<Vec<u8>> {
259    let tlv = tlv::TlvItemEnc {
260        tag: 0,
261        value: tlv::TlvItemValueEnc::StructInvisible(vec![
262        (0, tlv::TlvItemValueEnc::UInt8(tilt_percent100ths_value)).into(),
263        ]),
264    };
265    Ok(tlv.encode()?)
266}
267
268// Attribute decoders
269
270/// Decode Type attribute (0x0000)
271pub fn decode_type_(inp: &tlv::TlvItemValue) -> anyhow::Result<Type> {
272    if let tlv::TlvItemValue::Int(v) = inp {
273        Type::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
274    } else {
275        Err(anyhow::anyhow!("Expected Integer"))
276    }
277}
278
279/// Decode NumberOfActuationsLift attribute (0x0005)
280pub fn decode_number_of_actuations_lift(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
281    if let tlv::TlvItemValue::Int(v) = inp {
282        Ok(*v as u16)
283    } else {
284        Err(anyhow::anyhow!("Expected UInt16"))
285    }
286}
287
288/// Decode NumberOfActuationsTilt attribute (0x0006)
289pub fn decode_number_of_actuations_tilt(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
290    if let tlv::TlvItemValue::Int(v) = inp {
291        Ok(*v as u16)
292    } else {
293        Err(anyhow::anyhow!("Expected UInt16"))
294    }
295}
296
297/// Decode ConfigStatus attribute (0x0007)
298pub fn decode_config_status(inp: &tlv::TlvItemValue) -> anyhow::Result<ConfigStatus> {
299    if let tlv::TlvItemValue::Int(v) = inp {
300        Ok(*v as u8)
301    } else {
302        Err(anyhow::anyhow!("Expected Integer"))
303    }
304}
305
306/// Decode CurrentPositionLiftPercentage attribute (0x0008)
307pub fn decode_current_position_lift_percentage(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
308    if let tlv::TlvItemValue::Int(v) = inp {
309        Ok(Some(*v as u8))
310    } else {
311        Ok(None)
312    }
313}
314
315/// Decode CurrentPositionTiltPercentage attribute (0x0009)
316pub fn decode_current_position_tilt_percentage(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
317    if let tlv::TlvItemValue::Int(v) = inp {
318        Ok(Some(*v as u8))
319    } else {
320        Ok(None)
321    }
322}
323
324/// Decode OperationalStatus attribute (0x000A)
325pub fn decode_operational_status(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationalStatus> {
326    if let tlv::TlvItemValue::Int(v) = inp {
327        Ok(*v as u8)
328    } else {
329        Err(anyhow::anyhow!("Expected Integer"))
330    }
331}
332
333/// Decode TargetPositionLiftPercent100ths attribute (0x000B)
334pub fn decode_target_position_lift_percent100ths(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
335    if let tlv::TlvItemValue::Int(v) = inp {
336        Ok(Some(*v as u8))
337    } else {
338        Ok(None)
339    }
340}
341
342/// Decode TargetPositionTiltPercent100ths attribute (0x000C)
343pub fn decode_target_position_tilt_percent100ths(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
344    if let tlv::TlvItemValue::Int(v) = inp {
345        Ok(Some(*v as u8))
346    } else {
347        Ok(None)
348    }
349}
350
351/// Decode EndProductType attribute (0x000D)
352pub fn decode_end_product_type(inp: &tlv::TlvItemValue) -> anyhow::Result<EndProductType> {
353    if let tlv::TlvItemValue::Int(v) = inp {
354        EndProductType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
355    } else {
356        Err(anyhow::anyhow!("Expected Integer"))
357    }
358}
359
360/// Decode CurrentPositionLiftPercent100ths attribute (0x000E)
361pub fn decode_current_position_lift_percent100ths(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
362    if let tlv::TlvItemValue::Int(v) = inp {
363        Ok(Some(*v as u8))
364    } else {
365        Ok(None)
366    }
367}
368
369/// Decode CurrentPositionTiltPercent100ths attribute (0x000F)
370pub fn decode_current_position_tilt_percent100ths(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
371    if let tlv::TlvItemValue::Int(v) = inp {
372        Ok(Some(*v as u8))
373    } else {
374        Ok(None)
375    }
376}
377
378/// Decode Mode attribute (0x0017)
379pub fn decode_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<Mode> {
380    if let tlv::TlvItemValue::Int(v) = inp {
381        Ok(*v as u8)
382    } else {
383        Err(anyhow::anyhow!("Expected Integer"))
384    }
385}
386
387/// Decode SafetyStatus attribute (0x001A)
388pub fn decode_safety_status(inp: &tlv::TlvItemValue) -> anyhow::Result<SafetyStatus> {
389    if let tlv::TlvItemValue::Int(v) = inp {
390        Ok(*v as u16)
391    } else {
392        Err(anyhow::anyhow!("Expected Integer"))
393    }
394}
395
396
397// JSON dispatcher function
398
399/// Decode attribute value and return as JSON string
400///
401/// # Parameters
402/// * `cluster_id` - The cluster identifier
403/// * `attribute_id` - The attribute identifier
404/// * `tlv_value` - The TLV value to decode
405///
406/// # Returns
407/// JSON string representation of the decoded value or error
408pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
409    // Verify this is the correct cluster
410    if cluster_id != 0x0102 {
411        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0102, got {}\"}}", cluster_id);
412    }
413
414    match attribute_id {
415        0x0000 => {
416            match decode_type_(tlv_value) {
417                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
418                Err(e) => format!("{{\"error\": \"{}\"}}", e),
419            }
420        }
421        0x0005 => {
422            match decode_number_of_actuations_lift(tlv_value) {
423                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
424                Err(e) => format!("{{\"error\": \"{}\"}}", e),
425            }
426        }
427        0x0006 => {
428            match decode_number_of_actuations_tilt(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        0x0007 => {
434            match decode_config_status(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        0x0008 => {
440            match decode_current_position_lift_percentage(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        0x0009 => {
446            match decode_current_position_tilt_percentage(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        0x000A => {
452            match decode_operational_status(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        0x000B => {
458            match decode_target_position_lift_percent100ths(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        0x000C => {
464            match decode_target_position_tilt_percent100ths(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        0x000D => {
470            match decode_end_product_type(tlv_value) {
471                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
472                Err(e) => format!("{{\"error\": \"{}\"}}", e),
473            }
474        }
475        0x000E => {
476            match decode_current_position_lift_percent100ths(tlv_value) {
477                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
478                Err(e) => format!("{{\"error\": \"{}\"}}", e),
479            }
480        }
481        0x000F => {
482            match decode_current_position_tilt_percent100ths(tlv_value) {
483                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
484                Err(e) => format!("{{\"error\": \"{}\"}}", e),
485            }
486        }
487        0x0017 => {
488            match decode_mode(tlv_value) {
489                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
490                Err(e) => format!("{{\"error\": \"{}\"}}", e),
491            }
492        }
493        0x001A => {
494            match decode_safety_status(tlv_value) {
495                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
496                Err(e) => format!("{{\"error\": \"{}\"}}", e),
497            }
498        }
499        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
500    }
501}
502
503/// Get list of all attributes supported by this cluster
504///
505/// # Returns
506/// Vector of tuples containing (attribute_id, attribute_name)
507pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
508    vec![
509        (0x0000, "Type"),
510        (0x0005, "NumberOfActuationsLift"),
511        (0x0006, "NumberOfActuationsTilt"),
512        (0x0007, "ConfigStatus"),
513        (0x0008, "CurrentPositionLiftPercentage"),
514        (0x0009, "CurrentPositionTiltPercentage"),
515        (0x000A, "OperationalStatus"),
516        (0x000B, "TargetPositionLiftPercent100ths"),
517        (0x000C, "TargetPositionTiltPercent100ths"),
518        (0x000D, "EndProductType"),
519        (0x000E, "CurrentPositionLiftPercent100ths"),
520        (0x000F, "CurrentPositionTiltPercent100ths"),
521        (0x0017, "Mode"),
522        (0x001A, "SafetyStatus"),
523    ]
524}
525
526// Command listing
527
528pub fn get_command_list() -> Vec<(u32, &'static str)> {
529    vec![
530        (0x00, "UpOrOpen"),
531        (0x01, "DownOrClose"),
532        (0x02, "StopMotion"),
533        (0x05, "GoToLiftPercentage"),
534        (0x08, "GoToTiltPercentage"),
535    ]
536}
537
538pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
539    match cmd_id {
540        0x00 => Some("UpOrOpen"),
541        0x01 => Some("DownOrClose"),
542        0x02 => Some("StopMotion"),
543        0x05 => Some("GoToLiftPercentage"),
544        0x08 => Some("GoToTiltPercentage"),
545        _ => None,
546    }
547}
548
549pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
550    match cmd_id {
551        0x00 => Some(vec![]),
552        0x01 => Some(vec![]),
553        0x02 => Some(vec![]),
554        0x05 => Some(vec![
555            crate::clusters::codec::CommandField { tag: 0, name: "lift_percent100ths_value", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
556        ]),
557        0x08 => Some(vec![
558            crate::clusters::codec::CommandField { tag: 0, name: "tilt_percent100ths_value", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
559        ]),
560        _ => None,
561    }
562}
563
564pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
565    match cmd_id {
566        0x00 => Ok(vec![]),
567        0x01 => Ok(vec![]),
568        0x02 => Ok(vec![]),
569        0x05 => {
570        let lift_percent100ths_value = crate::clusters::codec::json_util::get_u8(args, "lift_percent100ths_value")?;
571        encode_go_to_lift_percentage(lift_percent100ths_value)
572        }
573        0x08 => {
574        let tilt_percent100ths_value = crate::clusters::codec::json_util::get_u8(args, "tilt_percent100ths_value")?;
575        encode_go_to_tilt_percentage(tilt_percent100ths_value)
576        }
577        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
578    }
579}
580
581// Typed facade (invokes + reads)
582
583/// Invoke `UpOrOpen` command on cluster `Window Covering`.
584pub async fn up_or_open(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
585    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_CMD_ID_UPOROPEN, &[]).await?;
586    Ok(())
587}
588
589/// Invoke `DownOrClose` command on cluster `Window Covering`.
590pub async fn down_or_close(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
591    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_CMD_ID_DOWNORCLOSE, &[]).await?;
592    Ok(())
593}
594
595/// Invoke `StopMotion` command on cluster `Window Covering`.
596pub async fn stop_motion(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
597    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_CMD_ID_STOPMOTION, &[]).await?;
598    Ok(())
599}
600
601/// Invoke `GoToLiftPercentage` command on cluster `Window Covering`.
602pub async fn go_to_lift_percentage(conn: &crate::controller::Connection, endpoint: u16, lift_percent100ths_value: u8) -> anyhow::Result<()> {
603    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_CMD_ID_GOTOLIFTPERCENTAGE, &encode_go_to_lift_percentage(lift_percent100ths_value)?).await?;
604    Ok(())
605}
606
607/// Invoke `GoToTiltPercentage` command on cluster `Window Covering`.
608pub async fn go_to_tilt_percentage(conn: &crate::controller::Connection, endpoint: u16, tilt_percent100ths_value: u8) -> anyhow::Result<()> {
609    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_CMD_ID_GOTOTILTPERCENTAGE, &encode_go_to_tilt_percentage(tilt_percent100ths_value)?).await?;
610    Ok(())
611}
612
613/// Read `Type` attribute from cluster `Window Covering`.
614pub async fn read_type_(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Type> {
615    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_TYPE).await?;
616    decode_type_(&tlv)
617}
618
619/// Read `NumberOfActuationsLift` attribute from cluster `Window Covering`.
620pub async fn read_number_of_actuations_lift(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
621    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_NUMBEROFACTUATIONSLIFT).await?;
622    decode_number_of_actuations_lift(&tlv)
623}
624
625/// Read `NumberOfActuationsTilt` attribute from cluster `Window Covering`.
626pub async fn read_number_of_actuations_tilt(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
627    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_NUMBEROFACTUATIONSTILT).await?;
628    decode_number_of_actuations_tilt(&tlv)
629}
630
631/// Read `ConfigStatus` attribute from cluster `Window Covering`.
632pub async fn read_config_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ConfigStatus> {
633    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_CONFIGSTATUS).await?;
634    decode_config_status(&tlv)
635}
636
637/// Read `CurrentPositionLiftPercentage` attribute from cluster `Window Covering`.
638pub async fn read_current_position_lift_percentage(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
639    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_CURRENTPOSITIONLIFTPERCENTAGE).await?;
640    decode_current_position_lift_percentage(&tlv)
641}
642
643/// Read `CurrentPositionTiltPercentage` attribute from cluster `Window Covering`.
644pub async fn read_current_position_tilt_percentage(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
645    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_CURRENTPOSITIONTILTPERCENTAGE).await?;
646    decode_current_position_tilt_percentage(&tlv)
647}
648
649/// Read `OperationalStatus` attribute from cluster `Window Covering`.
650pub async fn read_operational_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<OperationalStatus> {
651    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_OPERATIONALSTATUS).await?;
652    decode_operational_status(&tlv)
653}
654
655/// Read `TargetPositionLiftPercent100ths` attribute from cluster `Window Covering`.
656pub async fn read_target_position_lift_percent100ths(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
657    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_TARGETPOSITIONLIFTPERCENT100THS).await?;
658    decode_target_position_lift_percent100ths(&tlv)
659}
660
661/// Read `TargetPositionTiltPercent100ths` attribute from cluster `Window Covering`.
662pub async fn read_target_position_tilt_percent100ths(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
663    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_TARGETPOSITIONTILTPERCENT100THS).await?;
664    decode_target_position_tilt_percent100ths(&tlv)
665}
666
667/// Read `EndProductType` attribute from cluster `Window Covering`.
668pub async fn read_end_product_type(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<EndProductType> {
669    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_ENDPRODUCTTYPE).await?;
670    decode_end_product_type(&tlv)
671}
672
673/// Read `CurrentPositionLiftPercent100ths` attribute from cluster `Window Covering`.
674pub async fn read_current_position_lift_percent100ths(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
675    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_CURRENTPOSITIONLIFTPERCENT100THS).await?;
676    decode_current_position_lift_percent100ths(&tlv)
677}
678
679/// Read `CurrentPositionTiltPercent100ths` attribute from cluster `Window Covering`.
680pub async fn read_current_position_tilt_percent100ths(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
681    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_CURRENTPOSITIONTILTPERCENT100THS).await?;
682    decode_current_position_tilt_percent100ths(&tlv)
683}
684
685/// Read `Mode` attribute from cluster `Window Covering`.
686pub async fn read_mode(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Mode> {
687    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_MODE).await?;
688    decode_mode(&tlv)
689}
690
691/// Read `SafetyStatus` attribute from cluster `Window Covering`.
692pub async fn read_safety_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<SafetyStatus> {
693    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WINDOW_COVERING, crate::clusters::defs::CLUSTER_WINDOW_COVERING_ATTR_ID_SAFETYSTATUS).await?;
694    decode_safety_status(&tlv)
695}
696