wiz-light-core
v5.2.0
Published
Local WiZ light engine: protocol, discovery, colour math, and persisted state. Zero runtime dependencies.
Readme
wiz-light-core
The engine behind WiZ Light Controller: a small, dependency-free JavaScript library for controlling Philips WiZ smart bulbs over the LAN (UDP, port 38899) — no cloud, no account. It has zero runtime dependencies (Node built-ins only) and is the single, tested implementation shared by the wiz CLI and the native macOS app.
Install
pnpm add wiz-light-coreRequires Node 24+. ESM-only ("type": "module").
What's inside
protocol.js— the UDP transport.sendPilot(fire-and-forgetsetPilot),queryPilot(getPilot, resolving to the bulb's state ornullon timeout — it never rejects), and the statefulWizLightcontroller, which debounces rapid updates (slider drags) into one send and retries it to ride out UDP loss and firmware micro-sleeps. The socket factory is injectable, so the protocol is testable without real sockets.discovery.js—discover()broadcastsgetSystemConfigand resolves the set of bulbs on the LAN, deduplicated by MAC. Supports anonFoundcallback, anAbortSignal, and an injectable socket.color.js— pure colour maths:rgbToHsv/hsvToRgb,rgbToHex/hexToRgb,kelvinToRgb, and colour-wheel geometry (wheelToHS/hsToWheel).validate.js— input guards and clamps:isValidIp,isValidHex,normalizeHex,formatMac,clampBrightness,clampTemp,clampRgb, andtoDimming(the firmware-validdimming >= 10floor).model.js— the logicalLightStateand its translation to/from the wire format (parsePilot/buildSetPilotParams), per-device capability parsing fromgetModelConfig(deviceBoundsFromConfig,describeDevice,dimToWarmCurveFromConfig), the Warm-Glow curve (warmGlowKelvin), and presets.- stores — corruption-tolerant, atomic JSON persistence (settings, presets, saved lights, last state), wired to one directory by
createStores(dir).
The pure, browser-safe modules are also published as subpath exports — wiz-light-core/color, wiz-light-core/validate, wiz-light-core/model — so a UI layer can import the colour maths and the model without pulling in any Node built-ins.
Example
import { WizLight, discover } from 'wiz-light-core';
const bulbs = await discover(); // [{ ip, mac, moduleName }, …]
const light = new WizLight(bulbs[0].ip);
light.apply({ on: true, mode: 'rgb', rgb: [255, 0, 128], brightness: 80 });
const live = await light.getPilot(); // current state, or null if unreachable
light.close();Tests
node --test # from this package (or `pnpm test:core` from the repo root)