#![allow(clippy::too_many_arguments)]
use crate::tlv;
use anyhow;
use serde_json;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ActionError {
Unknown = 0,
Interrupted = 1,
}
impl ActionError {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ActionError::Unknown),
1 => Some(ActionError::Interrupted),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ActionError> for u8 {
fn from(val: ActionError) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ActionState {
Inactive = 0,
Active = 1,
Paused = 2,
Disabled = 3,
}
impl ActionState {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ActionState::Inactive),
1 => Some(ActionState::Active),
2 => Some(ActionState::Paused),
3 => Some(ActionState::Disabled),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ActionState> for u8 {
fn from(val: ActionState) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ActionType {
Other = 0,
Scene = 1,
Sequence = 2,
Automation = 3,
Exception = 4,
Notification = 5,
Alarm = 6,
}
impl ActionType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ActionType::Other),
1 => Some(ActionType::Scene),
2 => Some(ActionType::Sequence),
3 => Some(ActionType::Automation),
4 => Some(ActionType::Exception),
5 => Some(ActionType::Notification),
6 => Some(ActionType::Alarm),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ActionType> for u8 {
fn from(val: ActionType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum EndpointListType {
Other = 0,
Room = 1,
Zone = 2,
}
impl EndpointListType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(EndpointListType::Other),
1 => Some(EndpointListType::Room),
2 => Some(EndpointListType::Zone),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<EndpointListType> for u8 {
fn from(val: EndpointListType) -> Self {
val as u8
}
}
pub type CommandBits = u16;
pub mod commandbits {
pub const INSTANT_ACTION: u16 = 0x01;
pub const INSTANT_ACTION_WITH_TRANSITION: u16 = 0x02;
pub const START_ACTION: u16 = 0x04;
pub const START_ACTION_WITH_DURATION: u16 = 0x08;
pub const STOP_ACTION: u16 = 0x10;
pub const PAUSE_ACTION: u16 = 0x20;
pub const PAUSE_ACTION_WITH_DURATION: u16 = 0x40;
pub const RESUME_ACTION: u16 = 0x80;
pub const ENABLE_ACTION: u16 = 0x100;
pub const ENABLE_ACTION_WITH_DURATION: u16 = 0x200;
pub const DISABLE_ACTION: u16 = 0x400;
pub const DISABLE_ACTION_WITH_DURATION: u16 = 0x800;
}
#[derive(Debug, serde::Serialize)]
pub struct Action {
pub action_id: Option<u16>,
pub name: Option<String>,
pub type_: Option<ActionType>,
pub endpoint_list_id: Option<u16>,
pub supported_commands: Option<u8>,
pub state: Option<ActionState>,
}
#[derive(Debug, serde::Serialize)]
pub struct EndpointList {
pub endpoint_list_id: Option<u16>,
pub name: Option<String>,
pub type_: Option<EndpointListType>,
pub endpoints: Option<Vec<u16>>,
}
pub fn encode_instant_action(action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_instant_action_with_transition(action_id: u16, invoke_id: Option<u32>, transition_time: u16) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
tlv_fields.push((2, tlv::TlvItemValueEnc::UInt16(transition_time)).into());
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_start_action(action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_start_action_with_duration(action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
tlv_fields.push((2, tlv::TlvItemValueEnc::UInt32(duration)).into());
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_stop_action(action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_pause_action(action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_pause_action_with_duration(action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
tlv_fields.push((2, tlv::TlvItemValueEnc::UInt32(duration)).into());
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_resume_action(action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_enable_action(action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_enable_action_with_duration(action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
tlv_fields.push((2, tlv::TlvItemValueEnc::UInt32(duration)).into());
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_disable_action(action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn encode_disable_action_with_duration(action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(action_id)).into());
if let Some(x) = invoke_id { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
tlv_fields.push((2, tlv::TlvItemValueEnc::UInt32(duration)).into());
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn decode_action_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Action>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(Action {
action_id: item.get_int(&[0]).map(|v| v as u16),
name: item.get_string_owned(&[1]),
type_: item.get_int(&[2]).and_then(|v| ActionType::from_u8(v as u8)),
endpoint_list_id: item.get_int(&[3]).map(|v| v as u16),
supported_commands: item.get_int(&[4]).map(|v| v as u8),
state: item.get_int(&[5]).and_then(|v| ActionState::from_u8(v as u8)),
});
}
}
Ok(res)
}
pub fn decode_endpoint_lists(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<EndpointList>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(EndpointList {
endpoint_list_id: item.get_int(&[0]).map(|v| v as u16),
name: item.get_string_owned(&[1]),
type_: item.get_int(&[2]).and_then(|v| EndpointListType::from_u8(v as u8)),
endpoints: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_setup_url(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
if let tlv::TlvItemValue::String(v) = inp {
Ok(v.clone())
} else {
Err(anyhow::anyhow!("Expected String"))
}
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x0025 {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0025, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_action_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_endpoint_lists(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_setup_url(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
_ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
}
}
pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
vec![
(0x0000, "ActionList"),
(0x0001, "EndpointLists"),
(0x0002, "SetupURL"),
]
}
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "InstantAction"),
(0x01, "InstantActionWithTransition"),
(0x02, "StartAction"),
(0x03, "StartActionWithDuration"),
(0x04, "StopAction"),
(0x05, "PauseAction"),
(0x06, "PauseActionWithDuration"),
(0x07, "ResumeAction"),
(0x08, "EnableAction"),
(0x09, "EnableActionWithDuration"),
(0x0A, "DisableAction"),
(0x0B, "DisableActionWithDuration"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("InstantAction"),
0x01 => Some("InstantActionWithTransition"),
0x02 => Some("StartAction"),
0x03 => Some("StartActionWithDuration"),
0x04 => Some("StopAction"),
0x05 => Some("PauseAction"),
0x06 => Some("PauseActionWithDuration"),
0x07 => Some("ResumeAction"),
0x08 => Some("EnableAction"),
0x09 => Some("EnableActionWithDuration"),
0x0A => Some("DisableAction"),
0x0B => Some("DisableActionWithDuration"),
_ => None,
}
}
pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
match cmd_id {
0x00 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
]),
0x01 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "transition_time", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
]),
0x02 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
]),
0x03 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x04 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
]),
0x05 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
]),
0x06 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x07 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
]),
0x08 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
]),
0x09 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x0A => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
]),
0x0B => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "action_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "invoke_id", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "duration", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
_ => None,
}
}
pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
match cmd_id {
0x00 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
encode_instant_action(action_id, invoke_id)
}
0x01 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
let transition_time = crate::clusters::codec::json_util::get_u16(args, "transition_time")?;
encode_instant_action_with_transition(action_id, invoke_id, transition_time)
}
0x02 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
encode_start_action(action_id, invoke_id)
}
0x03 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
encode_start_action_with_duration(action_id, invoke_id, duration)
}
0x04 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
encode_stop_action(action_id, invoke_id)
}
0x05 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
encode_pause_action(action_id, invoke_id)
}
0x06 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
encode_pause_action_with_duration(action_id, invoke_id, duration)
}
0x07 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
encode_resume_action(action_id, invoke_id)
}
0x08 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
encode_enable_action(action_id, invoke_id)
}
0x09 => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
encode_enable_action_with_duration(action_id, invoke_id, duration)
}
0x0A => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
encode_disable_action(action_id, invoke_id)
}
0x0B => {
let action_id = crate::clusters::codec::json_util::get_u16(args, "action_id")?;
let invoke_id = crate::clusters::codec::json_util::get_opt_u32(args, "invoke_id")?;
let duration = crate::clusters::codec::json_util::get_u32(args, "duration")?;
encode_disable_action_with_duration(action_id, invoke_id, duration)
}
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
pub async fn instant_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn instant_action_with_transition(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>, transition_time: u16) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn start_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn start_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn stop_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn pause_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn pause_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn resume_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn enable_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn enable_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn disable_action(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn disable_action_with_duration(conn: &crate::controller::Connection, endpoint: u16, action_id: u16, invoke_id: Option<u32>, duration: u32) -> anyhow::Result<()> {
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?;
Ok(())
}
pub async fn read_action_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Action>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_ATTR_ID_ACTIONLIST).await?;
decode_action_list(&tlv)
}
pub async fn read_endpoint_lists(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<EndpointList>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_ATTR_ID_ENDPOINTLISTS).await?;
decode_endpoint_lists(&tlv)
}
pub async fn read_setup_url(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<String> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACTIONS, crate::clusters::defs::CLUSTER_ACTIONS_ATTR_ID_SETUPURL).await?;
decode_setup_url(&tlv)
}
#[derive(Debug, serde::Serialize)]
pub struct StateChangedEvent {
pub action_id: Option<u16>,
pub invoke_id: Option<u32>,
pub new_state: Option<ActionState>,
}
#[derive(Debug, serde::Serialize)]
pub struct ActionFailedEvent {
pub action_id: Option<u16>,
pub invoke_id: Option<u32>,
pub new_state: Option<ActionState>,
pub error: Option<ActionError>,
}
pub fn decode_state_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<StateChangedEvent> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(StateChangedEvent {
action_id: item.get_int(&[0]).map(|v| v as u16),
invoke_id: item.get_int(&[1]).map(|v| v as u32),
new_state: item.get_int(&[2]).and_then(|v| ActionState::from_u8(v as u8)),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_action_failed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ActionFailedEvent> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(ActionFailedEvent {
action_id: item.get_int(&[0]).map(|v| v as u16),
invoke_id: item.get_int(&[1]).map(|v| v as u32),
new_state: item.get_int(&[2]).and_then(|v| ActionState::from_u8(v as u8)),
error: item.get_int(&[3]).and_then(|v| ActionError::from_u8(v as u8)),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}