From d1d33a201c2c599ff39d8a6710330fbebf7ca023 Mon Sep 17 00:00:00 2001 From: Acgua <95978245+Acgua@users.noreply.github.com> Date: Sun, 19 Mar 2023 13:08:33 +0100 Subject: [PATCH] Update to 0.0.6 --- .github/workflows/test-and-release.yml | 1 + .releaseconfig.json | 2 +- README.md | 17 +- admin/i18n/de/translations.json | 28 +++ admin/i18n/en/translations.json | 28 +++ admin/i18n/es/translations.json | 28 +++ admin/i18n/fr/translations.json | 28 +++ admin/i18n/it/translations.json | 28 +++ admin/i18n/nl/translations.json | 28 +++ admin/i18n/pl/translations.json | 28 +++ admin/i18n/pt/translations.json | 28 +++ admin/i18n/ru/translations.json | 28 +++ admin/i18n/uk/translations.json | 28 +++ admin/i18n/zh-cn/translations.json | 28 +++ admin/jsonConfig.json | 280 +++---------------------- admin/words.js | 70 +++---- build/lib/interfaces.js.map | 2 +- build/lib/mqtt-server.js | 11 +- build/lib/mqtt-server.js.map | 4 +- build/main.js | 17 +- build/main.js.map | 4 +- {doc => docs}/en/README.md | 0 io-package.json | 15 +- package.json | 5 +- src/lib/interfaces.ts | 4 +- src/lib/mqtt-server.ts | 11 +- src/main.ts | 27 ++- 27 files changed, 441 insertions(+), 337 deletions(-) create mode 100644 admin/i18n/de/translations.json create mode 100644 admin/i18n/en/translations.json create mode 100644 admin/i18n/es/translations.json create mode 100644 admin/i18n/fr/translations.json create mode 100644 admin/i18n/it/translations.json create mode 100644 admin/i18n/nl/translations.json create mode 100644 admin/i18n/pl/translations.json create mode 100644 admin/i18n/pt/translations.json create mode 100644 admin/i18n/ru/translations.json create mode 100644 admin/i18n/uk/translations.json create mode 100644 admin/i18n/zh-cn/translations.json rename {doc => docs}/en/README.md (100%) diff --git a/.github/workflows/test-and-release.yml b/.github/workflows/test-and-release.yml index ea6c444..8d8d337 100644 --- a/.github/workflows/test-and-release.yml +++ b/.github/workflows/test-and-release.yml @@ -63,6 +63,7 @@ jobs: needs: [check-and-lint, adapter-tests] # Trigger this step only when a commit on any branch is tagged with a version number + # 15.03.2023 - uncommented as we want to always run it if: | contains(github.event.head_commit.message, '[skip ci]') == false && github.event_name == 'push' && diff --git a/.releaseconfig.json b/.releaseconfig.json index c0006f6..11f08dd 100644 --- a/.releaseconfig.json +++ b/.releaseconfig.json @@ -1,3 +1,3 @@ { - "plugins": ["iobroker", "license"] + "plugins": ["iobroker", "license", "manual-review"] } \ No newline at end of file diff --git a/README.md b/README.md index 79fea87..c6a0539 100644 --- a/README.md +++ b/README.md @@ -23,18 +23,18 @@ This adapter controls your [Fully Kiosk Browser](https://www.fully-kiosk.com) (a Unlike [ioBroker.fullybrowser](https://github.com/arteck/ioBroker.fullybrowser), this adapter also supports **MQTT**. -## Credits - -Many thanks to @arteck (https://github.com/arteck) for [ioBroker.fullybrowser](https://github.com/arteck/ioBroker.fullybrowser). While I planned to just extend to use the existing adapter for an MQTT enhancement, I finally decided to write a new adapter in TypeScript and include latest ioBroker development features, as I anyway would have had to rewrite approx. 70-80% of the code for the MQTT integration. -Once this ioBroker.fully-mqtt adapter is tested accordingly and runs stable, I would love to merge this Fully MQTT adapter into [ioBroker.fullybrowser](https://github.com/arteck/ioBroker.fullybrowser) to just have one single adapter for the ioBroker community. - ## Documentation -[🇺🇸 Documentation](./doc/en/README.md) +* [Documentation](./docs/en/README.md) ## ioBroker Forum Thread -[TEST - Adapter Fully Browser mit MQTT](https://forum.iobroker.net/topic/63705/) +* [TEST - Adapter Fully Browser mit MQTT](https://forum.iobroker.net/topic/63705/) + +## Credits + +Many thanks to @arteck (https://github.com/arteck) for [ioBroker.fullybrowser](https://github.com/arteck/ioBroker.fullybrowser). While I planned to just extend to use the existing adapter for an MQTT enhancement, I finally decided to write a new adapter in TypeScript and include latest ioBroker development features, as I anyway would have had to rewrite approx. 70-80% of the code for the MQTT integration. +Once this ioBroker.fully-mqtt adapter is tested accordingly and runs stable, I would love to merge this Fully MQTT adapter into [ioBroker.fullybrowser](https://github.com/arteck/ioBroker.fullybrowser) to just have one single adapter for the ioBroker community. ## Changelog @@ -45,7 +45,8 @@ Once this ioBroker.fully-mqtt adapter is tested accordingly and runs stable, I w ### **WORK IN PROGRESS** -- (Acgua) ... +- (Acgua) i18n (@iobroker/adapter-dev) implemented +- (Acgua) using adapter.setTimeout instead of standard setTimeout ### 0.0.5 (2023-03-15) diff --git a/admin/i18n/de/translations.json b/admin/i18n/de/translations.json new file mode 100644 index 0000000..c1c7e9b --- /dev/null +++ b/admin/i18n/de/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Eine ausführliche Dokumentation mit Erklärungen und allen weiteren Informationen finden Sie auf dieser GitHub-Seite:", + "Add new devices by clicking the plus (+) symbol...": "Neue Geräte hinzufügen, indem Sie auf das Plus-Symbol (+) klicken...", + "Always update info objects, even if value did not change": "Info-Datenpunkte werden damit immer aktualisiert, auch wenn sich der Wert nicht ändert", + "Always update info states": "Info-Datenpunkte immer aktualisieren", + "Client and Connection errors as info in log": "Client- und Connection-Fehler als info im Log", + "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", + "Enabled": "Aktiv", + "Expert Settings": "Experten-Einstellungen", + "Fully Browser Devices": "Fully-Browser-Geräte", + "Fully MQTT Adapter Documentation": "Fully-MQTT Adapter-Dokumentation", + "IP Address": "IP-Adresse", + "Logs client and connection errors as 'info' and not as 'error'": "Client- und Connection-Fehler als 'info' und nicht als 'error' im Log ausgeben", + "MQTT Configuration": "MQTT-Konfiguration", + "MQTT Password": "MQTT-Passwort", + "Password": "Passwort", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "Die Angabe der Port-Nummer ist immer erforderlich, aber Benutzername und Passwort kann man leer lassen. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", + "Protocol": "Protokoll", + "Remote Admin Password": "Remote Admin Passwort", + "This disables the username and password verification": "Die Überprüfung von Benutzername und Passwort wird damit deaktiviert", + "User name": "Benutzername", + "Welcome to Fully MQTT adapter": "Willkommen beim Fully-MQTT Adapter", + "in milliseconds (500-15000, default: 6000)": "in Millisekunden (500-15000, Default: 6000)", + "in seconds (2-120, default: 30)": "in Sekunden (2-120, Default: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "in Sekunden (min: 5, Default: 60); ignoriert wenn MQTT verwendet wird" +} diff --git a/admin/i18n/en/translations.json b/admin/i18n/en/translations.json new file mode 100644 index 0000000..37a4d40 --- /dev/null +++ b/admin/i18n/en/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "A detailed documentation with explanations and all further information can be found on this GitHub page:", + "Add new devices by clicking the plus (+) symbol...": "Add new devices by clicking the plus (+) symbol...", + "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", + "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", + "Enabled": "Enabled", + "Expert Settings": "Expert Settings", + "Fully Browser Devices": "Fully Browser Devices", + "Fully MQTT Adapter Documentation": "Fully MQTT Adapter Documentation", + "IP Address": "IP Address", + "Logs client and connection errors as 'info' and not as 'error'": "Logs client and connection errors as 'info' and not as 'error'", + "MQTT Configuration": "MQTT Configuration", + "MQTT Password": "MQTT Password", + "Password": "Password", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", + "Protocol": "Protokoll", + "Remote Admin Password": "Remote Admin Password", + "This disables the username and password verification": "This disables the username and password verification", + "User name": "User name", + "Welcome to Fully MQTT adapter": "Welcome to Fully MQTT adapter", + "in milliseconds (500-15000, default: 6000)": "in milliseconds (500-15000, default: 6000)", + "in seconds (2-120, default: 30)": "in seconds (2-120, default: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "in seconds (min: 5, default: 60); ignored if MQTT is used" +} diff --git a/admin/i18n/es/translations.json b/admin/i18n/es/translations.json new file mode 100644 index 0000000..17740f8 --- /dev/null +++ b/admin/i18n/es/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Puede encontrar una documentación detallada con explicaciones y toda la información adicional en esta página de GitHub:", + "Add new devices by clicking the plus (+) symbol...": "Agregue nuevos dispositivos haciendo clic en el símbolo más (+)...", + "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", + "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", + "Enabled": "Activado", + "Expert Settings": "Configuración experta", + "Fully Browser Devices": "Dispositivos completamente navegadores", + "Fully MQTT Adapter Documentation": "Documentación del adaptador completamente MQTT", + "IP Address": "Dirección IP", + "Logs client and connection errors as 'info' and not as 'error'": "Registra los errores de conexión y del cliente como 'información' y no como 'error'", + "MQTT Configuration": "Configuración MQTT", + "MQTT Password": "Contraseña MQTT", + "Password": "Contraseña", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "El puerto siempre es obligatorio, pero puede dejar el nombre de usuario y la contraseña vacíos. Aplicación de navegador completo: Configuración -> Otras configuraciones -> Integración MQTT (MÁS)", + "Protocol": "Protocolo", + "Remote Admin Password": "Contraseña de administrador remoto", + "This disables the username and password verification": "Esto deshabilita la verificación de nombre de usuario y contraseña.", + "User name": "Nombre de usuario", + "Welcome to Fully MQTT adapter": "Bienvenido al adaptador completamente MQTT", + "in milliseconds (500-15000, default: 6000)": "en milisegundos (500-15000, predeterminado: 6000)", + "in seconds (2-120, default: 30)": "en segundos (2-120, predeterminado: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "en segundos (mín.: 5, predeterminado: 60); ignorado si se usa MQTT" +} diff --git a/admin/i18n/fr/translations.json b/admin/i18n/fr/translations.json new file mode 100644 index 0000000..75aff0a --- /dev/null +++ b/admin/i18n/fr/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Une documentation détaillée avec des explications et toutes les autres informations peuvent être trouvées sur cette page GitHub :", + "Add new devices by clicking the plus (+) symbol...": "Ajoutez de nouveaux appareils en cliquant sur le symbole plus (+)...", + "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", + "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", + "Enabled": "Activé", + "Expert Settings": "Paramètres experts", + "Fully Browser Devices": "Appareils entièrement navigateurs", + "Fully MQTT Adapter Documentation": "Documentation complète de l'adaptateur MQTT", + "IP Address": "Adresse IP", + "Logs client and connection errors as 'info' and not as 'error'": "Consigne les erreurs de client et de connexion en tant qu''info' et non en tant qu''erreur'", + "MQTT Configuration": "Configuration MQTT", + "MQTT Password": "Mot de passe MQTT", + "Password": "Mot de passe", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "Le port est toujours requis, mais vous pouvez laisser le nom d'utilisateur et le mot de passe vides. Application entièrement navigateur : Paramètres -> Autres paramètres -> Intégration MQTT (PLUS)", + "Protocol": "Protocole", + "Remote Admin Password": "Mot de passe administrateur distant", + "This disables the username and password verification": "Cela désactive la vérification du nom d'utilisateur et du mot de passe", + "User name": "Nom d'utilisateur", + "Welcome to Fully MQTT adapter": "Bienvenue dans l'adaptateur entièrement MQTT", + "in milliseconds (500-15000, default: 6000)": "en millisecondes (500-15000, par défaut : 6000)", + "in seconds (2-120, default: 30)": "en secondes (2-120, par défaut : 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "en secondes (min : 5, par défaut : 60) ; ignoré si MQTT est utilisé" +} diff --git a/admin/i18n/it/translations.json b/admin/i18n/it/translations.json new file mode 100644 index 0000000..d7d9ad4 --- /dev/null +++ b/admin/i18n/it/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Una documentazione dettagliata con spiegazioni e tutte le ulteriori informazioni può essere trovata su questa pagina GitHub:", + "Add new devices by clicking the plus (+) symbol...": "Aggiungi nuovi dispositivi facendo clic sul simbolo più (+)...", + "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", + "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", + "Enabled": "Abilitato", + "Expert Settings": "Impostazioni avanzate", + "Fully Browser Devices": "Dispositivi completamente browser", + "Fully MQTT Adapter Documentation": "Documentazione completa dell'adattatore MQTT", + "IP Address": "Indirizzo IP", + "Logs client and connection errors as 'info' and not as 'error'": "Registra gli errori del client e della connessione come \"info\" e non come \"errore\"", + "MQTT Configuration": "Configurazione MQTT", + "MQTT Password": "Password MQTT", + "Password": "Parola d'ordine", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "La porta è sempre richiesta, ma puoi lasciare il nome utente e la password vuoti. App completamente browser: Impostazioni -> Altre impostazioni -> Integrazione MQTT (PLUS)", + "Protocol": "Protocollo", + "Remote Admin Password": "Password dell'amministratore remoto", + "This disables the username and password verification": "Ciò disabilita la verifica del nome utente e della password", + "User name": "Nome utente", + "Welcome to Fully MQTT adapter": "Benvenuto nell'adattatore Fully MQTT", + "in milliseconds (500-15000, default: 6000)": "in millisecondi (500-15000, predefinito: 6000)", + "in seconds (2-120, default: 30)": "in secondi (2-120, predefinito: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "in secondi (min: 5, default: 60); ignorato se viene utilizzato MQTT" +} diff --git a/admin/i18n/nl/translations.json b/admin/i18n/nl/translations.json new file mode 100644 index 0000000..394e93b --- /dev/null +++ b/admin/i18n/nl/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Een gedetailleerde documentatie met uitleg en alle verdere informatie is te vinden op deze GitHub-pagina:", + "Add new devices by clicking the plus (+) symbol...": "Voeg nieuwe apparaten toe door op het plusteken (+) te klikken...", + "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", + "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", + "Enabled": "Ingeschakeld", + "Expert Settings": "Expert-instellingen", + "Fully Browser Devices": "Volledig browserapparaten", + "Fully MQTT Adapter Documentation": "Volledig MQTT-adapterdocumentatie", + "IP Address": "IP adres", + "Logs client and connection errors as 'info' and not as 'error'": "Registreert client- en verbindingsfouten als 'info' en niet als 'fout'", + "MQTT Configuration": "MQTT-configuratie", + "MQTT Password": "MQTT-wachtwoord", + "Password": "Wachtwoord", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "Poort is altijd vereist, maar u kunt de gebruikersnaam en het wachtwoord leeg laten. Volledig browser-app: Instellingen -> Andere instellingen -> MQTT-integratie (PLUS)", + "Protocol": "Protocol", + "Remote Admin Password": "Wachtwoord voor beheerder op afstand", + "This disables the username and password verification": "Hierdoor wordt de gebruikersnaam- en wachtwoordverificatie uitgeschakeld", + "User name": "Gebruikersnaam", + "Welcome to Fully MQTT adapter": "Welkom bij de volledig MQTT-adapter", + "in milliseconds (500-15000, default: 6000)": "in milliseconden (500-15000, standaard: 6000)", + "in seconds (2-120, default: 30)": "in seconden (2-120, standaard: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "in seconden (min: 5, standaard: 60); genegeerd als MQTT wordt gebruikt" +} diff --git a/admin/i18n/pl/translations.json b/admin/i18n/pl/translations.json new file mode 100644 index 0000000..77e1004 --- /dev/null +++ b/admin/i18n/pl/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Szczegółową dokumentację z wyjaśnieniami i wszystkimi dodatkowymi informacjami można znaleźć na tej stronie GitHub:", + "Add new devices by clicking the plus (+) symbol...": "Dodaj nowe urządzenia, klikając symbol plusa (+)...", + "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", + "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", + "Enabled": "Włączony", + "Expert Settings": "Ustawienia eksperckie", + "Fully Browser Devices": "Urządzenia z pełną przeglądarką", + "Fully MQTT Adapter Documentation": "Pełna dokumentacja adaptera MQTT", + "IP Address": "Adres IP", + "Logs client and connection errors as 'info' and not as 'error'": "Rejestruje błędy klienta i połączenia jako „informacje”, a nie jako „błąd”", + "MQTT Configuration": "Konfiguracja MQTT", + "MQTT Password": "Hasło MQTT", + "Password": "Hasło", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "Port jest zawsze wymagany, ale nazwę użytkownika i hasło można pozostawić puste. Pełna aplikacja przeglądarki: Ustawienia -> Inne ustawienia -> Integracja MQTT (PLUS)", + "Protocol": "Protokół", + "Remote Admin Password": "Hasło administratora zdalnego", + "This disables the username and password verification": "Spowoduje to wyłączenie weryfikacji nazwy użytkownika i hasła", + "User name": "Nazwa użytkownika", + "Welcome to Fully MQTT adapter": "Witamy w adapterze Fully MQTT", + "in milliseconds (500-15000, default: 6000)": "w milisekundach (500-15000, domyślnie: 6000)", + "in seconds (2-120, default: 30)": "w sekundach (2-120, domyślnie: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "w sekundach (min: 5, domyślnie: 60); ignorowane, jeśli używany jest MQTT" +} diff --git a/admin/i18n/pt/translations.json b/admin/i18n/pt/translations.json new file mode 100644 index 0000000..e6707de --- /dev/null +++ b/admin/i18n/pt/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Uma documentação detalhada com explicações e todas as informações adicionais podem ser encontradas nesta página do GitHub:", + "Add new devices by clicking the plus (+) symbol...": "Adicione novos dispositivos clicando no símbolo de mais (+)...", + "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", + "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", + "Enabled": "Habilitado", + "Expert Settings": "Configurações avançadas", + "Fully Browser Devices": "Dispositivos totalmente navegadores", + "Fully MQTT Adapter Documentation": "Documentação completa do adaptador MQTT", + "IP Address": "Endereço de IP", + "Logs client and connection errors as 'info' and not as 'error'": "Registra erros de cliente e conexão como 'informações' e não como 'erro'", + "MQTT Configuration": "Configuração MQTT", + "MQTT Password": "Senha do MQTT", + "Password": "Senha", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "A porta é sempre necessária, mas você pode deixar o nome de usuário e a senha em branco. Aplicativo totalmente do navegador: Configurações -> Outras configurações -> Integração MQTT (PLUS)", + "Protocol": "Protocolo", + "Remote Admin Password": "Senha do administrador remoto", + "This disables the username and password verification": "Isso desativa a verificação de nome de usuário e senha", + "User name": "Nome de usuário", + "Welcome to Fully MQTT adapter": "Bem-vindo ao adaptador totalmente MQTT", + "in milliseconds (500-15000, default: 6000)": "em milissegundos (500-15000, padrão: 6000)", + "in seconds (2-120, default: 30)": "em segundos (2-120, padrão: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "em segundos (min: 5, padrão: 60); ignorado se o MQTT for usado" +} diff --git a/admin/i18n/ru/translations.json b/admin/i18n/ru/translations.json new file mode 100644 index 0000000..04b5cb1 --- /dev/null +++ b/admin/i18n/ru/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Подробную документацию с пояснениями и всю дополнительную информацию можно найти на этой странице GitHub:", + "Add new devices by clicking the plus (+) symbol...": "Добавьте новые устройства, щелкнув значок плюса (+)...", + "Always update info objects, even if value did not change": "Всегда обновлять информационные объекты, даже если значение не изменилось", + "Always update info states": "Всегда обновлять информационные статусы", + "Client and Connection errors as info in log": "Ошибки клиента и подключения как информация в журнале", + "Device Name": "Имя устройства", + "Do not process published info more than every x seconds": "Не обрабатывать опубликованную информацию чаще, чем каждые x секунд", + "Do not verify user and password": "Не проверять пользователя и пароль", + "Enabled": "Включено", + "Expert Settings": "Настройки эксперта", + "Fully Browser Devices": "Полностью браузерные устройства", + "Fully MQTT Adapter Documentation": "Полная документация по адаптеру MQTT", + "IP Address": "Айпи адрес", + "Logs client and connection errors as 'info' and not as 'error'": "Регистрирует ошибки клиента и подключения как «информация», а не как «ошибка».", + "MQTT Configuration": "Конфигурация MQTT", + "MQTT Password": "MQTT-пароль", + "Password": "Пароль", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "Порт требуется всегда, но вы можете оставить имя пользователя и пароль пустыми. Полностью браузерное приложение: Настройки -> Другие настройки -> Интеграция MQTT (ПЛЮС)", + "Protocol": "Протокол", + "Remote Admin Password": "Пароль удаленного администратора", + "This disables the username and password verification": "Это отключает проверку имени пользователя и пароля", + "User name": "Имя пользователя", + "Welcome to Fully MQTT adapter": "Добро пожаловать в адаптер Fully MQTT", + "in milliseconds (500-15000, default: 6000)": "в миллисекундах (500-15000, по умолчанию: 6000)", + "in seconds (2-120, default: 30)": "в секундах (2-120, по умолчанию: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "в секундах (мин.: 5, по умолчанию: 60); игнорируется, если используется MQTT" +} diff --git a/admin/i18n/uk/translations.json b/admin/i18n/uk/translations.json new file mode 100644 index 0000000..b5aae07 --- /dev/null +++ b/admin/i18n/uk/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "Детальну документацію з поясненнями та всю додаткову інформацію можна знайти на цій сторінці GitHub:", + "Add new devices by clicking the plus (+) symbol...": "Додайте нові пристрої, натиснувши символ плюс (+)...", + "Always update info objects, even if value did not change": "Завжди оновлюйте інформаційні об’єкти, навіть якщо значення не змінилося", + "Always update info states": "Завжди оновлюйте статуси інформації", + "Client and Connection errors as info in log": "Помилки клієнта та підключення як інформація в журналі", + "Device Name": "Ім'я пристрою", + "Do not process published info more than every x seconds": "Не обробляйте опубліковану інформацію частіше ніж кожні x секунд", + "Do not verify user and password": "Не перевіряйте користувача та пароль", + "Enabled": "Увімкнено", + "Expert Settings": "Експертні налаштування", + "Fully Browser Devices": "Повністю браузерні пристрої", + "Fully MQTT Adapter Documentation": "Повна документація адаптера MQTT", + "IP Address": "IP-адреса", + "Logs client and connection errors as 'info' and not as 'error'": "Реєструє помилки клієнта та підключення як «інформацію», а не як «помилку»", + "MQTT Configuration": "Конфігурація MQTT", + "MQTT Password": "Пароль MQTT", + "Password": "Пароль", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "Порт завжди потрібен, але ви можете залишити ім’я користувача та пароль пустими. Повністю браузерна програма: Налаштування -> Інші налаштування -> Інтеграція MQTT (PLUS)", + "Protocol": "протокол", + "Remote Admin Password": "Пароль віддаленого адміністратора", + "This disables the username and password verification": "Це вимикає перевірку імені користувача та пароля", + "User name": "Ім'я користувача", + "Welcome to Fully MQTT adapter": "Ласкаво просимо до адаптера Fully MQTT", + "in milliseconds (500-15000, default: 6000)": "у мілісекундах (500-15000, за замовчуванням: 6000)", + "in seconds (2-120, default: 30)": "за секунди (2-120, за замовчуванням: 30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "у секундах (хв.: 5, за замовчуванням: 60); ігнорується, якщо використовується MQTT" +} diff --git a/admin/i18n/zh-cn/translations.json b/admin/i18n/zh-cn/translations.json new file mode 100644 index 0000000..cdd6f85 --- /dev/null +++ b/admin/i18n/zh-cn/translations.json @@ -0,0 +1,28 @@ +{ + "A detailed documentation with explanations and all further information can be found on this GitHub page:": "可以在这个 GitHub 页面上找到带有解释和所有进一步信息的详细文档:", + "Add new devices by clicking the plus (+) symbol...": "通过单击加号 (+) 符号添加新设备...", + "Always update info objects, even if value did not change": "始终更新信息对象,即使值没有改变", + "Always update info states": "始终更新信息状态", + "Client and Connection errors as info in log": "客户端和连接错误作为日志中的信息", + "Device Name": "设备名称", + "Do not process published info more than every x seconds": "不要超过每 x 秒处理一次发布的信息", + "Do not verify user and password": "不验证用户和密码", + "Enabled": "启用", + "Expert Settings": "专家设置", + "Fully Browser Devices": "完全浏览器设备", + "Fully MQTT Adapter Documentation": "完整的 MQTT 适配器文档", + "IP Address": "IP地址", + "Logs client and connection errors as 'info' and not as 'error'": "将客户端和连接错误记录为“信息”而不是“错误”", + "MQTT Configuration": "MQTT 配置", + "MQTT Password": "MQTT密码", + "Password": "密码", + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": "端口始终是必需的,但您可以将用户名和密码留空。完全浏览器应用程序:设置 -> 其他设置 -> MQTT 集成 (PLUS)", + "Protocol": "协议书", + "Remote Admin Password": "远程管理员密码", + "This disables the username and password verification": "这将禁用用户名和密码验证", + "User name": "用户名", + "Welcome to Fully MQTT adapter": "欢迎使用 Fully MQTT 适配器", + "in milliseconds (500-15000, default: 6000)": "以毫秒为单位(500-15000,默认值:6000)", + "in seconds (2-120, default: 30)": "以秒为单位(2-120,默认值:30)", + "in seconds (min: 5, default: 60); ignored if MQTT is used": "以秒为单位(最小值:5,默认值:60);如果使用 MQTT,则忽略" +} diff --git a/admin/jsonConfig.json b/admin/jsonConfig.json index e583f60..4e3b845 100644 --- a/admin/jsonConfig.json +++ b/admin/jsonConfig.json @@ -1,57 +1,21 @@ { "_ignore_Acgua1": "The following '_ignore_' are just there to be able to add comments to this json file.", "_ignore_Acgua2": "See https://github.com/ioBroker/ioBroker.admin/blob/master/src/src/components/JsonConfigComponent/SCHEMA.md", - "i18n": false, + "i18n": true, "type": "tabs", "items": { "_tab_Start": { "type": "panel", - "label": { - "en": "Main Settings", - "de": "Haupteinstellungen", - "ru": "Основные настройки", - "pt": "Configurações principais", - "nl": "Hoofdstukken", - "fr": "Paramètres principaux", - "it": "Impostazioni principali", - "es": "Ajustes principales", - "pl": "Strona główna", - "uk": "Основні налаштування", - "zh-cn": "主要规定" - }, + "label": "Main Settings", "items": { "header_Start": { "type": "header", - "text": { - "en": "Welcome to Fully MQTT adapter", - "de": "Willkommen beim Fully-MQTT Adapter", - "ru": "Добро пожаловать в Fully MQTT адаптер", - "pt": "Bem-vindo ao adaptador totalmente MQTT", - "nl": "Welkom bij Fully MQT", - "fr": "Bienvenue sur l'adaptateur Fully MQTT", - "it": "Benvenuto nell'adattatore Fully MQTT", - "es": "Bienvenido al adaptador Fully MQTT", - "pl": "Welcome to Fully MQTT (ang.)", - "uk": "Ласкаво просимо до повного адаптера MQTT", - "zh-cn": "2. 欢迎全面适应外国直接投资" - }, + "text": "Welcome to Fully MQTT adapter", "size": 2 }, "_textStart": { "type": "staticText", - "text": { - "en": "A detailed documentation with explanations and all further information can be found on this GitHub page:", - "de": "Eine ausführliche Dokumentation mit Erläuterungen und allen weiteren Informationen ist auf dieser GitHub-Seite:", - "ru": "Подробную документацию с разъяснениями и всю дополнительную информацию можно найти на этой странице GitHub:", - "pt": "Uma documentação detalhada com explicações e todas as informações adicionais podem ser encontradas nesta página do GitHub:", - "nl": "Een gedetailleerde documentatie met verklaringen en alle verdere informatie kan worden gevonden op deze GitHub pagina:", - "fr": "Une documentation détaillée avec des explications et toute autre information peut être trouvée sur cette page GitHub:", - "it": "Una documentazione dettagliata con spiegazioni e tutte le ulteriori informazioni possono essere trovate su questa pagina GitHub:", - "es": "En esta página de GitHub se puede encontrar una documentación detallada con explicaciones y toda la información adicional:", - "pl": "Szczegółowe dokumentowanie z wyjaśnieniami i wszystkie inne informacje można znaleźć na stronie GitHub", - "uk": "На цій сторінці GitHub можна знайти детальну документацію з поясненнями та подальшою інформацією:", - "zh-cn": "本格特·哈布页可查阅附有解释和所有进一步资料的详细文件:" - }, + "text": "A detailed documentation with explanations and all further information can be found on this GitHub page:", "sm": 12, "md": 12, "lg": 12, @@ -62,22 +26,10 @@ }, "_documentationLink": { "type": "staticLink", - "label": { - "en": "Fully MQTT Adapter Documentation", - "de": "Fully-MQTT Adapter-Dokumentation", - "ru": "Полностью MQTT Адаптер Документация", - "pt": "Documentação do adaptador Fully MQTT", - "nl": "Fully MQT-adapter Document", - "fr": "Documentation de l'adaptateur Fully MQTT", - "it": "Documentazione dell'adattatore Fully MQTT", - "es": "Fully MQTT Adapter Documentation", - "pl": "Pełna dokumentacja Fully MQTT", - "uk": "Повністю Документація адаптера Fully MQTT", - "zh-cn": "全面记录" - }, + "label": "Fully MQTT Adapter Documentation", "button": true, "icon": "info", - "href": "https://github.com/Acgua/ioBroker.fully-mqtt/blob/main/doc/en/README.md", + "href": "https://github.com/Acgua/ioBroker.fully-mqtt/blob/main/docs/en/README.md", "sm": 12, "md": 12, "lg": 2, @@ -89,19 +41,7 @@ "header_Devices": { "newLine": "true", "type": "header", - "text": { - "en": "Fully Browser Devices", - "de": "Fully-Browser-Geräte", - "ru": "Fully Browser Devices", - "pt": "Fully Browser Devices", - "nl": "Fully Browser Devices", - "fr": "Fully Browser Devices", - "it": "Fully Browser Devices", - "es": "Fully Browser Devices", - "pl": "Fully Browser Device", - "uk": "Fully Browser Devices", - "zh-cn": "Fully Browser Devices" - }, + "text": "Fully Browser Devices", "size": 2, "style": { "marginTop": 20 @@ -109,19 +49,7 @@ }, "text_DevicesInfo": { "type": "staticText", - "label": { - "en": "Add new devices by clicking the plus (+) symbol...", - "de": "Neue Geräte hinzufügen, indem Sie auf das Plus-Symbol (+) klicken...", - "ru": "Добавьте новые устройства, нажав символ плюс (+)...", - "pt": "Adicione novos dispositivos clicando no símbolo plus (+)...", - "nl": "Voeg nieuwe apparaten toe door de plus te kliken symbool...", - "fr": "Ajoutez de nouveaux appareils en cliquant sur le symbole plus (+)...", - "it": "Aggiungi nuovi dispositivi facendo clic sul simbolo +...", - "es": "Añadir nuevos dispositivos haciendo clic en el símbolo más (+)...", - "pl": "Nowe urządzenia poprzez kliknięcie symbolu plus (+)...", - "uk": "Додати нові пристрої, натиснувши символ плюс (+)...", - "zh-cn": "通过点击加(+)象征来增加新的装置。..." - }, + "label": "Add new devices by clicking the plus (+) symbol...", "newLine": "true", "style": { "fontSize": 16 @@ -135,19 +63,7 @@ "items": [ { "type": "text", - "title": { - "en": "Device Name", - "de": "Gerätename", - "ru": "Наименование устройства", - "pt": "Nome do dispositivo", - "nl": "Vernietig Name", - "fr": "Nom du dispositif", - "it": "Nome del dispositivo", - "es": "Nombre del dispositivo", - "pl": "Nazwisko", - "uk": "Назва пристрою", - "zh-cn": "Device姓名" - }, + "title": "Device Name", "attr": "name", "maxLength": 50, "trim": false, @@ -159,19 +75,7 @@ }, { "type": "select", - "title": { - "en": "Protocol", - "de": "Protokoll", - "ru": "Протокол", - "pt": "Protocolo", - "nl": "Protocol", - "fr": "Protocole", - "it": "Protocollo", - "es": "Protocolo", - "pl": "Protokół", - "uk": "Протокол", - "zh-cn": "议定书" - }, + "title": "Protocol", "attr": "restProtocol", "default": "http", "width": "5%", @@ -190,19 +94,7 @@ }, { "type": "text", - "title": { - "en": "IP Address", - "de": "IP-Adresse", - "ru": "IP адрес", - "pt": "Endereço IP", - "nl": "IP Addres", - "fr": "Adresse IP", - "it": "Indirizzo IP", - "es": "Dirección IP", - "pl": "IP Address", - "uk": "IP-адреса", - "zh-cn": "IP地址" - }, + "title": "IP Address", "attr": "ip", "maxLength": 15, "trim": true, @@ -221,19 +113,7 @@ }, { "type": "password", - "title": { - "en": "Remote Admin Password", - "de": "Remote Admin Passwort", - "ru": "Remote Admin Password", - "pt": "Remote Admin Password", - "nl": "Remote Admin Password", - "fr": "Remote Admin Password", - "it": "Remote Admin Password", - "es": "Remote Admin Password", - "pl": "Remote Admin Password", - "uk": "Remote Admin Password", - "zh-cn": "Remote Admin Password" - }, + "title": "Remote Admin Password", "attr": "restPassword", "width": "15%", "validator": "data.restPassword.length > 0", @@ -248,19 +128,7 @@ }, { "type": "checkbox", - "attr": { - "en": "Enabled", - "de": "Aktiv", - "ru": "Включить", - "pt": "Activado", - "nl": "In staat", - "fr": "Enabled", - "it": "Abilitato", - "es": "Enabled", - "pl": "Enabled", - "uk": "Увімкнути", - "zh-cn": "附录" - }, + "attr": "Enabled", "width": "50px", "title": "Enabled", "default": true @@ -270,36 +138,12 @@ "header_MQTT": { "newLine": "true", "type": "header", - "text": { - "en": "MQTT Configuration", - "de": "MQTT-Konfiguration", - "ru": "MQTT Конфигурация", - "pt": "MQTT Configuração", - "nl": "MQT Configuratie", - "fr": "MQTT Configuration", - "it": "MQTT Configurazione", - "es": "MQTT Configuración", - "pl": "MQTT konfiguracja", - "uk": "MQTT Конфігурація", - "zh-cn": "进口 配 法" - }, + "text": "MQTT Configuration", "size": 2 }, "text_MQTT-info": { "type": "staticText", - "label": { - "en": "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "de": "Die Angabe der Port-Nummer ist immer erforderlich, aber Benutzername und Passwort kann man leer lassen. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "ru": "Порт всегда необходим, но вы можете оставить имя пользователя и пароль пустым. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "pt": "A porta é sempre necessária, mas você pode deixar o nome do usuário e a senha vazias. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "nl": "Port is altijd nodig, maar je kunt gebruikersnaam en wachtwoord leeg laten. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "fr": "Port est toujours nécessaire, mais vous pouvez laisser le nom d'utilisateur et mot de passe vide. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "it": "La porta è sempre richiesta, ma è possibile lasciare il nome utente e la password vuota. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "es": "El puerto siempre es necesario, pero puede dejar el nombre de usuario y la contraseña vacía. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "pl": "Port jest zawsze potrzebny, ale może odejść od nazwy użytkownika i przekazać słowo puste. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "uk": "Порт завжди необхідний, але ви можете залишити ім'я користувача і пароль порожній. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", - "zh-cn": "港口总是需要,但你可以离开用户名称和密码空。." - }, + "label": "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", "style": { "fontSize": 16 } @@ -315,42 +159,18 @@ }, "mqttUser": { "type": "text", - "label": { "en": "User name", "de": "Benutzername" }, - "help": { "en": "MQTT User name", "de": "MQTT Benutzername" } + "label": "User name", + "help": "MQTT User name" }, "mqttPassword": { "type": "password", - "label": { "en": "Password", "de": "Passwort" }, - "help": { "en": "MQTT Password", "de": "MQTT Passwort" } + "label": "Password", + "help": "MQTT Password" }, "mqttDoNotVerifyUserPw": { "type": "checkbox", - "label": { - "en": "Do not verify user and password", - "de": "Benutzername und Passwort nicht verifizieren", - "ru": "Не проверять пользователя и пароль", - "pt": "Não verifique o usuário e a senha", - "nl": "Verifieer geen gebruiker en wachtwoord", - "fr": "Ne vérifiez pas l'utilisateur et le mot de passe", - "it": "Non verificare l'utente e la password", - "es": "No verifique usuario y contraseña", - "pl": "Nie zweryfikuj użytkownika i przemij słowo", - "uk": "Не перевірити користувача і пароль", - "zh-cn": "核查用户和密码" - }, - "help": { - "en": "This disables the username and password verification", - "de": "Die Überprüfung von Benutzername und Passwort wird damit deaktiviert", - "ru": "Это отключает имя пользователя и проверку пароля", - "pt": "Isso desabilita o nome de usuário e a verificação de senha", - "nl": "Dit schakelt de gebruikersnaam en wachtwoord verificatie uit", - "fr": "Cela désactive la vérification du nom d'utilisateur et du mot de passe", - "it": "Questo disabilita la verifica del nome utente e password", - "es": "Esto desactiva el nombre de usuario y la verificación de contraseñas", - "pl": "To uniemożliwia użytkownikowi nazwę i weryfikację haseł", - "uk": "Це відключає перевірку імені користувача та пароля", - "zh-cn": "用户名称和密码核查" - }, + "label": "Do not verify user and password", + "help": "This disables the username and password verification", "default": false, "sm": 12, "md": 6, @@ -360,19 +180,7 @@ }, "_tab_Expert Settings": { "type": "panel", - "label": { - "en": "Expert Settings", - "de": "Experten-Einstellungen", - "ru": "Экспертные настройки", - "pt": "Configurações de especialistas", - "nl": "Expert Setting", - "fr": "Paramètres d ' experts", - "it": "Impostazioni degli esperti", - "es": "Configuración de expertos", - "pl": "Strona internetowa", - "uk": "Експертні налаштування", - "zh-cn": "专家设置" - }, + "label": "Expert Settings", "items": { "header_RESTAPI": { "newLine": "true", @@ -386,19 +194,7 @@ "max": 15000, "default": 6000, "label": "Request Timeout", - "help": { - "en": "in milliseconds (500-15000, default: 6000)", - "de": "in Millisekunden (500-15000, Default: 6000)", - "ru": "в миллисекундах (500-15000, по умолчанию: 6000)", - "pt": "em milissegundos (500-15000, padrão: 6000)", - "nl": "quality over quantity (qoq) releases vertaling:", - "fr": "en millisecondes (500-15000, par défaut: 6000)", - "it": "in millisecondi (500-15000, default: 6000)", - "es": "en milisegundos (500-15000, por defecto: 6000)", - "pl": "w milisekundach (500-15000) domyślnie: 6000", - "uk": "в мілісекундах (500-15000, за замовчуванням: 6000)", - "zh-cn": "(500-1 000美元,违约:6000 000美元)" - }, + "help": "in milliseconds (500-15000, default: 6000)", "sm": 12, "md": 6, "lg": 3 @@ -408,27 +204,15 @@ "min": 5, "label": "Request Interval", "default": 60, - "help": { - "en": "in seconds (min: 5, default: 60); ignored if MQTT is used", - "de": "in Sekunden (min: 5, Default: 60;), wird ignoriert wenn MQTT aktiv", - "ru": "в секундах (мин: 5, по умолчанию: 60;) игнорируется, если используется MQTT", - "pt": "em segundos (min: 5, padrão: 60;) ignorado se MQTT é usado", - "nl": "negeerde of MQTT gebruikt wordt", - "fr": "en secondes (min: 5, default: 60;) ignoré si MQTT est utilisé", - "it": "in secondi (min: 5, predefinito: 60;) ignorato se MQTT viene utilizzato", - "es": "en segundos (min: 5, default: 60;) ignorado si se utiliza MQTT", - "pl": "w drugim (min: 5, domyślna: 60; zignorowany, jeśli MQTT jest używany)", - "uk": "в секундах (min: 5, за замовчуванням: 60;) ігнорувати, якщо MQTT використовується", - "zh-cn": "在第二期(min:5,违约:60;如果使用了MQTT,则忽视了这一点。" - }, + "help": "in seconds (min: 5, default: 60); ignored if MQTT is used", "sm": 12, "md": 6, "lg": 3 }, "restUpdateUnchangedObjects": { "type": "checkbox", - "label": { "en": "Always update info states", "de": "Info-Datenpunkte immer aktualisieren" }, - "help": { "en": "Always update info objects, even if value did not change", "de": "Info-Datenpunkte werden damit immer aktualisiert, auch wenn sich der Wert nicht ändert" }, + "label": "Always update info states", + "help": "Always update info objects, even if value did not change", "default": false, "sm": 12, "md": 6, @@ -443,16 +227,16 @@ "type": "number", "min": 2, "max": 120, - "label": { "en": "Do not process published info more than every x seconds", "de": "Publizierte Infos nicht öfter als alle x Sekunden verarbeiten" }, - "help": { "en": "in seconds (2-120, default: 30)", "de": "in Sekunden (2-120, Default: 30)" }, + "label": "Do not process published info more than every x seconds", + "help": "in seconds (2-120, default: 30)", "sm": 12, "md": 6, "lg": 3 }, "mqttUpdateUnchangedObjects": { "type": "checkbox", - "label": { "en": "Always update info states", "de": "Info-Datenpunkte immer aktualisieren" }, - "help": { "en": "Always update info objects, even if value did not change", "de": "Info-Datenpunkte werden damit immer aktualisiert, auch wenn sich der Wert nicht ändert" }, + "label": "Always update info states", + "help": "Always update info objects, even if value did not change", "default": false, "sm": 12, "md": 6, @@ -460,8 +244,8 @@ }, "mqttConnErrorsAsInfo": { "type": "checkbox", - "label": { "en": "Client and Connection errors as info in log", "de": "Client- und Connection-Fehler als info im Log" }, - "help": { "en": "Logs client and connection errors as 'info' and not as 'error'", "de": "Client- und Connection-Fehler als 'info' und nicht als 'error' im Log ausgeben" }, + "label": "Client and Connection errors as info in log", + "help": "Logs client and connection errors as 'info' and not as 'error'", "default": true, "sm": 12, "md": 7, diff --git a/admin/words.js b/admin/words.js index 9b1f9cb..b391eef 100644 --- a/admin/words.js +++ b/admin/words.js @@ -1,41 +1,37 @@ /*global systemDictionary:true */ +/* ++===================== DO NOT MODIFY ======================+ +| This file was generated by translate-adapter, please use | +| `translate-adapter adminLanguages2words` to update it. | ++===================== DO NOT MODIFY ======================+ +*/ 'use strict'; systemDictionary = { - 'fully-mqtt adapter settings': { - en: 'Adapter settings for fully-mqtt', - de: 'Adaptereinstellungen für fully-mqtt', - ru: 'Настройки адаптера для fully-mqtt', - pt: 'Configurações do adaptador para fully-mqtt', - nl: 'Adapterinstellingen voor fully-mqtt', - fr: "Paramètres d'adaptateur pour fully-mqtt", - it: "Impostazioni dell'adattatore per fully-mqtt", - es: 'Ajustes del adaptador para fully-mqtt', - pl: 'Ustawienia adaptera dla fully-mqtt', - 'zh-cn': 'fully-mqtt的适配器设置', - }, - option1: { - en: 'option1', - de: 'Option 1', - ru: 'Опция 1', - pt: 'Opção 1', - nl: 'Optie 1', - fr: 'Option 1', - it: 'opzione 1', - es: 'Opción 1', - pl: 'opcja 1', - 'zh-cn': '选项1', - }, - option2: { - en: 'option2', - de: 'Option 2', - ru: 'вариант 2', - pt: 'opção 2', - nl: 'Optie 2', - fr: 'Option 2', - it: 'opzione 2', - es: 'opcion 2', - pl: 'Opcja 2', - 'zh-cn': '选项2', - }, -}; + "A detailed documentation with explanations and all further information can be found on this GitHub page:": {"en": "A detailed documentation with explanations and all further information can be found on this GitHub page:", "de": "Eine ausführliche Dokumentation mit Erklärungen und allen weiteren Informationen finden Sie auf dieser GitHub-Seite:", "ru": "Подробную документацию с пояснениями и всю дополнительную информацию можно найти на этой странице GitHub:", "pt": "Uma documentação detalhada com explicações e todas as informações adicionais podem ser encontradas nesta página do GitHub:", "nl": "Een gedetailleerde documentatie met uitleg en alle verdere informatie is te vinden op deze GitHub-pagina:", "fr": "Une documentation détaillée avec des explications et toutes les autres informations peuvent être trouvées sur cette page GitHub :", "it": "Una documentazione dettagliata con spiegazioni e tutte le ulteriori informazioni può essere trovata su questa pagina GitHub:", "es": "Puede encontrar una documentación detallada con explicaciones y toda la información adicional en esta página de GitHub:", "pl": "Szczegółową dokumentację z wyjaśnieniami i wszystkimi dodatkowymi informacjami można znaleźć na tej stronie GitHub:", "uk": "Детальну документацію з поясненнями та всю додаткову інформацію можна знайти на цій сторінці GitHub:", "zh-cn": "可以在这个 GitHub 页面上找到带有解释和所有进一步信息的详细文档:"}, + "Add new devices by clicking the plus (+) symbol...": {"en": "Add new devices by clicking the plus (+) symbol...", "de": "Neue Geräte hinzufügen, indem Sie auf das Plus-Symbol (+) klicken...", "ru": "Добавьте новые устройства, щелкнув значок плюса (+)...", "pt": "Adicione novos dispositivos clicando no símbolo de mais (+)...", "nl": "Voeg nieuwe apparaten toe door op het plusteken (+) te klikken...", "fr": "Ajoutez de nouveaux appareils en cliquant sur le symbole plus (+)...", "it": "Aggiungi nuovi dispositivi facendo clic sul simbolo più (+)...", "es": "Agregue nuevos dispositivos haciendo clic en el símbolo más (+)...", "pl": "Dodaj nowe urządzenia, klikając symbol plusa (+)...", "uk": "Додайте нові пристрої, натиснувши символ плюс (+)...", "zh-cn": "通过单击加号 (+) 符号添加新设备..."}, + "Always update info objects, even if value did not change": {"en": "Always update info objects, even if value did not change", "de": "Info-Datenpunkte 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-Datenpunkte 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 Connection-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": "客户端和连接错误作为日志中的信息"}, + "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": "不验证用户和密码"}, + "Enabled": { "en": "Enabled", "de": "Aktiv", "ru": "Включено", "pt": "Habilitado", "nl": "Ingeschakeld", "fr": "Activé", "it": "Abilitato", "es": "Activado", "pl": "Włączony", "uk": "Увімкнено", "zh-cn": "启用"}, + "Expert Settings": { "en": "Expert Settings", "de": "Experten-Einstellungen", "ru": "Настройки эксперта", "pt": "Configurações avançadas", "nl": "Expert-instellingen", "fr": "Paramètres experts", "it": "Impostazioni avanzate", "es": "Configuración experta", "pl": "Ustawienia eksperckie", "uk": "Експертні налаштування", "zh-cn": "专家设置"}, + "Fully Browser Devices": { "en": "Fully Browser Devices", "de": "Fully-Browser-Geräte", "ru": "Полностью браузерные устройства", "pt": "Dispositivos totalmente navegadores", "nl": "Volledig browserapparaten", "fr": "Appareils entièrement navigateurs", "it": "Dispositivi completamente browser", "es": "Dispositivos completamente navegadores", "pl": "Urządzenia z pełną przeglądarką", "uk": "Повністю браузерні пристрої", "zh-cn": "完全浏览器设备"}, + "Fully MQTT Adapter Documentation": { "en": "Fully MQTT Adapter Documentation", "de": "Fully-MQTT Adapter-Dokumentation", "ru": "Полная документация по адаптеру MQTT", "pt": "Documentação completa do adaptador MQTT", "nl": "Volledig MQTT-adapterdocumentatie", "fr": "Documentation complète de l'adaptateur MQTT", "it": "Documentazione completa dell'adattatore MQTT", "es": "Documentación del adaptador completamente MQTT", "pl": "Pełna dokumentacja adaptera MQTT", "uk": "Повна документація адаптера MQTT", "zh-cn": "完整的 MQTT 适配器文档"}, + "IP Address": { "en": "IP Address", "de": "IP-Adresse", "ru": "Айпи адрес", "pt": "Endereço de IP", "nl": "IP adres", "fr": "Adresse IP", "it": "Indirizzo IP", "es": "Dirección IP", "pl": "Adres IP", "uk": "IP-адреса", "zh-cn": "IP地址"}, + "Logs client and connection errors as 'info' and not as 'error'": {"en": "Logs client and connection errors as 'info' and not as 'error'", "de": "Client- und Connection-Fehler als 'info' und nicht als 'error' im Log ausgeben", "ru": "Регистрирует ошибки клиента и подключения как «информация», а не как «ошибка».", "pt": "Registra erros de cliente e conexão como 'informações' e não como 'erro'", "nl": "Registreert client- en verbindingsfouten als 'info' en niet als 'fout'", "fr": "Consigne les erreurs de client et de connexion en tant qu''info' et non en tant qu''erreur'", "it": "Registra gli errori del client e della connessione come \"info\" e non come \"errore\"", "es": "Registra los errores de conexión y del cliente como 'información' y no como 'error'", "pl": "Rejestruje błędy klienta i połączenia jako „informacje”, a nie jako „błąd”", "uk": "Реєструє помилки клієнта та підключення як «інформацію», а не як «помилку»", "zh-cn": "将客户端和连接错误记录为“信息”而不是“错误”"}, + "MQTT Configuration": { "en": "MQTT Configuration", "de": "MQTT-Konfiguration", "ru": "Конфигурация MQTT", "pt": "Configuração MQTT", "nl": "MQTT-configuratie", "fr": "Configuration MQTT", "it": "Configurazione MQTT", "es": "Configuración MQTT", "pl": "Konfiguracja MQTT", "uk": "Конфігурація MQTT", "zh-cn": "MQTT 配置"}, + "MQTT Password": { "en": "MQTT Password", "de": "MQTT-Passwort", "ru": "MQTT-пароль", "pt": "Senha do MQTT", "nl": "MQTT-wachtwoord", "fr": "Mot de passe MQTT", "it": "Password MQTT", "es": "Contraseña MQTT", "pl": "Hasło MQTT", "uk": "Пароль MQTT", "zh-cn": "MQTT密码"}, + "Password": { "en": "Password", "de": "Passwort", "ru": "Пароль", "pt": "Senha", "nl": "Wachtwoord", "fr": "Mot de passe", "it": "Parola d'ordine", "es": "Contraseña", "pl": "Hasło", "uk": "Пароль", "zh-cn": "密码"}, + "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)": {"en": "Port is always required, but you can leave user name and password empty. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", "de": "Die Angabe der Port-Nummer ist immer erforderlich, aber Benutzername und Passwort kann man leer lassen. Fully Browser App: Settings -> Other Settings -> MQTT Integration (PLUS)", "ru": "Порт требуется всегда, но вы можете оставить имя пользователя и пароль пустыми. Полностью браузерное приложение: Настройки -> Другие настройки -> Интеграция MQTT (ПЛЮС)", "pt": "A porta é sempre necessária, mas você pode deixar o nome de usuário e a senha em branco. Aplicativo totalmente do navegador: Configurações -> Outras configurações -> Integração MQTT (PLUS)", "nl": "Poort is altijd vereist, maar u kunt de gebruikersnaam en het wachtwoord leeg laten. Volledig browser-app: Instellingen -> Andere instellingen -> MQTT-integratie (PLUS)", "fr": "Le port est toujours requis, mais vous pouvez laisser le nom d'utilisateur et le mot de passe vides. Application entièrement navigateur : Paramètres -> Autres paramètres -> Intégration MQTT (PLUS)", "it": "La porta è sempre richiesta, ma puoi lasciare il nome utente e la password vuoti. App completamente browser: Impostazioni -> Altre impostazioni -> Integrazione MQTT (PLUS)", "es": "El puerto siempre es obligatorio, pero puede dejar el nombre de usuario y la contraseña vacíos. Aplicación de navegador completo: Configuración -> Otras configuraciones -> Integración MQTT (MÁS)", "pl": "Port jest zawsze wymagany, ale nazwę użytkownika i hasło można pozostawić puste. Pełna aplikacja przeglądarki: Ustawienia -> Inne ustawienia -> Integracja MQTT (PLUS)", "uk": "Порт завжди потрібен, але ви можете залишити ім’я користувача та пароль пустими. Повністю браузерна програма: Налаштування -> Інші налаштування -> Інтеграція MQTT (PLUS)", "zh-cn": "端口始终是必需的,但您可以将用户名和密码留空。完全浏览器应用程序:设置 -> 其他设置 -> MQTT 集成 (PLUS)"}, + "Protocol": { "en": "Protokoll", "de": "Protokoll", "ru": "Протокол", "pt": "Protocolo", "nl": "Protocol", "fr": "Protocole", "it": "Protocollo", "es": "Protocolo", "pl": "Protokół", "uk": "протокол", "zh-cn": "协议书"}, + "Remote Admin Password": { "en": "Remote Admin Password", "de": "Remote Admin Passwort", "ru": "Пароль удаленного администратора", "pt": "Senha do administrador remoto", "nl": "Wachtwoord voor beheerder op afstand", "fr": "Mot de passe administrateur distant", "it": "Password dell'amministratore remoto", "es": "Contraseña de administrador remoto", "pl": "Hasło administratora zdalnego", "uk": "Пароль віддаленого адміністратора", "zh-cn": "远程管理员密码"}, + "This disables the username and password verification": {"en": "This disables the username and password verification", "de": "Die Überprüfung von Benutzername und Passwort wird damit deaktiviert", "ru": "Это отключает проверку имени пользователя и пароля", "pt": "Isso desativa a verificação de nome de usuário e senha", "nl": "Hierdoor wordt de gebruikersnaam- en wachtwoordverificatie uitgeschakeld", "fr": "Cela désactive la vérification du nom d'utilisateur et du mot de passe", "it": "Ciò disabilita la verifica del nome utente e della password", "es": "Esto deshabilita la verificación de nombre de usuario y contraseña.", "pl": "Spowoduje to wyłączenie weryfikacji nazwy użytkownika i hasła", "uk": "Це вимикає перевірку імені користувача та пароля", "zh-cn": "这将禁用用户名和密码验证"}, + "User name": { "en": "User name", "de": "Benutzername", "ru": "Имя пользователя", "pt": "Nome de usuário", "nl": "Gebruikersnaam", "fr": "Nom d'utilisateur", "it": "Nome utente", "es": "Nombre de usuario", "pl": "Nazwa użytkownika", "uk": "Ім'я користувача", "zh-cn": "用户名"}, + "Welcome to Fully MQTT adapter": { "en": "Welcome to Fully MQTT adapter", "de": "Willkommen beim Fully-MQTT Adapter", "ru": "Добро пожаловать в адаптер Fully MQTT", "pt": "Bem-vindo ao adaptador totalmente MQTT", "nl": "Welkom bij de volledig MQTT-adapter", "fr": "Bienvenue dans l'adaptateur entièrement MQTT", "it": "Benvenuto nell'adattatore Fully MQTT", "es": "Bienvenido al adaptador completamente MQTT", "pl": "Witamy w adapterze Fully MQTT", "uk": "Ласкаво просимо до адаптера Fully MQTT", "zh-cn": "欢迎使用 Fully MQTT 适配器"}, + "in milliseconds (500-15000, default: 6000)": { "en": "in milliseconds (500-15000, default: 6000)", "de": "in Millisekunden (500-15000, Default: 6000)", "ru": "в миллисекундах (500-15000, по умолчанию: 6000)", "pt": "em milissegundos (500-15000, padrão: 6000)", "nl": "in milliseconden (500-15000, standaard: 6000)", "fr": "en millisecondes (500-15000, par défaut : 6000)", "it": "in millisecondi (500-15000, predefinito: 6000)", "es": "en milisegundos (500-15000, predeterminado: 6000)", "pl": "w milisekundach (500-15000, domyślnie: 6000)", "uk": "у мілісекундах (500-15000, за замовчуванням: 6000)", "zh-cn": "以毫秒为单位(500-15000,默认值:6000)"}, + "in seconds (2-120, default: 30)": { "en": "in seconds (2-120, default: 30)", "de": "in Sekunden (2-120, Default: 30)", "ru": "в секундах (2-120, по умолчанию: 30)", "pt": "em segundos (2-120, padrão: 30)", "nl": "in seconden (2-120, standaard: 30)", "fr": "en secondes (2-120, par défaut : 30)", "it": "in secondi (2-120, predefinito: 30)", "es": "en segundos (2-120, predeterminado: 30)", "pl": "w sekundach (2-120, domyślnie: 30)", "uk": "за секунди (2-120, за замовчуванням: 30)", "zh-cn": "以秒为单位(2-120,默认值:30)"}, + "in seconds (min: 5, default: 60); ignored if MQTT is used": {"en": "in seconds (min: 5, default: 60); ignored if MQTT is used", "de": "in Sekunden (min: 5, Default: 60); ignoriert wenn MQTT verwendet wird", "ru": "в секундах (мин.: 5, по умолчанию: 60); игнорируется, если используется MQTT", "pt": "em segundos (min: 5, padrão: 60); ignorado se o MQTT for usado", "nl": "in seconden (min: 5, standaard: 60); genegeerd als MQTT wordt gebruikt", "fr": "en secondes (min : 5, par défaut : 60) ; ignoré si MQTT est utilisé", "it": "in secondi (min: 5, default: 60); ignorato se viene utilizzato MQTT", "es": "en segundos (mín.: 5, predeterminado: 60); ignorado si se usa MQTT", "pl": "w sekundach (min: 5, domyślnie: 60); ignorowane, jeśli używany jest MQTT", "uk": "у секундах (хв.: 5, за замовчуванням: 60); ігнорується, якщо використовується MQTT", "zh-cn": "以秒为单位(最小值:5,默认值:60);如果使用 MQTT,则忽略"}, +}; \ No newline at end of file diff --git a/build/lib/interfaces.js.map b/build/lib/interfaces.js.map index bdc4024..73d1387 100644 --- a/build/lib/interfaces.js.map +++ b/build/lib/interfaces.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../src/lib/interfaces.ts"], - "sourcesContent": ["export interface IDevice {\n name: string; // e.g. \"Tablet Hallway Entry\"\n id: string; // e.g. \"Tablet-Hallway-Entry\" (meets ioBroker state convention)\n mqttClientId?: string; // e.g. \"xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n useMQTT: true | false;\n ip: string;\n restProtocol: 'http' | 'https';\n restPort: number;\n restPassword: string;\n lastSeen: number; // timestamp\n isAlive: true | false;\n timeoutRestRequestInfo: NodeJS.Timeout | undefined;\n mqttInfoObjectsCreated: true | false; // Set to true once first time creation initiated\n mqttInfoKeys: string[]; // Info keys from MQTT info, like 'batteryLevel', 'deviceID', ...\n restInfoKeys: string[]; // Info keys from Rest API info, like 'batteryLevel', 'deviceID', ...\n}\n\nexport interface ICmds {\n readonly id: string;\n readonly name: string;\n readonly type: 'number' | 'boolean' | 'string';\n readonly cmdOn?: string;\n readonly cmdOff?: string;\n readonly mqttOn?: string;\n readonly mqttOff?: string;\n}\n\nexport interface IMqttDevice {\n ip?: string;\n lastSeen?: number;\n mqttFirstReceived?: true | false;\n isActive?: true | false;\n timeoutNoUpdate?: NodeJS.Timeout | undefined;\n previousInfoPublishTime?: number;\n}\n\nexport interface IConst {\n readonly mqttEvents: string[];\n readonly cmds: ICmds[];\n readonly cmdsSwitches: ICmds[];\n}\n"], + "sourcesContent": ["export interface IDevice {\n name: string; // e.g. \"Tablet Hallway Entry\"\n id: string; // e.g. \"Tablet-Hallway-Entry\" (meets ioBroker state convention)\n mqttClientId?: string; // e.g. \"xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n useMQTT: true | false;\n ip: string;\n restProtocol: 'http' | 'https';\n restPort: number;\n restPassword: string;\n lastSeen: number; // timestamp\n isAlive: true | false;\n timeoutRestRequestInfo: ioBroker.Timeout | null;\n mqttInfoObjectsCreated: true | false; // Set to true once first time creation initiated\n mqttInfoKeys: string[]; // Info keys from MQTT info, like 'batteryLevel', 'deviceID', ...\n restInfoKeys: string[]; // Info keys from Rest API info, like 'batteryLevel', 'deviceID', ...\n}\n\nexport interface ICmds {\n readonly id: string;\n readonly name: string;\n readonly type: 'number' | 'boolean' | 'string';\n readonly cmdOn?: string;\n readonly cmdOff?: string;\n readonly mqttOn?: string;\n readonly mqttOff?: string;\n}\n\nexport interface IMqttDevice {\n ip?: string;\n lastSeen?: number;\n mqttFirstReceived?: true | false;\n isActive?: true | false;\n timeoutNoUpdate?: ioBroker.Timeout | null;\n previousInfoPublishTime?: number;\n}\n\nexport interface IConst {\n readonly mqttEvents: string[];\n readonly cmds: ICmds[];\n readonly cmdsSwitches: ICmds[];\n}\n"], "mappings": ";;;;;;;;;;;;;AAAA;AAAA;", "names": [] } diff --git a/build/lib/mqtt-server.js b/build/lib/mqtt-server.js index 2f6b054..e1e5abc 100644 --- a/build/lib/mqtt-server.js +++ b/build/lib/mqtt-server.js @@ -251,7 +251,8 @@ class MqttServer { if (isAlive) { this.scheduleCheckIfStillActive(clientId); } else { - clearTimeout(this.devices[clientId].timeoutNoUpdate); + if (this.devices[clientId].timeoutNoUpdate) + this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate); } } else { this.adapter.log.debug(`[MQTT] isAlive changed to ${isAlive}, but IP of client ${clientId} is still unknown.`); @@ -259,11 +260,12 @@ class MqttServer { } async scheduleCheckIfStillActive(clientId) { try { - clearTimeout(this.devices[clientId].timeoutNoUpdate); + if (this.devices[clientId].timeoutNoUpdate) + this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate); if (!this.devices[clientId]) this.devices[clientId] = {}; const interval = 70 * 1e3; - this.devices[clientId].timeoutNoUpdate = setTimeout(async () => { + this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => { try { const lastSeen = this.devices[clientId].lastSeen; if (!lastSeen) @@ -288,7 +290,8 @@ class MqttServer { terminate() { this.adapter.log.info(`[MQTT] Disconnect all clients and close server`); for (const clientId in this.devices) { - clearTimeout(this.devices[clientId].timeoutNoUpdate); + if (this.devices[clientId].timeoutNoUpdate) + this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate); this.setIsAlive(clientId, false); } if (this.aedes) { diff --git a/build/lib/mqtt-server.js.map b/build/lib/mqtt-server.js.map index 64f1e8c..0e755db 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 specific adapter directory\n */\n if (this.adapter.adapterDir === 'C:/iobroker/DEV1/node_modules/ioBroker.fully-mqtt/.dev-server/default/node_modules/iobroker.fully-mqtt') {\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 // log\n this.adapter.log.info(`[MQTT]\uD83D\uDE80 Server started and 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 && !this.adapter.activeDeviceIPs.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.info(`[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(`[MQTT]\uD83D\uDD11 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 // save client's last seen\n this.devices[client.id].lastSeen = Date.now();\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);\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 // save client's last seen and isActive\n this.devices[client.id].lastSeen = Date.now();\n this.setIsAlive(client.id, true);\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 (!this.adapter.activeDeviceIPs.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);\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);\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);\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): void {\n this.devices[clientId].isActive = isAlive;\n const ip = this.devices[clientId]?.ip;\n if (ip) {\n this.adapter.onAliveChange('MQTT', ip, isAlive);\n if (isAlive) {\n this.scheduleCheckIfStillActive(clientId); // restart timer\n } else {\n // clear timer\n 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 clearTimeout(this.devices[clientId].timeoutNoUpdate);\n\n if (!this.devices[clientId]) this.devices[clientId] = {};\n\n // IP\n // const ip = this.devices[clientId].ip;\n // const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`;\n const interval = 70 * 1000; // every 60s + 10s buffer\n this.devices[clientId].timeoutNoUpdate = setTimeout(async () => {\n try {\n const lastSeen = this.devices[clientId].lastSeen;\n if (!lastSeen) return;\n const diff = Date.now() - lastSeen;\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);\n } else {\n // this.adapter.log.debug(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, true);\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 clearTimeout(this.devices[clientId].timeoutNoUpdate); // Clear timeout\n this.setIsAlive(clientId, false);\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 specific adapter directory\n */\n if (this.adapter.adapterDir === 'C:/iobroker/DEV1/node_modules/ioBroker.fully-mqtt/.dev-server/default/node_modules/iobroker.fully-mqtt') {\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 // log\n this.adapter.log.info(`[MQTT]\uD83D\uDE80 Server started and 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 && !this.adapter.activeDeviceIPs.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.info(`[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(`[MQTT]\uD83D\uDD11 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 // save client's last seen\n this.devices[client.id].lastSeen = Date.now();\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);\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 // save client's last seen and isActive\n this.devices[client.id].lastSeen = Date.now();\n this.setIsAlive(client.id, true);\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 (!this.adapter.activeDeviceIPs.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);\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);\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);\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): void {\n this.devices[clientId].isActive = isAlive;\n const ip = this.devices[clientId]?.ip;\n if (ip) {\n this.adapter.onAliveChange('MQTT', ip, isAlive);\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 // @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 // IP\n // const ip = this.devices[clientId].ip;\n // const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`;\n const interval = 70 * 1000; // every 60s + 10s buffer\n this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => {\n try {\n const lastSeen = this.devices[clientId].lastSeen;\n if (!lastSeen) return;\n const diff = Date.now() - lastSeen;\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);\n } else {\n // this.adapter.log.debug(`[MQTT] ${ipMsg} is alive - last contact ${Math.round(diff / 1000)}s (${diff}ms) ago`);\n this.setIsAlive(clientId, true);\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);\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": ";;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,iBAAgB;AAKT,MAAM,WAAW;AAAA,EAWb,YAAY,SAAoB;AANvC,SAAQ,OAAO;AACf,SAAQ,uBAAiC,CAAC;AAMtC,SAAK,UAAU;AAEf,SAAK,QAAQ,IAAI,aAAAA,QAAM;AAEvB,SAAK,SAAS,WAAAC,QAAI,aAAa,QAAW,KAAK,MAAM,MAAM;AAC3D,SAAK,UAAU,CAAC;AAAA,EACpB;AAAA,EAKO,QAAc;AACjB,QAAI;AAIA,WAAK,OAAO,KAAK,QAAQ,OAAO;AAKhC,UAAI,KAAK,QAAQ,eAAe,0GAA0G;AACtI,aAAK,OAAO;AACZ,aAAK,QAAQ,IAAI,KAAK,8BAA8B,KAAK,iGAAiG;AAAA,MAC9J;AAKA,WAAK,OAAO,OAAO,KAAK,MAAM,MAAM;AAEhC,aAAK,QAAQ,IAAI,KAAK,wDAAiD,KAAK,MAAM;AAAA,MACtF,CAAC;AAOD,WAAK,MAAM,eAAe,CAAC,QAAQ,UAAU,UAAU,aAAa;AAChE,YAAI;AAEA,cAAI,KAAK,qBAAqB,SAAS,OAAO,EAAE,GAAG;AAC/C,qBAAS,MAAM,KAAK;AACpB;AAAA,UACJ;AAGA,cAAI,CAAC,KAAK,QAAQ,OAAO;AAAK,iBAAK,QAAQ,OAAO,MAAM,CAAC;AAOzD,cAAI,KAAyB;AAC7B,cAAI,OAAO,QAAQ,mBAAmB,OAAO,QAAQ,OAAO,OAAO,KAAK,kBAAkB,UAAU;AAChG,kBAAM,WAAW,OAAO,KAAK;AAC7B,iBAAK,QAAQ,IAAI,MAAM,uCAAuC,eAAe,OAAO,IAAI;AACxF,iBAAK,SAAS,UAAU,SAAS,YAAY,GAAG,IAAI,CAAC;AACrD,gBAAI,CAAC,KAAK,QAAQ,iBAAiB,EAAE;AAAG,qBAAO;AAAA,UACnD;AAEA,cAAI,MAAM,CAAC,KAAK,QAAQ,gBAAgB,SAAS,EAAE,GAAG;AAClD,iBAAK,QAAQ,IAAI,MAAM,iBAAiB,OAAO,sBAAsB,2DAA2D;AAChI,iBAAK,qBAAqB,KAAK,OAAO,EAAE;AACxC,qBAAS,MAAM,KAAK;AACpB;AAAA,UACJ;AAEA,gBAAM,QAAQ,KAAK,GAAG,KAAK,QAAQ,OAAO,IAAI,SAAS,QAAQ,GAAG,OAAO;AACzE,eAAK,QAAQ,IAAI,KAAK,iBAAiB,+BAA+B;AACtE,cAAI;AAAI,iBAAK,QAAQ,OAAO,IAAI,KAAK;AAKrC,cAAI,CAAC,KAAK,QAAQ,OAAO,uBAAuB;AAE5C,gBAAI,aAAa,KAAK,QAAQ,OAAO,UAAU;AAC3C,mBAAK,QAAQ,IAAI,KAAK,iBAAiB,qDAAqD,6BAA6B,KAAK,QAAQ,OAAO,gCAAgC;AAC7K,uBAAS,MAAM,KAAK;AACpB;AAAA,YACJ;AAEA,gBAAI,SAAS,SAAS,MAAM,KAAK,QAAQ,OAAO,cAAc;AAC1D,mBAAK,QAAQ,IAAI,KAAK,iBAAiB,mGAAmG;AAC1I,uBAAS,MAAM,KAAK;AACpB;AAAA,YACJ;AAAA,UACJ;AACA,eAAK,QAAQ,IAAI,KAAK,0BAAmB,mCAAmC;AAC5E,mBAAS,MAAM,IAAI;AAAA,QACvB,SAAS,GAAP;AACE,eAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC;AAC9C,mBAAS,MAAM,KAAK;AAAA,QACxB;AAAA,MACJ;AAKA,WAAK,MAAM,GAAG,UAAU,CAAC,WAAW;AAChC,YAAI;AACA,cAAI,CAAC;AAAQ;AAGb,cAAI,CAAC,KAAK,QAAQ,OAAO;AAAK,iBAAK,QAAQ,OAAO,MAAM,CAAC;AAGzD,gBAAM,KAAK,KAAK,QAAQ,OAAO,IAAI;AACnC,gBAAM,QAAQ,KAAK,GAAG,KAAK,QAAQ,OAAO,IAAI,SAAS,QAAQ,GAAG,OAAO;AAGzE,eAAK,QAAQ,OAAO,IAAI,WAAW,KAAK,IAAI;AAE5C,eAAK,QAAQ,IAAI,MAAM,iBAAiB,6BAA6B,KAAK,MAAM,IAAI;AACpF,eAAK,QAAQ,IAAI,KAAK,0BAAmB,+BAA+B;AAIxE,eAAK,WAAW,OAAO,IAAI,IAAI;AAG/B,eAAK,2BAA2B,OAAO,EAAE;AAAA,QAC7C,SAAS,GAAP;AACE,eAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC;AAC9C;AAAA,QACJ;AAAA,MACJ,CAAC;AAKD,WAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,WAAW;AACzC,YAAI;AACA,cAAI,CAAC,UAAU,CAAC;AAAQ;AAGxB,eAAK,QAAQ,OAAO,IAAI,WAAW,KAAK,IAAI;AAC5C,eAAK,WAAW,OAAO,IAAI,IAAI;AAG/B,cAAI,CAAC,KAAK,QAAQ,OAAO;AAAK,iBAAK,QAAQ,OAAO,MAAM,CAAC;AAGzD,cAAI,OAAO,QAAQ;AAAG;AAEtB,cAAI,OAAO,QAAQ;AAOf,kBAAM,OAAO,KAAK,MAAM,OAAO,QAAQ,SAAS,CAAC;AAIjD,gBAAI,EAAE,cAAc,SAAS,EAAE,SAAS,OAAO;AAC3C,mBAAK,QAAQ,IAAI,MAAM,2BAA2B,KAAK,8EAA8E,KAAK,UAAU;AACpJ;AAAA,YACJ;AAGA,kBAAM,KAAK,KAAK;AAChB,kBAAM,SAAS,GAAG,KAAK,QAAQ,OAAO,IAAI,SAAS;AAEnD,gBAAI,CAAC,KAAK,QAAQ,gBAAgB,SAAS,EAAE,GAAG;AAC5C,mBAAK,QAAQ,IAAI,MAAM,iBAAiB,mEAAmE,OAAO,IAAI;AACtH;AAAA,YACJ;AACA,iBAAK,QAAQ,OAAO,IAAI,KAAK;AAI7B,kBAAM,WAAW,KAAK,QAAQ,OAAO,IAAI;AACzC,kBAAM,QAAQ,KAAK,QAAQ,OAAO,yBAAyB;AAC3D,gBAAI,YAAY,aAAa,GAAG;AAC5B,kBAAI,KAAK,IAAI,IAAI,WAAW,OAAO;AAC/B,sBAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,qBAAK,QAAQ,IAAI,MAAM,UAAU,+CAA+C,aAAa,KAAK,MAAM,SAAS,GAAI,YAAY;AACjI;AAAA,cACJ;AAAA,YACJ;AACA,iBAAK,QAAQ,OAAO,IAAI,0BAA0B,KAAK,IAAI;AAK3D,gBAAI,CAAC,KAAK,QAAQ,OAAO,IAAI,mBAAmB;AAE5C,mBAAK,QAAQ,IAAI,MAAM,iBAAiB,OAAO,QAAQ,KAAK,QAAQ,OAAO,IAAI,UAAU,IAAI;AAE7F,mBAAK,QAAQ,OAAO,IAAI,oBAAoB;AAAA,YAChD;AAIA,kBAAM,SAAS;AAAA,cACX,UAAU,OAAO;AAAA,cACjB;AAAA,cACA,OAAO,OAAO;AAAA,cACd,SAAS;AAAA,YACb;AACA,iBAAK,QAAQ,WAAW,MAAM;AAAA,UAClC,WAAW,OAAO,QAAQ,KAAK,CAAC,OAAO,QAAQ;AAO3C,kBAAM,MAAM,KAAK,MAAM,OAAO,QAAQ,SAAS,CAAC;AAIhD,gBAAI,EAAE,WAAW,MAAM;AACnB,mBAAK,QAAQ,IAAI,MAAM,sFAAsF,OAAO,IAAI;AACxH;AAAA,YACJ;AAGA,gBAAI,IAAI,UAAU,iBAAiB;AAC/B,mBAAK,QAAQ,IAAI,MAAM,gEAAgE,IAAI,UAAU;AACrG;AAAA,YACJ;AAGA,gBAAI,CAAC,KAAK,QAAQ,OAAO,KAAK;AAC1B,mBAAK,QAAQ,IAAI,KAAK,0FAA0F;AAChH,mBAAK,QAAQ,IAAI,KAAK,iDAAiD,IAAI,UAAU;AACrF;AAAA,YACJ;AACA,kBAAM,KAAK,KAAK,QAAQ,OAAO,IAAI,KAAK,KAAK,QAAQ,OAAO,IAAI,KAAK;AACrE,gBAAI,OAAO,MAAM,OAAO,OAAO,UAAU;AACrC,mBAAK,QAAQ,IAAI,MAAM,iFAAiF,OAAO,IAAI;AACnH,mBAAK,QAAQ,IAAI,MAAM,wFAAwF;AAC/G;AAAA,YACJ;AAGA,kBAAM,SAAS;AAAA,cACX,UAAU,OAAO;AAAA,cACjB;AAAA,cACA,OAAO,OAAO;AAAA,cACd,KAAK,IAAI;AAAA,YACb;AACA,gBAAI,CAAC,KAAK,QAAQ,OAAO,IAAI,mBAAmB;AAE5C,mBAAK,QAAQ,IAAI,KAAK,2BAAoB,OAAO,QAAQ,KAAK,QAAQ,OAAO,IAAI,SAAS,KAAK;AAC/F,mBAAK,QAAQ,OAAO,IAAI,oBAAoB;AAAA,YAChD;AAIA,iBAAK,QAAQ,YAAY,MAAM;AAAA,UACnC,OAAO;AAEH;AAAA,UACJ;AAAA,QACJ,SAAS,GAAP;AACE,eAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC;AAC9C;AAAA,QACJ;AAAA,MACJ,CAAC;AAKD,WAAK,MAAM,GAAG,oBAAoB,CAAC,WAAW;AAC1C,cAAM,KAAK,KAAK,QAAQ,OAAO,IAAI;AACnC,cAAM,aAAa,KAAK,KAAK,QAAQ,OAAO,IAAI,OAAO,OAAO;AAC9D,YAAI,KAAK,QAAQ,OAAO,sBAAsB;AAC1C,eAAK,QAAQ,IAAI,KAAK,iBAAiB,0BAA0B;AAAA,QACrE,OAAO;AACH,eAAK,QAAQ,IAAI,MAAM,iBAAiB,0BAA0B;AAAA,QACtE;AACA,aAAK,WAAW,OAAO,IAAI,KAAK;AAAA,MACpC,CAAC;AAKD,WAAK,MAAM,GAAG,eAAe,CAAC,QAAQ,MAAM;AACxC,YAAI,KAAK,qBAAqB,SAAS,OAAO,EAAE;AAAG;AACnD,cAAM,KAAK,KAAK,QAAQ,OAAO,IAAI;AACnC,cAAM,aAAa,KAAK,KAAK,QAAQ,OAAO,IAAI,OAAO,OAAO;AAC9D,YAAI,KAAK,QAAQ,OAAO,sBAAsB;AAC1C,eAAK,QAAQ,IAAI,KAAK,UAAU,8BAA8B,EAAE,SAAS;AAAA,QAC7E,OAAO;AACH,eAAK,QAAQ,IAAI,MAAM,mBAAY,8BAA8B,EAAE,SAAS;AAAA,QAChF;AACA,aAAK,QAAQ,IAAI,MAAM,mBAAY,qCAAqC,EAAE,OAAO;AACjF,aAAK,WAAW,OAAO,IAAI,KAAK;AAAA,MACpC,CAAC;AAED,WAAK,MAAM,GAAG,mBAAmB,CAAC,QAAQ,MAAM;AAC5C,cAAM,KAAK,KAAK,QAAQ,OAAO,IAAI;AACnC,cAAM,aAAa,KAAK,KAAK,QAAQ,OAAO,IAAI,OAAO,OAAO;AAC9D,YAAI,KAAK,QAAQ,OAAO,sBAAsB;AAC1C,eAAK,QAAQ,IAAI,KAAK,UAAU,kCAAkC,EAAE,SAAS;AAAA,QACjF,OAAO;AACH,eAAK,QAAQ,IAAI,MAAM,mBAAY,kCAAkC,EAAE,SAAS;AAAA,QACpF;AACA,aAAK,QAAQ,IAAI,MAAM,mBAAY,yCAAyC,EAAE,OAAO;AACrF,aAAK,WAAW,OAAO,IAAI,KAAK;AAAA,MACpC,CAAC;AAKD,WAAK,OAAO,GAAG,SAAS,CAAC,MAAW;AAChC,YAAI,aAAa,SAAS,EAAE,QAAQ,WAAW,mBAAmB,GAAG;AACjE,eAAK,QAAQ,IAAI,MAAM,gCAAgC,EAAE,SAAS;AAClE,eAAK,QAAQ,IAAI,MAAM,8CAAuC,KAAK,+CAA+C;AAAA,QACtH,OAAO;AACH,eAAK,QAAQ,IAAI,MAAM,yCAAkC,EAAE,SAAS;AAAA,QACxE;AACA,aAAK,UAAU;AAAA,MACnB,CAAC;AAAA,IACL,SAAS,GAAP;AACE,WAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC;AAC9C;AAAA,IACJ;AAAA,EACJ;AAAA,EAKQ,WAAW,UAAkB,SAA6B;AA5VtE;AA6VQ,SAAK,QAAQ,UAAU,WAAW;AAClC,UAAM,MAAK,UAAK,QAAQ,cAAb,mBAAwB;AACnC,QAAI,IAAI;AACJ,WAAK,QAAQ,cAAc,QAAQ,IAAI,OAAO;AAC9C,UAAI,SAAS;AACT,aAAK,2BAA2B,QAAQ;AAAA,MAC5C,OAAO;AAGH,YAAI,KAAK,QAAQ,UAAU;AAAiB,eAAK,QAAQ,aAAa,KAAK,QAAQ,UAAU,eAAe;AAAA,MAChH;AAAA,IACJ,OAAO;AACH,WAAK,QAAQ,IAAI,MAAM,6BAA6B,6BAA6B,4BAA4B;AAAA,IACjH;AAAA,EACJ;AAAA,EAOA,MAAc,2BAA2B,UAAiC;AACtE,QAAI;AAEA,UAAI,KAAK,QAAQ,UAAU;AAAiB,aAAK,QAAQ,aAAa,KAAK,QAAQ,UAAU,eAAe;AAE5G,UAAI,CAAC,KAAK,QAAQ;AAAW,aAAK,QAAQ,YAAY,CAAC;AAKvD,YAAM,WAAW,KAAK;AACtB,WAAK,QAAQ,UAAU,kBAAkB,KAAK,QAAQ,WAAW,YAAY;AACzE,YAAI;AACA,gBAAM,WAAW,KAAK,QAAQ,UAAU;AACxC,cAAI,CAAC;AAAU;AACf,gBAAM,OAAO,KAAK,IAAI,IAAI;AAC1B,cAAI,OAAO,KAAO;AAEd,iBAAK,WAAW,UAAU,KAAK;AAAA,UACnC,OAAO;AAEH,iBAAK,WAAW,UAAU,IAAI;AAAA,UAClC;AAEA,eAAK,2BAA2B,QAAQ;AAAA,QAC5C,SAAS,GAAP;AACE,eAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC;AAC9C;AAAA,QACJ;AAAA,MACJ,GAAG,QAAQ;AAAA,IACf,SAAS,GAAP;AACE,WAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC;AAC9C;AAAA,IACJ;AAAA,EACJ;AAAA,EAKO,YAAkB;AACrB,SAAK,QAAQ,IAAI,KAAK,gDAAgD;AAEtE,eAAW,YAAY,KAAK,SAAS;AAEjC,UAAI,KAAK,QAAQ,UAAU;AAAiB,aAAK,QAAQ,aAAa,KAAK,QAAQ,UAAU,eAAe;AAC5G,WAAK,WAAW,UAAU,KAAK;AAAA,IACnC;AAEA,QAAI,KAAK,OAAO;AACZ,WAAK,MAAM,MAAM,MAAM;AACnB,aAAK,QAAQ,IAAI,MAAM,gCAAgC;AACvD,YAAI,KAAK,QAAQ;AACb,eAAK,OAAO,MAAM,MAAM;AACpB,iBAAK,QAAQ,IAAI,MAAM,iCAAiC;AAAA,UAC5D,CAAC;AAAA,QACL;AAAA,MACJ,CAAC;AAAA,IACL,WAAW,KAAK,QAAQ;AACpB,WAAK,OAAO,MAAM,MAAM;AACpB,aAAK,QAAQ,IAAI,MAAM,iCAAiC;AAAA,MAC5D,CAAC;AAAA,IACL;AAAA,EACJ;AACJ;", "names": ["Aedes", "net"] } diff --git a/build/main.js b/build/main.js index 55073e7..3492438 100644 --- a/build/main.js +++ b/build/main.js @@ -226,11 +226,12 @@ class FullyMqtt extends utils.Adapter { } async scheduleRestApiRequestInfo(ip) { try { - clearTimeout(this.fullys[ip].timeoutRestRequestInfo); + if (this.fullys[ip].timeoutRestRequestInfo) + this.clearTimeout(this.fullys[ip].timeoutRestRequestInfo); const interval = this.config.restInterval * 1e3; if (interval < 2e3) throw `[REST] We do not allow to set a REST API interval for info update every < 2 seconds!`; - this.fullys[ip].timeoutRestRequestInfo = setTimeout(async () => { + this.fullys[ip].timeoutRestRequestInfo = this.setTimeout(async () => { try { const infoObj = await this.restApi_inst.getInfo(ip); if (infoObj !== false) { @@ -254,8 +255,8 @@ class FullyMqtt extends utils.Adapter { this.log.warn(`Adapter instance settings: REST API timeout of ${this.config.restTimeout} ms is not allowed, set to default of 6000ms`); this.config.restTimeout = 6e3; } - if (this.isEmpty(this.config.restInterval) || this.config.restInterval < 5) { - this.log.warn(`Adapter instance settings: REST API timeout of ${this.config.restInterval}s is not allowed, set to default of 60s`); + if (this.isEmpty(this.config.restInterval) || this.config.restInterval < 5 || this.config.restInterval > 864e5) { + this.log.warn(`Adapter instance settings: REST API interval of ${this.config.restInterval}s is not allowed, set to default of 60s`); this.config.restInterval = 60; } if (this.isEmpty(this.config.mqttPort) || this.config.mqttPort < 1 || this.config.mqttPort > 65535) { @@ -284,7 +285,7 @@ class FullyMqtt extends utils.Adapter { restPassword: "", lastSeen: 0, isAlive: false, - timeoutRestRequestInfo: void 0, + timeoutRestRequestInfo: null, mqttInfoObjectsCreated: false, mqttInfoKeys: [], restInfoKeys: [] @@ -535,14 +536,16 @@ class FullyMqtt extends utils.Adapter { try { if (this.fullys) { for (const ip in this.fullys) { - clearTimeout(this.fullys[ip].timeoutRestRequestInfo); + if (this.fullys[ip].timeoutRestRequestInfo) + this.clearTimeout(this.fullys[ip].timeoutRestRequestInfo); this.log.info(`${this.fullys[ip].name}: Clear timeouts.`); this.setState(this.fullys[ip].id + ".alive", { val: false, ack: true }); } } if (this.mqtt_Server) { for (const clientId in this.mqtt_Server.devices) { - clearTimeout(this.mqtt_Server.devices[clientId].timeoutNoUpdate); + if (this.mqtt_Server.devices[clientId].timeoutNoUpdate) + this.clearTimeout(this.mqtt_Server.devices[clientId].timeoutNoUpdate); } } if (this.mqtt_Server) { diff --git a/build/main.js.map b/build/main.js.map index 562390a..1778d1d 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 // MQTT\n private mqtt_Server: MqttServer | undefined;\n public mqtt_useMqtt: true | false = false; // Is use of MQTT activated per adapter settings (each line of fully devices is checked)\n\n // REST API\n private restApi_inst = new RestApiFully(this); // RestApi Class Instance\n\n /**\n * Active 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 * Use this.getFullyPerKey() to get fully object per provided key\n */\n public fullys: { [ip: string]: IDevice } = {};\n\n // array of device ids, which are not activated\n public disabledDeviceIds = [] as string[];\n // All active IP addresses\n public activeDeviceIPs = [] as string[];\n\n // Has onAliveChange() ever been called before?\n private onAliveChange_EverBeenCalledBefore = false;\n\n /**\n * Constructor\n */\n public constructor(options: Partial = {}) {\n super({ ...options, name: 'fully-mqtt' });\n\n this.on('ready', this.iob_onReady.bind(this));\n this.on('stateChange', this.iob_onStateChange.bind(this));\n // this.on('objectChange', this.onObjectChange.bind(this));\n // this.on('message', this.onMessage.bind(this));\n this.on('unload', this.iob_onUnload.bind(this));\n }\n\n /**\n * Is called when databases are connected and adapter received configuration.\n */\n private async iob_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 * 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 /**\n * Start MQTT Server\n */\n if (this.mqtt_useMqtt) {\n this.mqtt_Server = new MqttServer(this);\n this.mqtt_Server.start();\n }\n\n /**\n * Call main() for each device\n */\n for (const ip in this.fullys) {\n await this.main(this.fullys[ip]);\n }\n\n /**\n * Remove device objects if device was renamed\n */\n // Get string array of all adapter objects: ['fully-mqtt.0.info', 'fully-mqtt.0.info.connection', ...];\n const paths = Object.keys(await this.getAdapterObjectsAsync());\n\n // Ignore fully-mqtt.0.info tree (which includes fully-mqtt.0.info.connection, ...)\n const idBlacklist = ['info'];\n\n // Get fully device ids of 'fully-mqtt.0.Kitchen' etc., like ['Kitchen', 'Tablet-Bathroom', ...]\n const allDeviceIds: Array = [];\n for (const path of paths) {\n const pathSplit = path.split('.');\n if (idBlacklist.includes(pathSplit[2])) {\n //this.log.debug(`Ignore ${path} since it should not be removed!`);\n } else {\n const id = pathSplit[2]; // e.g. 'Kitchen'\n if (!allDeviceIds.includes(id)) allDeviceIds.push(id);\n }\n }\n // process all device ids\n for (const id of allDeviceIds) {\n // We consider both enabled and disabled devices and only remove states if device row was deleted in config\n const enabledAndDisabled = this.disabledDeviceIds;\n for (const ip in this.fullys) {\n enabledAndDisabled.push(this.fullys[ip].id);\n }\n\n if (!enabledAndDisabled.includes(id)) {\n await this.delObjectAsync(id, { recursive: true });\n this.log.info(`Cleanup: Deleted no longer defined objects of '${id}'.`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * main function for each Fully Browser Device\n * @param device Fully Browser Device Object\n */\n private async main(device: IDevice): Promise {\n try {\n this.log.debug(`Start main() - ${device.name} (${device.ip})\u2026`);\n\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 and info update\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 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 + '.mqttActivated', { type: 'state', common: { name: 'Is MQTT activated?', desc: 'If MQTT is activated for at least one Fully Browser in adapter options', 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 (REST API)' }, 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 // REST API Create and update Info Objects\n if (!device.useMQTT) {\n const infoObj = await this.restApi_inst.getInfo(device.ip);\n if (!infoObj) return;\n await this.createInfoObjects('restApi', infoObj, device.ip);\n // REST API set info states now\n await this.setInfoStates('REST', infoObj, device.ip);\n }\n\n // Create MQTT Events Objects\n // More states are created once a new Event is received!\n if (device.useMQTT) {\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: 'MQTT Event: ' + event, type: 'boolean', role: 'switch', read: true, write: false }, native: {} });\n }\n }\n\n // Update MQTT Activated state\n this.setState(device.id + '.mqttActivated', { val: device.useMQTT, ack: true });\n\n /**\n * REST API: Subscribe to state changes\n */\n await this.subscribeStatesAsync(device.id + '.Commands.*');\n\n /**\n * REST API: INFO: Update and Schedule Update\n */\n if (!device.useMQTT) {\n // Schedule regular update\n await this.scheduleRestApiRequestInfo(device.ip);\n this.log.info(`[REST] ${device.name}: Regular info update requests scheduled (every ${this.config.restInterval} seconds).`);\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Create Info Objects either for MQTT or for REST API\n * @param source mqtt or restApi\n * @param device device object\n * @returns true if successful, false if not\n */\n private async createInfoObjects(source: 'mqtt' | 'restApi', infoObj: { [k: string]: any }, ip: string): Promise {\n try {\n const device = this.fullys[ip];\n for (const key in infoObj) {\n const val = infoObj[key];\n const valType = typeof val;\n if (valType === 'string' || valType === 'boolean' || valType === 'object' || valType === 'number') {\n if (source === 'mqtt') {\n // MQTT\n this.fullys[ip].mqttInfoKeys.push(key);\n } else {\n // REST API\n this.fullys[ip].restInfoKeys.push(key);\n }\n await this.setObjectNotExistsAsync(`${device.id}.Info.${key}`, { type: 'state', common: { name: 'Info: ' + key, type: valType, role: 'value', read: true, write: false }, native: {} });\n } else {\n this.log.warn(`Unknown type ${valType} of key '${key}' in info object`);\n continue;\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Update Info States - MQTT or REST API\n * @param ip IP Address\n * @returns void\n */\n private async setInfoStates(source: 'MQTT' | 'REST', infoObj: { [k: string]: any }, ip: string): Promise {\n try {\n for (const key in infoObj) {\n let isKeyUnknown = true;\n let updateUnchanged = false;\n if (source === 'MQTT') {\n if (this.fullys[ip].mqttInfoKeys.includes(key)) isKeyUnknown = false;\n if (this.config.mqttUpdateUnchangedObjects) updateUnchanged = true;\n } else if (source === 'REST') {\n if (this.fullys[ip].restInfoKeys.includes(key)) isKeyUnknown = false;\n if (this.config.restUpdateUnchangedObjects) updateUnchanged = true;\n }\n if (isKeyUnknown) {\n this.log.debug(`${this.fullys[ip].name}: Yet unknown key '${key}' in info object of ${source}, so create state`);\n this.createInfoObjects('mqtt', { [key]: infoObj[key] }, ip);\n }\n const newVal = typeof infoObj[key] === 'object' ? JSON.stringify(infoObj[key]) : infoObj[key]; // https://forum.iobroker.net/post/628870 - https://forum.iobroker.net/post/960260\n if (updateUnchanged) {\n this.setState(`${this.fullys[ip].id}.Info.${key}`, { val: newVal, ack: true });\n } else {\n this.setStateChanged(`${this.fullys[ip].id}.Info.${key}`, { val: newVal, ack: true });\n }\n }\n this.setState(this.fullys[ip].id + '.lastInfoUpdate', { val: Date.now(), ack: true });\n this.setState(this.fullys[ip].id + '.alive', { val: true, ack: true });\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Schedule: REST API get info through timeout\n * @param ip IP Address\n * @returns void\n */\n private async scheduleRestApiRequestInfo(ip: string): Promise {\n try {\n clearTimeout(this.fullys[ip].timeoutRestRequestInfo);\n const interval = this.config.restInterval * 1000;\n if (interval < 2000) throw `[REST] We do not allow to set a REST API interval for info update every < 2 seconds!`;\n this.fullys[ip].timeoutRestRequestInfo = setTimeout(async () => {\n try {\n // Update Info\n const infoObj = await this.restApi_inst.getInfo(ip);\n if (infoObj !== false) {\n // Successful (no error)\n // Set states\n await this.setInfoStates('REST', infoObj, ip);\n // Call this function again since we are in callback of timeout\n } else {\n // error, was handled before in calling function\n }\n this.scheduleRestApiRequestInfo(ip);\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }, interval);\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 * 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 if (this.isEmpty(this.config.restInterval) || this.config.restInterval < 5) {\n this.log.warn(`Adapter instance settings: REST API timeout of ${this.config.restInterval}s is not allowed, set to default of 60s`);\n this.config.restInterval = 60;\n }\n\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 * 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 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 mqttClientId: undefined,\n useMQTT: false,\n restProtocol: 'http',\n restPort: 0,\n restPassword: '',\n lastSeen: 0, // timestamp\n isAlive: false,\n timeoutRestRequestInfo: undefined,\n mqttInfoObjectsCreated: false,\n mqttInfoKeys: [],\n restInfoKeys: [],\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 // Use MQTT\n if (lpDevice.useMQTT) {\n finalDevice.useMQTT = true;\n } else {\n finalDevice.useMQTT = false;\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 } else {\n finalDevice.ip = lpDevice.ip;\n // global array for all active IPs\n if (lpDevice.isActive) {\n this.activeDeviceIPs.push(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 this.log.debug(`Final Config: ${JSON.stringify(finalDevice)}`);\n if (lpDevice.isActive) {\n // Is Active\n\n // if MQTT is activated, set variable to true\n if (lpDevice.useMQTT) {\n this.mqtt_useMqtt = true;\n this.log.info(`${finalDevice.name} (${finalDevice.ip}) MQTT is activated in adapter instance settings.`);\n } else {\n this.log.info(`${finalDevice.name} (${finalDevice.ip}) MQTT is not activated in adapter instance settings.`);\n }\n\n // Finalize\n this.fullys[finalDevice.ip] = finalDevice;\n this.log.info(`\uD83D\uDDF8 ${finalDevice.name} (${finalDevice.ip}): Config successfully verified.`);\n } else {\n // Skip if not active. (but we did verification anyway!)\n this.disabledDeviceIds.push(finalDevice.id);\n this.log.debug(`Device ${finalDevice.name} (${finalDevice.ip}) is not enabled, so skip it.`);\n continue;\n }\n }\n\n if (this.activeDeviceIPs.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 * for both REST API and MQTT\n */\n public async onAliveChange(source: 'MQTT' | 'REST', ip: string, isAlive: true | false): 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.onAliveChange_EverBeenCalledBefore; // Keep old value\n this.onAliveChange_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(`[${source}] ${this.fullys[ip].name} is alive.`);\n } else {\n this.log.warn(`[${source}] ${this.fullys[ip].name} is not alive!`);\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]\uD83D\uDCE1 ${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 // keep client id\n if (!this.fullys[obj.ip].mqttClientId) this.fullys[obj.ip].mqttClientId = obj.clientId;\n\n // Create info objects\n if (!this.fullys[obj.ip].mqttInfoObjectsCreated) {\n this.log.debug(`[MQTT] ${this.fullys[obj.ip].name}: Creating info objects (if not yet existing)`);\n await this.createInfoObjects('mqtt', obj.infoObj, obj.ip);\n this.fullys[obj.ip].mqttInfoObjectsCreated = true;\n }\n\n // Fill info objects\n await this.setInfoStates('MQTT', obj.infoObj, obj.ip);\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 // keep client id\n if (!this.fullys[obj.ip].mqttClientId) this.fullys[obj.ip].mqttClientId = obj.clientId;\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. Initialized by Class constructor.\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 iob_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(`${fully.name}: ${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 iob_onUnload(callback: () => void): void {\n try {\n // Here you must clear all timeouts or intervals that may still be active\n // clearTimeout(timeout1);\n // clearTimeout(timeout2);\n // ...\n // clearInterval(interval1);\n\n if (this.fullys) {\n for (const ip in this.fullys) {\n // Clear timeouts\n clearTimeout(this.fullys[ip].timeoutRestRequestInfo);\n this.log.info(`${this.fullys[ip].name}: Clear timeouts.`);\n // Set alive status to false\n this.setState(this.fullys[ip].id + '.alive', { val: false, ack: true });\n }\n }\n\n // Clear timeouts\n if (this.mqtt_Server) {\n for (const clientId in this.mqtt_Server.devices) {\n 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 // MQTT\n private mqtt_Server: MqttServer | undefined;\n public mqtt_useMqtt: true | false = false; // Is use of MQTT activated per adapter settings (each line of fully devices is checked)\n\n // REST API\n private restApi_inst = new RestApiFully(this); // RestApi Class Instance\n\n /**\n * Active 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 * Use this.getFullyPerKey() to get fully object per provided key\n */\n public fullys: { [ip: string]: IDevice } = {};\n\n // array of device ids, which are not activated\n public disabledDeviceIds = [] as string[];\n // All active IP addresses\n public activeDeviceIPs = [] as string[];\n\n // Has onAliveChange() ever been called before?\n private onAliveChange_EverBeenCalledBefore = false;\n\n /**\n * Constructor\n */\n public constructor(options: Partial = {}) {\n super({ ...options, name: 'fully-mqtt' });\n\n this.on('ready', this.iob_onReady.bind(this));\n this.on('stateChange', this.iob_onStateChange.bind(this));\n // this.on('objectChange', this.onObjectChange.bind(this));\n // this.on('message', this.onMessage.bind(this));\n this.on('unload', this.iob_onUnload.bind(this));\n }\n\n /**\n * Is called when databases are connected and adapter received configuration.\n */\n private async iob_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 * 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 /**\n * Start MQTT Server\n */\n if (this.mqtt_useMqtt) {\n this.mqtt_Server = new MqttServer(this);\n this.mqtt_Server.start();\n }\n\n /**\n * Call main() for each device\n */\n for (const ip in this.fullys) {\n await this.main(this.fullys[ip]);\n }\n\n /**\n * Remove device objects if device was renamed\n */\n // Get string array of all adapter objects: ['fully-mqtt.0.info', 'fully-mqtt.0.info.connection', ...];\n const paths = Object.keys(await this.getAdapterObjectsAsync());\n\n // Ignore fully-mqtt.0.info tree (which includes fully-mqtt.0.info.connection, ...)\n const idBlacklist = ['info'];\n\n // Get fully device ids of 'fully-mqtt.0.Kitchen' etc., like ['Kitchen', 'Tablet-Bathroom', ...]\n const allDeviceIds: Array = [];\n for (const path of paths) {\n const pathSplit = path.split('.');\n if (idBlacklist.includes(pathSplit[2])) {\n //this.log.debug(`Ignore ${path} since it should not be removed!`);\n } else {\n const id = pathSplit[2]; // e.g. 'Kitchen'\n if (!allDeviceIds.includes(id)) allDeviceIds.push(id);\n }\n }\n // process all device ids\n for (const id of allDeviceIds) {\n // We consider both enabled and disabled devices and only remove states if device row was deleted in config\n const enabledAndDisabled = this.disabledDeviceIds;\n for (const ip in this.fullys) {\n enabledAndDisabled.push(this.fullys[ip].id);\n }\n\n if (!enabledAndDisabled.includes(id)) {\n await this.delObjectAsync(id, { recursive: true });\n this.log.info(`Cleanup: Deleted no longer defined objects of '${id}'.`);\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * main function for each Fully Browser Device\n * @param device Fully Browser Device Object\n */\n private async main(device: IDevice): Promise {\n try {\n this.log.debug(`Start main() - ${device.name} (${device.ip})\u2026`);\n\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 and info update\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 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 + '.mqttActivated', { type: 'state', common: { name: 'Is MQTT activated?', desc: 'If MQTT is activated for at least one Fully Browser in adapter options', 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 (REST API)' }, 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 // REST API Create and update Info Objects\n if (!device.useMQTT) {\n const infoObj = await this.restApi_inst.getInfo(device.ip);\n if (!infoObj) return;\n await this.createInfoObjects('restApi', infoObj, device.ip);\n // REST API set info states now\n await this.setInfoStates('REST', infoObj, device.ip);\n }\n\n // Create MQTT Events Objects\n // More states are created once a new Event is received!\n if (device.useMQTT) {\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: 'MQTT Event: ' + event, type: 'boolean', role: 'switch', read: true, write: false }, native: {} });\n }\n }\n\n // Update MQTT Activated state\n this.setState(device.id + '.mqttActivated', { val: device.useMQTT, ack: true });\n\n /**\n * REST API: Subscribe to state changes\n */\n await this.subscribeStatesAsync(device.id + '.Commands.*');\n\n /**\n * REST API: INFO: Update and Schedule Update\n */\n if (!device.useMQTT) {\n // Schedule regular update\n await this.scheduleRestApiRequestInfo(device.ip);\n this.log.info(`[REST] ${device.name}: Regular info update requests scheduled (every ${this.config.restInterval} seconds).`);\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Create Info Objects either for MQTT or for REST API\n * @param source mqtt or restApi\n * @param device device object\n * @returns true if successful, false if not\n */\n private async createInfoObjects(source: 'mqtt' | 'restApi', infoObj: { [k: string]: any }, ip: string): Promise {\n try {\n const device = this.fullys[ip];\n for (const key in infoObj) {\n const val = infoObj[key];\n const valType = typeof val;\n if (valType === 'string' || valType === 'boolean' || valType === 'object' || valType === 'number') {\n if (source === 'mqtt') {\n // MQTT\n this.fullys[ip].mqttInfoKeys.push(key);\n } else {\n // REST API\n this.fullys[ip].restInfoKeys.push(key);\n }\n await this.setObjectNotExistsAsync(`${device.id}.Info.${key}`, { type: 'state', common: { name: 'Info: ' + key, type: valType, role: 'value', read: true, write: false }, native: {} });\n } else {\n this.log.warn(`Unknown type ${valType} of key '${key}' in info object`);\n continue;\n }\n }\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Update Info States - MQTT or REST API\n * @param ip IP Address\n * @returns void\n */\n private async setInfoStates(source: 'MQTT' | 'REST', infoObj: { [k: string]: any }, ip: string): Promise {\n try {\n for (const key in infoObj) {\n let isKeyUnknown = true;\n let updateUnchanged = false;\n if (source === 'MQTT') {\n if (this.fullys[ip].mqttInfoKeys.includes(key)) isKeyUnknown = false;\n if (this.config.mqttUpdateUnchangedObjects) updateUnchanged = true;\n } else if (source === 'REST') {\n if (this.fullys[ip].restInfoKeys.includes(key)) isKeyUnknown = false;\n if (this.config.restUpdateUnchangedObjects) updateUnchanged = true;\n }\n if (isKeyUnknown) {\n this.log.debug(`${this.fullys[ip].name}: Yet unknown key '${key}' in info object of ${source}, so create state`);\n this.createInfoObjects('mqtt', { [key]: infoObj[key] }, ip);\n }\n const newVal = typeof infoObj[key] === 'object' ? JSON.stringify(infoObj[key]) : infoObj[key]; // https://forum.iobroker.net/post/628870 - https://forum.iobroker.net/post/960260\n if (updateUnchanged) {\n this.setState(`${this.fullys[ip].id}.Info.${key}`, { val: newVal, ack: true });\n } else {\n this.setStateChanged(`${this.fullys[ip].id}.Info.${key}`, { val: newVal, ack: true });\n }\n }\n this.setState(this.fullys[ip].id + '.lastInfoUpdate', { val: Date.now(), ack: true });\n this.setState(this.fullys[ip].id + '.alive', { val: true, ack: true });\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }\n\n /**\n * Schedule: REST API get info through timeout\n * @param ip IP Address\n * @returns void\n */\n private async scheduleRestApiRequestInfo(ip: string): Promise {\n try {\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.fullys[ip].timeoutRestRequestInfo) this.clearTimeout(this.fullys[ip].timeoutRestRequestInfo);\n const interval = this.config.restInterval * 1000;\n if (interval < 2000) throw `[REST] We do not allow to set a REST API interval for info update every < 2 seconds!`;\n this.fullys[ip].timeoutRestRequestInfo = this.setTimeout(async () => {\n try {\n // Update Info\n const infoObj = await this.restApi_inst.getInfo(ip);\n if (infoObj !== false) {\n // Successful (no error)\n // Set states\n await this.setInfoStates('REST', infoObj, ip);\n // Call this function again since we are in callback of timeout\n } else {\n // error, was handled before in calling function\n }\n this.scheduleRestApiRequestInfo(ip);\n } catch (e) {\n this.log.error(this.err2Str(e));\n return;\n }\n }, interval);\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 * 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 if (this.isEmpty(this.config.restInterval) || this.config.restInterval < 5 || this.config.restInterval > 86400000) {\n this.log.warn(`Adapter instance settings: REST API interval of ${this.config.restInterval}s is not allowed, set to default of 60s`);\n this.config.restInterval = 60;\n }\n\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 * 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 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 mqttClientId: undefined,\n useMQTT: false,\n restProtocol: 'http',\n restPort: 0,\n restPassword: '',\n lastSeen: 0, // timestamp\n isAlive: false,\n timeoutRestRequestInfo: null,\n mqttInfoObjectsCreated: false,\n mqttInfoKeys: [],\n restInfoKeys: [],\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 // Use MQTT\n if (lpDevice.useMQTT) {\n finalDevice.useMQTT = true;\n } else {\n finalDevice.useMQTT = false;\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 } else {\n finalDevice.ip = lpDevice.ip;\n // global array for all active IPs\n if (lpDevice.isActive) {\n this.activeDeviceIPs.push(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 this.log.debug(`Final Config: ${JSON.stringify(finalDevice)}`);\n if (lpDevice.isActive) {\n // Is Active\n\n // if MQTT is activated, set variable to true\n if (lpDevice.useMQTT) {\n this.mqtt_useMqtt = true;\n this.log.info(`${finalDevice.name} (${finalDevice.ip}) MQTT is activated in adapter instance settings.`);\n } else {\n this.log.info(`${finalDevice.name} (${finalDevice.ip}) MQTT is not activated in adapter instance settings.`);\n }\n\n // Finalize\n this.fullys[finalDevice.ip] = finalDevice;\n this.log.info(`\uD83D\uDDF8 ${finalDevice.name} (${finalDevice.ip}): Config successfully verified.`);\n } else {\n // Skip if not active. (but we did verification anyway!)\n this.disabledDeviceIds.push(finalDevice.id);\n this.log.debug(`Device ${finalDevice.name} (${finalDevice.ip}) is not enabled, so skip it.`);\n continue;\n }\n }\n\n if (this.activeDeviceIPs.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 * for both REST API and MQTT\n */\n public async onAliveChange(source: 'MQTT' | 'REST', ip: string, isAlive: true | false): 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.onAliveChange_EverBeenCalledBefore; // Keep old value\n this.onAliveChange_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(`[${source}] ${this.fullys[ip].name} is alive.`);\n } else {\n this.log.warn(`[${source}] ${this.fullys[ip].name} is not alive!`);\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]\uD83D\uDCE1 ${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 // keep client id\n if (!this.fullys[obj.ip].mqttClientId) this.fullys[obj.ip].mqttClientId = obj.clientId;\n\n // Create info objects\n if (!this.fullys[obj.ip].mqttInfoObjectsCreated) {\n this.log.debug(`[MQTT] ${this.fullys[obj.ip].name}: Creating info objects (if not yet existing)`);\n await this.createInfoObjects('mqtt', obj.infoObj, obj.ip);\n this.fullys[obj.ip].mqttInfoObjectsCreated = true;\n }\n\n // Fill info objects\n await this.setInfoStates('MQTT', obj.infoObj, obj.ip);\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 // keep client id\n if (!this.fullys[obj.ip].mqttClientId) this.fullys[obj.ip].mqttClientId = obj.clientId;\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. Initialized by Class constructor.\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 iob_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(`${fully.name}: ${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 iob_onUnload(callback: () => void): void {\n try {\n if (this.fullys) {\n for (const ip in this.fullys) {\n // Clear Request Info timeout\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.fullys[ip].timeoutRestRequestInfo) this.clearTimeout(this.fullys[ip].timeoutRestRequestInfo);\n this.log.info(`${this.fullys[ip].name}: Clear timeouts.`);\n // Set alive status to false\n this.setState(this.fullys[ip].id + '.alive', { val: false, ack: true });\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/doc/en/README.md b/docs/en/README.md similarity index 100% rename from doc/en/README.md rename to docs/en/README.md diff --git a/io-package.json b/io-package.json index 66247dc..45dc50b 100644 --- a/io-package.json +++ b/io-package.json @@ -121,14 +121,19 @@ "adminUI": { "config": "json" }, - "dependencies": [ + "docs": { + "en": [ + "docs/en/README.md" + ] + }, + "globalDependencies": [ { - "js-controller": ">=4.0.15" + "admin": ">=6.0.0" } ], - "globalDependencies": [ + "dependencies": [ { - "admin": ">=6.0.0" + "js-controller": ">=4.0.15" } ] }, @@ -176,4 +181,4 @@ "native": {} } ] -} \ No newline at end of file +} diff --git a/package.json b/package.json index 2897adb..317bd0b 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,10 @@ "net": "^1.0.2" }, "devDependencies": { - "@alcalzone/release-script": "^2.2.2", + "@alcalzone/release-script": "^3.5.9", + "@alcalzone/release-script-plugin-iobroker": "^3.5.9", + "@alcalzone/release-script-plugin-license": "^3.5.9", + "@alcalzone/release-script-plugin-manual-review": "^3.5.9", "@iobroker/adapter-dev": "^1.2.0", "@iobroker/testing": "^4.1.0", "@types/chai": "^4.3.4", diff --git a/src/lib/interfaces.ts b/src/lib/interfaces.ts index 4c6fa39..1f136a6 100644 --- a/src/lib/interfaces.ts +++ b/src/lib/interfaces.ts @@ -9,7 +9,7 @@ export interface IDevice { restPassword: string; lastSeen: number; // timestamp isAlive: true | false; - timeoutRestRequestInfo: NodeJS.Timeout | undefined; + timeoutRestRequestInfo: ioBroker.Timeout | null; mqttInfoObjectsCreated: true | false; // Set to true once first time creation initiated mqttInfoKeys: string[]; // Info keys from MQTT info, like 'batteryLevel', 'deviceID', ... restInfoKeys: string[]; // Info keys from Rest API info, like 'batteryLevel', 'deviceID', ... @@ -30,7 +30,7 @@ export interface IMqttDevice { lastSeen?: number; mqttFirstReceived?: true | false; isActive?: true | false; - timeoutNoUpdate?: NodeJS.Timeout | undefined; + timeoutNoUpdate?: ioBroker.Timeout | null; previousInfoPublishTime?: number; } diff --git a/src/lib/mqtt-server.ts b/src/lib/mqtt-server.ts index 65aae87..f8db2c7 100644 --- a/src/lib/mqtt-server.ts +++ b/src/lib/mqtt-server.ts @@ -355,7 +355,8 @@ export class MqttServer { this.scheduleCheckIfStillActive(clientId); // restart timer } else { // clear timer - clearTimeout(this.devices[clientId].timeoutNoUpdate); + // @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if" + if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate); } } else { this.adapter.log.debug(`[MQTT] isAlive changed to ${isAlive}, but IP of client ${clientId} is still unknown.`); @@ -369,7 +370,8 @@ export class MqttServer { */ private async scheduleCheckIfStillActive(clientId: string): Promise { try { - clearTimeout(this.devices[clientId].timeoutNoUpdate); + // @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if" + if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate); if (!this.devices[clientId]) this.devices[clientId] = {}; @@ -377,7 +379,7 @@ export class MqttServer { // const ip = this.devices[clientId].ip; // const ipMsg = ip ? `${this.adapter.fullys[ip].name} (${ip})` : `${clientId} (IP unknown)`; const interval = 70 * 1000; // every 60s + 10s buffer - this.devices[clientId].timeoutNoUpdate = setTimeout(async () => { + this.devices[clientId].timeoutNoUpdate = this.adapter.setTimeout(async () => { try { const lastSeen = this.devices[clientId].lastSeen; if (!lastSeen) return; @@ -409,7 +411,8 @@ export class MqttServer { this.adapter.log.info(`[MQTT] Disconnect all clients and close server`); // isAlive for (const clientId in this.devices) { - clearTimeout(this.devices[clientId].timeoutNoUpdate); // Clear timeout + // @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if" + if (this.devices[clientId].timeoutNoUpdate) this.adapter.clearTimeout(this.devices[clientId].timeoutNoUpdate); this.setIsAlive(clientId, false); } diff --git a/src/main.ts b/src/main.ts index 3a70a77..7d92439 100644 --- a/src/main.ts +++ b/src/main.ts @@ -308,10 +308,11 @@ export class FullyMqtt extends utils.Adapter { */ private async scheduleRestApiRequestInfo(ip: string): Promise { try { - clearTimeout(this.fullys[ip].timeoutRestRequestInfo); + // @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if" + if (this.fullys[ip].timeoutRestRequestInfo) this.clearTimeout(this.fullys[ip].timeoutRestRequestInfo); const interval = this.config.restInterval * 1000; if (interval < 2000) throw `[REST] We do not allow to set a REST API interval for info update every < 2 seconds!`; - this.fullys[ip].timeoutRestRequestInfo = setTimeout(async () => { + this.fullys[ip].timeoutRestRequestInfo = this.setTimeout(async () => { try { // Update Info const infoObj = await this.restApi_inst.getInfo(ip); @@ -347,8 +348,8 @@ export class FullyMqtt extends utils.Adapter { this.log.warn(`Adapter instance settings: REST API timeout of ${this.config.restTimeout} ms is not allowed, set to default of 6000ms`); this.config.restTimeout = 6000; } - if (this.isEmpty(this.config.restInterval) || this.config.restInterval < 5) { - this.log.warn(`Adapter instance settings: REST API timeout of ${this.config.restInterval}s is not allowed, set to default of 60s`); + if (this.isEmpty(this.config.restInterval) || this.config.restInterval < 5 || this.config.restInterval > 86400000) { + this.log.warn(`Adapter instance settings: REST API interval of ${this.config.restInterval}s is not allowed, set to default of 60s`); this.config.restInterval = 60; } @@ -385,7 +386,7 @@ export class FullyMqtt extends utils.Adapter { restPassword: '', lastSeen: 0, // timestamp isAlive: false, - timeoutRestRequestInfo: undefined, + timeoutRestRequestInfo: null, mqttInfoObjectsCreated: false, mqttInfoKeys: [], restInfoKeys: [], @@ -755,26 +756,22 @@ export class FullyMqtt extends utils.Adapter { */ private iob_onUnload(callback: () => void): void { try { - // Here you must clear all timeouts or intervals that may still be active - // clearTimeout(timeout1); - // clearTimeout(timeout2); - // ... - // clearInterval(interval1); - if (this.fullys) { for (const ip in this.fullys) { - // Clear timeouts - clearTimeout(this.fullys[ip].timeoutRestRequestInfo); + // Clear Request Info timeout + // @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if" + if (this.fullys[ip].timeoutRestRequestInfo) this.clearTimeout(this.fullys[ip].timeoutRestRequestInfo); this.log.info(`${this.fullys[ip].name}: Clear timeouts.`); // Set alive status to false this.setState(this.fullys[ip].id + '.alive', { val: false, ack: true }); } } - // Clear timeouts + // Clear MQTT server timeouts if (this.mqtt_Server) { for (const clientId in this.mqtt_Server.devices) { - clearTimeout(this.mqtt_Server.devices[clientId].timeoutNoUpdate); + // @ts-expect-error "Type 'null' is not assignable to type 'Timeout'.ts(2345)" - we check for not being null via "if" + if (this.mqtt_Server.devices[clientId].timeoutNoUpdate) this.clearTimeout(this.mqtt_Server.devices[clientId].timeoutNoUpdate); } }