1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum EndProductType {
18 Rollershade = 0,
20 Romanshade = 1,
22 Balloonshade = 2,
24 Wovenwood = 3,
26 Pleatedshade = 4,
28 Cellularshade = 5,
30 Layeredshade = 6,
32 Layeredshade2d = 7,
34 Sheershade = 8,
36 Tiltonlyinteriorblind = 9,
38 Interiorblind = 10,
40 Verticalblindstripcurtain = 11,
42 Interiorvenetianblind = 12,
44 Exteriorvenetianblind = 13,
46 Lateralleftcurtain = 14,
48 Lateralrightcurtain = 15,
50 Centralcurtain = 16,
52 Rollershutter = 17,
54 Exteriorverticalscreen = 18,
56 Awningterracepatio = 19,
58 Awningverticalscreen = 20,
60 Tiltonlypergola = 21,
62 Swingingshutter = 22,
64 Slidingshutter = 23,
66 Unknown = 255,
68}
69
70impl EndProductType {
71 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 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 = 0,
120 Rollershade2motor = 1,
122 Rollershadeexterior = 2,
124 Rollershadeexterior2motor = 3,
126 Drapery = 4,
128 Awning = 5,
130 Shutter = 6,
132 Tiltblindtiltonly = 7,
134 Tiltblindliftandtilt = 8,
136 Projectorscreen = 9,
138 Unknown = 255,
140}
141
142impl Type {
143 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 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
173pub type ConfigStatus = u8;
177
178pub mod configstatus {
180 pub const OPERATIONAL: u8 = 0x01;
182 pub const ONLINE_RESERVED: u8 = 0x02;
183 pub const LIFT_MOVEMENT_REVERSED: u8 = 0x04;
185 pub const LIFT_POSITION_AWARE: u8 = 0x08;
187 pub const TILT_POSITION_AWARE: u8 = 0x10;
189 pub const LIFT_ENCODER_CONTROLLED: u8 = 0x20;
191 pub const TILT_ENCODER_CONTROLLED: u8 = 0x40;
193}
194
195pub type Mode = u8;
197
198pub mod mode {
200 pub const MOTOR_DIRECTION_REVERSED: u8 = 0x01;
202 pub const CALIBRATION_MODE: u8 = 0x02;
204 pub const MAINTENANCE_MODE: u8 = 0x04;
206 pub const LED_FEEDBACK: u8 = 0x08;
208}
209
210pub type OperationalStatus = u8;
212
213pub type SafetyStatus = u16;
215
216pub mod safetystatus {
218 pub const REMOTE_LOCKOUT: u16 = 0x01;
220 pub const TAMPER_DETECTION: u16 = 0x02;
222 pub const FAILED_COMMUNICATION: u16 = 0x04;
224 pub const POSITION_FAILURE: u16 = 0x08;
226 pub const THERMAL_PROTECTION: u16 = 0x10;
228 pub const OBSTACLE_DETECTED: u16 = 0x20;
230 pub const POWER: u16 = 0x40;
232 pub const STOP_INPUT: u16 = 0x80;
234 pub const MOTOR_JAMMED: u16 = 0x100;
236 pub const HARDWARE_FAILURE: u16 = 0x200;
238 pub const MANUAL_OPERATION: u16 = 0x400;
240 pub const PROTECTION: u16 = 0x800;
242}
243
244pub 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
257pub 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
268pub 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
279pub 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
288pub 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
297pub 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
306pub 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
315pub 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
324pub 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
333pub 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
342pub 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
351pub 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
360pub 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
369pub 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
378pub 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
387pub 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
397pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
409 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
503pub 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
526pub 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
581pub 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
589pub 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
595pub 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
601pub 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
607pub 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
613pub 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
619pub 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
625pub 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
631pub 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
637pub 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
643pub 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
649pub 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
655pub 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
661pub 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
667pub 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
673pub 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
679pub 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
685pub 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
691pub 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