pub mod acl;
pub mod clusters;
use std::collections::HashMap;
use async_trait::async_trait;
use crate::homeauto::matter::clusters::AttributePath;
use crate::homeauto::matter::error::MatterResult;
use crate::homeauto::matter::interaction_model::{
AttributeData, AttributeStatus, InteractionStatus,
};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Privilege {
View = 1,
ProxyView = 2,
Operate = 3,
Manage = 4,
Administer = 5,
}
#[async_trait]
pub trait ClusterServer: Send + Sync {
fn cluster_id(&self) -> u32;
async fn read_attribute(&self, attr_id: u32) -> MatterResult<Vec<u8>>;
async fn write_attribute(&self, attr_id: u32, value: &[u8]) -> MatterResult<()>;
async fn invoke_command(&self, cmd_id: u32, args: &[u8]) -> MatterResult<Vec<u8>>;
fn attribute_ids(&self) -> Vec<u32>;
fn command_ids(&self) -> Vec<u32>;
}
pub struct DataModelNode {
pub endpoints: HashMap<u16, HashMap<u32, Box<dyn ClusterServer>>>,
}
impl DataModelNode {
pub fn new() -> Self {
Self {
endpoints: HashMap::new(),
}
}
pub fn add_cluster(&mut self, endpoint: u16, cluster: Box<dyn ClusterServer>) {
let cluster_id = cluster.cluster_id();
self.endpoints
.entry(endpoint)
.or_default()
.insert(cluster_id, cluster);
}
pub async fn dispatch_read(&self, path: &AttributePath) -> Vec<AttributeData> {
let mut results = Vec::new();
for (ep_id, clusters) in &self.endpoints {
if let Some(want_ep) = path.endpoint_id
&& *ep_id != want_ep
{
continue;
}
for (cl_id, server) in clusters {
if let Some(want_cl) = path.cluster_id
&& *cl_id != want_cl
{
continue;
}
let attr_ids: Vec<u32> = if let Some(want_attr) = path.attribute_id {
vec![want_attr]
} else {
server.attribute_ids()
};
for attr_id in attr_ids {
match server.read_attribute(attr_id).await {
Ok(data) => {
results.push(AttributeData {
path: AttributePath::specific(*ep_id, *cl_id, attr_id),
data,
});
}
Err(_) => {
}
}
}
}
}
results
}
pub async fn dispatch_invoke(
&self,
endpoint: u16,
cluster_id: u32,
cmd_id: u32,
args: &[u8],
) -> MatterResult<Vec<u8>> {
let ep = self.endpoints.get(&endpoint).ok_or_else(|| {
crate::homeauto::matter::error::MatterError::Transport(format!(
"endpoint {endpoint} not found"
))
})?;
let server = ep.get(&cluster_id).ok_or_else(|| {
crate::homeauto::matter::error::MatterError::Transport(format!(
"cluster {cluster_id:#010x} not found on endpoint {endpoint}"
))
})?;
server.invoke_command(cmd_id, args).await
}
pub async fn dispatch_write(&self, data: &AttributeData) -> AttributeStatus {
let path = &data.path;
let ep_id = match path.endpoint_id {
Some(id) => id,
None => {
return AttributeStatus {
path: path.clone(),
status: InteractionStatus::UnsupportedEndpoint,
};
}
};
let cl_id = match path.cluster_id {
Some(id) => id,
None => {
return AttributeStatus {
path: path.clone(),
status: InteractionStatus::UnsupportedCluster,
};
}
};
let attr_id = match path.attribute_id {
Some(id) => id,
None => {
return AttributeStatus {
path: path.clone(),
status: InteractionStatus::UnsupportedAttribute,
};
}
};
match self.endpoints.get(&ep_id).and_then(|ep| ep.get(&cl_id)) {
None => AttributeStatus {
path: path.clone(),
status: InteractionStatus::UnsupportedCluster,
},
Some(server) => match server.write_attribute(attr_id, &data.data).await {
Ok(()) => AttributeStatus {
path: path.clone(),
status: InteractionStatus::Success,
},
Err(_) => AttributeStatus {
path: path.clone(),
status: InteractionStatus::Failure,
},
},
}
}
}
impl Default for DataModelNode {
fn default() -> Self {
Self::new()
}
}
pub use acl::{AccessControlEntry, AccessControlList, AclTarget};
pub use clusters::{
basic_information::BasicInformationCluster, general_commissioning::GeneralCommissioningCluster,
network_commissioning::NetworkCommissioningCluster,
operational_credentials::OperationalCredentialsCluster,
};