API Reference
For developers integrating with GEM or extending it. GEM exposes several distinct surfaces; this section documents each.
:::warning Stability contract
Socket event names, REST endpoints, driver method signatures, macro step data keys, and the gem.* / context.* macro-script surface are frozen. New ones are added; existing ones are never repurposed or removed without a deprecation period. Build against them with confidence.
:::
Surfaces
REST
/api/data/[entity] for CRUD on configuration entities, /api/control/[action] to issue commands and run macros. Token-authenticated, gated by the same RBAC actions as the socket surface.
WebSocket
Socket.io is the primary real-time channel: state subscriptions, commands, and most interactive operations. Every socket event is auto-enumerated for RBAC, so adding a server handler automatically makes it a permissionable action.
Web Services
/web_service/[service] — custom HTTP endpoints backed by a script you write, for webhooks and external integrations.
Macro Scripts
The gem.* and context.* objects available inside the Run Script macro step. This is a public API: signature changes silently break stored automations, so it is treated as frozen.
Driver Development
How to write a device driver: extend device_base.js (or a generic transport), implement connect() / command() / response() / disconnect(), and expose metadata and commands. Includes relay state translation, per-zone action overrides, and toggle emulation.
Common client API
The frontend GemApp class wraps the WebSocket surface. A representative slice:
const gem = GemApp.getInstance();
// Data
await gem.query('zone', { subsystem_id: 11 });
await gem.insertModel('model', data);
await gem.updateModel('model', id, updates);
// Attributes
await gem.getAttribute('device', deviceId, 'ip_address');
await gem.setAttribute('zone', zoneId, 'brightness', 50, 'integer');
// Commands
await gem.command({ device: deviceId, zone: zoneId, action: 'on', level: 100 });
// Subscriptions
const token = await gem.subscribe('zone', zoneId, callback);
await gem.unsubscribe('zone', zoneId, token);