UNPKG

iobroker.nspanel-lovelace-ui

Version:

NsPanel Lovelace UI is a Firmware for the nextion screen inside of NSPanel in the Design of Lovelace UI Design.

1,112 lines 84.8 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 __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 import_adminShareConfig = require("./lib/types/adminShareConfig"); var utils = __toESM(require("@iobroker/adapter-core")); var import_library = require("./lib/controller/library"); var import_register = require("source-map-support/register"); var MQTT = __toESM(require("./lib/classes/mqtt")); var import_controller = require("./lib/controller/controller"); var import_icon_mapping = require("./lib/const/icon_mapping"); var definition = __toESM(require("./lib/const/definition")); var import_config_manager = require("./lib/classes/config-manager"); var import_readme = require("./lib/tools/readme"); var import_url = require("url"); var fs = __toESM(require("fs")); var import_path = __toESM(require("path")); var import_test = require("./lib/const/test"); var import_function_and_const = require("./lib/types/function-and-const"); class NspanelLovelaceUi extends utils.Adapter { library; mqttClient; mqttServer; controller; unload = false; testSuccessful = true; timeoutAdmin; timeoutAdmin2; timeoutAdminArray = []; intervalAdminArray = []; mainConfiguration; testCaseConfig; // just for testing scriptConfigBacklog = []; fetchs = /* @__PURE__ */ new Map(); constructor(options = {}) { super({ ...options, name: "nspanel-lovelace-ui", useFormatDate: true }); this.library = new import_library.Library(this); this.on("ready", this.onReady.bind(this)); this.on("stateChange", this.onStateChange.bind(this)); this.on("message", this.onMessage.bind(this)); this.on("unload", this.onUnload.bind(this)); } /** * Is called when databases are connected and adapter received configuration. */ async onReady() { var _a, _b, _c, _d, _e, _f; await this.extendForeignObjectAsync(this.namespace, { type: "meta", common: { name: { en: "Nspanel Instance", de: "Nspanel Instanze" }, type: "meta.folder" }, native: {} }); if (this.config.forceTFTVersion) { this.log.warn( `\u26A0\uFE0F TFT firmware is pinned to version ${this.config.forceTFTVersion}. Remember: you will always stay on this version until you change it.` ); if (this.config.forceTFTVersion === "0.0.0") { this.log.warn( `\u26A0\uFE0F Developer version of the TFT firmware is used. This version may be unstable and have bugs! No support in the forum!` ); } } if (this.config.fixBrokenCommonTypes) { const states = await this.getForeignObjectsAsync("alias.0.*"); this.log.info("Fix broken common.type in alias.0"); if (states) { for (const id in states) { if (states[id] && states[id].type === "state" && states[id].common && //@ts-expect-error states[id].common.type === "state") { this.log.warn(`Fix broken common.type in ${id} set to 'mixed'`); states[id].common.type = "mixed"; await this.extendForeignObjectAsync(id, states[id]); } } } } const o = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`); if (o && o.native) { let change = false; const native = o.native; if (native.fixBrokenCommonTypes === true) { native.fixBrokenCommonTypes = false; change = true; } native.pageConfig = native.pageConfig || []; if (native.pageUnlockConfig && !native.pageConfig) { native.pageConfig = native.pageUnlockConfig; delete native.pageUnlockConfig; change = true; } if (native.pageQRdata) { native.pageQRdata.forEach((page) => { const temp = { card: "cardQR", uniqueName: page.pageName, headline: page.headline, selType: page.selType, ssidUrlTel: page.SSIDURLTEL, setState: page.setState || "", wlanhidden: page.wlanhidden || false, pwdhidden: page.pwdhidden || false, hidden: page.hiddenByTrigger || false, alwaysOn: page.alwaysOnDisplay ? "always" : "none" }; native.pageConfig.push(temp); }); delete native.pageQRdata; change = true; } if (change) { const uniquePages = /* @__PURE__ */ new Map(); for (const p of (_a = native.pageConfig) != null ? _a : []) { if (p == null ? void 0 : p.uniqueName) { if (uniquePages.has(p.uniqueName)) { this.log.warn(`Duplicate uniqueName '${p.uniqueName}' found in pageConfig!`); continue; } uniquePages.set(p.uniqueName, p); } } native.pageConfig = [...uniquePages.values()]; await this.setForeignObject(`system.adapter.${this.namespace}`, o); this.log.warn(`Updated configuration of ${this.namespace} to the latest version. Restart adapter!`); return; } } await (0, import_readme.generateAliasDocumentation)(); if (this.config.testCase) { this.log.warn("Testcase mode!"); } if (this.config.weatherEntity === void 0 || typeof this.config.weatherEntity !== "string") { this.config.weatherEntity = ""; } else if (this.config.weatherEntity !== "" && definition.weatherEntities.findIndex((a) => this.config.weatherEntity.startsWith(a)) === -1) { this.log.error( `Invalid weatherEntity index, set to ${this.config.weatherEntity}. Report this to the developer! Use custom.` ); this.config.weatherEntity = ""; } this.mainConfiguration = []; const obj = await this.getForeignObjectAsync(this.namespace); if (obj && obj.native) { const config = []; if (obj.native.scriptConfigRaw || obj.native.scriptConfig) { const panelsText = (this.config.panels || []).map((a) => `[${a.name}#${a.topic}]`).join(", "); const configsText = (_b = obj.native.scriptConfigRaw) == null ? void 0 : _b.map((a) => `${a.panelTopic}`).join(", "); this.log.info(`Configured panels: name#topic -> ${panelsText}`); this.log.info(`Found ${obj.native.scriptConfigRaw.length} script configs for topics: ${configsText}`); this.log.info( `Detailed configuration checks are suppressed here. Full validation output is only shown when the configuration script is sent to the adapter.` ); const manager = new import_config_manager.ConfigManager(this, true); manager.log.warn = function(_msg) { }; for (const a of this.config.panels) { if (!a || !a.topic) { continue; } let usedConfig = null; let rawFound = false; let rawConversionFailed = false; const raw = (_c = obj.native.scriptConfigRaw) == null ? void 0 : _c.find( (b) => b.panelTopic === a.topic ); if (raw) { rawFound = true; const c = await manager.setScriptConfig(raw); if (c && c.messages && c.messages.length > 0) { if (!c.messages[0].startsWith("Panel")) { this.log.warn(c.messages[0]); } } if (c && c.panelConfig) { config.push(c.panelConfig); usedConfig = "raw"; } else { rawConversionFailed = true; } } if (!usedConfig) { const conv = obj.native.scriptConfig.find( (b) => b.topic === a.topic ); if (conv) { config.push(conv); usedConfig = "converted"; } } if (!usedConfig) { this.log.warn(`No script config found for ${a.topic}`); await manager.delete(); } else if (usedConfig === "raw") { this.log.debug(`Config for ${a.topic}: raw`); } else { if (rawFound && rawConversionFailed) { this.log.warn( `Config for ${a.topic}: converted (RAW conversion failed). Please update the configuration script and send it to the adapter again.` ); } else { this.log.warn(`Config for ${a.topic}: converted`); } } } } try { this.mainConfiguration = await import_config_manager.ConfigManager.getConfig(this, config); } catch (e) { this.log.error(`Error in configuration: ${e.message}`); this.mainConfiguration = []; return; } } if (this.config.mqttServer && this.config.mqttPort && this.config.mqttUsername) { this.config.mqttPassword = this.config.mqttPassword || ""; const port = await this.getPortAsync(this.config.mqttPort); if (port != this.config.mqttPort) { this.log.error(`Port ${this.config.mqttPort} is already in use!`); this.log.error(`Please change the port in the admin settings to ${port}!`); this.log.error("Stopping adapter!"); if (this.stop) { await this.stop(); } return; } this.mqttServer = await MQTT.MQTTServerClass.createMQTTServer( this, this.config.mqttPort, this.config.mqttUsername, this.config.mqttPassword, "./mqtt", this.config.testCase ); this.config.mqttIp = "127.0.0.1"; } try { import_icon_mapping.Icons.adapter = this; await this.library.init(); const states = await this.getStatesAsync("*"); await this.library.initStates(states); await this.onMqttConnect(); await this.delay(1e3); for (const id in states) { if (id.endsWith(".info.isOnline")) { await this.library.writedp(id, false, definition.genericStateObjects.panel.panels.info.isOnline); } } this.log.debug("Check configuration!"); if (!this.config.pw1 || typeof this.config.pw1 !== "string") { this.log.warn("No pin entered for the service page! Please set a pin in the admin settings!"); } if (!(this.config.mqttIp && this.config.mqttPort && this.config.mqttUsername && this.config.mqttPassword)) { this.log.error("Invalid admin configuration for mqtt!"); this.testSuccessful = false; return; } this.mqttClient = new MQTT.MQTTClientClass( this, this.config.mqttIp, this.config.mqttPort, this.config.mqttUsername, this.config.mqttPassword, this.config.mqttServer, async (topic, message) => { this.log.debug(`${topic} ${message}`); } ); if (!this.mqttClient) { return; } await this.mqttClient.waitConnectAsync(5e3); if (this.config.testCase) { await this.extendForeignObjectAsync("0_userdata.0.boolean", { type: "state", common: { name: "boolean", type: "boolean" }, native: {} }); await this.extendForeignObjectAsync("0_userdata.0.number", { type: "state", common: { name: "number", type: "number" }, native: {} }); await this.extendForeignObjectAsync("0_userdata.0.string", { type: "state", common: { name: "string", type: "string" }, native: {} }); await this.onMessage({ _id: Date.now(), message: import_test.testScriptConfig, command: "ScriptConfig", from: "system.adapter.admin.0", callback: () => { } }); await this.delay(3e3); this.mainConfiguration = this.testCaseConfig; const test = new MQTT.MQTTClientClass( this, this.config.mqttIp, this.config.mqttPort, this.config.mqttUsername, this.config.mqttPassword, this.config.mqttServer, async (topic, message) => { this.log.debug(`${topic} ${message}`); } ); await test.waitConnectAsync(5e3); await test.subscribe("test/123456/cmnd/#", async (topic, message) => { this.log.debug(`Testcase ${topic}`); if (message === "pageType~pageStartup") { await test.publish("test/123456/stat/RESULT", '{"CustomSend": "Done"}'); await test.publish("test/123456/tele/RESULT", '{"CustomRecv":"event,startup,54,eu"}'); } else if (topic === "test/123456/cmnd/STATUS0") { await test.publish( "test/123456/stat/STATUS0", '{"Status":{"Module":0,"DeviceName":"NSPanel 4 Test","FriendlyName":["Tasmota",""],"Topic":"ns_panel4","ButtonTopic":"0","Power":"00","PowerLock":"00","PowerOnState":3,"LedState":1,"LedMask":"FFFF","SaveData":1,"SaveState":1,"SwitchTopic":"0","SwitchMode":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"ButtonRetain":0,"SwitchRetain":0,"SensorRetain":0,"PowerRetain":0,"InfoRetain":0,"StateRetain":0,"StatusRetain":0},"StatusPRM":{"Baudrate":115200,"SerialConfig":"8N1","GroupTopic":"tasmotas","OtaUrl":"http://ota.tasmota.com/tasmota32/release/tasmota32-nspanel.bin","RestartReason":"Vbat power on reset","Uptime":"0T00:07:28","StartupUTC":"2025-02-19T09:23:29","Sleep":50,"CfgHolder":4617,"BootCount":59,"BCResetTime":"2024-01-06T17:11:30","SaveCount":110},"StatusFWR":{"Version":"14.4.1(release-nspanel)","BuildDateTime":"2024-12-15T13:33:11","Core":"3_1_0","SDK":"5.3.2","CpuFrequency":160,"Hardware":"ESP32-D0WD-V3 v3.1","CR":"502/699"},"StatusLOG":{"SerialLog":2,"WebLog":1,"MqttLog":3,"SysLog":0,"LogHost":"","LogPort":514,"SSId":["xxx",""],"TelePeriod":300,"Resolution":"558180C0","SetOption":["00008009","2805C80001000600003C5A0A192800000000","00000080","00006000","00004000","00000000"]},"StatusMEM":{"ProgramSize":2017,"Free":862,"Heap":148,"StackLowMark":3,"PsrMax":2048,"PsrFree":2025,"ProgramFlashSize":4096,"FlashSize":4096,"FlashChipId":"16405E","FlashFrequency":40,"FlashMode":"DIO","Features":["0809","9F9AD7DF","0015A001","B7F7BFCF","05DA9BC4","E0360DC7","480840D2","20200000","D4BC482D","810A80F1","00000014"],"Drivers":"1,2,!3,!4,!5,7,!8,9,10,11,12,!14,!16,!17,!20,!21,24,26,!27,29,!34,!35,38,50,52,!59,!60,62,!63,!66,!67,!68,!73,!75,82,!86,!87,!88,!121","Sensors":"1,2,3,5,6,7,8,9,10,11,12,13,14,15,17,18,19,20,21,22,26,31,34,37,39,40,42,43,45,51,52,55,56,58,59,64,66,67,74,85,92,95,98,103,105,109,127","I2CDriver":"7,8,9,10,11,12,13,14,15,17,18,20,24,29,31,36,41,42,44,46,48,58,62,65,69,76,77,82,89"},"StatusNET":{"Hostname":"ns-panel4-0112","IPAddress":"192.168.178.174","Gateway":"192.168.178.1","Subnetmask":"255.255.254.0","DNSServer1":"192.168.179.21","DNSServer2":"0.0.0.0","Mac":"A0:B7:A5:54:C0:71","IP6Global":"","IP6Local":"xxx","Ethernet":{"Hostname":"","IPAddress":"0.0.0.0","Gateway":"0.0.0.0","Subnetmask":"0.0.0.0","DNSServer1":"192.168.179.21","DNSServer2":"0.0.0.0","Mac":"00:00:00:00:00:00","IP6Global":"","IP6Local":""},"Webserver":2,"HTTP_API":1,"WifiConfig":4,"WifiPower":16.0},"StatusMQT":{"MqttHost":"xxx","MqttPort":1883,"MqttClientMask":"ns_panel4","MqttClient":"ns_panel4","MqttUser":"xxx","MqttCount":1,"MAX_PACKET_SIZE":1200,"KEEPALIVE":30,"SOCKET_TIMEOUT":4},"StatusTIM":{"UTC":"2025-02-19T09:30:57Z","Local":"2025-02-19T10:30:57","StartDST":"2025-03-30T02:00:00","EndDST":"2025-10-26T03:00:00","Timezone":"+01:00","Sunrise":"07:50","Sunset":"18:17"},"StatusSNS":{"Time":"2025-02-19T10:30:57","ANALOG":{"Temperature1":-3.2},"TempUnit":"C"},"StatusSTS":{"Time":"2025-02-19T10:30:57","Uptime":"0T00:07:28","UptimeSec":448,"Heap":146,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":1,"Berry":{"HeapUsed":16,"Objects":212},"POWER1":"OFF","POWER2":"OFF","Wifi":{"AP":1,"SSId":"Keller","BSSId":"DC:15:C8:EB:3E:B8","Channel":7,"Mode":"HT40","RSSI":46,"Signal":-77,"LinkCount":1,"Downtime":"0T00:00:03"}}}' ); } }); } if (!this.mainConfiguration || !Array.isArray(this.mainConfiguration) || this.mainConfiguration.length === 0) { await this.delay(100); await this.mqttClient.destroy(); await this.delay(100); this.log.error("No configuration - adapter on hold!"); return; } this.mainConfiguration = structuredClone(this.mainConfiguration); let counter = 0; for (const a of this.mainConfiguration) { try { if (a && a.pages) { const names = []; for (const p of a.pages) { counter++; if (!("uniqueID" in p)) { continue; } if (((_d = p.config) == null ? void 0 : _d.card) === "screensaver" || ((_e = p.config) == null ? void 0 : _e.card) === "screensaver2" || ((_f = p.config) == null ? void 0 : _f.card) === "screensaver3") { p.uniqueID = `#${p.uniqueID}`; } if (names.indexOf(p.uniqueID) !== -1) { throw new Error( `PanelTopic: ${a.topic} uniqueID ${p.uniqueID} is double! Ignore this panel!` ); } names.push(p.uniqueID); } } } catch (e) { const index = this.mainConfiguration.findIndex((b) => b === a); this.mainConfiguration.splice(index, 1); this.log.error(`Error: ${e}`); } } await this.subscribeStatesAsync("*"); if (counter === 0) { return; } const mem = process.memoryUsage().heapUsed / 1024; this.log.debug(String(`${mem}k`)); this.controller = new import_controller.Controller(this, { mqttClient: this.mqttClient, name: "controller", panels: structuredClone(this.mainConfiguration) }); } catch (e) { this.testSuccessful = false; this.log.error(`Error onReady: ${e}`); } } onMqttConnect = async () => { const _helper = async (tasmota) => { try { const state = this.library.readdb(`panels.${tasmota.id}.info.nspanel.firmwareUpdate`); if (state && typeof state.val === "number" && state.val >= 100) { this.log.debug(`Force an MQTT reconnect from the Nspanel with the ip ${tasmota.ip} in 10 seconds!`); await this.fetch( `http://${tasmota.ip}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog Restart 1` ); } else { this.log.info(`Update detected on the Nspanel with the ip ${tasmota.ip}!!`); } } catch (e) { this.log.warn( `Error: This usually means that the NSpanel with ip ${tasmota.ip} is not online or has not been set up properly in the configuration! Error: ${e ? e.message : ""}` ); } }; for (const tasmota of this.config.panels) { if (tasmota && tasmota.ip) { void _helper(tasmota); } } await this.setState("info.connection", true, true); }; async fetch(url, init, timeout = 3e4) { var _a; const controller = new AbortController(); const timeoutId = this.setTimeout(() => { try { controller.abort(); } catch { } this.fetchs.delete(controller); }, timeout); this.fetchs.set(controller, timeoutId); try { const response = await fetch(url, { ...init, method: (_a = init == null ? void 0 : init.method) != null ? _a : "GET", signal: controller.signal }); if (response.status === 200) { return await response.json(); } throw new Error({ status: response.status, statusText: response.statusText }); } finally { const id = this.fetchs.get(controller); if (typeof id !== "undefined") { this.clearTimeout(id); } this.fetchs.delete(controller); } } /** * Is called when adapter shuts down - callback has to be called under any circumstances. * * @param callback Callback so the adapter can finish what it has to do. */ async onUnload(callback) { try { this.unload = true; if (this.timeoutAdmin) { this.clearTimeout(this.timeoutAdmin); } if (this.timeoutAdmin2) { this.clearTimeout(this.timeoutAdmin2); } for (const [controller, timeoutId] of this.fetchs.entries()) { try { if (timeoutId) { this.clearTimeout(timeoutId); } controller.abort(); } catch { } } this.fetchs.clear(); this.timeoutAdminArray.forEach((a) => { if (a) { this.clearTimeout(a); } }); this.intervalAdminArray.forEach((a) => { if (a) { this.clearInterval(a); } }); if (this.controller) { await this.controller.delete(); } if (this.mqttClient) { await this.mqttClient.destroy(); } if (this.mqttServer) { this.mqttServer.destroy(); } callback(); } catch { callback(); } } /** * Is called if a subscribed state changes * * @param id The id of the state that changed * @param state The state object holding the new value and meta information of the state */ async onStateChange(id, state) { if (state) { if (this.controller) { await this.controller.statesControler.onStateChange(id, state); } } else { } } // If you need to accept messages in your adapter, uncomment the following block and the corresponding line in the constructor. // /** // * Somee message was sent to this instance over message box. Used by email, pushover, text2speech, // * Using this method requires "common.messagebox" property to be set to true in io-package.json // */ async onMessage(obj) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L; if (typeof obj === "object" && obj.message !== void 0 && obj.command) { this.log.debug(JSON.stringify(obj)); if (obj.command === "tftInstallSendToMQTT") { if (obj.message.online === "no") { obj.command = "tftInstallSendTo"; } } const scriptPath = `script.js.${this.library.cleandp(this.namespace, false, true)}`; switch (obj.command) { case import_adminShareConfig.SENDTO_GET_PAGES_COMMAND: { let names = []; if ((_a = obj == null ? void 0 : obj.message) == null ? void 0 : _a.panelTopic) { if ((_b = this.controller) == null ? void 0 : _b.panels) { if (obj.message.panelTopic === import_adminShareConfig.ALL_PANELS_SPECIAL_ID) { const temp = /* @__PURE__ */ new Set(); this.controller.panels.forEach((a) => { const b = a.navigation.getDatabase().map((b2) => { var _a2; return (_a2 = b2 == null ? void 0 : b2.page) == null ? void 0 : _a2.name; }).filter((a2) => a2 != null); if (temp.size === 0) { for (const c of b) { if (c) { temp.add(c); } } } else { const lookup = new Set(b.filter(Boolean)); const toRemove = []; for (const t of temp) { if (!lookup.has(t)) { toRemove.push(t); } } for (const r of toRemove) { temp.delete(r); } } }); names = Array.from(temp); } else { const panel = this.controller.panels.find((a) => a.topic === obj.message.panelTopic); if (panel) { const db = panel.navigation.getDatabase(); if (db) { for (const p of db) { if (p == null ? void 0 : p.page) { names.push(p.page.name); } } } } } } } if (obj.callback) { this.sendTo(obj.from, obj.command, { result: names }, obj.callback); } break; } case import_adminShareConfig.SENDTO_GET_PAGES_All_COMMAND: { let names = []; if ((_c = this.controller) == null ? void 0 : _c.panels) { for (const panel of this.controller.panels) { if (panel) { const db = panel.navigation.getDatabase(); if (db) { for (const p of db) { if (p == null ? void 0 : p.page) { names.push(p.page.name); } } } } } names = Array.from(new Set(names)); } if (obj.callback) { this.sendTo(obj.from, obj.command, { result: names }, obj.callback); } break; } case import_adminShareConfig.SENDTO_GET_PANELS_COMMAND: { const names = []; if ((_d = this.controller) == null ? void 0 : _d.panels) { for (const p of this.controller.panels) { if (p) { names.push({ panelTopic: p.topic, friendlyName: p.friendlyName || p.name }); } } } if (obj.callback) { this.sendTo(obj.from, obj.command, { result: names }, obj.callback); } break; } case import_adminShareConfig.SAVE_PANEL_NAVIGATION_COMMAND: { if (obj.message && ((_e = this.controller) == null ? void 0 : _e.panels)) { const data = obj.message; const panel = this.controller.panels.find((a) => a.name === data.panelName); if (panel) { await panel.saveNavigationMap(data.pages); } } if (obj.callback) { this.sendTo(obj.from, obj.command, [], obj.callback); } break; } case import_adminShareConfig.SENDTO_GET_PANEL_NAVIGATION_COMMAND: { const nav = []; if ((_f = this.controller) == null ? void 0 : _f.panels) { for (const p of this.controller.panels) { if (p) { nav.push(await p.getNavigationArrayForFlow()); } } } else { break; } if (obj.callback) { this.sendTo(obj.from, obj.command, { result: nav }, obj.callback); } break; } case "getWeatherEntity": { const adapterObj = await this.getObjectViewAsync("system", "instance", { startkey: "system.adapter.", endkey: "system.adapter.\u9999" }); const adapters = []; if (adapterObj && adapterObj.rows && adapterObj.rows.length > 0) { for (const r of adapterObj.rows) { if (r && r.id && definition.weatherEntities.findIndex((a) => r.id.includes(a)) !== -1) { adapters.push(r.id.replace("system.adapter.", "")); } } } const result = adapters.sort().map((a) => { return { value: a, label: a }; }); result.unshift({ label: this.library.getTranslation("custom"), value: "" }); if (obj.callback) { this.sendTo( obj.from, obj.command, result ? result : [{ label: "Not available", value: "" }], obj.callback ); } break; } case "config": { const obj1 = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`); if (obj1 && obj1.native && JSON.stringify(obj1.native.Testconfig2) !== JSON.stringify(obj.message)) { obj1.native.Testconfig2 = obj.message; await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, obj1); } if (obj.callback) { this.sendTo(obj.from, obj.command, [], obj.callback); } break; } case "updateCustom": { if (obj.message && obj.message.state) { const state = await this.getForeignObjectAsync(obj.message.state); if (state && state.common && state.common.custom && state.common.custom[this.namespace]) { this.log.debug(`updateCustom ${JSON.stringify(state.common.custom[this.namespace])}`); } } if (obj.callback) { this.sendTo(obj.from, obj.command, [], obj.callback); } break; } case "ScriptConfigGlobal": { const manager = new import_config_manager.ConfigManager(this); try { let r = { messages: [], panelConfig: void 0 }; const config = structuredClone(obj.message.panelTopic); r = await manager.setScriptConfig({ ...obj.message, panelTopic: config }); await manager.delete(); const result = r.messages; if (obj.callback) { this.sendTo(obj.from, obj.command, result, obj.callback); } } catch (e) { this.log.error(`Error in script config processing: ${e.message}`); if (obj.callback) { this.sendTo( obj.from, obj.command, `Error in script config processing: ${e.message}`, obj.callback ); } } break; } case "ScriptConfig": { let result = ["something went wrong"]; if (obj.message) { if (this.scriptConfigBacklog.length > 3) { if (obj.callback) { this.sendTo( obj.from, obj.command, `\u26A0\uFE0F Too many configuration changes at once. Please wait a few seconds.`, obj.callback ); } break; } this.scriptConfigBacklog.push(obj); if (this.scriptConfigBacklog.length > 1) { break; } while (this.scriptConfigBacklog[0] != null) { const manager = new import_config_manager.ConfigManager(this); const obj2 = this.scriptConfigBacklog[0]; let r = { messages: [], panelConfig: void 0 }; if (obj2.message.panelTopic && Array.isArray(obj2.message.panelTopic)) { const topics = JSON.parse(JSON.stringify(obj2.message.panelTopic)); for (const a of topics) { r = await manager.setScriptConfig({ ...obj2.message, panelTopic: a }); } } else { r = await manager.setScriptConfig(obj2.message); } if (this.config.testCase) { this.testCaseConfig = [r.panelConfig]; } else { let reloaded = false; if (r.panelConfig) { const arr = await import_config_manager.ConfigManager.getConfig(this, [r.panelConfig]); if (arr && arr.length > 0) { const config = arr[0]; if (this.controller && config) { const topic = config.topic; if (topic) { const index = this.controller.panels.findIndex((a) => a.topic === topic); if (index !== -1) { const name = this.controller.panels[index].friendlyName || config.name || config.topic; await this.controller.removePanel(this.controller.panels[index]); if (this.unload) { if (obj2.callback) { this.sendTo( obj2.from, obj2.command, "Adapter is stopping", obj2.callback ); } return; } await this.delay(1500); if (this.unload) { if (obj2.callback) { this.sendTo( obj2.from, obj2.command, "Adapter is stopping", obj2.callback ); } return; } await this.controller.addPanel(config); const msg = `\u2705 Panel "${name}" reloaded with updated configuration.`; this.log.info(msg); r.messages.push(msg); reloaded = true; } else { r.messages.push( `Panel ${topic} not found in controller. Configuration saved. Adapter restart required!` ); } } else { r.messages.push( `Panel ${topic} not found in script. Configuration saved. Adapter restart required!` ); } } else { r.messages.push( this.controller ? `Controller not exist. Configuration saved. Adapter restart required!` : `Config not exist. ` ); } } else { r.messages.push(`No config found after conversion`); } } else { r.messages.push(`Please send more as 0, '', false, null or undefined!`); } if (!reloaded) { const msg = `\u274C Panel was not restarted due to configuration errors or missing panel instance. Please verify the panel topic and base configuration.`; this.log.info(msg); r.messages.push(msg); } } await manager.delete(); result = r.messages; if (obj2.callback) { this.sendTo(obj2.from, obj2.command, result, obj2.callback); } this.scriptConfigBacklog.shift(); if (this.scriptConfigBacklog.length > 0) { await this.delay(3e3); } } break; } if (obj.callback) { this.sendTo(obj.from, obj.command, "something when wrong", obj.callback); } break; } case "setPopupNotification": { if (this.controller && obj.message) { await this.controller.setPopupNotification(obj.message); } if (obj.callback) { this.sendTo(obj.from, obj.command, [], obj.callback); } break; } case "testCase": { if (obj.callback) { this.sendTo(obj.from, obj.command, { testSuccessful: this.testSuccessful }, obj.callback); } break; } case "getTasmotaDevices": { if (this.config.panels) { const devices = this.config.panels.map((a) => { return { label: `${a.ip} (${a.name})`, value: a.ip }; }); if (obj.callback) { this.sendTo(obj.from, obj.command, devices, obj.callback); } break; } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case "nsPanelInit": { if (obj.message) { try { if (obj.message.tasmotaIP && (obj.message.mqttIp || obj.message.internalServerIp) && obj.message.mqttServer != null && obj.message.mqttPort && obj.message.mqttUsername && obj.message.mqttPassword && obj.message.tasmotaTopic) { if (obj.message.mqttServer == "false" || !obj.message.mqttServer) { obj.message.mqttServer = false; } else { obj.message.mqttServer = true; } this.log.info( `Sending mqtt config & base config to tasmota: ${obj.message.tasmotaIP} with user ${obj.message.mqttUsername} && ${obj.message.mqttPassword}` ); let u = new import_url.URL( `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=status 5` ); let r = await this.fetch(u.href); if (!(0, import_function_and_const.isTasmotaStatusNet)(r)) { this.log.warn(`Device with topic ${obj.message.tasmotaTopic} not found!`); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToDeviceNotFound" }, obj.callback ); } break; } if (!r || !r.StatusNET || !r.StatusNET.Mac) { this.log.warn(`Device with topic ${obj.message.tasmotaTopic} not found!`); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToDeviceNotFound" }, obj.callback ); } break; } let mac = r.StatusNET.Mac; const topic = obj.message.tasmotaTopic; const appendix = r.StatusNET.Mac.replace(/:/g, "").slice(-6); const mqttClientId = `${this.library.cleandp(obj.message.tasmotaName)}-${appendix}`; const url = ` MqttHost ${obj.message.mqttServer ? obj.message.internalServerIp : obj.message.mqttIp}; MqttPort ${obj.message.mqttPort}; MqttUser ${obj.message.mqttUsername}; MqttPassword ${obj.message.mqttPassword}; FullTopic ${`${topic}/%prefix%/`.replaceAll("//", "/")}; MqttRetry 10; FriendlyName1 ${obj.message.tasmotaName}; Hostname ${obj.message.tasmotaName.replaceAll(/[^a-zA-Z0-9_-]/g, "_")}; MqttClient ${mqttClientId}; ${obj.message.mqttServer ? "SetOption132 1; SetOption103 1 " : "SetOption132 0; SetOption103 0"}; Restart 1`; u = new import_url.URL( `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog${encodeURIComponent(url)}` ); this.log.info( `Sending mqtt config & base config to tasmota with IP ${obj.message.tasmotaIP} and name ${obj.message.tasmotaName}.` ); await this.fetch(u.href); this.mqttClient && await this.mqttClient.waitPanelConnectAsync(topic, 6e4); u = new import_url.URL( `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog${encodeURIComponent( ` WebLog 2;SetOption111 1; template {"NAME":"${obj.message.tasmotaName}", "GPIO":[0,0,0,0,3872,0,0,0,0,0,32,0,0,0,0,225,0,480,224,1,0,0,0,33,0,0,0,0,0,0,0,0,0,0,4736,0],"FLAG":0,"BASE":1}; Module 0;${this.config.timezone ? definition.getTasmotaTimeZone(this.config.timezone) : ""}: restart 1` )}` ); await this.fetch(u.href); this.mqttClient && await this.mqttClient.waitPanelConnectAsync(topic, 6e4); u = new import_url.URL( `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=status 0` ); r = await this.fetch(u.href); if (!(0, import_function_and_const.isTasmotaStatusNet)(r)) { this.log.warn(`Device with topic ${obj.message.tasmotaTopic} not found!`); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToDeviceNotFound" }, obj.callback ); } break; } if (!r || !r.StatusNET || !r.StatusNET.Mac) { this.log.warn(`Device with topic ${obj.message.tasmotaTopic} not found!`); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToDeviceNotFound2" }, obj.callback ); } break; } const config = this.config; const panels = (_g = config.panels) != null ? _g : []; const index = panels.findIndex((a) => a.topic === obj.message.tasmotaTopic); const item = index === -1 ? { name: "", ip: "", topic: "", id: "", model: "" } : panels[index]; const ipIndex = panels.findIndex((a) => a.ip === obj.message.tasmotaIP); let update = false; if (index !== -1 && ipIndex !== index) { this.log.error("Topic and ip are not on the same panel!"); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToIpTopicDifferent" }, obj.callback ); } break; } else { update = index !== -1; } mac = r.StatusNET.Mac; item.model = obj.message.model; item.name = obj.message.tasmotaName; item.topic = topic; item.id = this.library.cleandp(mac); item.ip = r.StatusNET.IPAddress; if (index === -1) { panels.push(item); } let result = void 0; try { const url2 = `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=GetDriverVersion`; try { result = await this.fetch(url2, void 0, 3e3); } catch { } if (!result || result.nlui_driver_version !== "-1") { result = await this.fetch( "https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/main/json/version.json" ); if (!result) { this.log.error("No version found!"); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToRequestFail1" }, obj.callback ); } break; } const version = obj.message.useBetaTFT ? result[`berry-beta`].split("_")[0] : result.berry.split("_")[0]; const url3 = `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog UfsRename autoexec.be,autoexec.old; UrlFetch https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/main/tasmota/berry/${version}/autoexec.be; Restart 1`; this.log.info( `Installing berry on tasmota with IP ${obj.message.tasmotaIP}, name ${obj.message.tasmotaName}.` ); this.log.debug(`URL: ${url3}`); await this.fetch(url3); this.mqttClient && await this.mqttClient.waitPanelConnectAsync(topic, 2e4); await this.delay(7e3); } else { this.log.info( `Emulator detected on tasmota with IP ${obj.message.tasmotaIP} and name ${obj.message.tasmotaName}, skipping berry install.` ); } } catch (e) { this.log.error(`Error: while installing berry - ${e}`); } if ((result == null ? void 0 : result.nlui_driver_version) !== "-1") { try { await this.delay(3e3); const cmnd = await this.getTFTVersionOnline( obj.message.model, obj.message.useBetaTFT, this.config.forceTFTVersion, result ); if (!cmnd) { this.log.error("No version found!"); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToRequestFail2" }, obj.callback ); } break; } if (this.mqttClient) { await this.mqttClient.publish(`${topic}/cmnd/Backlog`, `${cmnd}`); await this.delay(100); await this.mqttClient.publish(`${topic}/cmnd/Backlog`, ``); } this.log.info( `Installing tft on tasmota with IP ${obj.message.tasmotaIP} and name ${obj.message.tasmotaName}.` ); } catch (e) { this.log.error(`Error: ${e}`); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToRequestFail3" }, obj.callback ); } throw e; } } await this.createConfigurationScript(item.name, item.topic); if (obj.callback) { this.sendTo( obj.from, obj.command, { result: update ? "sendToNSPanelUpdateDataSuccess" : "sendToNSPanelInitDataSuccess", native: { panels }, saveConfig: true }, obj.callback ); } } } catch (e) { this.log.error(`Error: while sending mqtt config & base config to tasmota - ${e}`); if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToRequestFail4" }, obj.callback); } } break; } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case "berryInstallSendTo": { if (obj.message) { if (obj.message.tasmotaIP) { try { let result = void 0; result = await this.fetch( "https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/main/json/version.json" ); if (!result) { this.log.error("No version found!"); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToRequestFail5" }, obj.callback ); } break; } const version = obj.message.useBetaTFT ? result[`berry-beta`].split("_")[0] : result.berry.split("_")[0]; const url = `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog UfsDelete autoexec.old; UfsRename