1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct AttributeValuePair {
15 pub attribute_id: Option<u8>,
16 pub value_unsigned8: Option<u8>,
17 pub value_signed8: Option<i8>,
18 pub value_unsigned16: Option<u16>,
19 pub value_signed16: Option<i16>,
20 pub value_unsigned32: Option<u32>,
21 pub value_signed32: Option<i32>,
22 pub value_unsigned64: Option<u64>,
23 pub value_signed64: Option<i64>,
24}
25
26#[derive(Debug, serde::Serialize)]
27pub struct ExtensionFieldSet {
28 pub cluster_id: Option<u32>,
29 pub attribute_value_list: Option<Vec<AttributeValuePair>>,
30}
31
32#[derive(Debug, serde::Serialize)]
33pub struct SceneInfo {
34 pub scene_count: Option<u8>,
35 pub current_scene: Option<u8>,
36 pub current_group: Option<u8>,
37 pub scene_valid: Option<bool>,
38 pub remaining_capacity: Option<u8>,
39}
40
41pub fn encode_add_scene(group_id: u8, scene_id: u8, transition_time: u32, scene_name: String, extension_field_set_structs: Vec<u8>) -> anyhow::Result<Vec<u8>> {
45 let tlv = tlv::TlvItemEnc {
46 tag: 0,
47 value: tlv::TlvItemValueEnc::StructInvisible(vec![
48 (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
49 (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
50 (2, tlv::TlvItemValueEnc::UInt32(transition_time)).into(),
51 (3, tlv::TlvItemValueEnc::String(scene_name)).into(),
52 (4, tlv::TlvItemValueEnc::StructAnon(extension_field_set_structs.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
53 ]),
54 };
55 Ok(tlv.encode()?)
56}
57
58pub fn encode_view_scene(group_id: u8, scene_id: u8) -> anyhow::Result<Vec<u8>> {
60 let tlv = tlv::TlvItemEnc {
61 tag: 0,
62 value: tlv::TlvItemValueEnc::StructInvisible(vec![
63 (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
64 (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
65 ]),
66 };
67 Ok(tlv.encode()?)
68}
69
70pub fn encode_remove_scene(group_id: u8, scene_id: u8) -> anyhow::Result<Vec<u8>> {
72 let tlv = tlv::TlvItemEnc {
73 tag: 0,
74 value: tlv::TlvItemValueEnc::StructInvisible(vec![
75 (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
76 (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
77 ]),
78 };
79 Ok(tlv.encode()?)
80}
81
82pub fn encode_remove_all_scenes(group_id: u8) -> anyhow::Result<Vec<u8>> {
84 let tlv = tlv::TlvItemEnc {
85 tag: 0,
86 value: tlv::TlvItemValueEnc::StructInvisible(vec![
87 (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
88 ]),
89 };
90 Ok(tlv.encode()?)
91}
92
93pub fn encode_store_scene(group_id: u8, scene_id: u8) -> anyhow::Result<Vec<u8>> {
95 let tlv = tlv::TlvItemEnc {
96 tag: 0,
97 value: tlv::TlvItemValueEnc::StructInvisible(vec![
98 (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
99 (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
100 ]),
101 };
102 Ok(tlv.encode()?)
103}
104
105pub fn encode_recall_scene(group_id: u8, scene_id: u8, transition_time: Option<u32>) -> anyhow::Result<Vec<u8>> {
107 let tlv = tlv::TlvItemEnc {
108 tag: 0,
109 value: tlv::TlvItemValueEnc::StructInvisible(vec![
110 (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
111 (1, tlv::TlvItemValueEnc::UInt8(scene_id)).into(),
112 (2, tlv::TlvItemValueEnc::UInt32(transition_time.unwrap_or(0))).into(),
113 ]),
114 };
115 Ok(tlv.encode()?)
116}
117
118pub fn encode_get_scene_membership(group_id: u8) -> anyhow::Result<Vec<u8>> {
120 let tlv = tlv::TlvItemEnc {
121 tag: 0,
122 value: tlv::TlvItemValueEnc::StructInvisible(vec![
123 (0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
124 ]),
125 };
126 Ok(tlv.encode()?)
127}
128
129pub fn encode_copy_scene(mode: u8, group_identifier_from: u8, scene_identifier_from: u8, group_identifier_to: u8, scene_identifier_to: u8) -> anyhow::Result<Vec<u8>> {
131 let tlv = tlv::TlvItemEnc {
132 tag: 0,
133 value: tlv::TlvItemValueEnc::StructInvisible(vec![
134 (0, tlv::TlvItemValueEnc::UInt8(mode)).into(),
135 (1, tlv::TlvItemValueEnc::UInt8(group_identifier_from)).into(),
136 (2, tlv::TlvItemValueEnc::UInt8(scene_identifier_from)).into(),
137 (3, tlv::TlvItemValueEnc::UInt8(group_identifier_to)).into(),
138 (4, tlv::TlvItemValueEnc::UInt8(scene_identifier_to)).into(),
139 ]),
140 };
141 Ok(tlv.encode()?)
142}
143
144pub fn decode_do_not_use(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
148 if let tlv::TlvItemValue::Int(v) = inp {
149 Ok(*v as u8)
150 } else {
151 Err(anyhow::anyhow!("Expected Integer"))
152 }
153}
154
155pub fn decode_scene_table_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
157 if let tlv::TlvItemValue::Int(v) = inp {
158 Ok(*v as u16)
159 } else {
160 Err(anyhow::anyhow!("Expected Integer"))
161 }
162}
163
164pub fn decode_fabric_scene_info(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SceneInfo>> {
166 let mut res = Vec::new();
167 if let tlv::TlvItemValue::List(v) = inp {
168 for item in v {
169 res.push(SceneInfo {
170 scene_count: item.get_int(&[0]).map(|v| v as u8),
171 current_scene: item.get_int(&[1]).map(|v| v as u8),
172 current_group: item.get_int(&[2]).map(|v| v as u8),
173 scene_valid: item.get_bool(&[3]),
174 remaining_capacity: item.get_int(&[4]).map(|v| v as u8),
175 });
176 }
177 }
178 Ok(res)
179}
180
181
182pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
194 if cluster_id != 0x0062 {
196 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0062, got {}\"}}", cluster_id);
197 }
198
199 match attribute_id {
200 0x0000 => {
201 match decode_do_not_use(tlv_value) {
202 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
203 Err(e) => format!("{{\"error\": \"{}\"}}", e),
204 }
205 }
206 0x0001 => {
207 match decode_scene_table_size(tlv_value) {
208 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
209 Err(e) => format!("{{\"error\": \"{}\"}}", e),
210 }
211 }
212 0x0002 => {
213 match decode_fabric_scene_info(tlv_value) {
214 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
215 Err(e) => format!("{{\"error\": \"{}\"}}", e),
216 }
217 }
218 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
219 }
220}
221
222pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
227 vec![
228 (0x0000, "DoNotUse"),
229 (0x0001, "SceneTableSize"),
230 (0x0002, "FabricSceneInfo"),
231 ]
232}
233