1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum ActionError {
16 Unknown = 0,
18 Interrupted = 1,
20}
21
22impl ActionError {
23 pub fn from_u8(value: u8) -> Option<Self> {
25 match value {
26 0 => Some(ActionError::Unknown),
27 1 => Some(ActionError::Interrupted),
28 _ => None,
29 }
30 }
31
32 pub fn to_u8(self) -> u8 {
34 self as u8
35 }
36}
37
38impl From<ActionError> for u8 {
39 fn from(val: ActionError) -> Self {
40 val as u8
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45#[repr(u8)]
46pub enum ActionState {
47 Inactive = 0,
49 Active = 1,
51 Paused = 2,
53 Disabled = 3,
55}
56
57impl ActionState {
58 pub fn from_u8(value: u8) -> Option<Self> {
60 match value {
61 0 => Some(ActionState::Inactive),
62 1 => Some(ActionState::Active),
63 2 => Some(ActionState::Paused),
64 3 => Some(ActionState::Disabled),
65 _ => None,
66 }
67 }
68
69 pub fn to_u8(self) -> u8 {
71 self as u8
72 }
73}
74
75impl From<ActionState> for u8 {
76 fn from(val: ActionState) -> Self {
77 val as u8
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
82#[repr(u8)]
83pub enum ActionType {
84 Other = 0,
86 Scene = 1,
88 Sequence = 2,
90 Automation = 3,
92 Exception = 4,
94 Notification = 5,
96 Alarm = 6,
98}
99
100impl ActionType {
101 pub fn from_u8(value: u8) -> Option<Self> {
103 match value {
104 0 => Some(ActionType::Other),
105 1 => Some(ActionType::Scene),
106 2 => Some(ActionType::Sequence),
107 3 => Some(ActionType::Automation),
108 4 => Some(ActionType::Exception),
109 5 => Some(ActionType::Notification),
110 6 => Some(ActionType::Alarm),
111 _ => None,
112 }
113 }
114
115 pub fn to_u8(self) -> u8 {
117 self as u8
118 }
119}
120
121impl From<ActionType> for u8 {
122 fn from(val: ActionType) -> Self {
123 val as u8
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
128#[repr(u8)]
129pub enum EndpointListType {
130 Other = 0,
132 Room = 1,
134 Zone = 2,
136}
137
138impl EndpointListType {
139 pub fn from_u8(value: u8) -> Option<Self> {
141 match value {
142 0 => Some(EndpointListType::Other),
143 1 => Some(EndpointListType::Room),
144 2 => Some(EndpointListType::Zone),
145 _ => None,
146 }
147 }
148
149 pub fn to_u8(self) -> u8 {
151 self as u8
152 }
153}
154
155impl From<EndpointListType> for u8 {
156 fn from(val: EndpointListType) -> Self {
157 val as u8
158 }
159}
160
161pub type CommandBits = u16;
165
166pub mod commandbits {
168 pub const INSTANT_ACTION: u16 = 0x01;
170 pub const INSTANT_ACTION_WITH_TRANSITION: u16 = 0x02;
172 pub const START_ACTION: u16 = 0x04;
174 pub const START_ACTION_WITH_DURATION: u16 = 0x08;
176 pub const STOP_ACTION: u16 = 0x10;
178 pub const PAUSE_ACTION: u16 = 0x20;
180 pub const PAUSE_ACTION_WITH_DURATION: u16 = 0x40;
182 pub const RESUME_ACTION: u16 = 0x80;
184 pub const ENABLE_ACTION: u16 = 0x100;
186 pub const ENABLE_ACTION_WITH_DURATION: u16 = 0x200;
188 pub const DISABLE_ACTION: u16 = 0x400;
190 pub const DISABLE_ACTION_WITH_DURATION: u16 = 0x800;
192}
193
194#[derive(Debug, serde::Serialize)]
197pub struct Action {
198 pub action_id: Option<u16>,
199 pub name: Option<String>,
200 pub type_: Option<ActionType>,
201 pub endpoint_list_id: Option<u16>,
202 pub supported_commands: Option<u8>,
203 pub state: Option<ActionState>,
204}
205
206#[derive(Debug, serde::Serialize)]
207pub struct EndpointList {
208 pub endpoint_list_id: Option<u16>,
209 pub name: Option<String>,
210 pub type_: Option<EndpointListType>,
211 pub endpoints: Option<Vec<u16>>,
212}
213
214pub fn encode_instant_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
218 let tlv = tlv::TlvItemEnc {
219 tag: 0,
220 value: tlv::TlvItemValueEnc::StructInvisible(vec![
221 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
222 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
223 ]),
224 };
225 Ok(tlv.encode()?)
226}
227
228pub fn encode_instant_action_with_transition(action_id: u16, invoke_id: u32, transition_time: u16) -> anyhow::Result<Vec<u8>> {
230 let tlv = tlv::TlvItemEnc {
231 tag: 0,
232 value: tlv::TlvItemValueEnc::StructInvisible(vec![
233 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
234 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
235 (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
236 ]),
237 };
238 Ok(tlv.encode()?)
239}
240
241pub fn encode_start_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
243 let tlv = tlv::TlvItemEnc {
244 tag: 0,
245 value: tlv::TlvItemValueEnc::StructInvisible(vec![
246 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
247 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
248 ]),
249 };
250 Ok(tlv.encode()?)
251}
252
253pub fn encode_start_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
255 let tlv = tlv::TlvItemEnc {
256 tag: 0,
257 value: tlv::TlvItemValueEnc::StructInvisible(vec![
258 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
259 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
260 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
261 ]),
262 };
263 Ok(tlv.encode()?)
264}
265
266pub fn encode_stop_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
268 let tlv = tlv::TlvItemEnc {
269 tag: 0,
270 value: tlv::TlvItemValueEnc::StructInvisible(vec![
271 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
272 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
273 ]),
274 };
275 Ok(tlv.encode()?)
276}
277
278pub fn encode_pause_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
280 let tlv = tlv::TlvItemEnc {
281 tag: 0,
282 value: tlv::TlvItemValueEnc::StructInvisible(vec![
283 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
284 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
285 ]),
286 };
287 Ok(tlv.encode()?)
288}
289
290pub fn encode_pause_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
292 let tlv = tlv::TlvItemEnc {
293 tag: 0,
294 value: tlv::TlvItemValueEnc::StructInvisible(vec![
295 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
296 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
297 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
298 ]),
299 };
300 Ok(tlv.encode()?)
301}
302
303pub fn encode_resume_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
305 let tlv = tlv::TlvItemEnc {
306 tag: 0,
307 value: tlv::TlvItemValueEnc::StructInvisible(vec![
308 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
309 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
310 ]),
311 };
312 Ok(tlv.encode()?)
313}
314
315pub fn encode_enable_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
317 let tlv = tlv::TlvItemEnc {
318 tag: 0,
319 value: tlv::TlvItemValueEnc::StructInvisible(vec![
320 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
321 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
322 ]),
323 };
324 Ok(tlv.encode()?)
325}
326
327pub fn encode_enable_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
329 let tlv = tlv::TlvItemEnc {
330 tag: 0,
331 value: tlv::TlvItemValueEnc::StructInvisible(vec![
332 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
333 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
334 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
335 ]),
336 };
337 Ok(tlv.encode()?)
338}
339
340pub fn encode_disable_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
342 let tlv = tlv::TlvItemEnc {
343 tag: 0,
344 value: tlv::TlvItemValueEnc::StructInvisible(vec![
345 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
346 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
347 ]),
348 };
349 Ok(tlv.encode()?)
350}
351
352pub fn encode_disable_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
354 let tlv = tlv::TlvItemEnc {
355 tag: 0,
356 value: tlv::TlvItemValueEnc::StructInvisible(vec![
357 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
358 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
359 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
360 ]),
361 };
362 Ok(tlv.encode()?)
363}
364
365pub fn decode_action_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Action>> {
369 let mut res = Vec::new();
370 if let tlv::TlvItemValue::List(v) = inp {
371 for item in v {
372 res.push(Action {
373 action_id: item.get_int(&[0]).map(|v| v as u16),
374 name: item.get_string_owned(&[1]),
375 type_: item.get_int(&[2]).and_then(|v| ActionType::from_u8(v as u8)),
376 endpoint_list_id: item.get_int(&[3]).map(|v| v as u16),
377 supported_commands: item.get_int(&[4]).map(|v| v as u8),
378 state: item.get_int(&[5]).and_then(|v| ActionState::from_u8(v as u8)),
379 });
380 }
381 }
382 Ok(res)
383}
384
385pub fn decode_endpoint_lists(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<EndpointList>> {
387 let mut res = Vec::new();
388 if let tlv::TlvItemValue::List(v) = inp {
389 for item in v {
390 res.push(EndpointList {
391 endpoint_list_id: item.get_int(&[0]).map(|v| v as u16),
392 name: item.get_string_owned(&[1]),
393 type_: item.get_int(&[2]).and_then(|v| EndpointListType::from_u8(v as u8)),
394 endpoints: {
395 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
396 let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
397 Some(items)
398 } else {
399 None
400 }
401 },
402 });
403 }
404 }
405 Ok(res)
406}
407
408pub fn decode_setup_url(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
410 if let tlv::TlvItemValue::String(v) = inp {
411 Ok(v.clone())
412 } else {
413 Err(anyhow::anyhow!("Expected String"))
414 }
415}
416
417
418pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
430 if cluster_id != 0x0025 {
432 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0025, got {}\"}}", cluster_id);
433 }
434
435 match attribute_id {
436 0x0000 => {
437 match decode_action_list(tlv_value) {
438 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
439 Err(e) => format!("{{\"error\": \"{}\"}}", e),
440 }
441 }
442 0x0001 => {
443 match decode_endpoint_lists(tlv_value) {
444 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
445 Err(e) => format!("{{\"error\": \"{}\"}}", e),
446 }
447 }
448 0x0002 => {
449 match decode_setup_url(tlv_value) {
450 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
451 Err(e) => format!("{{\"error\": \"{}\"}}", e),
452 }
453 }
454 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
455 }
456}
457
458pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
463 vec![
464 (0x0000, "ActionList"),
465 (0x0001, "EndpointLists"),
466 (0x0002, "SetupURL"),
467 ]
468}
469
470#[derive(Debug, serde::Serialize)]
471pub struct StateChangedEvent {
472 pub action_id: Option<u16>,
473 pub invoke_id: Option<u32>,
474 pub new_state: Option<ActionState>,
475}
476
477#[derive(Debug, serde::Serialize)]
478pub struct ActionFailedEvent {
479 pub action_id: Option<u16>,
480 pub invoke_id: Option<u32>,
481 pub new_state: Option<ActionState>,
482 pub error: Option<ActionError>,
483}
484
485pub fn decode_state_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<StateChangedEvent> {
489 if let tlv::TlvItemValue::List(_fields) = inp {
490 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
491 Ok(StateChangedEvent {
492 action_id: item.get_int(&[0]).map(|v| v as u16),
493 invoke_id: item.get_int(&[1]).map(|v| v as u32),
494 new_state: item.get_int(&[2]).and_then(|v| ActionState::from_u8(v as u8)),
495 })
496 } else {
497 Err(anyhow::anyhow!("Expected struct fields"))
498 }
499}
500
501pub fn decode_action_failed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ActionFailedEvent> {
503 if let tlv::TlvItemValue::List(_fields) = inp {
504 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
505 Ok(ActionFailedEvent {
506 action_id: item.get_int(&[0]).map(|v| v as u16),
507 invoke_id: item.get_int(&[1]).map(|v| v as u32),
508 new_state: item.get_int(&[2]).and_then(|v| ActionState::from_u8(v as u8)),
509 error: item.get_int(&[3]).and_then(|v| ActionError::from_u8(v as u8)),
510 })
511 } else {
512 Err(anyhow::anyhow!("Expected struct fields"))
513 }
514}
515