diff --git a/examples/onoff_light/src/main.rs b/examples/onoff_light/src/main.rs index 39de6e4..9350451 100644 --- a/examples/onoff_light/src/main.rs +++ b/examples/onoff_light/src/main.rs @@ -35,6 +35,7 @@ fn main() { pid: 0x8002, hw_ver: 2, sw_ver: 1, + serial_no: "aabbccdd".to_string(), }; let dev_att = Box::new(dev_att::HardCodedDevAtt::new()); diff --git a/matter/src/data_model/cluster_basic_information.rs b/matter/src/data_model/cluster_basic_information.rs index c480f48..d09e4dc 100644 --- a/matter/src/data_model/cluster_basic_information.rs +++ b/matter/src/data_model/cluster_basic_information.rs @@ -17,13 +17,18 @@ use super::objects::*; use crate::error::*; +use num_derive::FromPrimitive; pub const ID: u32 = 0x0028; + +#[derive(FromPrimitive)] enum Attributes { + DMRevision = 0, VendorId = 2, ProductId = 4, HwVer = 7, SwVer = 9, + SerialNo = 0x0f, } pub struct BasicInfoConfig { @@ -31,6 +36,16 @@ pub struct BasicInfoConfig { pub pid: u16, pub hw_ver: u16, pub sw_ver: u32, + pub serial_no: String, +} + +fn attr_dm_rev_new() -> Result { + Attribute::new( + Attributes::DMRevision as u16, + AttrValue::Uint8(1), + Access::RV, + Quality::FIXED, + ) } fn attr_vid_new(vid: u16) -> Result { @@ -69,6 +84,14 @@ fn attr_sw_ver_new(sw_ver: u32) -> Result { ) } +fn attr_serial_no_new(label: String) -> Result { + Attribute::new( + Attributes::SerialNo as u16, + AttrValue::Utf8(label), + Access::RV, + Quality::FIXED, + ) +} pub struct BasicInfoCluster { base: Cluster, } @@ -78,10 +101,14 @@ impl BasicInfoCluster { let mut cluster = Box::new(BasicInfoCluster { base: Cluster::new(ID)?, }); + cluster.base.add_attribute(attr_dm_rev_new()?)?; cluster.base.add_attribute(attr_vid_new(cfg.vid)?)?; cluster.base.add_attribute(attr_pid_new(cfg.pid)?)?; cluster.base.add_attribute(attr_hw_ver_new(cfg.hw_ver)?)?; cluster.base.add_attribute(attr_sw_ver_new(cfg.sw_ver)?)?; + cluster + .base + .add_attribute(attr_serial_no_new(cfg.serial_no)?)?; Ok(cluster) } } diff --git a/matter/src/data_model/core.rs b/matter/src/data_model/core.rs index 11bf060..e6fcf7c 100644 --- a/matter/src/data_model/core.rs +++ b/matter/src/data_model/core.rs @@ -84,7 +84,7 @@ impl DataModel { ) -> Result { let node = self.node.read().unwrap(); let cluster = node.get_cluster(endpoint, cluster)?; - cluster.base().read_attribute_raw(attr).map(|a| *a) + cluster.base().read_attribute_raw(attr).map(|a| a.clone()) } // Encode a write attribute from a path that may or may not be wildcard diff --git a/matter/src/data_model/device_types.rs b/matter/src/data_model/device_types.rs index a0c9c28..b621ce1 100644 --- a/matter/src/data_model/device_types.rs +++ b/matter/src/data_model/device_types.rs @@ -32,6 +32,11 @@ use crate::secure_channel::pake::PaseMgr; use std::sync::Arc; use std::sync::RwLockWriteGuard; +pub const DEV_TYPE_ROOT_NODE: DeviceType = DeviceType { + dtype: 0x0016, + drev: 1, +}; + type WriteNode<'a> = RwLockWriteGuard<'a, Box>; pub fn device_type_add_root_node( @@ -43,7 +48,7 @@ pub fn device_type_add_root_node( pase_mgr: PaseMgr, ) -> Result { // Add the root endpoint - let endpoint = node.add_endpoint()?; + let endpoint = node.add_endpoint(DEV_TYPE_ROOT_NODE)?; if endpoint != 0 { // Somehow endpoint 0 was already added, this shouldn't be the case return Err(Error::Invalid); @@ -63,8 +68,13 @@ pub fn device_type_add_root_node( Ok(endpoint) } +const DEV_TYPE_ON_OFF_LIGHT: DeviceType = DeviceType { + dtype: 0x0100, + drev: 2, +}; + pub fn device_type_add_on_off_light(node: &mut WriteNode) -> Result { - let endpoint = node.add_endpoint()?; + let endpoint = node.add_endpoint(DEV_TYPE_ON_OFF_LIGHT)?; node.add_cluster(endpoint, OnOffCluster::new()?)?; Ok(endpoint) } diff --git a/matter/src/data_model/objects/attribute.rs b/matter/src/data_model/objects/attribute.rs index 5498875..0ffbc73 100644 --- a/matter/src/data_model/objects/attribute.rs +++ b/matter/src/data_model/objects/attribute.rs @@ -88,7 +88,7 @@ bitflags! { * - instead of arrays, can use linked-lists to conserve space and avoid the internal fragmentation */ -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq, Clone)] pub enum AttrValue { Int64(i64), Uint8(u8), @@ -96,6 +96,7 @@ pub enum AttrValue { Uint32(u32), Uint64(u64), Bool(bool), + Utf8(String), Custom, } @@ -108,6 +109,7 @@ impl Debug for AttrValue { AttrValue::Uint32(v) => write!(f, "{:?}", *v), AttrValue::Uint64(v) => write!(f, "{:?}", *v), AttrValue::Bool(v) => write!(f, "{:?}", *v), + AttrValue::Utf8(v) => write!(f, "{:?}", *v), AttrValue::Custom => write!(f, "custom-attribute"), }?; Ok(()) @@ -123,6 +125,7 @@ impl ToTLV for AttrValue { AttrValue::Uint16(v) => tw.u16(tag_type, *v), AttrValue::Uint32(v) => tw.u32(tag_type, *v), AttrValue::Uint64(v) => tw.u64(tag_type, *v), + AttrValue::Utf8(v) => tw.utf8(tag_type, v.as_bytes()), _ => { error!("Attribute type not yet supported"); Err(Error::AttributeNotFound) diff --git a/matter/src/data_model/objects/cluster.rs b/matter/src/data_model/objects/cluster.rs index a42aac2..d279363 100644 --- a/matter/src/data_model/objects/cluster.rs +++ b/matter/src/data_model/objects/cluster.rs @@ -87,7 +87,6 @@ pub trait ClusterType { pub struct Cluster { pub(super) id: u32, attributes: Vec, - feature_map: Option, data_ver: u32, } @@ -96,7 +95,6 @@ impl Cluster { let mut c = Cluster { id, attributes: Vec::with_capacity(ATTRS_PER_CLUSTER), - feature_map: None, data_ver: rand::thread_rng().gen_range(0..0xFFFFFFFF), }; c.add_default_attributes()?; @@ -112,22 +110,20 @@ impl Cluster { } pub fn set_feature_map(&mut self, map: u32) -> Result<(), Error> { - if self.feature_map.is_none() { - self.add_attribute(Attribute::new( - GlobalElements::FeatureMap as u16, - AttrValue::Uint32(map), - Access::RV, - Quality::NONE, - )?)?; - } else { - self.write_attribute_raw(GlobalElements::FeatureMap as u16, AttrValue::Uint32(map)) - .map_err(|_| Error::Invalid)?; - } - self.feature_map = Some(map); + self.write_attribute_raw(GlobalElements::FeatureMap as u16, AttrValue::Uint32(map)) + .map_err(|_| Error::Invalid)?; Ok(()) } fn add_default_attributes(&mut self) -> Result<(), Error> { + // Default feature map is 0 + self.add_attribute(Attribute::new( + GlobalElements::FeatureMap as u16, + AttrValue::Uint32(0), + Access::RV, + Quality::NONE, + )?)?; + self.add_attribute(Attribute::new( GlobalElements::AttributeList as u16, AttrValue::Custom, @@ -233,8 +229,7 @@ impl Cluster { return; } GlobalElements::FeatureMap => { - let val = if let Some(m) = self.feature_map { m } else { 0 }; - encoder.encode(EncodeValue::Value(&val)); + encoder.encode(EncodeValue::Value(&attr.value)); return; } _ => { @@ -284,7 +279,7 @@ impl Cluster { ) -> Result<(), IMStatusCode> { let a = self.get_attribute_mut(attr_id)?; if a.value != AttrValue::Custom { - let mut value = a.value; + let mut value = a.value.clone(); value .update_from_tlv(data) .map_err(|_| IMStatusCode::Failure)?; diff --git a/matter/src/data_model/objects/encoder.rs b/matter/src/data_model/objects/encoder.rs index 3e756d8..24a81aa 100644 --- a/matter/src/data_model/objects/encoder.rs +++ b/matter/src/data_model/objects/encoder.rs @@ -115,3 +115,9 @@ pub trait Encoder { /// Encode a status report fn encode_status(&mut self, status: IMStatusCode, cluster_status: u16); } + +#[derive(ToTLV, Copy, Clone)] +pub struct DeviceType { + pub dtype: u16, + pub drev: u16, +} diff --git a/matter/src/data_model/objects/endpoint.rs b/matter/src/data_model/objects/endpoint.rs index a87f887..7ea22dd 100644 --- a/matter/src/data_model/objects/endpoint.rs +++ b/matter/src/data_model/objects/endpoint.rs @@ -19,17 +19,21 @@ use crate::{data_model::objects::ClusterType, error::*, interaction_model::core: use std::fmt; +use super::DeviceType; + pub const CLUSTERS_PER_ENDPT: usize = 9; pub struct Endpoint { + dev_type: DeviceType, clusters: Vec>, } pub type BoxedClusters = [Box]; impl Endpoint { - pub fn new() -> Result, Error> { + pub fn new(dev_type: DeviceType) -> Result, Error> { Ok(Box::new(Endpoint { + dev_type, clusters: Vec::with_capacity(CLUSTERS_PER_ENDPT), })) } @@ -43,6 +47,10 @@ impl Endpoint { } } + pub fn get_dev_type(&self) -> &DeviceType { + &self.dev_type + } + fn get_cluster_index(&self, cluster_id: u32) -> Option { self.clusters.iter().position(|c| c.base().id == cluster_id) } diff --git a/matter/src/data_model/objects/node.rs b/matter/src/data_model/objects/node.rs index 8e391cf..1f2b7cf 100644 --- a/matter/src/data_model/objects/node.rs +++ b/matter/src/data_model/objects/node.rs @@ -23,6 +23,8 @@ use crate::{ }; use std::fmt; +use super::DeviceType; + pub trait ChangeConsumer { fn endpoint_added(&self, id: u16, endpoint: &mut Endpoint) -> Result<(), Error>; } @@ -59,13 +61,13 @@ impl Node { self.changes_cb = Some(consumer); } - pub fn add_endpoint(&mut self) -> Result { + pub fn add_endpoint(&mut self, dev_type: DeviceType) -> Result { let index = self .endpoints .iter() .position(|x| x.is_none()) .ok_or(Error::NoSpace)?; - let mut endpoint = Endpoint::new()?; + let mut endpoint = Endpoint::new(dev_type)?; if let Some(cb) = &self.changes_cb { cb.endpoint_added(index as u16, &mut endpoint)?; } diff --git a/matter/src/data_model/system_model/descriptor.rs b/matter/src/data_model/system_model/descriptor.rs index 4609a73..29fb15b 100644 --- a/matter/src/data_model/system_model/descriptor.rs +++ b/matter/src/data_model/system_model/descriptor.rs @@ -21,7 +21,7 @@ use crate::data_model::core::DataModel; use crate::data_model::objects::*; use crate::error::*; use crate::interaction_model::messages::GenericPath; -use crate::tlv::{TLVWriter, TagType}; +use crate::tlv::{TLVWriter, TagType, ToTLV}; use log::error; pub const ID: u32 = 0x001D; @@ -48,11 +48,28 @@ impl DescriptorCluster { data_model, base: Cluster::new(ID)?, }); + c.base.add_attribute(attr_devtypelist_new()?)?; c.base.add_attribute(attr_serverlist_new()?)?; c.base.add_attribute(attr_partslist_new()?)?; Ok(c) } + fn encode_devtype_list(&self, tag: TagType, tw: &mut TLVWriter) { + let path = GenericPath { + endpoint: Some(self.endpoint_id), + cluster: None, + leaf: None, + }; + let _ = tw.start_array(tag); + let dm = self.data_model.node.read().unwrap(); + let _ = dm.for_each_endpoint(&path, |_, e| { + let dev_type = e.get_dev_type(); + let _ = dev_type.to_tlv(tw, TagType::Anonymous); + Ok(()) + }); + let _ = tw.end_container(); + } + fn encode_server_list(&self, tag: TagType, tw: &mut TLVWriter) { let path = GenericPath { endpoint: Some(self.endpoint_id), @@ -69,8 +86,24 @@ impl DescriptorCluster { } fn encode_parts_list(&self, tag: TagType, tw: &mut TLVWriter) { - // TODO: Support Partslist + let path = GenericPath { + endpoint: None, + cluster: None, + leaf: None, + }; let _ = tw.start_array(tag); + if self.endpoint_id == 0 { + // TODO: If endpoint is another than 0, need to figure out what to do + let dm = self.data_model.node.read().unwrap(); + let _ = dm.for_each_endpoint(&path, |current_path, _| { + if let Some(endpoint_id) = current_path.endpoint { + if endpoint_id != 0 { + let _ = tw.u16(TagType::Anonymous, endpoint_id); + } + } + Ok(()) + }); + } let _ = tw.end_container(); } } @@ -85,6 +118,9 @@ impl ClusterType for DescriptorCluster { fn read_custom_attribute(&self, encoder: &mut dyn Encoder, attr: &AttrDetails) { match num::FromPrimitive::from_u16(attr.attr_id) { + Some(Attributes::DeviceTypeList) => encoder.encode(EncodeValue::Closure(&|tag, tw| { + self.encode_devtype_list(tag, tw) + })), Some(Attributes::ServerList) => encoder.encode(EncodeValue::Closure(&|tag, tw| { self.encode_server_list(tag, tw) })), @@ -98,6 +134,14 @@ impl ClusterType for DescriptorCluster { } } +fn attr_devtypelist_new() -> Result { + Attribute::new( + Attributes::DeviceTypeList as u16, + AttrValue::Custom, + Access::RV, + Quality::NONE, + ) +} fn attr_serverlist_new() -> Result { Attribute::new( Attributes::ServerList as u16, diff --git a/matter/tests/common/im_engine.rs b/matter/tests/common/im_engine.rs index feddb96..45f8fe5 100644 --- a/matter/tests/common/im_engine.rs +++ b/matter/tests/common/im_engine.rs @@ -94,6 +94,7 @@ impl ImEngine { pid: 11, hw_ver: 12, sw_ver: 13, + serial_no: "aabbccdd".to_string(), }; let dev_att = Box::new(DummyDevAtt {}); let fabric_mgr = Arc::new(FabricMgr::new().unwrap()); diff --git a/matter/tests/data_model/acl_and_dataver.rs b/matter/tests/data_model/acl_and_dataver.rs index 11e3576..3dd2f60 100644 --- a/matter/tests/data_model/acl_and_dataver.rs +++ b/matter/tests/data_model/acl_and_dataver.rs @@ -204,10 +204,10 @@ fn read_cluster_id_write_attr(im: &ImEngine, endpoint: u16) -> AttrValue { let node = im.dm.node.read().unwrap(); let echo = node.get_cluster(endpoint, echo_cluster::ID).unwrap(); - *echo - .base() + echo.base() .read_attribute_raw(echo_cluster::Attributes::AttWrite as u16) .unwrap() + .clone() } fn read_cluster_id_data_ver(im: &ImEngine, endpoint: u16) -> u32 {