From 6b1bb5a054c98e97ff8183f321a603904798eed3 Mon Sep 17 00:00:00 2001 From: Acgua <95978245+Acgua@users.noreply.github.com> Date: Thu, 30 Mar 2023 18:42:58 +0200 Subject: [PATCH] Update --- README.md | 1 + admin/i18n/de/translations.json | 2 + admin/i18n/en/translations.json | 2 + admin/i18n/es/translations.json | 2 + admin/i18n/fr/translations.json | 2 + admin/i18n/it/translations.json | 2 + admin/i18n/nl/translations.json | 2 + admin/i18n/pl/translations.json | 2 + admin/i18n/pt/translations.json | 2 + admin/i18n/ru/translations.json | 2 + admin/i18n/uk/translations.json | 2 + admin/i18n/zh-cn/translations.json | 2 + admin/jsonConfig.json | 15 +- admin/words.js | 2 + build/lib/mqtt-server.js | 22 +- build/lib/mqtt-server.js.map | 4 +- build/main.js | 94 +++---- build/main.js.map | 4 +- io-package.json | 408 ++++++++++++++--------------- src/lib/adapter-config.d.ts | 1 + src/lib/mqtt-server.ts | 22 +- src/main.ts | 103 ++++---- 22 files changed, 371 insertions(+), 327 deletions(-) diff --git a/README.md b/README.md index e2da637..0bfd2d6 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Once this ioBroker.fully-mqtt adapter is tested accordingly and runs stable, I w - (Acgua) Removed info log 'MQTT is not activated in adapter instance settings.' - (Acgua) New object 'enabled' ('fully-mqtt.x.[Device Name].enabled') to indicate if the device is enabled in the adapter settings - (Acgua) 'alive' state is set to null if device is not enabled (to not show connection/disconnection symbols in device object 'fully-mqtt.x.[Device Name]) +- (Acgua) New option "Create default event objects at adapter start" ### 0.1.0 (2023-03-27) diff --git a/admin/i18n/de/translations.json b/admin/i18n/de/translations.json index 885f761..25ed515 100644 --- a/admin/i18n/de/translations.json +++ b/admin/i18n/de/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Info-Zustände werden damit immer aktualisiert, auch wenn sich der Wert nicht ändert", "Always update info states": "Info-Zustände immer aktualisieren", "Client and Connection errors as info in log": "Client- und Verbindungs-Fehler als Info im Log", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Standard Event-Objekte (facesDetected, hideKeyboard, usw.) werden hiermit beim ersten Adapter-Start erzeugt", + "Create default event objects at adapter start": "Standard Event-Objekte beim Adapter-Start anlegen", "Device Name": "Gerätename", "Do not process published info more than every x seconds": "Publizierte Infos nicht öfter als alle x Sekunden verarbeiten", "Do not verify user and password": "Benutzername und Passwort nicht verifizieren", diff --git a/admin/i18n/en/translations.json b/admin/i18n/en/translations.json index 56e1f96..64d2058 100644 --- a/admin/i18n/en/translations.json +++ b/admin/i18n/en/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Always update info objects, even if value did not change", "Always update info states": "Always update info states", "Client and Connection errors as info in log": "Client and Connection errors as info in log", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup", + "Create default event objects at adapter start": "Create default event objects at adapter start", "Device Name": "Device Name", "Do not process published info more than every x seconds": "Do not process published info more than every x seconds", "Do not verify user and password": "Do not verify user and password", diff --git a/admin/i18n/es/translations.json b/admin/i18n/es/translations.json index c81e78e..7ef6b42 100644 --- a/admin/i18n/es/translations.json +++ b/admin/i18n/es/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Siempre actualice los objetos de información, incluso si el valor no cambió", "Always update info states": "Actualizar siempre los estados de información", "Client and Connection errors as info in log": "Errores de cliente y conexión como información en el registro", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Cree objetos de eventos predeterminados (facesDetected, hideKeyboard, etc.) en el primer inicio del adaptador", + "Create default event objects at adapter start": "Crear objetos de eventos predeterminados al inicio del adaptador", "Device Name": "Nombre del dispositivo", "Do not process published info more than every x seconds": "No procesar información publicada más de cada x segundos", "Do not verify user and password": "No verificar usuario y contraseña", diff --git a/admin/i18n/fr/translations.json b/admin/i18n/fr/translations.json index d1da2ac..c8c2962 100644 --- a/admin/i18n/fr/translations.json +++ b/admin/i18n/fr/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Toujours mettre à jour les objets d'information, même si la valeur n'a pas changé", "Always update info states": "Toujours mettre à jour les états d'informations", "Client and Connection errors as info in log": "Erreurs de client et de connexion sous forme d'informations dans le journal", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Créer des objets d'événement par défaut (facesDetected, hideKeyboard, etc.) au premier démarrage de l'adaptateur", + "Create default event objects at adapter start": "Créer des objets d'événement par défaut au démarrage de l'adaptateur", "Device Name": "Nom de l'appareil", "Do not process published info more than every x seconds": "Ne traitez pas les informations publiées plus de toutes les x secondes", "Do not verify user and password": "Ne pas vérifier l'utilisateur et le mot de passe", diff --git a/admin/i18n/it/translations.json b/admin/i18n/it/translations.json index ab95401..d92078d 100644 --- a/admin/i18n/it/translations.json +++ b/admin/i18n/it/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Aggiorna sempre gli oggetti info, anche se il valore non è cambiato", "Always update info states": "Aggiorna sempre gli stati delle informazioni", "Client and Connection errors as info in log": "Errori del client e della connessione come informazioni nel registro", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Crea oggetti evento predefiniti (facesDetected, hideKeyboard, ecc.) al primo avvio dell'adattatore", + "Create default event objects at adapter start": "Crea oggetti evento predefiniti all'avvio dell'adattatore", "Device Name": "Nome del dispositivo", "Do not process published info more than every x seconds": "Non elaborare le informazioni pubblicate più di ogni x secondi", "Do not verify user and password": "Non verificare utente e password", diff --git a/admin/i18n/nl/translations.json b/admin/i18n/nl/translations.json index 494a842..923f6b5 100644 --- a/admin/i18n/nl/translations.json +++ b/admin/i18n/nl/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Werk info-objecten altijd bij, zelfs als de waarde niet is gewijzigd", "Always update info states": "Update de infostatussen altijd", "Client and Connection errors as info in log": "Client- en verbindingsfouten als info in logboek", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Maak standaardgebeurtenisobjecten (facesDetected, hideKeyboard, enz.) bij de eerste keer opstarten van de adapter", + "Create default event objects at adapter start": "Maak standaardgebeurtenisobjecten bij het starten van de adapter", "Device Name": "Toestelnaam", "Do not process published info more than every x seconds": "Verwerk gepubliceerde informatie niet vaker dan elke x seconden", "Do not verify user and password": "Verifieer de gebruiker en het wachtwoord niet", diff --git a/admin/i18n/pl/translations.json b/admin/i18n/pl/translations.json index 437aaa9..acfb611 100644 --- a/admin/i18n/pl/translations.json +++ b/admin/i18n/pl/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Zawsze aktualizuj obiekty informacyjne, nawet jeśli wartość się nie zmieniła", "Always update info states": "Zawsze aktualizuj stany informacji", "Client and Connection errors as info in log": "Błędy klienta i połączenia jako informacje w dzienniku", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Utwórz domyślne obiekty zdarzeń (facesDetected, hideKeyboard itp.) podczas pierwszego uruchomienia adaptera", + "Create default event objects at adapter start": "Utwórz domyślne obiekty zdarzeń podczas uruchamiania adaptera", "Device Name": "Nazwa urządzenia", "Do not process published info more than every x seconds": "Nie przetwarzaj opublikowanych informacji częściej niż co x sekund", "Do not verify user and password": "Nie weryfikuj użytkownika i hasła", diff --git a/admin/i18n/pt/translations.json b/admin/i18n/pt/translations.json index 22d0fa0..d1783b0 100644 --- a/admin/i18n/pt/translations.json +++ b/admin/i18n/pt/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Sempre atualize os objetos de informação, mesmo que o valor não tenha mudado", "Always update info states": "Sempre atualize os estados das informações", "Client and Connection errors as info in log": "Erros de cliente e conexão como informações no log", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Crie objetos de evento padrão (facesDetected, hideKeyboard, etc.) na primeira inicialização do adaptador", + "Create default event objects at adapter start": "Crie objetos de evento padrão na inicialização do adaptador", "Device Name": "Nome do dispositivo", "Do not process published info more than every x seconds": "Não processar informações publicadas mais do que a cada x segundos", "Do not verify user and password": "Não verificar usuário e senha", diff --git a/admin/i18n/ru/translations.json b/admin/i18n/ru/translations.json index 76ff3a1..4622f68 100644 --- a/admin/i18n/ru/translations.json +++ b/admin/i18n/ru/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Всегда обновлять информационные объекты, даже если значение не изменилось", "Always update info states": "Всегда обновлять информационные статусы", "Client and Connection errors as info in log": "Ошибки клиента и подключения как информация в журнале", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Создавать объекты событий по умолчанию (facesDetected, hideKeyboard и т. д.) при первом запуске адаптера.", + "Create default event objects at adapter start": "Создавать объекты событий по умолчанию при запуске адаптера", "Device Name": "Имя устройства", "Do not process published info more than every x seconds": "Не обрабатывать опубликованную информацию чаще, чем каждые x секунд", "Do not verify user and password": "Не проверять пользователя и пароль", diff --git a/admin/i18n/uk/translations.json b/admin/i18n/uk/translations.json index 8c39d13..7edaf3b 100644 --- a/admin/i18n/uk/translations.json +++ b/admin/i18n/uk/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "Завжди оновлюйте інформаційні об’єкти, навіть якщо значення не змінилося", "Always update info states": "Завжди оновлюйте статуси інформації", "Client and Connection errors as info in log": "Помилки клієнта та підключення як інформація в журналі", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "Створення об’єктів подій за замовчуванням (facesDetected, hideKeyboard тощо) під час першого запуску адаптера", + "Create default event objects at adapter start": "Створюйте об’єкти подій за замовчуванням під час запуску адаптера", "Device Name": "Ім'я пристрою", "Do not process published info more than every x seconds": "Не обробляйте опубліковану інформацію частіше ніж кожні x секунд", "Do not verify user and password": "Не перевіряйте користувача та пароль", diff --git a/admin/i18n/zh-cn/translations.json b/admin/i18n/zh-cn/translations.json index 856dd5f..2fd560c 100644 --- a/admin/i18n/zh-cn/translations.json +++ b/admin/i18n/zh-cn/translations.json @@ -4,6 +4,8 @@ "Always update info objects, even if value did not change": "始终更新信息对象,即使值没有改变", "Always update info states": "始终更新信息状态", "Client and Connection errors as info in log": "客户端和连接错误作为日志中的信息", + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": "在第一次适配器启动时创建默认事件对象(facesDetected、hideKeyboard 等)", + "Create default event objects at adapter start": "在适配器启动时创建默认事件对象", "Device Name": "设备名称", "Do not process published info more than every x seconds": "不要超过每 x 秒处理一次发布的信息", "Do not verify user and password": "不验证用户和密码", diff --git a/admin/jsonConfig.json b/admin/jsonConfig.json index 0781838..a3f1592 100644 --- a/admin/jsonConfig.json +++ b/admin/jsonConfig.json @@ -167,7 +167,7 @@ "label": "Do not verify user and password", "help": "This disables the username and password verification", "default": true - }, + }, "mqttUser": { "type": "text", "label": "User name", @@ -178,7 +178,7 @@ "type": "password", "label": "Password", "help": "MQTT Password", - "hidden": "data.mqttDoNotVerifyUserPw" + "hidden": "data.mqttDoNotVerifyUserPw" } } }, @@ -219,6 +219,15 @@ "md": 7, "lg": 5 }, + "mqttCreateDefaultEventObjects": { + "type": "checkbox", + "label": "Create default event objects at adapter start", + "help": "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup", + "default": true, + "sm": 12, + "md": 7, + "lg": 5 + }, "header_RESTAPI": { "newLine": "true", "type": "header", @@ -235,7 +244,7 @@ "sm": 12, "md": 6, "lg": 3 - } + } } } } diff --git a/admin/words.js b/admin/words.js index d4085b2..d1781d2 100644 --- a/admin/words.js +++ b/admin/words.js @@ -13,6 +13,8 @@ systemDictionary = { "Always update info objects, even if value did not change": {"en": "Always update info objects, even if value did not change", "de": "Info-Zustände werden damit immer aktualisiert, auch wenn sich der Wert nicht ändert", "ru": "Всегда обновлять информационные объекты, даже если значение не изменилось", "pt": "Sempre atualize os objetos de informação, mesmo que o valor não tenha mudado", "nl": "Werk info-objecten altijd bij, zelfs als de waarde niet is gewijzigd", "fr": "Toujours mettre à jour les objets d'information, même si la valeur n'a pas changé", "it": "Aggiorna sempre gli oggetti info, anche se il valore non è cambiato", "es": "Siempre actualice los objetos de información, incluso si el valor no cambió", "pl": "Zawsze aktualizuj obiekty informacyjne, nawet jeśli wartość się nie zmieniła", "uk": "Завжди оновлюйте інформаційні об’єкти, навіть якщо значення не змінилося", "zh-cn": "始终更新信息对象,即使值没有改变"}, "Always update info states": { "en": "Always update info states", "de": "Info-Zustände immer aktualisieren", "ru": "Всегда обновлять информационные статусы", "pt": "Sempre atualize os estados das informações", "nl": "Update de infostatussen altijd", "fr": "Toujours mettre à jour les états d'informations", "it": "Aggiorna sempre gli stati delle informazioni", "es": "Actualizar siempre los estados de información", "pl": "Zawsze aktualizuj stany informacji", "uk": "Завжди оновлюйте статуси інформації", "zh-cn": "始终更新信息状态"}, "Client and Connection errors as info in log": { "en": "Client and Connection errors as info in log", "de": "Client- und Verbindungs-Fehler als Info im Log", "ru": "Ошибки клиента и подключения как информация в журнале", "pt": "Erros de cliente e conexão como informações no log", "nl": "Client- en verbindingsfouten als info in logboek", "fr": "Erreurs de client et de connexion sous forme d'informations dans le journal", "it": "Errori del client e della connessione come informazioni nel registro", "es": "Errores de cliente y conexión como información en el registro", "pl": "Błędy klienta i połączenia jako informacje w dzienniku", "uk": "Помилки клієнта та підключення як інформація в журналі", "zh-cn": "客户端和连接错误作为日志中的信息"}, + "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup": {"en": "Create default event objects (facesDetected, hideKeyboard, etc.) at the first adapter startup", "de": "Standard Event-Objekte (facesDetected, hideKeyboard, usw.) werden hiermit beim ersten Adapter-Start erzeugt", "ru": "Создавать объекты событий по умолчанию (facesDetected, hideKeyboard и т. д.) при первом запуске адаптера.", "pt": "Crie objetos de evento padrão (facesDetected, hideKeyboard, etc.) na primeira inicialização do adaptador", "nl": "Maak standaardgebeurtenisobjecten (facesDetected, hideKeyboard, enz.) bij de eerste keer opstarten van de adapter", "fr": "Créer des objets d'événement par défaut (facesDetected, hideKeyboard, etc.) au premier démarrage de l'adaptateur", "it": "Crea oggetti evento predefiniti (facesDetected, hideKeyboard, ecc.) al primo avvio dell'adattatore", "es": "Cree objetos de eventos predeterminados (facesDetected, hideKeyboard, etc.) en el primer inicio del adaptador", "pl": "Utwórz domyślne obiekty zdarzeń (facesDetected, hideKeyboard itp.) podczas pierwszego uruchomienia adaptera", "uk": "Створення об’єктів подій за замовчуванням (facesDetected, hideKeyboard тощо) під час першого запуску адаптера", "zh-cn": "在第一次适配器启动时创建默认事件对象(facesDetected、hideKeyboard 等)"}, + "Create default event objects at adapter start": {"en": "Create default event objects at adapter start", "de": "Standard Event-Objekte beim Adapter-Start anlegen", "ru": "Создавать объекты событий по умолчанию при запуске адаптера", "pt": "Crie objetos de evento padrão na inicialização do adaptador", "nl": "Maak standaardgebeurtenisobjecten bij het starten van de adapter", "fr": "Créer des objets d'événement par défaut au démarrage de l'adaptateur", "it": "Crea oggetti evento predefiniti all'avvio dell'adattatore", "es": "Crear objetos de eventos predeterminados al inicio del adaptador", "pl": "Utwórz domyślne obiekty zdarzeń podczas uruchamiania adaptera", "uk": "Створюйте об’єкти подій за замовчуванням під час запуску адаптера", "zh-cn": "在适配器启动时创建默认事件对象"}, "Device Name": { "en": "Device Name", "de": "Gerätename", "ru": "Имя устройства", "pt": "Nome do dispositivo", "nl": "Toestelnaam", "fr": "Nom de l'appareil", "it": "Nome del dispositivo", "es": "Nombre del dispositivo", "pl": "Nazwa urządzenia", "uk": "Ім'я пристрою", "zh-cn": "设备名称"}, "Do not process published info more than every x seconds": {"en": "Do not process published info more than every x seconds", "de": "Publizierte Infos nicht öfter als alle x Sekunden verarbeiten", "ru": "Не обрабатывать опубликованную информацию чаще, чем каждые x секунд", "pt": "Não processar informações publicadas mais do que a cada x segundos", "nl": "Verwerk gepubliceerde informatie niet vaker dan elke x seconden", "fr": "Ne traitez pas les informations publiées plus de toutes les x secondes", "it": "Non elaborare le informazioni pubblicate più di ogni x secondi", "es": "No procesar información publicada más de cada x segundos", "pl": "Nie przetwarzaj opublikowanych informacji częściej niż co x sekund", "uk": "Не обробляйте опубліковану інформацію частіше ніж кожні x секунд", "zh-cn": "不要超过每 x 秒处理一次发布的信息"}, "Do not verify user and password": { "en": "Do not verify user and password", "de": "Benutzername und Passwort nicht verifizieren", "ru": "Не проверять пользователя и пароль", "pt": "Não verificar usuário e senha", "nl": "Verifieer de gebruiker en het wachtwoord niet", "fr": "Ne pas vérifier l'utilisateur et le mot de passe", "it": "Non verificare utente e password", "es": "No verificar usuario y contraseña", "pl": "Nie weryfikuj użytkownika i hasła", "uk": "Не перевіряйте користувача та пароль", "zh-cn": "不验证用户和密码"}, diff --git a/build/lib/mqtt-server.js b/build/lib/mqtt-server.js index e6d0b30..b9a7bf9 100644 --- a/build/lib/mqtt-server.js +++ b/build/lib/mqtt-server.js @@ -63,13 +63,13 @@ class MqttServer { if (!this.adapter.isIpAddressValid(ip)) ip === void 0; } - if (ip && !Object.keys(this.adapter.fullys).includes(ip)) { + if (ip && !Object.keys(this.adapter.fullysEnbl).includes(ip)) { this.adapter.log.error(`[MQTT] Client ${client.id} not authorized: ${ip} is not an active Fully device IP per adapter settings.`); this.notAuthorizedClients.push(client.id); callback(null, false); return; } - const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`; + const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`; this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`); if (ip) this.devices[client.id].ip = ip; @@ -99,7 +99,7 @@ class MqttServer { if (!this.devices[client.id]) this.devices[client.id] = {}; const ip = this.devices[client.id].ip; - const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`; + const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`; this.adapter.log.debug(`[MQTT] Client ${ipMsg} connected to broker ${this.aedes.id}`); this.adapter.log.info(`[MQTT]\u{1F517} Client ${ipMsg} successfully connected.`); this.setIsAlive(client.id, true, "client connected"); @@ -125,8 +125,8 @@ class MqttServer { return; } const ip = info.ip4; - const devMsg = `${this.adapter.fullys[ip].name} (${ip})`; - if (!Object.keys(this.adapter.fullys).includes(ip)) { + const devMsg = `${this.adapter.fullysEnbl[ip].name} (${ip})`; + if (!Object.keys(this.adapter.fullysEnbl).includes(ip)) { this.adapter.log.error(`[MQTT] Client ${devMsg} Packet rejected: IP is not allowed per adapter settings. ${client.id}`); return; } @@ -142,7 +142,7 @@ class MqttServer { } this.devices[client.id].previousInfoPublishTime = Date.now(); if (!this.devices[client.id].mqttFirstReceived) { - this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullys[ip].name} = ${ip}`); + this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} = ${ip}`); this.devices[client.id].mqttFirstReceived = true; } const result = { @@ -180,7 +180,7 @@ class MqttServer { cmd: msg.event }; if (!this.devices[client.id].mqttFirstReceived) { - this.adapter.log.info(`[MQTT] \u{1F517} Client ${client.id} = ${this.adapter.fullys[ip].name} (${ip})`); + this.adapter.log.info(`[MQTT] \u{1F517} Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} (${ip})`); this.devices[client.id].mqttFirstReceived = true; } this.adapter.onMqttEvent(result); @@ -194,7 +194,7 @@ class MqttServer { }); this.aedes.on("clientDisconnect", (client) => { const ip = this.devices[client.id].ip; - const logMsgName = ip ? this.adapter.fullys[ip].name : client.id; + const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id; if (this.adapter.config.mqttConnErrorsAsInfo) { this.adapter.log.info(`[MQTT] Client ${logMsgName} disconnected.`); } else { @@ -206,7 +206,7 @@ class MqttServer { if (this.notAuthorizedClients.includes(client.id)) return; const ip = this.devices[client.id].ip; - const logMsgName = ip ? this.adapter.fullys[ip].name : client.id; + const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id; if (this.adapter.config.mqttConnErrorsAsInfo) { this.adapter.log.info(`[MQTT] ${logMsgName}: Client error - ${e.message}`); } else { @@ -217,7 +217,7 @@ class MqttServer { }); this.aedes.on("connectionError", (client, e) => { const ip = this.devices[client.id].ip; - const logMsgName = ip ? this.adapter.fullys[ip].name : client.id; + const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id; if (this.adapter.config.mqttConnErrorsAsInfo) { this.adapter.log.info(`[MQTT] ${logMsgName}: Connection error - ${e.message}`); } else { @@ -261,7 +261,7 @@ class MqttServer { async scheduleCheckIfStillActive(clientId) { try { const ip = this.devices[clientId].ip; - const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`; + const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${clientId} (IP unknown)`; if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate); if (!this.devices[clientId]) diff --git a/build/lib/mqtt-server.js.map b/build/lib/mqtt-server.js.map index bf5cad0..30c60b3 100644 --- a/build/lib/mqtt-server.js.map +++ b/build/lib/mqtt-server.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../src/lib/mqtt-server.ts"], - "sourcesContent": ["import Aedes from 'aedes';\nimport net from 'net';\nimport { FullyMqtt } from '../main';\nimport { IMqttDevice } from './interfaces';\n//import { inspect } from 'util';\n\nexport class MqttServer {\n private readonly adapter: FullyMqtt;\n private server: net.Server;\n private aedes: Aedes;\n public devices: { [mqttClientId: string]: IMqttDevice }; // {}\n private port = -1;\n private notAuthorizedClients: string[] = []; // to avoid multiple log lines\n\n /**\n * Constructor\n */\n public constructor(adapter: FullyMqtt) {\n this.adapter = adapter;\n //this.server = new net.Server();\n this.aedes = new Aedes();\n /** @ts-expect-error - https://github.com/moscajs/aedes/issues/801 */\n this.server = net.createServer(undefined, this.aedes.handle);\n this.devices = {}; // key = MQTT Client ID, property: IMqttDevice\n }\n\n /**\n * Listen\n */\n public start(): void {\n try {\n /**\n * Port\n */\n this.port = this.adapter.config.mqttPort;\n /**\n * #############################################################\n * For Developer only: change port if in dev environment\n * #############################################################\n */\n if (this.adapter.adapterDir.includes('/.dev-server/default/node_modules')) {\n this.port = 3012;\n this.adapter.log.warn(`DEVELOPER: Port changed to ${this.port} as we are in DEV Environment! If you see this log message, please open an issue on Github.`);\n }\n\n /**\n * Start Listening\n */\n this.server.listen(this.port, () => {\n this.adapter.log.info(`\uD83D\uDE80 MQTT Server started and is listening on port ${this.port}.`);\n });\n\n /**\n * Verify authorization\n * This fires first and before this.aedes.on('client', (client) ...\n * https://github.com/moscajs/aedes/blob/main/docs/Aedes.md#handler-authenticate-client-username-password-callback\n */\n this.aedes.authenticate = (client, username, password, callback) => {\n try {\n // If we saw client before and is not authorized\n if (this.notAuthorizedClients.includes(client.id)) {\n callback(null, false);\n return;\n }\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n /**\n * Get IP\n * This rather complicated way is needed, see https://github.com/moscajs/aedes/issues/186\n * Not sure if this always works, but client.req was undefined in my test - which is suggested in https://github.com/moscajs/aedes/issues/527\n */\n let ip: string | undefined = undefined;\n if (client.conn && 'remoteAddress' in client.conn && typeof client.conn.remoteAddress === 'string') {\n const ipSource = client.conn.remoteAddress; // like: ::ffff:192.168.10.101\n this.adapter.log.debug(`[MQTT] client.conn.remoteAddress = \"${ipSource}\" - ${client.id}`);\n ip = ipSource.substring(ipSource.lastIndexOf(':') + 1); // get everything after last \":\"\n if (!this.adapter.isIpAddressValid(ip)) ip === undefined;\n }\n // Check if IP is an active device IP\n if (ip && !Object.keys(this.adapter.fullys).includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${client.id} not authorized: ${ip} is not an active Fully device IP per adapter settings.`);\n this.notAuthorizedClients.push(client.id);\n callback(null, false);\n return;\n }\n\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`);\n if (ip) this.devices[client.id].ip = ip;\n\n /**\n * Verify User and Password\n */\n if (!this.adapter.config.mqttDoNotVerifyUserPw) {\n // Username\n if (username !== this.adapter.config.mqttUser) {\n this.adapter.log.warn(`MQTT Client ${ipMsg} Authorization rejected: received user name '${username}' does not match '${this.adapter.config.mqttUser}' in adapter settings.`);\n callback(null, false);\n return;\n }\n // Password\n if (password.toString() !== this.adapter.config.mqttPassword) {\n this.adapter.log.warn(`MQTT Client ${ipMsg} Authorization rejected: received password does not match with password in adapter settings.`);\n callback(null, false);\n return;\n }\n }\n this.adapter.log.info(`\uD83D\uDD11 MQTT Client ${ipMsg} successfully authenticated.`);\n callback(null, true);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n callback(null, false);\n }\n };\n\n /**\n * fired when a client connects\n */\n this.aedes.on('client', (client) => {\n try {\n if (!client) return;\n\n // Create device entry with id as key, if not yet existing (should have been set in this.aedes.authenticate already)\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // IP\n const ip = this.devices[client.id].ip;\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n\n this.adapter.log.debug(`[MQTT] Client ${ipMsg} connected to broker ${this.aedes.id}`);\n this.adapter.log.info(`[MQTT]\uD83D\uDD17 Client ${ipMsg} successfully connected.`);\n //this.adapter.log.debug(inspect(client)); //https://stackoverflow.com/a/31557814\n\n // set isAlive\n this.setIsAlive(client.id, true, 'client connected');\n\n // Schedule check if still alive\n this.scheduleCheckIfStillActive(client.id);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client publishes a message packet on the topic\n */\n this.aedes.on('publish', (packet, client) => {\n try {\n if (!client || !packet) return;\n\n this.setIsAlive(client.id, true, 'client published message');\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // QOS is always 1 per Fully documentation\n if (packet.qos !== 1) return;\n\n if (packet.retain) {\n /**\n * Device Info coming in...\n * Per fully documentation: The complete device info will be published every 60 seconds as fully/deviceInfo/[deviceId] topic (retaining, QOS=1).\n */\n\n // Payload as object\n const info = JSON.parse(packet.payload.toString());\n\n // Verification of device info packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Device Info Topic\" settings.\n if (!('startUrl' in info) && !('ip4' in info)) {\n this.adapter.log.error(`[MQTT] Packet rejected: ${info.ip4} - Info packet expected, but ip4 and startUrl is not defined in packet. ${info.deviceId}`);\n return;\n }\n\n // IP\n const ip = info.ip4;\n const devMsg = `${this.adapter.fullys[ip].name} (${ip})`;\n // Check IP - already done in this.aedes.authenticate, but just in case we were unable to get ip there\n if (!Object.keys(this.adapter.fullys).includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${devMsg} Packet rejected: IP is not allowed per adapter settings. ${client.id}`);\n return;\n }\n this.devices[client.id].ip = ip;\n\n // Slow down: Don't accept info event more often than x seconds\n // Per Fully doc, should not come in more often than 60s anyway...\n const prevTime = this.devices[client.id].previousInfoPublishTime;\n const limit = this.adapter.config.mqttPublishedInfoDelay * 1000; // milliseconds\n if (prevTime && prevTime !== 0) {\n if (Date.now() - prevTime < limit) {\n const diffMs = Date.now() - prevTime;\n this.adapter.log.silly(`[MQTT] ${devMsg} Packet rejected: Last packet came in ${diffMs}ms (${Math.round(diffMs / 1000)}s) ago...`);\n return;\n }\n }\n this.devices[client.id].previousInfoPublishTime = Date.now(); // set for future events\n\n /**\n * First time received device info incl. IP address etc.\n */\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullys[ip].name} = ${ip}`);\n // set to true\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttInfo()\n */\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n infoObj: info,\n };\n this.adapter.onMqttInfo(result);\n } else if (packet.qos === 1 && !packet.retain) {\n /**\n * Event coming in...\n * Per fully documentation: Events will be published as fully/event/[eventId]/[deviceId] topic (non-retaining, QOS=1).\n */\n // {\"deviceId\":\"xxxxxxxx-xxxxxxxx\",\"event\":\"screenOn\"}\n // NOTE: Device ID is different to client id, we actually disregard deviceId\n const msg = JSON.parse(packet.payload.toString());\n\n // Verification of event packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Event Topic\" settings.\n if (!('event' in msg)) {\n this.adapter.log.error(`[MQTT] Packet rejected: Event packet expected, but event is not defined in packet. ${client.id}`);\n return;\n }\n\n // Disregard first event once connected: mqttConnected\n if (msg.event === 'mqttConnected') {\n this.adapter.log.silly(`[MQTT] Client Publish Event: Disregard mqttConnected event - ${msg.deviceId}`);\n return;\n }\n\n // Get IP\n if (!this.devices[client.id]) {\n this.adapter.log.info(`[MQTT] Client Publish Event: Device ID and according IP not yet seen thru \"Publish Info\"`);\n this.adapter.log.info(`[MQTT] We wait until first info is published. ${msg.deviceId}`);\n return;\n }\n const ip = this.devices[client.id].ip ? this.devices[client.id].ip : '';\n if (ip === '' || typeof ip !== 'string') {\n this.adapter.log.debug(`[MQTT] Client Publish Event: IP address could not be determined. - Client ID: ${client.id}`);\n this.adapter.log.debug(`[MQTT] Please be patient until first MQTT info packet coming in (takes up to 1 minute)`);\n return; // Disregard since IP is unknown!\n }\n\n // Call function\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n cmd: msg.event,\n };\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.info(`[MQTT] \uD83D\uDD17 Client ${client.id} = ${this.adapter.fullys[ip].name} (${ip})`);\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttEvent()\n */\n this.adapter.onMqttEvent(result);\n } else {\n // Ignore\n return;\n }\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client disconnects\n */\n this.aedes.on('clientDisconnect', (client) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] Client ${logMsgName} disconnected.`);\n } else {\n this.adapter.log.error(`[MQTT] Client ${logMsgName} disconnected.`);\n }\n this.setIsAlive(client.id, false, 'client disconnected');\n });\n\n /**\n * fired on client error\n */\n this.aedes.on('clientError', (client, e) => {\n if (this.notAuthorizedClients.includes(client.id)) return; // Error msg was already thrown in aedes.authenticate() before\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Client error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'client error');\n });\n\n this.aedes.on('connectionError', (client, e) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullys[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Connection error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'connection error');\n });\n\n /**\n * fired on server error\n */\n this.server.on('error', (e: any) => {\n if (e instanceof Error && e.message.startsWith('listen EADDRINUSE')) {\n this.adapter.log.debug(`[MQTT] Cannot start server - ${e.message}`);\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - Port ${this.port} is already in use. Try a different port!`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - ${e.message}`);\n }\n this.terminate();\n });\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * If Client is alive or not\n */\n private setIsAlive(clientId: string, isAlive: true | false, msg: string): void {\n if (isAlive) this.devices[clientId].lastTimeActive = Date.now();\n this.devices[clientId].isActive = isAlive;\n\n const ip = this.devices[clientId]?.ip;\n if (ip) {\n // Call Adapter function onMqttAliveChange()\n this.adapter.onMqttAlive(ip, isAlive, msg);\n if (isAlive) {\n this.scheduleCheckIfStillActive(clientId); // restart timer\n } else {\n // clear timer\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n }\n } else {\n this.adapter.log.debug(`[MQTT] isAlive changed to ${isAlive}, but IP of client ${clientId} is still unknown.`);\n }\n }\n\n /**\n * Schedule: Check if MQTT topic was sent last x seconds ago\n * @param ip IP Address\n * @returns void\n */\n private async scheduleCheckIfStillActive(clientId: string): Promise {\n try {\n const ip = this.devices[clientId].ip;\n const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`;\n // this.adapter.log.debug(`[MQTT] ${ipMsg}: - Start scheduleCheckIfStillActive`);\n\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n\n if (!this.devices[clientId]) this.devices[clientId] = {};\n\n const interval = 70 * 1000; // every 60s + 10s buffer\n this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => {\n try {\n const lastTimeActive = this.devices[clientId].lastTimeActive;\n if (!lastTimeActive) return;\n const diff = Date.now() - lastTimeActive;\n if (diff > 70000) {\n this.adapter.log.debug(`[MQTT] ${ipMsg} NOT ALIVE - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, false, 'client did not send message for more than 70 seconds');\n } else {\n this.adapter.log.warn(`[MQTT] ${ipMsg} Please open a issue on Github, this should never happen: scheduleCheckIfStillActive() timeout, and last contact was less than 70s ago.`);\n this.adapter.log.warn(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, true, `alive check is successful (last contact: ${Math.round(diff / 1000)}s ago)`);\n }\n // Call function again since we are in callback of timeout\n this.scheduleCheckIfStillActive(clientId);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }, interval);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * Terminate MQTT Server and close all...\n */\n public terminate(): void {\n this.adapter.log.info(`[MQTT] Disconnect all clients and close server`);\n // isAlive\n for (const clientId in this.devices) {\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n this.setIsAlive(clientId, false, 'MQTT server was terminated');\n }\n\n if (this.aedes) {\n this.aedes.close(() => {\n this.adapter.log.debug('[MQTT] aedes.close() succeeded');\n if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n });\n } else if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n }\n}\n"], - "mappings": "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", + "sourcesContent": ["import Aedes from 'aedes';\nimport net from 'net';\nimport { FullyMqtt } from '../main';\nimport { IMqttDevice } from './interfaces';\n//import { inspect } from 'util';\n\nexport class MqttServer {\n private readonly adapter: FullyMqtt;\n private server: net.Server;\n private aedes: Aedes;\n public devices: { [mqttClientId: string]: IMqttDevice }; // {}\n private port = -1;\n private notAuthorizedClients: string[] = []; // to avoid multiple log lines\n\n /**\n * Constructor\n */\n public constructor(adapter: FullyMqtt) {\n this.adapter = adapter;\n //this.server = new net.Server();\n this.aedes = new Aedes();\n /** @ts-expect-error - https://github.com/moscajs/aedes/issues/801 */\n this.server = net.createServer(undefined, this.aedes.handle);\n this.devices = {}; // key = MQTT Client ID, property: IMqttDevice\n }\n\n /**\n * Listen\n */\n public start(): void {\n try {\n /**\n * Port\n */\n this.port = this.adapter.config.mqttPort;\n /**\n * #############################################################\n * For Developer only: change port if in dev environment\n * #############################################################\n */\n if (this.adapter.adapterDir.includes('/.dev-server/default/node_modules')) {\n this.port = 3012;\n this.adapter.log.warn(`DEVELOPER: Port changed to ${this.port} as we are in DEV Environment! If you see this log message, please open an issue on Github.`);\n }\n\n /**\n * Start Listening\n */\n this.server.listen(this.port, () => {\n this.adapter.log.info(`\uD83D\uDE80 MQTT Server started and is listening on port ${this.port}.`);\n });\n\n /**\n * Verify authorization\n * This fires first and before this.aedes.on('client', (client) ...\n * https://github.com/moscajs/aedes/blob/main/docs/Aedes.md#handler-authenticate-client-username-password-callback\n */\n this.aedes.authenticate = (client, username, password, callback) => {\n try {\n // If we saw client before and is not authorized\n if (this.notAuthorizedClients.includes(client.id)) {\n callback(null, false);\n return;\n }\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n /**\n * Get IP\n * This rather complicated way is needed, see https://github.com/moscajs/aedes/issues/186\n * Not sure if this always works, but client.req was undefined in my test - which is suggested in https://github.com/moscajs/aedes/issues/527\n */\n let ip: string | undefined = undefined;\n if (client.conn && 'remoteAddress' in client.conn && typeof client.conn.remoteAddress === 'string') {\n const ipSource = client.conn.remoteAddress; // like: ::ffff:192.168.10.101\n this.adapter.log.debug(`[MQTT] client.conn.remoteAddress = \"${ipSource}\" - ${client.id}`);\n ip = ipSource.substring(ipSource.lastIndexOf(':') + 1); // get everything after last \":\"\n if (!this.adapter.isIpAddressValid(ip)) ip === undefined;\n }\n // Check if IP is an active device IP\n if (ip && !Object.keys(this.adapter.fullysEnbl).includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${client.id} not authorized: ${ip} is not an active Fully device IP per adapter settings.`);\n this.notAuthorizedClients.push(client.id);\n callback(null, false);\n return;\n }\n\n const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`);\n if (ip) this.devices[client.id].ip = ip;\n\n /**\n * Verify User and Password\n */\n if (!this.adapter.config.mqttDoNotVerifyUserPw) {\n // Username\n if (username !== this.adapter.config.mqttUser) {\n this.adapter.log.warn(`MQTT Client ${ipMsg} Authorization rejected: received user name '${username}' does not match '${this.adapter.config.mqttUser}' in adapter settings.`);\n callback(null, false);\n return;\n }\n // Password\n if (password.toString() !== this.adapter.config.mqttPassword) {\n this.adapter.log.warn(`MQTT Client ${ipMsg} Authorization rejected: received password does not match with password in adapter settings.`);\n callback(null, false);\n return;\n }\n }\n this.adapter.log.info(`\uD83D\uDD11 MQTT Client ${ipMsg} successfully authenticated.`);\n callback(null, true);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n callback(null, false);\n }\n };\n\n /**\n * fired when a client connects\n */\n this.aedes.on('client', (client) => {\n try {\n if (!client) return;\n\n // Create device entry with id as key, if not yet existing (should have been set in this.aedes.authenticate already)\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // IP\n const ip = this.devices[client.id].ip;\n const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`;\n\n this.adapter.log.debug(`[MQTT] Client ${ipMsg} connected to broker ${this.aedes.id}`);\n this.adapter.log.info(`[MQTT]\uD83D\uDD17 Client ${ipMsg} successfully connected.`);\n //this.adapter.log.debug(inspect(client)); //https://stackoverflow.com/a/31557814\n\n // set isAlive\n this.setIsAlive(client.id, true, 'client connected');\n\n // Schedule check if still alive\n this.scheduleCheckIfStillActive(client.id);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client publishes a message packet on the topic\n */\n this.aedes.on('publish', (packet, client) => {\n try {\n if (!client || !packet) return;\n\n this.setIsAlive(client.id, true, 'client published message');\n\n // Create device entry with id as key, if not yet existing\n if (!this.devices[client.id]) this.devices[client.id] = {};\n\n // QOS is always 1 per Fully documentation\n if (packet.qos !== 1) return;\n\n if (packet.retain) {\n /**\n * Device Info coming in...\n * Per fully documentation: The complete device info will be published every 60 seconds as fully/deviceInfo/[deviceId] topic (retaining, QOS=1).\n */\n\n // Payload as object\n const info = JSON.parse(packet.payload.toString());\n\n // Verification of device info packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Device Info Topic\" settings.\n if (!('startUrl' in info) && !('ip4' in info)) {\n this.adapter.log.error(`[MQTT] Packet rejected: ${info.ip4} - Info packet expected, but ip4 and startUrl is not defined in packet. ${info.deviceId}`);\n return;\n }\n\n // IP\n const ip = info.ip4;\n const devMsg = `${this.adapter.fullysEnbl[ip].name} (${ip})`;\n // Check IP - already done in this.aedes.authenticate, but just in case we were unable to get ip there\n if (!Object.keys(this.adapter.fullysEnbl).includes(ip)) {\n this.adapter.log.error(`[MQTT] Client ${devMsg} Packet rejected: IP is not allowed per adapter settings. ${client.id}`);\n return;\n }\n this.devices[client.id].ip = ip;\n\n // Slow down: Don't accept info event more often than x seconds\n // Per Fully doc, should not come in more often than 60s anyway...\n const prevTime = this.devices[client.id].previousInfoPublishTime;\n const limit = this.adapter.config.mqttPublishedInfoDelay * 1000; // milliseconds\n if (prevTime && prevTime !== 0) {\n if (Date.now() - prevTime < limit) {\n const diffMs = Date.now() - prevTime;\n this.adapter.log.silly(`[MQTT] ${devMsg} Packet rejected: Last packet came in ${diffMs}ms (${Math.round(diffMs / 1000)}s) ago...`);\n return;\n }\n }\n this.devices[client.id].previousInfoPublishTime = Date.now(); // set for future events\n\n /**\n * First time received device info incl. IP address etc.\n */\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} = ${ip}`);\n // set to true\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttInfo()\n */\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n infoObj: info,\n };\n this.adapter.onMqttInfo(result);\n } else if (packet.qos === 1 && !packet.retain) {\n /**\n * Event coming in...\n * Per fully documentation: Events will be published as fully/event/[eventId]/[deviceId] topic (non-retaining, QOS=1).\n */\n // {\"deviceId\":\"xxxxxxxx-xxxxxxxx\",\"event\":\"screenOn\"}\n // NOTE: Device ID is different to client id, we actually disregard deviceId\n const msg = JSON.parse(packet.payload.toString());\n\n // Verification of event packet\n // We don't use topic to check since we do not want to rely on user's input in Fully Browser \"MQTT Event Topic\" settings.\n if (!('event' in msg)) {\n this.adapter.log.error(`[MQTT] Packet rejected: Event packet expected, but event is not defined in packet. ${client.id}`);\n return;\n }\n\n // Disregard first event once connected: mqttConnected\n if (msg.event === 'mqttConnected') {\n this.adapter.log.silly(`[MQTT] Client Publish Event: Disregard mqttConnected event - ${msg.deviceId}`);\n return;\n }\n\n // Get IP\n if (!this.devices[client.id]) {\n this.adapter.log.info(`[MQTT] Client Publish Event: Device ID and according IP not yet seen thru \"Publish Info\"`);\n this.adapter.log.info(`[MQTT] We wait until first info is published. ${msg.deviceId}`);\n return;\n }\n const ip = this.devices[client.id].ip ? this.devices[client.id].ip : '';\n if (ip === '' || typeof ip !== 'string') {\n this.adapter.log.debug(`[MQTT] Client Publish Event: IP address could not be determined. - Client ID: ${client.id}`);\n this.adapter.log.debug(`[MQTT] Please be patient until first MQTT info packet coming in (takes up to 1 minute)`);\n return; // Disregard since IP is unknown!\n }\n\n // Call function\n const result = {\n clientId: client.id,\n ip: ip,\n topic: packet.topic,\n cmd: msg.event,\n };\n if (!this.devices[client.id].mqttFirstReceived) {\n // show only once\n this.adapter.log.info(`[MQTT] \uD83D\uDD17 Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} (${ip})`);\n this.devices[client.id].mqttFirstReceived = true;\n }\n /**\n * Call Adapter function onMqttEvent()\n */\n this.adapter.onMqttEvent(result);\n } else {\n // Ignore\n return;\n }\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n });\n\n /**\n * fired when a client disconnects\n */\n this.aedes.on('clientDisconnect', (client) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] Client ${logMsgName} disconnected.`);\n } else {\n this.adapter.log.error(`[MQTT] Client ${logMsgName} disconnected.`);\n }\n this.setIsAlive(client.id, false, 'client disconnected');\n });\n\n /**\n * fired on client error\n */\n this.aedes.on('clientError', (client, e) => {\n if (this.notAuthorizedClients.includes(client.id)) return; // Error msg was already thrown in aedes.authenticate() before\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Client error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Client error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'client error');\n });\n\n this.aedes.on('connectionError', (client, e) => {\n const ip = this.devices[client.id].ip;\n const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id;\n if (this.adapter.config.mqttConnErrorsAsInfo) {\n this.adapter.log.info(`[MQTT] ${logMsgName}: Connection error - ${e.message}`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - ${e.message}`);\n }\n this.adapter.log.debug(`[MQTT]\uD83D\uDD25 ${logMsgName}: Connection error - stack: ${e.stack}`);\n this.setIsAlive(client.id, false, 'connection error');\n });\n\n /**\n * fired on server error\n */\n this.server.on('error', (e: any) => {\n if (e instanceof Error && e.message.startsWith('listen EADDRINUSE')) {\n this.adapter.log.debug(`[MQTT] Cannot start server - ${e.message}`);\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - Port ${this.port} is already in use. Try a different port!`);\n } else {\n this.adapter.log.error(`[MQTT]\uD83D\uDD25 Cannot start server - ${e.message}`);\n }\n this.terminate();\n });\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * If Client is alive or not\n */\n private setIsAlive(clientId: string, isAlive: true | false, msg: string): void {\n if (isAlive) this.devices[clientId].lastTimeActive = Date.now();\n this.devices[clientId].isActive = isAlive;\n\n const ip = this.devices[clientId]?.ip;\n if (ip) {\n // Call Adapter function onMqttAliveChange()\n this.adapter.onMqttAlive(ip, isAlive, msg);\n if (isAlive) {\n this.scheduleCheckIfStillActive(clientId); // restart timer\n } else {\n // clear timer\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n }\n } else {\n this.adapter.log.debug(`[MQTT] isAlive changed to ${isAlive}, but IP of client ${clientId} is still unknown.`);\n }\n }\n\n /**\n * Schedule: Check if MQTT topic was sent last x seconds ago\n * @param ip IP Address\n * @returns void\n */\n private async scheduleCheckIfStillActive(clientId: string): Promise {\n try {\n const ip = this.devices[clientId].ip;\n const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${clientId} (IP unknown)`;\n // this.adapter.log.debug(`[MQTT] ${ipMsg}: - Start scheduleCheckIfStillActive`);\n\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n\n if (!this.devices[clientId]) this.devices[clientId] = {};\n\n const interval = 70 * 1000; // every 60s + 10s buffer\n this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => {\n try {\n const lastTimeActive = this.devices[clientId].lastTimeActive;\n if (!lastTimeActive) return;\n const diff = Date.now() - lastTimeActive;\n if (diff > 70000) {\n this.adapter.log.debug(`[MQTT] ${ipMsg} NOT ALIVE - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, false, 'client did not send message for more than 70 seconds');\n } else {\n this.adapter.log.warn(`[MQTT] ${ipMsg} Please open a issue on Github, this should never happen: scheduleCheckIfStillActive() timeout, and last contact was less than 70s ago.`);\n this.adapter.log.warn(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, true, `alive check is successful (last contact: ${Math.round(diff / 1000)}s ago)`);\n }\n // Call function again since we are in callback of timeout\n this.scheduleCheckIfStillActive(clientId);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }, interval);\n } catch (e) {\n this.adapter.log.error(this.adapter.err2Str(e));\n return;\n }\n }\n\n /**\n * Terminate MQTT Server and close all...\n */\n public terminate(): void {\n this.adapter.log.info(`[MQTT] Disconnect all clients and close server`);\n // isAlive\n for (const clientId in this.devices) {\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate);\n this.setIsAlive(clientId, false, 'MQTT server was terminated');\n }\n\n if (this.aedes) {\n this.aedes.close(() => {\n this.adapter.log.debug('[MQTT] aedes.close() succeeded');\n if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n });\n } else if (this.server) {\n this.server.close(() => {\n this.adapter.log.debug('[MQTT] server.close() succeeded');\n });\n }\n }\n}\n"], + "mappings": "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", "names": ["Aedes", "net"] } diff --git a/build/main.js b/build/main.js index d070bf7..8266732 100644 --- a/build/main.js +++ b/build/main.js @@ -50,8 +50,8 @@ class FullyMqtt extends utils.Adapter { this.getConfigValuePerKey = import_methods.getConfigValuePerKey.bind(this); this.isIpAddressValid = import_methods.isIpAddressValid.bind(this); this.restApi_inst = new import_restApi.RestApiFully(this); - this.fullys = {}; - this.fullysNotEnabled = {}; + this.fullysEnbl = {}; + this.fullysDisbl = {}; this.fullysAll = {}; this.onMqttAlive_EverBeenCalledBefore = false; this.on("ready", this.onReady.bind(this)); @@ -67,16 +67,18 @@ class FullyMqtt extends utils.Adapter { this.log.error(`Adapter settings initialization failed. ---> Please check your adapter instance settings!`); return; } - for (const ip in this.fullys) { - const res = await this.createFullyDeviceObjects(this.fullys[ip]); + for (const ip in this.fullysEnbl) { + const res = await this.createFullyDeviceObjects(this.fullysEnbl[ip]); if (res) - await this.subscribeStatesAsync(this.fullys[ip].id + ".Commands.*"); - this.setState(this.fullys[ip].id + ".enabled", { val: true, ack: true }); - this.setState(this.fullysNotEnabled[ip].id + ".alive", { val: false, ack: true }); + await this.subscribeStatesAsync(this.fullysEnbl[ip].id + ".Commands.*"); + this.setState(this.fullysEnbl[ip].id + ".enabled", { val: true, ack: true }); + this.setState(this.fullysEnbl[ip].id + ".alive", { val: false, ack: true }); } - for (const ip in this.fullysNotEnabled) { - this.setState(this.fullysNotEnabled[ip].id + ".enabled", { val: false, ack: true }); - this.setState(this.fullysNotEnabled[ip].id + ".alive", { val: null, ack: true }); + for (const ip in this.fullysDisbl) { + if (await this.getObjectAsync(this.fullysAll[ip].id)) { + this.setState(this.fullysDisbl[ip].id + ".enabled", { val: false, ack: true }); + this.setState(this.fullysDisbl[ip].id + ".alive", { val: null, ack: true }); + } } this.mqtt_Server = new import_mqtt_server.MqttServer(this); this.mqtt_Server.start(); @@ -127,8 +129,10 @@ class FullyMqtt extends utils.Adapter { await this.setObjectNotExistsAsync(device.id + ".Commands." + cmdObj.id, { type: "state", common: { name: "Command: " + cmdObj.name, type: cmdObj.type, role: lpRole, read: true, write: true }, native: {} }); } await this.setObjectNotExistsAsync(device.id + ".Events", { type: "channel", common: { name: "MQTT Events" }, native: {} }); - for (const event of import_constants.CONST.mqttEvents) { - await this.setObjectNotExistsAsync(device.id + ".Events." + event, { type: "state", common: { name: "Event: " + event, type: "boolean", role: "switch", read: true, write: false }, native: {} }); + if (this.config.mqttCreateDefaultEventObjects) { + for (const event of import_constants.CONST.mqttEvents) { + await this.setObjectNotExistsAsync(device.id + ".Events." + event, { type: "state", common: { name: "Event: " + event, type: "boolean", role: "switch", read: true, write: false }, native: {} }); + } } return true; } catch (e) { @@ -250,14 +254,14 @@ class FullyMqtt extends utils.Adapter { this.log.debug(`Final Config: ${JSON.stringify(logConfig)}`); this.fullysAll[finalDevice.ip] = finalDevice; if (lpDevice.enabled) { - this.fullys[finalDevice.ip] = finalDevice; + this.fullysEnbl[finalDevice.ip] = finalDevice; this.log.info(`\u{1F5F8} ${finalDevice.name} (${finalDevice.ip}): Config successfully verified.`); } else { - this.fullysNotEnabled[finalDevice.ip] = finalDevice; - this.log.info(`${finalDevice.name} (${finalDevice.ip}) is not enabled in adapter settings, so it will not be used by adapter.`); + this.fullysDisbl[finalDevice.ip] = finalDevice; + this.log.info(`${finalDevice.name} (${finalDevice.ip}) is not enabled in settings, so it will not be used by adapter.`); } } - if (Object.keys(this.fullys).length === 0) { + if (Object.keys(this.fullysEnbl).length === 0) { this.log.error(`No active devices with correct configuration found.`); return false; } @@ -269,24 +273,24 @@ class FullyMqtt extends utils.Adapter { } async onMqttAlive(ip, isAlive, msg) { try { - const prevIsAlive = this.fullys[ip].isAlive; - this.fullys[ip].isAlive = isAlive; + const prevIsAlive = this.fullysEnbl[ip].isAlive; + this.fullysEnbl[ip].isAlive = isAlive; const calledBefore = this.onMqttAlive_EverBeenCalledBefore; this.onMqttAlive_EverBeenCalledBefore = true; if (!calledBefore && isAlive === true || prevIsAlive !== isAlive) { - this.setState(this.fullys[ip].id + ".alive", { val: isAlive, ack: true }); + this.setState(this.fullysEnbl[ip].id + ".alive", { val: isAlive, ack: true }); if (isAlive) { - this.log.info(`${this.fullys[ip].name} is alive (MQTT: ${msg})`); + this.log.info(`${this.fullysEnbl[ip].name} is alive (MQTT: ${msg})`); } else { - this.log.warn(`${this.fullys[ip].name} is not alive! (MQTT: ${msg})`); + this.log.warn(`${this.fullysEnbl[ip].name} is not alive! (MQTT: ${msg})`); } } else { } let countAll = 0; let countAlive = 0; - for (const lpIpAddr in this.fullys) { + for (const lpIpAddr in this.fullysEnbl) { countAll++; - if (this.fullys[lpIpAddr].isAlive) { + if (this.fullysEnbl[lpIpAddr].isAlive) { countAlive++; } } @@ -301,36 +305,36 @@ class FullyMqtt extends utils.Adapter { } async onMqttInfo(obj) { try { - this.log.debug(`[MQTT] ${this.fullys[obj.ip].name} published info, topic: ${obj.topic}`); - const formerInfoKeysLength = this.fullys[obj.ip].mqttInfoKeys.length; + this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name} published info, topic: ${obj.topic}`); + const formerInfoKeysLength = this.fullysEnbl[obj.ip].mqttInfoKeys.length; const newInfoKeysAdded = []; for (const key in obj.infoObj) { const val = obj.infoObj[key]; const valType = typeof val; if (valType !== "string" && valType !== "boolean" && valType !== "object" && valType !== "number") { - this.log.warn(`[MQTT] ${this.fullys[obj.ip].name}: Unknown type ${valType} of key '${key}' in info object`); + this.log.warn(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Unknown type ${valType} of key '${key}' in info object`); continue; } - if (!this.fullys[obj.ip].mqttInfoKeys.includes(key)) { - this.fullys[obj.ip].mqttInfoKeys.push(key); + if (!this.fullysEnbl[obj.ip].mqttInfoKeys.includes(key)) { + this.fullysEnbl[obj.ip].mqttInfoKeys.push(key); newInfoKeysAdded.push(key); - await this.setObjectNotExistsAsync(`${this.fullys[obj.ip].id}.Info.${key}`, { type: "state", common: { name: "Info: " + key, type: valType, role: "value", read: true, write: false }, native: {} }); + await this.setObjectNotExistsAsync(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { type: "state", common: { name: "Info: " + key, type: valType, role: "value", read: true, write: false }, native: {} }); } } if (formerInfoKeysLength === 0) - this.log.debug(`[MQTT] ${this.fullys[obj.ip].name}: Initially create states for ${newInfoKeysAdded.length} info items (if not yet existing)`); + this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Initially create states for ${newInfoKeysAdded.length} info items (if not yet existing)`); if (formerInfoKeysLength > 0 && newInfoKeysAdded.length > 0) - this.log.info(`[MQTT] ${this.fullys[obj.ip].name}: Created new info object(s) as not seen before (if object(s) did not exist): ${newInfoKeysAdded.join(", ")}`); + this.log.info(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Created new info object(s) as not seen before (if object(s) did not exist): ${newInfoKeysAdded.join(", ")}`); for (const key in obj.infoObj) { const newVal = typeof obj.infoObj[key] === "object" ? JSON.stringify(obj.infoObj[key]) : obj.infoObj[key]; if (this.config.mqttUpdateUnchangedObjects) { - this.setState(`${this.fullys[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); + this.setState(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); } else { - this.setStateChanged(`${this.fullys[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); + this.setStateChanged(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); } } - this.setState(this.fullys[obj.ip].id + ".lastInfoUpdate", { val: Date.now(), ack: true }); - this.setState(this.fullys[obj.ip].id + ".alive", { val: true, ack: true }); + this.setState(this.fullysEnbl[obj.ip].id + ".lastInfoUpdate", { val: Date.now(), ack: true }); + this.setState(this.fullysEnbl[obj.ip].id + ".alive", { val: true, ack: true }); } catch (e) { this.log.error(this.err2Str(e)); return; @@ -338,14 +342,14 @@ class FullyMqtt extends utils.Adapter { } async onMqttEvent(obj) { try { - this.log.debug(`[MQTT] \u{1F4E1} ${this.fullys[obj.ip].name} published event, topic: ${obj.topic}, cmd: ${obj.cmd}`); - const pthEvent = `${this.fullys[obj.ip].id}.Events.${obj.cmd}`; + this.log.debug(`[MQTT] \u{1F4E1} ${this.fullysEnbl[obj.ip].name} published event, topic: ${obj.topic}, cmd: ${obj.cmd}`); + const pthEvent = `${this.fullysEnbl[obj.ip].id}.Events.${obj.cmd}`; if (!await this.getObjectAsync(pthEvent)) { - this.log.info(`[MQTT] ${this.fullys[obj.ip].name}: Event ${obj.cmd} received but state ${pthEvent} does not exist, so we create it first`); - await this.setObjectNotExistsAsync(pthEvent, { type: "state", common: { name: "MQTT Event: " + obj.cmd, type: "boolean", role: "switch", read: true, write: false }, native: {} }); + this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Event ${obj.cmd} received but state ${pthEvent} does not exist, so we create it first`); + await this.setObjectNotExistsAsync(pthEvent, { type: "state", common: { name: "Event: " + obj.cmd, type: "boolean", role: "switch", read: true, write: false }, native: {} }); } this.setState(pthEvent, { val: true, ack: true }); - const pthCmd = this.fullys[obj.ip].id + ".Commands"; + const pthCmd = this.fullysEnbl[obj.ip].id + ".Commands"; const idx = this.getIndexFromConf(import_constants.CONST.cmdsSwitches, ["mqttOn", "mqttOff"], obj.cmd); if (idx !== -1) { const conf = import_constants.CONST.cmdsSwitches[idx]; @@ -358,7 +362,7 @@ class FullyMqtt extends utils.Adapter { if (idx2 !== -1 && import_constants.CONST.cmds[idx2].type === "boolean") { await this.setStateAsync(`${pthCmd}.${obj.cmd}`, { val: true, ack: true }); } else { - this.log.silly(`[MQTT] ${this.fullys[obj.ip].name}: Event cmd ${obj.cmd} - no REST API command is existing, so skip confirmation with with ack:true`); + this.log.silly(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Event cmd ${obj.cmd} - no REST API command is existing, so skip confirmation with with ack:true`); } } } catch (e) { @@ -429,12 +433,12 @@ class FullyMqtt extends utils.Adapter { } } getFullyByKey(keyId, value) { - for (const ip in this.fullys) { - if (keyId in this.fullys[ip]) { + for (const ip in this.fullysEnbl) { + if (keyId in this.fullysEnbl[ip]) { const lpKeyId = keyId; - const lpVal = this.fullys[ip][lpKeyId]; + const lpVal = this.fullysEnbl[ip][lpKeyId]; if (lpVal === value) { - return this.fullys[ip]; + return this.fullysEnbl[ip]; } } } diff --git a/build/main.js.map b/build/main.js.map index e932e40..cc12109 100644 --- a/build/main.js.map +++ b/build/main.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/main.ts"], - "sourcesContent": ["/**\n * -------------------------------------------------------------------\n * ioBroker Fully Browser MQTT Adapter\n * @github https://github.com/Acgua/ioBroker.fully-mqtt\n * @forum https://forum.iobroker.net/topic/63705/\n * @author Acgua \n * @license Apache License 2.0\n * -------------------------------------------------------------------\n */\n\n/**\n * For all imported NPM modules, open console, change dir for example to \"C:\\iobroker\\node_modules\\ioBroker.fully-mqtt\\\"\n * and execute \"npm install \", e.g., npm install axios\n */\nimport * as utils from '@iobroker/adapter-core';\nimport { CONST } from './lib/constants';\nimport { ICmds, IDevice } from './lib/interfaces';\nimport { cleanDeviceName, err2Str, getConfigValuePerKey, isEmpty, isIpAddressValid, wait } from './lib/methods';\nimport { MqttServer } from './lib/mqtt-server';\nimport { RestApiFully } from './lib/restApi';\n\n/**\n * Main ioBroker Adapter Class\n */\nexport class FullyMqtt extends utils.Adapter {\n // Imported methods from ./lib/methods\n public err2Str = err2Str.bind(this);\n public isEmpty = isEmpty.bind(this);\n public wait = wait.bind(this);\n public cleanDeviceName = cleanDeviceName.bind(this);\n public getConfigValuePerKey = getConfigValuePerKey.bind(this);\n public isIpAddressValid = isIpAddressValid.bind(this);\n\n // MQTT Server\n private mqtt_Server: MqttServer | undefined;\n\n // REST API\n private restApi_inst = new RestApiFully(this);\n\n /**\n * Fullys: IP as key, and object per IDevice\n * {\n * '192.168.10.20': {name: 'Tablet Kitchen', id:'Tablet-Kitchen', ip:'192.168.10.20', ...},\n * '192.168.10.30': {name: 'Tablet Hallway', id:'Tablet-Hallway', ip:'192.168.10.30', ...},\n * }\n * Note: we can use this.getFullyPerKey() to get fully object per provided key\n */\n public fullys: { [ip: string]: IDevice } = {}; // enabled Fullys only\n public fullysNotEnabled: { [ip: string]: IDevice } = {}; // not enabled Fullys only\n public fullysAll: { [ip: string]: IDevice } = {}; // enabled and not enabled Fullys\n\n // Has onMqttAlive() ever been called before?\n private onMqttAlive_EverBeenCalledBefore = false;\n\n /**\n * Constructor\n */\n public constructor(options: Partial = {}) {\n super({ ...options, name: 'fully-mqtt' });\n this.on('ready', this.onReady.bind(this));\n this.on('stateChange', this.onStateChange.bind(this));\n this.on('unload', this.onUnload.bind(this));\n }\n\n /**\n * Is called when databases are connected and adapter received configuration.\n */\n private async onReady(): Promise {\n try {\n /**\n * Set the connection indicator to false during startup\n */\n this.setState('info.connection', { val: false, ack: true });\n\n /**\n * Verify and init configuration\n */\n if (await this.initConfig()) {\n this.log.debug(`Adapter settings successfully verified and initialized.`);\n } else {\n this.log.error(`Adapter settings initialization failed. ---> Please check your adapter instance settings!`);\n return;\n }\n\n for (const ip in this.fullys) {\n // Create Fully device objects\n const res = await this.createFullyDeviceObjects(this.fullys[ip]);\n // REST API: Subscribe to command state changes\n if (res) await this.subscribeStatesAsync(this.fullys[ip].id + '.Commands.*');\n // Set enabled state to true\n this.setState(this.fullys[ip].id + '.enabled', { val: true, ack: true });\n // Set alive state to false initially\n this.setState(this.fullysNotEnabled[ip].id + '.alive', { val: false, ack: true });\n }\n // Not enabled fullys: 1. Enabled state to false; 2. alive to null\n for (const ip in this.fullysNotEnabled) {\n this.setState(this.fullysNotEnabled[ip].id + '.enabled', { val: false, ack: true });\n this.setState(this.fullysNotEnabled[ip].id + '.alive', { val: null, ack: true });\n }\n\n /**\n * Start MQTT Server\n */\n this.mqtt_Server = new MqttServer(this);\n this.mqtt_Server.start();\n\n /**\n * Delete device object tree(s) if deleted or renamed in config\n */\n this.deleteRemovedDeviceObjects();\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Create Fully Browser Device ioBroker state objects\n * @param device Fully Browser Device Object\n * @returns true if successful, false if error\n */\n private async createFullyDeviceObjects(device: IDevice): Promise {\n try {\n /**\n * Create device object(s)\n */\n // Device and Info object\n await this.setObjectNotExistsAsync(device.id, {\n type: 'device',\n common: {\n name: device.name,\n //@ts-expect-error - Object \"statusStates\" is needed for status, error is: Object literal may only specify known properties, and 'statusStates' does not exist in type 'DeviceCommon'.ts(2345)\n statusStates: { onlineId: `${this.namespace}.${device.id}.alive` },\n },\n native: {},\n });\n await this.setObjectNotExistsAsync(device.id + '.Info', { type: 'channel', common: { name: 'Device Information' }, native: {} });\n\n // Alive\n await this.setObjectNotExistsAsync(device.id + '.alive', {\n type: 'state',\n common: {\n name: 'Is Fully alive?',\n desc: 'If Fully Browser is alive or not',\n type: 'boolean',\n role: 'indicator.reachable',\n icon: 'data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iTXVpU3ZnSWNvbi1yb290IE11aVN2Z0ljb24tZm9udFNpemVNZWRpdW0gaWNvbk93biBjc3MtdnViYnV2IiBmb2N1c2FibGU9ImZhbHNlIiBhcmlhLWhpZGRlbj0idHJ1ZSIgdmlld0JveD0iMCAwIDI0IDI0IiBkYXRhLXRlc3RpZD0iV2lmaUljb24iPjxwYXRoIGQ9Im0xIDkgMiAyYzQuOTctNC45NyAxMy4wMy00Ljk3IDE4IDBsMi0yQzE2LjkzIDIuOTMgNy4wOCAyLjkzIDEgOXptOCA4IDMgMyAzLTNjLTEuNjUtMS42Ni00LjM0LTEuNjYtNiAwem0tNC00IDIgMmMyLjc2LTIuNzYgNy4yNC0yLjc2IDEwIDBsMi0yQzE1LjE0IDkuMTQgOC44NyA5LjE0IDUgMTN6Ij48L3BhdGg+PC9zdmc+',\n read: true,\n write: false,\n },\n native: {},\n });\n // Last info update, and if enabled in adapter settings\n await this.setObjectNotExistsAsync(device.id + '.lastInfoUpdate', { type: 'state', common: { name: 'Last information update', desc: 'Date/time of last information update from Fully Browser', type: 'number', role: 'value.time', read: true, write: false }, native: {} });\n await this.setObjectNotExistsAsync(device.id + '.enabled', { type: 'state', common: { name: 'Is device enabled in adapter settings?', desc: 'If this device is enabled in the adapter settings', type: 'boolean', role: 'indicator', read: true, write: false }, native: {} });\n\n // REST API Commands Objects\n await this.setObjectNotExistsAsync(device.id + '.Commands', { type: 'channel', common: { name: 'Commands' }, native: {} });\n const allCommands = CONST.cmds.concat(CONST.cmdsSwitches); // join both arrays\n for (const cmdObj of allCommands) {\n let lpRole = '';\n if (cmdObj.type === 'boolean') lpRole = 'button';\n if (cmdObj.type === 'string') lpRole = 'text';\n if (cmdObj.type === 'number') lpRole = 'value';\n if (cmdObj.cmdOn && cmdObj.cmdOff) lpRole = 'switch';\n await this.setObjectNotExistsAsync(device.id + '.Commands.' + cmdObj.id, { type: 'state', common: { name: 'Command: ' + cmdObj.name, type: cmdObj.type, role: lpRole, read: true, write: true }, native: {} });\n }\n\n // Create MQTT Events Objects\n // More states are created once a new Event is received.\n await this.setObjectNotExistsAsync(device.id + '.Events', { type: 'channel', common: { name: 'MQTT Events' }, native: {} });\n for (const event of CONST.mqttEvents) {\n await this.setObjectNotExistsAsync(device.id + '.Events.' + event, { type: 'state', common: { name: 'Event: ' + event, type: 'boolean', role: 'switch', read: true, write: false }, native: {} });\n }\n return true;\n } catch (e) {\n this.log.error(this.err2Str(e));\n return false;\n }\n }\n\n /**\n * Delete device objects if device was (a) renamed or (b) deleted from devices table in adapter settings.\n * However, do not delete if it was just set inactive in table.\n */\n private async deleteRemovedDeviceObjects(): Promise {\n try {\n // Get string array of all adapter objects: ['fully-mqtt.0.info', 'fully-mqtt.0.info.connection', ...];\n const adapterObjectsIds: string[] = Object.keys(await this.getAdapterObjectsAsync());\n\n // Get all existing fully device ids of iobroker adapter objects in array: 'fully-mqtt.0.Tablet-Kitchen' -> 'Tablet-Kitchen', 'fully-mqtt.0.Tablet-Hallway' -> 'Tablet-Hallway', etc.\n const allObjectDeviceIds: Array = [];\n for (const objectId of adapterObjectsIds) {\n const deviceId = objectId.split('.')[2]; // e.g. 'Tablet-Kitchen'\n // Ignore fully-mqtt.0.info tree (which includes fully-mqtt.0.info.connection, ...). Add more to ignore as needed in the future...\n if (['info'].includes(deviceId)) {\n this.log.silly(`Cleanup: Ignore non device related state ${objectId}.`);\n } else {\n if (!allObjectDeviceIds.includes(deviceId)) allObjectDeviceIds.push(deviceId);\n }\n }\n\n // Get all adapter configuration device ids (enabled and disabled), like ['Tablet-Kitchen', 'Tablet-Hallway', ...]\n const allConfigDeviceIds: string[] = [];\n for (const ip in this.fullysAll) {\n allConfigDeviceIds.push(this.fullysAll[ip].id);\n }\n // Delete\n for (const id of allObjectDeviceIds) {\n if (!allConfigDeviceIds.includes(id)) {\n await this.delObjectAsync(id, { recursive: true });\n this.log.info(`Cleanup: Deleted no longer defined device objects of '${id}'.`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Verify adapter instance settings\n */\n private async initConfig(): Promise {\n try {\n /*************************\n * MQTT Fields\n *************************/\n if (this.isEmpty(this.config.mqttPort) || this.config.mqttPort < 1 || this.config.mqttPort > 65535) {\n this.log.warn(`Adapter instance settings: MQTT Port ${this.config.mqttPort} is not allowed, set to default of 1886`);\n this.config.mqttPort = 1886;\n }\n if (this.isEmpty(this.config.mqttPublishedInfoDelay) || this.config.mqttPublishedInfoDelay < 2 || this.config.mqttPublishedInfoDelay > 120) {\n this.log.warn(`Adapter instance settings: MQTT Publish Info Delay of ${this.config.mqttPublishedInfoDelay}s is not allowed, set to default of 30s`);\n this.config.mqttPublishedInfoDelay = 30;\n }\n\n /*************************\n * REST API Fields\n *************************/\n if (this.isEmpty(this.config.restTimeout) || this.config.restTimeout < 500 || this.config.restTimeout > 15000) {\n this.log.warn(`Adapter instance settings: REST API timeout of ${this.config.restTimeout} ms is not allowed, set to default of 6000ms`);\n this.config.restTimeout = 6000;\n }\n\n /*************************\n * Table Devices\n *************************/\n if (this.isEmpty(this.config.tableDevices)) {\n this.log.error(`No Fully devices defined in adapter instance settings!`);\n return false;\n }\n const deviceIds: string[] = []; // to check for duplicate device ids\n const deviceIPs: string[] = []; // to check for duplicate device IPs\n for (let i = 0; i < this.config.tableDevices.length; i++) {\n const lpDevice = this.config.tableDevices[i];\n const finalDevice: IDevice = {\n name: '',\n id: '',\n ip: '',\n enabled: false,\n mqttInfoObjectsCreated: false,\n mqttInfoKeys: [],\n restProtocol: 'http',\n restPort: 0,\n restPassword: '',\n lastSeen: 0, // timestamp\n isAlive: false,\n };\n\n // name\n if (this.isEmpty(lpDevice.name)) {\n this.log.error(`Provided device name \"${lpDevice.name}\" is empty!`);\n return false;\n }\n finalDevice.name = lpDevice.name.trim();\n\n // id\n finalDevice.id = this.cleanDeviceName(lpDevice.name);\n if (finalDevice.id.length < 1) {\n this.log.error(`Provided device name \"${lpDevice.name}\" is too short and/or has invalid characters!`);\n return false;\n }\n if (deviceIds.includes(finalDevice.id)) {\n this.log.error(`Device \"${finalDevice.name}\" -> id:\"${finalDevice.id}\" is used for more than once device.`);\n return false;\n } else {\n deviceIds.push(finalDevice.id);\n }\n\n // REST Protocol (http/https)\n if (lpDevice.restProtocol !== 'http' && lpDevice.restProtocol !== 'https') {\n this.log.warn(`${finalDevice.name}: REST API Protocol is empty, set to http as default.`);\n finalDevice.restProtocol = 'http';\n } else {\n finalDevice.restProtocol = lpDevice.restProtocol;\n }\n\n // IP Address\n if (!this.isIpAddressValid(lpDevice.ip)) {\n this.log.error(`${finalDevice.name}: Provided IP address \"${lpDevice.ip}\" is not valid!`);\n return false;\n }\n if (deviceIPs.includes(lpDevice.ip)) {\n this.log.error(`Device \"${finalDevice.name}\" -> IP:\"${lpDevice.ip}\" is used for more than once device.`);\n return false;\n } else {\n deviceIPs.push(lpDevice.ip);\n finalDevice.ip = lpDevice.ip;\n }\n\n // REST Port\n if (isNaN(lpDevice.restPort) || lpDevice.restPort < 0 || lpDevice.restPort > 65535) {\n this.log.error(`Adapter config Fully port number ${lpDevice.restPort} is not valid, should be >= 0 and < 65536.`);\n return false;\n } else {\n finalDevice.restPort = Math.round(lpDevice.restPort);\n }\n // REST Password\n if (isEmpty(lpDevice.restPassword)) {\n this.log.error(`Remote Admin (REST API) Password must not be empty!`);\n return false;\n } else {\n finalDevice.restPassword = lpDevice.restPassword;\n }\n\n // Enabled status\n finalDevice.enabled = lpDevice.enabled ? true : false;\n\n // Debug log of config\n const logConfig = { ...finalDevice }; // copy object using spread\n logConfig.restPassword = '(hidden)'; // do not show password in log !\n this.log.debug(`Final Config: ${JSON.stringify(logConfig)}`);\n\n // Finalize\n this.fullysAll[finalDevice.ip] = finalDevice;\n if (lpDevice.enabled) {\n this.fullys[finalDevice.ip] = finalDevice;\n this.log.info(`\uD83D\uDDF8 ${finalDevice.name} (${finalDevice.ip}): Config successfully verified.`);\n } else {\n this.fullysNotEnabled[finalDevice.ip] = finalDevice;\n this.log.info(`${finalDevice.name} (${finalDevice.ip}) is not enabled in adapter settings, so it will not be used by adapter.`);\n }\n }\n\n if (Object.keys(this.fullys).length === 0) {\n this.log.error(`No active devices with correct configuration found.`);\n return false;\n }\n return true;\n } catch (e) {\n this.log.error(this.err2Str(e));\n return false;\n }\n }\n\n /**\n * On Alive Changes\n * MQTT is being used only, REST API not.\n */\n public async onMqttAlive(ip: string, isAlive: true | false, msg: string): Promise {\n try {\n const prevIsAlive = this.fullys[ip].isAlive;\n this.fullys[ip].isAlive = isAlive;\n\n // Has this function ever been called before? If adapter is restarted, we ensure log, etc.\n const calledBefore = this.onMqttAlive_EverBeenCalledBefore; // Keep old value\n this.onMqttAlive_EverBeenCalledBefore = true; // Now it was called\n\n /***********\n * 1 - Fully Device\n ***********/\n // if alive status changed\n if ((!calledBefore && isAlive === true) || prevIsAlive !== isAlive) {\n // Set Device isAlive Status - we could also use setStateChanged()...\n this.setState(this.fullys[ip].id + '.alive', { val: isAlive, ack: true });\n\n // log\n if (isAlive) {\n this.log.info(`${this.fullys[ip].name} is alive (MQTT: ${msg})`);\n } else {\n this.log.warn(`${this.fullys[ip].name} is not alive! (MQTT: ${msg})`);\n }\n } else {\n // No change\n }\n\n /***********\n * 2 - Adapter Connection indicator\n ***********/\n let countAll = 0;\n let countAlive = 0;\n for (const lpIpAddr in this.fullys) {\n countAll++;\n if (this.fullys[lpIpAddr].isAlive) {\n countAlive++;\n }\n }\n let areAllAlive = false;\n if (countAll > 0 && countAll === countAlive) areAllAlive = true;\n this.setStateChanged('info.connection', { val: areAllAlive, ack: true });\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * MQTT: once new device info packet is coming in\n */\n public async onMqttInfo(obj: { clientId: string; ip: string; topic: string; infoObj: { [k: string]: any } }): Promise {\n try {\n // log\n this.log.debug(`[MQTT] ${this.fullys[obj.ip].name} published info, topic: ${obj.topic}`);\n //this.log.debug(`[MQTT] Client ${obj.ip} Publish Info: Details: ${JSON.stringify(obj.infoObj)}`);\n\n // Create info objects if not yet existing\n const formerInfoKeysLength: number = this.fullys[obj.ip].mqttInfoKeys.length;\n const newInfoKeysAdded: string[] = [];\n for (const key in obj.infoObj) {\n const val = obj.infoObj[key];\n const valType = typeof val;\n // only accept certain types\n if (valType !== 'string' && valType !== 'boolean' && valType !== 'object' && valType !== 'number') {\n this.log.warn(`[MQTT] ${this.fullys[obj.ip].name}: Unknown type ${valType} of key '${key}' in info object`);\n continue;\n }\n // Create info object if not yet seen - this check is used for increasing performance by not unnesserily call setObjectNotExistsAsync() every time new info package comes in\n if (!this.fullys[obj.ip].mqttInfoKeys.includes(key)) {\n this.fullys[obj.ip].mqttInfoKeys.push(key);\n newInfoKeysAdded.push(key);\n await this.setObjectNotExistsAsync(`${this.fullys[obj.ip].id}.Info.${key}`, { type: 'state', common: { name: 'Info: ' + key, type: valType, role: 'value', read: true, write: false }, native: {} });\n }\n }\n if (formerInfoKeysLength === 0) this.log.debug(`[MQTT] ${this.fullys[obj.ip].name}: Initially create states for ${newInfoKeysAdded.length} info items (if not yet existing)`);\n if (formerInfoKeysLength > 0 && newInfoKeysAdded.length > 0) this.log.info(`[MQTT] ${this.fullys[obj.ip].name}: Created new info object(s) as not seen before (if object(s) did not exist): ${newInfoKeysAdded.join(', ')}`);\n\n // Set info objects\n for (const key in obj.infoObj) {\n const newVal = typeof obj.infoObj[key] === 'object' ? JSON.stringify(obj.infoObj[key]) : obj.infoObj[key]; // https://forum.iobroker.net/post/628870 - https://forum.iobroker.net/post/960260\n if (this.config.mqttUpdateUnchangedObjects) {\n this.setState(`${this.fullys[obj.ip].id}.Info.${key}`, { val: newVal, ack: true });\n } else {\n this.setStateChanged(`${this.fullys[obj.ip].id}.Info.${key}`, { val: newVal, ack: true });\n }\n }\n this.setState(this.fullys[obj.ip].id + '.lastInfoUpdate', { val: Date.now(), ack: true });\n this.setState(this.fullys[obj.ip].id + '.alive', { val: true, ack: true });\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * MQTT: once new event packet is coming in\n */\n public async onMqttEvent(obj: { clientId: string; ip: string; topic: string; cmd: string }): Promise {\n try {\n // log\n this.log.debug(`[MQTT] \uD83D\uDCE1 ${this.fullys[obj.ip].name} published event, topic: ${obj.topic}, cmd: ${obj.cmd}`);\n\n /**\n * Set Event State\n */\n const pthEvent = `${this.fullys[obj.ip].id}.Events.${obj.cmd}`;\n if (!(await this.getObjectAsync(pthEvent))) {\n this.log.info(`[MQTT] ${this.fullys[obj.ip].name}: Event ${obj.cmd} received but state ${pthEvent} does not exist, so we create it first`);\n await this.setObjectNotExistsAsync(pthEvent, { type: 'state', common: { name: 'MQTT Event: ' + obj.cmd, type: 'boolean', role: 'switch', read: true, write: false }, native: {} });\n }\n this.setState(pthEvent, { val: true, ack: true });\n\n /**\n * Confirm Command state(s) with ack: true\n */\n const pthCmd = this.fullys[obj.ip].id + '.Commands';\n\n // Check if it is a switch with MQTT commands connected\n const idx = this.getIndexFromConf(CONST.cmdsSwitches, ['mqttOn', 'mqttOff'], obj.cmd);\n if (idx !== -1) {\n // We have a switch\n const conf = CONST.cmdsSwitches[idx]; // the found line from config array\n const onOrOffCmd = obj.cmd === conf.mqttOn ? true : false;\n await this.setStateAsync(`${pthCmd}.${conf.id}`, { val: onOrOffCmd, ack: true });\n await this.setStateAsync(`${pthCmd}.${conf.cmdOn}`, { val: onOrOffCmd, ack: true });\n await this.setStateAsync(`${pthCmd}.${conf.cmdOff}`, { val: !onOrOffCmd, ack: true });\n } else {\n // No switch\n const idx = this.getIndexFromConf(CONST.cmds, ['id'], obj.cmd);\n if (idx !== -1 && CONST.cmds[idx].type === 'boolean') {\n // We have a button, so set it to true\n await this.setStateAsync(`${pthCmd}.${obj.cmd}`, { val: true, ack: true });\n } else {\n this.log.silly(`[MQTT] ${this.fullys[obj.ip].name}: Event cmd ${obj.cmd} - no REST API command is existing, so skip confirmation with with ack:true`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Called once a subscribed state changes.\n * Ready once subscribeStatesAsync() is called...\n * @param id - e.g. \"fully-mqtt.0.Tablet-Bathroom.Commands.screenSwitch\"\n * @param stateObj - e.g. { val: true, ack: false, ts: 123456789, q: 0, lc: 123456789 }\n */\n private async onStateChange(stateId: string, stateObj: ioBroker.State | null | undefined): Promise {\n try {\n if (!stateObj) return; // state was deleted, we disregard...\n if (stateObj.ack) return; // ignore ack:true\n const idSplit = stateId.split('.');\n const deviceId = idSplit[2]; // \"Tablet-Bathroom\"\n const channel = idSplit[3]; // \"Commands\"\n const cmd = idSplit[4]; // \"screenSwitch\"\n const pth = deviceId + '.' + channel; // Tablet-Bathroom.Commands\n /**\n * Commands\n */\n if (channel === 'Commands') {\n this.log.debug(`state ${stateId} changed: ${stateObj.val} (ack = ${stateObj.ack})`);\n // Get device object\n const fully = this.getFullyByKey('id', deviceId);\n if (!fully) throw `Fully object for deviceId '${deviceId}' not found!`;\n\n let cmdToSend: string | undefined = cmd; // Command to send to Fully\n let switchConf: undefined | ICmds = undefined; // Config line of switch\n\n /****************\n * Check if it is a switch state cmd, like 'screenSwitch'\n ****************/\n const idxSw = this.getIndexFromConf(CONST.cmdsSwitches, ['id'], cmd);\n if (idxSw !== -1) {\n // It is a switch\n switchConf = CONST.cmdsSwitches[idxSw]; // the found line from config array\n cmdToSend = stateObj.val ? switchConf.cmdOn : switchConf.cmdOff;\n } else {\n // Not a switch.\n // If val is false, we disregard, since it is a button only\n if (!stateObj.val) return;\n }\n if (!cmdToSend) throw `onStateChange() - ${stateId}: fullyCmd could not be determined!`;\n\n /**\n * Send Command\n */\n const sendCommand = await this.restApi_inst.sendCmd(fully, cmdToSend, stateObj.val);\n if (sendCommand) {\n this.log.info(`\uD83D\uDDF8 ${fully.name}: Command ${cmd} successfully set to ${stateObj.val}`);\n /**\n * Confirm with ack:true\n */\n if (switchConf !== undefined) {\n // it is a switch\n const onOrOffCmdVal = cmd === switchConf.cmdOn ? true : false;\n await this.setStateAsync(`${pth}.${switchConf.id}`, { val: onOrOffCmdVal, ack: true });\n await this.setStateAsync(`${pth}.${switchConf.cmdOn}`, { val: onOrOffCmdVal, ack: true });\n await this.setStateAsync(`${pth}.${switchConf.cmdOff}`, { val: !onOrOffCmdVal, ack: true });\n } else {\n // No switch\n if (typeof stateObj.val === 'boolean') {\n const idx = this.getIndexFromConf(CONST.cmds, ['id'], cmd);\n if (idx !== -1) {\n if (CONST.cmds[idx].type === 'boolean') {\n // Is a button\n await this.setStateAsync(stateId, { val: true, ack: true });\n } else {\n // This should actually not happen, as we just define buttons in commands, but anyway\n this.log.warn(`${fully.name}: ${stateId} - val: ${stateObj.val} is boolean, but cmd ${cmd} is not defined in CONF`);\n await this.setStateAsync(stateId, { val: stateObj.val, ack: true });\n }\n } else {\n this.log.warn(`${fully.name}: ${stateId} - val: ${stateObj.val}, cmd ${cmd} is not defined in CONF`);\n }\n } else {\n // Non-boolean, so just set val with ack:true...\n await this.setStateAsync(stateId, { val: stateObj.val, ack: true });\n }\n }\n } else {\n // log, more log lines were already published by this.restApi_inst.sendCmd()\n this.log.debug(`${fully.name}: restApiSendCmd() was not successful (${stateId})`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Get Fully Object per provided key and value\n * {\n * '192.168.10.20': {name: 'Tablet Kitchen', id:'Tablet-Kitchen', ip:'192.168.10.20', ...},\n * '192.168.10.30': {name: 'Tablet Hallway', id:'Tablet-Hallway', ip:'192.168.10.30', ...},\n * }\n * getFullyByKey('id', 'Tablet-Hallway') will return the second object...\n * @param keyId - e.g. 'id', 'name', ...\n * @param value - e.g. 'Tablet Hallway', ...\n * @returns - fully object or false if not found\n */\n private getFullyByKey(keyId: string, value: any): IDevice | false {\n for (const ip in this.fullys) {\n if (keyId in this.fullys[ip]) {\n const lpKeyId = keyId as string;\n // Wow, what a line. Due to: https://bobbyhadz.com/blog/typescript-element-implicitly-has-any-type-expression\n const lpVal = this.fullys[ip][lpKeyId as keyof (typeof this.fullys)[typeof ip]];\n if (lpVal === value) {\n return this.fullys[ip];\n }\n }\n }\n return false;\n }\n\n /**\n * Gets Index for given keys and a value\n * @param config - config like CONST.cmds\n * @param keys - like ['mqttOn','mqttOff']\n * @param cmd - like 'onScreensaverStart'\n * @returns Index (0-...), or -1 if not found\n */\n private getIndexFromConf(config: { [k: string]: any }[], keys: string[], cmd: string): number {\n try {\n let index = -1;\n for (const key of keys) {\n // Get array index\n index = config.findIndex((x: { [k: string]: any }) => x[key] === cmd);\n if (index !== -1) break;\n }\n return index;\n } catch (e) {\n this.log.error(this.err2Str(e));\n return -1;\n }\n }\n\n /**\n * Is called when adapter shuts down - callback has to be called under any circumstances!\n */\n private async onUnload(callback: () => void): Promise {\n try {\n // All Fullys: Set alive status to null\n if (this.fullysAll) {\n for (const ip in this.fullysAll) {\n // We check first if object exists, as there were errors in log on when updating adpater via Github (related to missing objects)\n if (await this.getObjectAsync(this.fullysAll[ip].id)) {\n this.setState(this.fullysAll[ip].id + '.alive', { val: null, ack: true });\n }\n }\n }\n\n // Clear MQTT server timeouts\n if (this.mqtt_Server) {\n for (const clientId in this.mqtt_Server.devices) {\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.mqtt_Server.devices[clientId].timeoutNoUpdate) this.clearTimeout(this.mqtt_Server.devices[clientId].timeoutNoUpdate);\n }\n }\n\n // destroy MQTT Server\n if (this.mqtt_Server) {\n this.mqtt_Server.terminate();\n }\n\n callback();\n } catch (e) {\n callback();\n }\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial | undefined) => new FullyMqtt(options);\n} else {\n // otherwise start the instance directly\n (() => new FullyMqtt())();\n}\n"], - "mappings": "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", + "sourcesContent": ["/**\n * -------------------------------------------------------------------\n * ioBroker Fully Browser MQTT Adapter\n * @github https://github.com/Acgua/ioBroker.fully-mqtt\n * @forum https://forum.iobroker.net/topic/63705/\n * @author Acgua \n * @license Apache License 2.0\n * -------------------------------------------------------------------\n */\n\n/**\n * For all imported NPM modules, open console, change dir for example to \"C:\\iobroker\\node_modules\\ioBroker.fully-mqtt\\\"\n * and execute \"npm install \", e.g., npm install axios\n */\nimport * as utils from '@iobroker/adapter-core';\nimport { CONST } from './lib/constants';\nimport { ICmds, IDevice } from './lib/interfaces';\nimport { cleanDeviceName, err2Str, getConfigValuePerKey, isEmpty, isIpAddressValid, wait } from './lib/methods';\nimport { MqttServer } from './lib/mqtt-server';\nimport { RestApiFully } from './lib/restApi';\n\n/**\n * Main ioBroker Adapter Class\n */\nexport class FullyMqtt extends utils.Adapter {\n // Imported methods from ./lib/methods\n public err2Str = err2Str.bind(this);\n public isEmpty = isEmpty.bind(this);\n public wait = wait.bind(this);\n public cleanDeviceName = cleanDeviceName.bind(this);\n public getConfigValuePerKey = getConfigValuePerKey.bind(this);\n public isIpAddressValid = isIpAddressValid.bind(this);\n\n // MQTT Server\n private mqtt_Server: MqttServer | undefined;\n\n // REST API\n private restApi_inst = new RestApiFully(this);\n\n /**\n * Fullys: IP as key, and object per IDevice\n * {\n * '192.168.10.20': {name: 'Tablet Kitchen', id:'Tablet-Kitchen', ip:'192.168.10.20', ...},\n * '192.168.10.30': {name: 'Tablet Hallway', id:'Tablet-Hallway', ip:'192.168.10.30', ...},\n * }\n * Note: we can use this.getFullyPerKey() to get fully object per provided key\n */\n public fullysEnbl: { [ip: string]: IDevice } = {}; // enabled Fullys only\n public fullysDisbl: { [ip: string]: IDevice } = {}; // not enabled Fullys only\n public fullysAll: { [ip: string]: IDevice } = {}; // enabled and not enabled Fullys\n\n // Has onMqttAlive() ever been called before?\n private onMqttAlive_EverBeenCalledBefore = false;\n\n /**\n * Constructor\n */\n public constructor(options: Partial = {}) {\n super({ ...options, name: 'fully-mqtt' });\n this.on('ready', this.onReady.bind(this));\n this.on('stateChange', this.onStateChange.bind(this));\n this.on('unload', this.onUnload.bind(this));\n }\n\n /**\n * Is called when databases are connected and adapter received configuration.\n */\n private async onReady(): Promise {\n try {\n /**\n * Set the connection indicator to false during startup\n */\n this.setState('info.connection', { val: false, ack: true });\n\n /**\n * Verify and init configuration\n */\n if (await this.initConfig()) {\n this.log.debug(`Adapter settings successfully verified and initialized.`);\n } else {\n this.log.error(`Adapter settings initialization failed. ---> Please check your adapter instance settings!`);\n return;\n }\n\n for (const ip in this.fullysEnbl) {\n // Create Fully device objects\n const res = await this.createFullyDeviceObjects(this.fullysEnbl[ip]);\n\n // REST API: Subscribe to command state changes\n if (res) await this.subscribeStatesAsync(this.fullysEnbl[ip].id + '.Commands.*');\n\n // Set enabled and alive states\n this.setState(this.fullysEnbl[ip].id + '.enabled', { val: true, ack: true });\n this.setState(this.fullysEnbl[ip].id + '.alive', { val: false, ack: true });\n }\n // Not enabled fullys (if object exists at all): 1. Enabled state to false; 2. alive to null\n for (const ip in this.fullysDisbl) {\n if (await this.getObjectAsync(this.fullysAll[ip].id)) {\n this.setState(this.fullysDisbl[ip].id + '.enabled', { val: false, ack: true });\n this.setState(this.fullysDisbl[ip].id + '.alive', { val: null, ack: true });\n }\n }\n\n /**\n * Start MQTT Server\n */\n this.mqtt_Server = new MqttServer(this);\n this.mqtt_Server.start();\n\n /**\n * Delete device object tree(s) if deleted or renamed in config\n */\n this.deleteRemovedDeviceObjects();\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Create Fully Browser Device ioBroker state objects\n * @param device Fully Browser Device Object\n * @returns true if successful, false if error\n */\n private async createFullyDeviceObjects(device: IDevice): Promise {\n try {\n /**\n * Create device object(s)\n */\n // Device and Info object\n await this.setObjectNotExistsAsync(device.id, {\n type: 'device',\n common: {\n name: device.name,\n //@ts-expect-error - Object \"statusStates\" is needed for status, error is: Object literal may only specify known properties, and 'statusStates' does not exist in type 'DeviceCommon'.ts(2345)\n statusStates: { onlineId: `${this.namespace}.${device.id}.alive` },\n },\n native: {},\n });\n await this.setObjectNotExistsAsync(device.id + '.Info', { type: 'channel', common: { name: 'Device Information' }, native: {} });\n\n // Alive\n await this.setObjectNotExistsAsync(device.id + '.alive', {\n type: 'state',\n common: {\n name: 'Is Fully alive?',\n desc: 'If Fully Browser is alive or not',\n type: 'boolean',\n role: 'indicator.reachable',\n icon: 'data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iTXVpU3ZnSWNvbi1yb290IE11aVN2Z0ljb24tZm9udFNpemVNZWRpdW0gaWNvbk93biBjc3MtdnViYnV2IiBmb2N1c2FibGU9ImZhbHNlIiBhcmlhLWhpZGRlbj0idHJ1ZSIgdmlld0JveD0iMCAwIDI0IDI0IiBkYXRhLXRlc3RpZD0iV2lmaUljb24iPjxwYXRoIGQ9Im0xIDkgMiAyYzQuOTctNC45NyAxMy4wMy00Ljk3IDE4IDBsMi0yQzE2LjkzIDIuOTMgNy4wOCAyLjkzIDEgOXptOCA4IDMgMyAzLTNjLTEuNjUtMS42Ni00LjM0LTEuNjYtNiAwem0tNC00IDIgMmMyLjc2LTIuNzYgNy4yNC0yLjc2IDEwIDBsMi0yQzE1LjE0IDkuMTQgOC44NyA5LjE0IDUgMTN6Ij48L3BhdGg+PC9zdmc+',\n read: true,\n write: false,\n },\n native: {},\n });\n // Last info update, and if enabled in adapter settings\n await this.setObjectNotExistsAsync(device.id + '.lastInfoUpdate', { type: 'state', common: { name: 'Last information update', desc: 'Date/time of last information update from Fully Browser', type: 'number', role: 'value.time', read: true, write: false }, native: {} });\n await this.setObjectNotExistsAsync(device.id + '.enabled', { type: 'state', common: { name: 'Is device enabled in adapter settings?', desc: 'If this device is enabled in the adapter settings', type: 'boolean', role: 'indicator', read: true, write: false }, native: {} });\n\n // REST API Commands Objects\n await this.setObjectNotExistsAsync(device.id + '.Commands', { type: 'channel', common: { name: 'Commands' }, native: {} });\n const allCommands = CONST.cmds.concat(CONST.cmdsSwitches); // join both arrays\n for (const cmdObj of allCommands) {\n let lpRole = '';\n if (cmdObj.type === 'boolean') lpRole = 'button';\n if (cmdObj.type === 'string') lpRole = 'text';\n if (cmdObj.type === 'number') lpRole = 'value';\n if (cmdObj.cmdOn && cmdObj.cmdOff) lpRole = 'switch';\n await this.setObjectNotExistsAsync(device.id + '.Commands.' + cmdObj.id, { type: 'state', common: { name: 'Command: ' + cmdObj.name, type: cmdObj.type, role: lpRole, read: true, write: true }, native: {} });\n }\n\n // Create MQTT Events Objects\n // Any not yet created objects are created once a new Event is received.\n await this.setObjectNotExistsAsync(device.id + '.Events', { type: 'channel', common: { name: 'MQTT Events' }, native: {} });\n if (this.config.mqttCreateDefaultEventObjects) {\n for (const event of CONST.mqttEvents) {\n await this.setObjectNotExistsAsync(device.id + '.Events.' + event, { type: 'state', common: { name: 'Event: ' + event, type: 'boolean', role: 'switch', read: true, write: false }, native: {} });\n }\n }\n return true;\n } catch (e) {\n this.log.error(this.err2Str(e));\n return false;\n }\n }\n\n /**\n * Delete device objects if device was (a) renamed or (b) deleted from devices table in adapter settings.\n * However, do not delete if it was just set inactive in table.\n */\n private async deleteRemovedDeviceObjects(): Promise {\n try {\n // Get string array of all adapter objects: ['fully-mqtt.0.info', 'fully-mqtt.0.info.connection', ...];\n const adapterObjectsIds: string[] = Object.keys(await this.getAdapterObjectsAsync());\n\n // Get all existing fully device ids of iobroker adapter objects in array: 'fully-mqtt.0.Tablet-Kitchen' -> 'Tablet-Kitchen', 'fully-mqtt.0.Tablet-Hallway' -> 'Tablet-Hallway', etc.\n const allObjectDeviceIds: Array = [];\n for (const objectId of adapterObjectsIds) {\n const deviceId = objectId.split('.')[2]; // e.g. 'Tablet-Kitchen'\n // Ignore fully-mqtt.0.info tree (which includes fully-mqtt.0.info.connection, ...). Add more to ignore as needed in the future...\n if (['info'].includes(deviceId)) {\n this.log.silly(`Cleanup: Ignore non device related state ${objectId}.`);\n } else {\n if (!allObjectDeviceIds.includes(deviceId)) allObjectDeviceIds.push(deviceId);\n }\n }\n\n // Get all adapter configuration device ids (enabled and disabled), like ['Tablet-Kitchen', 'Tablet-Hallway', ...]\n const allConfigDeviceIds: string[] = [];\n for (const ip in this.fullysAll) {\n allConfigDeviceIds.push(this.fullysAll[ip].id);\n }\n // Delete\n for (const id of allObjectDeviceIds) {\n if (!allConfigDeviceIds.includes(id)) {\n await this.delObjectAsync(id, { recursive: true });\n this.log.info(`Cleanup: Deleted no longer defined device objects of '${id}'.`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Verify adapter instance settings\n */\n private async initConfig(): Promise {\n try {\n /*************************\n * MQTT Fields\n *************************/\n if (this.isEmpty(this.config.mqttPort) || this.config.mqttPort < 1 || this.config.mqttPort > 65535) {\n this.log.warn(`Adapter instance settings: MQTT Port ${this.config.mqttPort} is not allowed, set to default of 1886`);\n this.config.mqttPort = 1886;\n }\n if (this.isEmpty(this.config.mqttPublishedInfoDelay) || this.config.mqttPublishedInfoDelay < 2 || this.config.mqttPublishedInfoDelay > 120) {\n this.log.warn(`Adapter instance settings: MQTT Publish Info Delay of ${this.config.mqttPublishedInfoDelay}s is not allowed, set to default of 30s`);\n this.config.mqttPublishedInfoDelay = 30;\n }\n\n /*************************\n * REST API Fields\n *************************/\n if (this.isEmpty(this.config.restTimeout) || this.config.restTimeout < 500 || this.config.restTimeout > 15000) {\n this.log.warn(`Adapter instance settings: REST API timeout of ${this.config.restTimeout} ms is not allowed, set to default of 6000ms`);\n this.config.restTimeout = 6000;\n }\n\n /*************************\n * Table Devices\n *************************/\n if (this.isEmpty(this.config.tableDevices)) {\n this.log.error(`No Fully devices defined in adapter instance settings!`);\n return false;\n }\n const deviceIds: string[] = []; // to check for duplicate device ids\n const deviceIPs: string[] = []; // to check for duplicate device IPs\n for (let i = 0; i < this.config.tableDevices.length; i++) {\n const lpDevice = this.config.tableDevices[i];\n const finalDevice: IDevice = {\n name: '',\n id: '',\n ip: '',\n enabled: false,\n mqttInfoObjectsCreated: false,\n mqttInfoKeys: [],\n restProtocol: 'http',\n restPort: 0,\n restPassword: '',\n lastSeen: 0, // timestamp\n isAlive: false,\n };\n\n // name\n if (this.isEmpty(lpDevice.name)) {\n this.log.error(`Provided device name \"${lpDevice.name}\" is empty!`);\n return false;\n }\n finalDevice.name = lpDevice.name.trim();\n\n // id\n finalDevice.id = this.cleanDeviceName(lpDevice.name);\n if (finalDevice.id.length < 1) {\n this.log.error(`Provided device name \"${lpDevice.name}\" is too short and/or has invalid characters!`);\n return false;\n }\n if (deviceIds.includes(finalDevice.id)) {\n this.log.error(`Device \"${finalDevice.name}\" -> id:\"${finalDevice.id}\" is used for more than once device.`);\n return false;\n } else {\n deviceIds.push(finalDevice.id);\n }\n\n // REST Protocol (http/https)\n if (lpDevice.restProtocol !== 'http' && lpDevice.restProtocol !== 'https') {\n this.log.warn(`${finalDevice.name}: REST API Protocol is empty, set to http as default.`);\n finalDevice.restProtocol = 'http';\n } else {\n finalDevice.restProtocol = lpDevice.restProtocol;\n }\n\n // IP Address\n if (!this.isIpAddressValid(lpDevice.ip)) {\n this.log.error(`${finalDevice.name}: Provided IP address \"${lpDevice.ip}\" is not valid!`);\n return false;\n }\n if (deviceIPs.includes(lpDevice.ip)) {\n this.log.error(`Device \"${finalDevice.name}\" -> IP:\"${lpDevice.ip}\" is used for more than once device.`);\n return false;\n } else {\n deviceIPs.push(lpDevice.ip);\n finalDevice.ip = lpDevice.ip;\n }\n\n // REST Port\n if (isNaN(lpDevice.restPort) || lpDevice.restPort < 0 || lpDevice.restPort > 65535) {\n this.log.error(`Adapter config Fully port number ${lpDevice.restPort} is not valid, should be >= 0 and < 65536.`);\n return false;\n } else {\n finalDevice.restPort = Math.round(lpDevice.restPort);\n }\n // REST Password\n if (isEmpty(lpDevice.restPassword)) {\n this.log.error(`Remote Admin (REST API) Password must not be empty!`);\n return false;\n } else {\n finalDevice.restPassword = lpDevice.restPassword;\n }\n\n // Enabled status\n finalDevice.enabled = lpDevice.enabled ? true : false;\n\n // Debug log of config\n const logConfig = { ...finalDevice }; // copy object using spread\n logConfig.restPassword = '(hidden)'; // do not show password in log !\n this.log.debug(`Final Config: ${JSON.stringify(logConfig)}`);\n\n // Finalize\n this.fullysAll[finalDevice.ip] = finalDevice;\n if (lpDevice.enabled) {\n this.fullysEnbl[finalDevice.ip] = finalDevice;\n this.log.info(`\uD83D\uDDF8 ${finalDevice.name} (${finalDevice.ip}): Config successfully verified.`);\n } else {\n this.fullysDisbl[finalDevice.ip] = finalDevice;\n this.log.info(`${finalDevice.name} (${finalDevice.ip}) is not enabled in settings, so it will not be used by adapter.`);\n }\n }\n\n if (Object.keys(this.fullysEnbl).length === 0) {\n this.log.error(`No active devices with correct configuration found.`);\n return false;\n }\n return true;\n } catch (e) {\n this.log.error(this.err2Str(e));\n return false;\n }\n }\n\n /**\n * On Alive Changes\n * MQTT is being used only, REST API not.\n */\n public async onMqttAlive(ip: string, isAlive: true | false, msg: string): Promise {\n try {\n const prevIsAlive = this.fullysEnbl[ip].isAlive;\n this.fullysEnbl[ip].isAlive = isAlive;\n\n // Has this function ever been called before? If adapter is restarted, we ensure log, etc.\n const calledBefore = this.onMqttAlive_EverBeenCalledBefore; // Keep old value\n this.onMqttAlive_EverBeenCalledBefore = true; // Now it was called\n\n /***********\n * 1 - Fully Device\n ***********/\n // if alive status changed\n if ((!calledBefore && isAlive === true) || prevIsAlive !== isAlive) {\n // Set Device isAlive Status - we could also use setStateChanged()...\n this.setState(this.fullysEnbl[ip].id + '.alive', { val: isAlive, ack: true });\n\n // log\n if (isAlive) {\n this.log.info(`${this.fullysEnbl[ip].name} is alive (MQTT: ${msg})`);\n } else {\n this.log.warn(`${this.fullysEnbl[ip].name} is not alive! (MQTT: ${msg})`);\n }\n } else {\n // No change\n }\n\n /***********\n * 2 - Adapter Connection indicator\n ***********/\n let countAll = 0;\n let countAlive = 0;\n for (const lpIpAddr in this.fullysEnbl) {\n countAll++;\n if (this.fullysEnbl[lpIpAddr].isAlive) {\n countAlive++;\n }\n }\n let areAllAlive = false;\n if (countAll > 0 && countAll === countAlive) areAllAlive = true;\n this.setStateChanged('info.connection', { val: areAllAlive, ack: true });\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * MQTT: once new device info packet is coming in\n */\n public async onMqttInfo(obj: { clientId: string; ip: string; topic: string; infoObj: { [k: string]: any } }): Promise {\n try {\n // log\n this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name} published info, topic: ${obj.topic}`);\n //this.log.debug(`[MQTT] Client ${obj.ip} Publish Info: Details: ${JSON.stringify(obj.infoObj)}`);\n\n // Create info objects if not yet existing\n const formerInfoKeysLength: number = this.fullysEnbl[obj.ip].mqttInfoKeys.length;\n const newInfoKeysAdded: string[] = [];\n for (const key in obj.infoObj) {\n const val = obj.infoObj[key];\n const valType = typeof val;\n // only accept certain types\n if (valType !== 'string' && valType !== 'boolean' && valType !== 'object' && valType !== 'number') {\n this.log.warn(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Unknown type ${valType} of key '${key}' in info object`);\n continue;\n }\n // Create info object if not yet seen - this check is used for increasing performance by not unnesserily call setObjectNotExistsAsync() every time new info package comes in\n if (!this.fullysEnbl[obj.ip].mqttInfoKeys.includes(key)) {\n this.fullysEnbl[obj.ip].mqttInfoKeys.push(key);\n newInfoKeysAdded.push(key);\n await this.setObjectNotExistsAsync(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { type: 'state', common: { name: 'Info: ' + key, type: valType, role: 'value', read: true, write: false }, native: {} });\n }\n }\n if (formerInfoKeysLength === 0) this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Initially create states for ${newInfoKeysAdded.length} info items (if not yet existing)`);\n if (formerInfoKeysLength > 0 && newInfoKeysAdded.length > 0) this.log.info(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Created new info object(s) as not seen before (if object(s) did not exist): ${newInfoKeysAdded.join(', ')}`);\n\n // Set info objects\n for (const key in obj.infoObj) {\n const newVal = typeof obj.infoObj[key] === 'object' ? JSON.stringify(obj.infoObj[key]) : obj.infoObj[key]; // https://forum.iobroker.net/post/628870 - https://forum.iobroker.net/post/960260\n if (this.config.mqttUpdateUnchangedObjects) {\n this.setState(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { val: newVal, ack: true });\n } else {\n this.setStateChanged(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { val: newVal, ack: true });\n }\n }\n this.setState(this.fullysEnbl[obj.ip].id + '.lastInfoUpdate', { val: Date.now(), ack: true });\n this.setState(this.fullysEnbl[obj.ip].id + '.alive', { val: true, ack: true });\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * MQTT: once new event packet is coming in\n */\n public async onMqttEvent(obj: { clientId: string; ip: string; topic: string; cmd: string }): Promise {\n try {\n // log\n this.log.debug(`[MQTT] \uD83D\uDCE1 ${this.fullysEnbl[obj.ip].name} published event, topic: ${obj.topic}, cmd: ${obj.cmd}`);\n\n /**\n * Set Event State\n */\n const pthEvent = `${this.fullysEnbl[obj.ip].id}.Events.${obj.cmd}`;\n if (!(await this.getObjectAsync(pthEvent))) {\n this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Event ${obj.cmd} received but state ${pthEvent} does not exist, so we create it first`);\n await this.setObjectNotExistsAsync(pthEvent, { type: 'state', common: { name: 'Event: ' + obj.cmd, type: 'boolean', role: 'switch', read: true, write: false }, native: {} });\n }\n this.setState(pthEvent, { val: true, ack: true });\n\n /**\n * Confirm Command state(s) with ack: true\n */\n const pthCmd = this.fullysEnbl[obj.ip].id + '.Commands';\n\n // Check if it is a switch with MQTT commands connected\n const idx = this.getIndexFromConf(CONST.cmdsSwitches, ['mqttOn', 'mqttOff'], obj.cmd);\n if (idx !== -1) {\n // We have a switch\n const conf = CONST.cmdsSwitches[idx]; // the found line from config array\n const onOrOffCmd = obj.cmd === conf.mqttOn ? true : false;\n await this.setStateAsync(`${pthCmd}.${conf.id}`, { val: onOrOffCmd, ack: true });\n await this.setStateAsync(`${pthCmd}.${conf.cmdOn}`, { val: onOrOffCmd, ack: true });\n await this.setStateAsync(`${pthCmd}.${conf.cmdOff}`, { val: !onOrOffCmd, ack: true });\n } else {\n // No switch\n const idx = this.getIndexFromConf(CONST.cmds, ['id'], obj.cmd);\n if (idx !== -1 && CONST.cmds[idx].type === 'boolean') {\n // We have a button, so set it to true\n await this.setStateAsync(`${pthCmd}.${obj.cmd}`, { val: true, ack: true });\n } else {\n this.log.silly(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Event cmd ${obj.cmd} - no REST API command is existing, so skip confirmation with with ack:true`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Called once a subscribed state changes.\n * Ready once subscribeStatesAsync() is called...\n * @param id - e.g. \"fully-mqtt.0.Tablet-Bathroom.Commands.screenSwitch\"\n * @param stateObj - e.g. { val: true, ack: false, ts: 123456789, q: 0, lc: 123456789 }\n */\n private async onStateChange(stateId: string, stateObj: ioBroker.State | null | undefined): Promise {\n try {\n if (!stateObj) return; // state was deleted, we disregard...\n if (stateObj.ack) return; // ignore ack:true\n const idSplit = stateId.split('.');\n const deviceId = idSplit[2]; // \"Tablet-Bathroom\"\n const channel = idSplit[3]; // \"Commands\"\n const cmd = idSplit[4]; // \"screenSwitch\"\n const pth = deviceId + '.' + channel; // Tablet-Bathroom.Commands\n /**\n * Commands\n */\n if (channel === 'Commands') {\n this.log.debug(`state ${stateId} changed: ${stateObj.val} (ack = ${stateObj.ack})`);\n // Get device object\n const fully = this.getFullyByKey('id', deviceId);\n if (!fully) throw `Fully object for deviceId '${deviceId}' not found!`;\n\n let cmdToSend: string | undefined = cmd; // Command to send to Fully\n let switchConf: undefined | ICmds = undefined; // Config line of switch\n\n /****************\n * Check if it is a switch state cmd, like 'screenSwitch'\n ****************/\n const idxSw = this.getIndexFromConf(CONST.cmdsSwitches, ['id'], cmd);\n if (idxSw !== -1) {\n // It is a switch\n switchConf = CONST.cmdsSwitches[idxSw]; // the found line from config array\n cmdToSend = stateObj.val ? switchConf.cmdOn : switchConf.cmdOff;\n } else {\n // Not a switch.\n // If val is false, we disregard, since it is a button only\n if (!stateObj.val) return;\n }\n if (!cmdToSend) throw `onStateChange() - ${stateId}: fullyCmd could not be determined!`;\n\n /**\n * Send Command\n */\n const sendCommand = await this.restApi_inst.sendCmd(fully, cmdToSend, stateObj.val);\n if (sendCommand) {\n this.log.info(`\uD83D\uDDF8 ${fully.name}: Command ${cmd} successfully set to ${stateObj.val}`);\n /**\n * Confirm with ack:true\n */\n if (switchConf !== undefined) {\n // it is a switch\n const onOrOffCmdVal = cmd === switchConf.cmdOn ? true : false;\n await this.setStateAsync(`${pth}.${switchConf.id}`, { val: onOrOffCmdVal, ack: true });\n await this.setStateAsync(`${pth}.${switchConf.cmdOn}`, { val: onOrOffCmdVal, ack: true });\n await this.setStateAsync(`${pth}.${switchConf.cmdOff}`, { val: !onOrOffCmdVal, ack: true });\n } else {\n // No switch\n if (typeof stateObj.val === 'boolean') {\n const idx = this.getIndexFromConf(CONST.cmds, ['id'], cmd);\n if (idx !== -1) {\n if (CONST.cmds[idx].type === 'boolean') {\n // Is a button\n await this.setStateAsync(stateId, { val: true, ack: true });\n } else {\n // This should actually not happen, as we just define buttons in commands, but anyway\n this.log.warn(`${fully.name}: ${stateId} - val: ${stateObj.val} is boolean, but cmd ${cmd} is not defined in CONF`);\n await this.setStateAsync(stateId, { val: stateObj.val, ack: true });\n }\n } else {\n this.log.warn(`${fully.name}: ${stateId} - val: ${stateObj.val}, cmd ${cmd} is not defined in CONF`);\n }\n } else {\n // Non-boolean, so just set val with ack:true...\n await this.setStateAsync(stateId, { val: stateObj.val, ack: true });\n }\n }\n } else {\n // log, more log lines were already published by this.restApi_inst.sendCmd()\n this.log.debug(`${fully.name}: restApiSendCmd() was not successful (${stateId})`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Get Fully Object per provided key and value\n * {\n * '192.168.10.20': {name: 'Tablet Kitchen', id:'Tablet-Kitchen', ip:'192.168.10.20', ...},\n * '192.168.10.30': {name: 'Tablet Hallway', id:'Tablet-Hallway', ip:'192.168.10.30', ...},\n * }\n * getFullyByKey('id', 'Tablet-Hallway') will return the second object...\n * @param keyId - e.g. 'id', 'name', ...\n * @param value - e.g. 'Tablet Hallway', ...\n * @returns - fully object or false if not found\n */\n private getFullyByKey(keyId: string, value: any): IDevice | false {\n for (const ip in this.fullysEnbl) {\n if (keyId in this.fullysEnbl[ip]) {\n const lpKeyId = keyId as string;\n // Wow, what a line. Due to: https://bobbyhadz.com/blog/typescript-element-implicitly-has-any-type-expression\n const lpVal = this.fullysEnbl[ip][lpKeyId as keyof (typeof this.fullysEnbl)[typeof ip]];\n if (lpVal === value) {\n return this.fullysEnbl[ip];\n }\n }\n }\n return false;\n }\n\n /**\n * Gets Index for given keys and a value\n * @param config - config like CONST.cmds\n * @param keys - like ['mqttOn','mqttOff']\n * @param cmd - like 'onScreensaverStart'\n * @returns Index (0-...), or -1 if not found\n */\n private getIndexFromConf(config: { [k: string]: any }[], keys: string[], cmd: string): number {\n try {\n let index = -1;\n for (const key of keys) {\n // Get array index\n index = config.findIndex((x: { [k: string]: any }) => x[key] === cmd);\n if (index !== -1) break;\n }\n return index;\n } catch (e) {\n this.log.error(this.err2Str(e));\n return -1;\n }\n }\n\n /**\n * Is called when adapter shuts down - callback has to be called under any circumstances!\n */\n private async onUnload(callback: () => void): Promise {\n try {\n // All Fullys: Set alive status to null\n if (this.fullysAll) {\n for (const ip in this.fullysAll) {\n // We check first if object exists, as there were errors in log on when updating adpater via Github (related to missing objects)\n if (await this.getObjectAsync(this.fullysAll[ip].id)) {\n this.setState(this.fullysAll[ip].id + '.alive', { val: null, ack: true });\n }\n }\n }\n\n // Clear MQTT server timeouts\n if (this.mqtt_Server) {\n for (const clientId in this.mqtt_Server.devices) {\n // @ts-expect-error \"Type 'null' is not assignable to type 'Timeout'.ts(2345)\" - we check for not being null via \"if\"\n if (this.mqtt_Server.devices[clientId].timeoutNoUpdate) this.clearTimeout(this.mqtt_Server.devices[clientId].timeoutNoUpdate);\n }\n }\n\n // destroy MQTT Server\n if (this.mqtt_Server) {\n this.mqtt_Server.terminate();\n }\n\n callback();\n } catch (e) {\n callback();\n }\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial | undefined) => new FullyMqtt(options);\n} else {\n // otherwise start the instance directly\n (() => new FullyMqtt())();\n}\n"], + "mappings": "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", "names": ["idx"] } diff --git a/io-package.json b/io-package.json index 7e3bda9..d237da9 100644 --- a/io-package.json +++ b/io-package.json @@ -1,211 +1,211 @@ { - "common": { - "name": "fully-mqtt", - "version": "0.1.0", - "news": { - "0.1.0": { - "en": "**Breaking Change**: Using MQTT is required, and removed option to deactivate MQTT. Reasons: 1. requesting info thru REST API is redundant and simply not needed since MQTT is available, 2. It does not make sense to not use MQTT since it provides live updates of states.\n**Breaking Change**: Object 'mqttActivated' ('fully-mqtt.x.yyy.mqttActivated') is no longer used. Feel free to delete these objects if you update from a previous version as these will not be deleted automatically.\nRemoved all REST API info requests, as MQTT use is now required\nCode improvements", - "de": "*Breaking Change*: Die Verwendung von MQTT ist erforderlich und entfernte Option, um MQTT zu deaktivieren. Gründe: 1. Anfragen an info thru REST API ist redundant und einfach nicht benötigt, da MQTT verfügbar ist, 2. Es macht keinen Sinn, MQTT nicht zu verwenden, da es Live-Updates von Zuständen bietet.\n*Breaking Change*: Objekt mqtActivated (ful-mqtt.x.yyyy.mqtActivated) wird nicht mehr verwendet. Fühlen Sie sich frei, diese Objekte zu löschen, wenn Sie von einer vorherigen Version aktualisieren, da diese nicht automatisch gelöscht werden.\nEntfernt alle REST API-Info-Anfragen, da MQTT jetzt verwendet wird\nVerbesserungen des Codes", - "ru": "*Восстановление изменений*: Использование MQTT требуется, и удаленный вариант для деактивации MQTT. Причины: 1. запрос информации thru REST API является избыточным и просто не требуется, так как MQTT доступен, 2. Это не имеет смысла не использовать MQTT, так как он предоставляет живые обновления государств.\n*Восстановление изменений*: Объект mqttActivated (fully-mqtt.x.yy.mqttActivated) больше не используется. Не стесняйтесь удалить эти объекты, если вы обновляете с предыдущей версии, поскольку они не будут удалены автоматически.\nУдалены все запросы информации о REST API, так как использование MQTT теперь требуется\nУлучшения кода", - "pt": "* Mudança de travagem*: O uso do MQTT é necessário e a opção removida para desativar o MQTT. Razões: 1. solicitando informações thru REST API é redundante e simplesmente não é necessário desde MQTT está disponível, 2. Não faz sentido para não usar MQTT, uma vez que fornece atualizações ao vivo de estados.\n* Mudança de travagem*: Object mqttActivated (fully-mqtt.x.yyyy.mqttActivated) não é mais usado. Sinta-se livre para excluir esses objetos se você atualizar de uma versão anterior, pois estes não serão excluídos automaticamente.\nRemovido todos os pedidos de informações da API REST, já que o uso do MQTT agora é necessário\nMelhorias de código", - "nl": "♪ Breaking Change ♪ Het gebruik van MQTT is vereist en verwijderde optie om MQT te deactiveren. Reasons: 1. verzoekt info RESTAPI is overbodig en simpelweg niet meer nodig sinds MQTT beschikbaar is, 2.\n♪ Breaking Change ♪ Object MqtActivated (vol-mqtt. ..y.mqttActivatie is niet meer gebruikt. Voel je vrij om deze objecten te verwijderen als je updatet van een vorige versie zoals deze niet automatisch gewist worden.\nVerwijder alle API-informatie verzoeken, zoals MQTT nu nodig heeft\nCode verbetering", - "fr": "*Breaking Change*: L'utilisation de MQTT est requise, et l'option supprimée pour désactiver MQTT. Motifs: 1. demander des informations thru L'API REST est redondante et tout simplement pas nécessaire puisque MQTT est disponible, 2. Il n'est pas logique de ne pas utiliser MQTT car il fournit des mises à jour en direct des états.\n*Breaking Change*: Objet mqttActivated (fully-mqtt.x.yyy.mqttActivated) n'est plus utilisé. N'hésitez pas à supprimer ces objets si vous mettez à jour depuis une version précédente car ceux-ci ne seront pas supprimés automatiquement.\nEnlevé toutes les demandes d'information de l'API REST, comme l'utilisation de MQTT est maintenant requise\nAmélioration des codes", - "it": "*Breaking Change*: Utilizzando MQTT è necessario, e l'opzione rimossa per disattivare MQTT. Motivi: 1. richiedendo info thru REST API è ridondante e semplicemente non necessario dal momento che MQTT è disponibile, 2. Non ha senso non utilizzare MQTT dal momento che fornisce aggiornamenti dal vivo degli stati.\n*Breaking Change*: Oggetti mqttActivated (completamente-mqtt.x.yyy.mqttActivated) non è più utilizzato. Sentitevi liberi di eliminare questi oggetti se si aggiorna da una versione precedente in quanto questi non verranno eliminati automaticamente.\nRimuovete tutte le richieste di informazioni REST API, in quanto l'uso MQTT è ora richiesto\nMiglioramento del codice", - "es": "*Breaking Change*: Utilizar MQTT es necesario y eliminar la opción para desactivar MQTT. Razones: 1. solicitar información a través de REST API es redundante y simplemente no es necesario ya que MQTT está disponible, 2. No tiene sentido no utilizar MQTT ya que proporciona actualizaciones en vivo de estados.\n*Breaking Change*: Objeto mqttActivated (fully-mqtt.x.yyyy.mqttActivated) ya no se utiliza. Siéntase libre de eliminar estos objetos si se actualiza de una versión anterior, ya que estos no serán eliminados automáticamente.\nQuitar todas las solicitudes de información de REST API, ya que el uso de MQTT es ahora necesario\nMejoras del Código", - "pl": "*Breaking Change* (ang.). Zastosowanie MQTT jest wymagane i usunięcie opcji w celu dezaktywowania MQTT. Rozum: 1. żądanie API info thru REST jest zbędne i po prostu nie potrzebne, ponieważ MQTT jest dostępny, 2. Nie ma pojęcia, aby nie korzystać z MQTT.\n*Breaking Change* (ang.). Obiekt mqttActivated (w pełni mqtt.x.yyy.mqttActivated) nie jest już używany. Uczucie wolnej usuwania tych obiektów, jeśli uaktualnisz się od poprzedniej wersji, ponieważ nie zostanie usunięta automatycznie.\nUsunął wszystkie żądania API REST, ponieważ obecnie wymagane jest użycie MQTT\nPoprawa kodu", - "uk": "* Зміна маршрутизації*: За допомогою MQTT необхідно і видалити опцію для деактивації MQTT. Причини: 1. Запит на інформацію про API REST є надмірним і просто не потрібно, оскільки MQTT є доступним, 2. Він не має сенсу не використовувати MQTT, оскільки він забезпечує живі оновлення станів.\n* Зміна маршрутизації*: Об'єкт mqttActivated (fully-mqtt.x.yyy.mqttActivated) не використовується. Відчуйте безкоштовно, щоб видалити ці об'єкти, якщо ви оновлення з попередньої версії, оскільки це не буде видалено автоматично.\nВилучено всі запити REST API, оскільки використання MQTT тепер потрібно\nПоліпшення коду", - "zh-cn": "* 改动*: 需要使用MQTT,并取消取消对MQTT进行交易的选择。 理由: 1. 要求自技合会获得以来,光是多余的,而且根本不必要。 2 由于它提供实况的最新资料,这并不意味着不使用技转中心。.\n* 改动*: 不再使用物体(原状-mqt.x.y.mqt Activ) 如果你从前一版本中更新,不自动删除这些物品,那就不能删除。.\n由于现在需要使用氢氟烷烃的用途,取消所有含铅酸酐的要求。\nA. 守则的改进" - }, - "0.0.12": { - "en": "Logging improved\nInternal: test git branch", - "de": "Protokollierung verbessert\nIntern: Test git branch", - "ru": "Улучшается вход\nВнутреннее: test git Branch", - "pt": "Melhorar o registo\nInterna: ramo git de teste", - "nl": "Logging verbeterd\nVertaling:", - "fr": "Logging improved\nInterne: branche git test", - "it": "Registrazione migliorata\nInterno: ramo di prova", - "es": "Mejoramiento de los registros\nInterno: rama de punto de prueba", - "pl": "Logging poprawił\nWewnątrz: test gimnazjum", - "uk": "Удосконалено рухи\nВнутрішня: тест git branch", - "zh-cn": "B. 后勤改善\n内部:试验室" - }, - "0.0.11": { - "en": "Fix: Remote admin password issue\nImproved debug and error log when sending REST API commands", - "de": "Fix: Remote Admin Passwort Problem\nVerbesserte Debug- und Fehlerprotokolle beim Senden von REST API-Befehlen", - "ru": "Исправление: Удаленный пароль admin\nУлучшена отладка и ошибка лога при отправке команд REST API", - "pt": "Corrigir: Problema de senha de administrador remoto\nRegistro melhorado de depuração e erro ao enviar comandos de API REST", - "nl": "Verwijder het wachtwoord\nImproviseerde debug en fouten logboek als ze de API commando's sturen", - "fr": "Correction : Problème de mot de passe administrateur distant\nDébug amélioré et journal d'erreur lors de l'envoi de commandes API REST", - "it": "Risolvere il problema della password di amministratore remoto\nMigliorato il debug e il log degli errori quando si inviano i comandi REST API", - "es": "Corrección: Problema de contraseña de administración remota\nMejor depuración y registro de errores al enviar comandos REST API", - "pl": "Fix: Remote admin password\nImprowed debug and error log, gdy wysłano komendy API REST", - "uk": "Виправлення: Віддалене видалення пароля адміністратора\nУдосконалено дебат і журнал помилок при надсиланні команд API REST", - "zh-cn": "Fix:遥感密码问题\n在派遣区域采购计划指挥员时,改进了降解和错误的逻辑" - }, - "0.0.10": { - "en": "Do not show Remote Admin Password in debug log ([#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14))", - "de": "Nicht anzeigen Remote Admin Passwort in debug log [#14](https://github.com/Acgua/ioBroker.ful-mqt/issues/14)", - "ru": "Не показывать Remote Admin Password в журнале debug [#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14)", - "pt": "Não mostre Senha de Administrador Remoto no log de depuração [#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14)", - "nl": "Laat Remote Admin Pasword niet zien in debug log 14:", - "fr": "Ne pas afficher Remote Admin Password in debug log [#14](https://github.com/Acgua/ioBroker.fully-mqt/issues/14)", - "it": "Non mostrare la password di amministratore remoto nel registro di debug [#14](https://github.com/Acgua/ioBroker.fully-mqt/issues/14)", - "es": "No mostrar contraseña de administrador remoto en el registro de depuración [#14](https://github.com/Acgua/ioBroker.fully-mqt/issues/14)", - "pl": "Nie pokazuje Remote Admin Password w debug log #14(https:/github.com/Acgua/ioBroker.ful-mqtt/issues/14)", - "uk": "Не показувати пароль віддаленого адміністратора в журналі debug [#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14)", - "zh-cn": "不要在德布格逻辑(第14号)中展示遥感词(http://github.com/Acgua/ioBroker.ful-mqt/issues/14)。" - }, - "0.0.9": { - "en": "**Breaking Change** If you update from a previous version: In Adapter config, device table: disable and enable your all of your devices, and save options afterwards.\nBug fix: enabled devices in config were skipped\nEnglish adapter documentation added.\nAdapter was added to Weblate ([#108](https://github.com/ioBrokerTranslator/requests/issues/108))\nVerified that device objects are removed when device was deleted or renamed in config", - "de": "**Breaking Change** If you update from a previous version: In Adapter config, device table: disable and enable your all of your devices, and save options afterwards.\nBug fix: enabled devices in config were skipped\nEnglish adapter documentation added.\nAdapter was added to Weblate ([#108](https://github.com/ioBrokerTranslator/requests/issues/108))\nVerified that device objects are removed when device was deleted or renamed in config", - "ru": "*Восхождение изменения* Если вы обновляете из предыдущей версии: В адаптере config, таблица устройств: отключите и включите все ваши устройства и сохраните опции после.\nИсправление баг: включенные устройства в конфиг были пропущены\nДобавлена английская документация адаптера.\nАдаптер добавлен в Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nПроверено, что объекты устройства удаляются, когда устройство было удалено или переименовано в config", - "pt": "* Mudança de travagem* Se você atualizar de uma versão anterior: Na configuração do adaptador, tabela do dispositivo: desabilite e ative todos os seus dispositivos e salve opções depois.\nCorreção de Bug: dispositivos habilitados em config foram ignorados\nDocumentação do adaptador inglês adicionada.\nAdapter foi adicionado ao Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVerificado que os objetos do dispositivo são removidos quando o dispositivo foi excluído ou renomeado em config", - "nl": "♪ Breaking Change ♪ Als je update van een vorige versie: In Adapter Config, apparaattafel, uitschakelen en je apparatuur inschakelen, en daarna opties sparen.\nBug reparatie: gehandicapte apparaten in het leger zijn overgeslagen\nEngelse papieren toegevoegd.\nAdapter werd toegevoegd aan Weblate £108\nVertaald door:", - "fr": "*Breaking Change* Si vous mettez à jour à partir d'une version précédente: Dans la configuration d'adaptateur, tableau de périphériques : désactivez et activez tous vos appareils, et enregistrez des options après.\nCorrection de bug: les dispositifs activés dans la configuration ont été ignorés\nLa documentation de l'adaptateur anglais a été ajoutée.\nL ' adaptateur a été ajouté à Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVérifié que les objets de périphérique sont enlevés lorsque le dispositif a été supprimé ou renommé dans la configuration", - "it": "*Breaking Change* Se si aggiorna da una versione precedente: Nella configurazione dell'adattatore, tabella del dispositivo: disabilitare e abilitare tutti i dispositivi e salvare le opzioni dopo.\nCorrezione bug: i dispositivi abilitati in configurazione sono stati saltati\nDocumentazione adattatore inglese aggiunto.\nAdattatore è stato aggiunto a Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVerifica che gli oggetti del dispositivo vengano rimossi quando il dispositivo è stato cancellato o rinominato in configurazione", - "es": "*Breaking Change* Si se actualiza de una versión anterior: En el config Adapter, tabla de dispositivos: deshabilitar y activar todos sus dispositivos, y guardar opciones después.\nCorrección de errores: dispositivos habilitados en config fueron saltados\nDocumentación de adaptador en inglés.\nEl adaptador se añadió a la página web [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVerificado que los objetos de dispositivo se eliminan cuando el dispositivo fue eliminado o renombrado en config", - "pl": "*Breaking Change* (ang.). Jeśli uaktualnisz się z poprzedniej wersji: W Adapter config znajduje się tablica urządzenie: zdyskwalifikowana i umożliwiająca wszystkie swoje urządzenia i zachowane opcje.\nUruchomienie Bugu: umożliwiane urządzenia w konfigunie\nAngielska dokumentacja adaptacyjna.\nAdapter został dodany do Weblate #108(https:/github.com/ioBrokerTranslator/requests/issues/108)\nWeryfikowano, że obiekty urządzenia są usuwane, gdy urządzenie zostało usunięte lub przemianowane na config", - "uk": "* Зміна трафіку* Якщо ви оновлення з попередньої версії: У налаштуваннях адаптера, таблиці пристрою: відключити і увімкнути всі пристрої, і зберегти параметри після.\nВиправлення помилок: ввімкнені пристрої в конфігурації\nДодана документація англійською мовою.\nДодано адаптер в Weblate [#108](https://github.com/ioBrokerTranslator/requests/products/108)\nПеревірено, що пристрої видаляються при видаленні пристрою або перейменовані в налаштування", - "zh-cn": "* 改动* 如果你从以前的版本更新: 在阿达佩特,装置表:可区别,使你的所有装置得以使用,并挽救后的选择。.\nBug fix:破裂的装置被焚化\n英文适应文件增加。.\n添加到以下网址:www.github.com/ioBrokertranslator/requests/issues/108\n在装置被删除或改装时,清除了该装置的物品。" - }, - "0.0.8": { - "en": "Github Workflow Deploy - try to fix GitHub release failed with status: 403 undefined ([Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128))", - "de": "Github Workflow Deploy - versuchen, GitHub-Release mit dem Status zu beheben: 403 undefiniert [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", - "ru": "Github Workflow Deploy - попробуйте исправить GitHub релиз не удалось со статусом: 403 undefined [Ссылка](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", - "pt": "Github Workflow Deploy - tentar corrigir a versão GitHub falhou com o status: 403 indefinido [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", - "nl": "Github Workflow Deploy - proberen GitHub vrijlating te repareren gefaald met status: 403 onbepaalde [Link ) [Ghtub /com/ / 12-g-g-g-g-g-g-g-g-g-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-p-p- /12-b", - "fr": "Github Workflow Deploy - essayer de réparer GitHub release failed with status: 403 undefined [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", - "it": "Github Workflow Deploy - cercare di risolvere GitHub rilascio fallito con stato: 403 non definito [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", - "es": "Github Workflow Deploy - try to fix GitHub release failed with status: 403 un defined [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", - "pl": "Github Workflow Deploy – próba naprawy GitHub nie powiodła się ze statusem: 403 niedefiniowane Link(https:/github.com/softprops/action-gh-release/issues/236#comment-11500128", - "uk": "Github Workflow Deploy - спробувати виправити реліз GitHub не вдалося з статусом: 403 undefined [Link](https://github.com/softprops/action-gh-release/issues/236#productcomment-1150530128)", - "zh-cn": "Gthub工作流程Deploy——试图确定GitHub的释放没有地位:403个未界定的[Link](http://github.com/softs/action-gh-release/issues/236#issuecomment-1150530128)" - }, - "0.0.7": { - "en": "Test-only of release etc.", - "de": "Test-only of release etc.", - "ru": "Тест-только релиза и т.д.", - "pt": "Test-only da liberação etc.", - "nl": "Test-only of release etc.", - "fr": "Test-only of release etc.", - "it": "Test-solo di rilascio ecc.", - "es": "Prueba solo de liberación, etc.", - "pl": "Test-only of release (ang.).", - "uk": "Тест-тільки релізу і т.д.", - "zh-cn": "试验释放等。." - } + "common": { + "name": "fully-mqtt", + "version": "0.1.0", + "news": { + "0.1.0": { + "en": "**Breaking Change**: Using MQTT is required, and removed option to deactivate MQTT. Reasons: 1. requesting info thru REST API is redundant and simply not needed since MQTT is available, 2. It does not make sense to not use MQTT since it provides live updates of states.\n**Breaking Change**: Object 'mqttActivated' ('fully-mqtt.x.yyy.mqttActivated') is no longer used. Feel free to delete these objects if you update from a previous version as these will not be deleted automatically.\nRemoved all REST API info requests, as MQTT use is now required\nCode improvements", + "de": "*Breaking Change*: Die Verwendung von MQTT ist erforderlich und entfernte Option, um MQTT zu deaktivieren. Gründe: 1. Anfragen an info thru REST API ist redundant und einfach nicht benötigt, da MQTT verfügbar ist, 2. Es macht keinen Sinn, MQTT nicht zu verwenden, da es Live-Updates von Zuständen bietet.\n*Breaking Change*: Objekt mqtActivated (ful-mqtt.x.yyyy.mqtActivated) wird nicht mehr verwendet. Fühlen Sie sich frei, diese Objekte zu löschen, wenn Sie von einer vorherigen Version aktualisieren, da diese nicht automatisch gelöscht werden.\nEntfernt alle REST API-Info-Anfragen, da MQTT jetzt verwendet wird\nVerbesserungen des Codes", + "ru": "*Восстановление изменений*: Использование MQTT требуется, и удаленный вариант для деактивации MQTT. Причины: 1. запрос информации thru REST API является избыточным и просто не требуется, так как MQTT доступен, 2. Это не имеет смысла не использовать MQTT, так как он предоставляет живые обновления государств.\n*Восстановление изменений*: Объект mqttActivated (fully-mqtt.x.yy.mqttActivated) больше не используется. Не стесняйтесь удалить эти объекты, если вы обновляете с предыдущей версии, поскольку они не будут удалены автоматически.\nУдалены все запросы информации о REST API, так как использование MQTT теперь требуется\nУлучшения кода", + "pt": "* Mudança de travagem*: O uso do MQTT é necessário e a opção removida para desativar o MQTT. Razões: 1. solicitando informações thru REST API é redundante e simplesmente não é necessário desde MQTT está disponível, 2. Não faz sentido para não usar MQTT, uma vez que fornece atualizações ao vivo de estados.\n* Mudança de travagem*: Object mqttActivated (fully-mqtt.x.yyyy.mqttActivated) não é mais usado. Sinta-se livre para excluir esses objetos se você atualizar de uma versão anterior, pois estes não serão excluídos automaticamente.\nRemovido todos os pedidos de informações da API REST, já que o uso do MQTT agora é necessário\nMelhorias de código", + "nl": "♪ Breaking Change ♪ Het gebruik van MQTT is vereist en verwijderde optie om MQT te deactiveren. Reasons: 1. verzoekt info RESTAPI is overbodig en simpelweg niet meer nodig sinds MQTT beschikbaar is, 2.\n♪ Breaking Change ♪ Object MqtActivated (vol-mqtt. ..y.mqttActivatie is niet meer gebruikt. Voel je vrij om deze objecten te verwijderen als je updatet van een vorige versie zoals deze niet automatisch gewist worden.\nVerwijder alle API-informatie verzoeken, zoals MQTT nu nodig heeft\nCode verbetering", + "fr": "*Breaking Change*: L'utilisation de MQTT est requise, et l'option supprimée pour désactiver MQTT. Motifs: 1. demander des informations thru L'API REST est redondante et tout simplement pas nécessaire puisque MQTT est disponible, 2. Il n'est pas logique de ne pas utiliser MQTT car il fournit des mises à jour en direct des états.\n*Breaking Change*: Objet mqttActivated (fully-mqtt.x.yyy.mqttActivated) n'est plus utilisé. N'hésitez pas à supprimer ces objets si vous mettez à jour depuis une version précédente car ceux-ci ne seront pas supprimés automatiquement.\nEnlevé toutes les demandes d'information de l'API REST, comme l'utilisation de MQTT est maintenant requise\nAmélioration des codes", + "it": "*Breaking Change*: Utilizzando MQTT è necessario, e l'opzione rimossa per disattivare MQTT. Motivi: 1. richiedendo info thru REST API è ridondante e semplicemente non necessario dal momento che MQTT è disponibile, 2. Non ha senso non utilizzare MQTT dal momento che fornisce aggiornamenti dal vivo degli stati.\n*Breaking Change*: Oggetti mqttActivated (completamente-mqtt.x.yyy.mqttActivated) non è più utilizzato. Sentitevi liberi di eliminare questi oggetti se si aggiorna da una versione precedente in quanto questi non verranno eliminati automaticamente.\nRimuovete tutte le richieste di informazioni REST API, in quanto l'uso MQTT è ora richiesto\nMiglioramento del codice", + "es": "*Breaking Change*: Utilizar MQTT es necesario y eliminar la opción para desactivar MQTT. Razones: 1. solicitar información a través de REST API es redundante y simplemente no es necesario ya que MQTT está disponible, 2. No tiene sentido no utilizar MQTT ya que proporciona actualizaciones en vivo de estados.\n*Breaking Change*: Objeto mqttActivated (fully-mqtt.x.yyyy.mqttActivated) ya no se utiliza. Siéntase libre de eliminar estos objetos si se actualiza de una versión anterior, ya que estos no serán eliminados automáticamente.\nQuitar todas las solicitudes de información de REST API, ya que el uso de MQTT es ahora necesario\nMejoras del Código", + "pl": "*Breaking Change* (ang.). Zastosowanie MQTT jest wymagane i usunięcie opcji w celu dezaktywowania MQTT. Rozum: 1. żądanie API info thru REST jest zbędne i po prostu nie potrzebne, ponieważ MQTT jest dostępny, 2. Nie ma pojęcia, aby nie korzystać z MQTT.\n*Breaking Change* (ang.). Obiekt mqttActivated (w pełni mqtt.x.yyy.mqttActivated) nie jest już używany. Uczucie wolnej usuwania tych obiektów, jeśli uaktualnisz się od poprzedniej wersji, ponieważ nie zostanie usunięta automatycznie.\nUsunął wszystkie żądania API REST, ponieważ obecnie wymagane jest użycie MQTT\nPoprawa kodu", + "uk": "* Зміна маршрутизації*: За допомогою MQTT необхідно і видалити опцію для деактивації MQTT. Причини: 1. Запит на інформацію про API REST є надмірним і просто не потрібно, оскільки MQTT є доступним, 2. Він не має сенсу не використовувати MQTT, оскільки він забезпечує живі оновлення станів.\n* Зміна маршрутизації*: Об'єкт mqttActivated (fully-mqtt.x.yyy.mqttActivated) не використовується. Відчуйте безкоштовно, щоб видалити ці об'єкти, якщо ви оновлення з попередньої версії, оскільки це не буде видалено автоматично.\nВилучено всі запити REST API, оскільки використання MQTT тепер потрібно\nПоліпшення коду", + "zh-cn": "* 改动*: 需要使用MQTT,并取消取消对MQTT进行交易的选择。 理由: 1. 要求自技合会获得以来,光是多余的,而且根本不必要。 2 由于它提供实况的最新资料,这并不意味着不使用技转中心。.\n* 改动*: 不再使用物体(原状-mqt.x.y.mqt Activ) 如果你从前一版本中更新,不自动删除这些物品,那就不能删除。.\n由于现在需要使用氢氟烷烃的用途,取消所有含铅酸酐的要求。\nA. 守则的改进" + }, + "0.0.12": { + "en": "Logging improved\nInternal: test git branch", + "de": "Protokollierung verbessert\nIntern: Test git branch", + "ru": "Улучшается вход\nВнутреннее: test git Branch", + "pt": "Melhorar o registo\nInterna: ramo git de teste", + "nl": "Logging verbeterd\nVertaling:", + "fr": "Logging improved\nInterne: branche git test", + "it": "Registrazione migliorata\nInterno: ramo di prova", + "es": "Mejoramiento de los registros\nInterno: rama de punto de prueba", + "pl": "Logging poprawił\nWewnątrz: test gimnazjum", + "uk": "Удосконалено рухи\nВнутрішня: тест git branch", + "zh-cn": "B. 后勤改善\n内部:试验室" + }, + "0.0.11": { + "en": "Fix: Remote admin password issue\nImproved debug and error log when sending REST API commands", + "de": "Fix: Remote Admin Passwort Problem\nVerbesserte Debug- und Fehlerprotokolle beim Senden von REST API-Befehlen", + "ru": "Исправление: Удаленный пароль admin\nУлучшена отладка и ошибка лога при отправке команд REST API", + "pt": "Corrigir: Problema de senha de administrador remoto\nRegistro melhorado de depuração e erro ao enviar comandos de API REST", + "nl": "Verwijder het wachtwoord\nImproviseerde debug en fouten logboek als ze de API commando's sturen", + "fr": "Correction : Problème de mot de passe administrateur distant\nDébug amélioré et journal d'erreur lors de l'envoi de commandes API REST", + "it": "Risolvere il problema della password di amministratore remoto\nMigliorato il debug e il log degli errori quando si inviano i comandi REST API", + "es": "Corrección: Problema de contraseña de administración remota\nMejor depuración y registro de errores al enviar comandos REST API", + "pl": "Fix: Remote admin password\nImprowed debug and error log, gdy wysłano komendy API REST", + "uk": "Виправлення: Віддалене видалення пароля адміністратора\nУдосконалено дебат і журнал помилок при надсиланні команд API REST", + "zh-cn": "Fix:遥感密码问题\n在派遣区域采购计划指挥员时,改进了降解和错误的逻辑" + }, + "0.0.10": { + "en": "Do not show Remote Admin Password in debug log ([#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14))", + "de": "Nicht anzeigen Remote Admin Passwort in debug log [#14](https://github.com/Acgua/ioBroker.ful-mqt/issues/14)", + "ru": "Не показывать Remote Admin Password в журнале debug [#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14)", + "pt": "Não mostre Senha de Administrador Remoto no log de depuração [#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14)", + "nl": "Laat Remote Admin Pasword niet zien in debug log 14:", + "fr": "Ne pas afficher Remote Admin Password in debug log [#14](https://github.com/Acgua/ioBroker.fully-mqt/issues/14)", + "it": "Non mostrare la password di amministratore remoto nel registro di debug [#14](https://github.com/Acgua/ioBroker.fully-mqt/issues/14)", + "es": "No mostrar contraseña de administrador remoto en el registro de depuración [#14](https://github.com/Acgua/ioBroker.fully-mqt/issues/14)", + "pl": "Nie pokazuje Remote Admin Password w debug log #14(https:/github.com/Acgua/ioBroker.ful-mqtt/issues/14)", + "uk": "Не показувати пароль віддаленого адміністратора в журналі debug [#14](https://github.com/Acgua/ioBroker.fully-mqtt/issues/14)", + "zh-cn": "不要在德布格逻辑(第14号)中展示遥感词(http://github.com/Acgua/ioBroker.ful-mqt/issues/14)。" + }, + "0.0.9": { + "en": "**Breaking Change** If you update from a previous version: In Adapter config, device table: disable and enable your all of your devices, and save options afterwards.\nBug fix: enabled devices in config were skipped\nEnglish adapter documentation added.\nAdapter was added to Weblate ([#108](https://github.com/ioBrokerTranslator/requests/issues/108))\nVerified that device objects are removed when device was deleted or renamed in config", + "de": "**Breaking Change** If you update from a previous version: In Adapter config, device table: disable and enable your all of your devices, and save options afterwards.\nBug fix: enabled devices in config were skipped\nEnglish adapter documentation added.\nAdapter was added to Weblate ([#108](https://github.com/ioBrokerTranslator/requests/issues/108))\nVerified that device objects are removed when device was deleted or renamed in config", + "ru": "*Восхождение изменения* Если вы обновляете из предыдущей версии: В адаптере config, таблица устройств: отключите и включите все ваши устройства и сохраните опции после.\nИсправление баг: включенные устройства в конфиг были пропущены\nДобавлена английская документация адаптера.\nАдаптер добавлен в Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nПроверено, что объекты устройства удаляются, когда устройство было удалено или переименовано в config", + "pt": "* Mudança de travagem* Se você atualizar de uma versão anterior: Na configuração do adaptador, tabela do dispositivo: desabilite e ative todos os seus dispositivos e salve opções depois.\nCorreção de Bug: dispositivos habilitados em config foram ignorados\nDocumentação do adaptador inglês adicionada.\nAdapter foi adicionado ao Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVerificado que os objetos do dispositivo são removidos quando o dispositivo foi excluído ou renomeado em config", + "nl": "♪ Breaking Change ♪ Als je update van een vorige versie: In Adapter Config, apparaattafel, uitschakelen en je apparatuur inschakelen, en daarna opties sparen.\nBug reparatie: gehandicapte apparaten in het leger zijn overgeslagen\nEngelse papieren toegevoegd.\nAdapter werd toegevoegd aan Weblate £108\nVertaald door:", + "fr": "*Breaking Change* Si vous mettez à jour à partir d'une version précédente: Dans la configuration d'adaptateur, tableau de périphériques : désactivez et activez tous vos appareils, et enregistrez des options après.\nCorrection de bug: les dispositifs activés dans la configuration ont été ignorés\nLa documentation de l'adaptateur anglais a été ajoutée.\nL ' adaptateur a été ajouté à Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVérifié que les objets de périphérique sont enlevés lorsque le dispositif a été supprimé ou renommé dans la configuration", + "it": "*Breaking Change* Se si aggiorna da una versione precedente: Nella configurazione dell'adattatore, tabella del dispositivo: disabilitare e abilitare tutti i dispositivi e salvare le opzioni dopo.\nCorrezione bug: i dispositivi abilitati in configurazione sono stati saltati\nDocumentazione adattatore inglese aggiunto.\nAdattatore è stato aggiunto a Weblate [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVerifica che gli oggetti del dispositivo vengano rimossi quando il dispositivo è stato cancellato o rinominato in configurazione", + "es": "*Breaking Change* Si se actualiza de una versión anterior: En el config Adapter, tabla de dispositivos: deshabilitar y activar todos sus dispositivos, y guardar opciones después.\nCorrección de errores: dispositivos habilitados en config fueron saltados\nDocumentación de adaptador en inglés.\nEl adaptador se añadió a la página web [#108](https://github.com/ioBrokerTranslator/requests/issues/108)\nVerificado que los objetos de dispositivo se eliminan cuando el dispositivo fue eliminado o renombrado en config", + "pl": "*Breaking Change* (ang.). Jeśli uaktualnisz się z poprzedniej wersji: W Adapter config znajduje się tablica urządzenie: zdyskwalifikowana i umożliwiająca wszystkie swoje urządzenia i zachowane opcje.\nUruchomienie Bugu: umożliwiane urządzenia w konfigunie\nAngielska dokumentacja adaptacyjna.\nAdapter został dodany do Weblate #108(https:/github.com/ioBrokerTranslator/requests/issues/108)\nWeryfikowano, że obiekty urządzenia są usuwane, gdy urządzenie zostało usunięte lub przemianowane na config", + "uk": "* Зміна трафіку* Якщо ви оновлення з попередньої версії: У налаштуваннях адаптера, таблиці пристрою: відключити і увімкнути всі пристрої, і зберегти параметри після.\nВиправлення помилок: ввімкнені пристрої в конфігурації\nДодана документація англійською мовою.\nДодано адаптер в Weblate [#108](https://github.com/ioBrokerTranslator/requests/products/108)\nПеревірено, що пристрої видаляються при видаленні пристрою або перейменовані в налаштування", + "zh-cn": "* 改动* 如果你从以前的版本更新: 在阿达佩特,装置表:可区别,使你的所有装置得以使用,并挽救后的选择。.\nBug fix:破裂的装置被焚化\n英文适应文件增加。.\n添加到以下网址:www.github.com/ioBrokertranslator/requests/issues/108\n在装置被删除或改装时,清除了该装置的物品。" + }, + "0.0.8": { + "en": "Github Workflow Deploy - try to fix GitHub release failed with status: 403 undefined ([Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128))", + "de": "Github Workflow Deploy - versuchen, GitHub-Release mit dem Status zu beheben: 403 undefiniert [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", + "ru": "Github Workflow Deploy - попробуйте исправить GitHub релиз не удалось со статусом: 403 undefined [Ссылка](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", + "pt": "Github Workflow Deploy - tentar corrigir a versão GitHub falhou com o status: 403 indefinido [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", + "nl": "Github Workflow Deploy - proberen GitHub vrijlating te repareren gefaald met status: 403 onbepaalde [Link ) [Ghtub /com/ / 12-g-g-g-g-g-g-g-g-g-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-b-p-p- /12-b", + "fr": "Github Workflow Deploy - essayer de réparer GitHub release failed with status: 403 undefined [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", + "it": "Github Workflow Deploy - cercare di risolvere GitHub rilascio fallito con stato: 403 non definito [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", + "es": "Github Workflow Deploy - try to fix GitHub release failed with status: 403 un defined [Link](https://github.com/softprops/action-gh-release/issues/236#issuecomment-1150530128)", + "pl": "Github Workflow Deploy – próba naprawy GitHub nie powiodła się ze statusem: 403 niedefiniowane Link(https:/github.com/softprops/action-gh-release/issues/236#comment-11500128", + "uk": "Github Workflow Deploy - спробувати виправити реліз GitHub не вдалося з статусом: 403 undefined [Link](https://github.com/softprops/action-gh-release/issues/236#productcomment-1150530128)", + "zh-cn": "Gthub工作流程Deploy——试图确定GitHub的释放没有地位:403个未界定的[Link](http://github.com/softs/action-gh-release/issues/236#issuecomment-1150530128)" + }, + "0.0.7": { + "en": "Test-only of release etc.", + "de": "Test-only of release etc.", + "ru": "Тест-только релиза и т.д.", + "pt": "Test-only da liberação etc.", + "nl": "Test-only of release etc.", + "fr": "Test-only of release etc.", + "it": "Test-solo di rilascio ecc.", + "es": "Prueba solo de liberación, etc.", + "pl": "Test-only of release (ang.).", + "uk": "Тест-тільки релізу і т.д.", + "zh-cn": "试验释放等。." + } + }, + "titleLang": { + "en": "Fully Kiosk Browser (MQTT)", + "de": "Fully Kiosk Browser (MQTT)", + "ru": "Fully Kiosk Browser (MQTT)", + "pt": "Fully Kiosk Browser (MQTT)", + "nl": "Fully Kiosk Browser (MQTT)", + "fr": "Fully Kiosk Browser (MQTT)", + "it": "Fully Kiosk Browser (MQTT)", + "es": "Fully Kiosk Browser (MQTT)", + "pl": "Fully Kiosk Browser (MQTT)", + "uk": "Fully Kiosk Browser (MQTT)", + "zh-cn": "Fully Kiosk Browser (MQTT)" + }, + "desc": { + "en": "Control Fully Kiosk Browser", + "de": "Fully Browser steuern", + "ru": "Полный контроль браузера киоска", + "pt": "Controle totalmente o navegador de quiosque", + "nl": "Controle Volledig Kiosk Browser", + "fr": "Contrôlez entièrement le navigateur Kiosk", + "it": "Controlla completamente il browser Kiosk", + "es": "Navegador de quiosco de control completo", + "pl": "Kontroluj w pełni przeglądarkę kiosku", + "uk": "Control Fully Kiosk Browser", + "zh-cn": "完全控制 Kiosk 浏览器" + }, + "authors": [ + "Acgua " + ], + "keywords": [ + "fully", + "kiosk", + "browser", + "tablet", + "mqtt" + ], + "license": "Apache-2.0", + "platform": "Javascript/Node.js", + "main": "build/main.js", + "icon": "fully-mqtt.png", + "enabled": true, + "extIcon": "https://raw.githubusercontent.com/Acgua/ioBroker.fully-mqtt/main/admin/fully-mqtt.png", + "readme": "https://github.com/Acgua/ioBroker.fully-mqtt/blob/main/README.md", + "loglevel": "info", + "mode": "daemon", + "type": "multimedia", + "compact": true, + "connectionType": "local", + "dataSource": "poll", + "adminUI": { + "config": "json" + }, + "docs": { + "en": [ + "docs/en/README.md" + ], + "de": [ + "docs/de/README.md" + ] + }, + "globalDependencies": [ + { + "admin": ">=6.0.0" + } + ], + "dependencies": [ + { + "js-controller": ">=4.0.15" + } + ] }, - "titleLang": { - "en": "Fully Kiosk Browser (MQTT)", - "de": "Fully Kiosk Browser (MQTT)", - "ru": "Fully Kiosk Browser (MQTT)", - "pt": "Fully Kiosk Browser (MQTT)", - "nl": "Fully Kiosk Browser (MQTT)", - "fr": "Fully Kiosk Browser (MQTT)", - "it": "Fully Kiosk Browser (MQTT)", - "es": "Fully Kiosk Browser (MQTT)", - "pl": "Fully Kiosk Browser (MQTT)", - "uk": "Fully Kiosk Browser (MQTT)", - "zh-cn": "Fully Kiosk Browser (MQTT)" + "native": { + "tableDevices": [], + "restUseHttps": false, + "restTimeout": 2000, + "mqttUser": "", + "mqttPassword": "", + "mqttPort": 3000, + "mqttPublishedInfoDelay": 30, + "mqttDoNotVerifyUserPw": true, + "mqttUpdateUnchangedObjects": false, + "mqttConnErrorsAsInfo": true }, - "desc": { - "en": "Control Fully Kiosk Browser", - "de": "Fully Browser steuern", - "ru": "Полный контроль браузера киоска", - "pt": "Controle totalmente o navegador de quiosque", - "nl": "Controle Volledig Kiosk Browser", - "fr": "Contrôlez entièrement le navigateur Kiosk", - "it": "Controlla completamente il browser Kiosk", - "es": "Navegador de quiosco de control completo", - "pl": "Kontroluj w pełni przeglądarkę kiosku", - "uk": "Control Fully Kiosk Browser", - "zh-cn": "完全控制 Kiosk 浏览器" - }, - "authors": [ - "Acgua " + "encryptedNative": [ + "mqttPassword" ], - "keywords": [ - "fully", - "kiosk", - "browser", - "tablet", - "mqtt" + "protectedNative": [ + "mqttPassword" ], - "license": "Apache-2.0", - "platform": "Javascript/Node.js", - "main": "build/main.js", - "icon": "fully-mqtt.png", - "enabled": true, - "extIcon": "https://raw.githubusercontent.com/Acgua/ioBroker.fully-mqtt/main/admin/fully-mqtt.png", - "readme": "https://github.com/Acgua/ioBroker.fully-mqtt/blob/main/README.md", - "loglevel": "info", - "mode": "daemon", - "type": "multimedia", - "compact": true, - "connectionType": "local", - "dataSource": "poll", - "adminUI": { - "config": "json" - }, - "docs": { - "en": [ - "docs/en/README.md" - ], - "de": [ - "docs/de/README.md" - ] - }, - "globalDependencies": [ - { - "admin": ">=6.0.0" - } - ], - "dependencies": [ - { - "js-controller": ">=4.0.15" - } + "objects": [], + "instanceObjects": [ + { + "_id": "info", + "type": "channel", + "common": { + "name": "Information" + }, + "native": {} + }, + { + "_id": "info.connection", + "type": "state", + "common": { + "role": "indicator.connected", + "name": "Device or service connected", + "type": "boolean", + "read": true, + "write": false, + "def": false + }, + "native": {} + } ] - }, - "native": { - "tableDevices": [], - "restUseHttps": false, - "restTimeout": 2000, - "mqttUser": "", - "mqttPassword": "", - "mqttPort": 3000, - "mqttPublishedInfoDelay": 30, - "mqttDoNotVerifyUserPw": true, - "mqttUpdateUnchangedObjects": false, - "mqttConnErrorsAsInfo": true - }, - "encryptedNative": [ - "mqttPassword" - ], - "protectedNative": [ - "mqttPassword" - ], - "objects": [], - "instanceObjects": [ - { - "_id": "info", - "type": "channel", - "common": { - "name": "Information" - }, - "native": {} - }, - { - "_id": "info.connection", - "type": "state", - "common": { - "role": "indicator.connected", - "name": "Device or service connected", - "type": "boolean", - "read": true, - "write": false, - "def": false - }, - "native": {} - } - ] } diff --git a/src/lib/adapter-config.d.ts b/src/lib/adapter-config.d.ts index ec5273c..170ac5b 100644 --- a/src/lib/adapter-config.d.ts +++ b/src/lib/adapter-config.d.ts @@ -21,6 +21,7 @@ declare global { mqttDoNotVerifyUserPw: true | false; mqttUpdateUnchangedObjects: true | false; mqttConnErrorsAsInfo: true | false; + mqttCreateDefaultEventObjects: true | false; restTimeout: number; } } diff --git a/src/lib/mqtt-server.ts b/src/lib/mqtt-server.ts index f77d911..ab4c66c 100644 --- a/src/lib/mqtt-server.ts +++ b/src/lib/mqtt-server.ts @@ -79,14 +79,14 @@ export class MqttServer { if (!this.adapter.isIpAddressValid(ip)) ip === undefined; } // Check if IP is an active device IP - if (ip && !Object.keys(this.adapter.fullys).includes(ip)) { + if (ip && !Object.keys(this.adapter.fullysEnbl).includes(ip)) { this.adapter.log.error(`[MQTT] Client ${client.id} not authorized: ${ip} is not an active Fully device IP per adapter settings.`); this.notAuthorizedClients.push(client.id); callback(null, false); return; } - const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`; + const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`; this.adapter.log.debug(`[MQTT] Client ${ipMsg} trys to authenticate...`); if (ip) this.devices[client.id].ip = ip; @@ -127,7 +127,7 @@ export class MqttServer { // IP const ip = this.devices[client.id].ip; - const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${client.id} (IP unknown)`; + const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${client.id} (IP unknown)`; this.adapter.log.debug(`[MQTT] Client ${ipMsg} connected to broker ${this.aedes.id}`); this.adapter.log.info(`[MQTT]🔗 Client ${ipMsg} successfully connected.`); @@ -177,9 +177,9 @@ export class MqttServer { // IP const ip = info.ip4; - const devMsg = `${this.adapter.fullys[ip].name} (${ip})`; + const devMsg = `${this.adapter.fullysEnbl[ip].name} (${ip})`; // Check IP - already done in this.aedes.authenticate, but just in case we were unable to get ip there - if (!Object.keys(this.adapter.fullys).includes(ip)) { + if (!Object.keys(this.adapter.fullysEnbl).includes(ip)) { this.adapter.log.error(`[MQTT] Client ${devMsg} Packet rejected: IP is not allowed per adapter settings. ${client.id}`); return; } @@ -203,7 +203,7 @@ export class MqttServer { */ if (!this.devices[client.id].mqttFirstReceived) { // show only once - this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullys[ip].name} = ${ip}`); + this.adapter.log.debug(`[MQTT] Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} = ${ip}`); // set to true this.devices[client.id].mqttFirstReceived = true; } @@ -261,7 +261,7 @@ export class MqttServer { }; if (!this.devices[client.id].mqttFirstReceived) { // show only once - this.adapter.log.info(`[MQTT] 🔗 Client ${client.id} = ${this.adapter.fullys[ip].name} (${ip})`); + this.adapter.log.info(`[MQTT] 🔗 Client ${client.id} = ${this.adapter.fullysEnbl[ip].name} (${ip})`); this.devices[client.id].mqttFirstReceived = true; } /** @@ -283,7 +283,7 @@ export class MqttServer { */ this.aedes.on('clientDisconnect', (client) => { const ip = this.devices[client.id].ip; - const logMsgName = ip ? this.adapter.fullys[ip].name : client.id; + const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id; if (this.adapter.config.mqttConnErrorsAsInfo) { this.adapter.log.info(`[MQTT] Client ${logMsgName} disconnected.`); } else { @@ -298,7 +298,7 @@ export class MqttServer { this.aedes.on('clientError', (client, e) => { if (this.notAuthorizedClients.includes(client.id)) return; // Error msg was already thrown in aedes.authenticate() before const ip = this.devices[client.id].ip; - const logMsgName = ip ? this.adapter.fullys[ip].name : client.id; + const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id; if (this.adapter.config.mqttConnErrorsAsInfo) { this.adapter.log.info(`[MQTT] ${logMsgName}: Client error - ${e.message}`); } else { @@ -310,7 +310,7 @@ export class MqttServer { this.aedes.on('connectionError', (client, e) => { const ip = this.devices[client.id].ip; - const logMsgName = ip ? this.adapter.fullys[ip].name : client.id; + const logMsgName = ip ? this.adapter.fullysEnbl[ip].name : client.id; if (this.adapter.config.mqttConnErrorsAsInfo) { this.adapter.log.info(`[MQTT] ${logMsgName}: Connection error - ${e.message}`); } else { @@ -369,7 +369,7 @@ export class MqttServer { private async scheduleCheckIfStillActive(clientId: string): Promise { try { const ip = this.devices[clientId].ip; - const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`; + const ipMsg = ip ? `${this.adapter.fullysEnbl[ip].name} (${ip})` : `${clientId} (IP unknown)`; // this.adapter.log.debug(`[MQTT] ${ipMsg}: - Start scheduleCheckIfStillActive`); // @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if" diff --git a/src/main.ts b/src/main.ts index a84bcf8..c39fd18 100644 --- a/src/main.ts +++ b/src/main.ts @@ -45,8 +45,8 @@ export class FullyMqtt extends utils.Adapter { * } * Note: we can use this.getFullyPerKey() to get fully object per provided key */ - public fullys: { [ip: string]: IDevice } = {}; // enabled Fullys only - public fullysNotEnabled: { [ip: string]: IDevice } = {}; // not enabled Fullys only + public fullysEnbl: { [ip: string]: IDevice } = {}; // enabled Fullys only + public fullysDisbl: { [ip: string]: IDevice } = {}; // not enabled Fullys only public fullysAll: { [ip: string]: IDevice } = {}; // enabled and not enabled Fullys // Has onMqttAlive() ever been called before? @@ -82,20 +82,23 @@ export class FullyMqtt extends utils.Adapter { return; } - for (const ip in this.fullys) { + for (const ip in this.fullysEnbl) { // Create Fully device objects - const res = await this.createFullyDeviceObjects(this.fullys[ip]); + const res = await this.createFullyDeviceObjects(this.fullysEnbl[ip]); + // REST API: Subscribe to command state changes - if (res) await this.subscribeStatesAsync(this.fullys[ip].id + '.Commands.*'); - // Set enabled state to true - this.setState(this.fullys[ip].id + '.enabled', { val: true, ack: true }); - // Set alive state to false initially - this.setState(this.fullysNotEnabled[ip].id + '.alive', { val: false, ack: true }); + if (res) await this.subscribeStatesAsync(this.fullysEnbl[ip].id + '.Commands.*'); + + // Set enabled and alive states + this.setState(this.fullysEnbl[ip].id + '.enabled', { val: true, ack: true }); + this.setState(this.fullysEnbl[ip].id + '.alive', { val: false, ack: true }); } - // Not enabled fullys: 1. Enabled state to false; 2. alive to null - for (const ip in this.fullysNotEnabled) { - this.setState(this.fullysNotEnabled[ip].id + '.enabled', { val: false, ack: true }); - this.setState(this.fullysNotEnabled[ip].id + '.alive', { val: null, ack: true }); + // Not enabled fullys (if object exists at all): 1. Enabled state to false; 2. alive to null + for (const ip in this.fullysDisbl) { + if (await this.getObjectAsync(this.fullysAll[ip].id)) { + this.setState(this.fullysDisbl[ip].id + '.enabled', { val: false, ack: true }); + this.setState(this.fullysDisbl[ip].id + '.alive', { val: null, ack: true }); + } } /** @@ -167,10 +170,12 @@ export class FullyMqtt extends utils.Adapter { } // Create MQTT Events Objects - // More states are created once a new Event is received. + // Any not yet created objects are created once a new Event is received. await this.setObjectNotExistsAsync(device.id + '.Events', { type: 'channel', common: { name: 'MQTT Events' }, native: {} }); - for (const event of CONST.mqttEvents) { - await this.setObjectNotExistsAsync(device.id + '.Events.' + event, { type: 'state', common: { name: 'Event: ' + event, type: 'boolean', role: 'switch', read: true, write: false }, native: {} }); + if (this.config.mqttCreateDefaultEventObjects) { + for (const event of CONST.mqttEvents) { + await this.setObjectNotExistsAsync(device.id + '.Events.' + event, { type: 'state', common: { name: 'Event: ' + event, type: 'boolean', role: 'switch', read: true, write: false }, native: {} }); + } } return true; } catch (e) { @@ -335,15 +340,15 @@ export class FullyMqtt extends utils.Adapter { // Finalize this.fullysAll[finalDevice.ip] = finalDevice; if (lpDevice.enabled) { - this.fullys[finalDevice.ip] = finalDevice; + this.fullysEnbl[finalDevice.ip] = finalDevice; this.log.info(`🗸 ${finalDevice.name} (${finalDevice.ip}): Config successfully verified.`); } else { - this.fullysNotEnabled[finalDevice.ip] = finalDevice; - this.log.info(`${finalDevice.name} (${finalDevice.ip}) is not enabled in adapter settings, so it will not be used by adapter.`); + this.fullysDisbl[finalDevice.ip] = finalDevice; + this.log.info(`${finalDevice.name} (${finalDevice.ip}) is not enabled in settings, so it will not be used by adapter.`); } } - if (Object.keys(this.fullys).length === 0) { + if (Object.keys(this.fullysEnbl).length === 0) { this.log.error(`No active devices with correct configuration found.`); return false; } @@ -360,8 +365,8 @@ export class FullyMqtt extends utils.Adapter { */ public async onMqttAlive(ip: string, isAlive: true | false, msg: string): Promise { try { - const prevIsAlive = this.fullys[ip].isAlive; - this.fullys[ip].isAlive = isAlive; + const prevIsAlive = this.fullysEnbl[ip].isAlive; + this.fullysEnbl[ip].isAlive = isAlive; // Has this function ever been called before? If adapter is restarted, we ensure log, etc. const calledBefore = this.onMqttAlive_EverBeenCalledBefore; // Keep old value @@ -373,13 +378,13 @@ export class FullyMqtt extends utils.Adapter { // if alive status changed if ((!calledBefore && isAlive === true) || prevIsAlive !== isAlive) { // Set Device isAlive Status - we could also use setStateChanged()... - this.setState(this.fullys[ip].id + '.alive', { val: isAlive, ack: true }); + this.setState(this.fullysEnbl[ip].id + '.alive', { val: isAlive, ack: true }); // log if (isAlive) { - this.log.info(`${this.fullys[ip].name} is alive (MQTT: ${msg})`); + this.log.info(`${this.fullysEnbl[ip].name} is alive (MQTT: ${msg})`); } else { - this.log.warn(`${this.fullys[ip].name} is not alive! (MQTT: ${msg})`); + this.log.warn(`${this.fullysEnbl[ip].name} is not alive! (MQTT: ${msg})`); } } else { // No change @@ -390,9 +395,9 @@ export class FullyMqtt extends utils.Adapter { ***********/ let countAll = 0; let countAlive = 0; - for (const lpIpAddr in this.fullys) { + for (const lpIpAddr in this.fullysEnbl) { countAll++; - if (this.fullys[lpIpAddr].isAlive) { + if (this.fullysEnbl[lpIpAddr].isAlive) { countAlive++; } } @@ -411,41 +416,41 @@ export class FullyMqtt extends utils.Adapter { public async onMqttInfo(obj: { clientId: string; ip: string; topic: string; infoObj: { [k: string]: any } }): Promise { try { // log - this.log.debug(`[MQTT] ${this.fullys[obj.ip].name} published info, topic: ${obj.topic}`); + this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name} published info, topic: ${obj.topic}`); //this.log.debug(`[MQTT] Client ${obj.ip} Publish Info: Details: ${JSON.stringify(obj.infoObj)}`); // Create info objects if not yet existing - const formerInfoKeysLength: number = this.fullys[obj.ip].mqttInfoKeys.length; + const formerInfoKeysLength: number = this.fullysEnbl[obj.ip].mqttInfoKeys.length; const newInfoKeysAdded: string[] = []; for (const key in obj.infoObj) { const val = obj.infoObj[key]; const valType = typeof val; // only accept certain types if (valType !== 'string' && valType !== 'boolean' && valType !== 'object' && valType !== 'number') { - this.log.warn(`[MQTT] ${this.fullys[obj.ip].name}: Unknown type ${valType} of key '${key}' in info object`); + this.log.warn(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Unknown type ${valType} of key '${key}' in info object`); continue; } // Create info object if not yet seen - this check is used for increasing performance by not unnesserily call setObjectNotExistsAsync() every time new info package comes in - if (!this.fullys[obj.ip].mqttInfoKeys.includes(key)) { - this.fullys[obj.ip].mqttInfoKeys.push(key); + if (!this.fullysEnbl[obj.ip].mqttInfoKeys.includes(key)) { + this.fullysEnbl[obj.ip].mqttInfoKeys.push(key); newInfoKeysAdded.push(key); - await this.setObjectNotExistsAsync(`${this.fullys[obj.ip].id}.Info.${key}`, { type: 'state', common: { name: 'Info: ' + key, type: valType, role: 'value', read: true, write: false }, native: {} }); + await this.setObjectNotExistsAsync(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { type: 'state', common: { name: 'Info: ' + key, type: valType, role: 'value', read: true, write: false }, native: {} }); } } - if (formerInfoKeysLength === 0) this.log.debug(`[MQTT] ${this.fullys[obj.ip].name}: Initially create states for ${newInfoKeysAdded.length} info items (if not yet existing)`); - if (formerInfoKeysLength > 0 && newInfoKeysAdded.length > 0) this.log.info(`[MQTT] ${this.fullys[obj.ip].name}: Created new info object(s) as not seen before (if object(s) did not exist): ${newInfoKeysAdded.join(', ')}`); + if (formerInfoKeysLength === 0) this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Initially create states for ${newInfoKeysAdded.length} info items (if not yet existing)`); + if (formerInfoKeysLength > 0 && newInfoKeysAdded.length > 0) this.log.info(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Created new info object(s) as not seen before (if object(s) did not exist): ${newInfoKeysAdded.join(', ')}`); // Set info objects for (const key in obj.infoObj) { const newVal = typeof obj.infoObj[key] === 'object' ? JSON.stringify(obj.infoObj[key]) : obj.infoObj[key]; // https://forum.iobroker.net/post/628870 - https://forum.iobroker.net/post/960260 if (this.config.mqttUpdateUnchangedObjects) { - this.setState(`${this.fullys[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); + this.setState(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); } else { - this.setStateChanged(`${this.fullys[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); + this.setStateChanged(`${this.fullysEnbl[obj.ip].id}.Info.${key}`, { val: newVal, ack: true }); } } - this.setState(this.fullys[obj.ip].id + '.lastInfoUpdate', { val: Date.now(), ack: true }); - this.setState(this.fullys[obj.ip].id + '.alive', { val: true, ack: true }); + this.setState(this.fullysEnbl[obj.ip].id + '.lastInfoUpdate', { val: Date.now(), ack: true }); + this.setState(this.fullysEnbl[obj.ip].id + '.alive', { val: true, ack: true }); } catch (e) { this.log.error(this.err2Str(e)); return; @@ -458,22 +463,22 @@ export class FullyMqtt extends utils.Adapter { public async onMqttEvent(obj: { clientId: string; ip: string; topic: string; cmd: string }): Promise { try { // log - this.log.debug(`[MQTT] 📡 ${this.fullys[obj.ip].name} published event, topic: ${obj.topic}, cmd: ${obj.cmd}`); + this.log.debug(`[MQTT] 📡 ${this.fullysEnbl[obj.ip].name} published event, topic: ${obj.topic}, cmd: ${obj.cmd}`); /** * Set Event State */ - const pthEvent = `${this.fullys[obj.ip].id}.Events.${obj.cmd}`; + const pthEvent = `${this.fullysEnbl[obj.ip].id}.Events.${obj.cmd}`; if (!(await this.getObjectAsync(pthEvent))) { - this.log.info(`[MQTT] ${this.fullys[obj.ip].name}: Event ${obj.cmd} received but state ${pthEvent} does not exist, so we create it first`); - await this.setObjectNotExistsAsync(pthEvent, { type: 'state', common: { name: 'MQTT Event: ' + obj.cmd, type: 'boolean', role: 'switch', read: true, write: false }, native: {} }); + this.log.debug(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Event ${obj.cmd} received but state ${pthEvent} does not exist, so we create it first`); + await this.setObjectNotExistsAsync(pthEvent, { type: 'state', common: { name: 'Event: ' + obj.cmd, type: 'boolean', role: 'switch', read: true, write: false }, native: {} }); } this.setState(pthEvent, { val: true, ack: true }); /** * Confirm Command state(s) with ack: true */ - const pthCmd = this.fullys[obj.ip].id + '.Commands'; + const pthCmd = this.fullysEnbl[obj.ip].id + '.Commands'; // Check if it is a switch with MQTT commands connected const idx = this.getIndexFromConf(CONST.cmdsSwitches, ['mqttOn', 'mqttOff'], obj.cmd); @@ -491,7 +496,7 @@ export class FullyMqtt extends utils.Adapter { // We have a button, so set it to true await this.setStateAsync(`${pthCmd}.${obj.cmd}`, { val: true, ack: true }); } else { - this.log.silly(`[MQTT] ${this.fullys[obj.ip].name}: Event cmd ${obj.cmd} - no REST API command is existing, so skip confirmation with with ack:true`); + this.log.silly(`[MQTT] ${this.fullysEnbl[obj.ip].name}: Event cmd ${obj.cmd} - no REST API command is existing, so skip confirmation with with ack:true`); } } } catch (e) { @@ -601,13 +606,13 @@ export class FullyMqtt extends utils.Adapter { * @returns - fully object or false if not found */ private getFullyByKey(keyId: string, value: any): IDevice | false { - for (const ip in this.fullys) { - if (keyId in this.fullys[ip]) { + for (const ip in this.fullysEnbl) { + if (keyId in this.fullysEnbl[ip]) { const lpKeyId = keyId as string; // Wow, what a line. Due to: https://bobbyhadz.com/blog/typescript-element-implicitly-has-any-type-expression - const lpVal = this.fullys[ip][lpKeyId as keyof (typeof this.fullys)[typeof ip]]; + const lpVal = this.fullysEnbl[ip][lpKeyId as keyof (typeof this.fullysEnbl)[typeof ip]]; if (lpVal === value) { - return this.fullys[ip]; + return this.fullysEnbl[ip]; } } }