#![allow(clippy::too_many_arguments)]
use crate::tlv;
use anyhow;
use serde_json;
use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum GroupKeyMulticastPolicy {
Pergroupid = 0,
Allnodes = 1,
}
impl GroupKeyMulticastPolicy {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(GroupKeyMulticastPolicy::Pergroupid),
1 => Some(GroupKeyMulticastPolicy::Allnodes),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<GroupKeyMulticastPolicy> for u8 {
fn from(val: GroupKeyMulticastPolicy) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum GroupKeySecurityPolicy {
Trustfirst = 0,
Cacheandsync = 1,
}
impl GroupKeySecurityPolicy {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(GroupKeySecurityPolicy::Trustfirst),
1 => Some(GroupKeySecurityPolicy::Cacheandsync),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<GroupKeySecurityPolicy> for u8 {
fn from(val: GroupKeySecurityPolicy) -> Self {
val as u8
}
}
#[derive(Debug, serde::Serialize)]
pub struct GroupInfoMap {
pub group_id: Option<u8>,
pub endpoints: Option<Vec<u16>>,
pub group_name: Option<String>,
}
#[derive(Debug, serde::Serialize)]
pub struct GroupKeyMap {
pub group_id: Option<u8>,
pub group_key_set_id: Option<u16>,
}
#[derive(Debug, serde::Serialize)]
pub struct GroupKeySet {
pub group_key_set_id: Option<u16>,
pub group_key_security_policy: Option<GroupKeySecurityPolicy>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub epoch_key0: Option<Vec<u8>>,
pub epoch_start_time0: Option<u64>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub epoch_key1: Option<Vec<u8>>,
pub epoch_start_time1: Option<u64>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub epoch_key2: Option<Vec<u8>>,
pub epoch_start_time2: Option<u64>,
pub group_key_multicast_policy: Option<GroupKeyMulticastPolicy>,
}
pub fn encode_key_set_write(group_key_set: GroupKeySet) -> anyhow::Result<Vec<u8>> {
let mut group_key_set_fields = Vec::new();
if let Some(x) = group_key_set.group_key_set_id { group_key_set_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = group_key_set.group_key_security_policy { group_key_set_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
if let Some(x) = group_key_set.epoch_key0 { group_key_set_fields.push((2, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
if let Some(x) = group_key_set.epoch_start_time0 { group_key_set_fields.push((3, tlv::TlvItemValueEnc::UInt64(x)).into()); }
if let Some(x) = group_key_set.epoch_key1 { group_key_set_fields.push((4, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
if let Some(x) = group_key_set.epoch_start_time1 { group_key_set_fields.push((5, tlv::TlvItemValueEnc::UInt64(x)).into()); }
if let Some(x) = group_key_set.epoch_key2 { group_key_set_fields.push((6, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
if let Some(x) = group_key_set.epoch_start_time2 { group_key_set_fields.push((7, tlv::TlvItemValueEnc::UInt64(x)).into()); }
if let Some(x) = group_key_set.group_key_multicast_policy { group_key_set_fields.push((8, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::StructInvisible(group_key_set_fields)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_key_set_read(group_key_set_id: u16) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt16(group_key_set_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_key_set_remove(group_key_set_id: u16) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt16(group_key_set_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_key_set_read_all_indices(do_not_use: Option<u8>) -> anyhow::Result<Vec<u8>> {
let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
if let Some(x) = do_not_use { tlv_fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
};
Ok(tlv.encode()?)
}
pub fn decode_group_key_map(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<GroupKeyMap>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(GroupKeyMap {
group_id: item.get_int(&[1]).map(|v| v as u8),
group_key_set_id: item.get_int(&[2]).map(|v| v as u16),
});
}
}
Ok(res)
}
pub fn decode_group_table(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<GroupInfoMap>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(GroupInfoMap {
group_id: item.get_int(&[1]).map(|v| v as u8),
endpoints: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
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
}
},
group_name: item.get_string_owned(&[3]),
});
}
}
Ok(res)
}
pub fn decode_max_groups_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_max_group_keys_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x003F {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x003F, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_group_key_map(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_group_table(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_max_groups_per_fabric(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_max_group_keys_per_fabric(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, "GroupKeyMap"),
(0x0001, "GroupTable"),
(0x0002, "MaxGroupsPerFabric"),
(0x0003, "MaxGroupKeysPerFabric"),
]
}
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "KeySetWrite"),
(0x01, "KeySetRead"),
(0x03, "KeySetRemove"),
(0x04, "KeySetReadAllIndices"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("KeySetWrite"),
0x01 => Some("KeySetRead"),
0x03 => Some("KeySetRemove"),
0x04 => Some("KeySetReadAllIndices"),
_ => 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: "group_key_set", kind: crate::clusters::codec::FieldKind::Struct { name: "GroupKeySetStruct" }, optional: false, nullable: false },
]),
0x01 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "group_key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
]),
0x03 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "group_key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
]),
0x04 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "do_not_use", kind: crate::clusters::codec::FieldKind::U8, optional: true, nullable: false },
]),
_ => None,
}
}
pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
match cmd_id {
0x00 => Err(anyhow::anyhow!("command \"KeySetWrite\" has complex args: use raw mode")),
0x01 => {
let group_key_set_id = crate::clusters::codec::json_util::get_u16(args, "group_key_set_id")?;
encode_key_set_read(group_key_set_id)
}
0x03 => {
let group_key_set_id = crate::clusters::codec::json_util::get_u16(args, "group_key_set_id")?;
encode_key_set_remove(group_key_set_id)
}
0x04 => {
let do_not_use = crate::clusters::codec::json_util::get_opt_u8(args, "do_not_use")?;
encode_key_set_read_all_indices(do_not_use)
}
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
#[derive(Debug, serde::Serialize)]
pub struct KeySetReadResponse {
pub group_key_set: Option<GroupKeySet>,
}
#[derive(Debug, serde::Serialize)]
pub struct KeySetReadAllIndicesResponse {
pub group_key_set_i_ds: Option<Vec<u16>>,
}
pub fn decode_key_set_read_response(inp: &tlv::TlvItemValue) -> anyhow::Result<KeySetReadResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(KeySetReadResponse {
group_key_set: {
if let Some(nested_tlv) = item.get(&[0]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
Some(GroupKeySet {
group_key_set_id: nested_item.get_int(&[0]).map(|v| v as u16),
group_key_security_policy: nested_item.get_int(&[1]).and_then(|v| GroupKeySecurityPolicy::from_u8(v as u8)),
epoch_key0: nested_item.get_octet_string_owned(&[2]),
epoch_start_time0: nested_item.get_int(&[3]),
epoch_key1: nested_item.get_octet_string_owned(&[4]),
epoch_start_time1: nested_item.get_int(&[5]),
epoch_key2: nested_item.get_octet_string_owned(&[6]),
epoch_start_time2: nested_item.get_int(&[7]),
group_key_multicast_policy: nested_item.get_int(&[8]).and_then(|v| GroupKeyMulticastPolicy::from_u8(v as u8)),
})
} else {
None
}
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_key_set_read_all_indices_response(inp: &tlv::TlvItemValue) -> anyhow::Result<KeySetReadAllIndicesResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(KeySetReadAllIndicesResponse {
group_key_set_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
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
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub async fn key_set_write(conn: &crate::controller::Connection, endpoint: u16, group_key_set: GroupKeySet) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETWRITE, &encode_key_set_write(group_key_set)?).await?;
Ok(())
}
pub async fn key_set_read(conn: &crate::controller::Connection, endpoint: u16, group_key_set_id: u16) -> anyhow::Result<KeySetReadResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETREAD, &encode_key_set_read(group_key_set_id)?).await?;
decode_key_set_read_response(&tlv)
}
pub async fn key_set_remove(conn: &crate::controller::Connection, endpoint: u16, group_key_set_id: u16) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETREMOVE, &encode_key_set_remove(group_key_set_id)?).await?;
Ok(())
}
pub async fn key_set_read_all_indices(conn: &crate::controller::Connection, endpoint: u16, do_not_use: Option<u8>) -> anyhow::Result<KeySetReadAllIndicesResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_CMD_ID_KEYSETREADALLINDICES, &encode_key_set_read_all_indices(do_not_use)?).await?;
decode_key_set_read_all_indices_response(&tlv)
}
pub async fn read_group_key_map(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<GroupKeyMap>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_GROUPKEYMAP).await?;
decode_group_key_map(&tlv)
}
pub async fn read_group_table(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<GroupInfoMap>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_GROUPTABLE).await?;
decode_group_table(&tlv)
}
pub async fn read_max_groups_per_fabric(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_MAXGROUPSPERFABRIC).await?;
decode_max_groups_per_fabric(&tlv)
}
pub async fn read_max_group_keys_per_fabric(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUP_KEY_MANAGEMENT, crate::clusters::defs::CLUSTER_GROUP_KEY_MANAGEMENT_ATTR_ID_MAXGROUPKEYSPERFABRIC).await?;
decode_max_group_keys_per_fabric(&tlv)
}