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}
112
113impl From<ErrorCode> for IMStatusCode {
114 fn from(e: ErrorCode) -> Self {
115 match e {
116 ErrorCode::NodeNotFound => IMStatusCode::UnsupportedNode,
117 ErrorCode::EndpointNotFound => IMStatusCode::UnsupportedEndpoint,
118 ErrorCode::ClusterNotFound => IMStatusCode::UnsupportedCluster,
119 ErrorCode::AttributeNotFound => IMStatusCode::UnsupportedAttribute,
120 ErrorCode::CommandNotFound => IMStatusCode::UnsupportedCommand,
121 ErrorCode::EventNotFound => IMStatusCode::UnsupportedEvent,
122 ErrorCode::InvalidAction => IMStatusCode::InvalidAction,
123 ErrorCode::InvalidCommand => IMStatusCode::InvalidCommand,
124 ErrorCode::InvalidDataType => IMStatusCode::InvalidDataType,
125 ErrorCode::UnsupportedAccess => IMStatusCode::UnsupportedAccess,
126 ErrorCode::Busy => IMStatusCode::Busy,
127 ErrorCode::DataVersionMismatch => IMStatusCode::DataVersionMismatch,
128 ErrorCode::ResourceExhausted => IMStatusCode::ResourceExhausted,
129 ErrorCode::FailSafeRequired => IMStatusCode::FailSafeRequired,
130 ErrorCode::NeedsTimedInteraction => IMStatusCode::NeedsTimedInteraction,
131 ErrorCode::ConstraintError => IMStatusCode::ConstraintError,
132 ErrorCode::DynamicConstraintError => IMStatusCode::DynamicConstraintError,
133 ErrorCode::NotFound => IMStatusCode::NotFound,
134 ErrorCode::Failure => IMStatusCode::Failure,
135 _ => IMStatusCode::Failure,
136 }
137 }
138}
139
140impl From<Error> for IMStatusCode {
141 fn from(value: Error) -> Self {
142 Self::from(value.code())
143 }
144}
145
146impl IMStatusCode {
147 pub fn to_error_code(self) -> Option<ErrorCode> {
151 match self {
152 Self::Success => None,
153 Self::UnsupportedAccess => Some(ErrorCode::UnsupportedAccess),
154 Self::InvalidAction => Some(ErrorCode::InvalidAction),
155 Self::UnsupportedCommand => Some(ErrorCode::CommandNotFound),
156 Self::InvalidCommand => Some(ErrorCode::InvalidCommand),
157 Self::UnsupportedAttribute => Some(ErrorCode::AttributeNotFound),
158 Self::ConstraintError => Some(ErrorCode::ConstraintError),
159 Self::DynamicConstraintError => Some(ErrorCode::DynamicConstraintError),
160 Self::ResourceExhausted => Some(ErrorCode::ResourceExhausted),
161 Self::NotFound => Some(ErrorCode::NotFound),
162 Self::InvalidDataType => Some(ErrorCode::InvalidDataType),
163 Self::DataVersionMismatch => Some(ErrorCode::DataVersionMismatch),
164 Self::Busy => Some(ErrorCode::Busy),
165 Self::UnsupportedNode => Some(ErrorCode::NodeNotFound),
166 Self::UnsupportedEndpoint => Some(ErrorCode::EndpointNotFound),
167 Self::UnsupportedCluster => Some(ErrorCode::ClusterNotFound),
168 Self::UnsupportedEvent => Some(ErrorCode::EventNotFound),
169 Self::NeedsTimedInteraction => Some(ErrorCode::NeedsTimedInteraction),
170 Self::FailSafeRequired => Some(ErrorCode::FailSafeRequired),
171 _ => Some(ErrorCode::Failure),
172 }
173 }
174}
175
176impl FromTLV<'_> for IMStatusCode {
177 fn from_tlv(t: &TLVElement) -> Result<Self, Error> {
178 FromPrimitive::from_u16(t.u16()?).ok_or_else(|| ErrorCode::Invalid.into())
179 }
180}
181
182impl ToTLV for IMStatusCode {
183 fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
184 tw.u16(tag, *self as _)
185 }
186
187 fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
188 TLV::u16(tag, *self as _).into_tlv_iter()
189 }
190}
191
192#[derive(FromPrimitive, Debug, Copy, Clone, Eq, PartialEq)]
194#[cfg_attr(feature = "defmt", derive(defmt::Format))]
195pub enum OpCode {
196 Reserved = 0,
197 StatusResponse = 1,
198 ReadRequest = 2,
199 SubscribeRequest = 3,
200 SubscribeResponse = 4,
201 ReportData = 5,
202 WriteRequest = 6,
203 WriteResponse = 7,
204 InvokeRequest = 8,
205 InvokeResponse = 9,
206 TimedRequest = 10,
207}
208
209impl OpCode {
210 pub const fn meta(&self) -> MessageMeta {
215 MessageMeta {
216 proto_id: PROTO_ID_INTERACTION_MODEL,
217 proto_opcode: *self as u8,
218 reliable: true,
219 }
220 }
221
222 pub const fn is_tlv(&self) -> bool {
226 !matches!(self, Self::Reserved)
227 }
228}
229
230impl From<OpCode> for MessageMeta {
231 fn from(opcode: OpCode) -> Self {
232 opcode.meta()
233 }
234}
235
236#[derive(Default, Clone, Debug, PartialEq)]
248#[cfg_attr(feature = "defmt", derive(defmt::Format))]
249pub struct GenericPath {
250 pub endpoint: Option<EndptId>,
252 pub cluster: Option<ClusterId>,
254 pub leaf: Option<u32>,
256}
257
258impl GenericPath {
259 pub const fn new(
261 endpoint: Option<EndptId>,
262 cluster: Option<ClusterId>,
263 leaf: Option<u32>,
264 ) -> Self {
265 Self {
266 endpoint,
267 cluster,
268 leaf,
269 }
270 }
271
272 pub fn not_wildcard(&self) -> Result<(EndptId, ClusterId, u32), Error> {
274 match *self {
275 GenericPath {
276 endpoint: Some(e),
277 cluster: Some(c),
278 leaf: Some(l),
279 } => Ok((e, c, l)),
280 _ => Err(ErrorCode::Invalid.into()),
281 }
282 }
283
284 pub const fn is_wildcard(&self) -> bool {
286 !matches!(
287 *self,
288 GenericPath {
289 endpoint: Some(_),
290 cluster: Some(_),
291 leaf: Some(_),
292 }
293 )
294 }
295}