node-shellies-ds9
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-ds9About
Handles communication with the next generation of Shelly devices.
Supported devices
- Shelly Plus 1 + V3
- Shelly 1L Gen3
- Shelly Plus 1 PM + V3
- Shelly Plus 1 Mini + V3
- Shelly Plus 1 PM Mini + V3
- Shelly Plus PM Mini + V3
- Shelly Plus 2 PM
- Shelly 2PM Gen3
- Shelly Plus I4 +V3
- Shelly Plus I4 DC
- Shelly Plus Plug S
- Shelly Plus Plug US
- Shelly Plus Plug UK
- Shelly Plus Plug IT
- Shelly Plus H&T +V3
- Shelly Plus 0-10V Dimmer
- Shelly Plug S Gen3 EU
- Shelly AZ Plug Gen3
- Shelly Outdoor Plug S Gen3
- Shelly Plug M Gen3
- Shelly Plug PM Gen3
- Shelly Dimmer 0/1-10V PM
- Shelly Dimmer
- Shelly Pro 1
- Shelly Pro 1 PM
- Shelly Pro 2
- Shelly Pro 2 PM
- Shelly Pro 3
- Shelly Pro 4 PM
- Shelly Pro Dual Cover PM
- Shelly Pro Dimmer 1PM
- Shelly Pro Dimmer 0/1-10V PM
- Shelly Pro Dimmer 2PM
- Shelly 1 Gen4
- Shelly 1PM Gen4
- Shelly 2PM Gen4
- Shelly 1 Gen4 ANZ
- Shelly 1PM Gen4 ANZ
- Shelly 2PM Gen4 ANZ
- Shelly 1 Mini Gen4
- Shelly 1 PM Mini Gen4
- Shelly Plug US Gen4
- Shelly Power Strip 4 Gen4
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 savingTLS 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
changeevents andcall()RPC access - The
unknownevent includes awillAddGenericflag:truewhen aGenericDeviceaddfollows,falsewhen 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:
- imuab - Maintained the upstream fork that this is based on
- Alexander Rydén - Original node-shellies author
Special thanks to the Shelly community and all contributors who have made this ecosystem possible.