A typed Node.js client for the Heatzy (Gizwits) API, providing access to Heatzy pilot-wire modules and connected radiators — Pilote (V1/V2/V4), Glow, Onyx, Shine and Pro.
- Strongly typed — full TypeScript types for the Gizwits wire format, with 100% TSDoc coverage on the public surface.
- Every generation supported — Pilote V1's positional
rawtriplet, V2/V4 derogations, Glow's split temperature registers (incl. Onyx and Shine), and Pro's measures, open-window detection and presence mode — behind one facade family. - Resilient by default — auto-retry on transient failures, pre-emptive session refresh, and reactive re-login on Gizwits's 400-coded token expiry.
- Validated boundaries — Zod schemas guard every consumed payload, so upstream shape drift surfaces as a typed
ValidationErrorinstead of a deepundefinedcrash. - Registry + facades — an identity-preserving device registry with product-aware facades (
supportsV2/supportsGlow/supportsPronarrowing). - Tree-shakable — ESM only,
sideEffects: false.
- Node.js >= 22.20.0
- A valid Heatzy account
- For installing the package: a GitHub personal access token with the
read:packagesscope
Important
This package is published to GitHub Packages, not the public npm registry.
Configure your project so npm fetches the @olivierzal scope from GitHub:
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
@olivierzal:registry=https://npm.pkg.github.qkg1.topNODE_AUTH_TOKEN must be a GitHub personal access token with the read:packages scope (export it in your shell or set it in your CI environment). Then:
npm install @olivierzal/heatzy-apiimport { FacadeManager, HeatzyAPI, supportsGlow } from '@olivierzal/heatzy-api'
const api = await HeatzyAPI.create({
username: 'user@example.com',
password: 'password',
})
const manager = new FacadeManager(api)
// Interact with a device through its facade
for (const device of api.registry.getDevices()) {
const facade = manager.get(device)
console.log(facade.name, facade.mode, facade.isOn)
if (supportsGlow(facade)) {
console.log(facade.currentTemperature, facade.comfortTemperature)
}
}The client keeps its session alive without caller involvement:
- Pre-emptive refresh — the Gizwits token is renewed when it comes within 5 minutes of expiry, so no request pays the re-authentication round-trip on its critical path.
- Reactive recovery — Gizwits reports an invalid or expired token as HTTP 400 (not 401); the SDK re-authenticates once from persisted credentials and replays the original request. A cooldown guard prevents retry loops.
- Persistence — pass a
settingManager(a simpleget/setstring store) to persist the token and credentials across restarts; without one, everything stays in memory and a new instance signs in from scratch.
Important
The SettingManager receives the user token and the account password as plain strings. You are responsible for backing it with secure storage (encrypted settings store, OS keychain, …) — do not write it to a world-readable file. The SDK redacts credentials and tokens from its own log output.
const settings = new Map<string, string>()
const api = await HeatzyAPI.create({
settingManager: {
get: (key) => settings.get(key) ?? null,
set: (key, value) => settings.set(key, value),
},
})The request pipeline applies two policies, outermost first:
- Auth retry (HTTP 400/401) — a single re-authentication + replay, as described above.
- Transient retry (GET only) — 502/503/504 responses are retried up to 4 times with exponential backoff and jitter (1 s initial delay, 16 s cap). Mutations (
POST) are never retried automatically: a write that may have landed server-side must not be silently duplicated. Each retry is observable through theonRequestRetrylifecycle event.
Full API reference at https://olivierzal.github.io/heatzy-api/.
See CONTRIBUTING.md.
For vulnerability reports, see SECURITY.md.
See CHANGELOG.md.
Caution
This API is not endorsed, verified or approved by Heatzy or Gizwits. Heatzy cannot be held liable for any claims or damages that may occur when using this client to control Heatzy devices.
MIT © Olivier Zalmanski