rs_matter/lib.rs
1/*
2 *
3 * Copyright (c) 2022-2026 Project CHIP Authors
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18//! Native Rust implementation of the Matter protocol by CSA-IOT.
19//!
20//! This crate implements the Matter specification that can be run on embedded devices
21//! to build Matter-compatible smart-home/IoT devices.
22//!
23//! Look at the examples project in the workspace for example applications built using this crate, and the tests directory for unit tests.
24//!
25//! Start off exploring by going to the [Matter] object.
26#![cfg_attr(not(feature = "std"), no_std)]
27#![allow(async_fn_in_trait)]
28#![allow(unknown_lints)]
29#![allow(clippy::uninlined_format_args)]
30#![recursion_limit = "1024"]
31
32use crate::crypto::Crypto;
33use crate::dm::clusters::basic_info::{
34 self, BasicInfoConfig, BasicInfoSettings, FULL_CLUSTER as BASIC_INFO_CLUSTER,
35};
36use crate::dm::clusters::dev_att::DeviceAttestation;
37use crate::dm::clusters::time_sync::Rtc;
38use crate::dm::endpoints::ROOT_ENDPOINT_ID;
39use crate::dm::AttrChangeNotifier;
40use crate::error::{Error, ErrorCode};
41use crate::fabric::Fabrics;
42use crate::failsafe::FailSafe;
43use crate::pairing::qr::{
44 no_optional_data, CommFlowType, NoOptionalData, Qr, QrPayload, QrTextType,
45};
46use crate::pairing::DiscoveryCapabilities;
47use crate::persist::{KvBlobStore, KvBlobStoreAccess, Persist, BASIC_INFO_KEY};
48use crate::sc::pase::spake2p::{Spake2pVerifierPassword, SPAKE2P_VERIFIER_SALT_ZEROED};
49use crate::sc::pase::Pase;
50use crate::transport::network::MatterLocalService;
51use crate::transport::network::{NetworkMulticast, NetworkReceive, NetworkSend};
52use crate::transport::session::Sessions;
53use crate::transport::{
54 PacketBufferExternalAccess, Transport, TransportRunner, MAX_RX_BUF_SIZE, MAX_TX_BUF_SIZE,
55};
56use crate::utils::cell::RefCell;
57use crate::utils::init::{init, Init};
58use crate::utils::storage::pooled::Buffers;
59use crate::utils::sync::blocking::Mutex;
60
61use rand_core::RngCore;
62
63#[cfg(feature = "alloc")]
64extern crate alloc;
65
66// This mod MUST go first, so that the others see its macros.
67pub(crate) mod fmt;
68
69pub mod acl;
70pub mod attest;
71pub mod bdx;
72pub mod cert;
73pub mod crypto;
74pub mod dm;
75pub mod error;
76pub mod fabric;
77pub mod failsafe;
78pub mod group_keys;
79pub mod im;
80pub mod onboard;
81pub mod pairing;
82pub mod persist;
83pub mod respond;
84pub mod sc;
85pub mod tlv;
86pub mod transport;
87pub mod utils;
88
89/// Re-export several crates
90///
91/// This is necessary for crates used in the code generated by the proc-macros
92pub mod reexport {
93 pub use bitflags;
94 #[cfg(feature = "defmt")]
95 pub use defmt;
96 #[cfg(feature = "log")]
97 pub use log;
98 pub use strum;
99}
100
101#[cfg(feature = "alloc")]
102#[macro_export]
103macro_rules! alloc {
104 ($val:expr) => {
105 alloc::boxed::Box::new($val)
106 };
107}
108
109#[cfg(not(feature = "alloc"))]
110#[macro_export]
111macro_rules! alloc {
112 ($val:expr) => {
113 $val
114 };
115}
116
117/// The Matter UDP port
118pub const MATTER_PORT: u16 = 5540;
119
120/// Device basic commissioning data
121#[derive(Debug, Clone)]
122#[cfg_attr(feature = "defmt", derive(defmt::Format))]
123pub struct BasicCommData {
124 /// The password which is necessary to authenticate the device in either
125 /// initial commissioning, or when the basic commissioning window is opened
126 pub password: Spake2pVerifierPassword,
127 /// The 12-bit discriminator used to differentiate between multiple devices
128 pub discriminator: u16,
129}
130
131/// The primary Matter Object
132pub struct Matter<'a> {
133 /// The internal state of the Matter Object, protected by a mutex for concurrent access from different threads and async tasks.
134 state: Mutex<RefCell<MatterState>>,
135 /// The transport state of the Matter Object
136 transport: Transport,
137 /// The basic information configuration for this Matter device
138 dev_det: &'a BasicInfoConfig<'a>,
139 /// The basic commissioning data for this Matter device
140 dev_comm: BasicCommData,
141 /// The device attestation data fetcher for this Matter device
142 dev_att: &'a dyn DeviceAttestation,
143 /// The port number on which the Matter stack will listen for incoming connections
144 port: u16,
145 /// The scratch buffer used by the key-value persistence machinery for
146 /// (de)serializing BLOBs. Behind a blocking mutex so [`Matter::kv`] can
147 /// recombine it with the user's raw [`KvBlobStore`](crate::persist::KvBlobStore)
148 /// into a full [`KvBlobStoreAccess`](crate::persist::KvBlobStoreAccess). Its
149 /// size is set by the `kv-blob-store-*` Cargo features (see
150 /// [`KV_BUF_SIZE`](crate::persist::KV_BUF_SIZE)).
151 kv_buf: Mutex<RefCell<[u8; crate::persist::KV_BUF_SIZE]>>,
152}
153
154impl<'a> Matter<'a> {
155 /// Create a new Matter object.
156 ///
157 /// # Parameters
158 /// * dev_det: An object of type [BasicInfoConfig].
159 /// * dev_comm: An object of type [BasicCommData]. This object contains the basic commissioning
160 /// data required for the device.
161 /// * dev_att: An object that implements the trait [DevAttDataFetcher]. Any Matter device
162 /// requires a set of device attestation certificates and keys. It is the responsibility of
163 /// this object to return the device attestation details when queried upon.
164 /// * port: The port number on which the Matter stack will listen for incoming connections.
165 #[inline(always)]
166 pub const fn new(
167 dev_det: &'a BasicInfoConfig<'a>,
168 dev_comm: BasicCommData,
169 dev_att: &'a dyn DeviceAttestation,
170 port: u16,
171 ) -> Self {
172 Self {
173 state: Mutex::new(RefCell::new(MatterState::new())),
174 transport: Transport::new(dev_det),
175 dev_det,
176 dev_comm,
177 dev_att,
178 port,
179 kv_buf: Mutex::new(RefCell::new([0; crate::persist::KV_BUF_SIZE])),
180 }
181 }
182
183 /// Create an in-place initializer for a Matter object.
184 ///
185 /// # Parameters
186 /// * dev_det: An object of type [BasicInfoConfig].
187 /// * dev_comm: An object of type [BasicCommData]. This object contains the basic commissioning
188 /// data required for the device.
189 /// * dev_att: An object that implements the trait [DevAttDataFetcher]. Any Matter device
190 /// requires a set of device attestation certificates and keys. It is the responsibility of
191 /// this object to return the device attestation details when queried upon.
192 /// * port: The port number on which the Matter stack will listen for incoming connections.
193 pub fn init(
194 dev_det: &'a BasicInfoConfig<'a>,
195 dev_comm: BasicCommData,
196 dev_att: &'a dyn DeviceAttestation,
197 port: u16,
198 ) -> impl Init<Self> {
199 init!(
200 Self {
201 state <- Mutex::init(RefCell::init(MatterState::init())),
202 transport <- Transport::init(dev_det),
203 dev_det,
204 dev_comm,
205 dev_att,
206 port,
207 kv_buf <- Mutex::init(RefCell::init(crate::utils::init::zeroed())),
208 }
209 )
210 }
211
212 pub fn dev_det(&self) -> &BasicInfoConfig<'_> {
213 self.dev_det
214 }
215
216 pub fn dev_att(&self) -> &dyn DeviceAttestation {
217 self.dev_att
218 }
219
220 pub fn dev_comm(&self) -> &BasicCommData {
221 &self.dev_comm
222 }
223
224 pub fn port(&self) -> u16 {
225 self.port
226 }
227
228 /// Combine a user-provided raw [`KvBlobStore`] with the scratch buffer owned
229 /// by this `Matter` object to obtain a full [`KvBlobStoreAccess`].
230 ///
231 /// This is the single entry point for persistence: the application passes its
232 /// raw store (sync `load`/`store`/`remove`) and gets back an access object
233 /// that recombines it with `Matter`'s feature-sized scratch buffer (see
234 /// [`KV_BUF_SIZE`](crate::persist::KV_BUF_SIZE)). The returned value is then
235 /// lent (by `&`) to [`Matter::load_persist`], [`Matter::reset_persist`],
236 /// [`InteractionModelState::load_persist`](crate::im::InteractionModelState::load_persist)
237 /// and [`InteractionModel::new`](crate::im::InteractionModel::new).
238 ///
239 /// # Arguments
240 /// - `store` - the raw [`KvBlobStore`] implementation to wrap
241 pub fn kv<'s, S: KvBlobStore + 's>(&'s self, store: S) -> impl KvBlobStoreAccess + 's {
242 crate::persist::SharedKvBlobStore::new(store, &self.kv_buf)
243 }
244
245 /// Get a reference to the transport state of this Matter object.
246 ///
247 /// All transport-related state and operations (mDNS change/resolve
248 /// rendezvous, session/group notifications, RX/TX buffers, exchange
249 /// initiation/acceptance) live on [`Transport`].
250 #[inline(always)]
251 pub const fn transport(&self) -> &Transport {
252 &self.transport
253 }
254
255 pub fn transport_rx_buffer(&self) -> PacketBufferExternalAccess<'_, MAX_RX_BUF_SIZE> {
256 self.transport().rx_buffer()
257 }
258
259 pub fn transport_tx_buffer(&self) -> PacketBufferExternalAccess<'_, MAX_TX_BUF_SIZE> {
260 self.transport().tx_buffer()
261 }
262
263 /// A utility method to replace the initial Device Attestation with another one.
264 pub fn replace_dev_att(&mut self, dev_att: &'a dyn DeviceAttestation) {
265 self.dev_att = dev_att;
266 }
267
268 /// Print the standard QR code text to the console
269 ///
270 /// The printed QR code text corresponds to the standard commissioning flow (i.e. `CommFlowType::Standard`)
271 /// and contains no optional data.
272 ///
273 /// This method is useful primarily during development, when the Matter device is
274 /// attached to a console. It is expected that the developer will call this method prior to running the Matter transport.
275 ///
276 /// # Arguments
277 /// - `disc_caps`: The discovery capabilities to be used in the QR code payload
278 pub fn print_standard_qr_text(&self, disc_caps: DiscoveryCapabilities) -> Result<(), Error> {
279 let rx_buf = self.transport().rx_buffer();
280
281 let mut buf = rx_buf.get_immediate().ok_or(ErrorCode::NoMemory)?;
282 let buf = &mut *buf;
283
284 let payload = self.standard_qr_payload(disc_caps)?;
285
286 let (text, _) = payload.as_str(buf)?;
287
288 // Do not remove this logging line or change its formatting.
289 // C++ E2E tests rely on this log line to grep the QR code
290 info!("SetupQRCode: [{}]", text);
291
292 Ok(())
293 }
294
295 /// Print the standard QR code to the console
296 ///
297 /// The printed QR code corresponds to the standard commissioning flow (i.e. `CommFlowType::Standard`)
298 /// and contains no optional data.
299 ///
300 /// This method is useful primarily during development, when the Matter device is
301 /// attached to a console. It is expected that the developer will call this method prior to running the Matter transport.
302 ///
303 /// # Arguments
304 /// - `text_type`: The type of text representation to use when printing the QR code
305 /// - `disc_caps`: The discovery capabilities to be used in the QR code payload
306 pub fn print_standard_qr_code(
307 &self,
308 text_type: QrTextType,
309 disc_caps: DiscoveryCapabilities,
310 ) -> Result<(), Error> {
311 // Also print the pairing code for convenience
312 info!(
313 "PairingCode: [{}]",
314 self.dev_comm.compute_pretty_pairing_code()
315 );
316
317 let rx_buf = self.transport().rx_buffer();
318
319 let mut buf = rx_buf.get_immediate().ok_or(ErrorCode::NoMemory)?;
320 let buf = &mut *buf;
321
322 let payload = self.standard_qr_payload(disc_caps)?;
323
324 let (text, buf) = payload.as_str(buf)?;
325
326 let (tmp_buf, out_buf) = buf.split_at_mut(buf.len() / 2);
327
328 let qr = Qr::compute(text, tmp_buf, out_buf)?;
329
330 const BORDER_SIZE: u8 = 4;
331
332 for y in qr.lines_range(text_type, BORDER_SIZE) {
333 info!(
334 "{}",
335 qr.line_as_str(text_type, BORDER_SIZE, false, false, y, tmp_buf)?
336 .0
337 );
338 }
339
340 Ok(())
341 }
342
343 /// Return the standard QR code payload
344 ///
345 /// The returned QR code payload corresponds to the standard commissioning flow (i.e. `CommFlowType::Standard`)
346 /// and contains no optional data.
347 ///
348 /// # Arguments
349 /// - `disc_caps`: The discovery capabilities to be used in the QR code payload
350 fn standard_qr_payload(
351 &self,
352 disc_caps: DiscoveryCapabilities,
353 ) -> Result<QrPayload<'_, NoOptionalData>, Error> {
354 let payload = QrPayload::new_from_basic_info(
355 disc_caps,
356 CommFlowType::Standard,
357 self.dev_comm.clone(),
358 self.dev_det,
359 no_optional_data as _,
360 );
361
362 Ok(payload)
363 }
364
365 /// Return `true` if there is at least one commissioned fabric
366 //
367 // TODO:
368 // The implementation of this method needs to change in future,
369 // because the current implementation does not really track whether
370 // `CommissioningComplete` had been actually received for the fabric.
371 //
372 // The fabric is created once we receive `AddNoc`, but that's just
373 // not enough. The fabric should NOT be considered commissioned until
374 // after we receive `CommissioningComplete` on behalf of a Case session
375 // for the fabric in question.
376 pub fn is_commissioned(&self) -> bool {
377 self.with_state(|state| state.fabrics.iter().count() > 0)
378 }
379
380 /// Open a basic commissioning window
381 ///
382 /// The method will return an error if the commissioning window cannot be opened
383 /// (due to another window already being opened, for example).
384 ///
385 /// # Arguments
386 /// - `timeout_secs`: The timeout in seconds for the basic commissioning window
387 ///
388 /// **Note:** This is the low-level building block that mutates PASE
389 /// state and routes a `notify_cluster_changed(...)` to subscribers
390 /// via `notify`, but does **not** bump the per-cluster `Dataver` of
391 /// `AdministratorCommissioning` — a subsequent dataver-filtered
392 /// read could therefore cache-hit and miss the change. Application
393 /// code that holds a `InteractionModel` should prefer
394 /// [`crate::im::InteractionModel::open_basic_comm_window`], which delegates
395 /// here and additionally bumps dataver via its
396 /// [`AttrChangeNotifier`] impl.
397 pub fn open_basic_comm_window<C: Crypto>(
398 &self,
399 timeout_secs: u16,
400 crypto: C,
401 notify: &dyn AttrChangeNotifier,
402 ) -> Result<(), Error> {
403 let notify_mdns = || self.transport().notify_mdns_changed();
404 let notify_change = |endpt_id, clust_id| notify.notify_cluster_changed(endpt_id, clust_id);
405
406 self.with_state(|state| {
407 let mut rand = crypto.rand()?;
408
409 let mdns_id = rand.next_u64();
410
411 let mut salt = SPAKE2P_VERIFIER_SALT_ZEROED;
412 rand.fill_bytes(salt.access_mut());
413
414 state.pase.open_basic_comm_window(
415 mdns_id,
416 salt.access(),
417 self.dev_comm.password.reference(),
418 self.dev_comm.discriminator,
419 timeout_secs,
420 None,
421 notify_mdns,
422 notify_change,
423 )
424 })
425 }
426
427 /// Close the commissioning window (basic or other)
428 ///
429 /// The method will return Ok(false) if there is no active PASE commissioning window to close.
430 ///
431 /// **Note:** As with [`Matter::open_basic_comm_window`], this does
432 /// not bump the per-cluster `Dataver` of
433 /// `AdministratorCommissioning`. Prefer
434 /// [`crate::im::InteractionModel::close_comm_window`] when a `InteractionModel`
435 /// is available.
436 pub fn close_comm_window(&self, notify: &dyn AttrChangeNotifier) -> Result<bool, Error> {
437 let notify_mdns = || self.transport().notify_mdns_changed();
438 let notify_change = |endpt_id, clust_id| notify.notify_cluster_changed(endpt_id, clust_id);
439
440 self.with_state(|state| state.pase.close_comm_window(notify_mdns, notify_change))
441 }
442
443 /// Bump `BasicInformation::ConfigurationVersion` by one, persist
444 /// the new value via `kv`, and route an attribute-change
445 /// notification to subscribers via `notify`.
446 ///
447 /// Per Matter Core Spec, the device MUST bump
448 /// this attribute on any change to its exposed fixed-quality
449 /// surface (a firmware update that adds or removes functionality,
450 /// internal reconfiguration that changes any `F`-quality attribute,
451 /// bridged-node add/remove on a bridge). `rs-matter` cannot detect
452 /// such events on its own — the application drives the bump.
453 ///
454 /// **Note:** Like the other low-level mutators on `Matter`, this
455 /// does **not** bump the per-cluster `Dataver` of
456 /// `BasicInformation`. A subsequent dataver-filtered read could
457 /// therefore cache-hit and miss the change. Application code that
458 /// holds a `InteractionModel` should prefer
459 /// [`crate::im::InteractionModel::bump_configuration_version`], which
460 /// delegates here and additionally bumps dataver via its
461 /// [`AttrChangeNotifier`] impl.
462 ///
463 /// Returns the new `ConfigurationVersion` value.
464 pub fn bump_configuration_version<S: KvBlobStoreAccess>(
465 &self,
466 kv: S,
467 notify: &dyn AttrChangeNotifier,
468 ) -> Result<u32, Error> {
469 let mut persist = Persist::new(kv);
470
471 let new_version = self.with_state(|state| {
472 let new_version = state.basic_info_settings.bump_configuration_version();
473
474 persist.store_tlv(BASIC_INFO_KEY, &state.basic_info_settings)?;
475
476 notify.notify_attr_changed(
477 ROOT_ENDPOINT_ID,
478 BASIC_INFO_CLUSTER.id,
479 basic_info::AttributeId::ConfigurationVersion as _,
480 );
481
482 Ok::<_, Error>(new_version)
483 })?;
484
485 persist.run()?;
486
487 Ok(new_version)
488 }
489
490 /// Create a new transport runner instance
491 pub fn transport_runner<C: Crypto>(&self, crypto: C) -> TransportRunner<'_, C> {
492 TransportRunner::new(self, crypto)
493 }
494
495 /// Run the Matter transport layer.
496 ///
497 /// # Arguments
498 /// - `crypto`: The crypto backend
499 /// - `send`: The network send interface
500 /// - `recv`: The network receive interface
501 /// - `multicast`: The multicast network interface (for receiving groupcast messages)
502 /// When running on top of non-IP networks like BLE pass a no-op implementation like `NoNetwork` here and the multicast functionality will be disabled.
503 pub async fn run<C, S, R, M>(
504 &self,
505 crypto: C,
506 send: S,
507 recv: R,
508 multicast: M,
509 ) -> Result<(), Error>
510 where
511 C: Crypto,
512 S: NetworkSend,
513 R: NetworkReceive,
514 M: NetworkMulticast,
515 {
516 let mut transport_runner = self.transport_runner(crypto);
517
518 transport_runner.run(send, recv, multicast).await
519 }
520
521 /// Access the Matter state by invoking a closure with a mutable reference to the state.
522 pub fn with_state<F, R>(&self, f: F) -> R
523 where
524 F: FnOnce(&mut MatterState) -> R,
525 {
526 self.state.lock(|state| {
527 let mut state = state.borrow_mut();
528 f(&mut state)
529 })
530 }
531
532 /// Access the Real-Time-clock by invoking a closure with a mutable reference to it.
533 pub fn with_rtc<F, R>(&self, f: F) -> R
534 where
535 F: FnOnce(&mut Rtc) -> R,
536 {
537 self.with_state(|state| f(&mut state.rtc))
538 }
539
540 /// Reset the transport layer by clearing all sessions, exchanges, the RX buffer and the TX buffer
541 /// NOTE: User should be careful _not_ to call this method while the transport layer and/or the built-in mDNS is running.
542 pub fn reset_transport(&self) -> Result<(), Error> {
543 self.with_state(|state| {
544 state.sessions.reset();
545
546 self.transport().reset()
547 })
548 }
549
550 /// Reset the Matter persistable state by removing all fabrics and resetting basic info settings
551 ///
552 /// Arguments:
553 /// - `kv`: The key-value store access (obtained via [`Matter::kv`]) to remove the fabrics
554 /// and basic info settings from. Provides both the store and the scratch buffer.
555 pub async fn reset_persist<K: KvBlobStoreAccess>(&self, kv: K) -> Result<(), Error> {
556 self.with_state(|state| {
557 // The KV ops are sync, so do them all inside a single `access` closure.
558 kv.access(|mut store, buf| {
559 state.fabrics.reset_persist(&mut store, buf)?;
560 state.basic_info_settings.reset_persist(&mut store, buf)?;
561 state.rtc.reset_persist(&mut store, buf)?;
562
563 Ok::<_, Error>(())
564 })
565 })?;
566
567 self.transport().notify_mdns_changed();
568
569 Ok(())
570 }
571
572 /// Load fabrics from the given data
573 ///
574 /// Arguments:
575 /// - `kv`: The key-value store access (obtained via [`Matter::kv`]) to load the fabrics
576 /// and basic info settings from. Provides both the store and the scratch buffer.
577 pub async fn load_persist<K: KvBlobStoreAccess>(&self, kv: K) -> Result<(), Error> {
578 self.with_state(|state| {
579 // The KV ops are sync, so do them all inside a single `access` closure.
580 kv.access(|mut store, buf| {
581 state.fabrics.load_persist(&mut store, buf)?;
582 state.basic_info_settings.load_persist(&mut store, buf)?;
583 state.rtc.load_persist(&mut store, buf)?;
584
585 Ok::<_, Error>(())
586 })
587 })?;
588
589 self.transport().notify_mdns_changed();
590
591 Ok(())
592 }
593
594 /// Invoke the given closure for each currently published Matter mDNS service.
595 pub fn mdns_services<F>(&self, mut f: F) -> Result<(), Error>
596 where
597 F: FnMut(MatterLocalService) -> Result<(), Error>,
598 {
599 debug!("=== Currently published mDNS services");
600
601 self.with_state(|state| {
602 if let Some(comm_window) = state.pase.comm_window() {
603 // Do not remove this logging line or change its formatting.
604 // C++ E2E tests rely on this log line to determine when the mDNS service is published
605 debug!("mDNS service published: {:?}", comm_window.mdns_service());
606
607 f(comm_window.mdns_service())?;
608 }
609
610 for fabric in state.fabrics.iter() {
611 if let Some(service) = fabric.mdns_service() {
612 // Do not remove this logging line or change its formatting.
613 // C++ E2E tests rely on this log line to determine when the mDNS service is published
614 debug!("mDNS service published: {:?}", service);
615
616 f(service)?;
617 }
618 }
619
620 debug!("===");
621
622 Ok(())
623 })
624 }
625}
626
627/// The internal state of the Matter Object
628///
629/// Public for unit tests.
630pub struct MatterState {
631 /// All fabrics
632 ///
633 /// Public for unit tests
634 pub fabrics: Fabrics,
635 /// All sessions
636 sessions: Sessions,
637 /// The PASE session state
638 pase: Pase,
639 /// The Failsafe state
640 failsafe: FailSafe,
641 /// The mutable basic information settings
642 basic_info_settings: BasicInfoSettings,
643 /// Real Time Clock state and Last-Known-Good UTC Time tracking (Matter Core spec).
644 rtc: Rtc,
645}
646
647impl MatterState {
648 /// Create a new instance of MatterState
649 #[inline(always)]
650 const fn new() -> Self {
651 Self {
652 fabrics: Fabrics::new(),
653 sessions: Sessions::new(),
654 pase: Pase::new(),
655 failsafe: FailSafe::new(),
656 basic_info_settings: BasicInfoSettings::new(),
657 rtc: Rtc::new(),
658 }
659 }
660
661 /// Return an in-place initializer for MatterState
662 fn init() -> impl Init<Self> {
663 init!(Self {
664 fabrics <- Fabrics::init(),
665 sessions <- Sessions::init(),
666 pase <- Pase::init(),
667 failsafe <- FailSafe::init(),
668 basic_info_settings <- BasicInfoSettings::init(),
669 rtc <- Rtc::init(),
670 })
671 }
672}
673
674#[cfg(test)]
675pub mod test {
676 use crate::Matter;
677
678 pub fn test_matter() -> Matter<'static> {
679 Matter::new(
680 &crate::dm::devices::test::TEST_DEV_DET,
681 crate::dm::devices::test::TEST_DEV_COMM,
682 &crate::dm::devices::test::TEST_DEV_ATT,
683 0,
684 )
685 }
686}