BasicInfo: Add ProductName/VendorName/NodeLabel

This commit is contained in:
Kedar Sovani 2023-08-08 15:28:24 +05:30
parent ce3bf6b6fb
commit 4bb0831168
4 changed files with 86 additions and 5 deletions

View file

@ -72,6 +72,8 @@ fn run() -> Result<(), Error> {
sw_ver_str: "1", sw_ver_str: "1",
serial_no: "aabbccdd", serial_no: "aabbccdd",
device_name: "OnOff Light", device_name: "OnOff Light",
product_name: "Light123",
vendor_name: "Vendor PQR",
}; };
let (ipv4_addr, ipv6_addr, interface) = initialize_network()?; let (ipv4_addr, ipv6_addr, interface) = initialize_network()?;

View file

@ -15,10 +15,15 @@
* limitations under the License. * limitations under the License.
*/ */
use core::convert::TryInto; use core::{cell::RefCell, convert::TryInto};
use super::objects::*; use super::objects::*;
use crate::{attribute_enum, error::Error, utils::rand::Rand}; use crate::{
attribute_enum,
error::{Error, ErrorCode},
utils::rand::Rand,
};
use heapless::String;
use strum::FromRepr; use strum::FromRepr;
pub const ID: u32 = 0x0028; pub const ID: u32 = 0x0028;
@ -27,8 +32,11 @@ pub const ID: u32 = 0x0028;
#[repr(u16)] #[repr(u16)]
pub enum Attributes { pub enum Attributes {
DMRevision(AttrType<u8>) = 0, DMRevision(AttrType<u8>) = 0,
VendorName(AttrUtfType) = 1,
VendorId(AttrType<u16>) = 2, VendorId(AttrType<u16>) = 2,
ProductName(AttrUtfType) = 3,
ProductId(AttrType<u16>) = 4, ProductId(AttrType<u16>) = 4,
NodeLabel(AttrUtfType) = 5,
HwVer(AttrType<u16>) = 7, HwVer(AttrType<u16>) = 7,
SwVer(AttrType<u32>) = 9, SwVer(AttrType<u32>) = 9,
SwVerString(AttrUtfType) = 0xa, SwVerString(AttrUtfType) = 0xa,
@ -39,8 +47,11 @@ attribute_enum!(Attributes);
pub enum AttributesDiscriminants { pub enum AttributesDiscriminants {
DMRevision = 0, DMRevision = 0,
VendorName = 1,
VendorId = 2, VendorId = 2,
ProductName = 3,
ProductId = 4, ProductId = 4,
NodeLabel = 5,
HwVer = 7, HwVer = 7,
SwVer = 9, SwVer = 9,
SwVerString = 0xa, SwVerString = 0xa,
@ -57,6 +68,8 @@ pub struct BasicInfoConfig<'a> {
pub serial_no: &'a str, pub serial_no: &'a str,
/// Device name; up to 32 characters /// Device name; up to 32 characters
pub device_name: &'a str, pub device_name: &'a str,
pub vendor_name: &'a str,
pub product_name: &'a str,
} }
pub const CLUSTER: Cluster<'static> = Cluster { pub const CLUSTER: Cluster<'static> = Cluster {
@ -70,16 +83,31 @@ pub const CLUSTER: Cluster<'static> = Cluster {
Access::RV, Access::RV,
Quality::FIXED, Quality::FIXED,
), ),
Attribute::new(
AttributesDiscriminants::VendorName as u16,
Access::RV,
Quality::FIXED,
),
Attribute::new( Attribute::new(
AttributesDiscriminants::VendorId as u16, AttributesDiscriminants::VendorId as u16,
Access::RV, Access::RV,
Quality::FIXED, Quality::FIXED,
), ),
Attribute::new(
AttributesDiscriminants::ProductName as u16,
Access::RV,
Quality::FIXED,
),
Attribute::new( Attribute::new(
AttributesDiscriminants::ProductId as u16, AttributesDiscriminants::ProductId as u16,
Access::RV, Access::RV,
Quality::FIXED, Quality::FIXED,
), ),
Attribute::new(
AttributesDiscriminants::NodeLabel as u16,
Access::RWVM,
Quality::N,
),
Attribute::new( Attribute::new(
AttributesDiscriminants::HwVer as u16, AttributesDiscriminants::HwVer as u16,
Access::RV, Access::RV,
@ -107,13 +135,16 @@ pub const CLUSTER: Cluster<'static> = Cluster {
pub struct BasicInfoCluster<'a> { pub struct BasicInfoCluster<'a> {
data_ver: Dataver, data_ver: Dataver,
cfg: &'a BasicInfoConfig<'a>, cfg: &'a BasicInfoConfig<'a>,
node_label: RefCell<String<32>>, // Max node-label as per the spec
} }
impl<'a> BasicInfoCluster<'a> { impl<'a> BasicInfoCluster<'a> {
pub fn new(cfg: &'a BasicInfoConfig<'a>, rand: Rand) -> Self { pub fn new(cfg: &'a BasicInfoConfig<'a>, rand: Rand) -> Self {
let node_label = RefCell::new(String::from(""));
Self { Self {
data_ver: Dataver::new(rand), data_ver: Dataver::new(rand),
cfg, cfg,
node_label,
} }
} }
@ -124,8 +155,13 @@ impl<'a> BasicInfoCluster<'a> {
} else { } else {
match attr.attr_id.try_into()? { match attr.attr_id.try_into()? {
Attributes::DMRevision(codec) => codec.encode(writer, 1), Attributes::DMRevision(codec) => codec.encode(writer, 1),
Attributes::VendorName(codec) => codec.encode(writer, self.cfg.vendor_name),
Attributes::VendorId(codec) => codec.encode(writer, self.cfg.vid), Attributes::VendorId(codec) => codec.encode(writer, self.cfg.vid),
Attributes::ProductName(codec) => codec.encode(writer, self.cfg.product_name),
Attributes::ProductId(codec) => codec.encode(writer, self.cfg.pid), Attributes::ProductId(codec) => codec.encode(writer, self.cfg.pid),
Attributes::NodeLabel(codec) => {
codec.encode(writer, self.node_label.borrow().as_str())
}
Attributes::HwVer(codec) => codec.encode(writer, self.cfg.hw_ver), Attributes::HwVer(codec) => codec.encode(writer, self.cfg.hw_ver),
Attributes::SwVer(codec) => codec.encode(writer, self.cfg.sw_ver), Attributes::SwVer(codec) => codec.encode(writer, self.cfg.sw_ver),
Attributes::SwVerString(codec) => codec.encode(writer, self.cfg.sw_ver_str), Attributes::SwVerString(codec) => codec.encode(writer, self.cfg.sw_ver_str),
@ -136,12 +172,35 @@ impl<'a> BasicInfoCluster<'a> {
Ok(()) Ok(())
} }
} }
pub fn write(&self, attr: &AttrDetails, data: AttrData) -> Result<(), Error> {
let data = data.with_dataver(self.data_ver.get())?;
match attr.attr_id.try_into()? {
Attributes::NodeLabel(codec) => {
*self.node_label.borrow_mut() = String::from(
codec
.decode(data)
.map_err(|_| Error::new(ErrorCode::InvalidAction))?,
);
}
_ => return Err(Error::new(ErrorCode::InvalidAction)),
}
self.data_ver.changed();
Ok(())
}
} }
impl<'a> Handler for BasicInfoCluster<'a> { impl<'a> Handler for BasicInfoCluster<'a> {
fn read(&self, attr: &AttrDetails, encoder: AttrDataEncoder) -> Result<(), Error> { fn read(&self, attr: &AttrDetails, encoder: AttrDataEncoder) -> Result<(), Error> {
BasicInfoCluster::read(self, attr, encoder) BasicInfoCluster::read(self, attr, encoder)
} }
fn write(&self, attr: &AttrDetails, data: AttrData) -> Result<(), Error> {
BasicInfoCluster::write(self, attr, data)
}
} }
impl<'a> NonBlockingHandler for BasicInfoCluster<'a> {} impl<'a> NonBlockingHandler for BasicInfoCluster<'a> {}

View file

@ -70,6 +70,8 @@ const BASIC_INFO: BasicInfoConfig<'static> = BasicInfoConfig {
sw_ver_str: "13", sw_ver_str: "13",
serial_no: "aabbccdd", serial_no: "aabbccdd",
device_name: "Test Device", device_name: "Test Device",
product_name: "TestProd",
vendor_name: "TestVendor",
}; };
struct DummyDevAtt; struct DummyDevAtt;

