matc/clusters/codec/
scenes.rs

1//! Matter TLV encoders and decoders for Scenes Management Cluster
2//! Cluster ID: 0x0062
3//!
4//! This file is automatically generated from Scenes.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Bitmap definitions
12
13/// CopyMode bitmap type
14pub type CopyMode = u8;
15
16/// Constants for CopyMode
17pub mod copymode {
18    /// Copy all scenes in the scene table
19    pub const COPY_ALL_SCENES: u8 = 0x01;
20}
21
22// Struct definitions
23
24#[derive(Debug, serde::Serialize)]
25pub struct AttributeValuePair {
26    pub attribute_id: Option<u32>,
27    pub value_unsigned8: Option<u8>,
28    pub value_signed8: Option<i8>,
29    pub value_unsigned16: Option<u16>,
30    pub value_signed16: Option<i16>,
31    pub value_unsigned32: Option<u32>,
32    pub value_signed32: Option<i32>,
33    pub value_unsigned64: Option<u64>,
34    pub value_signed64: Option<i64>,
35}
36
37#[derive(Debug, serde::Serialize)]
38pub struct ExtensionFieldSet {
39    pub cluster_id: Option<u32>,
40    pub attribute_value_list: Option<Vec<AttributeValuePair>>,
41}
42
43#[derive(Debug, serde::Serialize)]
44pub struct SceneInfo {
45    pub scene_count: Option<u8>,
46    pub current_scene: Option<u8>,
47    pub current_group: Option<u8>,
48    pub scene_valid: Option<bool>,
49    pub remaining_capacity: Option<u8>,
50}
51
52// Command encoders
53
54/// Encode AddScene command (0x00)
55pub fn encode_add_scene(group_id: u8, scene_id: u8, transition_time: u32, scene_name: String, extension_field_set_structs: Vec<ExtensionFieldSet>) -> anyhow::Result<Vec<u8>> {
56    let tlv = tlv::TlvItemEnc {
57        tag: 0,
58        value: tlv::TlvItemValueEnc::StructInvisible(vec![
59        (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
60        (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
61        (2, tlv::TlvItemValueEnc::UInt32(transition_time)).into(),
62        (3, tlv::TlvItemValueEnc::String(scene_name)).into(),
63        (4, tlv::TlvItemValueEnc::Array(extension_field_set_structs.into_iter().map(|v| {
64                    let mut fields = Vec::new();
65                    if let Some(x) = v.cluster_id { fields.push((0, tlv::TlvItemValueEnc::UInt32(x)).into()); }
66                    if let Some(listv) = v.attribute_value_list {
67                        let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
68                            let mut nested_fields = Vec::new();
69                                if let Some(x) = inner.attribute_id { nested_fields.push((0, tlv::TlvItemValueEnc::UInt32(x)).into()); }
70                                if let Some(x) = inner.value_unsigned8 { nested_fields.push((1, tlv::TlvItemValueEnc::UInt8(x)).into()); }
71                                if let Some(x) = inner.value_signed8 { nested_fields.push((2, tlv::TlvItemValueEnc::Int8(x)).into()); }
72                                if let Some(x) = inner.value_unsigned16 { nested_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
73                                if let Some(x) = inner.value_signed16 { nested_fields.push((4, tlv::TlvItemValueEnc::Int16(x)).into()); }
74                                if let Some(x) = inner.value_unsigned32 { nested_fields.push((5, tlv::TlvItemValueEnc::UInt32(x)).into()); }
75                                if let Some(x) = inner.value_signed32 { nested_fields.push((6, tlv::TlvItemValueEnc::Int32(x)).into()); }
76                                if let Some(x) = inner.value_unsigned64 { nested_fields.push((7, tlv::TlvItemValueEnc::UInt64(x)).into()); }
77                                if let Some(x) = inner.value_signed64 { nested_fields.push((8, tlv::TlvItemValueEnc::Int64(x)).into()); }
78                            (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
79                        }).collect();
80                        fields.push((1, tlv::TlvItemValueEnc::Array(inner_vec)).into());
81                    }
82                    (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
83                }).collect())).into(),
84        ]),
85    };
86    Ok(tlv.encode()?)
87}
88
89/// Encode ViewScene command (0x01)
90pub fn encode_view_scene(group_id: u8, scene_id: u8) -> anyhow::Result<Vec<u8>> {
91    let tlv = tlv::TlvItemEnc {
92        tag: 0,
93        value: tlv::TlvItemValueEnc::StructInvisible(vec![
94        (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
95        (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
96        ]),
97    };
98    Ok(tlv.encode()?)
99}
100
101/// Encode RemoveScene command (0x02)
102pub fn encode_remove_scene(group_id: u8, scene_id: u8) -> anyhow::Result<Vec<u8>> {
103    let tlv = tlv::TlvItemEnc {
104        tag: 0,
105        value: tlv::TlvItemValueEnc::StructInvisible(vec![
106        (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
107        (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
108        ]),
109    };
110    Ok(tlv.encode()?)
111}
112
113/// Encode RemoveAllScenes command (0x03)
114pub fn encode_remove_all_scenes(group_id: u8) -> anyhow::Result<Vec<u8>> {
115    let tlv = tlv::TlvItemEnc {
116        tag: 0,
117        value: tlv::TlvItemValueEnc::StructInvisible(vec![
118        (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
119        ]),
120    };
121    Ok(tlv.encode()?)
122}
123
124/// Encode StoreScene command (0x04)
125pub fn encode_store_scene(group_id: u8, scene_id: u8) -> anyhow::Result<Vec<u8>> {
126    let tlv = tlv::TlvItemEnc {
127        tag: 0,
128        value: tlv::TlvItemValueEnc::StructInvisible(vec![
129        (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
130        (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
131        ]),
132    };
133    Ok(tlv.encode()?)
134}
135
136/// Encode RecallScene command (0x05)
137pub fn encode_recall_scene(group_id: u8, scene_id: u8, transition_time: Option<u32>) -> anyhow::Result<Vec<u8>> {
138    let tlv = tlv::TlvItemEnc {
139        tag: 0,
140        value: tlv::TlvItemValueEnc::StructInvisible(vec![
141        (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
142        (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
143        (2, tlv::TlvItemValueEnc::UInt32(transition_time.unwrap_or(0))).into(),
144        ]),
145    };
146    Ok(tlv.encode()?)
147}
148
149/// Encode GetSceneMembership command (0x06)
150pub fn encode_get_scene_membership(group_id: u8) -> anyhow::Result<Vec<u8>> {
151    let tlv = tlv::TlvItemEnc {
152        tag: 0,
153        value: tlv::TlvItemValueEnc::StructInvisible(vec![
154        (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
155        ]),
156    };
157    Ok(tlv.encode()?)
158}
159
160/// Encode CopyScene command (0x40)
161pub fn encode_copy_scene(mode: CopyMode, group_identifier_from: u8, scene_identifier_from: u8, group_identifier_to: u8, scene_identifier_to: u8) -> anyhow::Result<Vec<u8>> {
162    let tlv = tlv::TlvItemEnc {
163        tag: 0,
164        value: tlv::TlvItemValueEnc::StructInvisible(vec![
165        (0, tlv::TlvItemValueEnc::UInt8(mode)).into(),
166        (1, tlv::TlvItemValueEnc::UInt8(group_identifier_from)).into(),
167        (2, tlv::TlvItemValueEnc::UInt8(scene_identifier_from)).into(),
168        (3, tlv::TlvItemValueEnc::UInt8(group_identifier_to)).into(),
169        (4, tlv::TlvItemValueEnc::UInt8(scene_identifier_to)).into(),
170        ]),
171    };
172    Ok(tlv.encode()?)
173}
174
175// Attribute decoders
176
177/// Decode DoNotUse attribute (0x0000)
178pub fn decode_do_not_use(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
179    if let tlv::TlvItemValue::Int(v) = inp {
180        Ok(*v as u8)
181    } else {
182        Err(anyhow::anyhow!("Expected UInt8"))
183    }
184}
185
186/// Decode SceneTableSize attribute (0x0001)
187pub fn decode_scene_table_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
188    if let tlv::TlvItemValue::Int(v) = inp {
189        Ok(*v as u16)
190    } else {
191        Err(anyhow::anyhow!("Expected UInt16"))
192    }
193}
194
195/// Decode FabricSceneInfo attribute (0x0002)
196pub fn decode_fabric_scene_info(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SceneInfo>> {
197    let mut res = Vec::new();
198    if let tlv::TlvItemValue::List(v) = inp {
199        for item in v {
200            res.push(SceneInfo {
201                scene_count: item.get_int(&[0]).map(|v| v as u8),
202                current_scene: item.get_int(&[1]).map(|v| v as u8),
203                current_group: item.get_int(&[2]).map(|v| v as u8),
204                scene_valid: item.get_bool(&[3]),
205                remaining_capacity: item.get_int(&[4]).map(|v| v as u8),
206            });
207        }
208    }
209    Ok(res)
210}
211
212
213// JSON dispatcher function
214
215/// Decode attribute value and return as JSON string
216///
217/// # Parameters
218/// * `cluster_id` - The cluster identifier
219/// * `attribute_id` - The attribute identifier
220/// * `tlv_value` - The TLV value to decode
221///
222/// # Returns
223/// JSON string representation of the decoded value or error
224pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
225    // Verify this is the correct cluster
226    if cluster_id != 0x0062 {
227        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0062, got {}\"}}", cluster_id);
228    }
229
230    match attribute_id {
231        0x0000 => {
232            match decode_do_not_use(tlv_value) {
233                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
234                Err(e) => format!("{{\"error\": \"{}\"}}", e),
235            }
236        }
237        0x0001 => {
238            match decode_scene_table_size(tlv_value) {
239                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
240                Err(e) => format!("{{\"error\": \"{}\"}}", e),
241            }
242        }
243        0x0002 => {
244            match decode_fabric_scene_info(tlv_value) {
245                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
246                Err(e) => format!("{{\"error\": \"{}\"}}", e),
247            }
248        }
249        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
250    }
251}
252
253/// Get list of all attributes supported by this cluster
254///
255/// # Returns
256/// Vector of tuples containing (attribute_id, attribute_name)
257pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
258    vec![
259        (0x0000, "DoNotUse"),
260        (0x0001, "SceneTableSize"),
261        (0x0002, "FabricSceneInfo"),
262    ]
263}
264
265#[derive(Debug, serde::Serialize)]
266pub struct AddSceneResponse {
267    pub status: Option<u8>,
268    pub group_id: Option<u8>,
269    pub scene_id: Option<u8>,
270}
271
272#[derive(Debug, serde::Serialize)]
273pub struct ViewSceneResponse {
274    pub status: Option<u8>,
275    pub group_id: Option<u8>,
276    pub scene_id: Option<u8>,
277    pub transition_time: Option<u32>,
278    pub scene_name: Option<String>,
279    pub extension_field_set_structs: Option<Vec<ExtensionFieldSet>>,
280}
281
282#[derive(Debug, serde::Serialize)]
283pub struct RemoveSceneResponse {
284    pub status: Option<u8>,
285    pub group_id: Option<u8>,
286    pub scene_id: Option<u8>,
287}
288
289#[derive(Debug, serde::Serialize)]
290pub struct RemoveAllScenesResponse {
291    pub status: Option<u8>,
292    pub group_id: Option<u8>,
293}
294
295#[derive(Debug, serde::Serialize)]
296pub struct StoreSceneResponse {
297    pub status: Option<u8>,
298    pub group_id: Option<u8>,
299    pub scene_id: Option<u8>,
300}
301
302#[derive(Debug, serde::Serialize)]
303pub struct GetSceneMembershipResponse {
304    pub status: Option<u8>,
305    pub capacity: Option<u8>,
306    pub group_id: Option<u8>,
307    pub scene_list: Option<Vec<u8>>,
308}
309
310#[derive(Debug, serde::Serialize)]
311pub struct CopySceneResponse {
312    pub status: Option<u8>,
313    pub group_identifier_from: Option<u8>,
314    pub scene_identifier_from: Option<u8>,
315}
316
317// Command response decoders
318
319/// Decode AddSceneResponse command response (00)
320pub fn decode_add_scene_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AddSceneResponse> {
321    if let tlv::TlvItemValue::List(_fields) = inp {
322        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
323        Ok(AddSceneResponse {
324                status: item.get_int(&[0]).map(|v| v as u8),
325                group_id: item.get_int(&[1]).map(|v| v as u8),
326                scene_id: item.get_int(&[2]).map(|v| v as u8),
327        })
328    } else {
329        Err(anyhow::anyhow!("Expected struct fields"))
330    }
331}
332
333/// Decode ViewSceneResponse command response (01)
334pub fn decode_view_scene_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ViewSceneResponse> {
335    if let tlv::TlvItemValue::List(_fields) = inp {
336        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
337        Ok(ViewSceneResponse {
338                status: item.get_int(&[0]).map(|v| v as u8),
339                group_id: item.get_int(&[1]).map(|v| v as u8),
340                scene_id: item.get_int(&[2]).map(|v| v as u8),
341                transition_time: item.get_int(&[3]).map(|v| v as u32),
342                scene_name: item.get_string_owned(&[4]),
343                extension_field_set_structs: {
344                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[5]) {
345                        let mut items = Vec::new();
346                        for list_item in l {
347                            items.push(ExtensionFieldSet {
348                cluster_id: list_item.get_int(&[0]).map(|v| v as u32),
349                attribute_value_list: {
350                    if let Some(tlv::TlvItemValue::List(l)) = list_item.get(&[1]) {
351                        let mut items = Vec::new();
352                        for list_item in l {
353                            items.push(AttributeValuePair {
354                attribute_id: list_item.get_int(&[0]).map(|v| v as u32),
355                value_unsigned8: list_item.get_int(&[1]).map(|v| v as u8),
356                value_signed8: list_item.get_int(&[2]).map(|v| v as i8),
357                value_unsigned16: list_item.get_int(&[3]).map(|v| v as u16),
358                value_signed16: list_item.get_int(&[4]).map(|v| v as i16),
359                value_unsigned32: list_item.get_int(&[5]).map(|v| v as u32),
360                value_signed32: list_item.get_int(&[6]).map(|v| v as i32),
361                value_unsigned64: list_item.get_int(&[7]),
362                value_signed64: list_item.get_int(&[8]).map(|v| v as i64),
363                            });
364                        }
365                        Some(items)
366                    } else {
367                        None
368                    }
369                },
370                            });
371                        }
372                        Some(items)
373                    } else {
374                        None
375                    }
376                },
377        })
378    } else {
379        Err(anyhow::anyhow!("Expected struct fields"))
380    }
381}
382
383/// Decode RemoveSceneResponse command response (02)
384pub fn decode_remove_scene_response(inp: &tlv::TlvItemValue) -> anyhow::Result<RemoveSceneResponse> {
385    if let tlv::TlvItemValue::List(_fields) = inp {
386        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
387        Ok(RemoveSceneResponse {
388                status: item.get_int(&[0]).map(|v| v as u8),
389                group_id: item.get_int(&[1]).map(|v| v as u8),
390                scene_id: item.get_int(&[2]).map(|v| v as u8),
391        })
392    } else {
393        Err(anyhow::anyhow!("Expected struct fields"))
394    }
395}
396
397/// Decode RemoveAllScenesResponse command response (03)
398pub fn decode_remove_all_scenes_response(inp: &tlv::TlvItemValue) -> anyhow::Result<RemoveAllScenesResponse> {
399    if let tlv::TlvItemValue::List(_fields) = inp {
400        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
401        Ok(RemoveAllScenesResponse {
402                status: item.get_int(&[0]).map(|v| v as u8),
403                group_id: item.get_int(&[1]).map(|v| v as u8),
404        })
405    } else {
406        Err(anyhow::anyhow!("Expected struct fields"))
407    }
408}
409
410/// Decode StoreSceneResponse command response (04)
411pub fn decode_store_scene_response(inp: &tlv::TlvItemValue) -> anyhow::Result<StoreSceneResponse> {
412    if let tlv::TlvItemValue::List(_fields) = inp {
413        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
414        Ok(StoreSceneResponse {
415                status: item.get_int(&[0]).map(|v| v as u8),
416                group_id: item.get_int(&[1]).map(|v| v as u8),
417                scene_id: item.get_int(&[2]).map(|v| v as u8),
418        })
419    } else {
420        Err(anyhow::anyhow!("Expected struct fields"))
421    }
422}
423
424/// Decode GetSceneMembershipResponse command response (06)
425pub fn decode_get_scene_membership_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetSceneMembershipResponse> {
426    if let tlv::TlvItemValue::List(_fields) = inp {
427        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
428        Ok(GetSceneMembershipResponse {
429                status: item.get_int(&[0]).map(|v| v as u8),
430                capacity: item.get_int(&[1]).map(|v| v as u8),
431                group_id: item.get_int(&[2]).map(|v| v as u8),
432                scene_list: {
433                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
434                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
435                        Some(items)
436                    } else {
437                        None
438                    }
439                },
440        })
441    } else {
442        Err(anyhow::anyhow!("Expected struct fields"))
443    }
444}
445
446/// Decode CopySceneResponse command response (40)
447pub fn decode_copy_scene_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CopySceneResponse> {
448    if let tlv::TlvItemValue::List(_fields) = inp {
449        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
450        Ok(CopySceneResponse {
451                status: item.get_int(&[0]).map(|v| v as u8),
452                group_identifier_from: item.get_int(&[1]).map(|v| v as u8),
453                scene_identifier_from: item.get_int(&[2]).map(|v| v as u8),
454        })
455    } else {
456        Err(anyhow::anyhow!("Expected struct fields"))
457    }
458}
459