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