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,083 lines (1,079 loc) 64.3 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 utils = __toESM(require("@iobroker/adapter-core")); var import_library = require("./lib/classes/library"); var import_register = require("source-map-support/register"); var MQTT = __toESM(require("./lib/classes/mqtt")); var import_config = require("./lib/config"); 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_axios = __toESM(require("axios")); var import_url = require("url"); var fs = __toESM(require("fs")); var import_path = __toESM(require("path")); import_axios.default.defaults.timeout = 3e3; class NspanelLovelaceUi extends utils.Adapter { library; mqttClient; mqttServer; controller; unload = false; testSuccessful = true; httpServer = []; timeoutAdmin; timeoutAdmin2; timeoutAdminArray = []; intervalAdminArray = []; mainConfiguration; 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; await this.extendForeignObjectAsync(this.namespace, { type: "meta", common: { name: { en: "Nspanel Instance", de: "Nspanel Instanze" }, type: "meta.folder" }, native: {} }); this.library = new import_library.Library(this); 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.mqttIp = "127.0.0.1"; } 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) { o.native.fixBrokenCommonTypes = false; await this.extendForeignObjectAsync(`system.adapter.${this.namespace}`, o); return; } } await (0, import_readme.generateAliasDocumentation)(); if (this.config.testCase) { this.log.warn("Testcase mode!"); } this.config.Testconfig2 = []; const obj = await this.getForeignObjectAsync(this.namespace); if (obj && obj.native) { const config = []; if (obj.native.scriptConfigRaw) { 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) { const page = obj.native.scriptConfigRaw.find( (b) => b.panelTopic === a.topic ); if (page) { const c = await manager.setScriptConfig(page); if (c && c.messages && c.messages.length > 0) { if (!c.messages[0].startsWith("Panel")) { this.log.warn(c.messages[0]); } } if (c && c.panelConfig) { this.log.info(`Raw script config found for ${a.topic}`); config.push(c.panelConfig); continue; } } { const c = obj.native.scriptConfig.find( (b) => b.topic === a.topic ); if (c) { this.log.info(`Converted script config found for ${a.topic}`); config.push(c); continue; } } } this.log.warn(`No script config found for ${a.topic}`); await manager.delete(); } } const scriptConfig = config; if (scriptConfig.length === 0) { if (!this.config.testCase) { this.log.error("No compatible config found, paused!"); return; } } if (scriptConfig) { for (let b = 0; b < scriptConfig.length; b++) { for (let c = b <= 0 ? 1 : b - 1; c < scriptConfig.length; c++) { if (c === b || !scriptConfig[c] || !scriptConfig[b].pages || !scriptConfig[c].pages) { continue; } let pages = structuredClone(scriptConfig[c].pages); if (pages) { pages = pages.filter((a) => { var _a2, _b2, _c2; if (((_a2 = a.config) == null ? void 0 : _a2.card) === "screensaver" || ((_b2 = a.config) == null ? void 0 : _b2.card) === "screensaver2" || ((_c2 = a.config) == null ? void 0 : _c2.card) === "screensaver3") { return false; } if (scriptConfig[b].pages.find((b2) => b2.uniqueID === a.uniqueID)) { return false; } return true; }); scriptConfig[b].pages = scriptConfig[b].pages.concat(pages); } } } for (let b = 0; b < scriptConfig.length; b++) { const s = scriptConfig[b]; if (!s || !s.pages) { continue; } this.config.Testconfig2[b] = {}; if (!this.config.Testconfig2[b].pages) { this.config.Testconfig2[b].pages = []; } if (!this.config.Testconfig2[b].navigation) { this.config.Testconfig2[b].navigation = []; } this.config.Testconfig2[b].pages = this.config.Testconfig2[b].pages.filter( (a) => { if (s.pages.find((b2) => b2.uniqueID === a.uniqueID)) { return false; } return true; } ); this.config.Testconfig2[b].navigation = this.config.Testconfig2[b].navigation.filter((a) => { if (s.navigation && s.navigation.find((b2) => a == null || b2 == null || b2.name === a.name)) { return false; } return true; }); s.navigation = (this.config.Testconfig2[b].navigation || []).concat(s.navigation || []); s.pages = (this.config.Testconfig2[b].pages || []).concat(s.pages || []); this.config.Testconfig2[b] = { ...this.config.Testconfig2[b] || {}, ...s }; } } } if (this.config.doubleClickTime === void 0 || typeof this.config.doubleClickTime !== "number" || !(this.config.doubleClickTime > 0)) { this.config.doubleClickTime = 350; } 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(2e3); 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: {} }); this.config.Testconfig2 = import_config.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.subscript("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.config.Testconfig2 || !Array.isArray(this.config.Testconfig2) || this.config.Testconfig2.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.config.Testconfig2); 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 (((_a = p.config) == null ? void 0 : _a.card) === "screensaver" || ((_b = p.config) == null ? void 0 : _b.card) === "screensaver2" || ((_c = p.config) == null ? void 0 : _c.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}`); } } 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) }); await this.controller.init(); } 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.info(`Force an MQTT reconnect from the Nspanel with the ip ${tasmota.ip} in 10 seconds!`); await import_axios.default.get( `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! ${e}` ); } }; for (const tasmota of this.config.panels) { if (tasmota && tasmota.ip) { void _helper(tasmota); } } await this.setState("info.connection", true, true); }; /** * 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); } 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(); } for (const server of this.httpServer) { if (!server.unload) { await server.delete(); } } if (this.mqttClient) { await this.mqttClient.destroy(); } if (this.mqttServer) { this.mqttServer.destroy(); } callback(); } catch { callback(); } } //test // If you need to react to object changes, uncomment the following block and the corresponding line in the constructor. // You also need to subscribe to the objects with `this.subscribeObjects`, similar to `this.subscribeStates`. // /** // * Is called if a subscribed object changes // */ // private onObjectChange(id: string, obj: ioBroker.Object | null | undefined): void { // if (obj) { // // The object was changed // this.log.info(`object ${id} changed: ${JSON.stringify(obj)}`); // } else { // // The object was deleted // this.log.info(`object ${id} deleted`); // } // } /** * 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; if (typeof obj === "object" && obj.message) { 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 "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 "ScriptConfig": { let result = ["something went wrong"]; if (obj.message) { const manager = new import_config_manager.ConfigManager(this); let r = { messages: [], panelConfig: void 0 }; if (obj.message.panelTopic && Array.isArray(obj.message.panelTopic)) { const topics = JSON.parse(JSON.stringify(obj.message.panelTopic)); for (const a of topics) { r = await manager.setScriptConfig({ ...obj.message, panelTopic: a }); } } else { r = await manager.setScriptConfig(obj.message); } await manager.delete(); result = r.messages; } if (obj.callback) { this.sendTo(obj.from, obj.command, result, obj.callback); } break; } /*case 'RefreshDevices': { if (this.timeoutAdmin) { if (obj.callback) { this.sendTo(obj.from, obj.command, { error: 'sendToAdminRunning' }, obj.callback); break; } } const device = { id: '', name: obj.message.name, topic: obj.message.topic, ip: '' }; const mqtt = new MQTT.MQTTClientClass( this, this.config.mqttIp, this.config.mqttPort, this.config.mqttUsername, this.config.mqttPassword, this.config.mqttServer, (topic, message) => { this.log.debug(`${topic} ${message}`); }, ); await this.delay(100); const checkTasmota = async ( mqtt: MQTT.MQTTClientClass, topic: string, ): Promise<{ status: boolean; id: string; ip: string }> => { return new Promise(resolve => { this.timeoutAdmin = this.setTimeout(() => { this.timeoutAdmin = null; resolve({ status: false, id: '', ip: '' }); }, 5000); void mqtt .subscript(`${topic}/stat/STATUS0`, (_topic: string, _message: string) => { const msg = JSON.parse(_message) as STATUS0; if (msg.StatusNET) { resolve({ status: true, ip: msg.StatusNET.IPAddress, id: this.library.cleandp(msg.StatusNET.Mac, false, true), }); } }) .then(() => { void mqtt.publish(`${topic}/cmnd/STATUS0`, ''); }); }); }; const result = await checkTasmota(mqtt, device.topic); if (this.timeoutAdmin) { this.clearTimeout(this.timeoutAdmin); this.timeoutAdmin = null; } await mqtt.destroy(); if (result.status) { device.id = result.id; device.ip = result.ip; const index = this.config.panels.findIndex(a => a.topic === device.topic); //if (index !== -1) { this.config.panels[index] = device; if (obj.callback) { this.sendTo(obj.from, obj.command, { native: device }, obj.callback); this.sendTo(obj.from, obj.command, { result: 'ok' }, obj.callback); break; } //} } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: 'sendToRefreshFail' }, 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 import_axios.default.get(u.href); if (!r || !r.data || !r.data.StatusNET || !r.data.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.data.StatusNET.Mac; const topic = obj.message.tasmotaTopic; const appendix = r.data.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, "_")}; WebLog 2; 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; MqttClient ${mqttClientId};${this.config.timezone ? definition.getTasmotaTimeZone(this.config.timezone) : ""} ${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 import_axios.default.get(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 import_axios.default.get(u.href); if (!r || !r.data || !r.data.StatusNET || !r.data.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 = (_a = config.panels) != null ? _a : []; const index = panels.findIndex((a) => a.topic === obj.message.tasmotaTopic); const item = index === -1 ? { name: "", ip: "", topic: "", id: "" } : panels[index]; const ipIndex = panels.findIndex((a) => a.ip === obj.message.tasmotaIP); let update = false; if (ipIndex !== -1 && index !== -1 && ipIndex !== index) { this.log.error("Topic and ip are already in use on different panels!"); if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToNameExist" }, obj.callback); } break; } else { update = index !== -1; } mac = r.data.StatusNET.Mac; item.name = obj.message.tasmotaName; item.topic = topic; item.id = this.library.cleandp(mac); item.ip = r.data.StatusNET.IPAddress; if (index === -1) { panels.push(item); } try { const url2 = `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog UrlFetch https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be; Restart 1`; this.log.info( `Installing berry on tasmota with IP ${obj.message.tasmotaIP} and name ${obj.message.tasmotaName}.` ); await import_axios.default.get(url2); this.mqttClient && await this.mqttClient.waitPanelConnectAsync(topic, 2e4); await this.delay(7e3); } catch (e) { this.log.error(`Error: while installing berry - ${e}`); } try { const result = await import_axios.default.get( "https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/main/json/version.json" ); if (!result.data) { this.log.error("No version found!"); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToRequestFail" }, obj.callback ); } break; } const version = obj.message.useBetaTFT ? result.data["tft-beta"].split("_")[0] : result.data.tft.split("_")[0]; const fileName = `nspanel-v${version}.tft`; if (this.mqttClient) { await this.mqttClient.publish( `${topic}/cmnd/Backlog`, `FlashNextion http://nspanel.de/${fileName}` ); } 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: "sendToRequestFail" }, obj.callback ); } } 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: "sendToRequestFail" }, 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 { const url = `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog UrlFetch https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be; Restart 1`; this.log.info(`Installing berry on tasmota with IP ${obj.message.tasmotaIP}`); await import_axios.default.get(url); if (obj.callback) { this.sendTo(obj.from, obj.command, [], obj.callback); } } catch (e) { this.log.error(`Error: while installing berry - ${e}`); if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToRequestFail" }, obj.callback); } } break; } } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case "tftInstallSendTo": { if (obj.message) { if (obj.message.tasmotaIP) { try { const result = await import_axios.default.get( "https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/main/json/version.json" ); if (!result.data) { this.log.error("No version found!"); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToRequestFail" }, obj.callback ); } break; } const version = obj.message.useBetaTFT ? result.data["tft-beta"].split("_")[0] : result.data.tft.split("_")[0]; const fileName = `nspanel-v${version}.tft`; const url = `http://${obj.message.tasmotaIP}/cm?${this.config.useTasmotaAdmin ? `user=admin&password=${this.config.tasmotaAdminPassword}` : ``}&cmnd=Backlog FlashNextion http://nspanel.de/${fileName}`; this.log.debug(url); await import_axios.default.get(url); if (obj.callback) { this.sendTo(obj.from, obj.command, [], obj.callback); } } catch (e) { this.log.error(`Error: ${e}`); if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToRequestFail" }, obj.callback); } } break; } } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case "tftInstallSendToMQTT": { if (obj.message) { if (obj.message.topic) { try { const result = await import_axios.default.get( "https://raw.githubusercontent.com/ticaki/ioBroker.nspanel-lovelace-ui/main/json/version.json" ); if (!result.data) { this.log.error("No version found!"); if (obj.callback) { this.sendTo( obj.from, obj.command, { error: "sendToRequestFail" }, obj.callback ); } break; } const version = obj.message.useBetaTFT ? result.data["tft-beta"].split("_")[0] : result.data.tft.split("_")[0]; const fileName = `nspanel-v${version}.tft`; const cmnd = `FlashNextion http://nspanel.de/${fileName}`; this.log.debug(cmnd); if ((_b = this.controller) == null ? void 0 : _b.panels) { const index = this.controller.panels.findIndex((a) => a.topic === obj.message.topic); if (index !== -1) { const panel = this.controller.panels[index]; panel.sendToTasmota(`${panel.topic}/cmnd/Backlog`, cmnd); } } if (obj.callback) { this.sendTo(obj.from, obj.command, [], obj.callback); } } catch (e) { this.log.error(`Error: ${e}`); if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToRequestFail" }, obj.callback); } } break; } } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case "getRandomMqttCredentials": { if (obj.message) { const allowedChars = [ ..."abcdefghijklmnopqrstuvwxyz", ..."ABCDEFGHIJKLMNOPQRSTUVWXYZ", ..."0123456789", ..."()*+-.:<=>[]_" ]; const allowedCharsUser = [ ..."abcdefghijklmnopqrstuvwxyz", ..."ABCDEFGHIJKLMNOPQRSTUVWXYZ" // c|Yh7Pe<&1ap34t/]S&TxDwL&KDWqW-Se_D@vtXh,z]|T[RIqLgz.>^3H1j< ]; const passwordLength = 50; const usernameLength = 15; const getString = (c, length) => { let result2 = ""; for (let i = 0; i < length; i++) { const random = Math.floor(Math.random() * c.length); result2 += c[random]; } return result2; }; const result = { native: { mqttUsername: getString(allowedCharsUser, usernameLength), mqttPassword: getString(allowedChars, passwordLength), mqttPort: await this.getPortAsync(8883), saveConfig: true } }; if (obj.callback) { this.sendTo(obj.from, obj.command, result, obj.callback); } break; } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "error" }, obj.callback); } break; } case "selectPanel": { if (this.mainConfiguration && ((_c = obj.message) == null ? void 0 : _c.id)) { let msg = []; switch (obj.message.id) { case "panel": { msg = this.mainConfiguration.map((a) => { const index = this.config.panels.findIndex((b) => b.topic === a.topic); if (index !== -1) { return { value: a.topic, label: this.config.panels[index].name }; } return null; }); msg = msg.filter((a) => a); msg.sort((a, b) => a.label.localeCompare(b.label)); break; } case "uniqueID": { if (obj.message.panel) { const index = this.mainConfiguration.findIndex( (a) => a.topic === obj.message.panel ); if (index !== -1) { msg = this.mainConfiguration[index].pages.map((a) => { return { label: a.uniqueID, value: a.uniqueID }; }); msg.sort((a, b) => a.label.localeCompare(b.label)); break; } } msg = []; break; } case "navigationNames": { if (obj.message.table && Array.isArray(obj.message.table)) { msg = obj.message.table.map((a) => { return a.name; }); msg = msg.filter((a) => a && a !== obj.message.name); msg.sort((a, b) => a.localeCompare(b)); break; } msg = []; break; } } if (obj.callback) { this.sendTo(obj.from, obj.command, msg, obj.callback); break; } } this.sendTo(obj.from, obj.command, null, obj.callback); break; } case "_loadNavigationOverview": { if (this.mainConfiguration && ((_d = obj.message) == null ? void 0 : _d.panel)) { let msg = []; let useNavigation = false; let configFrom = ""; const index = this.mainConfiguration.findIndex((a) => a.topic === obj.message.panel); if (index !== -1) { let nav = []; const o = await this.getForeignObjectAsync(this.namespace); if (((_e = o == null ? void 0 : o.native) == null ? void 0 : _e.navigation) && o.native.navigation[obj.message.panel]) { nav = o.native.navigation[obj.message.panel].data; useNavigation = o.native.navigation[obj.message.panel].useNavigation; configFrom = "Adminconfiguration"; } else { nav = this.mainConfiguration[index].navigation; configFrom = "Scriptconfiguration"; } msg = nav.map((a) => { var _a2, _b2, _c2, _d2; return a ? { name: a.name, page: a.page, left1: (_a2 = a.left) == null ? void 0 : _a2.single, left2: (_b2 = a.left) == null ? void 0 : _b2.double, right1: (_c2 = a.right) == null ? void 0 : _c2.single, right2: (_d2 = a.right) == null ? void 0 : _d2.double } : null; }); msg = msg.filter((a) => a); } if (obj.callback) { this.sendTo( obj.from, obj.command, { native: { _NavigationOverviewTable: msg, _useNavigation: useNavigation, _configFrom: configFrom } }, obj.callback ); } break; } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case "_saveNavigationOverview": { if (((_f = obj.message) == null ? void 0 : _f.table) && ((_g = obj.message) == null ? void 0 : _g.panel) && this.mainConfiguration) { const o = await this.getForeignObjectAsync(this.namespace); if (o && o.native) { const index = this.mainConfiguration.findIndex((a) => a.topic === obj.message.panel); if (index !== -1) { let result = obj.message.table.map( (a) => { return a && a.name && a.page && (a.left1 || a.left2 || a.right1 || a.right2) ? { name: a.name, page: a.page, left: a.left1 || a.left2 ? { single: a.left1, double: a.left2 } : null, right: a.right1 || a.right2 ? { single: a.right1, double: a.right2 } : null } : null; } ); result = result.filter((a) => a); o.native.navigation = (_h = o.native.navigation) != null ? _h : {}; o.native.navigation[obj.message.panel] = { useNavigation: obj.message.useNavigation === "true", data: result }; await this.setForeignObjectAsync(this.namespace, o); } if (obj.callback) { this.sendTo(obj.from, obj.command, null, obj.callback); } break; } } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case "_clearNavigationOverview": { if (((_i = obj.message) == null ? void 0 : _i.table) && ((_j = obj.message) == null ? void 0 : _j.panel) && this.mainConfiguration) { const o = await this.getForeignObjectAsync(this.namespace); if (o && o.native && o.native.navigation && o.native.navigation[obj.message.panel]) { o.native.navigation[obj.message.panel] = void 0; await this.setForeignObjectAsync(this.namespace, o); } if (obj.callback) { this.sendTo( obj.from, obj.command, { native: { _NavigationOverviewTable: [], _useNavigation: false, _configFrom: "None!" } }, obj.callback ); } break; } if (obj.callback) { this.sendTo(obj.from, obj.command, { error: "sendToAnyError" }, obj.callback); } break; } case