Skip to main content

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 devicesnodesList and nodesByIDMap for fast lookup
  • Track real-time changes — Parameters, connectivity, OTA status
  • Sync with SDK — Keep _raw node objects aligned with observable state
  • Manage local transports — Track WiFi, BLE, Thread discovery

API reference

Properties

PropertyTypePurpose
nodesListESPCDFNode[]All nodes (computed from nodesByIdMap)
nodesByIdMapRecord<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 },
});

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

Implementation

API Reference

On this page