Node Store
NodeStore is the device graph manager. It stores and tracks all devices (nodes), their parameters, services, connectivity status, and local transports.
What it does
When you fetch a user's devices or receive a device update (via push), the store makes that data observable so your UI re-renders automatically when anything changes.
Core responsibilities:
- Store devices —
nodesListandnodesByIDMapfor fast lookup - Track real-time changes — Parameters, connectivity, OTA status
- Sync with SDK — Keep
_rawnode objects aligned with observable state - Manage local transports — Track WiFi, BLE, Thread discovery
API reference
Properties
| Property | Type | Purpose |
|---|---|---|
nodesList | ESPCDFNode[] | All nodes (computed from nodesByIdMap) |
nodesByIdMap | Record<string, ESPCDFNode> | Observable map for fast O(1) lookups |
Getting the store
import { initCDF } from "@espressif/rainmaker-base-cdf";
const espCDF = await initCDF({ sdkAdaptorRegistry });
const { nodeStore } = espCDF;
Transport discovery
Local transports (BLE, WiFi, Thread) are registered via subscriptionStore.transport.listen(), not through NodeStore. See Subscription Store for details.
How to use it
Query nodes
// All nodes
const nodes = espCDF.nodeStore.nodesList;
// Single node by ID
const node = espCDF.nodeStore.getNodeById("node-id");
Control device parameters
Set one parameter:
const param = node.devices?.[0]?.params?.find((p) => p.name === "Power");
await param?.setValue(true);
Set multiple parameters at once:
const node = espCDF.nodeStore.getNodeById("node-id")!;
await node.setMultipleParams({
Light: { Power: true, Brightness: 80 },
Fan: { Speed: 3 },
});
Navigate the device graph
Devices contain parameters. Services contain params too. Both can be read-only or controllable.
const node = espCDF.nodeStore.getNodeById("node-id")!;
// Iterate devices and their params
for (const device of node.devices ?? []) {
console.log("Device:", device.name, device.type);
for (const param of device.params ?? []) {
console.log(" Param:", param.name, "=", param.value);
}
}
// Iterate services
for (const service of node.services ?? []) {
console.log("Service:", service.name, service.type);
for (const param of service.params ?? []) {
console.log(" Param:", param.name);
}
}
Check connectivity
Connectivity updates arrive via the real-time push path. Read the observable field:
const node = espCDF.nodeStore.getNodeById("node-id")!;
const isConnected = node.connectivityStatus?.connected;
// Available transports (local WiFi/BLE/Thread discovery)
const transports = node.availableTransports;
Update device metadata
await node.updateMetadata({
location: "Living Room",
timezone: "Asia/Kolkata"
});
// Or set timezone separately
await node.setTimeZone("Asia/Kolkata");
Manage OTA updates
Check and push firmware updates:
const { data: otaInfo } = (await node.checkOTAUpdate?.()) ?? {};
if (otaInfo?.available) {
await node.pushOTAUpdate({ firmwareImageId: otaInfo.imageId });
}
// Check update status by job ID
const status = await node.getOTAUpdateStatus(otaJobId);
Remove a node
await node.delete();
// NodeStoreSynchronizer removes it automatically
Reactive UI components
Wrap components that read nodes with observer(). MobX re-renders only when the fields you read actually change.
Example: Watch node list size
// Re-renders when nodesList changes (nodes added/removed)
const NodeCount = observer(function NodeCount({ espCDF }: { espCDF: ESPCDF }) {
return <Text>{espCDF.nodeStore.nodesList.length} devices</Text>;
});
Example: Control a single parameter
import { observer } from "mobx-react-lite";
import type { ESPCDF } from "@espressif/rainmaker-base-cdf";
// Re-renders ONLY when this param's value changes
// Other params on the node changing won't trigger a re-render
const LightSwitch = observer(function LightSwitch({
espCDF,
nodeId,
}: {
espCDF: ESPCDF;
nodeId: string;
}) {
const node = espCDF.nodeStore.getNodeById(nodeId);
const powerParam = node?.devices?.find(d => d.type === "esp.device.lightbulb")
?.params?.find(p => p.name === "Power");
if (!powerParam) return null;
return (
<Switch
value={powerParam.value as boolean}
onValueChange={(val) => powerParam.setValue(val)}
/>
);
});
Best practice: Keep observer components small and granular. A component that reads only param.value re-renders only when that value changes, avoiding unnecessary renders.
See also
Real-time updates
- Subscription Store — how parameter and connectivity changes arrive
Implementation
API Reference