matc/clusters/codec/
level_control.rs

1//! Matter TLV encoders and decoders for Level Control Cluster
2//! Cluster ID: 0x0008
3//!
4//! This file is automatically generated from LevelControl.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Enum definitions
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum MoveMode {
16    /// Increase the level
17    Up = 0,
18    /// Decrease the level
19    Down = 1,
20}
21
22impl MoveMode {
23    /// Convert from u8 value
24    pub fn from_u8(value: u8) -> Option<Self> {
25        match value {
26            0 => Some(MoveMode::Up),
27            1 => Some(MoveMode::Down),
28            _ => None,
29        }
30    }
31
32    /// Convert to u8 value
33    pub fn to_u8(self) -> u8 {
34        self as u8
35    }
36}
37
38impl From<MoveMode> for u8 {
39    fn from(val: MoveMode) -> Self {
40        val as u8
41    }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45#[repr(u8)]
46pub enum StepMode {
47    /// Step upwards
48    Up = 0,
49    /// Step downwards
50    Down = 1,
51}
52
53impl StepMode {
54    /// Convert from u8 value
55    pub fn from_u8(value: u8) -> Option<Self> {
56        match value {
57            0 => Some(StepMode::Up),
58            1 => Some(StepMode::Down),
59            _ => None,
60        }
61    }
62
63    /// Convert to u8 value
64    pub fn to_u8(self) -> u8 {
65        self as u8
66    }
67}
68
69impl From<StepMode> for u8 {
70    fn from(val: StepMode) -> Self {
71        val as u8
72    }
73}
74
75// Bitmap definitions
76
77/// Options bitmap type
78pub type Options = u8;
79
80/// Constants for Options
81pub mod options {
82    /// Dependency on On/Off cluster
83    pub const EXECUTE_IF_OFF: u8 = 0x01;
84    /// Dependency on Color Control cluster
85    pub const COUPLE_COLOR_TEMP_TO_LEVEL: u8 = 0x02;
86}
87
88// Command encoders
89
90/// Encode MoveToLevel command (0x00)
91pub fn encode_move_to_level(level: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
92    let tlv = tlv::TlvItemEnc {
93        tag: 0,
94        value: tlv::TlvItemValueEnc::StructInvisible(vec![
95        (0, tlv::TlvItemValueEnc::UInt8(level)).into(),
96        (1, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
97        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
98        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
99        ]),
100    };
101    Ok(tlv.encode()?)
102}
103
104/// Encode Move command (0x01)
105pub fn encode_move_(move_mode: MoveMode, rate: Option<u8>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
106    let tlv = tlv::TlvItemEnc {
107        tag: 0,
108        value: tlv::TlvItemValueEnc::StructInvisible(vec![
109        (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
110        (1, tlv::TlvItemValueEnc::UInt8(rate.unwrap_or(0))).into(),
111        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
112        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
113        ]),
114    };
115    Ok(tlv.encode()?)
116}
117
118/// Encode Step command (0x02)
119pub fn encode_step(step_mode: StepMode, step_size: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
120    let tlv = tlv::TlvItemEnc {
121        tag: 0,
122        value: tlv::TlvItemValueEnc::StructInvisible(vec![
123        (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
124        (1, tlv::TlvItemValueEnc::UInt8(step_size)).into(),
125        (2, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
126        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
127        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
128        ]),
129    };
130    Ok(tlv.encode()?)
131}
132
133/// Encode Stop command (0x03)
134pub fn encode_stop(options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
135    let tlv = tlv::TlvItemEnc {
136        tag: 0,
137        value: tlv::TlvItemValueEnc::StructInvisible(vec![
138        (0, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
139        (1, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
140        ]),
141    };
142    Ok(tlv.encode()?)
143}
144
145/// Encode MoveToLevelWithOnOff command (0x04)
146pub fn encode_move_to_level_with_on_off(level: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
147    let tlv = tlv::TlvItemEnc {
148        tag: 0,
149        value: tlv::TlvItemValueEnc::StructInvisible(vec![
150        (0, tlv::TlvItemValueEnc::UInt8(level)).into(),
151        (1, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
152        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
153        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
154        ]),
155    };
156    Ok(tlv.encode()?)
157}
158
159/// Encode MoveWithOnOff command (0x05)
160pub fn encode_move_with_on_off(move_mode: MoveMode, rate: Option<u8>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
161    let tlv = tlv::TlvItemEnc {
162        tag: 0,
163        value: tlv::TlvItemValueEnc::StructInvisible(vec![
164        (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
165        (1, tlv::TlvItemValueEnc::UInt8(rate.unwrap_or(0))).into(),
166        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
167        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
168        ]),
169    };
170    Ok(tlv.encode()?)
171}
172
173/// Encode StepWithOnOff command (0x06)
174pub fn encode_step_with_on_off(step_mode: StepMode, step_size: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
175    let tlv = tlv::TlvItemEnc {
176        tag: 0,
177        value: tlv::TlvItemValueEnc::StructInvisible(vec![
178        (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
179        (1, tlv::TlvItemValueEnc::UInt8(step_size)).into(),
180        (2, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
181        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
182        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
183        ]),
184    };
185    Ok(tlv.encode()?)
186}
187
188/// Encode StopWithOnOff command (0x07)
189pub fn encode_stop_with_on_off(options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
190    let tlv = tlv::TlvItemEnc {
191        tag: 0,
192        value: tlv::TlvItemValueEnc::StructInvisible(vec![
193        (0, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
194        (1, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
195        ]),
196    };
197    Ok(tlv.encode()?)
198}
199
200/// Encode MoveToClosestFrequency command (0x08)
201pub fn encode_move_to_closest_frequency(frequency: u16) -> anyhow::Result<Vec<u8>> {
202    let tlv = tlv::TlvItemEnc {
203        tag: 0,
204        value: tlv::TlvItemValueEnc::StructInvisible(vec![
205        (0, tlv::TlvItemValueEnc::UInt16(frequency)).into(),
206        ]),
207    };
208    Ok(tlv.encode()?)
209}
210
211// Attribute decoders
212
213/// Decode CurrentLevel attribute (0x0000)
214pub fn decode_current_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
215    if let tlv::TlvItemValue::Int(v) = inp {
216        Ok(Some(*v as u8))
217    } else {
218        Ok(None)
219    }
220}
221
222/// Decode RemainingTime attribute (0x0001)
223pub fn decode_remaining_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
224    if let tlv::TlvItemValue::Int(v) = inp {
225        Ok(*v as u16)
226    } else {
227        Err(anyhow::anyhow!("Expected UInt16"))
228    }
229}
230
231/// Decode MinLevel attribute (0x0002)
232pub fn decode_min_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
233    if let tlv::TlvItemValue::Int(v) = inp {
234        Ok(*v as u8)
235    } else {
236        Err(anyhow::anyhow!("Expected UInt8"))
237    }
238}
239
240/// Decode MaxLevel attribute (0x0003)
241pub fn decode_max_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
242    if let tlv::TlvItemValue::Int(v) = inp {
243        Ok(*v as u8)
244    } else {
245        Err(anyhow::anyhow!("Expected UInt8"))
246    }
247}
248
249/// Decode CurrentFrequency attribute (0x0004)
250pub fn decode_current_frequency(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
251    if let tlv::TlvItemValue::Int(v) = inp {
252        Ok(*v as u16)
253    } else {
254        Err(anyhow::anyhow!("Expected UInt16"))
255    }
256}
257
258/// Decode MinFrequency attribute (0x0005)
259pub fn decode_min_frequency(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
260    if let tlv::TlvItemValue::Int(v) = inp {
261        Ok(*v as u16)
262    } else {
263        Err(anyhow::anyhow!("Expected UInt16"))
264    }
265}
266
267/// Decode MaxFrequency attribute (0x0006)
268pub fn decode_max_frequency(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
269    if let tlv::TlvItemValue::Int(v) = inp {
270        Ok(*v as u16)
271    } else {
272        Err(anyhow::anyhow!("Expected UInt16"))
273    }
274}
275
276/// Decode Options attribute (0x000F)
277pub fn decode_options(inp: &tlv::TlvItemValue) -> anyhow::Result<Options> {
278    if let tlv::TlvItemValue::Int(v) = inp {
279        Ok(*v as u8)
280    } else {
281        Err(anyhow::anyhow!("Expected Integer"))
282    }
283}
284
285/// Decode OnOffTransitionTime attribute (0x0010)
286pub fn decode_on_off_transition_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
287    if let tlv::TlvItemValue::Int(v) = inp {
288        Ok(*v as u16)
289    } else {
290        Err(anyhow::anyhow!("Expected UInt16"))
291    }
292}
293
294/// Decode OnLevel attribute (0x0011)
295pub fn decode_on_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
296    if let tlv::TlvItemValue::Int(v) = inp {
297        Ok(Some(*v as u8))
298    } else {
299        Ok(None)
300    }
301}
302
303/// Decode OnTransitionTime attribute (0x0012)
304pub fn decode_on_transition_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
305    if let tlv::TlvItemValue::Int(v) = inp {
306        Ok(Some(*v as u16))
307    } else {
308        Ok(None)
309    }
310}
311
312/// Decode OffTransitionTime attribute (0x0013)
313pub fn decode_off_transition_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
314    if let tlv::TlvItemValue::Int(v) = inp {
315        Ok(Some(*v as u16))
316    } else {
317        Ok(None)
318    }
319}
320
321/// Decode DefaultMoveRate attribute (0x0014)
322pub fn decode_default_move_rate(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
323    if let tlv::TlvItemValue::Int(v) = inp {
324        Ok(Some(*v as u8))
325    } else {
326        Ok(None)
327    }
328}
329
330/// Decode StartUpCurrentLevel attribute (0x4000)
331pub fn decode_start_up_current_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
332    if let tlv::TlvItemValue::Int(v) = inp {
333        Ok(Some(*v as u8))
334    } else {
335        Ok(None)
336    }
337}
338
339
340// JSON dispatcher function
341
342/// Decode attribute value and return as JSON string
343///
344/// # Parameters
345/// * `cluster_id` - The cluster identifier
346/// * `attribute_id` - The attribute identifier
347/// * `tlv_value` - The TLV value to decode
348///
349/// # Returns
350/// JSON string representation of the decoded value or error
351pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
352    // Verify this is the correct cluster
353    if cluster_id != 0x0008 {
354        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0008, got {}\"}}", cluster_id);
355    }
356
357    match attribute_id {
358        0x0000 => {
359            match decode_current_level(tlv_value) {
360                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
361                Err(e) => format!("{{\"error\": \"{}\"}}", e),
362            }
363        }
364        0x0001 => {
365            match decode_remaining_time(tlv_value) {
366                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
367                Err(e) => format!("{{\"error\": \"{}\"}}", e),
368            }
369        }
370        0x0002 => {
371            match decode_min_level(tlv_value) {
372                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
373                Err(e) => format!("{{\"error\": \"{}\"}}", e),
374            }
375        }
376        0x0003 => {
377            match decode_max_level(tlv_value) {
378                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
379                Err(e) => format!("{{\"error\": \"{}\"}}", e),
380            }
381        }
382        0x0004 => {
383            match decode_current_frequency(tlv_value) {
384                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
385                Err(e) => format!("{{\"error\": \"{}\"}}", e),
386            }
387        }
388        0x0005 => {
389            match decode_min_frequency(tlv_value) {
390                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
391                Err(e) => format!("{{\"error\": \"{}\"}}", e),
392            }
393        }
394        0x0006 => {
395            match decode_max_frequency(tlv_value) {
396                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
397                Err(e) => format!("{{\"error\": \"{}\"}}", e),
398            }
399        }
400        0x000F => {
401            match decode_options(tlv_value) {
402                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
403                Err(e) => format!("{{\"error\": \"{}\"}}", e),
404            }
405        }
406        0x0010 => {
407            match decode_on_off_transition_time(tlv_value) {
408                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
409                Err(e) => format!("{{\"error\": \"{}\"}}", e),
410            }
411        }
412        0x0011 => {
413            match decode_on_level(tlv_value) {
414                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
415                Err(e) => format!("{{\"error\": \"{}\"}}", e),
416            }
417        }
418        0x0012 => {
419            match decode_on_transition_time(tlv_value) {
420                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
421                Err(e) => format!("{{\"error\": \"{}\"}}", e),
422            }
423        }
424        0x0013 => {
425            match decode_off_transition_time(tlv_value) {
426                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
427                Err(e) => format!("{{\"error\": \"{}\"}}", e),
428            }
429        }
430        0x0014 => {
431            match decode_default_move_rate(tlv_value) {
432                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
433                Err(e) => format!("{{\"error\": \"{}\"}}", e),
434            }
435        }
436        0x4000 => {
437            match decode_start_up_current_level(tlv_value) {
438                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
439                Err(e) => format!("{{\"error\": \"{}\"}}", e),
440            }
441        }
442        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
443    }
444}
445
446/// Get list of all attributes supported by this cluster
447///
448/// # Returns
449/// Vector of tuples containing (attribute_id, attribute_name)
450pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
451    vec![
452        (0x0000, "CurrentLevel"),
453        (0x0001, "RemainingTime"),
454        (0x0002, "MinLevel"),
455        (0x0003, "MaxLevel"),
456        (0x0004, "CurrentFrequency"),
457        (0x0005, "MinFrequency"),
458        (0x0006, "MaxFrequency"),
459        (0x000F, "Options"),
460        (0x0010, "OnOffTransitionTime"),
461        (0x0011, "OnLevel"),
462        (0x0012, "OnTransitionTime"),
463        (0x0013, "OffTransitionTime"),
464        (0x0014, "DefaultMoveRate"),
465        (0x4000, "StartUpCurrentLevel"),
466    ]
467}
468