1use crate::error::{Error, ErrorCode};
19use crate::im::{EventFilter, NodeId};
20use crate::tlv::{FromTLV, Nullable, TLVArray, TLVElement, ToTLV};
21
22use super::{AttrId, ClusterId, EndptId, EventPath, EventResp, GenericPath, IMStatusCode, Status};
23
24pub use read::*;
25pub use read_builder::*;
26pub use subscribe::*;
27pub use subscribe_builder::*;
28pub use write::*;
29pub use write_builder::*;
30
31mod read;
32mod read_builder;
33mod subscribe;
34mod subscribe_builder;
35mod write;
36mod write_builder;
37
38#[derive(Default, Clone, Debug, PartialEq, Eq, Hash, FromTLV, ToTLV)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43#[tlvargs(datatype = "list")]
44pub struct AttrPath {
45 pub tag_compression: Option<bool>,
68 pub node: Option<NodeId>,
69 pub endpoint: Option<EndptId>,
70 pub cluster: Option<ClusterId>,
71 pub attr: Option<AttrId>,
72 pub list_index: Option<Nullable<u16>>,
73}
74
75#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81#[repr(u8)]
82pub enum AttrPathTag {
83 TagCompression = 0,
84 Node = 1,
85 Endpoint = 2,
86 Cluster = 3,
87 Attribute = 4,
88 ListIndex = 5,
89}
90
91impl AttrPath {
92 pub const fn from_gp(path: &GenericPath) -> Self {
95 Self {
96 endpoint: path.endpoint,
97 cluster: path.cluster,
98 attr: path.leaf,
99 tag_compression: None,
100 node: None,
101 list_index: None,
102 }
103 }
104
105 pub const fn to_gp(&self) -> GenericPath {
107 GenericPath::new(self.endpoint, self.cluster, self.attr)
108 }
109
110 pub const fn is_wildcard(&self) -> bool {
112 self.endpoint.is_none() || self.cluster.is_none() || self.attr.is_none()
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
120#[cfg_attr(feature = "defmt", derive(defmt::Format))]
121pub struct AttrStatus {
122 pub path: AttrPath,
124 pub status: Status,
126}
127
128impl AttrStatus {
129 pub const fn new(path: AttrPath, status: IMStatusCode, cluster_status: Option<u16>) -> Self {
131 Self {
132 path,
133 status: Status::new(status, cluster_status),
134 }
135 }
136
137 pub const fn from_gp(
144 path: &GenericPath,
145 status: IMStatusCode,
146 cluster_status: Option<u16>,
147 ) -> Self {
148 Self::new(AttrPath::from_gp(path), status, cluster_status)
149 }
150}
151
152#[derive(Debug, Clone, PartialEq, FromTLV, ToTLV)]
156#[cfg_attr(feature = "defmt", derive(defmt::Format))]
157#[tlvargs(lifetime = "'a")]
158pub struct AttrData<'a> {
159 pub data_ver: Option<u32>,
161 pub path: AttrPath,
163 pub data: TLVElement<'a>,
165}
166
167impl<'a> AttrData<'a> {
168 pub const fn new(data_ver: Option<u32>, path: AttrPath, data: TLVElement<'a>) -> Self {
170 Self {
171 data_ver,
172 path,
173 data,
174 }
175 }
176}
177
178#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
183#[cfg_attr(feature = "defmt", derive(defmt::Format))]
184#[repr(u8)]
185pub enum AttrDataTag {
186 DataVer = 0,
187 Path = 1,
188 Data = 2,
189}
190
191#[derive(Clone, FromTLV, ToTLV, PartialEq, Debug)]
195#[cfg_attr(feature = "defmt", derive(defmt::Format))]
196#[tlvargs(lifetime = "'a")]
197pub enum AttrResp<'a> {
198 Status(AttrStatus),
199 Data(AttrData<'a>),
200}
201
202impl<'a> From<AttrData<'a>> for AttrResp<'a> {
203 fn from(value: AttrData<'a>) -> Self {
204 Self::Data(value)
205 }
206}
207
208impl From<AttrStatus> for AttrResp<'_> {
209 fn from(value: AttrStatus) -> Self {
210 Self::Status(value)
211 }
212}
213
214#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
219#[cfg_attr(feature = "defmt", derive(defmt::Format))]
220#[repr(u8)]
221pub enum AttrRespTag {
222 Status = 0,
223 Data = 1,
224}
225
226#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
230#[tlvargs(datatype = "list")]
231#[cfg_attr(feature = "defmt", derive(defmt::Format))]
232pub struct ClusterPath {
233 pub node: Option<u64>,
234 pub endpoint: EndptId,
235 pub cluster: ClusterId,
236}
237
238#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
242#[cfg_attr(feature = "defmt", derive(defmt::Format))]
243pub struct DataVersionFilter {
244 pub path: ClusterPath,
245 pub data_ver: u32,
246}
247
248#[derive(Debug, Clone, Eq, PartialEq, Hash)]
251#[cfg_attr(feature = "defmt", derive(defmt::Format))]
252pub enum ReportDataReq<'a> {
253 Read(&'a ReadReq<'a>),
254 Subscribe(&'a SubscribeReq<'a>),
255 SubscribeReport(&'a SubscribeReq<'a>),
256}
257
258impl<'a> ReportDataReq<'a> {
259 pub fn attr_requests(&self) -> Result<Option<TLVArray<'a, AttrPath>>, Error> {
260 match self {
261 Self::Read(req) => req.attr_requests(),
262 Self::Subscribe(req) | Self::SubscribeReport(req) => req.attr_requests(),
263 }
264 }
265
266 pub fn event_requests(&self) -> Result<Option<TLVArray<'a, EventPath>>, Error> {
267 match self {
268 Self::Read(req) => req.event_requests(),
269 Self::Subscribe(req) | Self::SubscribeReport(req) => req.event_requests(),
270 }
271 }
272
273 pub fn dataver_filters(&self) -> Result<Option<TLVArray<'_, DataVersionFilter>>, Error> {
274 match self {
275 Self::Read(req) => req.dataver_filters(),
276 Self::Subscribe(req) => req.dataver_filters(),
277 Self::SubscribeReport(_) => Ok(None),
278 }
279 }
280
281 pub fn event_filters(&self) -> Result<Option<TLVArray<'_, EventFilter>>, Error> {
282 match self {
283 Self::Read(req) => req.event_filters(),
284 Self::Subscribe(req) | Self::SubscribeReport(req) => req.event_filters(),
285 }
286 }
287
288 pub fn fabric_filtered(&self) -> Result<bool, Error> {
289 match self {
290 Self::Read(req) => req.fabric_filtered(),
291 Self::Subscribe(req) | Self::SubscribeReport(req) => req.fabric_filtered(),
292 }
293 }
294}
295
296#[derive(FromTLV, ToTLV, Debug)]
300#[cfg_attr(feature = "defmt", derive(defmt::Format))]
301#[tlvargs(lifetime = "'a")]
302pub struct ReportDataResp<'a> {
303 pub subscription_id: Option<u32>,
304 pub attr_reports: Option<TLVArray<'a, AttrResp<'a>>>,
305 pub event_reports: Option<TLVArray<'a, EventResp<'a>>>,
306 pub more_chunks: Option<bool>,
307 pub suppress_response: Option<bool>,
308 #[tagval(crate::im::encoding::IM_REVISION_TAG)]
312 pub interaction_model_revision: Option<u8>,
313}
314
315impl<'a> ReportDataResp<'a> {
316 pub fn attrs<T>(
334 &self,
335 cluster: ClusterId,
336 attr: AttrId,
337 ) -> impl Iterator<Item = (EndptId, Result<T, Error>)> + use<'_, 'a, T>
338 where
339 T: FromTLV<'a> + 'a,
340 {
341 self.attr_reports
342 .as_ref()
343 .into_iter()
344 .flat_map(|arr| arr.iter())
345 .filter_map(move |resp| filter_attr_resp::<T>(resp.ok()?, cluster, attr))
346 }
347}
348
349fn filter_attr_resp<'a, T>(
353 resp: AttrResp<'a>,
354 cluster: ClusterId,
355 attr: AttrId,
356) -> Option<(EndptId, Result<T, Error>)>
357where
358 T: FromTLV<'a>,
359{
360 match resp {
361 AttrResp::Data(data) => {
362 if data.path.cluster != Some(cluster) || data.path.attr != Some(attr) {
363 return None;
364 }
365 let endpoint = data.path.endpoint?;
366 Some((endpoint, T::from_tlv(&data.data)))
367 }
368 AttrResp::Status(s) => {
369 if s.path.cluster != Some(cluster) || s.path.attr != Some(attr) {
370 return None;
371 }
372 let endpoint = s.path.endpoint?;
373 let err: Error = s
374 .status
375 .status
376 .to_error_code()
377 .unwrap_or(ErrorCode::Failure)
378 .into();
379 Some((endpoint, Err(err)))
380 }
381 }
382}
383
384#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
389#[cfg_attr(feature = "defmt", derive(defmt::Format))]
390#[repr(u8)]
391pub enum ReportDataRespTag {
392 SubscriptionId = 0,
393 AttributeReports = 1,
394 EventReports = 2,
395 MoreChunkedMsgs = 3,
396 SupressResponse = 4,
397}