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;