#![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 DatastoreAccessControlEntryAuthMode {
Pase = 1,
Case = 2,
Group = 3,
}
impl DatastoreAccessControlEntryAuthMode {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(DatastoreAccessControlEntryAuthMode::Pase),
2 => Some(DatastoreAccessControlEntryAuthMode::Case),
3 => Some(DatastoreAccessControlEntryAuthMode::Group),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<DatastoreAccessControlEntryAuthMode> for u8 {
fn from(val: DatastoreAccessControlEntryAuthMode) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DatastoreAccessControlEntryPrivilege {
View = 1,
Proxyview = 2,
Operate = 3,
Manage = 4,
Administer = 5,
}
impl DatastoreAccessControlEntryPrivilege {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(DatastoreAccessControlEntryPrivilege::View),
2 => Some(DatastoreAccessControlEntryPrivilege::Proxyview),
3 => Some(DatastoreAccessControlEntryPrivilege::Operate),
4 => Some(DatastoreAccessControlEntryPrivilege::Manage),
5 => Some(DatastoreAccessControlEntryPrivilege::Administer),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<DatastoreAccessControlEntryPrivilege> for u8 {
fn from(val: DatastoreAccessControlEntryPrivilege) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DatastoreGroupKeyMulticastPolicy {
Pergroupid = 0,
Allnodes = 1,
}
impl DatastoreGroupKeyMulticastPolicy {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(DatastoreGroupKeyMulticastPolicy::Pergroupid),
1 => Some(DatastoreGroupKeyMulticastPolicy::Allnodes),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<DatastoreGroupKeyMulticastPolicy> for u8 {
fn from(val: DatastoreGroupKeyMulticastPolicy) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DatastoreGroupKeySecurityPolicy {
Trustfirst = 0,
}
impl DatastoreGroupKeySecurityPolicy {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(DatastoreGroupKeySecurityPolicy::Trustfirst),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<DatastoreGroupKeySecurityPolicy> for u8 {
fn from(val: DatastoreGroupKeySecurityPolicy) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DatastoreState {
Pending = 0,
Committed = 1,
Deletepending = 2,
Commitfailed = 3,
}
impl DatastoreState {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(DatastoreState::Pending),
1 => Some(DatastoreState::Committed),
2 => Some(DatastoreState::Deletepending),
3 => Some(DatastoreState::Commitfailed),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<DatastoreState> for u8 {
fn from(val: DatastoreState) -> Self {
val as u8
}
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreACLEntry {
pub node_id: Option<u64>,
pub list_id: Option<u16>,
pub acl_entry: Option<DatastoreAccessControlEntry>,
pub status_entry: Option<DatastoreStatusEntry>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreAccessControlEntry {
pub privilege: Option<DatastoreAccessControlEntryPrivilege>,
pub auth_mode: Option<DatastoreAccessControlEntryAuthMode>,
pub subjects: Option<Vec<u64>>,
pub targets: Option<Vec<DatastoreAccessControlTarget>>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreAccessControlTarget {
pub cluster: Option<u32>,
pub endpoint: Option<u16>,
pub device_type: Option<u32>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreAdministratorInformationEntry {
pub node_id: Option<u64>,
pub friendly_name: Option<String>,
pub vendor_id: Option<u16>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub icac: Option<Vec<u8>>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreBindingTarget {
pub node: Option<u64>,
pub group: Option<u8>,
pub endpoint: Option<u16>,
pub cluster: Option<u32>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreEndpointBindingEntry {
pub node_id: Option<u64>,
pub endpoint_id: Option<u16>,
pub list_id: Option<u16>,
pub binding: Option<DatastoreBindingTarget>,
pub status_entry: Option<DatastoreStatusEntry>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreEndpointEntry {
pub endpoint_id: Option<u16>,
pub node_id: Option<u64>,
pub friendly_name: Option<String>,
pub status_entry: Option<DatastoreStatusEntry>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreEndpointGroupIDEntry {
pub node_id: Option<u64>,
pub endpoint_id: Option<u16>,
pub group_id: Option<u8>,
pub status_entry: Option<DatastoreStatusEntry>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreGroupInformationEntry {
pub group_id: Option<u64>,
pub friendly_name: Option<String>,
pub group_key_set_id: Option<u16>,
pub group_cat: Option<u16>,
pub group_cat_version: Option<u16>,
pub group_permission: Option<DatastoreAccessControlEntryPrivilege>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreGroupKeySet {
pub group_key_set_id: Option<u16>,
pub group_key_security_policy: Option<DatastoreGroupKeySecurityPolicy>,
#[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<DatastoreGroupKeyMulticastPolicy>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreNodeInformationEntry {
pub node_id: Option<u64>,
pub friendly_name: Option<String>,
pub commissioning_status_entry: Option<DatastoreStatusEntry>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreNodeKeySetEntry {
pub node_id: Option<u64>,
pub group_key_set_id: Option<u16>,
pub status_entry: Option<DatastoreStatusEntry>,
}
#[derive(Debug, serde::Serialize)]
pub struct DatastoreStatusEntry {
pub state: Option<DatastoreState>,
pub update_timestamp: Option<u64>,
pub failure_code: Option<u8>,
}
pub fn encode_add_key_set(group_key_set: DatastoreGroupKeySet) -> 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_update_key_set(group_key_set: DatastoreGroupKeySet) -> 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_remove_key_set(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_add_group(group_id: u8, friendly_name: String, group_key_set_id: Option<u16>, group_cat: Option<u16>, group_cat_version: Option<u16>, group_permission: DatastoreAccessControlEntryPrivilege) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
(1, tlv::TlvItemValueEnc::String(friendly_name)).into(),
(2, tlv::TlvItemValueEnc::UInt16(group_key_set_id.unwrap_or(0))).into(),
(3, tlv::TlvItemValueEnc::UInt16(group_cat.unwrap_or(0))).into(),
(4, tlv::TlvItemValueEnc::UInt16(group_cat_version.unwrap_or(0))).into(),
(5, tlv::TlvItemValueEnc::UInt8(group_permission.to_u8())).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_update_group(group_id: u8, friendly_name: Option<String>, group_key_set_id: Option<u16>, group_cat: Option<u16>, group_cat_version: Option<u16>, group_permission: Option<DatastoreAccessControlEntryPrivilege>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
(1, tlv::TlvItemValueEnc::String(friendly_name.unwrap_or("".to_string()))).into(),
(2, tlv::TlvItemValueEnc::UInt16(group_key_set_id.unwrap_or(0))).into(),
(3, tlv::TlvItemValueEnc::UInt16(group_cat.unwrap_or(0))).into(),
(4, tlv::TlvItemValueEnc::UInt16(group_cat_version.unwrap_or(0))).into(),
(5, tlv::TlvItemValueEnc::UInt8(group_permission.map(|e| e.to_u8()).unwrap_or(0))).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_group(group_id: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_admin(node_id: u64, friendly_name: String, vendor_id: u16, icac: Vec<u8>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(1, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(2, tlv::TlvItemValueEnc::String(friendly_name)).into(),
(3, tlv::TlvItemValueEnc::UInt16(vendor_id)).into(),
(4, tlv::TlvItemValueEnc::OctetString(icac)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_update_admin(node_id: Option<u64>, friendly_name: Option<String>, icac: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id.unwrap_or(0))).into(),
(1, tlv::TlvItemValueEnc::String(friendly_name.unwrap_or("".to_string()))).into(),
(2, tlv::TlvItemValueEnc::OctetString(icac.unwrap_or(vec![]))).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_admin(node_id: u64) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_pending_node(node_id: u64, friendly_name: String) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(1, tlv::TlvItemValueEnc::String(friendly_name)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_refresh_node(node_id: u64) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_update_node(node_id: u64, friendly_name: String) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(1, tlv::TlvItemValueEnc::String(friendly_name)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_node(node_id: u64) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_update_endpoint_for_node(endpoint_id: u16, node_id: u64, friendly_name: String) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt16(endpoint_id)).into(),
(1, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(2, tlv::TlvItemValueEnc::String(friendly_name)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_group_id_to_endpoint_for_node(node_id: u64, endpoint_id: u16, group_id: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(1, tlv::TlvItemValueEnc::UInt16(endpoint_id)).into(),
(2, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_group_id_from_endpoint_for_node(node_id: u64, endpoint_id: u16, group_id: u8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(1, tlv::TlvItemValueEnc::UInt16(endpoint_id)).into(),
(2, tlv::TlvItemValueEnc::UInt8(group_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_binding_to_endpoint_for_node(node_id: u64, endpoint_id: u16, binding: DatastoreBindingTarget) -> anyhow::Result<Vec<u8>> {
let mut binding_fields = Vec::new();
if let Some(x) = binding.node { binding_fields.push((1, tlv::TlvItemValueEnc::UInt64(x)).into()); }
if let Some(x) = binding.endpoint { binding_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = binding.cluster { binding_fields.push((4, tlv::TlvItemValueEnc::UInt32(x)).into()); }
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(1, tlv::TlvItemValueEnc::UInt16(endpoint_id)).into(),
(2, tlv::TlvItemValueEnc::StructInvisible(binding_fields)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_binding_from_endpoint_for_node(list_id: u16, endpoint_id: u16, node_id: u64) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt16(list_id)).into(),
(1, tlv::TlvItemValueEnc::UInt16(endpoint_id)).into(),
(2, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_add_acl_to_node(node_id: u64, acl_entry: DatastoreAccessControlEntry) -> anyhow::Result<Vec<u8>> {
let mut acl_entry_fields = Vec::new();
if let Some(x) = acl_entry.privilege { acl_entry_fields.push((1, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
if let Some(x) = acl_entry.auth_mode { acl_entry_fields.push((2, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
if let Some(listv) = acl_entry.subjects { acl_entry_fields.push((3, tlv::TlvItemValueEnc::StructAnon(listv.into_iter().map(|x| (0, tlv::TlvItemValueEnc::UInt64(x)).into()).collect())).into()); }
if let Some(listv) = acl_entry.targets {
let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
let mut nested_fields = Vec::new();
if let Some(x) = inner.cluster { nested_fields.push((0, tlv::TlvItemValueEnc::UInt32(x)).into()); }
if let Some(x) = inner.endpoint { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
if let Some(x) = inner.device_type { nested_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
(0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
}).collect();
acl_entry_fields.push((4, tlv::TlvItemValueEnc::Array(inner_vec)).into());
}
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
(1, tlv::TlvItemValueEnc::StructInvisible(acl_entry_fields)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_remove_acl_from_node(list_id: u16, node_id: u64) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt16(list_id)).into(),
(1, tlv::TlvItemValueEnc::UInt64(node_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn decode_anchor_root_ca(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
if let tlv::TlvItemValue::OctetString(v) = inp {
Ok(v.clone())
} else {
Err(anyhow::anyhow!("Expected OctetString"))
}
}
pub fn decode_anchor_node_id(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected UInt64"))
}
}
pub fn decode_anchor_vendor_id(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_friendly_name(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_group_key_set_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreGroupKeySet>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreGroupKeySet {
group_key_set_id: item.get_int(&[0]).map(|v| v as u16),
group_key_security_policy: item.get_int(&[1]).and_then(|v| DatastoreGroupKeySecurityPolicy::from_u8(v as u8)),
epoch_key0: item.get_octet_string_owned(&[2]),
epoch_start_time0: item.get_int(&[3]),
epoch_key1: item.get_octet_string_owned(&[4]),
epoch_start_time1: item.get_int(&[5]),
epoch_key2: item.get_octet_string_owned(&[6]),
epoch_start_time2: item.get_int(&[7]),
group_key_multicast_policy: item.get_int(&[8]).and_then(|v| DatastoreGroupKeyMulticastPolicy::from_u8(v as u8)),
});
}
}
Ok(res)
}
pub fn decode_group_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreGroupInformationEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreGroupInformationEntry {
group_id: item.get_int(&[0]),
friendly_name: item.get_string_owned(&[1]),
group_key_set_id: item.get_int(&[2]).map(|v| v as u16),
group_cat: item.get_int(&[3]).map(|v| v as u16),
group_cat_version: item.get_int(&[4]).map(|v| v as u16),
group_permission: item.get_int(&[5]).and_then(|v| DatastoreAccessControlEntryPrivilege::from_u8(v as u8)),
});
}
}
Ok(res)
}
pub fn decode_node_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreNodeInformationEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreNodeInformationEntry {
node_id: item.get_int(&[1]),
friendly_name: item.get_string_owned(&[2]),
commissioning_status_entry: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(DatastoreStatusEntry {
state: nested_item.get_int(&[0]).and_then(|v| DatastoreState::from_u8(v as u8)),
update_timestamp: nested_item.get_int(&[1]),
failure_code: nested_item.get_int(&[2]).map(|v| v as u8),
})
} else {
None
}
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_admin_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreAdministratorInformationEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreAdministratorInformationEntry {
node_id: item.get_int(&[1]),
friendly_name: item.get_string_owned(&[2]),
vendor_id: item.get_int(&[3]).map(|v| v as u16),
icac: item.get_octet_string_owned(&[4]),
});
}
}
Ok(res)
}
pub fn decode_status(inp: &tlv::TlvItemValue) -> anyhow::Result<DatastoreStatusEntry> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(DatastoreStatusEntry {
state: item.get_int(&[0]).and_then(|v| DatastoreState::from_u8(v as u8)),
update_timestamp: item.get_int(&[1]),
failure_code: item.get_int(&[2]).map(|v| v as u8),
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_endpoint_group_id_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreEndpointGroupIDEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreEndpointGroupIDEntry {
node_id: item.get_int(&[0]),
endpoint_id: item.get_int(&[1]).map(|v| v as u16),
group_id: item.get_int(&[2]).map(|v| v as u8),
status_entry: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(DatastoreStatusEntry {
state: nested_item.get_int(&[0]).and_then(|v| DatastoreState::from_u8(v as u8)),
update_timestamp: nested_item.get_int(&[1]),
failure_code: nested_item.get_int(&[2]).map(|v| v as u8),
})
} else {
None
}
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_endpoint_binding_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreEndpointBindingEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreEndpointBindingEntry {
node_id: item.get_int(&[0]),
endpoint_id: item.get_int(&[1]).map(|v| v as u16),
list_id: item.get_int(&[2]).map(|v| v as u16),
binding: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(DatastoreBindingTarget {
node: nested_item.get_int(&[1]),
group: nested_item.get_int(&[2]).map(|v| v as u8),
endpoint: nested_item.get_int(&[3]).map(|v| v as u16),
cluster: nested_item.get_int(&[4]).map(|v| v as u32),
})
} else {
None
}
} else {
None
}
},
status_entry: {
if let Some(nested_tlv) = item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(DatastoreStatusEntry {
state: nested_item.get_int(&[0]).and_then(|v| DatastoreState::from_u8(v as u8)),
update_timestamp: nested_item.get_int(&[1]),
failure_code: nested_item.get_int(&[2]).map(|v| v as u8),
})
} else {
None
}
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_node_key_set_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreNodeKeySetEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreNodeKeySetEntry {
node_id: item.get_int(&[0]),
group_key_set_id: item.get_int(&[1]).map(|v| v as u16),
status_entry: {
if let Some(nested_tlv) = item.get(&[2]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
Some(DatastoreStatusEntry {
state: nested_item.get_int(&[0]).and_then(|v| DatastoreState::from_u8(v as u8)),
update_timestamp: nested_item.get_int(&[1]),
failure_code: nested_item.get_int(&[2]).map(|v| v as u8),
})
} else {
None
}
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_node_acl_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreACLEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreACLEntry {
node_id: item.get_int(&[0]),
list_id: item.get_int(&[1]).map(|v| v as u16),
acl_entry: {
if let Some(nested_tlv) = item.get(&[2]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
Some(DatastoreAccessControlEntry {
privilege: nested_item.get_int(&[1]).and_then(|v| DatastoreAccessControlEntryPrivilege::from_u8(v as u8)),
auth_mode: nested_item.get_int(&[2]).and_then(|v| DatastoreAccessControlEntryAuthMode::from_u8(v as u8)),
subjects: {
if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[3]) {
let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
Some(items)
} else {
None
}
},
targets: {
if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[4]) {
let mut items = Vec::new();
for list_item in l {
items.push(DatastoreAccessControlTarget {
cluster: list_item.get_int(&[0]).map(|v| v as u32),
endpoint: list_item.get_int(&[1]).map(|v| v as u16),
device_type: list_item.get_int(&[2]).map(|v| v as u32),
});
}
Some(items)
} else {
None
}
},
})
} else {
None
}
} else {
None
}
},
status_entry: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(DatastoreStatusEntry {
state: nested_item.get_int(&[0]).and_then(|v| DatastoreState::from_u8(v as u8)),
update_timestamp: nested_item.get_int(&[1]),
failure_code: nested_item.get_int(&[2]).map(|v| v as u8),
})
} else {
None
}
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_node_endpoint_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DatastoreEndpointEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DatastoreEndpointEntry {
endpoint_id: item.get_int(&[0]).map(|v| v as u16),
node_id: item.get_int(&[1]),
friendly_name: item.get_string_owned(&[2]),
status_entry: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(DatastoreStatusEntry {
state: nested_item.get_int(&[0]).and_then(|v| DatastoreState::from_u8(v as u8)),
update_timestamp: nested_item.get_int(&[1]),
failure_code: nested_item.get_int(&[2]).map(|v| v as u8),
})
} else {
None
}
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x0752 {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0752, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_anchor_root_ca(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_anchor_node_id(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_anchor_vendor_id(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_friendly_name(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_group_key_set_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_group_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_node_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0007 => {
match decode_admin_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0008 => {
match decode_status(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0009 => {
match decode_endpoint_group_id_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000A => {
match decode_endpoint_binding_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000B => {
match decode_node_key_set_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000C => {
match decode_node_acl_list(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000D => {
match decode_node_endpoint_list(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, "AnchorRootCA"),
(0x0001, "AnchorNodeID"),
(0x0002, "AnchorVendorID"),
(0x0003, "FriendlyName"),
(0x0004, "GroupKeySetList"),
(0x0005, "GroupList"),
(0x0006, "NodeList"),
(0x0007, "AdminList"),
(0x0008, "Status"),
(0x0009, "EndpointGroupIDList"),
(0x000A, "EndpointBindingList"),
(0x000B, "NodeKeySetList"),
(0x000C, "NodeACLList"),
(0x000D, "NodeEndpointList"),
]
}
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "AddKeySet"),
(0x01, "UpdateKeySet"),
(0x02, "RemoveKeySet"),
(0x03, "AddGroup"),
(0x04, "UpdateGroup"),
(0x05, "RemoveGroup"),
(0x06, "AddAdmin"),
(0x07, "UpdateAdmin"),
(0x08, "RemoveAdmin"),
(0x09, "AddPendingNode"),
(0x0A, "RefreshNode"),
(0x0B, "UpdateNode"),
(0x0C, "RemoveNode"),
(0x0D, "UpdateEndpointForNode"),
(0x0E, "AddGroupIDToEndpointForNode"),
(0x0F, "RemoveGroupIDFromEndpointForNode"),
(0x10, "AddBindingToEndpointForNode"),
(0x11, "RemoveBindingFromEndpointForNode"),
(0x12, "AddACLToNode"),
(0x13, "RemoveACLFromNode"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("AddKeySet"),
0x01 => Some("UpdateKeySet"),
0x02 => Some("RemoveKeySet"),
0x03 => Some("AddGroup"),
0x04 => Some("UpdateGroup"),
0x05 => Some("RemoveGroup"),
0x06 => Some("AddAdmin"),
0x07 => Some("UpdateAdmin"),
0x08 => Some("RemoveAdmin"),
0x09 => Some("AddPendingNode"),
0x0A => Some("RefreshNode"),
0x0B => Some("UpdateNode"),
0x0C => Some("RemoveNode"),
0x0D => Some("UpdateEndpointForNode"),
0x0E => Some("AddGroupIDToEndpointForNode"),
0x0F => Some("RemoveGroupIDFromEndpointForNode"),
0x10 => Some("AddBindingToEndpointForNode"),
0x11 => Some("RemoveBindingFromEndpointForNode"),
0x12 => Some("AddACLToNode"),
0x13 => Some("RemoveACLFromNode"),
_ => 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: "DatastoreGroupKeySetStruct" }, optional: false, nullable: false },
]),
0x01 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "group_key_set", kind: crate::clusters::codec::FieldKind::Struct { name: "DatastoreGroupKeySetStruct" }, optional: false, nullable: false },
]),
0x02 => 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_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "friendly_name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "group_key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 3, name: "group_cat", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 4, name: "group_cat_version", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 5, name: "group_permission", kind: crate::clusters::codec::FieldKind::Enum { name: "DatastoreAccessControlEntryPrivilege", variants: &[(1, "View"), (2, "Proxyview"), (3, "Operate"), (4, "Manage"), (5, "Administer")] }, optional: false, nullable: false },
]),
0x04 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "group_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "friendly_name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 2, name: "group_key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 3, name: "group_cat", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 4, name: "group_cat_version", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 5, name: "group_permission", kind: crate::clusters::codec::FieldKind::Enum { name: "DatastoreAccessControlEntryPrivilege", variants: &[(1, "View"), (2, "Proxyview"), (3, "Operate"), (4, "Manage"), (5, "Administer")] }, optional: false, nullable: true },
]),
0x05 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "group_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x06 => Some(vec![
crate::clusters::codec::CommandField { tag: 1, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "friendly_name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 3, name: "vendor_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 4, name: "icac", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
]),
0x07 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 1, name: "friendly_name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: true },
crate::clusters::codec::CommandField { tag: 2, name: "icac", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: true },
]),
0x08 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
]),
0x09 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "friendly_name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
]),
0x0A => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
]),
0x0B => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "friendly_name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
]),
0x0C => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
]),
0x0D => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "friendly_name", kind: crate::clusters::codec::FieldKind::String, optional: false, nullable: false },
]),
0x0E => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "group_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x0F => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "group_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x10 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "binding", kind: crate::clusters::codec::FieldKind::Struct { name: "DatastoreBindingTargetStruct" }, optional: false, nullable: false },
]),
0x11 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "list_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "endpoint_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 2, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
]),
0x12 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "acl_entry", kind: crate::clusters::codec::FieldKind::Struct { name: "DatastoreAccessControlEntryStruct" }, optional: false, nullable: false },
]),
0x13 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "list_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
crate::clusters::codec::CommandField { tag: 1, name: "node_id", kind: crate::clusters::codec::FieldKind::U64, 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 => Err(anyhow::anyhow!("command \"AddKeySet\" has complex args: use raw mode")),
0x01 => Err(anyhow::anyhow!("command \"UpdateKeySet\" has complex args: use raw mode")),
0x02 => {
let group_key_set_id = crate::clusters::codec::json_util::get_u16(args, "group_key_set_id")?;
encode_remove_key_set(group_key_set_id)
}
0x03 => {
let group_id = crate::clusters::codec::json_util::get_u8(args, "group_id")?;
let friendly_name = crate::clusters::codec::json_util::get_string(args, "friendly_name")?;
let group_key_set_id = crate::clusters::codec::json_util::get_opt_u16(args, "group_key_set_id")?;
let group_cat = crate::clusters::codec::json_util::get_opt_u16(args, "group_cat")?;
let group_cat_version = crate::clusters::codec::json_util::get_opt_u16(args, "group_cat_version")?;
let group_permission = {
let n = crate::clusters::codec::json_util::get_u64(args, "group_permission")?;
DatastoreAccessControlEntryPrivilege::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid DatastoreAccessControlEntryPrivilege: {}", n))?
};
encode_add_group(group_id, friendly_name, group_key_set_id, group_cat, group_cat_version, group_permission)
}
0x04 => {
let group_id = crate::clusters::codec::json_util::get_u8(args, "group_id")?;
let friendly_name = crate::clusters::codec::json_util::get_opt_string(args, "friendly_name")?;
let group_key_set_id = crate::clusters::codec::json_util::get_opt_u16(args, "group_key_set_id")?;
let group_cat = crate::clusters::codec::json_util::get_opt_u16(args, "group_cat")?;
let group_cat_version = crate::clusters::codec::json_util::get_opt_u16(args, "group_cat_version")?;
let group_permission = crate::clusters::codec::json_util::get_opt_u64(args, "group_permission")?
.and_then(|n| DatastoreAccessControlEntryPrivilege::from_u8(n as u8));
encode_update_group(group_id, friendly_name, group_key_set_id, group_cat, group_cat_version, group_permission)
}
0x05 => {
let group_id = crate::clusters::codec::json_util::get_u8(args, "group_id")?;
encode_remove_group(group_id)
}
0x06 => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
let friendly_name = crate::clusters::codec::json_util::get_string(args, "friendly_name")?;
let vendor_id = crate::clusters::codec::json_util::get_u16(args, "vendor_id")?;
let icac = crate::clusters::codec::json_util::get_octstr(args, "icac")?;
encode_add_admin(node_id, friendly_name, vendor_id, icac)
}
0x07 => {
let node_id = crate::clusters::codec::json_util::get_opt_u64(args, "node_id")?;
let friendly_name = crate::clusters::codec::json_util::get_opt_string(args, "friendly_name")?;
let icac = crate::clusters::codec::json_util::get_opt_octstr(args, "icac")?;
encode_update_admin(node_id, friendly_name, icac)
}
0x08 => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
encode_remove_admin(node_id)
}
0x09 => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
let friendly_name = crate::clusters::codec::json_util::get_string(args, "friendly_name")?;
encode_add_pending_node(node_id, friendly_name)
}
0x0A => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
encode_refresh_node(node_id)
}
0x0B => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
let friendly_name = crate::clusters::codec::json_util::get_string(args, "friendly_name")?;
encode_update_node(node_id, friendly_name)
}
0x0C => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
encode_remove_node(node_id)
}
0x0D => {
let endpoint_id = crate::clusters::codec::json_util::get_u16(args, "endpoint_id")?;
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
let friendly_name = crate::clusters::codec::json_util::get_string(args, "friendly_name")?;
encode_update_endpoint_for_node(endpoint_id, node_id, friendly_name)
}
0x0E => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
let endpoint_id = crate::clusters::codec::json_util::get_u16(args, "endpoint_id")?;
let group_id = crate::clusters::codec::json_util::get_u8(args, "group_id")?;
encode_add_group_id_to_endpoint_for_node(node_id, endpoint_id, group_id)
}
0x0F => {
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
let endpoint_id = crate::clusters::codec::json_util::get_u16(args, "endpoint_id")?;
let group_id = crate::clusters::codec::json_util::get_u8(args, "group_id")?;
encode_remove_group_id_from_endpoint_for_node(node_id, endpoint_id, group_id)
}
0x10 => Err(anyhow::anyhow!("command \"AddBindingToEndpointForNode\" has complex args: use raw mode")),
0x11 => {
let list_id = crate::clusters::codec::json_util::get_u16(args, "list_id")?;
let endpoint_id = crate::clusters::codec::json_util::get_u16(args, "endpoint_id")?;
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
encode_remove_binding_from_endpoint_for_node(list_id, endpoint_id, node_id)
}
0x12 => Err(anyhow::anyhow!("command \"AddACLToNode\" has complex args: use raw mode")),
0x13 => {
let list_id = crate::clusters::codec::json_util::get_u16(args, "list_id")?;
let node_id = crate::clusters::codec::json_util::get_u64(args, "node_id")?;
encode_remove_acl_from_node(list_id, node_id)
}
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
pub async fn add_key_set(conn: &crate::controller::Connection, endpoint: u16, group_key_set: DatastoreGroupKeySet) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_ADDKEYSET, &encode_add_key_set(group_key_set)?).await?;
Ok(())
}
pub async fn update_key_set(conn: &crate::controller::Connection, endpoint: u16, group_key_set: DatastoreGroupKeySet) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_UPDATEKEYSET, &encode_update_key_set(group_key_set)?).await?;
Ok(())
}
pub async fn remove_key_set(conn: &crate::controller::Connection, endpoint: u16, group_key_set_id: u16) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REMOVEKEYSET, &encode_remove_key_set(group_key_set_id)?).await?;
Ok(())
}
pub async fn add_group(conn: &crate::controller::Connection, endpoint: u16, group_id: u8, friendly_name: String, group_key_set_id: Option<u16>, group_cat: Option<u16>, group_cat_version: Option<u16>, group_permission: DatastoreAccessControlEntryPrivilege) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_ADDGROUP, &encode_add_group(group_id, friendly_name, group_key_set_id, group_cat, group_cat_version, group_permission)?).await?;
Ok(())
}
pub async fn update_group(conn: &crate::controller::Connection, endpoint: u16, group_id: u8, friendly_name: Option<String>, group_key_set_id: Option<u16>, group_cat: Option<u16>, group_cat_version: Option<u16>, group_permission: Option<DatastoreAccessControlEntryPrivilege>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_UPDATEGROUP, &encode_update_group(group_id, friendly_name, group_key_set_id, group_cat, group_cat_version, group_permission)?).await?;
Ok(())
}
pub async fn remove_group(conn: &crate::controller::Connection, endpoint: u16, group_id: u8) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REMOVEGROUP, &encode_remove_group(group_id)?).await?;
Ok(())
}
pub async fn add_admin(conn: &crate::controller::Connection, endpoint: u16, node_id: u64, friendly_name: String, vendor_id: u16, icac: Vec<u8>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_ADDADMIN, &encode_add_admin(node_id, friendly_name, vendor_id, icac)?).await?;
Ok(())
}
pub async fn update_admin(conn: &crate::controller::Connection, endpoint: u16, node_id: Option<u64>, friendly_name: Option<String>, icac: Option<Vec<u8>>) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_UPDATEADMIN, &encode_update_admin(node_id, friendly_name, icac)?).await?;
Ok(())
}
pub async fn remove_admin(conn: &crate::controller::Connection, endpoint: u16, node_id: u64) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REMOVEADMIN, &encode_remove_admin(node_id)?).await?;
Ok(())
}
pub async fn add_pending_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64, friendly_name: String) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_ADDPENDINGNODE, &encode_add_pending_node(node_id, friendly_name)?).await?;
Ok(())
}
pub async fn refresh_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REFRESHNODE, &encode_refresh_node(node_id)?).await?;
Ok(())
}
pub async fn update_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64, friendly_name: String) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_UPDATENODE, &encode_update_node(node_id, friendly_name)?).await?;
Ok(())
}
pub async fn remove_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REMOVENODE, &encode_remove_node(node_id)?).await?;
Ok(())
}
pub async fn update_endpoint_for_node(conn: &crate::controller::Connection, endpoint: u16, endpoint_id: u16, node_id: u64, friendly_name: String) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_UPDATEENDPOINTFORNODE, &encode_update_endpoint_for_node(endpoint_id, node_id, friendly_name)?).await?;
Ok(())
}
pub async fn add_group_id_to_endpoint_for_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64, endpoint_id: u16, group_id: u8) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_ADDGROUPIDTOENDPOINTFORNODE, &encode_add_group_id_to_endpoint_for_node(node_id, endpoint_id, group_id)?).await?;
Ok(())
}
pub async fn remove_group_id_from_endpoint_for_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64, endpoint_id: u16, group_id: u8) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REMOVEGROUPIDFROMENDPOINTFORNODE, &encode_remove_group_id_from_endpoint_for_node(node_id, endpoint_id, group_id)?).await?;
Ok(())
}
pub async fn add_binding_to_endpoint_for_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64, endpoint_id: u16, binding: DatastoreBindingTarget) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_ADDBINDINGTOENDPOINTFORNODE, &encode_add_binding_to_endpoint_for_node(node_id, endpoint_id, binding)?).await?;
Ok(())
}
pub async fn remove_binding_from_endpoint_for_node(conn: &crate::controller::Connection, endpoint: u16, list_id: u16, endpoint_id: u16, node_id: u64) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REMOVEBINDINGFROMENDPOINTFORNODE, &encode_remove_binding_from_endpoint_for_node(list_id, endpoint_id, node_id)?).await?;
Ok(())
}
pub async fn add_acl_to_node(conn: &crate::controller::Connection, endpoint: u16, node_id: u64, acl_entry: DatastoreAccessControlEntry) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_ADDACLTONODE, &encode_add_acl_to_node(node_id, acl_entry)?).await?;
Ok(())
}
pub async fn remove_acl_from_node(conn: &crate::controller::Connection, endpoint: u16, list_id: u16, node_id: u64) -> anyhow::Result<()> {
conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_CMD_ID_REMOVEACLFROMNODE, &encode_remove_acl_from_node(list_id, node_id)?).await?;
Ok(())
}
pub async fn read_anchor_root_ca(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<u8>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_ANCHORROOTCA).await?;
decode_anchor_root_ca(&tlv)
}
pub async fn read_anchor_node_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u64> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_ANCHORNODEID).await?;
decode_anchor_node_id(&tlv)
}
pub async fn read_anchor_vendor_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_ANCHORVENDORID).await?;
decode_anchor_vendor_id(&tlv)
}
pub async fn read_friendly_name(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<String> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_FRIENDLYNAME).await?;
decode_friendly_name(&tlv)
}
pub async fn read_group_key_set_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreGroupKeySet>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_GROUPKEYSETLIST).await?;
decode_group_key_set_list(&tlv)
}
pub async fn read_group_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreGroupInformationEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_GROUPLIST).await?;
decode_group_list(&tlv)
}
pub async fn read_node_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreNodeInformationEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_NODELIST).await?;
decode_node_list(&tlv)
}
pub async fn read_admin_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreAdministratorInformationEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_ADMINLIST).await?;
decode_admin_list(&tlv)
}
pub async fn read_status(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<DatastoreStatusEntry> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_STATUS).await?;
decode_status(&tlv)
}
pub async fn read_endpoint_group_id_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreEndpointGroupIDEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_ENDPOINTGROUPIDLIST).await?;
decode_endpoint_group_id_list(&tlv)
}
pub async fn read_endpoint_binding_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreEndpointBindingEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_ENDPOINTBINDINGLIST).await?;
decode_endpoint_binding_list(&tlv)
}
pub async fn read_node_key_set_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreNodeKeySetEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_NODEKEYSETLIST).await?;
decode_node_key_set_list(&tlv)
}
pub async fn read_node_acl_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreACLEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_NODEACLLIST).await?;
decode_node_acl_list(&tlv)
}
pub async fn read_node_endpoint_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DatastoreEndpointEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_JOINT_FABRIC_DATASTORE, crate::clusters::defs::CLUSTER_JOINT_FABRIC_DATASTORE_ATTR_ID_NODEENDPOINTLIST).await?;
decode_node_endpoint_list(&tlv)
}