1#![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
17pub const CLUSTER_ID: u32 = 0x0202;
19pub const CLUSTER_REVISION: u16 = 6;
21
22pub mod command_id {
24 pub const STEP: u32 = 0x00;
26}
27
28pub mod attribute_id {
30 pub const FAN_MODE: u32 = 0x0000;
32 pub const FAN_MODE_SEQUENCE: u32 = 0x0001;
34 pub const PERCENT_SETTING: u32 = 0x0002;
36 pub const PERCENT_CURRENT: u32 = 0x0003;
38 pub const SPEED_MAX: u32 = 0x0004;
40 pub const SPEED_SETTING: u32 = 0x0005;
42 pub const SPEED_CURRENT: u32 = 0x0006;
44 pub const ROCK_SUPPORT: u32 = 0x0007;
46 pub const ROCK_SETTING: u32 = 0x0008;
48 pub const WIND_SUPPORT: u32 = 0x0009;
50 pub const WIND_SETTING: u32 = 0x000A;
52 pub const AIRFLOW_DIRECTION: u32 = 0x000B;
54}
55
56bitflags::bitflags! {
57 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
59 pub struct Feature: u32 {
60 const SPD = 1 << 0;
62 const AUT = 1 << 1;
64 const RCK = 1 << 2;
66 const WND = 1 << 3;
68 const STEP = 1 << 4;
70 const DIR = 1 << 5;
72 }
73}
74
75#[derive(Copy, Clone, Debug, PartialEq, Eq)]
77pub enum AirflowDirectionEnum {
78 Forward,
80 Reverse,
82 Unknown(u8),
84}
85
86impl AirflowDirectionEnum {
87 #[must_use]
89 pub fn from_raw(v: u8) -> Self {
90 match v {
91 0 => Self::Forward,
92 1 => Self::Reverse,
93 other => Self::Unknown(other),
94 }
95 }
96 #[must_use]
98 pub fn to_raw(self) -> u8 {
99 match self {
100 Self::Forward => 0,
101 Self::Reverse => 1,
102 Self::Unknown(v) => v,
103 }
104 }
105}
106
107#[derive(Copy, Clone, Debug, PartialEq, Eq)]
109pub enum FanModeEnum {
110 Off,
112 Low,
114 Medium,
116 High,
118 On,
120 Auto,
122 Smart,
124 Unknown(u8),
126}
127
128impl FanModeEnum {
129 #[must_use]
131 pub fn from_raw(v: u8) -> Self {
132 match v {
133 0 => Self::Off,
134 1 => Self::Low,
135 2 => Self::Medium,
136 3 => Self::High,
137 4 => Self::On,
138 5 => Self::Auto,
139 6 => Self::Smart,
140 other => Self::Unknown(other),
141 }
142 }
143 #[must_use]
145 pub fn to_raw(self) -> u8 {
146 match self {
147 Self::Off => 0,
148 Self::Low => 1,
149 Self::Medium => 2,
150 Self::High => 3,
151 Self::On => 4,
152 Self::Auto => 5,
153 Self::Smart => 6,
154 Self::Unknown(v) => v,
155 }
156 }
157}
158
159#[derive(Copy, Clone, Debug, PartialEq, Eq)]
161pub enum FanModeSequenceEnum {
162 OffLowMedHigh,
164 OffLowHigh,
166 OffLowMedHighAuto,
168 OffLowHighAuto,
170 OffHighAuto,
172 OffHigh,
174 Unknown(u8),
176}
177
178impl FanModeSequenceEnum {
179 #[must_use]
181 pub fn from_raw(v: u8) -> Self {
182 match v {
183 0 => Self::OffLowMedHigh,
184 1 => Self::OffLowHigh,
185 2 => Self::OffLowMedHighAuto,
186 3 => Self::OffLowHighAuto,
187 4 => Self::OffHighAuto,
188 5 => Self::OffHigh,
189 other => Self::Unknown(other),
190 }
191 }
192 #[must_use]
194 pub fn to_raw(self) -> u8 {
195 match self {
196 Self::OffLowMedHigh => 0,
197 Self::OffLowHigh => 1,
198 Self::OffLowMedHighAuto => 2,
199 Self::OffLowHighAuto => 3,
200 Self::OffHighAuto => 4,
201 Self::OffHigh => 5,
202 Self::Unknown(v) => v,
203 }
204 }
205}
206
207bitflags::bitflags! {
208 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
210 pub struct RockBitmap: u8 {
211 const ROCK_LEFT_RIGHT = 1 << 0;
213 const ROCK_UP_DOWN = 1 << 1;
215 const ROCK_ROUND = 1 << 2;
217 }
218}
219
220#[derive(Copy, Clone, Debug, PartialEq, Eq)]
222pub enum StepDirectionEnum {
223 Increase,
225 Decrease,
227 Unknown(u8),
229}
230
231impl StepDirectionEnum {
232 #[must_use]
234 pub fn from_raw(v: u8) -> Self {
235 match v {
236 0 => Self::Increase,
237 1 => Self::Decrease,
238 other => Self::Unknown(other),
239 }
240 }
241 #[must_use]
243 pub fn to_raw(self) -> u8 {
244 match self {
245 Self::Increase => 0,
246 Self::Decrease => 1,
247 Self::Unknown(v) => v,
248 }
249 }
250}
251
252bitflags::bitflags! {
253 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
255 pub struct WindBitmap: u8 {
256 const SLEEP_WIND = 1 << 0;
258 const NATURAL_WIND = 1 << 1;
260 }
261}
262
263pub fn decode_fan_mode(tlv: &[u8]) -> Result<FanModeEnum, ClusterError> {
268 let mut r = TlvReader::new(tlv);
269 match r.next()? {
270 Some(Element::Scalar {
271 value: Value::Uint(v),
272 ..
273 }) => Ok(FanModeEnum::from_raw(
274 u8::try_from(v).map_err(|_| ClusterError::InvalidLength("FanMode"))?,
275 )),
276 _ => Err(ClusterError::UnexpectedType { context: "FanMode" }),
277 }
278}
279
280#[must_use]
282#[allow(clippy::expect_used, clippy::missing_panics_doc)] pub fn encode_fan_mode(value: FanModeEnum) -> Vec<u8> {
284 let mut buf = Vec::new();
285 let mut w = TlvWriter::new(&mut buf);
286 w.put_uint(Tag::Anonymous, u64::from(value.to_raw()))
287 .expect("infallible: vec writer");
288 buf
289}
290
291pub fn decode_fan_mode_sequence(tlv: &[u8]) -> Result<FanModeSequenceEnum, ClusterError> {
296 let mut r = TlvReader::new(tlv);
297 match r.next()? {
298 Some(Element::Scalar {
299 value: Value::Uint(v),
300 ..
301 }) => Ok(FanModeSequenceEnum::from_raw(
302 u8::try_from(v).map_err(|_| ClusterError::InvalidLength("FanModeSequence"))?,
303 )),
304 _ => Err(ClusterError::UnexpectedType {
305 context: "FanModeSequence",
306 }),
307 }
308}
309
310pub fn decode_percent_setting(tlv: &[u8]) -> Result<Nullable<u8>, ClusterError> {
315 let mut r = TlvReader::new(tlv);
316 match r.next()? {
317 Some(Element::Scalar {
318 value: Value::Null, ..
319 }) => Ok(Nullable::Null),
320 Some(Element::Scalar {
321 value: Value::Uint(v),
322 ..
323 }) => {
324 Ok(Nullable::Value(u8::try_from(v).map_err(|_| {
325 ClusterError::InvalidLength("PercentSetting")
326 })?))
327 }
328 _ => Err(ClusterError::UnexpectedType {
329 context: "PercentSetting",
330 }),
331 }
332}
333
334#[must_use]
336#[allow(clippy::expect_used, clippy::missing_panics_doc)] pub fn encode_percent_setting(value: Nullable<u8>) -> Vec<u8> {
338 let mut buf = Vec::new();
339 let mut w = TlvWriter::new(&mut buf);
340 match value {
341 Nullable::Null => w.put_null(Tag::Anonymous).expect("infallible: vec writer"),
342 Nullable::Value(value) => {
343 w.put_uint(Tag::Anonymous, u64::from(value))
344 .expect("infallible: vec writer");
345 }
346 }
347 buf
348}
349
350pub fn decode_percent_current(tlv: &[u8]) -> Result<u8, ClusterError> {
355 let mut r = TlvReader::new(tlv);
356 match r.next()? {
357 Some(Element::Scalar {
358 value: Value::Uint(v),
359 ..
360 }) => Ok(u8::try_from(v).map_err(|_| ClusterError::InvalidLength("PercentCurrent"))?),
361 _ => Err(ClusterError::UnexpectedType {
362 context: "PercentCurrent",
363 }),
364 }
365}
366
367pub fn decode_speed_max(tlv: &[u8]) -> Result<u8, ClusterError> {
372 let mut r = TlvReader::new(tlv);
373 match r.next()? {
374 Some(Element::Scalar {
375 value: Value::Uint(v),
376 ..
377 }) => Ok(u8::try_from(v).map_err(|_| ClusterError::InvalidLength("SpeedMax"))?),
378 _ => Err(ClusterError::UnexpectedType {
379 context: "SpeedMax",
380 }),
381 }
382}
383
384pub fn decode_speed_setting(tlv: &[u8]) -> Result<Nullable<u8>, ClusterError> {
389 let mut r = TlvReader::new(tlv);
390 match r.next()? {
391 Some(Element::Scalar {
392 value: Value::Null, ..
393 }) => Ok(Nullable::Null),
394 Some(Element::Scalar {
395 value: Value::Uint(v),
396 ..
397 }) => {
398 Ok(Nullable::Value(u8::try_from(v).map_err(|_| {
399 ClusterError::InvalidLength("SpeedSetting")
400 })?))
401 }
402 _ => Err(ClusterError::UnexpectedType {
403 context: "SpeedSetting",
404 }),
405 }
406}
407
408#[must_use]
410#[allow(clippy::expect_used, clippy::missing_panics_doc)] pub fn encode_speed_setting(value: Nullable<u8>) -> Vec<u8> {
412 let mut buf = Vec::new();
413 let mut w = TlvWriter::new(&mut buf);
414 match value {
415 Nullable::Null => w.put_null(Tag::Anonymous).expect("infallible: vec writer"),
416 Nullable::Value(value) => {
417 w.put_uint(Tag::Anonymous, u64::from(value))
418 .expect("infallible: vec writer");
419 }
420 }
421 buf
422}
423
424pub fn decode_speed_current(tlv: &[u8]) -> Result<u8, ClusterError> {
429 let mut r = TlvReader::new(tlv);
430 match r.next()? {
431 Some(Element::Scalar {
432 value: Value::Uint(v),
433 ..
434 }) => Ok(u8::try_from(v).map_err(|_| ClusterError::InvalidLength("SpeedCurrent"))?),
435 _ => Err(ClusterError::UnexpectedType {
436 context: "SpeedCurrent",
437 }),
438 }
439}
440
441pub fn decode_rock_support(tlv: &[u8]) -> Result<RockBitmap, ClusterError> {
446 let mut r = TlvReader::new(tlv);
447 match r.next()? {
448 Some(Element::Scalar {
449 value: Value::Uint(v),
450 ..
451 }) => Ok(RockBitmap::from_bits_retain(
452 u8::try_from(v).map_err(|_| ClusterError::InvalidLength("RockSupport"))?,
453 )),
454 _ => Err(ClusterError::UnexpectedType {
455 context: "RockSupport",
456 }),
457 }
458}
459
460pub fn decode_rock_setting(tlv: &[u8]) -> Result<RockBitmap, ClusterError> {
465 let mut r = TlvReader::new(tlv);
466 match r.next()? {
467 Some(Element::Scalar {
468 value: Value::Uint(v),
469 ..
470 }) => Ok(RockBitmap::from_bits_retain(
471 u8::try_from(v).map_err(|_| ClusterError::InvalidLength("RockSetting"))?,
472 )),
473 _ => Err(ClusterError::UnexpectedType {
474 context: "RockSetting",
475 }),
476 }
477}
478
479#[must_use]
481#[allow(clippy::expect_used, clippy::missing_panics_doc)] pub fn encode_rock_setting(value: RockBitmap) -> Vec<u8> {
483 let mut buf = Vec::new();
484 let mut w = TlvWriter::new(&mut buf);
485 w.put_uint(Tag::Anonymous, u64::from(value.bits()))
486 .expect("infallible: vec writer");
487 buf
488}
489
490pub fn decode_wind_support(tlv: &[u8]) -> Result<WindBitmap, ClusterError> {
495 let mut r = TlvReader::new(tlv);
496 match r.next()? {
497 Some(Element::Scalar {
498 value: Value::Uint(v),
499 ..
500 }) => Ok(WindBitmap::from_bits_retain(
501 u8::try_from(v).map_err(|_| ClusterError::InvalidLength("WindSupport"))?,
502 )),
503 _ => Err(ClusterError::UnexpectedType {
504 context: "WindSupport",
505 }),
506 }
507}
508
509pub fn decode_wind_setting(tlv: &[u8]) -> Result<WindBitmap, ClusterError> {
514 let mut r = TlvReader::new(tlv);
515 match r.next()? {
516 Some(Element::Scalar {
517 value: Value::Uint(v),
518 ..
519 }) => Ok(WindBitmap::from_bits_retain(
520 u8::try_from(v).map_err(|_| ClusterError::InvalidLength("WindSetting"))?,
521 )),
522 _ => Err(ClusterError::UnexpectedType {
523 context: "WindSetting",
524 }),
525 }
526}
527
528#[must_use]
530#[allow(clippy::expect_used, clippy::missing_panics_doc)] pub fn encode_wind_setting(value: WindBitmap) -> Vec<u8> {
532 let mut buf = Vec::new();
533 let mut w = TlvWriter::new(&mut buf);
534 w.put_uint(Tag::Anonymous, u64::from(value.bits()))
535 .expect("infallible: vec writer");
536 buf
537}
538
539pub fn decode_airflow_direction(tlv: &[u8]) -> Result<AirflowDirectionEnum, ClusterError> {
544 let mut r = TlvReader::new(tlv);
545 match r.next()? {
546 Some(Element::Scalar {
547 value: Value::Uint(v),
548 ..
549 }) => Ok(AirflowDirectionEnum::from_raw(
550 u8::try_from(v).map_err(|_| ClusterError::InvalidLength("AirflowDirection"))?,
551 )),
552 _ => Err(ClusterError::UnexpectedType {
553 context: "AirflowDirection",
554 }),
555 }
556}
557
558#[must_use]
560#[allow(clippy::expect_used, clippy::missing_panics_doc)] pub fn encode_airflow_direction(value: AirflowDirectionEnum) -> Vec<u8> {
562 let mut buf = Vec::new();
563 let mut w = TlvWriter::new(&mut buf);
564 w.put_uint(Tag::Anonymous, u64::from(value.to_raw()))
565 .expect("infallible: vec writer");
566 buf
567}
568
569#[must_use]
571#[allow(clippy::expect_used, clippy::missing_panics_doc)] pub fn encode_step(
573 direction: StepDirectionEnum,
574 wrap: Option<bool>,
575 lowest_off: Option<bool>,
576) -> Vec<u8> {
577 let mut buf = Vec::new();
578 let mut w = TlvWriter::new(&mut buf);
579 w.start_structure(Tag::Anonymous)
580 .expect("infallible: vec writer");
581 w.put_uint(Tag::Context(0), u64::from(direction.to_raw()))
582 .expect("infallible: vec writer");
583 if let Some(wrap) = wrap {
584 w.put_bool(Tag::Context(1), wrap)
585 .expect("infallible: vec writer");
586 }
587 if let Some(lowest_off) = lowest_off {
588 w.put_bool(Tag::Context(2), lowest_off)
589 .expect("infallible: vec writer");
590 }
591 w.end_container().expect("infallible: vec writer");
592 buf
593}