1use num::FromPrimitive;
28use num_derive::FromPrimitive;
29
30use crate::error::{Error, ErrorCode};
31use crate::tlv::{FromTLV, TLVElement, TLVTag, TLVWrite, ToTLV, TLV};
32use crate::transport::exchange::MessageMeta;
33
34pub use attr::*;
35pub use event::*;
36pub use invoke::*;
37pub use invoke_builder::*;
38pub use status::*;
39pub use timed::*;
40pub use types::*;
41
42mod attr;
43mod event;
44mod invoke;
45mod invoke_builder;
46mod status;
47mod timed;
48pub(crate) mod types;
49
50pub type IMBuffer = crate::transport::exchange::Buffer;
53
54pub const PROTO_ID_INTERACTION_MODEL: u16 = 0x01;
56
57pub const IM_REVISION: u8 = 13;
68
69pub const IM_REVISION_TAG: u8 = 0xFF;
72
73pub const FABRIC_INDEX_TAG: u8 = 0xFE;
76
77#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq, Hash)]
79#[cfg_attr(feature = "defmt", derive(defmt::Format))]
80pub enum IMStatusCode {
81 Success = 0,
82 Failure = 1,
83 InvalidSubscription = 0x7D,
84 UnsupportedAccess = 0x7E,
85 UnsupportedEndpoint = 0x7F,
86 InvalidAction = 0x80,
87 UnsupportedCommand = 0x81,
88 InvalidCommand = 0x85,
89 UnsupportedAttribute = 0x86,
90 ConstraintError = 0x87,
91 UnsupportedWrite = 0x88,
92 ResourceExhausted = 0x89,
93 NotFound = 0x8b,
94 UnreportableAttribute = 0x8c,
95 InvalidDataType = 0x8d,
96 UnsupportedRead = 0x8f,
97 DataVersionMismatch = 0x92,
98 Timeout = 0x94,
99 UnsupportedNode = 0x9b,
100 Busy = 0x9c,
101 UnsupportedCluster = 0xc3,
102 NoUpstreamSubscription = 0xc5,
103 NeedsTimedInteraction = 0xc6,
104 UnsupportedEvent = 0xc7,
105 PathsExhausted = 0xc8,
106 TimedRequestMisMatch = 0xc9,
107 FailSafeRequired = 0xca,
108 InvalidInState = 0xcb,
109 NoCommandResponse = 0xcc,
110 DynamicConstraintError = 0xcf,
111 AlreadyExists = 0xd0,
112}
113
114impl From<ErrorCode> for IMStatusCode {
115 fn from(e: ErrorCode) -> Self {
116 match e {
117 ErrorCode::NodeNotFound => IMStatusCode::UnsupportedNode,
118 ErrorCode::EndpointNotFound => IMStatusCode::UnsupportedEndpoint,
119 ErrorCode::ClusterNotFound => IMStatusCode::UnsupportedCluster,
120 ErrorCode::AttributeNotFound => IMStatusCode::UnsupportedAttribute,
121 ErrorCode::CommandNotFound => IMStatusCode::UnsupportedCommand,
122 ErrorCode::EventNotFound => IMStatusCode::UnsupportedEvent,
123 ErrorCode::InvalidAction => IMStatusCode::InvalidAction,
124 ErrorCode::InvalidCommand => IMStatusCode::InvalidCommand,
125 ErrorCode::InvalidDataType => IMStatusCode::InvalidDataType,
126 ErrorCode::UnsupportedAccess => IMStatusCode::UnsupportedAccess,
127 ErrorCode::Busy => IMStatusCode::Busy,
128 ErrorCode::DataVersionMismatch => IMStatusCode::DataVersionMismatch,
129 ErrorCode::ResourceExhausted => IMStatusCode::ResourceExhausted,
130 ErrorCode::FailSafeRequired => IMStatusCode::FailSafeRequired,
131 ErrorCode::NeedsTimedInteraction => IMStatusCode::NeedsTimedInteraction,
132 ErrorCode::ConstraintError => IMStatusCode::ConstraintError,
133 ErrorCode::DynamicConstraintError => IMStatusCode::DynamicConstraintError,
134 ErrorCode::NotFound => IMStatusCode::NotFound,
135 ErrorCode::AlreadyExists => IMStatusCode::AlreadyExists,
136 ErrorCode::Failure => IMStatusCode::Failure,
137 _ => IMStatusCode::Failure,
138 }
139 }
140}
141
142impl From<Error> for IMStatusCode {
143 fn from(value: Error) -> Self {
144 Self::from(value.code())
145 }
146}
147
148impl IMStatusCode {
149 pub fn to_error_code(self) -> Option<ErrorCode> {
153 match self {
154 Self::Success => None,
155 Self::UnsupportedAccess => Some(ErrorCode::UnsupportedAccess),
156 Self::InvalidAction => Some(ErrorCode::InvalidAction),
157 Self::UnsupportedCommand => Some(ErrorCode::CommandNotFound),
158 Self::InvalidCommand => Some(ErrorCode::InvalidCommand),
159 Self::UnsupportedAttribute => Some(ErrorCode::AttributeNotFound),
160 Self::ConstraintError => Some(ErrorCode::ConstraintError),
161 Self::DynamicConstraintError => Some(ErrorCode::DynamicConstraintError),
162 Self::ResourceExhausted => Some(ErrorCode::ResourceExhausted),
163 Self::NotFound => Some(ErrorCode::NotFound),
164 Self::InvalidDataType => Some(ErrorCode::InvalidDataType),
165 Self::DataVersionMismatch => Some(ErrorCode::DataVersionMismatch),
166 Self::Busy => Some(ErrorCode::Busy),
167 Self::UnsupportedNode => Some(ErrorCode::NodeNotFound),
168 Self::UnsupportedEndpoint => Some(ErrorCode::EndpointNotFound),
169 Self::UnsupportedCluster => Some(ErrorCode::ClusterNotFound),
170 Self::UnsupportedEvent => Some(ErrorCode::EventNotFound),
171 Self::NeedsTimedInteraction => Some(ErrorCode::NeedsTimedInteraction),
172 Self::FailSafeRequired => Some(ErrorCode::FailSafeRequired),
173 _ => Some(ErrorCode::Failure),
174 }
175 }
176}
177
178impl FromTLV<'_> for IMStatusCode {
179 fn from_tlv(t: &TLVElement) -> Result<Self, Error> {
180 FromPrimitive::from_u16(t.u16()?).ok_or_else(|| ErrorCode::Invalid.into())
181 }
182}
183
184impl ToTLV for IMStatusCode {
185 fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
186 tw.u16(tag, *self as _)
187 }
188
189 fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
190 TLV::u16(tag, *self as _).into_tlv_iter()
191 }
192}
193
194#[derive(FromPrimitive, Debug, Copy, Clone, Eq, PartialEq)]
196#[cfg_attr(feature = "defmt", derive(defmt::Format))]
197pub enum OpCode {
198 Reserved = 0,
199 StatusResponse = 1,
200 ReadRequest = 2,
201 SubscribeRequest = 3,
202 SubscribeResponse = 4,
203 ReportData = 5,
204 WriteRequest = 6,
205 WriteResponse = 7,
206 InvokeRequest = 8,
207 InvokeResponse = 9,
208 TimedRequest = 10,
209}
210
211impl OpCode {
212 pub const fn meta(&self) -> MessageMeta {
217 MessageMeta {
218 proto_id: PROTO_ID_INTERACTION_MODEL,
219 proto_opcode: *self as u8,
220 reliable: true,
221 }
222 }
223
224 pub const fn is_tlv(&self) -> bool {
228 !matches!(self, Self::Reserved)
229 }
230}
231
232impl From<OpCode> for MessageMeta {
233 fn from(opcode: OpCode) -> Self {
234 opcode.meta()
235 }
236}
237
238#[derive(Default, Clone, Debug, PartialEq)]
250#[cfg_attr(feature = "defmt", derive(defmt::Format))]
251pub struct GenericPath {
252 pub endpoint: Option<EndptId>,
254 pub cluster: Option<ClusterId>,
256 pub leaf: Option<u32>,
258}
259
260impl GenericPath {
261 pub const fn new(
263 endpoint: Option<EndptId>,
264 cluster: Option<ClusterId>,
265 leaf: Option<u32>,
266 ) -> Self {
267 Self {
268 endpoint,
269 cluster,
270 leaf,
271 }
272 }
273
274 pub fn not_wildcard(&self) -> Result<(EndptId, ClusterId, u32), Error> {
276 match *self {
277 GenericPath {
278 endpoint: Some(e),
279 cluster: Some(c),
280 leaf: Some(l),
281 } => Ok((e, c, l)),
282 _ => Err(ErrorCode::Invalid.into()),
283 }
284 }
285
286 pub const fn is_wildcard(&self) -> bool {
288 !matches!(
289 *self,
290 GenericPath {
291 endpoint: Some(_),
292 cluster: Some(_),
293 leaf: Some(_),
294 }
295 )
296 }
297}