matc/clusters/codec/
color_control.rs

1//! Matter TLV encoders and decoders for Color Control Cluster
2//! Cluster ID: 0x0300
3//!
4//! This file is automatically generated from ColorControl.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 ColorLoopAction {
16    /// De-activate the color loop.
17    Deactivate = 0,
18    /// Activate the color loop from the value in the ColorLoopStartEnhancedHue field.
19    Activatefromcolorloopstartenhancedhue = 1,
20    /// Activate the color loop from the value of the EnhancedCurrentHue attribute.
21    Activatefromenhancedcurrenthue = 2,
22}
23
24impl ColorLoopAction {
25    /// Convert from u8 value
26    pub fn from_u8(value: u8) -> Option<Self> {
27        match value {
28            0 => Some(ColorLoopAction::Deactivate),
29            1 => Some(ColorLoopAction::Activatefromcolorloopstartenhancedhue),
30            2 => Some(ColorLoopAction::Activatefromenhancedcurrenthue),
31            _ => None,
32        }
33    }
34
35    /// Convert to u8 value
36    pub fn to_u8(self) -> u8 {
37        self as u8
38    }
39}
40
41impl From<ColorLoopAction> for u8 {
42    fn from(val: ColorLoopAction) -> Self {
43        val as u8
44    }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48#[repr(u8)]
49pub enum ColorLoopDirection {
50    /// Decrement the hue in the color loop.
51    Decrement = 0,
52    /// Increment the hue in the color loop.
53    Increment = 1,
54}
55
56impl ColorLoopDirection {
57    /// Convert from u8 value
58    pub fn from_u8(value: u8) -> Option<Self> {
59        match value {
60            0 => Some(ColorLoopDirection::Decrement),
61            1 => Some(ColorLoopDirection::Increment),
62            _ => None,
63        }
64    }
65
66    /// Convert to u8 value
67    pub fn to_u8(self) -> u8 {
68        self as u8
69    }
70}
71
72impl From<ColorLoopDirection> for u8 {
73    fn from(val: ColorLoopDirection) -> Self {
74        val as u8
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
79#[repr(u8)]
80pub enum ColorMode {
81    /// The current hue and saturation attributes determine the color.
82    Currenthueandcurrentsaturation = 0,
83    /// The current X and Y attributes determine the color.
84    Currentxandcurrenty = 1,
85    /// The color temperature attribute determines the color.
86    Colortemperaturemireds = 2,
87}
88
89impl ColorMode {
90    /// Convert from u8 value
91    pub fn from_u8(value: u8) -> Option<Self> {
92        match value {
93            0 => Some(ColorMode::Currenthueandcurrentsaturation),
94            1 => Some(ColorMode::Currentxandcurrenty),
95            2 => Some(ColorMode::Colortemperaturemireds),
96            _ => None,
97        }
98    }
99
100    /// Convert to u8 value
101    pub fn to_u8(self) -> u8 {
102        self as u8
103    }
104}
105
106impl From<ColorMode> for u8 {
107    fn from(val: ColorMode) -> Self {
108        val as u8
109    }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
113#[repr(u8)]
114pub enum Direction {
115    /// Shortest distance
116    Shortest = 0,
117    /// Longest distance
118    Longest = 1,
119    /// Up
120    Up = 2,
121    /// Down
122    Down = 3,
123}
124
125impl Direction {
126    /// Convert from u8 value
127    pub fn from_u8(value: u8) -> Option<Self> {
128        match value {
129            0 => Some(Direction::Shortest),
130            1 => Some(Direction::Longest),
131            2 => Some(Direction::Up),
132            3 => Some(Direction::Down),
133            _ => None,
134        }
135    }
136
137    /// Convert to u8 value
138    pub fn to_u8(self) -> u8 {
139        self as u8
140    }
141}
142
143impl From<Direction> for u8 {
144    fn from(val: Direction) -> Self {
145        val as u8
146    }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
150#[repr(u8)]
151pub enum DriftCompensation {
152    /// There is no compensation.
153    None = 0,
154    /// The compensation is based on other or unknown mechanism.
155    Otherorunknown = 1,
156    /// The compensation is based on temperature monitoring.
157    Temperaturemonitoring = 2,
158    /// The compensation is based on optical luminance monitoring and feedback.
159    Opticalluminancemonitoringandfeedback = 3,
160    /// The compensation is based on optical color monitoring and feedback.
161    Opticalcolormonitoringandfeedback = 4,
162}
163
164impl DriftCompensation {
165    /// Convert from u8 value
166    pub fn from_u8(value: u8) -> Option<Self> {
167        match value {
168            0 => Some(DriftCompensation::None),
169            1 => Some(DriftCompensation::Otherorunknown),
170            2 => Some(DriftCompensation::Temperaturemonitoring),
171            3 => Some(DriftCompensation::Opticalluminancemonitoringandfeedback),
172            4 => Some(DriftCompensation::Opticalcolormonitoringandfeedback),
173            _ => None,
174        }
175    }
176
177    /// Convert to u8 value
178    pub fn to_u8(self) -> u8 {
179        self as u8
180    }
181}
182
183impl From<DriftCompensation> for u8 {
184    fn from(val: DriftCompensation) -> Self {
185        val as u8
186    }
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
190#[repr(u8)]
191pub enum EnhancedColorMode {
192    /// The current hue and saturation attributes determine the color.
193    Currenthueandcurrentsaturation = 0,
194    /// The current X and Y attributes determine the color.
195    Currentxandcurrenty = 1,
196    /// The color temperature attribute determines the color.
197    Colortemperaturemireds = 2,
198    /// The enhanced current hue and saturation attributes determine the color.
199    Enhancedcurrenthueandcurrentsaturation = 3,
200}
201
202impl EnhancedColorMode {
203    /// Convert from u8 value
204    pub fn from_u8(value: u8) -> Option<Self> {
205        match value {
206            0 => Some(EnhancedColorMode::Currenthueandcurrentsaturation),
207            1 => Some(EnhancedColorMode::Currentxandcurrenty),
208            2 => Some(EnhancedColorMode::Colortemperaturemireds),
209            3 => Some(EnhancedColorMode::Enhancedcurrenthueandcurrentsaturation),
210            _ => None,
211        }
212    }
213
214    /// Convert to u8 value
215    pub fn to_u8(self) -> u8 {
216        self as u8
217    }
218}
219
220impl From<EnhancedColorMode> for u8 {
221    fn from(val: EnhancedColorMode) -> Self {
222        val as u8
223    }
224}
225
226#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
227#[repr(u8)]
228pub enum MoveMode {
229    /// Stop the movement
230    Stop = 0,
231    /// Move in an upwards direction
232    Up = 1,
233    /// Move in a downwards direction
234    Down = 3,
235}
236
237impl MoveMode {
238    /// Convert from u8 value
239    pub fn from_u8(value: u8) -> Option<Self> {
240        match value {
241            0 => Some(MoveMode::Stop),
242            1 => Some(MoveMode::Up),
243            3 => Some(MoveMode::Down),
244            _ => None,
245        }
246    }
247
248    /// Convert to u8 value
249    pub fn to_u8(self) -> u8 {
250        self as u8
251    }
252}
253
254impl From<MoveMode> for u8 {
255    fn from(val: MoveMode) -> Self {
256        val as u8
257    }
258}
259
260#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
261#[repr(u8)]
262pub enum StepMode {
263    /// Step in an upwards direction
264    Up = 1,
265    /// Step in a downwards direction
266    Down = 3,
267}
268
269impl StepMode {
270    /// Convert from u8 value
271    pub fn from_u8(value: u8) -> Option<Self> {
272        match value {
273            1 => Some(StepMode::Up),
274            3 => Some(StepMode::Down),
275            _ => None,
276        }
277    }
278
279    /// Convert to u8 value
280    pub fn to_u8(self) -> u8 {
281        self as u8
282    }
283}
284
285impl From<StepMode> for u8 {
286    fn from(val: StepMode) -> Self {
287        val as u8
288    }
289}
290
291// Bitmap definitions
292
293/// ColorCapabilities bitmap type
294pub type ColorCapabilities = u8;
295
296/// Constants for ColorCapabilities
297pub mod colorcapabilities {
298    /// Supports color specification via hue/saturation.
299    pub const HUE_SATURATION: u8 = 0x01;
300    /// Enhanced hue is supported.
301    pub const ENHANCED_HUE: u8 = 0x02;
302    /// Color loop is supported.
303    pub const COLOR_LOOP: u8 = 0x04;
304    /// Supports color specification via XY.
305    pub const XY: u8 = 0x08;
306    /// Supports color specification via color temperature.
307    pub const COLOR_TEMPERATURE: u8 = 0x10;
308}
309
310/// Options bitmap type
311pub type Options = u8;
312
313/// Constants for Options
314pub mod options {
315    /// Dependency on On/Off cluster
316    pub const EXECUTE_IF_OFF: u8 = 0x01;
317}
318
319/// UpdateFlags bitmap type
320pub type UpdateFlags = u8;
321
322/// Constants for UpdateFlags
323pub mod updateflags {
324    /// Device adheres to the associated action field.
325    pub const UPDATE_ACTION: u8 = 0x01;
326    /// Device updates the associated direction attribute.
327    pub const UPDATE_DIRECTION: u8 = 0x02;
328    /// Device updates the associated time attribute.
329    pub const UPDATE_TIME: u8 = 0x04;
330    /// Device updates the associated start hue attribute.
331    pub const UPDATE_START_HUE: u8 = 0x08;
332}
333
334// Command encoders
335
336/// Encode MoveToHue command (0x00)
337pub fn encode_move_to_hue(hue: u8, direction: Direction, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
338    let tlv = tlv::TlvItemEnc {
339        tag: 0,
340        value: tlv::TlvItemValueEnc::StructInvisible(vec![
341        (0, tlv::TlvItemValueEnc::UInt8(hue)).into(),
342        (1, tlv::TlvItemValueEnc::UInt8(direction.to_u8())).into(),
343        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
344        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
345        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
346        ]),
347    };
348    Ok(tlv.encode()?)
349}
350
351/// Encode MoveHue command (0x01)
352pub fn encode_move_hue(move_mode: MoveMode, rate: u8, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
353    let tlv = tlv::TlvItemEnc {
354        tag: 0,
355        value: tlv::TlvItemValueEnc::StructInvisible(vec![
356        (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
357        (1, tlv::TlvItemValueEnc::UInt8(rate)).into(),
358        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
359        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
360        ]),
361    };
362    Ok(tlv.encode()?)
363}
364
365/// Encode StepHue command (0x02)
366pub fn encode_step_hue(step_mode: StepMode, step_size: u8, transition_time: u8, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
367    let tlv = tlv::TlvItemEnc {
368        tag: 0,
369        value: tlv::TlvItemValueEnc::StructInvisible(vec![
370        (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
371        (1, tlv::TlvItemValueEnc::UInt8(step_size)).into(),
372        (2, tlv::TlvItemValueEnc::UInt8(transition_time)).into(),
373        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
374        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
375        ]),
376    };
377    Ok(tlv.encode()?)
378}
379
380/// Encode MoveToSaturation command (0x03)
381pub fn encode_move_to_saturation(saturation: u8, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
382    let tlv = tlv::TlvItemEnc {
383        tag: 0,
384        value: tlv::TlvItemValueEnc::StructInvisible(vec![
385        (0, tlv::TlvItemValueEnc::UInt8(saturation)).into(),
386        (1, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
387        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
388        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
389        ]),
390    };
391    Ok(tlv.encode()?)
392}
393
394/// Encode MoveSaturation command (0x04)
395pub fn encode_move_saturation(move_mode: MoveMode, rate: u8, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
396    let tlv = tlv::TlvItemEnc {
397        tag: 0,
398        value: tlv::TlvItemValueEnc::StructInvisible(vec![
399        (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
400        (1, tlv::TlvItemValueEnc::UInt8(rate)).into(),
401        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
402        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
403        ]),
404    };
405    Ok(tlv.encode()?)
406}
407
408/// Encode StepSaturation command (0x05)
409pub fn encode_step_saturation(step_mode: StepMode, step_size: u8, transition_time: u8, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
410    let tlv = tlv::TlvItemEnc {
411        tag: 0,
412        value: tlv::TlvItemValueEnc::StructInvisible(vec![
413        (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
414        (1, tlv::TlvItemValueEnc::UInt8(step_size)).into(),
415        (2, tlv::TlvItemValueEnc::UInt8(transition_time)).into(),
416        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
417        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
418        ]),
419    };
420    Ok(tlv.encode()?)
421}
422
423/// Encode MoveToHueAndSaturation command (0x06)
424pub fn encode_move_to_hue_and_saturation(hue: u8, saturation: u8, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
425    let tlv = tlv::TlvItemEnc {
426        tag: 0,
427        value: tlv::TlvItemValueEnc::StructInvisible(vec![
428        (0, tlv::TlvItemValueEnc::UInt8(hue)).into(),
429        (1, tlv::TlvItemValueEnc::UInt8(saturation)).into(),
430        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
431        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
432        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
433        ]),
434    };
435    Ok(tlv.encode()?)
436}
437
438/// Encode MoveToColor command (0x07)
439pub fn encode_move_to_color(color_x: u16, color_y: u16, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
440    let tlv = tlv::TlvItemEnc {
441        tag: 0,
442        value: tlv::TlvItemValueEnc::StructInvisible(vec![
443        (0, tlv::TlvItemValueEnc::UInt16(color_x)).into(),
444        (1, tlv::TlvItemValueEnc::UInt16(color_y)).into(),
445        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
446        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
447        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
448        ]),
449    };
450    Ok(tlv.encode()?)
451}
452
453/// Encode MoveColor command (0x08)
454pub fn encode_move_color(rate_x: i16, rate_y: i16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
455    let tlv = tlv::TlvItemEnc {
456        tag: 0,
457        value: tlv::TlvItemValueEnc::StructInvisible(vec![
458        (0, tlv::TlvItemValueEnc::Int16(rate_x)).into(),
459        (1, tlv::TlvItemValueEnc::Int16(rate_y)).into(),
460        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
461        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
462        ]),
463    };
464    Ok(tlv.encode()?)
465}
466
467/// Encode StepColor command (0x09)
468pub fn encode_step_color(step_x: i16, step_y: i16, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
469    let tlv = tlv::TlvItemEnc {
470        tag: 0,
471        value: tlv::TlvItemValueEnc::StructInvisible(vec![
472        (0, tlv::TlvItemValueEnc::Int16(step_x)).into(),
473        (1, tlv::TlvItemValueEnc::Int16(step_y)).into(),
474        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
475        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
476        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
477        ]),
478    };
479    Ok(tlv.encode()?)
480}
481
482/// Encode MoveToColorTemperature command (0x0A)
483pub fn encode_move_to_color_temperature(color_temperature_mireds: u16, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
484    let tlv = tlv::TlvItemEnc {
485        tag: 0,
486        value: tlv::TlvItemValueEnc::StructInvisible(vec![
487        (0, tlv::TlvItemValueEnc::UInt16(color_temperature_mireds)).into(),
488        (1, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
489        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
490        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
491        ]),
492    };
493    Ok(tlv.encode()?)
494}
495
496/// Encode EnhancedMoveToHue command (0x40)
497pub fn encode_enhanced_move_to_hue(enhanced_hue: u16, direction: Direction, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
498    let tlv = tlv::TlvItemEnc {
499        tag: 0,
500        value: tlv::TlvItemValueEnc::StructInvisible(vec![
501        (0, tlv::TlvItemValueEnc::UInt16(enhanced_hue)).into(),
502        (1, tlv::TlvItemValueEnc::UInt8(direction.to_u8())).into(),
503        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
504        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
505        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
506        ]),
507    };
508    Ok(tlv.encode()?)
509}
510
511/// Encode EnhancedMoveHue command (0x41)
512pub fn encode_enhanced_move_hue(move_mode: MoveMode, rate: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
513    let tlv = tlv::TlvItemEnc {
514        tag: 0,
515        value: tlv::TlvItemValueEnc::StructInvisible(vec![
516        (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
517        (1, tlv::TlvItemValueEnc::UInt16(rate)).into(),
518        (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
519        (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
520        ]),
521    };
522    Ok(tlv.encode()?)
523}
524
525/// Encode EnhancedStepHue command (0x42)
526pub fn encode_enhanced_step_hue(step_mode: StepMode, step_size: u16, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
527    let tlv = tlv::TlvItemEnc {
528        tag: 0,
529        value: tlv::TlvItemValueEnc::StructInvisible(vec![
530        (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
531        (1, tlv::TlvItemValueEnc::UInt16(step_size)).into(),
532        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
533        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
534        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
535        ]),
536    };
537    Ok(tlv.encode()?)
538}
539
540/// Encode EnhancedMoveToHueAndSaturation command (0x43)
541pub fn encode_enhanced_move_to_hue_and_saturation(enhanced_hue: u16, saturation: u8, transition_time: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
542    let tlv = tlv::TlvItemEnc {
543        tag: 0,
544        value: tlv::TlvItemValueEnc::StructInvisible(vec![
545        (0, tlv::TlvItemValueEnc::UInt16(enhanced_hue)).into(),
546        (1, tlv::TlvItemValueEnc::UInt8(saturation)).into(),
547        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
548        (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
549        (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
550        ]),
551    };
552    Ok(tlv.encode()?)
553}
554
555/// Encode ColorLoopSet command (0x44)
556pub fn encode_color_loop_set(update_flags: UpdateFlags, action: ColorLoopAction, direction: ColorLoopDirection, time: u16, start_hue: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
557    let tlv = tlv::TlvItemEnc {
558        tag: 0,
559        value: tlv::TlvItemValueEnc::StructInvisible(vec![
560        (0, tlv::TlvItemValueEnc::UInt8(update_flags)).into(),
561        (1, tlv::TlvItemValueEnc::UInt8(action.to_u8())).into(),
562        (2, tlv::TlvItemValueEnc::UInt8(direction.to_u8())).into(),
563        (3, tlv::TlvItemValueEnc::UInt16(time)).into(),
564        (4, tlv::TlvItemValueEnc::UInt16(start_hue)).into(),
565        (5, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
566        (6, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
567        ]),
568    };
569    Ok(tlv.encode()?)
570}
571
572/// Encode StopMoveStep command (0x47)
573pub fn encode_stop_move_step(options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
574    let tlv = tlv::TlvItemEnc {
575        tag: 0,
576        value: tlv::TlvItemValueEnc::StructInvisible(vec![
577        (0, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
578        (1, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
579        ]),
580    };
581    Ok(tlv.encode()?)
582}
583
584/// Encode MoveColorTemperature command (0x4B)
585pub fn encode_move_color_temperature(move_mode: MoveMode, rate: u16, color_temperature_minimum_mireds: u16, color_temperature_maximum_mireds: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
586    let tlv = tlv::TlvItemEnc {
587        tag: 0,
588        value: tlv::TlvItemValueEnc::StructInvisible(vec![
589        (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
590        (1, tlv::TlvItemValueEnc::UInt16(rate)).into(),
591        (2, tlv::TlvItemValueEnc::UInt16(color_temperature_minimum_mireds)).into(),
592        (3, tlv::TlvItemValueEnc::UInt16(color_temperature_maximum_mireds)).into(),
593        (4, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
594        (5, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
595        ]),
596    };
597    Ok(tlv.encode()?)
598}
599
600/// Encode StepColorTemperature command (0x4C)
601pub fn encode_step_color_temperature(step_mode: StepMode, step_size: u16, transition_time: u16, color_temperature_minimum_mireds: u16, color_temperature_maximum_mireds: u16, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
602    let tlv = tlv::TlvItemEnc {
603        tag: 0,
604        value: tlv::TlvItemValueEnc::StructInvisible(vec![
605        (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
606        (1, tlv::TlvItemValueEnc::UInt16(step_size)).into(),
607        (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
608        (3, tlv::TlvItemValueEnc::UInt16(color_temperature_minimum_mireds)).into(),
609        (4, tlv::TlvItemValueEnc::UInt16(color_temperature_maximum_mireds)).into(),
610        (5, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
611        (6, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
612        ]),
613    };
614    Ok(tlv.encode()?)
615}
616
617// Attribute decoders
618
619/// Decode CurrentHue attribute (0x0000)
620pub fn decode_current_hue(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
621    if let tlv::TlvItemValue::Int(v) = inp {
622        Ok(*v as u8)
623    } else {
624        Err(anyhow::anyhow!("Expected UInt8"))
625    }
626}
627
628/// Decode CurrentSaturation attribute (0x0001)
629pub fn decode_current_saturation(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
630    if let tlv::TlvItemValue::Int(v) = inp {
631        Ok(*v as u8)
632    } else {
633        Err(anyhow::anyhow!("Expected UInt8"))
634    }
635}
636
637/// Decode RemainingTime attribute (0x0002)
638pub fn decode_remaining_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
639    if let tlv::TlvItemValue::Int(v) = inp {
640        Ok(*v as u16)
641    } else {
642        Err(anyhow::anyhow!("Expected UInt16"))
643    }
644}
645
646/// Decode CurrentX attribute (0x0003)
647pub fn decode_current_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
648    if let tlv::TlvItemValue::Int(v) = inp {
649        Ok(*v as u16)
650    } else {
651        Err(anyhow::anyhow!("Expected UInt16"))
652    }
653}
654
655/// Decode CurrentY attribute (0x0004)
656pub fn decode_current_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
657    if let tlv::TlvItemValue::Int(v) = inp {
658        Ok(*v as u16)
659    } else {
660        Err(anyhow::anyhow!("Expected UInt16"))
661    }
662}
663
664/// Decode DriftCompensation attribute (0x0005)
665pub fn decode_drift_compensation(inp: &tlv::TlvItemValue) -> anyhow::Result<DriftCompensation> {
666    if let tlv::TlvItemValue::Int(v) = inp {
667        DriftCompensation::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
668    } else {
669        Err(anyhow::anyhow!("Expected Integer"))
670    }
671}
672
673/// Decode CompensationText attribute (0x0006)
674pub fn decode_compensation_text(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
675    if let tlv::TlvItemValue::String(v) = inp {
676        Ok(v.clone())
677    } else {
678        Err(anyhow::anyhow!("Expected String"))
679    }
680}
681
682/// Decode ColorTemperatureMireds attribute (0x0007)
683pub fn decode_color_temperature_mireds(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
684    if let tlv::TlvItemValue::Int(v) = inp {
685        Ok(*v as u16)
686    } else {
687        Err(anyhow::anyhow!("Expected UInt16"))
688    }
689}
690
691/// Decode ColorMode attribute (0x0008)
692pub fn decode_color_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ColorMode> {
693    if let tlv::TlvItemValue::Int(v) = inp {
694        ColorMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
695    } else {
696        Err(anyhow::anyhow!("Expected Integer"))
697    }
698}
699
700/// Decode Options attribute (0x000F)
701pub fn decode_options(inp: &tlv::TlvItemValue) -> anyhow::Result<Options> {
702    if let tlv::TlvItemValue::Int(v) = inp {
703        Ok(*v as u8)
704    } else {
705        Err(anyhow::anyhow!("Expected Integer"))
706    }
707}
708
709/// Decode NumberOfPrimaries attribute (0x0010)
710pub fn decode_number_of_primaries(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
711    if let tlv::TlvItemValue::Int(v) = inp {
712        Ok(Some(*v as u8))
713    } else {
714        Ok(None)
715    }
716}
717
718/// Decode Primary1X attribute (0x0011)
719pub fn decode_primary1_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
720    if let tlv::TlvItemValue::Int(v) = inp {
721        Ok(*v as u16)
722    } else {
723        Err(anyhow::anyhow!("Expected UInt16"))
724    }
725}
726
727/// Decode Primary1Y attribute (0x0012)
728pub fn decode_primary1_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
729    if let tlv::TlvItemValue::Int(v) = inp {
730        Ok(*v as u16)
731    } else {
732        Err(anyhow::anyhow!("Expected UInt16"))
733    }
734}
735
736/// Decode Primary1Intensity attribute (0x0013)
737pub fn decode_primary1_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
738    if let tlv::TlvItemValue::Int(v) = inp {
739        Ok(Some(*v as u8))
740    } else {
741        Ok(None)
742    }
743}
744
745/// Decode Primary2X attribute (0x0015)
746pub fn decode_primary2_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
747    if let tlv::TlvItemValue::Int(v) = inp {
748        Ok(*v as u16)
749    } else {
750        Err(anyhow::anyhow!("Expected UInt16"))
751    }
752}
753
754/// Decode Primary2Y attribute (0x0016)
755pub fn decode_primary2_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
756    if let tlv::TlvItemValue::Int(v) = inp {
757        Ok(*v as u16)
758    } else {
759        Err(anyhow::anyhow!("Expected UInt16"))
760    }
761}
762
763/// Decode Primary2Intensity attribute (0x0017)
764pub fn decode_primary2_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
765    if let tlv::TlvItemValue::Int(v) = inp {
766        Ok(Some(*v as u8))
767    } else {
768        Ok(None)
769    }
770}
771
772/// Decode Primary3X attribute (0x0019)
773pub fn decode_primary3_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
774    if let tlv::TlvItemValue::Int(v) = inp {
775        Ok(*v as u16)
776    } else {
777        Err(anyhow::anyhow!("Expected UInt16"))
778    }
779}
780
781/// Decode Primary3Y attribute (0x001A)
782pub fn decode_primary3_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
783    if let tlv::TlvItemValue::Int(v) = inp {
784        Ok(*v as u16)
785    } else {
786        Err(anyhow::anyhow!("Expected UInt16"))
787    }
788}
789
790/// Decode Primary3Intensity attribute (0x001B)
791pub fn decode_primary3_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
792    if let tlv::TlvItemValue::Int(v) = inp {
793        Ok(Some(*v as u8))
794    } else {
795        Ok(None)
796    }
797}
798
799/// Decode Primary4X attribute (0x0020)
800pub fn decode_primary4_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
801    if let tlv::TlvItemValue::Int(v) = inp {
802        Ok(*v as u16)
803    } else {
804        Err(anyhow::anyhow!("Expected UInt16"))
805    }
806}
807
808/// Decode Primary4Y attribute (0x0021)
809pub fn decode_primary4_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
810    if let tlv::TlvItemValue::Int(v) = inp {
811        Ok(*v as u16)
812    } else {
813        Err(anyhow::anyhow!("Expected UInt16"))
814    }
815}
816
817/// Decode Primary4Intensity attribute (0x0022)
818pub fn decode_primary4_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
819    if let tlv::TlvItemValue::Int(v) = inp {
820        Ok(Some(*v as u8))
821    } else {
822        Ok(None)
823    }
824}
825
826/// Decode Primary5X attribute (0x0024)
827pub fn decode_primary5_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
828    if let tlv::TlvItemValue::Int(v) = inp {
829        Ok(*v as u16)
830    } else {
831        Err(anyhow::anyhow!("Expected UInt16"))
832    }
833}
834
835/// Decode Primary5Y attribute (0x0025)
836pub fn decode_primary5_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
837    if let tlv::TlvItemValue::Int(v) = inp {
838        Ok(*v as u16)
839    } else {
840        Err(anyhow::anyhow!("Expected UInt16"))
841    }
842}
843
844/// Decode Primary5Intensity attribute (0x0026)
845pub fn decode_primary5_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
846    if let tlv::TlvItemValue::Int(v) = inp {
847        Ok(Some(*v as u8))
848    } else {
849        Ok(None)
850    }
851}
852
853/// Decode Primary6X attribute (0x0028)
854pub fn decode_primary6_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
855    if let tlv::TlvItemValue::Int(v) = inp {
856        Ok(*v as u16)
857    } else {
858        Err(anyhow::anyhow!("Expected UInt16"))
859    }
860}
861
862/// Decode Primary6Y attribute (0x0029)
863pub fn decode_primary6_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
864    if let tlv::TlvItemValue::Int(v) = inp {
865        Ok(*v as u16)
866    } else {
867        Err(anyhow::anyhow!("Expected UInt16"))
868    }
869}
870
871/// Decode Primary6Intensity attribute (0x002A)
872pub fn decode_primary6_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
873    if let tlv::TlvItemValue::Int(v) = inp {
874        Ok(Some(*v as u8))
875    } else {
876        Ok(None)
877    }
878}
879
880/// Decode WhitePointX attribute (0x0030)
881pub fn decode_white_point_x(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
882    if let tlv::TlvItemValue::Int(v) = inp {
883        Ok(*v as u16)
884    } else {
885        Err(anyhow::anyhow!("Expected UInt16"))
886    }
887}
888
889/// Decode WhitePointY attribute (0x0031)
890pub fn decode_white_point_y(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
891    if let tlv::TlvItemValue::Int(v) = inp {
892        Ok(*v as u16)
893    } else {
894        Err(anyhow::anyhow!("Expected UInt16"))
895    }
896}
897
898/// Decode ColorPointRX attribute (0x0032)
899pub fn decode_color_point_rx(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
900    if let tlv::TlvItemValue::Int(v) = inp {
901        Ok(*v as u16)
902    } else {
903        Err(anyhow::anyhow!("Expected UInt16"))
904    }
905}
906
907/// Decode ColorPointRY attribute (0x0033)
908pub fn decode_color_point_ry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
909    if let tlv::TlvItemValue::Int(v) = inp {
910        Ok(*v as u16)
911    } else {
912        Err(anyhow::anyhow!("Expected UInt16"))
913    }
914}
915
916/// Decode ColorPointRIntensity attribute (0x0034)
917pub fn decode_color_point_r_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
918    if let tlv::TlvItemValue::Int(v) = inp {
919        Ok(Some(*v as u8))
920    } else {
921        Ok(None)
922    }
923}
924
925/// Decode ColorPointGX attribute (0x0036)
926pub fn decode_color_point_gx(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
927    if let tlv::TlvItemValue::Int(v) = inp {
928        Ok(*v as u16)
929    } else {
930        Err(anyhow::anyhow!("Expected UInt16"))
931    }
932}
933
934/// Decode ColorPointGY attribute (0x0037)
935pub fn decode_color_point_gy(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
936    if let tlv::TlvItemValue::Int(v) = inp {
937        Ok(*v as u16)
938    } else {
939        Err(anyhow::anyhow!("Expected UInt16"))
940    }
941}
942
943/// Decode ColorPointGIntensity attribute (0x0038)
944pub fn decode_color_point_g_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
945    if let tlv::TlvItemValue::Int(v) = inp {
946        Ok(Some(*v as u8))
947    } else {
948        Ok(None)
949    }
950}
951
952/// Decode ColorPointBX attribute (0x003A)
953pub fn decode_color_point_bx(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
954    if let tlv::TlvItemValue::Int(v) = inp {
955        Ok(*v as u16)
956    } else {
957        Err(anyhow::anyhow!("Expected UInt16"))
958    }
959}
960
961/// Decode ColorPointBY attribute (0x003B)
962pub fn decode_color_point_by(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
963    if let tlv::TlvItemValue::Int(v) = inp {
964        Ok(*v as u16)
965    } else {
966        Err(anyhow::anyhow!("Expected UInt16"))
967    }
968}
969
970/// Decode ColorPointBIntensity attribute (0x003C)
971pub fn decode_color_point_b_intensity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
972    if let tlv::TlvItemValue::Int(v) = inp {
973        Ok(Some(*v as u8))
974    } else {
975        Ok(None)
976    }
977}
978
979/// Decode EnhancedCurrentHue attribute (0x4000)
980pub fn decode_enhanced_current_hue(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
981    if let tlv::TlvItemValue::Int(v) = inp {
982        Ok(*v as u16)
983    } else {
984        Err(anyhow::anyhow!("Expected UInt16"))
985    }
986}
987
988/// Decode EnhancedColorMode attribute (0x4001)
989pub fn decode_enhanced_color_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<EnhancedColorMode> {
990    if let tlv::TlvItemValue::Int(v) = inp {
991        EnhancedColorMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
992    } else {
993        Err(anyhow::anyhow!("Expected Integer"))
994    }
995}
996
997/// Decode ColorLoopActive attribute (0x4002)
998pub fn decode_color_loop_active(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
999    if let tlv::TlvItemValue::Int(v) = inp {
1000        Ok(*v as u8)
1001    } else {
1002        Err(anyhow::anyhow!("Expected UInt8"))
1003    }
1004}
1005
1006/// Decode ColorLoopDirection attribute (0x4003)
1007pub fn decode_color_loop_direction(inp: &tlv::TlvItemValue) -> anyhow::Result<ColorLoopDirection> {
1008    if let tlv::TlvItemValue::Int(v) = inp {
1009        ColorLoopDirection::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1010    } else {
1011        Err(anyhow::anyhow!("Expected Integer"))
1012    }
1013}
1014
1015/// Decode ColorLoopTime attribute (0x4004)
1016pub fn decode_color_loop_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1017    if let tlv::TlvItemValue::Int(v) = inp {
1018        Ok(*v as u16)
1019    } else {
1020        Err(anyhow::anyhow!("Expected UInt16"))
1021    }
1022}
1023
1024/// Decode ColorLoopStartEnhancedHue attribute (0x4005)
1025pub fn decode_color_loop_start_enhanced_hue(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1026    if let tlv::TlvItemValue::Int(v) = inp {
1027        Ok(*v as u16)
1028    } else {
1029        Err(anyhow::anyhow!("Expected UInt16"))
1030    }
1031}
1032
1033/// Decode ColorLoopStoredEnhancedHue attribute (0x4006)
1034pub fn decode_color_loop_stored_enhanced_hue(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1035    if let tlv::TlvItemValue::Int(v) = inp {
1036        Ok(*v as u16)
1037    } else {
1038        Err(anyhow::anyhow!("Expected UInt16"))
1039    }
1040}
1041
1042/// Decode ColorCapabilities attribute (0x400A)
1043pub fn decode_color_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<ColorCapabilities> {
1044    if let tlv::TlvItemValue::Int(v) = inp {
1045        Ok(*v as u8)
1046    } else {
1047        Err(anyhow::anyhow!("Expected Integer"))
1048    }
1049}
1050
1051/// Decode ColorTempPhysicalMinMireds attribute (0x400B)
1052pub fn decode_color_temp_physical_min_mireds(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1053    if let tlv::TlvItemValue::Int(v) = inp {
1054        Ok(*v as u16)
1055    } else {
1056        Err(anyhow::anyhow!("Expected UInt16"))
1057    }
1058}
1059
1060/// Decode ColorTempPhysicalMaxMireds attribute (0x400C)
1061pub fn decode_color_temp_physical_max_mireds(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1062    if let tlv::TlvItemValue::Int(v) = inp {
1063        Ok(*v as u16)
1064    } else {
1065        Err(anyhow::anyhow!("Expected UInt16"))
1066    }
1067}
1068
1069/// Decode CoupleColorTempToLevelMinMireds attribute (0x400D)
1070pub fn decode_couple_color_temp_to_level_min_mireds(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1071    if let tlv::TlvItemValue::Int(v) = inp {
1072        Ok(*v as u16)
1073    } else {
1074        Err(anyhow::anyhow!("Expected UInt16"))
1075    }
1076}
1077
1078/// Decode StartUpColorTemperatureMireds attribute (0x4010)
1079pub fn decode_start_up_color_temperature_mireds(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
1080    if let tlv::TlvItemValue::Int(v) = inp {
1081        Ok(Some(*v as u16))
1082    } else {
1083        Ok(None)
1084    }
1085}
1086
1087
1088// JSON dispatcher function
1089
1090/// Decode attribute value and return as JSON string
1091///
1092/// # Parameters
1093/// * `cluster_id` - The cluster identifier
1094/// * `attribute_id` - The attribute identifier
1095/// * `tlv_value` - The TLV value to decode
1096///
1097/// # Returns
1098/// JSON string representation of the decoded value or error
1099pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
1100    // Verify this is the correct cluster
1101    if cluster_id != 0x0300 {
1102        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0300, got {}\"}}", cluster_id);
1103    }
1104
1105    match attribute_id {
1106        0x0000 => {
1107            match decode_current_hue(tlv_value) {
1108                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1109                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1110            }
1111        }
1112        0x0001 => {
1113            match decode_current_saturation(tlv_value) {
1114                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1115                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1116            }
1117        }
1118        0x0002 => {
1119            match decode_remaining_time(tlv_value) {
1120                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1121                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1122            }
1123        }
1124        0x0003 => {
1125            match decode_current_x(tlv_value) {
1126                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1127                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1128            }
1129        }
1130        0x0004 => {
1131            match decode_current_y(tlv_value) {
1132                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1133                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1134            }
1135        }
1136        0x0005 => {
1137            match decode_drift_compensation(tlv_value) {
1138                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1139                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1140            }
1141        }
1142        0x0006 => {
1143            match decode_compensation_text(tlv_value) {
1144                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1145                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1146            }
1147        }
1148        0x0007 => {
1149            match decode_color_temperature_mireds(tlv_value) {
1150                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1151                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1152            }
1153        }
1154        0x0008 => {
1155            match decode_color_mode(tlv_value) {
1156                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1157                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1158            }
1159        }
1160        0x000F => {
1161            match decode_options(tlv_value) {
1162                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1163                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1164            }
1165        }
1166        0x0010 => {
1167            match decode_number_of_primaries(tlv_value) {
1168                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1169                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1170            }
1171        }
1172        0x0011 => {
1173            match decode_primary1_x(tlv_value) {
1174                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1175                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1176            }
1177        }
1178        0x0012 => {
1179            match decode_primary1_y(tlv_value) {
1180                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1181                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1182            }
1183        }
1184        0x0013 => {
1185            match decode_primary1_intensity(tlv_value) {
1186                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1187                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1188            }
1189        }
1190        0x0015 => {
1191            match decode_primary2_x(tlv_value) {
1192                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1193                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1194            }
1195        }
1196        0x0016 => {
1197            match decode_primary2_y(tlv_value) {
1198                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1199                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1200            }
1201        }
1202        0x0017 => {
1203            match decode_primary2_intensity(tlv_value) {
1204                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1205                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1206            }
1207        }
1208        0x0019 => {
1209            match decode_primary3_x(tlv_value) {
1210                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1211                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1212            }
1213        }
1214        0x001A => {
1215            match decode_primary3_y(tlv_value) {
1216                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1217                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1218            }
1219        }
1220        0x001B => {
1221            match decode_primary3_intensity(tlv_value) {
1222                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1223                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1224            }
1225        }
1226        0x0020 => {
1227            match decode_primary4_x(tlv_value) {
1228                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1229                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1230            }
1231        }
1232        0x0021 => {
1233            match decode_primary4_y(tlv_value) {
1234                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1235                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1236            }
1237        }
1238        0x0022 => {
1239            match decode_primary4_intensity(tlv_value) {
1240                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1241                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1242            }
1243        }
1244        0x0024 => {
1245            match decode_primary5_x(tlv_value) {
1246                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1247                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1248            }
1249        }
1250        0x0025 => {
1251            match decode_primary5_y(tlv_value) {
1252                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1253                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1254            }
1255        }
1256        0x0026 => {
1257            match decode_primary5_intensity(tlv_value) {
1258                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1259                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1260            }
1261        }
1262        0x0028 => {
1263            match decode_primary6_x(tlv_value) {
1264                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1265                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1266            }
1267        }
1268        0x0029 => {
1269            match decode_primary6_y(tlv_value) {
1270                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1271                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1272            }
1273        }
1274        0x002A => {
1275            match decode_primary6_intensity(tlv_value) {
1276                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1277                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1278            }
1279        }
1280        0x0030 => {
1281            match decode_white_point_x(tlv_value) {
1282                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1283                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1284            }
1285        }
1286        0x0031 => {
1287            match decode_white_point_y(tlv_value) {
1288                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1289                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1290            }
1291        }
1292        0x0032 => {
1293            match decode_color_point_rx(tlv_value) {
1294                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1295                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1296            }
1297        }
1298        0x0033 => {
1299            match decode_color_point_ry(tlv_value) {
1300                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1301                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1302            }
1303        }
1304        0x0034 => {
1305            match decode_color_point_r_intensity(tlv_value) {
1306                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1307                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1308            }
1309        }
1310        0x0036 => {
1311            match decode_color_point_gx(tlv_value) {
1312                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1313                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1314            }
1315        }
1316        0x0037 => {
1317            match decode_color_point_gy(tlv_value) {
1318                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1319                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1320            }
1321        }
1322        0x0038 => {
1323            match decode_color_point_g_intensity(tlv_value) {
1324                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1325                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1326            }
1327        }
1328        0x003A => {
1329            match decode_color_point_bx(tlv_value) {
1330                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1331                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1332            }
1333        }
1334        0x003B => {
1335            match decode_color_point_by(tlv_value) {
1336                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1337                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1338            }
1339        }
1340        0x003C => {
1341            match decode_color_point_b_intensity(tlv_value) {
1342                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1343                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1344            }
1345        }
1346        0x4000 => {
1347            match decode_enhanced_current_hue(tlv_value) {
1348                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1349                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1350            }
1351        }
1352        0x4001 => {
1353            match decode_enhanced_color_mode(tlv_value) {
1354                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1355                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1356            }
1357        }
1358        0x4002 => {
1359            match decode_color_loop_active(tlv_value) {
1360                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1361                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1362            }
1363        }
1364        0x4003 => {
1365            match decode_color_loop_direction(tlv_value) {
1366                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1367                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1368            }
1369        }
1370        0x4004 => {
1371            match decode_color_loop_time(tlv_value) {
1372                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1373                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1374            }
1375        }
1376        0x4005 => {
1377            match decode_color_loop_start_enhanced_hue(tlv_value) {
1378                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1379                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1380            }
1381        }
1382        0x4006 => {
1383            match decode_color_loop_stored_enhanced_hue(tlv_value) {
1384                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1385                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1386            }
1387        }
1388        0x400A => {
1389            match decode_color_capabilities(tlv_value) {
1390                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1391                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1392            }
1393        }
1394        0x400B => {
1395            match decode_color_temp_physical_min_mireds(tlv_value) {
1396                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1397                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1398            }
1399        }
1400        0x400C => {
1401            match decode_color_temp_physical_max_mireds(tlv_value) {
1402                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1403                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1404            }
1405        }
1406        0x400D => {
1407            match decode_couple_color_temp_to_level_min_mireds(tlv_value) {
1408                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1409                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1410            }
1411        }
1412        0x4010 => {
1413            match decode_start_up_color_temperature_mireds(tlv_value) {
1414                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1415                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1416            }
1417        }
1418        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
1419    }
1420}
1421
1422/// Get list of all attributes supported by this cluster
1423///
1424/// # Returns
1425/// Vector of tuples containing (attribute_id, attribute_name)
1426pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
1427    vec![
1428        (0x0000, "CurrentHue"),
1429        (0x0001, "CurrentSaturation"),
1430        (0x0002, "RemainingTime"),
1431        (0x0003, "CurrentX"),
1432        (0x0004, "CurrentY"),
1433        (0x0005, "DriftCompensation"),
1434        (0x0006, "CompensationText"),
1435        (0x0007, "ColorTemperatureMireds"),
1436        (0x0008, "ColorMode"),
1437        (0x000F, "Options"),
1438        (0x0010, "NumberOfPrimaries"),
1439        (0x0011, "Primary1X"),
1440        (0x0012, "Primary1Y"),
1441        (0x0013, "Primary1Intensity"),
1442        (0x0015, "Primary2X"),
1443        (0x0016, "Primary2Y"),
1444        (0x0017, "Primary2Intensity"),
1445        (0x0019, "Primary3X"),
1446        (0x001A, "Primary3Y"),
1447        (0x001B, "Primary3Intensity"),
1448        (0x0020, "Primary4X"),
1449        (0x0021, "Primary4Y"),
1450        (0x0022, "Primary4Intensity"),
1451        (0x0024, "Primary5X"),
1452        (0x0025, "Primary5Y"),
1453        (0x0026, "Primary5Intensity"),
1454        (0x0028, "Primary6X"),
1455        (0x0029, "Primary6Y"),
1456        (0x002A, "Primary6Intensity"),
1457        (0x0030, "WhitePointX"),
1458        (0x0031, "WhitePointY"),
1459        (0x0032, "ColorPointRX"),
1460        (0x0033, "ColorPointRY"),
1461        (0x0034, "ColorPointRIntensity"),
1462        (0x0036, "ColorPointGX"),
1463        (0x0037, "ColorPointGY"),
1464        (0x0038, "ColorPointGIntensity"),
1465        (0x003A, "ColorPointBX"),
1466        (0x003B, "ColorPointBY"),
1467        (0x003C, "ColorPointBIntensity"),
1468        (0x4000, "EnhancedCurrentHue"),
1469        (0x4001, "EnhancedColorMode"),
1470        (0x4002, "ColorLoopActive"),
1471        (0x4003, "ColorLoopDirection"),
1472        (0x4004, "ColorLoopTime"),
1473        (0x4005, "ColorLoopStartEnhancedHue"),
1474        (0x4006, "ColorLoopStoredEnhancedHue"),
1475        (0x400A, "ColorCapabilities"),
1476        (0x400B, "ColorTempPhysicalMinMireds"),
1477        (0x400C, "ColorTempPhysicalMaxMireds"),
1478        (0x400D, "CoupleColorTempToLevelMinMireds"),
1479        (0x4010, "StartUpColorTemperatureMireds"),
1480    ]
1481}
1482