Docs

State

The one piece of memory that survives between handler calls.

state is a plain object the hub injects before every handler and saves back after it returns. It is the only memory an instance has between calls: top-level variables reset on every run.

function onMotion() {
  state.motionCount = (state.motionCount ?? 0) + 1;
  state.lastMotionAt = new Date().toISOString();
}

function onDailyReport() {
  sendPushNotification(`${state.motionCount ?? 0} motion events today`);
  state.motionCount = 0;
}

Rules

  • JSON only. Strings, numbers, booleans, arrays and plain objects. An EntityWrapper stored in state serialises to { entityId, type: 'EntityWrapper' }; store the id and rebuild the wrapper with new EntityWrapper(id) when you need it.
  • Start empty. A new instance begins with {}. Write code that tolerates missing keys (state.count ?? 0) rather than seeding defaults in installed only.
  • Whole-object save. The hub persists the object after the handler returns; there is no partial update and no versioning. Keep it small (kilobytes, not megabytes).
  • Per instance. Two installs of the same definition have separate state. To share between apps, use location events or an input_* helper entity.
  • Survives reboots, settings changes and code updates. Cleared on uninstall.

Reading state outside a handler

Only handlers run inside the isolate, so there is no way to read state from a dynamic preferences(values) function or from another app. Surface anything a person should see through console.log, a notification, or by writing it to an input_text / input_number helper entity that dashboards can show.

Patterns

Debounce / inactivity timers do not need state: runIn replaces its own pending timer.

Per-entity bookkeeping keyed by entity id:

function onAnyDoor(evt) {
  state.openSince ??= {};
  if (evt.newState.state === 'on') state.openSince[evt.entityId] = Date.now();
  else delete state.openSince[evt.entityId];
}

Idempotent handlers: timers that came due while the hub was off fire once on boot, and a re-delivered event is possible after a crash. Make handlers safe to run twice.

On this page