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

@apocaliss92/noderademacher

v0.1.0

Published

Node.js/TypeScript client for Rademacher HomePilot / Start2Smart hubs over the local network (REST + SSDP discovery)

Readme

noderademacher

Node.js / TypeScript client for Rademacher HomePilot / Start2Smart hubs over the local network (no cloud).

Spiritual successor to pyrademacher — same local REST API, TypeScript-native. If you can control a device in the HomePilot app, you can control it with this library. Covers/blinds, switches/plugs, dimmable actuators, Zigbee lights, thermostats/radiator valves, sensors and wall controllers.

Node only, local only. Talks HTTP to the hub on your LAN. No Rademacher cloud account is used or required.


Install

npm i @apocaliss92/noderademacher

Requires Node 20 or later.


Quick start

import { Rademacher, CoverDevice } from '@apocaliss92/noderademacher';

// Point at your hub (host or IP). Add `password` if the hub is protected.
const hub = new Rademacher({ host: '192.168.1.50' });

// 1. Connect (auto-resolves the api-version base path, logs in if protected)
await hub.connect();

// 2. List devices registered to the hub
const devices = await hub.listDevices();

// 3. Create a typed handle (fetches its initial state)
const cover = (await hub.createDevice(devices[0])) as CoverDevice;

// 4. React to polled state changes
cover.on('stateChanged', ({ changed }) => console.log('changed:', changed));

// 5. Control it
await cover.open();
await cover.setPosition(60); // 0 = closed, 100 = open

// 6. Re-poll whenever you like (HomePilot has no realtime push)
await cover.refresh();

Discovery

HomePilot has no broadcast/mDNS discovery (Rademacher's own integration finds it via DHCP MAC-OUI). So noderademacher discovers hubs by HTTP-probing the LAN — it confirms each host via the hub's identity endpoint:

import { Rademacher } from '@apocaliss92/noderademacher';

// Sweep every local /24 (bounded concurrency), or pass { subnet: '192.168.1' } / { hosts: [...] }
const hubs = await Rademacher.discover();
// → [{ host, model: 'HomePilot' | 'Start2Smart', name, firmware?, uid?, basePath, passwordProtected }]

for (const found of hubs) {
  const hub = new Rademacher({ host: found.host, basePath: found.basePath });
  await hub.connect();
}

Rademacher.discover({ subnet, hosts, concurrency, timeoutMs }) — narrow the sweep for speed. The Rademacher MAC OUIs (B0:1F:81, 38:FD:FE) are exported as RADEMACHER_MAC_PREFIXES if you want to pre-filter an ARP scan.


Devices — the gateway included, with all info

listDevices() / getDevices() return the hub/gateway itself as a separate device (a HubDevice, did = -1, category hub) first, then every physical device. Pass { includeHub: false } to omit it.

const devices = await hub.getDevices(); // typed handles, gateway first, each already polled
for (const dev of devices) {
  console.log(dev.name, dev.category, dev.describe());
}

describe() returns everything retrievable about a device: identity, battery, every capability (with value + min/max/step) and the latest statuses / readings. The HubDevice exposes firmware, hardware/software platform, DuoFern-stick version, node name, MAC, LED status and the firmware-update status.

Supported devices

| Class | DEVICE_TYPE_LOC | Highlights | | ---------------------- | ----------------- | ---------------------------------------------------------------- | | CoverDevice | 2, 8 | open / close / stop, setPosition, setTilt, ventilation | | SwitchDevice | 1 | turnOn / turnOff / setOn, isOn | | ActuatorDevice | 4 | dimmable: setBrightness, brightness, isOn | | LightDevice | 70–76 | actuator + setRgb, setColorTemperature | | ThermostatDevice | 5 | setTargetTemperature, currentTemperature, boost | | SensorDevice | 3 | temperature, wind, brightness, rain, contact, motion, smoke | | WallControllerDevice | 10 | channel key-press timestamps | | UnknownDevice | — | raw state + passthrough commands |

Feature detection is by capability-name presence (device.hasCapability(...)), exactly like the reference library — never by product code.


How it works

  1. Connect — probes http://<host>/… to resolve the api-version base path ('' v1 / /hp v2) and detect password protection; logs in via a two-stage salted SHA-256 challenge when a password is set (cookie session).
  2. DeviceslistDevices() reads /devices (full capability lists → feature detection + category).
  3. StatecreateDevice() / refresh() read /v4/devices/{did} (statusesMap + readings).
  4. Commands — every control is PUT /devices/{did} with { name, value? }.

HomePilot's local API has no realtime push, so state is refreshed by polling (device.refresh()).

Two gotchas worth knowing (handled for you)

  • Cover position is inverted on the wire (HomePilot 0 = open). This library exposes the HA-style position (100 = open) and inverts on both read and write.
  • Thermostat temperatures are deci-°C in state (/10) but plain °C in the write command — the model converts both ways.

Diagnostics dumper

import { createDumper } from '@apocaliss92/noderademacher';
const snap = createDumper(cover).dump();
// { did, category: 'cover', model, name: 'Ki*****', capabilities: [...], state: { statuses, readings } }

redact: true (default) masks the friendly name so dumps can be shared safely.


Error types

| Class | Thrown when | | -------------------------- | ------------------------------------------------------- | | RademacherError | Base class | | RademacherAuthError | Login / re-auth failure | | RademacherApiError | Non-2xx HTTP or a non-zero hub error_code (.status) | | RademacherTransportError | Network error reaching the hub |


Credits & scope

Reverse-engineered from pyrademacher (the library behind the peribeir Home Assistant Rademacher integration). See docs/rademacher-homepilot-api-spec.md for the full protocol notes this port is built from. Not affiliated with or endorsed by Rademacher.

License

MIT — see LICENSE.