Skip to main content

Macro Scripts

The Run Script macro step executes arbitrary JavaScript in a sandboxed VM context. Use it for logic that doesn't fit the other step types — computing a value, iterating dynamic collections, conditional branching beyond the built-in condition steps.

The execution surface

A script runs in an isolated vm context with these globals:

GlobalWhat it is
gemThe GemServer instance — the full server API: gem.command(...), gem.macro(...), gem.getAttribute(...), gem.setAttribute(...), and the live in-memory collections gem.zones, gem.devices, gem.subsystems, …
contextThe current macro context — variables and per-run state shared across the macro's steps (e.g. values set by a Set Variable step, the current item in a For Each).
signalAn AbortSignal that fires when the macro is stopped.
console, timers, Buffer, URLStandard utilities.
JSON, Math, Date, and core built-insStandard JS.

A script may be synchronous or async (return a Promise). Its return value becomes the step's result.

// set a zone state
await gem.setAttribute('zone', 5, 'state', 'on', 'string');

// summarize live state
const zones = Object.values(gem.zones);
const active = zones.filter(z => z.state === 'on');
return { count: active.length };

Limits

  • 30-second hard timeout — the script is aborted if it runs longer. Avoid long loops; the timeout will kill them.
  • Scripts respect the macro's abort signal, so a long-running async script exits when the macro is stopped.

:::warning gem.* and context.* are a frozen public API Saved macros and user scripts are stored JSON written by past versions. Changing the signature of a gem.* or context.* method silently breaks every stored script that uses it. New helpers are added; existing ones are never repurposed or removed. Prefer the documented gem.<helper> methods over raw database access. :::

See the Automation guides for building macros, and Architecture for how gem relates to the rest of the system.