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
JavaScript
"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