matc/clusters/codec/
on_off.rs

1//! Matter TLV encoders and decoders for On/Off Cluster
2//! Cluster ID: 0x0006
3//!
4//! This file is automatically generated from OnOff.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 DelayedAllOffEffectVariant {
16    /// Fade to off in 0.8 seconds
17    Delayedofffastfade = 0,
18    /// No fade
19    Nofade = 1,
20    /// 50% dim down in 0.8 seconds then fade to off in 12 seconds
21    Delayedoffslowfade = 2,
22}
23
24impl DelayedAllOffEffectVariant {
25    /// Convert from u8 value
26    pub fn from_u8(value: u8) -> Option<Self> {
27        match value {
28            0 => Some(DelayedAllOffEffectVariant::Delayedofffastfade),
29            1 => Some(DelayedAllOffEffectVariant::Nofade),
30            2 => Some(DelayedAllOffEffectVariant::Delayedoffslowfade),
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<DelayedAllOffEffectVariant> for u8 {
42    fn from(val: DelayedAllOffEffectVariant) -> Self {
43        val as u8
44    }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48#[repr(u8)]
49pub enum DyingLightEffectVariant {
50    /// 20% dim up in 0.5s then fade to off in 1 second
51    Dyinglightfadeoff = 0,
52}
53
54impl DyingLightEffectVariant {
55    /// Convert from u8 value
56    pub fn from_u8(value: u8) -> Option<Self> {
57        match value {
58            0 => Some(DyingLightEffectVariant::Dyinglightfadeoff),
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<DyingLightEffectVariant> for u8 {
70    fn from(val: DyingLightEffectVariant) -> Self {
71        val as u8
72    }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
76#[repr(u8)]
77pub enum EffectIdentifier {
78    /// Delayed All Off
79    Delayedalloff = 0,
80    /// Dying Light
81    Dyinglight = 1,
82}
83
84impl EffectIdentifier {
85    /// Convert from u8 value
86    pub fn from_u8(value: u8) -> Option<Self> {
87        match value {
88            0 => Some(EffectIdentifier::Delayedalloff),
89            1 => Some(EffectIdentifier::Dyinglight),
90            _ => None,
91        }
92    }
93
94    /// Convert to u8 value
95    pub fn to_u8(self) -> u8 {
96        self as u8
97    }
98}
99
100impl From<EffectIdentifier> for u8 {
101    fn from(val: EffectIdentifier) -> Self {
102        val as u8
103    }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
107#[repr(u8)]
108pub enum StartUpOnOff {
109    /// Set the OnOff attribute to FALSE
110    Off = 0,
111    /// Set the OnOff attribute to TRUE
112    On = 1,
113    /// If the previous value of the OnOff attribute is equal to FALSE, set the OnOff attribute to TRUE. If the previous value of the OnOff attribute is equal to TRUE, set the OnOff attribute to FALSE (toggle).
114    Toggle = 2,
115}
116
117impl StartUpOnOff {
118    /// Convert from u8 value
119    pub fn from_u8(value: u8) -> Option<Self> {
120        match value {
121            0 => Some(StartUpOnOff::Off),
122            1 => Some(StartUpOnOff::On),
123            2 => Some(StartUpOnOff::Toggle),
124            _ => None,
125        }
126    }
127
128    /// Convert to u8 value
129    pub fn to_u8(self) -> u8 {
130        self as u8
131    }
132}
133
134impl From<StartUpOnOff> for u8 {
135    fn from(val: StartUpOnOff) -> Self {
136        val as u8
137    }
138}
139
140// Bitmap definitions
141
142/// OnOffControl bitmap type
143pub type OnOffControl = u8;
144
145/// Constants for OnOffControl
146pub mod onoffcontrol {
147    /// Indicates a command is only accepted when in On state.
148    pub const ACCEPT_ONLY_WHEN_ON: u8 = 0x01;
149}
150
151// Command encoders
152
153/// Encode OffWithEffect command (0x40)
154pub fn encode_off_with_effect(effect_identifier: EffectIdentifier, effect_variant: u8) -> anyhow::Result<Vec<u8>> {
155    let tlv = tlv::TlvItemEnc {
156        tag: 0,
157        value: tlv::TlvItemValueEnc::StructInvisible(vec![
158        (0, tlv::TlvItemValueEnc::UInt8(effect_identifier.to_u8())).into(),
159        (1, tlv::TlvItemValueEnc::UInt8(effect_variant)).into(),
160        ]),
161    };
162    Ok(tlv.encode()?)
163}
164
165/// Encode OnWithTimedOff command (0x42)
166pub fn encode_on_with_timed_off(on_off_control: OnOffControl, on_time: u16, off_wait_time: u16) -> anyhow::Result<Vec<u8>> {
167    let tlv = tlv::TlvItemEnc {
168        tag: 0,
169        value: tlv::TlvItemValueEnc::StructInvisible(vec![
170        (0, tlv::TlvItemValueEnc::UInt8(on_off_control)).into(),
171        (1, tlv::TlvItemValueEnc::UInt16(on_time)).into(),
172        (2, tlv::TlvItemValueEnc::UInt16(off_wait_time)).into(),
173        ]),
174    };
175    Ok(tlv.encode()?)
176}
177
178// Attribute decoders
179
180/// Decode OnOff attribute (0x0000)
181pub fn decode_on_off(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
182    if let tlv::TlvItemValue::Bool(v) = inp {
183        Ok(*v)
184    } else {
185        Err(anyhow::anyhow!("Expected Bool"))
186    }
187}
188
189/// Decode GlobalSceneControl attribute (0x4000)
190pub fn decode_global_scene_control(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
191    if let tlv::TlvItemValue::Bool(v) = inp {
192        Ok(*v)
193    } else {
194        Err(anyhow::anyhow!("Expected Bool"))
195    }
196}
197
198/// Decode OnTime attribute (0x4001)
199pub fn decode_on_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
200    if let tlv::TlvItemValue::Int(v) = inp {
201        Ok(*v as u16)
202    } else {
203        Err(anyhow::anyhow!("Expected UInt16"))
204    }
205}
206
207/// Decode OffWaitTime attribute (0x4002)
208pub fn decode_off_wait_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
209    if let tlv::TlvItemValue::Int(v) = inp {
210        Ok(*v as u16)
211    } else {
212        Err(anyhow::anyhow!("Expected UInt16"))
213    }
214}
215
216/// Decode StartUpOnOff attribute (0x4003)
217pub fn decode_start_up_on_off(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<StartUpOnOff>> {
218    if let tlv::TlvItemValue::Int(v) = inp {
219        Ok(StartUpOnOff::from_u8(*v as u8))
220    } else {
221        Ok(None)
222    }
223}
224
225
226// JSON dispatcher function
227
228/// Decode attribute value and return as JSON string
229///
230/// # Parameters
231/// * `cluster_id` - The cluster identifier
232/// * `attribute_id` - The attribute identifier
233/// * `tlv_value` - The TLV value to decode
234///
235/// # Returns
236/// JSON string representation of the decoded value or error
237pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
238    // Verify this is the correct cluster
239    if cluster_id != 0x0006 {
240        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0006, got {}\"}}", cluster_id);
241    }
242
243    match attribute_id {
244        0x0000 => {
245            match decode_on_off(tlv_value) {
246                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
247                Err(e) => format!("{{\"error\": \"{}\"}}", e),
248            }
249        }
250        0x4000 => {
251            match decode_global_scene_control(tlv_value) {
252                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
253                Err(e) => format!("{{\"error\": \"{}\"}}", e),
254            }
255        }
256        0x4001 => {
257            match decode_on_time(tlv_value) {
258                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
259                Err(e) => format!("{{\"error\": \"{}\"}}", e),
260            }
261        }
262        0x4002 => {
263            match decode_off_wait_time(tlv_value) {
264                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
265                Err(e) => format!("{{\"error\": \"{}\"}}", e),
266            }
267        }
268        0x4003 => {
269            match decode_start_up_on_off(tlv_value) {
270                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
271                Err(e) => format!("{{\"error\": \"{}\"}}", e),
272            }
273        }
274        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
275    }
276}
277
278/// Get list of all attributes supported by this cluster
279///
280/// # Returns
281/// Vector of tuples containing (attribute_id, attribute_name)
282pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
283    vec![
284        (0x0000, "OnOff"),
285        (0x4000, "GlobalSceneControl"),
286        (0x4001, "OnTime"),
287        (0x4002, "OffWaitTime"),
288        (0x4003, "StartUpOnOff"),
289    ]
290}
291