Device control
Changing what a device does means writing one of its parameters — Power on a light, Brightness on a dimmer. There are two ways to write: param.setValue() for a single parameter, and node.setParams() for several at once. Both pick the transport for you.
Write one parameter
const light = node.devices.find((d) => d.name === "Light");
const power = light?.params.find((p) => p.id === "Power");
await power?.setValue(true);
setValue(value) returns Promise<ESPAPIResponse>. The value type follows the parameter's own dataType, a boolean for power, a number for brightness, a string for a mode. The SDK does not coerce, so send the type the device expects.
Use primaryParam when you want the device's main control without naming it:
await light?.primaryParam?.setValue(true);
primaryParam is the parameter the firmware nominated, falling back to the first parameter when none was.
Write several at once
await node.setParams({
Light: { Power: true, Brightness: 60 },
});
setParams(params) returns Promise<ESPAPIResponse> and takes a map of device or service names to their parameter maps. One call, one publish; prefer it over several setValue() calls on the same node, which each publish separately.
It throws ESPAPICallValidationError when params is empty.
What a parameter tells you
Read these before building a control for it:
| Property | Contents |
|---|---|
id | Parameter name, as used in a payload |
dataType | bool, int, float or string |
properties | Capability flags such as write and time-series support |
uiType | Hint for which control to render |
bounds | min and max when the parameter is ranged |
validStrings | Allowed values for an enumerated parameter |
value | Last known value |
deviceName | Owning device |
bounds, validStrings and uiType are optional and firmware-supplied. Respect bounds in your UI; the SDK does not clamp, so an out-of-range write reaches the device and is rejected there.
Read current values
const params = await light?.getParams();
const live = await light?.getParams({ cache: false });
getParams(options?) returns Promise<ESPRMNeoDeviceParam[]>, the same instances as device.params, with their value updated. It never returns copies, so a reference you already hold reflects the read.
The cache option defaults to true:
cache: truereads from the cached node configuration, no networkcache: falsefetches live over the best available transport
Use the default when opening a screen and cache: false when the user asks to refresh. For continuous freshness, do neither and let live updates keep the values current. See Live updates.
What a successful write means
The response tells you the transport accepted the publish; a local acknowledgement, or an MQTT publish that resolved. It does not mean the device received or applied the value; cloud publishes are fire-and-forget. Render the value the device reports back, not the value you sent, or your UI will show changes that never happened.
The promise resolves at the third step. Everything below it happens afterwards, and may not happen at all.
The reported value arrives through the node's own subscription, so the way to confirm a write is to observe the update:
import { ESPRMNeoEventType } from "@espressif/rainmaker-neo-base-sdk";
user.subscribe(ESPRMNeoEventType.nodeUpdates, (update) => {
console.log(update.nodeId, update.payload);
});
When a write fails
A write throws when no transport is available (ESPAPICallValidationError with code NODE_UNREACHABLE) or with the last transport's error when every transport was tried and failed. A node that has gone offline in the cloud has no transport at all.
Writing through a parameter whose node has been garbage-collected throws MISSING_NODE_REF. See Nodes.
Related
- Service control — writing service parameters
- Group control — one payload to every node
- Transports — how local and cloud are chosen
- Live updates — observing reported state
- Time series — a parameter's history