npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bobfrankston/wzlan

v0.1.10

Published

Wiz LAN protocol library for device control via UDP

Readme

@bobfrankston/wzlan

Platform-agnostic Wiz LAN protocol library for controlling Wiz smart bulbs via UDP.

Architecture

wzlan (protocol core, platform-agnostic)
├── wzlan-node (Node.js adapter — dual-socket UDP via rmfudp)
└── wzlan-browser (Browser adapter — WebSocket via httpudp-client)

Wiz uses JSON-over-UDP on two ports:

  • 38899 — Send commands, receive responses
  • 38900 — Receive heartbeat/syncPilot status (after registration)

Quick Start (Node.js)

import { createClient } from '@bobfrankston/wzlan-node';

const client = createClient();

client.on('device', (device) => {
    console.log(`Found: ${device.mac} ${device.ip}`);
});

client.on('state', (device) => {
    console.log(`${device.mac} power=${device.power} dim=${device.dimming} temp=${device.temp}`);
});

await client.start();   // Binds sockets, starts registration loop
// Heartbeats arrive automatically (~1/sec per bulb for ~20s, re-registered every 15s)

API

WzClient

Created via createClient() (wzlan-node) or createWzlanClient() (wzlan-browser).

| Method | Description | |--------|-------------| | start() | Bind transport, start registration loop | | stop() | Close sockets, stop timers | | register(heartbeats?) | Broadcast registration (true=heartbeats, false=discovery only) | | discover() | Shorthand for register(false) | | getDevice(mac) | Look up device by MAC | | addDevice(mac, ip?, port?) | Add device manually | | devices | Map<string, WzDevice> of all known devices |

Events

| Event | Args | Description | |-------|------|-------------| | device | (device) | New device discovered | | state | (device) | State updated (syncPilot heartbeat or setPilot response) | | pilot | (device, msg) | getPilot response received | | systemConfig | (device, msg) | getSystemConfig response received | | message | (device, msg) | Any message received | | error | (err) | Transport or protocol error |

WzDevice

Extends DeviceBase from @bobfrankston/devdefs — shared state (mac, ip, port, power, label, online, lastSeen, markSeen()) and transport plumbing. See devdefs README for base class details and MAC utilities (mac12, macmac).

Properties updated automatically from heartbeats:

| Property | Type | Description | |----------|------|-------------| | mac | string | MAC address (lowercase, colon-separated) | | ip | string | Current IP address | | power | boolean | On/off state | | dimming | number | Brightness 0-100 | | color | WizColor | { r, g, b, c, w } channels (0-255) | | temp | number | Color temperature in Kelvin | | sceneId | number | Active scene (0 = none) | | speed | number | Animation speed | | rssi | number | WiFi signal strength (dBm) | | fwVersion | string | Firmware version | | online | boolean | Responding to messages | | lastSeen | number | Timestamp of last message |

Control Methods (fire-and-forget)

device.setPower(true);              // Turn on
device.setPower(false);             // Turn off
device.setBrightness(75);            // Set brightness 0-100
device.setColor(255, 0, 0);        // Set RGB
device.setColor(0, 255, 0, 50);    // Set RGB with dimming
device.setWhite(4200);             // Set color temperature
device.setWhite(2700, 80);         // Set temp with dimming
device.setScene(4);                // Set scene by ID
device.setScene(5, 100, 80);       // Scene with speed and dimming
device.pulse(-30, 900);            // Pulse brightness
device.getPilot();                 // Query current state
device.getSystemConfig();          // Query system config

Protocol Functions

For building custom messages:

import { encodeMessage, decodeMessage, createSetPilot, createRegistration } from '@bobfrankston/wzlan';

const msg = createSetPilot({ state: true, dimming: 75 });
const bytes = encodeMessage(msg);
// Send bytes via UDP to port 38899

const response = decodeMessage(receivedBytes);

Scenes

| ID | Name | ID | Name | |----|------|----|------| | 1 | Ocean | 17 | True Colors | | 2 | Romance | 18 | TV Time | | 3 | Sunset | 19 | Plant Growth | | 4 | Party | 20 | Spring | | 5 | Fireplace | 21 | Summer | | 6 | Cozy | 22 | Fall | | 7 | Forest | 23 | Deep Dive | | 8 | Pastel Colors | 24 | Jungle | | 9 | Wake Up | 25 | Mojito | | 10 | Bedtime | 26 | Club | | 11 | Warm White | 27 | Christmas | | 12 | Daylight | 28 | Halloween | | 13 | Cool White | 29 | Candlelight | | 14 | Night Light | 30 | Golden White | | 15 | Focus | 31 | Pulse | | 16 | Relax | 32 | Steampunk | | 1000 | Rhythm | | |

Constants

import { WIZ_PORT, WIZ_LISTEN_PORT, WizMethod, WizScenes } from '@bobfrankston/wzlan';

WIZ_PORT          // 38899 — command port
WIZ_LISTEN_PORT   // 38900 — heartbeat port
WizMethod.SetPilot      // 'setPilot'
WizMethod.GetPilot      // 'getPilot'
WizMethod.SyncPilot     // 'syncPilot'
WizMethod.Registration  // 'registration'
WizScenes[12]           // 'Daylight'

wzlan-node

Dual-socket transport: ephemeral port for commands (→38899), port 38900 for heartbeats. Both merged into single message handler.

import { createClient } from '@bobfrankston/wzlan-node';

const client = createClient({
    debug: true,                  // Log messages to console
    registrationInterval: 15000,  // Re-register every 15s (default)
    homeId: 704603,              // Optional home ID
});

wzlan-browser

WebSocket transport via httpudp proxy. The httpudp server handles dual-port proxying server-side.

import { createWzlanClient } from '@bobfrankston/wzlan-browser';

const client = await createWzlanClient({
    httpudpUrl: 'ws://localhost:9321',
    registrationInterval: 15000,
});

wztest CLI

Simple test/control tool:

wztest                          Interactive mode
wztest list                     List discovered bulbs
wztest <mac> -on [dim]          Turn on
wztest <mac> -off               Turn off
wztest <mac> -dim 75            Set dimming
wztest <mac> -color 255 0 0     Set RGB
wztest <mac> -white 4200        Set white temp
wztest <mac> -scene 4           Set scene (Party)
wztest -v                       Verbose (show heartbeats)

Device can be partial MAC, partial IP, or #N from list.