UNPKG

@lucavb/shellies-ds9

Version:

Handles communication with the next generation of Shelly devices

1,910 lines (1,874 loc) 94.1 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; // src/index.ts var index_exports = {}; __export(index_exports, { BluetoothLowEnergy: () => BluetoothLowEnergy, Cloud: () => Cloud, Component: () => Component, ComponentBase: () => ComponentBase, ComponentWithId: () => ComponentWithId, Cover: () => Cover, Device: () => Device, DeviceDiscoverer: () => DeviceDiscoverer, DevicePower: () => DevicePower, Ethernet: () => Ethernet, HtUi: () => HtUi, HttpService: () => HttpService, Humidity: () => Humidity, Input: () => Input, JSONRPCClientWithAuthentication: () => JSONRPCClientWithAuthentication, KvsService: () => KvsService, Light: () => Light, MdnsDeviceDiscoverer: () => MdnsDeviceDiscoverer, Mqtt: () => Mqtt, MultiProfileDevice: () => MultiProfileDevice, OutboundWebSocket: () => OutboundWebSocket, Pm1: () => Pm1, RpcHandler: () => RpcHandler, ScheduleService: () => ScheduleService, Script: () => Script, Service: () => Service, Shellies: () => Shellies, ShellyDimmer: () => ShellyDimmer, ShellyGen32Pm: () => ShellyGen32Pm, ShellyPlugSG3Eu: () => ShellyPlugSG3Eu, ShellyPlus1: () => ShellyPlus1, ShellyPlus1Mini: () => ShellyPlus1Mini, ShellyPlus1MiniV3: () => ShellyPlus1MiniV3, ShellyPlus1Pm: () => ShellyPlus1Pm, ShellyPlus1PmMini: () => ShellyPlus1PmMini, ShellyPlus1PmMiniV3: () => ShellyPlus1PmMiniV3, ShellyPlus1PmUl: () => ShellyPlus1PmUl, ShellyPlus1PmV3: () => ShellyPlus1PmV3, ShellyPlus1Ul: () => ShellyPlus1Ul, ShellyPlus1V3: () => ShellyPlus1V3, ShellyPlus2Pm: () => ShellyPlus2Pm, ShellyPlus2PmRev1: () => ShellyPlus2PmRev1, ShellyPlusDimmer: () => ShellyPlusDimmer, ShellyPlusHt: () => ShellyPlusHt, ShellyPlusHtV3: () => ShellyPlusHtV3, ShellyPlusI4: () => ShellyPlusI4, ShellyPlusI4V3: () => ShellyPlusI4V3, ShellyPlusPMDimmer: () => ShellyPlusPMDimmer, ShellyPlusPlugEu: () => ShellyPlusPlugEu, ShellyPlusPlugIt: () => ShellyPlusPlugIt, ShellyPlusPlugUk: () => ShellyPlusPlugUk, ShellyPlusPlugUs: () => ShellyPlusPlugUs, ShellyPlusPmMini: () => ShellyPlusPmMini, ShellyPlusPmMiniV3: () => ShellyPlusPmMiniV3, ShellyPro1: () => ShellyPro1, ShellyPro1Pm: () => ShellyPro1Pm, ShellyPro1PmRev1: () => ShellyPro1PmRev1, ShellyPro1PmRev2: () => ShellyPro1PmRev2, ShellyPro1Rev1: () => ShellyPro1Rev1, ShellyPro1Rev2: () => ShellyPro1Rev2, ShellyPro2: () => ShellyPro2, ShellyPro2Pm: () => ShellyPro2Pm, ShellyPro2PmRev1: () => ShellyPro2PmRev1, ShellyPro2PmRev2: () => ShellyPro2PmRev2, ShellyPro2Rev1: () => ShellyPro2Rev1, ShellyPro2Rev2: () => ShellyPro2Rev2, ShellyPro3: () => ShellyPro3, ShellyPro4Pm: () => ShellyPro4Pm, ShellyPro4PmV2: () => ShellyPro4PmV2, ShellyProDimmer1Pm: () => ShellyProDimmer1Pm, ShellyProDimmer1Pm2: () => ShellyProDimmer1Pm2, ShellyProDimmer2Pm: () => ShellyProDimmer2Pm, ShellyProDualCoverPm: () => ShellyProDualCoverPm, ShellyService: () => ShellyService, Switch: () => Switch, System: () => System, Temperature: () => Temperature, Ui: () => Ui, WebSocketRpcHandler: () => WebSocketRpcHandler, WebSocketRpcHandlerFactory: () => WebSocketRpcHandlerFactory, WebhookService: () => WebhookService, WiFi: () => WiFi, characteristic: () => characteristic, component: () => component }); module.exports = __toCommonJS(index_exports); // src/components/base.ts var import_fast_deep_equal = __toESM(require("fast-deep-equal")); var import_eventemitter3 = __toESM(require("eventemitter3")); var characteristic = (target, propertyName) => { if (!Object.prototype.hasOwnProperty.call(target, "_characteristicProps")) { target._characteristicProps = new Array(); } const props = target._characteristicProps; props.push(propertyName); }; var ComponentBase = class extends import_eventemitter3.default { /** * @param name - The name of this component. Used when making RPCs. * @param device - The device that owns this component. * @param key - The key used to identify the component in status updates etc. If `null`, the component name * in lower case letters will be used. */ constructor(name, device, key = null) { super(); this.name = name; this.device = device; this._characteristics = null; this.key = key !== null ? key : name.toLowerCase(); } /** * A list of all characteristics. */ get characteristics() { if (this._characteristics === null) { const props = new Array(); let proto = Object.getPrototypeOf(this); while (proto !== null) { if (Object.prototype.hasOwnProperty.call(proto, "_characteristicProps")) { props.push(...proto._characteristicProps); } proto = Object.getPrototypeOf(proto); } this._characteristics = new Set(props); } return this._characteristics; } /** * Updates the characteristics of the component. * This method will emit `change` events for all characteristics that are * updated. * @param data - A data object that contains characteristics and their values. */ update(data) { const cs = this.characteristics; const changed = /* @__PURE__ */ new Set(); if (!cs) { return; } for (const c of cs) { if (!Object.prototype.hasOwnProperty.call(data, c)) { continue; } if (typeof data[c] === "object" && this[c]) { if ((0, import_fast_deep_equal.default)(data[c], this[c])) { continue; } Object.assign(this[c], data[c]); } else { if (data[c] === this[c]) { continue; } this[c] = data[c]; } changed.add(c); } for (const c of changed) { this.emit("change", c, this[c]); this.emit("change:" + c, this[c]); } } /** * Handles events received from the device RPC handler. * Subclasses should override this method to handle their specific events. * @param event - The event that has occurred. */ handleEvent(event) { if (event.event === "config_changed") { this.emit("configChange", event.cfg_rev, event.restart_required); } } /** * Shorthand method for making an RPC. */ rpc(method, params) { return this.device.rpcHandler.request(`${this.name}.${method}`, params); } }; var Component = class extends ComponentBase { /** * Retrieves the status of this component. */ getStatus() { return this.rpc("GetStatus"); } /** * Retrieves the configuration of this component. */ getConfig() { return this.rpc("GetConfig"); } /** * Requests changes in the configuration of this component. * @param config - The configuration options to set. */ setConfig(config) { return this.rpc("SetConfig", { config }); } }; var ComponentWithId = class extends Component { /** * @param name - The name of this component. Used when making RPCs. * @param device - The device that owns this component. * @param id - ID of this component. * @param key - The key used to identify the component in status updates etc. If `null`, the component name * in lower case letters will be used. The component ID will be appended to this key. */ constructor(name, device, id = 0, key = null) { super(name, device, (key !== null ? key : name.toLowerCase()) + ":" + id); this.id = id; } /** * Retrieves the status of this component. */ getStatus() { return this.rpc("GetStatus", { id: this.id }); } /** * Retrieves the configuration of this component. */ getConfig() { return this.rpc("GetConfig", { id: this.id }); } /** * Requests changes in the configuration of this component. * @param config - The configuration options to set. */ setConfig(config) { return this.rpc("SetConfig", { id: this.id, config }); } }; // src/components/bluetooth-low-energy.ts var BluetoothLowEnergy = class extends Component { constructor(device) { super("BLE", device); } }; // src/components/cloud.ts var Cloud = class extends Component { constructor(device) { super("Cloud", device); this.connected = false; } }; __decorateClass([ characteristic ], Cloud.prototype, "connected", 2); // src/components/cover.ts var Cover = class extends ComponentWithId { constructor(device, id = 0) { super("Cover", device, id); this.source = ""; this.state = "stopped"; this.apower = 0; this.voltage = 0; this.current = 0; this.pf = 0; this.aenergy = { total: 0, by_minute: [], minute_ts: 0 }; this.current_pos = null; this.target_pos = null; this.pos_control = false; } /** * Opens the cover. * @param duration - Move in open direction for the specified time (in seconds). */ open(duration) { return this.rpc("Open", { id: this.id, duration }); } /** * Closes the cover. * @param duration - Move in close direction for the specified time (in seconds). */ close(duration) { return this.rpc("Close", { id: this.id, duration }); } /** * Stops any ongoing movement. */ stop() { return this.rpc("Stop", { id: this.id }); } /** * Moves the cover to the given position. * One, but not both, of `pos` and `rel` must be specified. * @param pos - An absolute position (in percent). * @param rel - A relative position (in percent). */ goToPosition(pos, rel) { return this.rpc("GoToPosition", { id: this.id, pos, rel }); } /** * Starts the calibration procedure. */ calibrate() { return this.rpc("Calibrate", { id: this.id }); } }; __decorateClass([ characteristic ], Cover.prototype, "source", 2); __decorateClass([ characteristic ], Cover.prototype, "state", 2); __decorateClass([ characteristic ], Cover.prototype, "apower", 2); __decorateClass([ characteristic ], Cover.prototype, "voltage", 2); __decorateClass([ characteristic ], Cover.prototype, "current", 2); __decorateClass([ characteristic ], Cover.prototype, "pf", 2); __decorateClass([ characteristic ], Cover.prototype, "aenergy", 2); __decorateClass([ characteristic ], Cover.prototype, "current_pos", 2); __decorateClass([ characteristic ], Cover.prototype, "target_pos", 2); __decorateClass([ characteristic ], Cover.prototype, "move_timeout", 2); __decorateClass([ characteristic ], Cover.prototype, "move_started_at", 2); __decorateClass([ characteristic ], Cover.prototype, "pos_control", 2); __decorateClass([ characteristic ], Cover.prototype, "temperature", 2); __decorateClass([ characteristic ], Cover.prototype, "errors", 2); // src/components/device-power.ts var DevicePower = class extends ComponentWithId { constructor(device, id = 0) { super("DevicePower", device, id); this.battery = { V: null, percent: null }; } }; __decorateClass([ characteristic ], DevicePower.prototype, "battery", 2); __decorateClass([ characteristic ], DevicePower.prototype, "external", 2); __decorateClass([ characteristic ], DevicePower.prototype, "errors", 2); // src/components/ethernet.ts var Ethernet = class extends Component { constructor(device) { super("Eth", device); this.ip = null; } }; __decorateClass([ characteristic ], Ethernet.prototype, "ip", 2); // src/components/ht-ui.ts var HtUi = class extends Component { constructor(device) { super("HT_UI", device); } }; // src/components/humidity.ts var Humidity = class extends ComponentWithId { constructor(device, id = 0) { super("Humidity", device, id); this.rh = null; } }; __decorateClass([ characteristic ], Humidity.prototype, "rh", 2); __decorateClass([ characteristic ], Humidity.prototype, "errors", 2); // src/components/input.ts var Input = class extends ComponentWithId { constructor(device, id = 0) { super("Input", device, id); this.state = null; } handleEvent(event) { switch (event.event) { case "btn_down": this.emit("buttonDown"); break; case "btn_up": this.emit("buttonUp"); break; case "single_push": this.emit("singlePush"); break; case "double_push": this.emit("doublePush"); break; case "long_push": this.emit("longPush"); break; default: super.handleEvent(event); } } }; __decorateClass([ characteristic ], Input.prototype, "state", 2); // src/components/light.ts var Light = class extends ComponentWithId { constructor(device, id = 0) { super("Light", device, id); this.source = ""; this.output = false; this.brightness = 0; } /** * Toggles the output state. */ toggle() { return this.rpc("Toggle", { id: this.id }); } /** * Sets the output and brightness level of the light. * At least one of `on` and `brightness` must be specified. * @param on - Whether to switch on or off. * @param brightness - Brightness level. * @param toggle_after - Flip-back timer, in seconds. */ set(on, brightness, toggle_after) { return this.rpc("Set", { id: this.id, on, brightness, toggle_after }); } }; __decorateClass([ characteristic ], Light.prototype, "source", 2); __decorateClass([ characteristic ], Light.prototype, "output", 2); __decorateClass([ characteristic ], Light.prototype, "brightness", 2); __decorateClass([ characteristic ], Light.prototype, "timer_started_at", 2); __decorateClass([ characteristic ], Light.prototype, "timer_duration", 2); // src/components/mqtt.ts var Mqtt = class extends Component { constructor(device) { super("MQTT", device); this.connected = false; } }; __decorateClass([ characteristic ], Mqtt.prototype, "connected", 2); // src/components/outbound-websocket.ts var OutboundWebSocket = class extends Component { constructor(device) { super("Ws", device); this.connected = false; } }; __decorateClass([ characteristic ], OutboundWebSocket.prototype, "connected", 2); // src/components/script.ts var Script = class extends ComponentBase { constructor(device) { super("Script", device); } /** * Retrieves the status of a script. * @param id - The script ID. */ getStatus(id) { return this.rpc("GetStatus", { id }); } /** * Retrieves the configuration of a script. * @param id - The script ID. */ getConfig(id) { return this.rpc("GetConfig", { id }); } /** * Requests changes in the configuration of a script. * @param id - The script ID. * @param config - The configuration options to set. */ setConfig(id, config) { return this.rpc("SetConfig", { id, config }); } /** * Lists all scripts. */ list() { return this.rpc("List"); } /** * Creates a new script. * @param name - The name of the script. */ create(name) { return this.rpc("Create", { name }); } /** * Removes a script. * @param id - The script ID. */ delete(id) { return this.rpc("Delete", { id }); } /** * Runs a script. * @param id - The script ID. */ start(id) { return this.rpc("Start", { id }); } /** * Stops the execution of a script. * @param id - The script ID. */ stop(id) { return this.rpc("Stop", { id }); } /** * Uploads code to a script. * @param id - The script ID. * @param code - The code to upload. * @param append - Whether the code should be appended to the script or overwrite any existing code. */ putCode(id, code, append = false) { return this.rpc("PutCode", { id, code, append }); } /** * Downloads code from a script. * @param id - The script ID. * @param offset - The byte offset from the beginning. * @param len - The number of bytes to download. */ getCode(id, offset = 0, len) { return this.rpc("GetCode", { id, offset, len }); } /** * Evaluates or executes code inside of a script. * @param id - The script ID. * @param code - The code to evaluate. */ eval(id, code) { return this.rpc("Eval", { id, code }); } }; // src/components/switch.ts var Switch = class extends ComponentWithId { constructor(device, id = 0) { super("Switch", device, id); this.source = ""; this.output = false; this.temperature = { tC: null, tF: null }; } /** * Toggles the switch. */ toggle() { return this.rpc("Toggle", { id: this.id }); } /** * Sets the output of the switch. * @param on - Whether to switch on or off. * @param toggle_after - Flip-back timer, in seconds. */ set(on, toggle_after) { return this.rpc("Set", { id: this.id, on, toggle_after }); } }; __decorateClass([ characteristic ], Switch.prototype, "source", 2); __decorateClass([ characteristic ], Switch.prototype, "output", 2); __decorateClass([ characteristic ], Switch.prototype, "timer_started_at", 2); __decorateClass([ characteristic ], Switch.prototype, "timer_duration", 2); __decorateClass([ characteristic ], Switch.prototype, "apower", 2); __decorateClass([ characteristic ], Switch.prototype, "voltage", 2); __decorateClass([ characteristic ], Switch.prototype, "current", 2); __decorateClass([ characteristic ], Switch.prototype, "pf", 2); __decorateClass([ characteristic ], Switch.prototype, "aenergy", 2); __decorateClass([ characteristic ], Switch.prototype, "temperature", 2); __decorateClass([ characteristic ], Switch.prototype, "errors", 2); // src/components/system.ts var System = class extends Component { constructor(device) { super("Sys", device); this.mac = ""; this.restart_required = false; this.time = ""; this.unixtime = 0; this.uptime = 0; this.ram_size = 0; this.ram_free = 0; this.fs_size = 0; this.fs_free = 0; this.cfg_rev = 0; this.kvs_rev = 0; this.available_updates = {}; } handleEvent(event) { switch (event.event) { case "ota_begin": this.emit("otaBegin", event.msg); break; case "ota_progress": this.emit("otaProgress", event.progress_percent, event.msg); break; case "ota_success": this.emit("otaSuccess", event.msg); break; case "ota_error": this.emit("otaError", event.msg); break; case "sleep": this.emit("sleep"); break; default: super.handleEvent(event); } } }; __decorateClass([ characteristic ], System.prototype, "mac", 2); __decorateClass([ characteristic ], System.prototype, "restart_required", 2); __decorateClass([ characteristic ], System.prototype, "time", 2); __decorateClass([ characteristic ], System.prototype, "unixtime", 2); __decorateClass([ characteristic ], System.prototype, "uptime", 2); __decorateClass([ characteristic ], System.prototype, "ram_size", 2); __decorateClass([ characteristic ], System.prototype, "ram_free", 2); __decorateClass([ characteristic ], System.prototype, "fs_size", 2); __decorateClass([ characteristic ], System.prototype, "fs_free", 2); __decorateClass([ characteristic ], System.prototype, "cfg_rev", 2); __decorateClass([ characteristic ], System.prototype, "kvs_rev", 2); __decorateClass([ characteristic ], System.prototype, "schedule_rev", 2); __decorateClass([ characteristic ], System.prototype, "webhook_rev", 2); __decorateClass([ characteristic ], System.prototype, "available_updates", 2); __decorateClass([ characteristic ], System.prototype, "wakeup_reason", 2); // src/components/temperature.ts var Temperature = class extends ComponentWithId { constructor(device, id = 0) { super("Temperature", device, id); this.tC = null; this.tF = null; } }; __decorateClass([ characteristic ], Temperature.prototype, "tC", 2); __decorateClass([ characteristic ], Temperature.prototype, "tF", 2); __decorateClass([ characteristic ], Temperature.prototype, "errors", 2); // src/components/ui.ts var Ui = class extends Component { constructor(device) { super("UI", device); } }; // src/components/wifi.ts var WiFi = class extends Component { constructor(device) { super("WiFi", device); this.sta_ip = null; this.status = "disconnected"; this.ssid = null; this.rssi = 0; } /** * Retrieves a list of available networks. */ scan() { return this.rpc("Scan"); } /** * Returns a list of clients currently connected to the device's access point. */ listApClients() { return this.rpc("ListAPClients"); } handleEvent(event) { switch (event.event) { case "sta_connect_fail": this.emit("connectionError", event.reason); break; case "sta_disconnected": this.emit("disconnect", event.reason, event.ssid, event.sta_ip); break; default: super.handleEvent(event); } } }; __decorateClass([ characteristic ], WiFi.prototype, "sta_ip", 2); __decorateClass([ characteristic ], WiFi.prototype, "status", 2); __decorateClass([ characteristic ], WiFi.prototype, "ssid", 2); __decorateClass([ characteristic ], WiFi.prototype, "rssi", 2); __decorateClass([ characteristic ], WiFi.prototype, "ap_client_count", 2); // src/components/pm1.ts var Pm1 = class extends ComponentWithId { constructor(device, id = 0) { super("Pm1", device, id); this.output = false; } }; __decorateClass([ characteristic ], Pm1.prototype, "output", 2); __decorateClass([ characteristic ], Pm1.prototype, "voltage", 2); __decorateClass([ characteristic ], Pm1.prototype, "current", 2); __decorateClass([ characteristic ], Pm1.prototype, "apower", 2); __decorateClass([ characteristic ], Pm1.prototype, "aprtpower", 2); __decorateClass([ characteristic ], Pm1.prototype, "pf", 2); __decorateClass([ characteristic ], Pm1.prototype, "freq", 2); __decorateClass([ characteristic ], Pm1.prototype, "aenergy", 2); __decorateClass([ characteristic ], Pm1.prototype, "ret_aenergy", 2); __decorateClass([ characteristic ], Pm1.prototype, "errors", 2); // src/devices/base.ts var import_eventemitter32 = __toESM(require("eventemitter3")); // src/services/base.ts var Service = class { /** * @param name - The name of this service. * @param device - The device that owns this service. */ constructor(name, device) { this.name = name; this.device = device; } /** * Shorthand method for making an RPC. */ rpc(method, params) { return this.device.rpcHandler.request(`${this.name}.${method}`, params); } }; // src/services/http.ts var HttpService = class extends Service { constructor(device) { super("HTTP", device); } /** * Sends an HTTP GET request. * @param url - The URL to send the request to. * @param timeout - Timeout, in seconds. * @param ssl_ca - The certificate authority to use for HTTPS requests. */ get(url, timeout, ssl_ca) { return this.rpc("GET", { url, timeout, ssl_ca }); } /** * Sends an HTTP POST request. * Either `body` or `body_b64` must be specified. * @param url - The URL to send the request to. * @param body - The request body. * @param body_b64 - The request body (base64 encoded). * @param content_type - The type of content being sent. * @param timeout - Timeout, in seconds. * @param ssl_ca - The certificate authority to use for HTTPS requests. */ post(url, body, body_b64, content_type = "application/json", timeout, ssl_ca) { return this.rpc("POST", { url, body, body_b64, content_type, timeout, ssl_ca }); } /** * Sends an HTTP request. * Either `body` or `body_b64` must be specified for POST and PUT requests. * @param method - The HTTP method to use. * @param url - The URL to send the request to. * @param body - The request body. * @param body_b64 - The request body (base64 encoded). * @param headers - User supplied request headers. * @param timeout - Timeout, in seconds. * @param ssl_ca - The certificate authority to use for HTTPS requests. */ request(method, url, body, body_b64, headers, timeout, ssl_ca) { return this.rpc("Request", { method, url, body, body_b64, headers, timeout, ssl_ca }); } }; // src/services/kvs.ts var KvsService = class extends Service { constructor(device) { super("KVS", device); } /** * Adds a new key-value pair to the store or updates an existing one. * @param key - The key to add or update. * @param value - Value for the key. * @param etag - Generated hash uniquely identifying the key-value pair. */ set(key, value, etag) { return this.rpc("Set", { key, value, etag }); } /** * Returns the value stored and etag for a given key. * @param key - The key to lookup. */ get(key) { return this.rpc("Get", { key }); } /** * Returns the full information stored for items in the store based on an optional key matching parameter. * @param match - Pattern against which keys are matched. */ getMany(match) { return this.rpc("GetMany", { match }); } /** * Returns a list of existing keys and etags based on an optional match parameter. * @param match - Pattern against which keys are matched. */ list(match) { return this.rpc("List", { match }); } /** * Deletes an existing key from the store. * @param key - The key to delete. * @param etag - Generated hash uniquely identifying the key-value pair. */ delete(key, etag) { return this.rpc("Delete", { key, etag }); } }; // src/services/schedule.ts var ScheduleService = class extends Service { constructor(device) { super("Schedule", device); } /** * Lists all existing scheduled jobs. */ list() { return this.rpc("List"); } /** * Creates a new scheduled job. * @param job - The job to add. */ create(job) { return this.rpc("Create", { ...job }); } /** * Updates an existing scheduled job. * @param job - The job to update. */ update(job) { return this.rpc("Update", { ...job }); } /** * Deletes a scheduled job. * @param id - ID of the job to delete. */ delete(id) { return this.rpc("Delete", { id }); } /** * Deletes all existing scheduled jobs. */ deleteAll() { return this.rpc("DeleteAll"); } }; // src/services/shelly.ts var import_crypto = __toESM(require("crypto")); var ShellyService = class extends Service { constructor(device) { super("Shelly", device); } /** * Retrieves the status of all of the components of the device. */ getStatus() { return this.rpc("GetStatus"); } /** * Retrieves the configuration of all the components of the device. */ getConfig() { return this.rpc("GetConfig"); } /** * Lists all available RPC methods. */ listMethods() { return this.rpc("ListMethods"); } /** * Retrieves information about the device. */ getDeviceInfo(ident) { return this.rpc("GetDeviceInfo", { ident }); } /** * Retrieves a list of all available profiles and the type/count of functional components exposed for each profile. */ listProfiles() { return this.rpc("ListProfiles"); } /** * Sets the device profile. * @param name - Name of the device profile. */ setProfile(name) { return this.rpc("SetProfile", { name }); } /** * Retrieves a list of all timezones. */ listTimezones() { return this.rpc("ListTimezones"); } /** * Retrieves the location of the device. */ detectLocation() { return this.rpc("DetectLocation"); } /** * Checks for a new firmware version. */ checkForUpdate() { return this.rpc("CheckForUpdate"); } /** * Requests an update of the device firmware. * Either `stage` or `url` must be specified. * @param stage - The type of the new version. * @param url - URL of the update. */ update(stage, url) { return this.rpc("Update", { stage, url }); } /** * Requests a factory reset of the device. */ factoryReset() { return this.rpc("FactoryReset"); } /** * Requests that the device's WiFi configuration is reset. */ resetWiFiConfig() { return this.rpc("ResetWiFiConfig"); } /** * Requests a reboot of the device. * @param delay_ms - A delay until the device reboots, in milliseconds. The * minimum delay is 500 ms. */ reboot(delay_ms) { return this.rpc("Reboot", { delay_ms }); } /** * Sets the authentication details (password) for the device. * @param password - The new password. Set to `null` to disable. */ setAuth(password) { const user = "admin"; const hash = password === null ? null : import_crypto.default.createHash("sha256").update(`${user}:${this.device.id}:${password}`).digest("hex"); return this.rpc("SetAuth", { user, realm: this.device.id, ha1: hash }); } /** * Uploads a custom certificate authority (CA) PEM bundle. * @param data - Contents of the PEM file (`null` to remove the existing file). * @param append - Whether more data will be appended in a subsequent call. */ putUserCa(data, append) { return this.rpc("PutUserCA", { data, append }); } }; // src/services/webhook.ts var WebhookService = class extends Service { constructor(device) { super("Webhook", device); } /** * Lists all supported event types. */ listSupported() { return this.rpc("ListSupported"); } /** * Lists all existing webhooks. */ list() { return this.rpc("List"); } /** * Creates a new webhook. * @param hook - The webhook to add. */ create(hook) { return this.rpc("Create", { ...hook }); } /** * Updates an existing webhook. * @param hook - The webhook to update. */ update(hook) { return this.rpc("Update", { ...hook }); } /** * Deletes a webhook. * @param id - ID of the webhook to delete. */ delete(id) { return this.rpc("Delete", { id }); } /** * Deletes all existing webhooks. */ deleteAll() { return this.rpc("DeleteAll"); } }; // src/devices/base.ts var component = (target, propertyName) => { if (!Object.prototype.hasOwnProperty.call(target, "_componentProps")) { target._componentProps = new Array(); } const props = target._componentProps; props.push(propertyName); }; var _Device = class _Device extends import_eventemitter32.default { /** * @param info - Information about this device. * @param rpcHandler - Used to make remote procedure calls. */ constructor(info, rpcHandler) { super(); this.rpcHandler = rpcHandler; /** * This device's Shelly service. */ this.shelly = new ShellyService(this); /** * This device's Schedule service. */ this.schedule = new ScheduleService(this); /** * This device's Webhook service. */ this.webhook = new WebhookService(this); /** * This device's HTTP service. */ this.http = new HttpService(this); /** * This device's KVS service. */ this.kvs = new KvsService(this); this.system = new System(this); this._components = null; this.id = info.id; this.macAddress = info.mac; this.firmware = { id: info.fw_id, version: info.ver }; this._model = info.model; rpcHandler.on("statusUpdate", this.statusUpdateHandler, this); rpcHandler.on("event", this.eventHandler, this); } /** * Registers a device class, so that it can later be found based on its device model * using the `Device.getClass()` method. * @param cls - A subclass of `Device`. */ static registerClass(cls) { const model = cls.model.toUpperCase(); if (_Device.subclasses.has(model)) { throw new Error(`A device class for ${model} has already been registered`); } _Device.subclasses.set(model, cls); } /** * Returns the device class for the given device model, if one has been registered. * @param model - The model designation to lookup. */ static getClass(model) { return _Device.subclasses.get(model.toUpperCase()); } /** * The model designation of this device. */ get model() { return this._model || this.constructor.model; } /** * A human-friendly name of the device model. */ get modelName() { return this.constructor.modelName; } /** * Maps component keys to property names. */ get components() { if (this._components === null) { this._components = /* @__PURE__ */ new Map(); const props = new Array(); let proto = Object.getPrototypeOf(this); while (proto !== null) { if (Object.prototype.hasOwnProperty.call(proto, "_componentProps")) { props.push(...proto._componentProps); } proto = Object.getPrototypeOf(proto); } for (const p of props) { const cmpnt = this[p]; if (!cmpnt) { continue; } this._components.set(cmpnt.key, p); } } return this._components; } /** * Determines whether this device has a component with a given key. * @param key - The component key. */ hasComponent(key) { return this.components.has(key); } /** * Returns the component with the given key. * @param key - The component key. */ getComponent(key) { const prop = this.components.get(key); if (prop) { return this[prop]; } return void 0; } /** * Returns a new Iterator object that contains each of the device's * components. */ *[Symbol.iterator]() { for (const [key, prop] of this.components.entries()) { yield [key, this[prop]]; } } /** * Loads the status for all of the device's components and populates their characteristics. */ async loadStatus() { const status = await this.shelly.getStatus(); for (const cmpnt in status) { if (Object.prototype.hasOwnProperty.call(status, cmpnt) && typeof status[cmpnt] === "object") { this.getComponent(cmpnt)?.update(status[cmpnt]); } } } /** * Loads the condiguration for all of the device's components and populates their `config` properties. */ async loadConfig() { const config = await this.shelly.getConfig(); for (const cmpnt in config) { if (Object.prototype.hasOwnProperty.call(config, cmpnt) && typeof config[cmpnt] === "object") { const c = this.getComponent(cmpnt); if (c && c instanceof Component) { c.config = config[cmpnt]; } } } } /** * Handles 'statusUpdate' events from our RPC handler. */ statusUpdateHandler(update) { for (const cmpnt in update) { if (cmpnt !== "ts" && Object.prototype.hasOwnProperty.call(update, cmpnt) && typeof update[cmpnt] === "object") { this.getComponent(cmpnt)?.update(update[cmpnt]); } } } /** * Handles 'event' events from our RPC handler. */ eventHandler(events) { for (const event of events.events) { this.getComponent(event.component)?.handleEvent(event); } } }; /** * Holds all registered subclasses. */ _Device.subclasses = /* @__PURE__ */ new Map(); __decorateClass([ component ], _Device.prototype, "system", 2); var Device = _Device; var MultiProfileDevice = class extends Device { /** * @param info - Information about this device. * @param rpcHandler - Used to make remote procedure calls. */ constructor(info, rpcHandler) { super(info, rpcHandler); this.rpcHandler = rpcHandler; this.profile = info.profile ?? ""; } }; // src/devices/shelly-plus-1.ts var ShellyPlus1 = class extends Device { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.input0 = new Input(this, 0); this.switch0 = new Switch(this, 0); this.script = new Script(this); } }; ShellyPlus1.model = "SNSW-001X16EU"; ShellyPlus1.modelName = "Shelly Plus 1"; __decorateClass([ component ], ShellyPlus1.prototype, "wifi", 2); __decorateClass([ component ], ShellyPlus1.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPlus1.prototype, "cloud", 2); __decorateClass([ component ], ShellyPlus1.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPlus1.prototype, "outboundWebSocket", 2); __decorateClass([ component ], ShellyPlus1.prototype, "input0", 2); __decorateClass([ component ], ShellyPlus1.prototype, "switch0", 2); __decorateClass([ component ], ShellyPlus1.prototype, "script", 2); Device.registerClass(ShellyPlus1); var ShellyPlus1Ul = class extends ShellyPlus1 { }; ShellyPlus1Ul.model = "SNSW-001X15UL"; Device.registerClass(ShellyPlus1Ul); var ShellyPlus1Mini = class extends ShellyPlus1 { }; ShellyPlus1Mini.model = "SNSW-001X8EU"; ShellyPlus1Mini.modelName = "Shelly Plus 1 Mini"; Device.registerClass(ShellyPlus1Mini); var ShellyPlus1MiniV3 = class extends ShellyPlus1 { }; ShellyPlus1MiniV3.model = "S3SW-001X8EU"; ShellyPlus1MiniV3.modelName = "Shelly Plus 1 Mini"; Device.registerClass(ShellyPlus1MiniV3); var ShellyPlus1V3 = class extends ShellyPlus1 { }; ShellyPlus1V3.model = "S3SW-001X16EU"; Device.registerClass(ShellyPlus1V3); // src/devices/shelly-plus-pm.ts var ShellyPlusPmMini = class extends Device { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.script = new Script(this); this.pm1 = new Pm1(this); } }; ShellyPlusPmMini.model = "SNPM-001PCEU16"; ShellyPlusPmMini.modelName = "Shelly Plus PM Mini"; __decorateClass([ component ], ShellyPlusPmMini.prototype, "wifi", 2); __decorateClass([ component ], ShellyPlusPmMini.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPlusPmMini.prototype, "cloud", 2); __decorateClass([ component ], ShellyPlusPmMini.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPlusPmMini.prototype, "outboundWebSocket", 2); __decorateClass([ component ], ShellyPlusPmMini.prototype, "script", 2); __decorateClass([ component ], ShellyPlusPmMini.prototype, "pm1", 2); Device.registerClass(ShellyPlusPmMini); var ShellyPlusPmMiniV3 = class extends ShellyPlusPmMini { }; ShellyPlusPmMiniV3.model = "S3PM-001PCEU16"; Device.registerClass(ShellyPlusPmMiniV3); // src/devices/shelly-plus-1-pm.ts var ShellyPlus1Pm = class extends Device { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.input0 = new Input(this, 0); this.switch0 = new Switch(this, 0); this.script = new Script(this); } }; ShellyPlus1Pm.model = "SNSW-001P16EU"; ShellyPlus1Pm.modelName = "Shelly Plus 1 PM"; __decorateClass([ component ], ShellyPlus1Pm.prototype, "wifi", 2); __decorateClass([ component ], ShellyPlus1Pm.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPlus1Pm.prototype, "cloud", 2); __decorateClass([ component ], ShellyPlus1Pm.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPlus1Pm.prototype, "outboundWebSocket", 2); __decorateClass([ component ], ShellyPlus1Pm.prototype, "input0", 2); __decorateClass([ component ], ShellyPlus1Pm.prototype, "switch0", 2); __decorateClass([ component ], ShellyPlus1Pm.prototype, "script", 2); Device.registerClass(ShellyPlus1Pm); var ShellyPlus1PmUl = class extends ShellyPlus1Pm { }; ShellyPlus1PmUl.model = "SNSW-001P15UL"; Device.registerClass(ShellyPlus1PmUl); var ShellyPlus1PmMini = class extends ShellyPlus1Pm { }; ShellyPlus1PmMini.model = "SNSW-001P8EU"; ShellyPlus1PmMini.modelName = "Shelly Plus 1 PM Mini"; Device.registerClass(ShellyPlus1PmMini); var ShellyPlus1PmMiniV3 = class extends ShellyPlus1Pm { }; ShellyPlus1PmMiniV3.model = "S3SW-001P8EU"; ShellyPlus1PmMiniV3.modelName = "Shelly Plus 1 PM Mini"; Device.registerClass(ShellyPlus1PmMiniV3); var ShellyPlus1PmV3 = class extends ShellyPlus1Pm { }; ShellyPlus1PmV3.model = "S3SW-001P16EU"; Device.registerClass(ShellyPlus1PmV3); // src/devices/shelly-plus-2-pm.ts var ShellyPlus2Pm = class extends MultiProfileDevice { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.cover0 = new Cover(this, 0); this.input0 = new Input(this, 0); this.input1 = new Input(this, 1); this.switch0 = new Switch(this, 0); this.switch1 = new Switch(this, 1); this.script = new Script(this); } }; ShellyPlus2Pm.model = "SNSW-002P16EU"; ShellyPlus2Pm.modelName = "Shelly Plus 2 PM"; __decorateClass([ component ], ShellyPlus2Pm.prototype, "wifi", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "cloud", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "outboundWebSocket", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "cover0", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "input0", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "input1", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "switch0", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "switch1", 2); __decorateClass([ component ], ShellyPlus2Pm.prototype, "script", 2); Device.registerClass(ShellyPlus2Pm); var ShellyPlus2PmRev1 = class extends ShellyPlus2Pm { }; ShellyPlus2PmRev1.model = "SNSW-102P16EU"; Device.registerClass(ShellyPlus2PmRev1); // src/devices/shelly-plus-ht.ts var ShellyPlusHt = class extends Device { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.temperature0 = new Temperature(this, 0); this.humidity0 = new Humidity(this, 0); this.devicePower0 = new DevicePower(this, 0); this.htUi = new HtUi(this); } }; ShellyPlusHt.model = "SNSN-0013A"; ShellyPlusHt.modelName = "Shelly Plus H&T"; __decorateClass([ component ], ShellyPlusHt.prototype, "wifi", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "cloud", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "outboundWebSocket", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "temperature0", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "humidity0", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "devicePower0", 2); __decorateClass([ component ], ShellyPlusHt.prototype, "htUi", 2); Device.registerClass(ShellyPlusHt); var ShellyPlusHtV3 = class extends ShellyPlusHt { }; ShellyPlusHtV3.model = "S3SN-0U12A"; Device.registerClass(ShellyPlusHtV3); // src/devices/shelly-plus-i4.ts var ShellyPlusI4 = class extends Device { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.input0 = new Input(this, 0); this.input1 = new Input(this, 1); this.input2 = new Input(this, 2); this.input3 = new Input(this, 3); this.script = new Script(this); } }; ShellyPlusI4.model = "SNSN-0024X"; ShellyPlusI4.modelName = "Shelly Plus I4"; __decorateClass([ component ], ShellyPlusI4.prototype, "wifi", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "cloud", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "outboundWebSocket", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "input0", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "input1", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "input2", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "input3", 2); __decorateClass([ component ], ShellyPlusI4.prototype, "script", 2); Device.registerClass(ShellyPlusI4); var ShellyPlusI4V3 = class extends ShellyPlusI4 { }; ShellyPlusI4V3.model = "S3SN-0024X"; Device.registerClass(ShellyPlusI4V3); // src/devices/shelly-plus-plug.ts var ShellyPlusPlugUs = class extends Device { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.switch0 = new Switch(this, 0); this.script = new Script(this); } }; ShellyPlusPlugUs.model = "SNPL-00116US"; ShellyPlusPlugUs.modelName = "Shelly Plus Plug US"; __decorateClass([ component ], ShellyPlusPlugUs.prototype, "wifi", 2); __decorateClass([ component ], ShellyPlusPlugUs.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPlusPlugUs.prototype, "cloud", 2); __decorateClass([ component ], ShellyPlusPlugUs.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPlusPlugUs.prototype, "outboundWebSocket", 2); __decorateClass([ component ], ShellyPlusPlugUs.prototype, "switch0", 2); __decorateClass([ component ], ShellyPlusPlugUs.prototype, "script", 2); Device.registerClass(ShellyPlusPlugUs); var ShellyPlusPlugEu = class extends ShellyPlusPlugUs { }; ShellyPlusPlugEu.model = "SNPL-00112EU"; ShellyPlusPlugEu.modelName = "Shelly Plus Plug EU"; Device.registerClass(ShellyPlusPlugEu); var ShellyPlusPlugUk = class extends ShellyPlusPlugUs { }; ShellyPlusPlugUk.model = "SNPL-00112UK"; ShellyPlusPlugUk.modelName = "Shelly Plus Plug UK"; Device.registerClass(ShellyPlusPlugUk); var ShellyPlusPlugIt = class extends ShellyPlusPlugUs { }; ShellyPlusPlugIt.model = "SNPL-00110IT"; ShellyPlusPlugIt.modelName = "Shelly Plus Plug IT"; Device.registerClass(ShellyPlusPlugIt); var ShellyPlugSG3Eu = class extends ShellyPlusPlugUs { }; ShellyPlugSG3Eu.model = "S3PL-00112EU"; ShellyPlugSG3Eu.modelName = "Shelly Plug S Gen3"; Device.registerClass(ShellyPlugSG3Eu); // src/devices/shelly-pro-1.ts var ShellyPro1 = class extends Device { constructor() { super(...arguments); this.wifi = new WiFi(this); this.bluetoothLowEnergy = new BluetoothLowEnergy(this); this.cloud = new Cloud(this); this.mqtt = new Mqtt(this); this.outboundWebSocket = new OutboundWebSocket(this); this.input0 = new Input(this, 0); this.input1 = new Input(this, 1); this.switch0 = new Switch(this, 0); this.script = new Script(this); } }; ShellyPro1.model = "SPSW-001XE16EU"; ShellyPro1.modelName = "Shelly Pro 1"; __decorateClass([ component ], ShellyPro1.prototype, "wifi", 2); __decorateClass([ component ], ShellyPro1.prototype, "bluetoothLowEnergy", 2); __decorateClass([ component ], ShellyPro1.prototype, "cloud", 2); __decorateClass([ component ], ShellyPro1.prototype, "mqtt", 2); __decorateClass([ component ], ShellyPro1.prototype, "outboundWebSocket", 2); __de