1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11pub type CopyMode = u8;
15
16pub mod copymode {
18 pub const COPY_ALL_SCENES: u8 = 0x01;
20}
21
22#[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
52pub 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
89pub 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
101pub 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
113pub 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
124pub 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
136pub 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
149pub 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
160pub 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
175pub 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
186pub 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
195pub 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
213pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
225 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
253pub 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
317pub 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
333pub 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
383pub 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
397pub 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
410pub 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
424pub 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
446pub 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