Skip to main content

matc/
lib.rs

1//! Matter controller library
2//!
3//! This library allows to controll Matter compatible devices. Library uses asynchronous Rust and depends on Tokio.
4//! Following are main parts of api:
5//! - [Transport](transport::Transport) - Representation of IP/UDP transport. Binds to specified IP/port,
6//!                             allows to define virtual connections for remote destinations
7//!                             and demultiplexes incoming messages based on these connections.
8//! - [CertManager](certmanager::CertManager) - Trait allowing to supply external certificate storage.
9//!                                Default implementation [certmanager::FileCertManager] stores certificates to specified directory in PEM format.
10//! - [Controller](controller::Controller) - Matter controller - uses [Transport](transport::Transport) to send/receive messages,
11//!                              [CertManager](certmanager::CertManager) to get certificates.
12//!                              Allows to [commission](controller::Controller::commission) device, [authenticate](controller::Controller::auth_sigma)
13//!                              commissioned device. Authenticated device is represented by [Connection](controller::Connection) which allows to
14//!                              [read attributes](controller::Connection::read_request) and [invoke commands](controller::Connection::invoke_request).
15//! - [tlv](tlv) - Module with simple matter tlv encoders and decoders which can be used to encode command parameters
16//!                and decode complex responses.
17//! - [im](im) - Typed Interaction Model report layer - decoded attribute/event reports used by
18//!              [Connection::read_request2](controller::Connection::read_request2) and the subscription API
19//!              ([Connection::subscribe_attrs](controller::Connection::subscribe_attrs) returns a
20//!              [Subscription](controller::Subscription) delivering decoded updates).
21//! - [discover](discover) - simple mdns based discovery of matter devices on local network
22//! - [devman](devman) - High level device manager which uses all above components to provide simpler api.
23//!                      It stores device information and certificates in specified directory and allows
24//!                      to commission new devices (by address, by manual pairing code with mDNS discovery,
25//!                      or over BLE with Wi-Fi/Thread credential provisioning - requires `ble` feature)
26//!                      and connect to already commissioned devices by name.
27//!                      Connections automatically re-discover devices via operational mDNS if the stored
28//!                      address is stale (e.g. device changed IP).
29//! - [clusters](clusters) - matter cluster definitions and encoders/decoders for cluster attributes and commands.
30//!
31//!
32//! Examples directory contains simple demo application and simple standalone examples on how to use APIs.
33//!
34//! Library can be used through high level device manager api or through lower level controller and transport apis.
35//! Device manager api is simpler to use, but does not provide same flexibility like lower level apis.
36//! For example how to use device manager see simple-devman.rs and devman_demo.rs examples in examples directory.
37//!
38//! Example how to initialize device manager
39//! ```no_run
40//! # use matc::devman::DeviceManager;
41//! # use anyhow::Result;
42//! # use matc::devman::ManagerConfig;
43//! # #[tokio::main]
44//! # async fn main() -> Result<()> {
45//! const FABRIC_ID: u64 = 100;
46//! const CONTROLLER_ID: u64 = 200;
47//! const LOCAL_ADDRESS: &str = "0.0.0.0:5555";
48//! const DATA_DIR: &str = "./matter-data";
49//! let config = ManagerConfig {
50//!             fabric_id: FABRIC_ID,
51//!             controller_id: CONTROLLER_ID,
52//!             local_address: LOCAL_ADDRESS.to_string(),
53//! };
54//! let devman = DeviceManager::create(DATA_DIR, config).await?;
55//! # Ok(())
56//! # }
57//! ```
58//!
59//! Example how to load existing device manager configuration and commission device using it.
60//! Shows both ways to talk to the device - typed facade (recommended) and raw API:
61//! ```no_run
62//! # use matc::devman::DeviceManager;
63//! # use anyhow::Result;
64//! # use matc::devman::ManagerConfig;
65//! # use matc::clusters;
66//! # use matc::clusters::codec::on_off;
67//! # #[tokio::main]
68//! # async fn main() -> Result<()> {
69//! const CONTROLLER_ID: u64 = 200;
70//! const NODE_ID: u64 = 300;
71//! const NAME: &str = "My Device";
72//! const DATA_DIR: &str = "./matter-data";
73//! const PIN: u32 = 123456;
74//! let devman = DeviceManager::load(DATA_DIR).await?;
75//! let device = devman.commission("1.1.1.1:5540", PIN, NODE_ID, NAME).await?;
76//!
77//! // Option A - typed facade: one call per command / attribute, typed args and return value.
78//! on_off::on(&device, 1).await?;
79//! let state: bool = on_off::read_on_off(&device, 1).await?;
80//!
81//! // Option B - raw API: cluster/command IDs + raw TLV payload. Useful when the cluster
82//! // is not covered by the facade or when the payload is built dynamically at runtime.
83//! device.invoke_request(1, clusters::defs::CLUSTER_ID_ON_OFF, clusters::defs::CLUSTER_ON_OFF_CMD_ID_ON, &[]).await?;
84//! # let _ = state;
85//! # Ok(())
86//! # }
87//! ```
88//!
89//! Example how to commission device using manual pairing code (mDNS discovery happens automatically):
90//! ```no_run
91//! # use matc::devman::DeviceManager;
92//! # use anyhow::Result;
93//! # #[tokio::main]
94//! # async fn main() -> Result<()> {
95//! const DATA_DIR: &str = "./matter-data";
96//! let devman = DeviceManager::load(DATA_DIR).await?;
97//! let device = devman.commission_with_code("0251-520-0076", 300, "My Device").await?;
98//! # Ok(())
99//! # }
100//! ```
101//!
102//! Example how to commission a Wi-Fi device that advertises over BLE (requires `ble` feature):
103//! ```no_run
104//! # #[cfg(feature = "ble")]
105//! # {
106//! # use matc::devman::DeviceManager;
107//! # use anyhow::Result;
108//! # use matc::NetworkCreds;
109//! # #[tokio::main]
110//! # async fn main() -> Result<()> {
111//! const DATA_DIR: &str = "./matter-data";
112//! let devman = DeviceManager::load(DATA_DIR).await?;
113//! let device = devman.commission_ble_with_code(
114//!     "MT:Y.K908...",   // QR or manual pairing code
115//!     300,              // node ID to assign
116//!     "kitchen light",  // friendly name
117//!     NetworkCreds::WiFi {
118//!         ssid: b"HomeWifi".to_vec(),
119//!         creds: b"secret".to_vec(),
120//!     },
121//! ).await?;
122//! # Ok(())
123//! # }
124//! # }
125//! ```
126//!
127//! Example how to connect to already commissioned device by name and send command to it.
128//! If the device changed its IP, the connection automatically re-discovers it via operational mDNS:
129//! ```no_run
130//! # use matc::devman::DeviceManager;
131//! # use anyhow::Result;
132//! # use matc::devman::ManagerConfig;
133//! # use matc::clusters;
134//! # #[tokio::main]
135//! # async fn main() -> Result<()> {
136//! const DATA_DIR: &str = "./matter-data";
137//! const NAME: &str = "My Device";
138//! let devman = DeviceManager::load(DATA_DIR).await?;
139//! let device = devman.connect_by_name(NAME).await?;
140//! device.invoke_request(1, clusters::defs::CLUSTER_ID_ON_OFF, clusters::defs::CLUSTER_ON_OFF_CMD_ID_ON, &[]).await?;
141//! # Ok(())
142//! # }
143//! ```
144//!
145//! Following are examples how to use lower level APIs without device manager.
146//!
147//! Example how to initialize certificate authority and create controller user - stores certificates in pem directory:
148//! ```no_run
149//! # use matc::certmanager::FileCertManager;
150//! # use anyhow::Result;
151//! # fn main() -> Result<()> {
152//! let fabric_id = 1000;
153//! let controller_id = 100;
154//! let cm = FileCertManager::new(fabric_id, "./pem");
155//! cm.bootstrap()?;
156//! cm.create_user(controller_id)?;
157//! # Ok(())
158//! # }
159//! ```
160//!
161//! Example how to commission device using certificates pre-created in pem directory:
162//! ```no_run
163//! # use matc::certmanager;
164//! # use anyhow::Result;
165//! # use std::sync::Arc;
166//! # use matc::transport;
167//! # use matc::controller;
168//! # use matc::clusters;
169//! # #[tokio::main]
170//! # async fn main() -> Result<()> {
171//! let fabric_id = 1000;
172//! let device_id = 300;
173//! let controller_id = 100;
174//! let pin = 123456;
175//! let cm: Arc<dyn certmanager::CertManager> = certmanager::FileCertManager::load("./pem")?;
176//! let transport = transport::Transport::new("0.0.0.0:5555").await?;
177//! let controller = controller::Controller::new(&cm, &transport, fabric_id)?;
178//! let connection = transport.create_connection("1.2.3.4:5540").await;
179//! let mut connection = controller.commission(&connection, pin, device_id, controller_id).await?;
180//! // commission method returns authenticated connection which can be used to send commands
181//! // now we can send ON command:
182//! connection.invoke_request(1,  // endpoint
183//!                           clusters::defs::CLUSTER_ID_ON_OFF,
184//!                           clusters::defs::CLUSTER_ON_OFF_CMD_ID_ON,
185//!                           &[]).await?;
186//! # Ok(())
187//! # }
188//! ```
189//!
190//! Example sending ON command to device which is already commissioned using certificates pre-created in pem directory:
191//! ```no_run
192//! # use matc::certmanager;
193//! # use anyhow::Result;
194//! # use std::sync::Arc;
195//! # use matc::transport;
196//! # use matc::controller;
197//! # use matc::tlv;
198//! # use matc::clusters;
199//! # #[tokio::main]
200//! # async fn main() -> Result<()> {
201//! let fabric_id = 1000;
202//! let device_id = 300;
203//! let controller_id = 100;
204//! let cm: Arc<dyn certmanager::CertManager> = certmanager::FileCertManager::load("./pem")?;
205//! let transport = transport::Transport::new("0.0.0.0:5555").await?;
206//! let controller = controller::Controller::new(&cm, &transport, fabric_id)?;
207//! let connection = transport.create_connection("1.2.3.4:5540").await;
208//! let mut c = controller.auth_sigma(&connection, device_id, controller_id).await?;
209//! // send ON command
210//! c.invoke_request(1, // endpoint
211//!                  clusters::defs::CLUSTER_ID_ON_OFF,
212//!                  clusters::defs::CLUSTER_ON_OFF_CMD_ID_ON,
213//!                  &[]).await?;
214//! //
215//! // invoke SetLevel command to show how to supply command parameters
216//! let tlv = tlv::TlvItemEnc {
217//!   tag: 0,
218//!   value: tlv::TlvItemValueEnc::StructInvisible(vec![
219//!     tlv::TlvItemEnc { tag: 0, value: tlv::TlvItemValueEnc::UInt8(50)   }, // level
220//!     tlv::TlvItemEnc { tag: 1, value: tlv::TlvItemValueEnc::UInt16(1000)}, // transition time
221//!     tlv::TlvItemEnc { tag: 2, value: tlv::TlvItemValueEnc::UInt8(0)    }, // options mask
222//!     tlv::TlvItemEnc { tag: 3, value: tlv::TlvItemValueEnc::UInt8(0)    }, // options override
223//!   ])
224//! }.encode()?;
225//! c.invoke_request(1, // endpoint
226//!                  clusters::defs::CLUSTER_ID_LEVEL_CONTROL,
227//!                  clusters::defs::CLUSTER_LEVEL_CONTROL_CMD_ID_MOVETOLEVEL,
228//!                  &tlv).await?;
229//! //
230//! // read level
231//! let result = c.read_request2(1,
232//!                              clusters::defs::CLUSTER_ID_LEVEL_CONTROL,
233//!                              clusters::defs::CLUSTER_LEVEL_CONTROL_ATTR_ID_CURRENTLEVEL,
234//!                              ).await?;
235//! println!("{:?}", result);
236//! # Ok(())
237//! # }
238//! ```
239//!
240//! ## Cluster access: typed facade vs. raw API
241//!
242//! The examples above use a mix of two styles for talking to a cluster on a connected
243//! device. Both are supported and can be mixed freely on the same `Connection`:
244//!
245//! 1. **Typed facade (recommended for known clusters)** - each generated cluster module in
246//!    [clusters::codec] exposes one `pub async fn` per command and one `read_<attr>` per
247//!    attribute. Calls take `&Connection, endpoint, ...args` and do encode+invoke+decode
248//!    (or read+decode) in a single step, with typed parameters and typed return values
249//!    (`Result<()>` for ACK-only commands, `Result<FooResponse>` for commands with a
250//!    response struct, the decoder's native Rust type for attributes). See
251//!    `examples/simple.rs` for a minimal end-to-end usage.
252//!
253//!    ```ignore
254//!    use matc::clusters::codec::on_off;
255//!    on_off::on(&conn, 1).await?;
256//!    let state: bool = on_off::read_on_off(&conn, 1).await?;
257//!    ```
258//!
259//! 2. **Raw API (for dynamic / untyped / debugging use)** - the facade is an *alternative*,
260//!    not a replacement. The lower-level
261//!    [Connection::invoke_request](controller::Connection::invoke_request) /
262//!    [Connection::read_request2](controller::Connection::read_request2) methods take
263//!    cluster/command/attribute IDs from [clusters::defs] and raw TLV byte payloads, and
264//!    return the raw response TLV. Use this when you need to:
265//!    - talk to a cluster or field not covered by the generated facade,
266//!    - build command payloads dynamically at runtime (e.g. a generic CLI or REPL - see
267//!      `examples/demo.rs` and `examples/shell.rs`),
268//!    - inspect the raw response TLV (e.g. `res.tlv.dump(1)` for protocol-level debugging),
269//!    - use `invoke_request_timed` and other specialized paths the facade does not wrap.
270//!
271//!
272#![doc = include_str!("../readme.md")]
273
274mod active_connection;
275#[cfg(feature = "ble")]
276pub mod ble;
277#[cfg(feature = "ble")]
278pub mod btp;
279pub mod cert_matter;
280pub mod cert_x509;
281pub mod certmanager;
282pub mod clusters;
283mod commission;
284pub use commission::NetworkCreds;
285pub mod controller;
286pub mod device;
287mod device_messages;
288pub mod devman;
289pub mod discover;
290pub mod fabric;
291pub mod im;
292pub mod mdns;
293pub mod mdns2;
294pub mod messages;
295pub mod mrp;
296pub mod onboarding;
297mod retransmit;
298mod session;
299mod sigma;
300pub mod spake2p;
301pub mod tlv;
302pub mod transport;
303pub mod util;