Official starter template for building an external integration for
Gladys Assistant with the JavaScript SDK
@gladysassistant/integration-sdk.
Fork it, add the GitHub topic
gladys-assistant-integration, push a multi-arch image, bump the version — that's publishing. No account, no review.
This is not a 40-line hello-world: it deliberately shows several device
types so you can copy the one closest to your hardware. Everything lives in
the src/devices/ folder (one file per device type), and every
place where you would talk to your real hardware / cloud API is marked with a
DO THE WORK comment and a logger call.
| Device | Type illustrated | SDK hooks used |
|---|---|---|
| Weather station | Read-only sensors (temperature + humidity), real data via Open-Meteo | onPoll, publishStates, onAction |
| Living room switch | Binary actuator (ON/OFF) | onSetValue, publishState |
| Living room light | Dimmable light (on/off + brightness), identify action target |
onSetValue per feature, identify |
| Office plug | Mixed: actuator + power metering, transport badge + degraded | onSetValue, onPoll, publishTransports |
| Entrance motion sensor | Push / event-driven sensor | startPush, publishState |
| Entrance camera | Camera images: periodic snapshot + on-demand fresh capture | publishCameraImage, onGetImage |
The wiring (connection, auth, reconnection, dispatch) is in
index.js — you rarely need to touch it.
.
├─ index.js # SDK bootstrap + event wiring (no device logic)
├─ src/
│ ├─ devices/ # ← one file per device type (edit these)
│ │ ├─ index.js # registry: list your devices here
│ │ ├─ weatherStation.js # read-only sensors (poll)
│ │ ├─ switchDevice.js # binary actuator
│ │ ├─ light.js # dimmable light (on/off + brightness)
│ │ ├─ plug.js # actuator + power metering + transport badge
│ │ ├─ motionSensor.js # push / event-driven sensor
│ │ └─ camera.js # camera images (push + pull)
│ ├─ weather.js # example real "driver" (Open-Meteo)
│ └─ config.js # config defaults + normalization
├─ docs/
│ ├─ en.md # user documentation (re-hosted by Gladys,
│ └─ fr.md # linked from the Configuration screen)
├─ gladys-assistant-integration.json # manifest (name, config schema, image…)
├─ Dockerfile # Node 24 Alpine, read-only rootfs ready
├─ .github/workflows/release.yml # UI-driven release: bump + tag + build
├─ .github/workflows/build.yml # multi-arch build (git tag or called by release)
└─ cover.png # catalog cover, 800×534 px, ≤150 KB
To add a device type, create a new file in src/devices/ following the same
shape as the existing ones, then register it in src/devices/index.js. Business
logic (the device modules) and utilities (weather.js, config.js) are kept
separate so the parts you edit stay small.
The plumbing you would otherwise copy into every integration comes straight from the SDK (v0.12.0+):
logger/createLogger({ name })— leveled console logger (LOG_LEVELenv var), with named/child loggers per module. Since SDK v0.4 the SDK also logs its own connection lifecycle (under thegladys-sdkname), so connectivity problems show up indocker logswithout extra code;DEVICE_FEATURE_CATEGORIES,DEVICE_FEATURE_TYPES,DEVICE_FEATURE_UNITS— the standard Gladys categories / types / units, no manual string copying. The catalog grows with the SDK, so bumping the dependency is how you get the newest ones:battery-storage,doorbellandwater-valvecategories, the climatefan-speed/swing-horizontal/swing-verticalfeatures and thecubic-meter-per-hourunit (SDK v0.10), thencharging-stationandwater-heatercategories plus the thermostatmode/operating-statefeatures (SDK v0.11), then thegrid-sensor/home-output-sensor/maintenancecategories, theno2/o3/so2gas-concentration sensors, the camera PTZ features (move/preset/ absolute positions), the solarproductionpowerfeature and the dynamictextselecttype whose per-device choices live insupported_options(SDK v0.12). A recent category only renders on a Gladys that knows it, so keep the manifestgladys_versionrange in sync with what you publish;gladys.externalIds(type, platformId)— builds the unique, stable device and feature external ids;gladys.handleShutdown(cleanup)— graceful SIGTERM/SIGINT handling;gladys.setConnectionStatus(connected, message?)— application-level connection status shown in the Configuration screen (the template reports it after every (re)initialization);gladys.onAction(key, cb)— handler of a manifestactionsbutton: the template declares atest_weatheraction (manifestactionsfield) and the weather station blueprint implements it, returning the multi-language message displayed under the button;gladys.publishCameraImage(externalId, image)/gladys.onGetImage(cb)(SDK v0.5) — the camera image channel: push a periodic snapshot and answer on-demand capture requests with animage/jpg;base64,...string (≤ 150 KB, max 12 images/minute per device). Dedicated channel: images never go through the states history. Seesrc/devices/camera.js;gladys.publishTransports(entries)+DEVICE_TRANSPORTS(SDK v0.5) — the per-device cloud/local transport badge for dual-channel devices. The manifest declares"transports": ["local", "cloud"], so the Configuration screen shows a standard "Prefer the local connection" toggle whose value arrives as the reserved, read-only config keyGLADYS_PREFER_LOCAL(boolean, defaulttrue). The demo plug applies the preference and reports its effective transport. Since SDK v0.7 an entry can also flag a degraded state ({ degraded: true, message }) — "it works, but not in the nominal mode": the demo plug uses it when local is preferred but the LAN session is refused, so the cloud fallback shows an orange dot with the reason instead of a silently normal badge. Seesrc/devices/plug.js;- dynamic device selects (SDK v0.7) — a manifest
selectfield can replace its staticoptionswith"source": "devices": the Configuration screen fills it with the integration's own created devices and the handler receives the chosenexternal_id. The template'sidentifyaction uses it to make the chosen device signal itself — the answer to "act on THIS device" without asking the user to copy an identifier; sectionconfig blocks + the Documentation link (SDK v0.8) — purely presentational intro blocks in the manifestconfig_schema(title, plain-text description, https links) for the onboarding guidance a compact form cannot carry; they store no value. For the long step-by-step, the Configuration screen shows a permanent Documentation link to the repo'sdocs/en.md/docs/fr.md, re-hosted by Gladys.
The SDK offers more for integrations that need it — OAuth2 cloud flows
(onOAuthAuthorizeUrl / onOAuthCallback + an oauth2 config field, or the
account_link variant — SDK v0.12 — for providers that never redirect back:
QR sign-in approved in the vendor app, redirectUri undefined, no callback,
the integration polls the provider and reports through
setConnectionStatus(true)), sub-containers (getContainers, startContainer… + the manifest containers
field, whose published ports now come back as
{ container_port, protocol, host_port, label, name, browsable } — SDK v0.11,
host_port being the one Gladys allocated, null until it does, and name
what makes it referenceable in a manifest section text through the
{{gladys_host}} / {{port:<name>}} placeholders the frontend substitutes at
render time), mediated network discovery (scanNetwork + the manifest
network_discovery field, for UDP-broadcast / mDNS / SSDP scans from the
core — including the active query/response variant udp-active-broadcast,
SDK v0.7, where the integration forges the discovery request and the core
broadcasts it), communication channels (manifest type: "communication":
bidirectional Telegram-like bots linked by code — SDK v0.6,
publishMessage / onSendMessage / linkContact — and, since SDK v0.9,
send-only notification channels — messaging: { receive: false } plus a
manifest contact_schema describing the per-user credentials that
onSendMessage(contact, message) receives), and incoming webhooks relayed
by Gladys Plus (SDK v0.9: manifest webhooks field +
getWebhooks / onWebhook / onWebhookUpdated, for cloud services that
push their events Netatmo-style — the demo weather API only supports
polling, so the template does not declare any), Wake-on-LAN (SDK v0.12:
wakeOnLan(mac, options?) + the manifest network_wake field — the core,
which sits on the host network the bridge container cannot broadcast to,
builds and emits the standard magic packet itself, rate-limited to 1 wake
per 2 s per integration), and weather providers
(SDK v0.11: manifest type: "weather", onWeatherGet answering with the
pivot weather format in the unit system the user asked for, plus the optional
onWeatherGetImage for a vigilance map or a rain radar and
requestWeatherRefresh() to nudge the core into re-pulling instead of waiting
for its 30-minute check — a provider feeding the dashboard widget and the chat
assistant, not devices, so it is a different integration type than this
template's type: "device", even though the demo weather station here reads
the same kind of API). See the
SDK README for those
patterns; this template stays focused on devices.
npm install
GLADYS_HOST_API_URL="http://localhost:1443" \
GLADYS_INTEGRATION_TOKEN="<token>" \
GLADYS_INTEGRATION_SELECTOR="demo-devices-template" \
LOG_LEVEL=debug \
npm startThe three GLADYS_* variables are injected by the Gladys supervisor when the
integration runs inside its sandboxed container. The SDK reads them
automatically.
The template ships with the tooling every integration should keep. The same
three checks run automatically on every push and pull request (see
.github/workflows/ci.yml):
npm run format:check # Prettier: is everything formatted?
npm run format # Prettier: format everything in place
npm run lint # ESLint: catch real mistakes (unused vars, dead code…)
npm test # Unit tests, via the built-in `node --test` runnerTests live in test/ and use Node's native test runner — no extra
test framework to install. Add a *.test.js file next to the ones already
there and it is picked up automatically.
Before you tag a release, you can check that your integration passes the store validation locally, without waiting for the hourly indexer. Run the store's validator against your integration directory:
npx github:GladysAssistant/integration-store .It runs the exact same checks as the store indexer — manifest JSON & schema,
Docker image availability (main and sub-containers), cover image (format,
dimensions, size) and the code rules — and reports every problem at once so
you can fix them in a single pass. It exits 0 when the integration is valid,
1 otherwise. A few things can only be confirmed once the repository is public
(public repo, the gladys-assistant-integration topic, and the manifest sitting
at the root of the default branch), and the tool tells you which ones. See the
integration store for
details.
- Fork this template (or use Use this template on GitHub).
- Edit the files in
src/devices/andgladys-assistant-integration.jsonfor your devices, and replacedocker_image/cover_imagewith your own. Pick the manifestcategories(Gladys 4.86+) your integration belongs to — 1 to 3 keys amongclimate,lighting,energy,security,multimedia,appliances,environment,protocols,network,notifications,assistants,services— they are the catalog shelves the integration sits on (without them it only shows under "All" and in search). Declaring the field requires agladys_versionrange starting at 4.86.0 or later — older cores reject unknown manifest fields, and the store validator enforces the coupling. The template declareslighting,securityandenvironmentto match its demo devices. - Add the GitHub topic
gladys-assistant-integrationto your repo. - Release from the GitHub UI: open Actions → Release → Run workflow,
pick
patch,minorormajor. The workflow bumps the version everywhere (package.json+ manifestversion/docker_image), pushes thevX.Y.Ztag, and builds thelinux/amd64+linux/arm64image toghcr.io(:X.Y.Zand:latest). No local tag, no manual version edit. - The decentralized indexer picks up the new manifest
versionand Gladys offers a one-click install / update.
Prefer the terminal?
git tag v1.0.0 && git push --tagsstill works — the hand-pushed tag triggers the same multi-arch build. This path only publishes the Docker tags, though: it does not touchpackage.json,package-lock.jsonor the manifest. Bumpversion(anddocker_image) ingladys-assistant-integration.jsonand commit it before tagging, or the indexer will keep serving the old version. The Release workflow above does all of this for you.
Full documentation: https://gladysassistant.com (integrations developer guide).
- Requires Node.js ≥ 20 (uses the built-in global
fetch; no HTTP dep). - All external identifiers are prefixed with
ext:<selector>:— always build them withgladys.externalIds(type, platformId)(or the lower-levelgladys.externalId(suffix)); the server rejects anything else. DeriveplatformIdfrom the unique id the external platform gives you (serial, cloud id, MAC…), never from a hard-coded label. has_feedback: truefeatures should publish the state confirmed by the device; the template publishes the requested value for simplicity.- Replace
cover.pngwith your own 800×534 px image (≤150 KB, PNG or JPEG) before publishing. The bundled one is a plain gradient placeholder.
Apache-2.0