1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum ActionError {
18 Unknown = 0,
20 Interrupted = 1,
22}
23
24impl ActionError {
25 pub fn from_u8(value: u8) -> Option<Self> {
27 match value {
28 0 => Some(ActionError::Unknown),
29 1 => Some(ActionError::Interrupted),
30 _ => None,
31 }
32 }
33
34 pub fn to_u8(self) -> u8 {
36 self as u8
37 }
38}
39
40impl From<ActionError> for u8 {
41 fn from(val: ActionError) -> Self {
42 val as u8
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
47#[repr(u8)]
48pub enum ActionState {
49 Inactive = 0,
51 Active = 1,
53 Paused = 2,
55 Disabled = 3,
57}
58
59impl ActionState {
60 pub fn from_u8(value: u8) -> Option<Self> {
62 match value {
63 0 => Some(ActionState::Inactive),
64 1 => Some(ActionState::Active),
65 2 => Some(ActionState::Paused),
66 3 => Some(ActionState::Disabled),
67 _ => None,
68 }
69 }
70
71 pub fn to_u8(self) -> u8 {
73 self as u8
74 }
75}
76
77impl From<ActionState> for u8 {
78 fn from(val: ActionState) -> Self {
79 val as u8
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
84#[repr(u8)]
85pub enum ActionType {
86 Other = 0,
88 Scene = 1,
90 Sequence = 2,
92 Automation = 3,
94 Exception = 4,
96 Notification = 5,
98 Alarm = 6,
100}
101
102impl ActionType {
103 pub fn from_u8(value: u8) -> Option<Self> {
105 match value {
106 0 => Some(ActionType::Other),
107 1 => Some(ActionType::Scene),
108 2 => Some(ActionType::Sequence),
109 3 => Some(ActionType::Automation),
110 4 => Some(ActionType::Exception),
111 5 => Some(ActionType::Notification),
112 6 => Some(ActionType::Alarm),
113 _ => None,
114 }
115 }
116
117 pub fn to_u8(self) -> u8 {
119 self as u8
120 }
121}
122
123impl From<ActionType> for u8 {
124 fn from(val: ActionType) -> Self {
125 val as u8
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
130#[repr(u8)]
131pub enum EndpointListType {
132 Other = 0,
134 Room = 1,
136 Zone = 2,
138}
139
140impl EndpointListType {
141 pub fn from_u8(value: u8) -> Option<Self> {
143 match value {
144 0 => Some(EndpointListType::Other),
145 1 => Some(EndpointListType::Room),
146 2 => Some(EndpointListType::Zone),
147 _ => None,
148 }
149 }
150
151 pub fn to_u8(self) -> u8 {
153 self as u8
154 }
155}
156
157impl From<EndpointListType> for u8 {
158 fn from(val: EndpointListType) -> Self {
159 val as u8
160 }
161}
162
163pub type CommandBits = u16;
167
168pub mod commandbits {
170 pub const INSTANT_ACTION: u16 = 0x01;
172 pub const INSTANT_ACTION_WITH_TRANSITION: u16 = 0x02;
174 pub const START_ACTION: u16 = 0x04;
176 pub const START_ACTION_WITH_DURATION: u16 = 0x08;
178 pub const STOP_ACTION: u16 = 0x10;
180 pub const PAUSE_ACTION: u16 = 0x20;
182 pub const PAUSE_ACTION_WITH_DURATION: u16 = 0x40;
184 pub const RESUME_ACTION: u16 = 0x80;
186 pub const ENABLE_ACTION: u16 = 0x100;
188 pub const ENABLE_ACTION_WITH_DURATION: u16 = 0x200;
190 pub const DISABLE_ACTION: u16 = 0x400;
192 pub const DISABLE_ACTION_WITH_DURATION: u16 = 0x800;
194}
195
196#[derive(Debug, serde::Serialize)]
199pub struct Action {
200 pub action_id: Option<u16>,
201 pub name: Option<String>,
202 pub type_: Option<ActionType>,
203 pub endpoint_list_id: Option<u16>,
204 pub supported_commands: Option<u8>,
205 pub state: Option<ActionState>,
206}
207
208#[derive(Debug, serde::Serialize)]
209pub struct EndpointList {
210 pub endpoint_list_id: Option<u16>,
211 pub name: Option<String>,
212 pub type_: Option<EndpointListType>,
213 pub endpoints: Option<Vec<u16>>,
214}
215
216pub fn encode_instant_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
220 let tlv = tlv::TlvItemEnc {
221 tag: 0,
222 value: tlv::TlvItemValueEnc::StructInvisible(vec![
223 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
224 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
225 ]),
226 };
227 Ok(tlv.encode()?)
228}
229
230pub fn encode_instant_action_with_transition(action_id: u16, invoke_id: u32, transition_time: u16) -> anyhow::Result<Vec<u8>> {
232 let tlv = tlv::TlvItemEnc {
233 tag: 0,
234 value: tlv::TlvItemValueEnc::StructInvisible(vec![
235 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
236 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
237 (2, tlv::TlvItemValueEnc::UInt16(transition_time)).into(),
238 ]),
239 };
240 Ok(tlv.encode()?)
241}
242
243pub fn encode_start_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
245 let tlv = tlv::TlvItemEnc {
246 tag: 0,
247 value: tlv::TlvItemValueEnc::StructInvisible(vec![
248 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
249 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
250 ]),
251 };
252 Ok(tlv.encode()?)
253}
254
255pub fn encode_start_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
257 let tlv = tlv::TlvItemEnc {
258 tag: 0,
259 value: tlv::TlvItemValueEnc::StructInvisible(vec![
260 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
261 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
262 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
263 ]),
264 };
265 Ok(tlv.encode()?)
266}
267
268pub fn encode_stop_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
270 let tlv = tlv::TlvItemEnc {
271 tag: 0,
272 value: tlv::TlvItemValueEnc::StructInvisible(vec![
273 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
274 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
275 ]),
276 };
277 Ok(tlv.encode()?)
278}
279
280pub fn encode_pause_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
282 let tlv = tlv::TlvItemEnc {
283 tag: 0,
284 value: tlv::TlvItemValueEnc::StructInvisible(vec![
285 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
286 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
287 ]),
288 };
289 Ok(tlv.encode()?)
290}
291
292pub fn encode_pause_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
294 let tlv = tlv::TlvItemEnc {
295 tag: 0,
296 value: tlv::TlvItemValueEnc::StructInvisible(vec![
297 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
298 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
299 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
300 ]),
301 };
302 Ok(tlv.encode()?)
303}
304
305pub fn encode_resume_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
307 let tlv = tlv::TlvItemEnc {
308 tag: 0,
309 value: tlv::TlvItemValueEnc::StructInvisible(vec![
310 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
311 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
312 ]),
313 };
314 Ok(tlv.encode()?)
315}
316
317pub fn encode_enable_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
319 let tlv = tlv::TlvItemEnc {
320 tag: 0,
321 value: tlv::TlvItemValueEnc::StructInvisible(vec![
322 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
323 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
324 ]),
325 };
326 Ok(tlv.encode()?)
327}
328
329pub fn encode_enable_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
331 let tlv = tlv::TlvItemEnc {
332 tag: 0,
333 value: tlv::TlvItemValueEnc::StructInvisible(vec![
334 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
335 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
336 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
337 ]),
338 };
339 Ok(tlv.encode()?)
340}
341
342pub fn encode_disable_action(action_id: u16, invoke_id: u32) -> anyhow::Result<Vec<u8>> {
344 let tlv = tlv::TlvItemEnc {
345 tag: 0,
346 value: tlv::TlvItemValueEnc::StructInvisible(vec![
347 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
348 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
349 ]),
350 };
351 Ok(tlv.encode()?)
352}
353
354pub fn encode_disable_action_with_duration(action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<Vec<u8>> {
356 let tlv = tlv::TlvItemEnc {
357 tag: 0,
358 value: tlv::TlvItemValueEnc::StructInvisible(vec![
359 (0, tlv::TlvItemValueEnc::UInt16(action_id)).into(),
360 (1, tlv::TlvItemValueEnc::UInt32(invoke_id)).into(),
361 (2, tlv::TlvItemValueEnc::UInt32(duration)).into(),
362 ]),
363 };
364 Ok(tlv.encode()?)
365}
366
367pub fn decode_action_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Action>> {
371 let mut res = Vec::new();
372 if let tlv::TlvItemValue::List(v) = inp {
373 for item in v {
374 res.push(Action {
375 action_id: item.get_int(&[0]).map(|v| v as u16),
376 name: item.get_string_owned(&[1]),
377 type_: item.get_int(&[2]).and_then(|v| ActionType::from_u8(v as u8)),
378 endpoint_list_id: item.get_int(&[3]).map(|v| v as u16),
379 supported_commands: item.get_int(&[4]).map(|v| v as u8),
380 state: item.get_int(&[5]).and_then(|v| ActionState::from_u8(v as u8)),
381 });
382 }
383 }
384 Ok(res)
385}
386
387pub fn decode_endpoint_lists(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<EndpointList>> {
389 let mut res = Vec::new();
390 if let tlv::TlvItemValue::List(v) = inp {
391 for item in v {
392 res.push(EndpointList {
393 endpoint_list_id: item.get_int(&[0]).map(|v| v as u16),
394 name: item.get_string_owned(&[1]),
395 type_: item.get_int(&[2]).and_then(|v| EndpointListType::from_u8(v as u8)),
396 endpoints: {
397 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
398 let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
399 Some(items)
400 } else {
401 None
402 }
403 },
404 });
405 }
406 }
407 Ok(res)
408}
409
410pub fn decode_setup_url(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
412 if let tlv::TlvItemValue::String(v) = inp {
413 Ok(v.clone())
414 } else {
415 Err(anyhow::anyhow!("Expected String"))
416 }
417}
418
419
420pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
432 if cluster_id != 0x0025 {
434 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0025, got {}\"}}", cluster_id);
435 }
436
437 match attribute_id {
438 0x0000 => {
439 match decode_action_list(tlv_value) {
440 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
441 Err(e) => format!("{{\"error\": \"{}\"}}", e),
442 }
443 }
444 0x0001 => {
445 match decode_endpoint_lists(tlv_value) {
446 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
447 Err(e) => format!("{{\"error\": \"{}\"}}", e),
448 }
449 }
450 0x0002 => {
451 match decode_setup_url(tlv_value) {
452 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
453 Err(e) => format!("{{\"error\": \"{}\"}}", e),
454 }
455 }
456 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
457 }
458}
459
460pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
465 vec![
466 (0x0000, "ActionList"),
467 (0x0001, "EndpointLists"),
468 (0x0002, "SetupURL"),
469 ]
470}
471
472pub fn get_command_list() -> Vec<(u32, &'static str)> {
475 vec![
476 (0x00, "InstantAction"),
477 (0x01, "InstantActionWithTransition"),
478 (0x02, "StartAction"),
479 (0x03, "StartActionWithDuration"),
480 (0x04, "StopAction"),
481 (0x05, "PauseAction"),
482 (0x06, "PauseActionWithDuration"),
483 (0x07, "ResumeAction"),
484 (0x08, "EnableAction"),
485 (0x09, "EnableActionWithDuration"),
486 (0x0A, "DisableAction"),
487 (0x0B, "DisableActionWithDuration"),
488 ]
489}
490
491pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
492 match cmd_id {
493 0x00 => Some("InstantAction"),
494 0x01 => Some("InstantActionWithTransition"),
495 0x02 => Some("StartAction"),
496 0x03 => Some("StartActionWithDuration"),
497 0x04 => Some("StopAction"),
498 0x05 => Some("PauseAction"),
499 0x06 => Some("PauseActionWithDuration"),
500 0x07 => Some("ResumeAction"),
501 0x08 => Some("EnableAction"),
502 0x09 => Some("EnableActionWithDuration"),
503 0x0A => Some("DisableAction"),
504 0x0B => Some("DisableActionWithDuration"),
505 _ => None,
506 }
507}
508
509pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
510 match cmd_id {
511 0x00 => Some(vec![
512 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
513 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
514 ]),
515 0x01 => Some(vec![
516 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
517 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
518 crate::clusters::codec::CommandField { tag: 2, name: "transition_time", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
519 ]),
520 0x02 => Some(vec![
521 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
522 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
523 ]),
524 0x03 => Some(vec![
525 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
526 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
527 crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
528 ]),
529 0x04 => Some(vec![
530 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
531 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
532 ]),
533 0x05 => Some(vec![
534 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
535 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
536 ]),
537 0x06 => Some(vec![
538 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
539 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
540 crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
541 ]),
542 0x07 => Some(vec![
543 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
544 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
545 ]),
546 0x08 => Some(vec![
547 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
548 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
549 ]),
550 0x09 => Some(vec![
551 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
552 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
553 crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
554 ]),
555 0x0A => Some(vec![
556 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
557 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
558 ]),
559 0x0B => Some(vec![
560 crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
561 crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
562 crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
563 ]),
564 _ => None,
565 }
566}
567
568pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
569 match cmd_id {
570 0x00 => {
571 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
572 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
573 encode_instant_action(action_id, invoke_id)
574 }
575 0x01 => {
576 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
577 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
578 let transition_time = crate::clusters::codec::json_util::get_u16(args, "transition_time")?;
579 encode_instant_action_with_transition(action_id, invoke_id, transition_time)
580 }
581 0x02 => {
582 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
583 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
584 encode_start_action(action_id, invoke_id)
585 }
586 0x03 => {
587 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
588 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
589 let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
590 encode_start_action_with_duration(action_id, invoke_id, duration)
591 }
592 0x04 => {
593 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
594 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
595 encode_stop_action(action_id, invoke_id)
596 }
597 0x05 => {
598 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
599 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
600 encode_pause_action(action_id, invoke_id)
601 }
602 0x06 => {
603 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
604 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
605 let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
606 encode_pause_action_with_duration(action_id, invoke_id, duration)
607 }
608 0x07 => {
609 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
610 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
611 encode_resume_action(action_id, invoke_id)
612 }
613 0x08 => {
614 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
615 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
616 encode_enable_action(action_id, invoke_id)
617 }
618 0x09 => {
619 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
620 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
621 let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
622 encode_enable_action_with_duration(action_id, invoke_id, duration)
623 }
624 0x0A => {
625 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
626 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
627 encode_disable_action(action_id, invoke_id)
628 }
629 0x0B => {
630 let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
631 let invoke_id = crate::clusters::codec::json_util::get_u32(args, "invoke_id")?;
632 let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
633 encode_disable_action_with_duration(action_id, invoke_id, duration)
634 }
635 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
636 }
637}
638
639pub async fn instant_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32) -> anyhow::Result<()> {
643 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_INSTANTACTION, &encode_instant_action(action_id, invoke_id)?).await?;
644 Ok(())
645}
646
647pub async fn instant_action_with_transition(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32, transition_time: u16) -> anyhow::Result<()> {
649 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_INSTANTACTIONWITHTRANSITION, &encode_instant_action_with_transition(action_id, invoke_id, transition_time)?).await?;
650 Ok(())
651}
652
653pub async fn start_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32) -> anyhow::Result<()> {
655 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_STARTACTION, &encode_start_action(action_id, invoke_id)?).await?;
656 Ok(())
657}
658
659pub async fn start_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<()> {
661 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_STARTACTIONWITHDURATION, &encode_start_action_with_duration(action_id, invoke_id, duration)?).await?;
662 Ok(())
663}
664
665pub async fn stop_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32) -> anyhow::Result<()> {
667 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_STOPACTION, &encode_stop_action(action_id, invoke_id)?).await?;
668 Ok(())
669}
670
671pub async fn pause_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32) -> anyhow::Result<()> {
673 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_PAUSEACTION, &encode_pause_action(action_id, invoke_id)?).await?;
674 Ok(())
675}
676
677pub async fn pause_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<()> {
679 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_PAUSEACTIONWITHDURATION, &encode_pause_action_with_duration(action_id, invoke_id, duration)?).await?;
680 Ok(())
681}
682
683pub async fn resume_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32) -> anyhow::Result<()> {
685 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_RESUMEACTION, &encode_resume_action(action_id, invoke_id)?).await?;
686 Ok(())
687}
688
689pub async fn enable_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32) -> anyhow::Result<()> {
691 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_ENABLEACTION, &encode_enable_action(action_id, invoke_id)?).await?;
692 Ok(())
693}
694
695pub async fn enable_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<()> {
697 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_ENABLEACTIONWITHDURATION, &encode_enable_action_with_duration(action_id, invoke_id, duration)?).await?;
698 Ok(())
699}
700
701pub async fn disable_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32) -> anyhow::Result<()> {
703 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_DISABLEACTION, &encode_disable_action(action_id, invoke_id)?).await?;
704 Ok(())
705}
706
707pub async fn disable_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: u32, duration: u32) -> anyhow::Result<()> {
709 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_CMD_ID_DISABLEACTIONWITHDURATION, &encode_disable_action_with_duration(action_id, invoke_id, duration)?).await?;
710 Ok(())
711}
712
713pub async fn read_action_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Action>> {
715 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_ATTR_ID_ACTIONLIST).await?;
716 decode_action_list(&tlv)
717}
718
719pub async fn read_endpoint_lists(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<EndpointList>> {
721 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_ATTR_ID_ENDPOINTLISTS).await?;
722 decode_endpoint_lists(&tlv)
723}
724
725pub async fn read_setup_url(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<String> {
727 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_ATTR_ID_SETUPURL).await?;
728 decode_setup_url(&tlv)
729}
730
731#[derive(Debug, serde::Serialize)]
732pub struct StateChangedEvent {
733 pub action_id: Option<u16>,
734 pub invoke_id: Option<u32>,
735 pub new_state: Option<ActionState>,
736}
737
738#[derive(Debug, serde::Serialize)]
739pub struct ActionFailedEvent {
740 pub action_id: Option<u16>,
741 pub invoke_id: Option<u32>,
742 pub new_state: Option<ActionState>,
743 pub error: Option<ActionError>,
744}
745
746pub fn decode_state_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<StateChangedEvent> {
750 if let tlv::TlvItemValue::List(_fields) = inp {
751 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
752 Ok(StateChangedEvent {
753 action_id: item.get_int(&[0]).map(|v| v as u16),
754 invoke_id: item.get_int(&[1]).map(|v| v as u32),
755 new_state: item.get_int(&[2]).and_then(|v| ActionState::from_u8(v as u8)),
756 })
757 } else {
758 Err(anyhow::anyhow!("Expected struct fields"))
759 }
760}
761
762pub fn decode_action_failed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ActionFailedEvent> {
764 if let tlv::TlvItemValue::List(_fields) = inp {
765 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
766 Ok(ActionFailedEvent {
767 action_id: item.get_int(&[0]).map(|v| v as u16),
768 invoke_id: item.get_int(&[1]).map(|v| v as u32),
769 new_state: item.get_int(&[2]).and_then(|v| ActionState::from_u8(v as u8)),
770 error: item.get_int(&[3]).and_then(|v| ActionError::from_u8(v as u8)),
771 })
772 } else {
773 Err(anyhow::anyhow!("Expected struct fields"))
774 }
775}
776