UNPKG

wled-client

Version:

A friendly JS interface for controlling your [WLED](https://github.com/Aircoookie/WLED) devices from Node.js or the browser.

1,289 lines (1,282 loc) 44.7 kB
// src/constants.ts var DEFAULT_OPTIONS = { immediate: true, websocket: { reconnect: true }, secure: false, host: "" }; var DEFAULT_CLIENT_CONTEXT = { state: { nightlight: {}, udpSync: {}, segments: [] }, info: { leds: {}, wifi: {}, fs: {} }, effects: [], palettes: [], presets: {}, deviceOptions: {}, lightCapabilities: {}, live: { leds: false }, config: { id: {}, network: {}, accessPoint: {}, wifi: {}, hardware: {}, light: {}, defaults: {}, interfaces: {}, overlay: {}, timers: {}, ota: {}, usermods: {} } }; var WLEDNightlightMode = /* @__PURE__ */ ((WLEDNightlightMode3) => { WLEDNightlightMode3[WLEDNightlightMode3["INSTANT"] = 0] = "INSTANT"; WLEDNightlightMode3[WLEDNightlightMode3["FADE"] = 1] = "FADE"; WLEDNightlightMode3[WLEDNightlightMode3["COLOR_FADE"] = 2] = "COLOR_FADE"; WLEDNightlightMode3[WLEDNightlightMode3["SUNRISE"] = 3] = "SUNRISE"; return WLEDNightlightMode3; })(WLEDNightlightMode || {}); var WLEDLiveDataOverride = /* @__PURE__ */ ((WLEDLiveDataOverride2) => { WLEDLiveDataOverride2[WLEDLiveDataOverride2["OFF"] = 0] = "OFF"; WLEDLiveDataOverride2[WLEDLiveDataOverride2["UNTIL_END"] = 1] = "UNTIL_END"; WLEDLiveDataOverride2[WLEDLiveDataOverride2["UNTIL_REBOOT"] = 2] = "UNTIL_REBOOT"; return WLEDLiveDataOverride2; })(WLEDLiveDataOverride || {}); var WLEDDeviceOptionMasks = { DEBUG: 128, DISABLE_ALEXA: 64, DISABLE_BLYNK: 32, USERMOD_CRONIXIE: 16, DISABLE_FILESYSTEM: 8, DISABLE_HUESYNC: 4, ENABLE_ADALIGHT: 2, DISABLE_OTA: 1 }; var WLEDLightCapabilityMasks = { CCT: 4, WHITE: 2, RGB: 1 }; var WLEDAPOpenBehavior = /* @__PURE__ */ ((WLEDAPOpenBehavior2) => { WLEDAPOpenBehavior2[WLEDAPOpenBehavior2["NO_CONNECTION_AFTER_BOOT"] = 0] = "NO_CONNECTION_AFTER_BOOT"; WLEDAPOpenBehavior2[WLEDAPOpenBehavior2["DISCONNECTED"] = 1] = "DISCONNECTED"; WLEDAPOpenBehavior2[WLEDAPOpenBehavior2["AWLAYS"] = 2] = "AWLAYS"; WLEDAPOpenBehavior2[WLEDAPOpenBehavior2["NEVER"] = 3] = "NEVER"; return WLEDAPOpenBehavior2; })(WLEDAPOpenBehavior || {}); var WLEDAutoWhiteMode = /* @__PURE__ */ ((WLEDAutoWhiteMode2) => { WLEDAutoWhiteMode2[WLEDAutoWhiteMode2["NONE"] = 0] = "NONE"; WLEDAutoWhiteMode2[WLEDAutoWhiteMode2["BRIGHTER"] = 1] = "BRIGHTER"; WLEDAutoWhiteMode2[WLEDAutoWhiteMode2["ACCURATE"] = 2] = "ACCURATE"; WLEDAutoWhiteMode2[WLEDAutoWhiteMode2["DUAL"] = 3] = "DUAL"; return WLEDAutoWhiteMode2; })(WLEDAutoWhiteMode || {}); var WLEDBusColorOrder = /* @__PURE__ */ ((WLEDBusColorOrder2) => { WLEDBusColorOrder2[WLEDBusColorOrder2["GRB"] = 0] = "GRB"; WLEDBusColorOrder2[WLEDBusColorOrder2["RGB"] = 1] = "RGB"; WLEDBusColorOrder2[WLEDBusColorOrder2["BRG"] = 2] = "BRG"; WLEDBusColorOrder2[WLEDBusColorOrder2["RBG"] = 3] = "RBG"; WLEDBusColorOrder2[WLEDBusColorOrder2["BGR"] = 4] = "BGR"; WLEDBusColorOrder2[WLEDBusColorOrder2["GBR"] = 5] = "GBR"; return WLEDBusColorOrder2; })(WLEDBusColorOrder || {}); var WLEDBusType = /* @__PURE__ */ ((WLEDBusType2) => { WLEDBusType2[WLEDBusType2["WS281x"] = 22] = "WS281x"; WLEDBusType2[WLEDBusType2["SK6812_RGBW"] = 30] = "SK6812_RGBW"; WLEDBusType2[WLEDBusType2["TM1814"] = 31] = "TM1814"; WLEDBusType2[WLEDBusType2["KHZ400"] = 24] = "KHZ400"; WLEDBusType2[WLEDBusType2["WS2801"] = 50] = "WS2801"; WLEDBusType2[WLEDBusType2["APA102"] = 51] = "APA102"; WLEDBusType2[WLEDBusType2["LPD8806"] = 52] = "LPD8806"; WLEDBusType2[WLEDBusType2["P9813"] = 53] = "P9813"; WLEDBusType2[WLEDBusType2["PWM_White"] = 41] = "PWM_White"; WLEDBusType2[WLEDBusType2["PWM_CCT"] = 42] = "PWM_CCT"; WLEDBusType2[WLEDBusType2["PWM_RGB"] = 43] = "PWM_RGB"; WLEDBusType2[WLEDBusType2["PWM_RGBW"] = 44] = "PWM_RGBW"; WLEDBusType2[WLEDBusType2["PWM_RGB_CCT"] = 45] = "PWM_RGB_CCT"; WLEDBusType2[WLEDBusType2["DDP_RGB_NETWORK"] = 80] = "DDP_RGB_NETWORK"; return WLEDBusType2; })(WLEDBusType || {}); var WLEDButtonType = /* @__PURE__ */ ((WLEDButtonType2) => { WLEDButtonType2[WLEDButtonType2["DISABLED"] = 0] = "DISABLED"; WLEDButtonType2[WLEDButtonType2["PUSHBUTTON"] = 2] = "PUSHBUTTON"; WLEDButtonType2[WLEDButtonType2["PUSHBUTTON_INVERTED"] = 3] = "PUSHBUTTON_INVERTED"; WLEDButtonType2[WLEDButtonType2["SWITCH"] = 4] = "SWITCH"; WLEDButtonType2[WLEDButtonType2["PIR_SENSOR"] = 5] = "PIR_SENSOR"; WLEDButtonType2[WLEDButtonType2["TOUCH"] = 6] = "TOUCH"; WLEDButtonType2[WLEDButtonType2["ANALOG"] = 7] = "ANALOG"; WLEDButtonType2[WLEDButtonType2["ANALOG_INVERTED"] = 8] = "ANALOG_INVERTED"; return WLEDButtonType2; })(WLEDButtonType || {}); var WLEDIRRemoteType = /* @__PURE__ */ ((WLEDIRRemoteType2) => { WLEDIRRemoteType2[WLEDIRRemoteType2["REMOTE_DISABLED"] = 0] = "REMOTE_DISABLED"; WLEDIRRemoteType2[WLEDIRRemoteType2["KEY_24_RGB"] = 1] = "KEY_24_RGB"; WLEDIRRemoteType2[WLEDIRRemoteType2["KEY_24_WITH_CT"] = 2] = "KEY_24_WITH_CT"; WLEDIRRemoteType2[WLEDIRRemoteType2["KEY_40_BLUE"] = 3] = "KEY_40_BLUE"; WLEDIRRemoteType2[WLEDIRRemoteType2["KEY_44_RGB"] = 4] = "KEY_44_RGB"; WLEDIRRemoteType2[WLEDIRRemoteType2["KEY_21_RGB"] = 5] = "KEY_21_RGB"; WLEDIRRemoteType2[WLEDIRRemoteType2["KEY_6_BLACK"] = 6] = "KEY_6_BLACK"; WLEDIRRemoteType2[WLEDIRRemoteType2["KEY_9_RED"] = 7] = "KEY_9_RED"; WLEDIRRemoteType2[WLEDIRRemoteType2["JSON_REMOTE"] = 8] = "JSON_REMOTE"; return WLEDIRRemoteType2; })(WLEDIRRemoteType || {}); var WLEDPaletteBlendingMode = /* @__PURE__ */ ((WLEDPaletteBlendingMode2) => { WLEDPaletteBlendingMode2[WLEDPaletteBlendingMode2["LINEAR_WRAP_IF_MOVING"] = 0] = "LINEAR_WRAP_IF_MOVING"; WLEDPaletteBlendingMode2[WLEDPaletteBlendingMode2["LINEAR_ALWAYS_WRAP"] = 1] = "LINEAR_ALWAYS_WRAP"; WLEDPaletteBlendingMode2[WLEDPaletteBlendingMode2["LINEAR_NEVER_WRAP"] = 2] = "LINEAR_NEVER_WRAP"; WLEDPaletteBlendingMode2[WLEDPaletteBlendingMode2["NONE"] = 3] = "NONE"; return WLEDPaletteBlendingMode2; })(WLEDPaletteBlendingMode || {}); var WLEDClockOverlay = /* @__PURE__ */ ((WLEDClockOverlay2) => { WLEDClockOverlay2[WLEDClockOverlay2["NONE"] = 0] = "NONE"; WLEDClockOverlay2[WLEDClockOverlay2["ANALOG"] = 1] = "ANALOG"; WLEDClockOverlay2[WLEDClockOverlay2["DIGITAL"] = 2] = "DIGITAL"; return WLEDClockOverlay2; })(WLEDClockOverlay || {}); var WLEDDMXMode = /* @__PURE__ */ ((WLEDDMXMode2) => { WLEDDMXMode2[WLEDDMXMode2["DISABLED"] = 0] = "DISABLED"; WLEDDMXMode2[WLEDDMXMode2["SINGLE_RGB"] = 1] = "SINGLE_RGB"; WLEDDMXMode2[WLEDDMXMode2["SINGLE_DRGB"] = 2] = "SINGLE_DRGB"; WLEDDMXMode2[WLEDDMXMode2["EFFECT"] = 3] = "EFFECT"; WLEDDMXMode2[WLEDDMXMode2["MULTI_RGB"] = 4] = "MULTI_RGB"; WLEDDMXMode2[WLEDDMXMode2["DIMMER_MULTI_RGB"] = 5] = "DIMMER_MULTI_RGB"; WLEDDMXMode2[WLEDDMXMode2["MULTI_RGBW"] = 6] = "MULTI_RGBW"; return WLEDDMXMode2; })(WLEDDMXMode || {}); // src/apis/json.ts import { fetch, AbortController } from "@js-bits/fetch"; // src/utils.emitter.ts function isCustomEvent(event) { return !!event.detail; } var IsomorphicCustomEvent = class extends Event { detail; constructor(typeArg, eventInitDict) { super(typeArg, eventInitDict); this.detail = eventInitDict == null ? void 0 : eventInitDict.detail; } }; var IsomorphicEventEmitter = class extends EventTarget { on(eventName, listener) { return this.addEventListener(eventName, (event) => { if (isCustomEvent(event)) return listener(...event.detail); listener(event); }); } once(eventName, listener) { let event_listener = this.on(eventName, listener); this.addEventListener(eventName, () => this.off(eventName, listener)); return event_listener; } off(eventName, listener) { return this.removeEventListener(eventName, listener); } emit(eventName, ...args) { const event = new IsomorphicCustomEvent(eventName, { detail: args }); return this.dispatchEvent(event); } }; // src/apis/json.ts var WLEDJSONAPI = class extends IsomorphicEventEmitter { api_endpoint; authority; constructor({ secure, host, port }) { super(); this.authority = `${secure ? "https" : "http"}://${host}${port ? ":" + port : ""}`; this.api_endpoint = `${this.authority}/${"json" /* JSON */}`; } handleErrors(response) { if (!response.ok) { this.emit("error", response); throw response; } return response; } async fetch(resource, options = {}) { const { timeout = 5e3 } = options; const controller = new AbortController(); const id = setTimeout(() => controller.abort(), timeout); const response = await fetch(resource, { ...options, signal: controller.signal }); clearTimeout(id); return response; } async getAll(options = {}) { const { timeout } = options; let response = await this.fetch(this.api_endpoint, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getPalettes(options = {}) { const { timeout } = options; let response = await this.fetch(`${this.api_endpoint}/pal`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getEffects(options = {}) { const { timeout } = options; let response = await this.fetch(`${this.api_endpoint}/eff`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getInfo(options = {}) { const { timeout } = options; let response = await this.fetch(`${this.api_endpoint}/info`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getState(options = {}) { const { timeout } = options; let response = await this.fetch(`${this.api_endpoint}/state`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getLive(options = {}) { const { timeout } = options; let response = await this.fetch(`${this.api_endpoint}/live`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getPresets(options = {}) { const { timeout } = options; let response = await this.fetch(`${this.authority}/presets.json`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getConfig(options = {}) { const { timeout } = options; let response = await this.fetch(`${this.api_endpoint}/cfg`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async getPalettesDataPage(page = 0, options = {}) { const { timeout } = options; let response = await this.fetch(`${this.api_endpoint}/palx?page=${page}`, { timeout }).then(this.handleErrors); let object = await response.json(); return object; } async updateState(state, options = {}) { const { timeout } = options; let result = await this.fetch(this.api_endpoint, { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/json" }, body: JSON.stringify(state), timeout }).then(this.handleErrors); let context = await result.json(); if (state.v) return context; return context; } async updateConfig(config, options = {}) { const { timeout } = options; let result = await this.fetch(`${this.api_endpoint}/cfg`, { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/json" }, body: JSON.stringify(config), timeout }).then(this.handleErrors); let context = await result.json(); return context; } }; // src/utils.ts function isWLEDContext(object) { let { state, info } = object; return !!state && !!info; } function isWLEDLiveLEDs(object) { let { leds, n } = object; return !!leds && !!n; } function isBuildStateFunction(state) { return typeof state === "function"; } function deepCloneTransform(object, transformer, key_path, stack) { const isArray = Array.isArray(object); const cloned_object = isArray ? [] : {}; for (let [key, value] of Object.entries(object)) { const current_key_path = (key_path ? key_path + "." : "") + key; let cloned_value; if (typeof value == "object" || Array.isArray(value)) { if (!stack) stack = /* @__PURE__ */ new Map(); const existing = stack.get(value); if (existing) { cloned_value = existing; } else { cloned_value = deepCloneTransform(value, transformer, current_key_path, stack); stack.set(value, cloned_value); } } else { cloned_value = value; } if (transformer) { let [transformed_key, transformed_value] = transformer(key, cloned_value, key_path); cloned_object[transformed_key] = transformed_value; } else { cloned_object[key] = value; } } return cloned_object; } var sleep = (time) => new Promise((res) => setTimeout(res, time)); // src/apis/websocket.ts import WebSocket from "isomorphic-ws"; var WLEDWebsocketAPI = class extends IsomorphicEventEmitter { api_endpoint; available = false; websocket; reconnect = true; constructor({ secure, host, port, websocket }) { super(); if (websocket && typeof websocket == "object") { if (typeof websocket.reconnect == "boolean") this.reconnect = websocket.reconnect; } this.api_endpoint = `${secure ? "wss" : "ws"}://${host}${port ? ":" + port : ""}/${"ws" /* WS */}`; } disconnect() { if (this.websocket && this.websocket.readyState == this.websocket.OPEN) { this.websocket.close(); } } connect() { if (this.websocket && this.websocket.readyState == this.websocket.OPEN) return Promise.resolve(true); this.websocket = new WebSocket(this.api_endpoint); this.websocket.binaryType = "arraybuffer"; return new Promise((resolve, reject) => { this.websocket.addEventListener("error", reject); this.websocket.addEventListener("open", () => { this.websocket.removeEventListener("error", reject); this.emit("open"); this.init(); resolve(true); }); }); } init() { this.available = true; this.websocket.addEventListener("message", ({ data }) => { if (data instanceof ArrayBuffer) { const header = new Uint8Array(data.slice(0, 2)); const type = String.fromCharCode(header[0]); const version = header[1]; if (type == "L") switch (version) { case 1: default: const leds = []; const raw_leds = data.slice(2); for (let i = 0; i < raw_leds.byteLength / 3; i++) leds.push(new Uint8Array(raw_leds.slice(i, i + 3))); this.emit("live:leds", leds); } } else if (typeof data == "string") { let message = JSON.parse(data); if (isWLEDContext(message)) { let { state, info } = message; this.emit("update:context", { state, info }); } else if (isWLEDLiveLEDs(message)) { this.emit("live:leds", message.leds); } } }); this.websocket.addEventListener("close", (event) => { this.emit("close", event); if (!event.wasClean) { if (this.reconnect) setTimeout(() => this.connect(), 1e3); } this.available = false; }); this.websocket.addEventListener("error", (error) => { this.emit("error", error); }); } send(data, cb) { if (this.available) { this.websocket.send(data); cb(); return; } else { let err = new Error("WebSocket is not available."); if (cb) cb(err); else throw err; } } updateState(state) { return new Promise((resolve, reject) => { this.send(JSON.stringify(state), (err) => { if (err) reject(err); resolve(void 0); }); }); } startLEDStream() { return new Promise((resolve, reject) => { this.send(JSON.stringify({ lv: true }), (err) => { if (err) reject(err); resolve(void 0); }); }); } stopLEDStream() { return new Promise((resolve, reject) => { this.send(JSON.stringify({ lv: false }), (err) => { if (err) reject(err); resolve(void 0); }); }); } }; // src/adapters.ts var key_regexes = {}; function keyTransformer(transform_map) { return (key, value, path) => { const current_key_path = (path ? path + "." : "") + key; const transform_map_key = Object.keys(transform_map).find((transform_key) => { if (!key_regexes[transform_key]) key_regexes[transform_key] = new RegExp("^" + transform_key.replaceAll(".", "\\.").replaceAll("*", "[^_]+") + "$", "m"); return !!current_key_path.match(key_regexes[transform_key]); }); const transformed_key_path = transform_map_key ? transform_map[transform_map_key] : key; let split_path = transformed_key_path.split("."); let transformed_key = split_path[split_path.length - 1]; return [transformed_key || key, value]; }; } function wledToClientLightCapabilities(options) { return { cct: !!(options & WLEDLightCapabilityMasks.CCT), white: !!(options & WLEDLightCapabilityMasks.WHITE), rgb: !!(options & WLEDLightCapabilityMasks.RGB) }; } var WLED_TO_CLIENT_INFO_MAP = { "ver": "version", "vid": "buildId", "leds": "leds", "leds.count": "leds.count", "leds.fps": "leds.fps", "leds.rgbw": "leds.rgbw", "leds.cct": "leds.cct", "leds.wv": "leds.whiteValueInput", "leds.lc": "leds.lightCapabilities", "leds.seglc": "leds.segmentLightCapabilities", "leds.pwr": "leds.currentPower", "leds.maxpwr": "leds.maxPower", "leds.maxseg": "leds.maxSegments", "str": "syncToggleReceive", "name": "name", "udpport": "udpPort", "live": "live", "lm": "liveSource", "lip": "liveIp", "ws": "wsConnectedCount", "fxcount": "effectsCount", "palcount": "palettesCount", "wifi": "wifi", "wifi.bssid": "wifi.bssid", "wifi.rssi": "wifi.rssi", "wifi.txPower": "wifi.txPower", "wifi.sleep": "wifi.sleep", "wifi.signal": "wifi.signal", "wifi.channel": "wifi.channel", "fs": "fs", "fs.u": "fs.used", "fs.t": "fs.total", "fs.pmt": "fs.presetsModifiedTime", "ndc": "discoveredDevicesCount", "arch": "arch", "core": "core", "freeheap": "freeheap", "uptime": "uptime", "opt": "options", "resetReason": "resetReason", "resetReason0": "resetReason0", "resetReason1": "resetReason1", "brand": "brand", "product": "product", "mac": "mac" }; var CLIENT_TO_WLED_INFO_MAP = Object.fromEntries( Object.entries(WLED_TO_CLIENT_INFO_MAP).map(([key, value]) => [value, key]) ); var wledToClientInfoTransformer = keyTransformer(WLED_TO_CLIENT_INFO_MAP); var clientToWLEDInfoTransformer = keyTransformer(CLIENT_TO_WLED_INFO_MAP); function wledToClientInfo(info) { return deepCloneTransform(info, wledToClientInfoTransformer); } var WLED_TO_CLIENT_STATE_MAP = { "error": "error", "on": "on", "bri": "brightness", "transition": "transition", "tt": "temporaryTransition", "ps": "presetId", "n": "name", "psave": "savePresetId", "pdel": "deletePresetId", "ib": "includeBrightness", "sb": "segmentBounds", "o": "overwriteState", "ql": "label", "pl": "playlistId", "nl": "nightlight", "nl.on": "nightlight.on", "nl.dur": "nightlight.duration", "nl.mode": "nightlight.mode", "nl.tbri": "nightlight.targetBrightness", "nl.rem": "nightlight.remaining", "udpn": "udpSync", "udpn.send": "udpSync.send", "udpn.recv": "udpSync.receive", "udpn.nn": "udpSync.noSync", "lor": "liveDataOverride", "mainseg": "mainSegmentId", "seg": "segments", "seg.*.id": "segments.*.id", "seg.*.n": "segments.*.name", "seg.*.start": "segments.*.start", "seg.*.stop": "segments.*.stop", "seg.*.len": "segments.*.length", "seg.*.of": "segments.*.offset", "seg.*.grp": "segments.*.grouping", "seg.*.spc": "segments.*.spacing", "seg.*.rpt": "segments.*.repeat", "seg.*.frz": "segments.*.freeze", "seg.*.col": "segments.*.colors", "seg.*.cct": "segments.*.cct", "seg.*.fx": "segments.*.effectId", "seg.*.sx": "segments.*.effectSpeed", "seg.*.ix": "segments.*.effectIntensity", "seg.*.pal": "segments.*.paletteId", "seg.*.sel": "segments.*.selected", "seg.*.rev": "segments.*.reverse", "seg.*.on": "segments.*.on", "seg.*.bri": "segments.*.brightness", "seg.*.mi": "segments.*.mirror", "seg.*.lx": "segments.*.loxonePrimaryColor", "seg.*.ly": "segments.*.loxoneSecondaryColor", "playlist": "playlist", "playlist.ps": "playlist.presets", "playlist.dur": "playlist.durations", "playlist.transition": "playlist.transition", "playlist.repeat": "playlist.repeat", "playlist.end": "playlist.end", "v": "returnFullState", "rb": "reboot", "time": "time" }; var CLIENT_TO_WLED_STATE_MAP = Object.fromEntries( Object.entries(WLED_TO_CLIENT_STATE_MAP).map(([key, value]) => [value, key]) ); var wledToClientStateTransformer = keyTransformer(WLED_TO_CLIENT_STATE_MAP); var clientToWLEDStateTransformer = keyTransformer(CLIENT_TO_WLED_STATE_MAP); function wledToClientState(state) { return deepCloneTransform(state, wledToClientStateTransformer); } function clientToWLEDState(state) { return deepCloneTransform(state, clientToWLEDStateTransformer); } var WLED_TO_CLIENT_PRESET_MAP = { "*.n": "*.name", "*.ql": "*.label", "*.on": "*.on", "*.bri": "*.brightness", "*.transition": "*.transition", "*.mainseg": "*.mainSegment", "*.seg": "*.segments", "*.seg.*.id": "*.segments.*.id", "*.seg.*.start": "*.segments.*.start", "*.seg.*.stop": "*.segments.*.stop", "*.seg.*.len": "*.segments.*.length", "*.seg.*.grp": "*.segments.*.grouping", "*.seg.*.spc": "*.segments.*.spacing", "*.seg.*.rpt": "*.segments.*.repeat", "*.seg.*.frz": "*.segments.*.freeze", "*.seg.*.col": "*.segments.*.colors", "*.seg.*.fx": "*.segments.*.effectId", "*.seg.*.sx": "*.segments.*.effectSpeed", "*.seg.*.ix": "*.segments.*.effectIntensity", "*.seg.*.pal": "*.segments.*.paletteId", "*.seg.*.sel": "*.segments.*.selected", "*.seg.*.rev": "*.segments.*.reverse", "*.seg.*.on": "*.segments.*.on", "*.seg.*.bri": "*.segments.*.brightness", "*.seg.*.mi": "*.segments.*.mirror", "*.seg.*.lx": "*.segments.*.loxonePrimaryColor", "*.seg.*.ly": "*.segments.*.loxoneSecondaryColor" }; var CLIENT_TO_WLED_PRESET_MAP = Object.fromEntries( Object.entries(WLED_TO_CLIENT_PRESET_MAP).map(([key, value]) => [value, key]) ); var wledToClientPresetTransformer = keyTransformer(WLED_TO_CLIENT_PRESET_MAP); var clientToWLEDPresetTransformer = keyTransformer(CLIENT_TO_WLED_PRESET_MAP); function wledToClientPresets(presets) { return deepCloneTransform(presets, wledToClientPresetTransformer); } function wledToClientDeviceOptions(options) { return { debug: !!(options & WLEDDeviceOptionMasks.DEBUG), alexa: !!(options & WLEDDeviceOptionMasks.DISABLE_ALEXA), blynk: !!(options & WLEDDeviceOptionMasks.DISABLE_BLYNK), cronixie: !!(options & WLEDDeviceOptionMasks.USERMOD_CRONIXIE), filesystem: !!(options & WLEDDeviceOptionMasks.DISABLE_FILESYSTEM), huesync: !!(options & WLEDDeviceOptionMasks.DISABLE_HUESYNC), adalight: !(options & WLEDDeviceOptionMasks.ENABLE_ADALIGHT), OTA: !!(options & WLEDDeviceOptionMasks.DISABLE_OTA) }; } var WLED_TO_CLIENT_CONFIG_MAP = { "eth": "ethernet", "eth.type": "ethernet.type", "id": "id", "id.mdns": "id.mdns", "id.name": "id.name", "id.inv": "id.invocationName", "nw": "network", "nw.ins": "nw.instances", "nw.ins.*.ssid": "nw.instances.*.ssid", "nw.ins.*.ip": "nw.instances.*.ip", "nw.ins.*.gw": "nw.instances.*.gateway", "nw.ins.*.sn": "nw.instances.*.subnet", "ap": "accessPoint", "ap.ssid": "accessPoint.ssid", "ap.chan": "accessPoint.channel", "ap.hide": "accessPoint.hide", "ap.behav": "accessPoint.openBehavior", "wifi": "wifi", "wifi.sleep": "wifi.sleep", "hw": "hardware", "hw.led": "hardware.led", "hw.led.maxpwr": "hardware.led.maxCurrent", "hw.led.ledma": "hardware.led.maxCurrentPerLED", "hw.led.rgbwm": "hardware.led.autoWhiteMode", "hw.led.cct": "hardware.led.cctCorrection", "hw.led.cr": "hardware.led.cctFromRGB", "hw.led.cb": "hardware.led.cctBlending", "hw.led.fps": "hardware.led.fps", "hw.led.ins": "hardware.led.instances", "hw.led.ins.*.type": "hardware.led.instances.*.type", "hw.led.ins.*.start": "hardware.led.instances.*.start", "hw.led.ins.*.len": "hardware.led.instances.*.length", "hw.led.ins.*.skip": "hardware.led.instances.*.skip", "hw.led.ins.*.order": "hardware.led.instances.*.colorOrder", "hw.led.ins.*.pin": "hardware.led.instances.*.pins", "hw.led.ins.*.ref": "hardware.led.instances.*.offRefresh", "hw.led.ins.*.rev": "hardware.led.instances.*.reverse", "hw.led.ins.*.rgbw": "hardware.led.instances.*.rgbw", "hw.btn": "hardware.button", "hw.btn.max": "hardware.button.max", "hw.btn.ins": "hardware.button.instances", "hw.btn.ins.*.type": "hardware.button.instances.*.type", "hw.btn.ins.*.pin": "hardware.button.instances.*.pin", "hw.btn.ins.*.macros": "hardware.button.instances.*.macros", "hw.btn.tt": "hardware.button.touchThreshold", "hw.btn.mqtt": "hardware.button.mqtt", "hw.ir": "hardware.ir", "hw.pin": "hardware.pin", "hw.type": "hardware.type", "hw.relay": "hardware.relay", "hw.relay.pin": "hardware.relay.pin", "hw.relay.rev": "hardware.relay.reverse", "light": "light", "light.scale-bri": "light.scaleBrightness", "light.pal-mode": "light.paletteBlendingMode", "light.aseg": "light.autoSegments", "light.gc": "light.gammaCorrection", "light.gc.bri": "light.gammaCorrection.brightness", "light.gc.col": "light.gammaCorrection.color", "light.tr": "light.transition", "light.tr.mode": "light.transition.enabled", "light.tr.dur": "light.transition.duration", "light.tr.pal": "light.transition.palettes", "light.nl": "light.nightlight", "light.nl.mode": "light.nightlight.mode", "light.nl.dur": "light.nightlight.duration", "light.nl.tbri": "light.nightlight.targetBrightness", "def": "defaults", "def.ps": "defaults.preset", "def.on": "defaults.on", "def.bri": "defaults.brightness", "if": "interfaces", "if.blynk": "interfaces.blynk", "if.blynk.host": "interfaces.blynk.host", "if.blynk.port": "interfaces.blynk.port", "if.blynk.token": "interfaces.blynk.token", "if.hue": "interfaces.hue", "if.hue.en": "interfaces.hue.enabled", "if.hue.id": "interfaces.hue.id", "if.hue.ip": "interfaces.hue.ip", "if.hue.iv": "interfaces.hue.interval", "if.hue.recv": "interfaces.hue.receive", "if.hue.recv.on": "interfaces.hue.receive.on", "if.hue.recv.bri": "interfaces.hue.receive.brightness", "if.hue.recv.col": "interfaces.hue.receive.color", "if.live": "interfaces.live", "if.live.dmx": "interfaces.live.dmx", "if.live.dmx.addr": "interfaces.live.dmx.address", "if.live.dmx.mode": "interfaces.live.dmx.mode", "if.live.dmx.seqskip": "interfaces.live.dmx.sequenceSkip", "if.live.dmx.uni": "interfaces.live.dmx.universe", "if.live.mc": "interfaces.live.multicast", "if.live.port": "interfaces.live.port", "if.live.en": "interfaces.live.enabled", "if.live.maxbri": "interfaces.live.maxBrightness", "if.live.no-gc": "interfaces.live.noGammaCorrection", "if.live.offset": "interfaces.live.offset", "if.live.timeout": "interfaces.live.timeout", "if.mqtt": "interfaces.mqtt", "if.mqtt.en": "interfaces.mqtt.enabled", "if.mqtt.broker": "interfaces.mqtt.broker", "if.mqtt.port": "interfaces.mqtt.port", "if.mqtt.cid": "interfaces.mqtt.clientId", "if.mqtt.user": "interfaces.mqtt.user", "if.mqtt.topics": "interfaces.mqtt.topics", "if.mqtt.topics.device": "interfaces.mqtt.topics.device", "if.mqtt.topics.group": "interfaces.mqtt.topics.group", "if.nodes": "interfaces.nodes", "if.nodes.list": "interfaces.nodes.list", "if.nodes.bcast": "interfaces.nodes.broadcast", "if.ntp": "interfaces.ntp", "if.ntp.en": "interfaces.ntp.enabled", "if.ntp.host": "interfaces.ntp.host", "if.ntp.ampm": "interfaces.ntp.ampm", "if.ntp.tz": "interfaces.ntp.timezone", "if.ntp.ln": "interfaces.ntp.lon", "if.ntp.lt": "interfaces.ntp.lat", "if.ntp.offset": "interfaces.ntp.offset", "if.sync": "interfaces.sync", "if.sync.port0": "interfaces.sync.port0", "if.sync.port1": "interfaces.sync.port1", "if.sync.recv": "interfaces.sync.receive", "if.sync.recv.bri": "interfaces.sync.receive.brightness", "if.sync.recv.col": "interfaces.sync.receive.color", "if.sync.recv.fx": "interfaces.sync.receive.effects", "if.sync.recv.grp": "interfaces.sync.receive.groups", "if.sync.send": "interfaces.sync.send", "if.sync.send.btn": "interfaces.sync.send.button", "if.sync.send.dir": "interfaces.sync.send.direct", "if.sync.send.hue": "interfaces.sync.send.hue", "if.sync.send.macro": "interfaces.sync.send.macro", "if.sync.send.va": "interfaces.sync.send.alexa", "if.sync.send.twice": "interfaces.sync.send.twice", "if.sync.send.grp": "interfaces.sync.send.groups", "if.va": "interfaces.alexa", "if.va.alexa": "interfaces.alexa.enabled", "if.va.macros": "interfaces.alexa.macros", "ol": "overlay", "ol.clock": "overlay.clock", "ol.cntdwn": "overlay.countdown", "ol.min": "overlay.min", "ol.max": "overlay.max", "ol.o12pix": "overlay.o12pix", "ol.o5m": "overlay.show5MinuteMarks", "ol.osec": "overlay.showSecondsTrail", "timers": "timers", "timers.cntdwn": "timers.countdown", "timers.cntdwn.goal": "timers.countdown.goal", "timers.cntdwn.macro": "timers.countdown.macro", "timers.ins": "timers.instances", "timers.ins.*.en": "timers.instances.*.enabled", "timers.ins.*.hour": "timers.instances.*.hour", "timers.ins.*.min": "timers.instances.*.minute", "timers.ins.*.dow": "timers.instances.*.dayOfWeek", "timers.ins.*.macro": "timers.instances.*.macro", "timers.ins.*.start": "timers.instances.*.start", "timers.ins.*.start.mon": "timers.instances.*.start.month", "timers.ins.*.start.day": "timers.instances.*.start.day", "timers.ins.*.end": "timers.instances.*.end", "timers.ins.*.end.mon": "timers.instances.*.end.month", "timers.ins.*.end.day": "timers.instances.*.end.day", "ota": "ota", "ota.aota": "ota.arduinoOTA", "ota.lock": "ota.lock", "ota.lock-wifi": "ota.lockWiFi", "dmx": "dmx", "dmx.chan": "dmx.channel", "dmx.gap": "dmx.gap", "dmx.start": "dmx.start", "dmx.start-led": "dmx.startLED", "dmx.fixmap": "dmx.fixtureMap", "dmx.e131proxy": "dmx.e131Proxy", "um": "usermods" }; var CLIENT_TO_WLED_CONFIG_MAP = Object.fromEntries( Object.entries(WLED_TO_CLIENT_CONFIG_MAP).map(([key, value]) => [value, key]) ); var wledToClientConfigTransformer = keyTransformer(WLED_TO_CLIENT_CONFIG_MAP); var clientToWLEDConfigTransformer = keyTransformer(CLIENT_TO_WLED_CONFIG_MAP); function wledToClientConfig(config) { return deepCloneTransform(config, wledToClientConfigTransformer); } function clientToWLEDConfig(config) { return deepCloneTransform(config, clientToWLEDConfigTransformer); } // src/client.ts var WLEDClient = class extends IsomorphicEventEmitter { state; info; effects; palettes; presets; config; deviceOptions; lightCapabilities; live; isReady; options; get wsReadyState() { return this.WSAPI.websocket.readyState; } JSONAPI; WSAPI; constructor(host_or_options = {}) { super(); let options; if (typeof host_or_options == "string") options = { host: host_or_options }; else options = host_or_options; const resolved_options = Object.assign(DEFAULT_OPTIONS, options); this.options = resolved_options; Object.assign(this, DEFAULT_CLIENT_CONTEXT); this.WSAPI = new WLEDWebsocketAPI(resolved_options); this.WSAPI.on("error", (event) => this.emit("error", event)); this.WSAPI.on("close", (event) => this.emit("close", event)); this.WSAPI.on("open", (event) => this.emit("open", event)); this.WSAPI.on("live:leds", (event) => this.emit("live:leds", event)); this.WSAPI.on("update:context", this.setContext.bind(this)); this.JSONAPI = new WLEDJSONAPI(resolved_options); this.JSONAPI.on("error", (event) => this.emit("error", event)); if (resolved_options.immediate) this.init(); } async init() { if (this.isReady) return this.isReady; let initializing = this.options.websocket ? [this.refreshContext(this.options.init), this.WSAPI.connect()] : [this.refreshContext(this.options.init)]; let isReady = Promise.allSettled(initializing); this.isReady = isReady.then(([json_result, ws_result]) => { if (ws_result && ws_result.status == "rejected" && json_result.status == "rejected") { this.emit("error", json_result.reason); return Promise.reject(json_result.reason); } if (json_result.status == "fulfilled") { this.emit("success", { transport: "http" }); this.emit("success:http"); } if (ws_result && ws_result.status == "fulfilled") { this.emit("success", { transport: "ws" }); this.emit("success:ws"); } this.emit("ready"); return true; }); } async refreshContext(options = {}) { const { presets: get_presets = true, config: get_config = true } = options; const [context, presets, config] = await Promise.all([ this.JSONAPI.getAll(), get_presets ? this.JSONAPI.getPresets() : Promise.resolve({}), get_config ? this.JSONAPI.getConfig() : Promise.resolve({}) ]); this.setContext({ ...context, presets, config }); } async refreshState() { const state = await this.JSONAPI.getState(); this.setContext({ state }); } async refreshInfo() { const info = await this.JSONAPI.getInfo(); this.setContext({ info }); } async refreshEffects() { const effects = await this.JSONAPI.getEffects(); this.setContext({ effects }); } async refreshPalettes() { const palettes = await this.JSONAPI.getPalettes(); this.setContext({ palettes }); } async refreshPresets() { const presets = await this.JSONAPI.getPresets(); this.setContext({ presets }); } async refreshConfig() { const config = await this.JSONAPI.getConfig(); this.setContext({ config }); } setContext({ state, info, effects, palettes, presets, config }) { let client_state = state ? wledToClientState(state) : this.state; let client_info = info ? wledToClientInfo(info) : this.info; let client_effects = effects ? effects : this.effects; let client_palettes = palettes ? palettes : this.palettes; let client_presets = presets ? wledToClientPresets(presets) : this.presets; let client_config = config ? wledToClientConfig(config) : this.config; let context = { state: client_state, info: client_info, effects: client_effects, palettes: client_palettes, presets: client_presets, deviceOptions: info ? wledToClientDeviceOptions(info.opt) : this.deviceOptions, lightCapabilities: info ? wledToClientLightCapabilities(info.leds.lc) : this.lightCapabilities, live: this.live, config: client_config }; Object.assign(this, { ...context }); this.emit("update:context", context); if (state) this.emit("update:state", client_state); if (info) this.emit("update:info", client_info); if (effects) this.emit("update:effects", client_effects); if (palettes) this.emit("update:palettes", client_palettes); if (presets) this.emit("update:presets", client_presets); if (config) this.emit("update:config", client_config); } async updateState(state, options) { let use_method; let timeout; if (options) { const { transition, noSync, method, timeout: o_timeout } = options; if (transition) state.temporaryTransition = transition; if (noSync) state.udpSync = { ...state.udpSync || {}, noSync }; if (method) use_method = method; if (timeout) timeout = o_timeout; } const wled_state = clientToWLEDState(state); if ((!use_method || use_method != "json") && this.WSAPI.available) { try { this.emit("loading"); await this.WSAPI.updateState(wled_state); this.emit("success", { transport: "ws" }); this.emit("success:ws"); return; } catch (e) { this.emit("error", e); } } if (!use_method || use_method != "ws") { try { this.emit("loading"); let new_context = await this.JSONAPI.updateState({ ...wled_state, v: true }, { timeout }); this.emit("success", { transport: "http" }); this.emit("success:http"); return this.setContext(new_context); } catch (e) { this.emit("error", e); } } throw new Error("No transport available to handle state update."); } async updateConfig(config) { const wled_config = clientToWLEDConfig(config); const { success } = await this.JSONAPI.updateConfig(wled_config); if (success) this.refreshContext(); return success; } buildStateWithSegments(state, segmentId) { let new_state; if (segmentId !== void 0) new_state = { segments: (Array.isArray(segmentId) ? segmentId : [segmentId]).map((id) => ({ id, ...isBuildStateFunction(state) ? state(this.getSegment(id)) : state })) }; else new_state = isBuildStateFunction(state) ? state() : state; return new_state; } connect() { return this.WSAPI.connect(); } disconnect() { return this.WSAPI.disconnect(); } async startLEDStream() { await this.WSAPI.startLEDStream(); this.live.leds = true; this.emit("update:live", this.live); } async stopLEDStream() { await this.WSAPI.stopLEDStream(); this.live.leds = false; this.emit("update:live", this.live); } toggleLEDStream() { if (this.live.leds) return this.stopLEDStream(); return this.startLEDStream(); } reboot() { return this.updateState({ reboot: true }); } turnOn({ segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ on: true }, segmentId), options); } turnOff({ segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ on: false }, segmentId), options); } toggle({ segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments((segment) => { return { on: segment ? !segment.on : !this.state.on }; }, segmentId), options); } setBrightness(value, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ brightness: value }, segmentId), options); } setColor(color, options) { return this.setPrimaryColor(color, options); } setPrimaryColor(color, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ colors: [color] }, segmentId || 0), options); } setSecondaryColor(color, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ colors: [void 0, color] }, segmentId || 0), options); } setTertiaryColor(color, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ colors: [void 0, void 0, color] }, segmentId || 0), options); } setCCT(kelvin, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ cct: kelvin }, segmentId || 0), options); } setPalette(paletteId, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ paletteId }, segmentId || 0), options); } paletteDataCache; async getPalettesData(page) { let palettes_data = {}; if (page) { const { p } = await this.JSONAPI.getPalettesDataPage(page); Object.assign(palettes_data, p); } else if (this.paletteDataCache) { palettes_data = this.paletteDataCache; } else { let max_page = 1; page = 0; while (page <= max_page || page > 100) { let result = await this.JSONAPI.getPalettesDataPage(page); if (result != null) { let { m, p } = result; Object.assign(palettes_data, p); max_page = m; page++; } else { await sleep(100); } } this.paletteDataCache = palettes_data; } return palettes_data; } setEffect(effectId, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ effectId }, segmentId || 0), options); } setEffectSpeed(value, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ effectSpeed: value }, segmentId || 0), options); } setEffectIntensity(value, { segmentId, ...options } = {}) { return this.updateState(this.buildStateWithSegments({ effectIntensity: value }, segmentId || 0), options); } setTransitionTime(value) { return this.updateState({ transitionTime: value }); } setMainSegmentId(id) { return this.updateState({ mainSegmentId: id }); } getSegment(id) { return this.state.segments[id]; } createSegment(data) { return this.updateState({ segments: [ ...this.state.segments, data ] }); } updateSegment(id, data, options) { return this.updateState({ segments: [ { id, ...data } ] }, options); } deleteSegment(id) { return this.updateState({ segments: [ { id, stop: 0 } ] }); } async setSegments(segments) { await this.clearSegments(); return this.updateState({ segments }); } clearSegments() { return this.updateState({ segments: this.state.segments.map(() => ({ stop: 0 })) }); } setPlaylist(playlist) { return this.updateState({ playlist }); } nightlight = (() => { const wled = this; return { get state() { return wled.state.nightlight; }, enable(with_state = {}) { if (typeof with_state == "number") with_state = { duration: with_state }; this.state.remaining = this.state.duration; return wled.updateState({ nightlight: { on: true, ...with_state } }); }, disable() { return wled.updateState({ nightlight: { on: false } }); }, toggle() { return wled.updateState({ nightlight: { on: !wled.state.nightlight.on } }); }, setDuration(value) { return wled.updateState({ nightlight: { duration: value } }); }, setTargetBrightness(value) { return wled.updateState({ nightlight: { targetBrightness: value } }); }, setMode(mode) { return wled.updateState({ nightlight: { mode } }); } }; })(); ignoreLiveData(until_reboot) { let liveDataOverride = until_reboot ? 2 /* UNTIL_REBOOT */ : 1 /* UNTIL_END */; return this.updateState({ liveDataOverride }); } allowLiveData() { return this.updateState({ liveDataOverride: 0 /* OFF */ }); } enableUDPSync(options) { if (!options) { options = { send: true }; if (this.info.syncToggleReceive) options.receive = true; } return this.updateState({ udpSync: options }); } disableUDPSync() { let udpSync = { send: false }; if (this.info.syncToggleReceive) udpSync.receive = false; return this.updateState({ udpSync }); } getPreset(id) { return this.presets[id]; } setPreset(id) { return this.updateState({ presetId: id }); } async saveStateAsPreset(id, preset) { preset = Object.assign({ includeBrightness: true, segmentBounds: true }, preset); await this.updateState({ savePresetId: id, ...preset, time: new Date().getTime() }, { method: "json" }); } async savePreset(id, preset) { await this.updateState({ savePresetId: id, overwriteState: true, ...preset }, { method: "json" }); this.presets[id] = preset; } async deletePreset(id) { await this.updateState({ deletePresetId: id }); delete this.presets[id]; } }; export { WLEDAPOpenBehavior, WLEDAutoWhiteMode, WLEDBusColorOrder, WLEDBusType, WLEDButtonType, WLEDClient, WLEDClockOverlay, WLEDDMXMode, WLEDIRRemoteType, WLEDLiveDataOverride, WLEDNightlightMode, WLEDPaletteBlendingMode }; //# sourceMappingURL=wled-client.js.map