npm.io
1.7.0 • Published 6d ago

@lucavb/shellies-ds9

Licence
GPL-3.0
Version
1.7.0
Deps
5
Size
1.3 MB
Vulns
0
Weekly
751

node-shellies-ds9

npm version npm downloads license

About This Fork

This is a fork of imuab/node-shellies-ds9, created to include device support that wasn't merged upstream.

Why This Fork Exists

The imuab repository had a pull request for Shelly Plug S Gen 3 EU support that didn't get merged. This fork includes that support along with additional device compatibility for:

  • Shelly Plus 1 Mini
  • Shelly Plus 1 PM Mini
  • Shelly Plus PM Mini
  • Shelly Plug S Gen 3 EU
  • Shelly 1L Gen3

This fork is specifically maintained for Homebridge compatibility where these devices are essential.

Installation

npm install @lucavb/shellies-ds9

About

Handles communication with the next generation of Shelly devices.

Supported devices

Outbound WebSocket server

Devices can dial into your Node process instead of requiring inbound LAN access — useful for NAT, battery-powered devices, and fleets.

Install the server entry point separately from the main client API:

import { ShellyOutboundServer } from '@lucavb/shellies-ds9/server';

const server = new ShellyOutboundServer({ port: 7011, path: '/shelly' });

server.on('device', async (device) => {
    console.log(`Device connected: ${device.id} (${device.modelName})`);
    await device.switch0.toggle();
});

server.on('disconnect', (deviceId, code, reason) => {
    console.log(`Device disconnected: ${deviceId} (${code}: ${reason})`);
});

await server.listen();

Configure a Shelly device to connect outbound (via inbound RPC or the device web UI):

await device.outboundWebSocket.setConfig({
    enable: true,
    server: 'ws://your-server:7011/shelly', // or wss:// for TLS
    ssl_ca: '*', // use 'user_ca.pem' + upload CA for self-signed TLS
});
// Ws.SetConfig returns restart_required: true — reboot the device after saving

TLS example:

import fs from 'fs';
import { ShellyOutboundServer } from '@lucavb/shellies-ds9/server';

const server = new ShellyOutboundServer({
    port: 7011,
    path: '/shelly',
    tls: {
        cert: fs.readFileSync('cert.pem'),
        key: fs.readFileSync('key.pem'),
    },
});

Common server URLs:

Deployment URL
This library (default path) ws://host:7011/rpc
Shelly Fleet Manager style ws://host:7011/shelly
Home Assistant style ws://host:8123/api/shelly/ws

When a device reconnects, the server emits disconnect then a new device event with a fresh Device instance.

Basic usage example

import { Device, DeviceId, MdnsDeviceDiscoverer, Shellies, ShellyPlus1 } from '@lucavb/shellies-ds9';

const shellies = new Shellies();

// handle discovered devices
shellies.on('add', async (device: Device) => {
    console.log(`${device.modelName} discovered`);
    console.log(`ID: ${device.id}`);

    // use instanceof to determine the device model
    if (device instanceof ShellyPlus1) {
        const plus1 = device as ShellyPlus1;

        // toggle the switch
        await plus1.switch0.toggle();
    }
});

// handle asynchronous errors
shellies.on('error', (deviceId: DeviceId, error: Error) => {
    console.error('An error occured:', error.message);
});

// create an mDNS device discoverer
const discoverer = new MdnsDeviceDiscoverer();
// register it
shellies.registerDiscoverer(discoverer);
// start discovering devices
discoverer.start();

See homebridge-shelly-ng for a real-world example.

Generic / unsupported devices

By default, unrecognized model strings are ignored and an unknown event is emitted. You can opt in to generic device support so Gen2+ hardware is discovered and controlled without a typed device class:

import { GenericDevice, isGenericDevice, MdnsDeviceDiscoverer, Shellies } from '@lucavb/shellies-ds9';

const shellies = new Shellies({ genericDevices: true });

shellies.on('unknown', (deviceId, model, identifiers, willAddGeneric) => {
    if (willAddGeneric) {
        console.log(`Unknown model ${model} (${deviceId}) — will add as generic device`);
    } else {
        console.log(`Unknown model ${model} (${deviceId}) — ignored`);
    }
});

shellies.on('add', async (device) => {
    if (isGenericDevice(device)) {
        const sw = device.get('switch:0');
        sw.on('change:output', (on) => console.log('switch output:', on));
        await device.call('Switch.Toggle', { id: 0 });
    }
});

// manual path
const device = await shellies.addGeneric(info, rpcHandler);

When genericDevices is enabled:

  • Components are discovered at runtime via Shelly.GetComponents
  • Known component keys (switch:0, cover:0, etc.) use the same typed component classes as registered devices
  • Unknown component types are exposed as dynamic proxies with change events and call() RPC access
  • The unknown event includes a willAddGeneric flag: true when a GenericDevice add follows, false when the device is ignored

genericDevices defaults to false today and may default to true in a future major release.

Credits

This fork builds upon the excellent work of:

Special thanks to the Shelly community and all contributors who have made this ecosystem possible.

Keywords