View file

@ -69,18 +69,36 @@ fn wildcard_read_resp(part: u8) -> Vec<AttrResp<'static>> {
basic_info::AttributesDiscriminants::DMRevision, basic_info::AttributesDiscriminants::DMRevision,
dont_care.clone() dont_care.clone()
), ),
attr_data!(
0,
40,
basic_info::AttributesDiscriminants::VendorName,
dont_care.clone()
),
attr_data!( attr_data!(
0, 0,
40, 40,
basic_info::AttributesDiscriminants::VendorId, basic_info::AttributesDiscriminants::VendorId,
dont_care.clone() dont_care.clone()
), ),
attr_data!(
0,
40,
basic_info::AttributesDiscriminants::ProductName,
dont_care.clone()
),
attr_data!( attr_data!(
0, 0,
40, 40,
basic_info::AttributesDiscriminants::ProductId, basic_info::AttributesDiscriminants::ProductId,
dont_care.clone() dont_care.clone()
), ),
attr_data!(
0,
40,
basic_info::AttributesDiscriminants::NodeLabel,
dont_care.clone()
),
attr_data!( attr_data!(
0, 0,
40, 40,
@ -195,6 +213,9 @@ fn wildcard_read_resp(part: u8) -> Vec<AttrResp<'static>> {
adm_comm::AttributesDiscriminants::AdminVendorId, adm_comm::AttributesDiscriminants::AdminVendorId,
dont_care.clone() dont_care.clone()
), ),
];
let part2 = vec![
attr_data!(0, 62, GlobalElements::FeatureMap, dont_care.clone()), attr_data!(0, 62, GlobalElements::FeatureMap, dont_care.clone()),
attr_data!(0, 62, GlobalElements::AttributeList, dont_care.clone()), attr_data!(0, 62, GlobalElements::AttributeList, dont_care.clone()),
attr_data!( attr_data!(
@ -203,9 +224,6 @@ fn wildcard_read_resp(part: u8) -> Vec<AttrResp<'static>> {
noc::AttributesDiscriminants::CurrentFabricIndex, noc::AttributesDiscriminants::CurrentFabricIndex,
dont_care.clone() dont_care.clone()
), ),
];
let part2 = vec![
attr_data!( attr_data!(
0, 0,
62, 62,