1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct Action {
15 pub action_id: Option<u16>,
16 pub name: Option<String>,
17 pub type_: Option<u8>,
18 pub endpoint_list_id: Option<u16>,
19 pub supported_commands: Option<u8>,
20 pub state: Option<u8>,
21}
22
23#[derive(Debug, serde::Serialize)]
24pub struct EndpointList {
25 pub endpoint_list_id: Option<u16>,
26 pub name: Option<String>,
27 pub type_: Option<u8>,
28 pub endpoints: Option<Vec<u16>>,
29}
30
31pub fn encode_instant_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
35 let tlv = tlv::TlvItemEnc {
36 tag: 0,
37 value: tlv::TlvItemValueEnc::StructInvisible(vec![
38 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
39 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
40 ]),
41 };
42 Ok(tlv.encode()?)
43}
44
45pub fn encode_instant_action_with_transition(action_id: u16, invoke_id: u32, transition_time: u16) -> anyhow::Result<Vec<u8>> {
47 let tlv = tlv::TlvItemEnc {
48 tag: 0,
49 value: tlv::TlvItemValueEnc::StructInvisible(vec![
50 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
51 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
52 (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
53 ]),
54 };
55 Ok(tlv.encode()?)
56}
57
58pub fn encode_start_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
60 let tlv = tlv::TlvItemEnc {
61 tag: 0,
62 value: tlv::TlvItemValueEnc::StructInvisible(vec![
63 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
64 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
65 ]),
66 };
67 Ok(tlv.encode()?)
68}
69
70pub fn encode_start_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
72 let tlv = tlv::TlvItemEnc {
73 tag: 0,
74 value: tlv::TlvItemValueEnc::StructInvisible(vec![
75 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
76 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
77 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
78 ]),
79 };
80 Ok(tlv.encode()?)
81}
82
83pub fn encode_stop_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
85 let tlv = tlv::TlvItemEnc {
86 tag: 0,
87 value: tlv::TlvItemValueEnc::StructInvisible(vec![
88 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
89 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
90 ]),
91 };
92 Ok(tlv.encode()?)
93}
94
95pub fn encode_pause_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
97 let tlv = tlv::TlvItemEnc {
98 tag: 0,
99 value: tlv::TlvItemValueEnc::StructInvisible(vec![
100 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
101 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
102 ]),
103 };
104 Ok(tlv.encode()?)
105}
106
107pub fn encode_pause_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
109 let tlv = tlv::TlvItemEnc {
110 tag: 0,
111 value: tlv::TlvItemValueEnc::StructInvisible(vec![
112 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
113 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
114 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
115 ]),
116 };
117 Ok(tlv.encode()?)
118}
119
120pub fn encode_resume_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
122 let tlv = tlv::TlvItemEnc {
123 tag: 0,
124 value: tlv::TlvItemValueEnc::StructInvisible(vec![
125 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
126 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
127 ]),
128 };
129 Ok(tlv.encode()?)
130}
131
132pub fn encode_enable_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
134 let tlv = tlv::TlvItemEnc {
135 tag: 0,
136 value: tlv::TlvItemValueEnc::StructInvisible(vec![
137 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
138 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
139 ]),
140 };
141 Ok(tlv.encode()?)
142}
143
144pub fn encode_enable_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
146 let tlv = tlv::TlvItemEnc {
147 tag: 0,
148 value: tlv::TlvItemValueEnc::StructInvisible(vec![
149 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
150 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
151 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
152 ]),
153 };
154 Ok(tlv.encode()?)
155}
156
157pub fn encode_disable_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
159 let tlv = tlv::TlvItemEnc {
160 tag: 0,
161 value: tlv::TlvItemValueEnc::StructInvisible(vec![
162 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
163 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
164 ]),
165 };
166 Ok(tlv.encode()?)
167}
168
169pub fn encode_disable_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
171 let tlv = tlv::TlvItemEnc {
172 tag: 0,
173 value: tlv::TlvItemValueEnc::StructInvisible(vec![
174 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
175 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
176 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
177 ]),
178 };
179 Ok(tlv.encode()?)
180}
181
182pub fn decode_action_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Action>> {
186 let mut res = Vec::new();
187 if let tlv::TlvItemValue::List(v) = inp {
188 for item in v {
189 res.push(Action {
190 action_id: item.get_int(&[0]).map(|v| v as u16),
191 name: item.get_string_owned(&[1]),
192 type_: item.get_int(&[2]).map(|v| v as u8),
193 endpoint_list_id: item.get_int(&[3]).map(|v| v as u16),
194 supported_commands: item.get_int(&[4]).map(|v| v as u8),
195 state: item.get_int(&[5]).map(|v| v as u8),
196 });
197 }
198 }
199 Ok(res)
200}
201
202pub fn decode_endpoint_lists(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<EndpointList>> {
204 let mut res = Vec::new();
205 if let tlv::TlvItemValue::List(v) = inp {
206 for item in v {
207 res.push(EndpointList {
208 endpoint_list_id: item.get_int(&[0]).map(|v| v as u16),
209 name: item.get_string_owned(&[1]),
210 type_: item.get_int(&[2]).map(|v| v as u8),
211 endpoints: {
212 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
213 let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
214 Some(items)
215 } else {
216 None
217 }
218 },
219 });
220 }
221 }
222 Ok(res)
223}
224
225pub fn decode_setup_url(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
227 if let tlv::TlvItemValue::String(v) = inp {
228 Ok(v.clone())
229 } else {
230 Err(anyhow::anyhow!("Expected String"))
231 }
232}
233
234
235pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
247 if cluster_id != 0x0025 {
249 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0025, got {}\"}}", cluster_id);
250 }
251
252 match attribute_id {
253 0x0000 => {
254 match decode_action_list(tlv_value) {
255 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
256 Err(e) => format!("{{\"error\": \"{}\"}}", e),
257 }
258 }
259 0x0001 => {
260 match decode_endpoint_lists(tlv_value) {
261 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
262 Err(e) => format!("{{\"error\": \"{}\"}}", e),
263 }
264 }
265 0x0002 => {
266 match decode_setup_url(tlv_value) {
267 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
268 Err(e) => format!("{{\"error\": \"{}\"}}", e),
269 }
270 }
271 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
272 }
273}
274
275pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
280 vec![
281 (0x0000, "ActionList"),
282 (0x0001, "EndpointLists"),
283 (0x0002, "SetupURL"),
284 ]
285}
286