App-Side Controller Wiring
The Enable Controller Transport guide covers the SDK contract (availableTransports.matter_controller + controllerNodeId). This page documents a reference architecture used in the Espressif RainMaker Home app: how the app reacts to controller discovery, reads peer reachability from the controller setup service, and adds or removes remote transport on peers.
Cmd-resp create, poll, and parse stay in the Matter SDK. The app supplies controllerNodeId only. Adapt file names and store APIs to your project.
What “app side” means
| Layer | Responsibility |
|---|---|
| Matter SDK | syncMatterControllers, found/lost events, executeMatterControl, cmd-resp handler |
| Group sync | Load fabric nodes; subscribe to controller found/lost once per session |
| Transport handler | On found: cloud-subscribe controller, sync peer transports from MTDevices |
| CDF / store | handleNodeTransportUpdate add/remove; fan-out peer param updates to UI |
| Controller settings UI | Optional: MTCtlCMD = 2 to refresh the hub’s Matter device list |
Found: discover hub → subscribe → read reachability → attach matter_controller on online peers (and refresh when setup params change).
Lost: detach matter_controller from peers that used that hub.
Subscribe after group sync
After the user is logged in and group/fabric nodes are loaded, subscribe once:
import { ESPRMMatterEventType } from "@espressif/rainmaker-matter-sdk";
user.subscribe(ESPRMMatterEventType.matterControllerFound, (event) => {
handleMatterControllerFound(event); // event.nodeId = controller RainMaker id
});
user.subscribe(ESPRMMatterEventType.matterControllerLost, (event) => {
handleMatterControllerLost(event);
});
Home app entry point (reference): subscribeMatterControllerTransport(esprmUser) from the Matter base adaptor / group sync path.
On controller found
- Resolve the controller node in your store
- If the controller is not cloud-connected, treat as lost (remove peer transports)
- Cloud-subscribe the controller node so you receive connect / disconnect / param updates
- Read setup-service reachability and add
matter_controlleron online peers
peer.availableTransports.matter_controller = {
type: "matter_controller",
metadata: { controllerNodeId },
};
In CDF apps, prefer the store helper (e.g. handleNodeTransportUpdate(store, peerId, { type, metadata }, "add")) so UI reachability stays consistent.
Peer reachability from setup service
| Constant (concept) | Value |
|---|---|
| Setup service | esp.service.matter-controller-setup |
| Devices param type | esp.param.matter-devices |
| Notification key | MatterCTLSetup → nested MTDevices |
Each MTDevices entry typically includes a RainMaker node id and an online flag. Suggested rules:
- Online + rainmaker_node_id → add
matter_controllerwith that controller’s id - Offline → remove
matter_controllerfrom that peer - Controller lost / disconnected → remove
matter_controllerfrom all peers that pointed at that controller
Fan-out live updates
When the controller setup param changes, the hub may push peer attribute snapshots. The Home app:
- Parses
MatterCTLSetup/MTDevicesfrom the controller’s param update - Builds a synthetic peer
ESPNodeUpdateData - Forwards it through the same Matter node-update handler used for local Matter subscriptions
That keeps UI state fresh for peers reached only via the controller, without requiring LAN Matter subscriptions.
Refresh controller device list
If peers are missing from the hub’s list, request a refresh by writing MTCtlCMD = 2 on the controller (or setup) service’s writable command param.
Home app helpers (reference): getMatterControllerConfig / updateMatterControllerDeviceList under shared Matter controller utilities.
On controller lost
- Unsubscribe from the controller’s cloud subscription
- Find peers whose
matter_controllermetadatacontrollerNodeIdmatches - Remove that transport from those peers
Do not rely on the controller still being in the node store — resolve peer ids from your registered-transports map if needed.
Explicit contract
| Owned by SDK | Owned by app |
|---|---|
| Detect controllers in group lists | Subscribe to found/lost |
| Emit found/lost events | Decide which peers get transport |
| Cmd-resp create / poll / parse | Supply controllerNodeId |
| Default transport order | Add/remove availableTransports |
| Local control via adapter | Optional MTDevices fan-out + settings UX |
What could go wrong
| Symptom | Likely cause |
|---|---|
| Found event but no remote control | Forgot to register peer transports |
| Transports stuck after hub offline | Missing lost/disconnect handler |
| UI not updating remotely | No fan-out from setup param pushes |
| Empty MTDevices | Hub list stale — try MTCtlCMD = 2 |
Related
- Remote Control overview
- RainMaker Controller
- Enable Controller Transport
- Native Bridge — same “reference architecture” idea for commissioning