Bump version

This commit is contained in:
GermanBluefox
2026-05-16 20:07:53 +02:00
parent 962189ba74
commit 1883937e4a
22 changed files with 396 additions and 229 deletions
+5 -6
View File
@@ -9,7 +9,7 @@
[![Translation status](https://weblate.iobroker.net/widgets/adapters/-/hass/svg-badge.svg)](https://weblate.iobroker.net/engage/adapters/?utm_source=widget) [![Translation status](https://weblate.iobroker.net/widgets/adapters/-/hass/svg-badge.svg)](https://weblate.iobroker.net/engage/adapters/?utm_source=widget)
[![Downloads](https://img.shields.io/npm/dm/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass) [![Downloads](https://img.shields.io/npm/dm/iobroker.hass.svg)](https://www.npmjs.com/package/iobroker.hass)
**This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and for information how to disable the error reporting see [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Sentry reporting is used starting with js-controller 3.0. **This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and for information on how to disable the error reporting, see [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Sentry reporting is used starting with js-controller 3.0.
This adapter allows the connecting of Home Assistant to ioBroker. This adapter allows the connecting of Home Assistant to ioBroker.
@@ -20,7 +20,7 @@ Create a long-term token in HASS and use it as PW (copy it also in the repeat fi
Then it should read out all attributes for all devices. Services might be controllable (e.g. "turn_on"). To control services, you have two options: Then it should read out all attributes for all devices. Services might be controllable (e.g. "turn_on"). To control services, you have two options:
### Set a direct value ### Set a direct value
Set the state with an ack=false value which is not a string (e.g. Boolean true) then it will be triggered also in HASS without additional service data. This will only work if the service has one field to be sent - then the value is sent as this field! If the service has more than one field, you will find a warning in the log that provides more details about the fields that are possible to be sent, e.g. Set the state with an ack=false value which is not a string (e.g. Boolean true), then it will be triggered also in HASS without additional service data. This will only work if the service has one field to be sent - then the value is sent as this field! If the service has more than one field, you will find a warning in the log that provides more details about the fields that are possible to be sent, e.g.
``` ```
Please make sure to provide a stringified JSON as value to set relevant fields! Please refer to the Readme for details! Please make sure to provide a stringified JSON as value to set relevant fields! Please refer to the Readme for details!
@@ -94,7 +94,7 @@ For some services like set_speed it is required to call with a JSON object like
``` ```
## Configuration ## Configuration
There is a good article about connection. There is a good article about the connection.
Please check it https://www.smarthomejetzt.de/mit-iobroker-auf-eine-home-assistant-hass-io-installation-und-die-geraete-zugreifen/ Please check it https://www.smarthomejetzt.de/mit-iobroker-auf-eine-home-assistant-hass-io-installation-und-die-geraete-zugreifen/
@@ -102,7 +102,7 @@ Please check it https://www.smarthomejetzt.de/mit-iobroker-auf-eine-home-assista
## Entity exclude filter ## Entity exclude filter
Optionally restrict which Home Assistant entities are synchronised into ioBroker. Optionally, restrict which Home Assistant entities are synchronised into ioBroker.
Each non-empty, non-comment line in the **Exclude patterns** field is a glob Each non-empty, non-comment line in the **Exclude patterns** field is a glob
(only `*` is a wildcard and matches any sequence of characters, including `.`). (only `*` is a wildcard and matches any sequence of characters, including `.`).
@@ -138,10 +138,9 @@ An empty pattern list leaves the adapter behaviour identical to previous version
## Changelog ## Changelog
### **WORK IN PROGRESS** ### **WORK IN PROGRESS**
* (mokusone) Added optional entity exclude filter with glob patterns, configurable via the admin UI, plus a verbose-logging toggle for inspecting matches * (mokusone) Added optional entity exclude filter with glob patterns, configurable via the admin UI, plus a verbose-logging toggle for inspecting matches
* (@klein0r) Use /core/ instead of /api/ when connecting to supervisor directly (e.g. in ha app) * (@klein0r) Use `/core/` instead of `/api/` when connecting to supervisor directly (e.g., in ha app)
* (@klein0r) Use ENV var SUPERVISOR_TOKEN as fallback for password * (@klein0r) Use ENV var SUPERVISOR_TOKEN as fallback for password
### 2.0.4 (2026-05-05) ### 2.0.4 (2026-05-05)
* (@GermanBluefox) Tried to keep the custom settings of the objects when updating them with new data from HASS * (@GermanBluefox) Tried to keep the custom settings of the objects when updating them with new data from HASS
+7 -7
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "IP-Adresse von Home Assistant", "Cleanup excluded entities on adapter start": "Ausgefilterte Entitäten beim Adapter-Start löschen",
"Home assistant WS Port": "Home Assistant WebSocket-Port",
"Password repeat": "Passwort wiederholen",
"Password": "Passwort",
"Passwords missmatch!": "Passwörter stimmen nicht überein!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Filter-Patterns (eines pro Zeile)", "Exclude patterns (one per line)": "Filter-Patterns (eines pro Zeile)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-Patterns mit * als Wildcard. Zeilen mit # sind Kommentare. Leer = Filter aus.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-Patterns mit * als Wildcard. Zeilen mit # sind Kommentare. Leer = Filter aus.",
"Home assistant IP": "IP-Adresse von Home Assistant",
"Home assistant WS Port": "Home Assistant WebSocket-Port",
"Password": "Passwort",
"Password repeat": "Passwort wiederholen",
"Passwords missmatch!": "Passwörter stimmen nicht überein!",
"Secure": "HTTPS?",
"Verbose filter logging": "Ausführliches Filter-Logging", "Verbose filter logging": "Ausführliches Filter-Logging",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Wenn aktiv: jede ausgeschlossene entity_id wird beim initialen Adapter-Sync einzeln geloggt (benötigt Loglevel info oder debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Wenn aktiv: jede ausgeschlossene entity_id wird beim initialen Adapter-Sync einzeln geloggt (benötigt Loglevel info oder debug).",
"Cleanup excluded entities on adapter start": "Ausgefilterte Entitäten beim Adapter-Start löschen",
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Wenn aktiv: bei jedem Adapter-Start werden vorhandene Objekte unter entities.*, deren entity_id auf die Filter-Patterns matched, gelöscht. Objekte mit Custom-Adapter-Konfiguration (history/influxdb/sql) bleiben erhalten und werden geloggt." "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Wenn aktiv: bei jedem Adapter-Start werden vorhandene Objekte unter entities.*, deren entity_id auf die Filter-Patterns matched, gelöscht. Objekte mit Custom-Adapter-Konfiguration (history/influxdb/sql) bleiben erhalten und werden geloggt."
} }
+7 -7
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "Home assistant IP", "Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start",
"Home assistant WS Port": "Home assistant WS Port",
"Password repeat": "Password repeat",
"Password": "Password",
"Passwords missmatch!": "Passwords missmatch!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Exclude patterns (one per line)", "Exclude patterns (one per line)": "Exclude patterns (one per line)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.",
"Home assistant IP": "Home assistant IP",
"Home assistant WS Port": "Home assistant WS Port",
"Password": "Password",
"Password repeat": "Password repeat",
"Passwords missmatch!": "Passwords missmatch!",
"Secure": "HTTPS?",
"Verbose filter logging": "Verbose filter logging", "Verbose filter logging": "Verbose filter logging",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start",
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged." "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "IP del asistente de hogar", "Cleanup excluded entities on adapter start": "Limpiar las entidades excluidas al iniciar el adaptador.",
"Home assistant WS Port": "Asistente doméstico Puerto WS",
"Password repeat": "Repite la contraseña",
"Password": "Clave",
"Passwords missmatch!": "¡Las contraseñas no coinciden!",
"Secure": "¿HTTPS?",
"Exclude patterns (one per line)": "Patrones de exclusión (uno por línea)", "Exclude patterns (one per line)": "Patrones de exclusión (uno por línea)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Patrones glob, * como comodín. Las líneas que empiezan con # son comentarios. Vacío = filtro desactivado.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Patrones glob, * como comodín. Las líneas que empiezan con # son comentarios. Vacío = filtro desactivado.",
"Home assistant IP": "IP del asistente de hogar",
"Home assistant WS Port": "Asistente doméstico Puerto WS",
"Password": "Clave",
"Password repeat": "Repite la contraseña",
"Passwords missmatch!": "¡Las contraseñas no coinciden!",
"Secure": "¿HTTPS?",
"Verbose filter logging": "Registro detallado del filtro", "Verbose filter logging": "Registro detallado del filtro",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Cuando está activado, cada entity_id excluido se registra individualmente durante la sincronización inicial del adaptador (requiere loglevel info o debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Cuando está activado, cada entity_id excluido se registra individualmente durante la sincronización inicial del adaptador (requiere loglevel info o debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Cuando está habilitada, en cada inicio del adaptador se eliminan los objetos existentes en entities.* cuyo entity_id coincida con los patrones de exclusión. Los objetos con configuración de adaptador personalizada (history/influxdb/sql) se conservan y se registran."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "IP de l'assistant à domicile ", "Cleanup excluded entities on adapter start": "Nettoyage des entités exclues au démarrage de l'adaptateur",
"Home assistant WS Port": "Port WS de l'assistant domestique ",
"Password repeat": "Répéter le mot de passe ",
"Password": "Mot de passe",
"Passwords missmatch!": "Les mots de passe ne correspondent pas !",
"Secure": "HTTPS ?",
"Exclude patterns (one per line)": "Modèles d'exclusion (un par ligne)", "Exclude patterns (one per line)": "Modèles d'exclusion (un par ligne)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Modèles glob, * comme joker. Les lignes commençant par # sont des commentaires. Vide = filtre désactivé.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Modèles glob, * comme joker. Les lignes commençant par # sont des commentaires. Vide = filtre désactivé.",
"Home assistant IP": "IP de l'assistant à domicile ",
"Home assistant WS Port": "Port WS de l'assistant domestique ",
"Password": "Mot de passe",
"Password repeat": "Répéter le mot de passe ",
"Passwords missmatch!": "Les mots de passe ne correspondent pas !",
"Secure": "HTTPS ?",
"Verbose filter logging": "Journalisation détaillée du filtre", "Verbose filter logging": "Journalisation détaillée du filtre",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Lorsqu'activé, chaque entity_id exclu est journalisé individuellement pendant la synchronisation initiale de l'adaptateur (nécessite loglevel info ou debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Lorsqu'activé, chaque entity_id exclu est journalisé individuellement pendant la synchronisation initiale de l'adaptateur (nécessite loglevel info ou debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Lorsqu'elle est activée, cette option supprime, à chaque démarrage de l'adaptateur, les objets existants sous entities.* dont l'entity_id correspond aux critères d'exclusion. Les objets avec une configuration d'adaptateur personnalisée (history/influxdb/sql) sont conservés et consignés."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "IP assistente domestico", "Cleanup excluded entities on adapter start": "Pulizia delle entità escluse all'avvio dell'adattatore",
"Home assistant WS Port": "Porta WS dell'assistente domestico",
"Password repeat": "Ripeti password",
"Password": "Parola d'ordine",
"Passwords missmatch!": "Le password non corrispondono!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Pattern di esclusione (uno per riga)", "Exclude patterns (one per line)": "Pattern di esclusione (uno per riga)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Pattern glob, * come wildcard. Le righe che iniziano con # sono commenti. Vuoto = filtro disattivato.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Pattern glob, * come wildcard. Le righe che iniziano con # sono commenti. Vuoto = filtro disattivato.",
"Home assistant IP": "IP assistente domestico",
"Home assistant WS Port": "Porta WS dell'assistente domestico",
"Password": "Parola d'ordine",
"Password repeat": "Ripeti password",
"Passwords missmatch!": "Le password non corrispondono!",
"Secure": "HTTPS?",
"Verbose filter logging": "Logging dettagliato del filtro", "Verbose filter logging": "Logging dettagliato del filtro",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Se attivato, registra ogni entity_id escluso individualmente durante la sincronizzazione iniziale dell'adattatore (richiede loglevel info o debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Se attivato, registra ogni entity_id escluso individualmente durante la sincronizzazione iniziale dell'adattatore (richiede loglevel info o debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Quando abilitata, all'avvio di ogni adattatore vengono eliminati gli oggetti esistenti in entities.* il cui entity_id corrisponde ai modelli di esclusione. Gli oggetti con configurazione personalizzata dell'adattatore (history/influxdb/sql) vengono mantenuti e registrati."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "Thuisassistent IP", "Cleanup excluded entities on adapter start": "Verwijder uitgesloten entiteiten bij het opstarten van de adapter.",
"Home assistant WS Port": "Thuisassistent WS Poort",
"Password repeat": "Wachtwoord herhalen",
"Password": "Wachtwoord",
"Passwords missmatch!": "Wachtwoorden komen niet overeen!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Uitsluitpatronen (één per regel)", "Exclude patterns (one per line)": "Uitsluitpatronen (één per regel)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-patronen, * als wildcard. Regels die beginnen met # zijn commentaar. Leeg = filter uit.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-patronen, * als wildcard. Regels die beginnen met # zijn commentaar. Leeg = filter uit.",
"Home assistant IP": "Thuisassistent IP",
"Home assistant WS Port": "Thuisassistent WS Poort",
"Password": "Wachtwoord",
"Password repeat": "Wachtwoord herhalen",
"Passwords missmatch!": "Wachtwoorden komen niet overeen!",
"Secure": "HTTPS?",
"Verbose filter logging": "Uitgebreide filterlogging", "Verbose filter logging": "Uitgebreide filterlogging",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Indien ingeschakeld, wordt elke uitgesloten entity_id afzonderlijk gelogd tijdens de initiële adapter-synchronisatie (vereist loglevel info of debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Indien ingeschakeld, wordt elke uitgesloten entity_id afzonderlijk gelogd tijdens de initiële adapter-synchronisatie (vereist loglevel info of debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Indien ingeschakeld, worden bij elke adapterstart bestaande objecten onder entities.* verwijderd waarvan de entity_id overeenkomt met de uitsluitingspatronen. Objecten met een aangepaste adapterconfiguratie (history/influxdb/sql) worden behouden en gelogd."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "Adres IP asystenta domowego", "Cleanup excluded entities on adapter start": "Wyczyść wykluczone jednostki podczas uruchamiania adaptera",
"Home assistant WS Port": "Asystent domowy Port WS",
"Password repeat": "Powtórz hasło",
"Password": "Hasło",
"Passwords missmatch!": "Niezgodność haseł!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Wzorce wykluczeń (jeden na linię)", "Exclude patterns (one per line)": "Wzorce wykluczeń (jeden na linię)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Wzorce glob, * jako symbol wieloznaczny. Linie zaczynające się od # to komentarze. Puste = filtr wyłączony.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Wzorce glob, * jako symbol wieloznaczny. Linie zaczynające się od # to komentarze. Puste = filtr wyłączony.",
"Home assistant IP": "Adres IP asystenta domowego",
"Home assistant WS Port": "Asystent domowy Port WS",
"Password": "Hasło",
"Password repeat": "Powtórz hasło",
"Passwords missmatch!": "Niezgodność haseł!",
"Secure": "HTTPS?",
"Verbose filter logging": "Szczegółowe logowanie filtra", "Verbose filter logging": "Szczegółowe logowanie filtra",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Gdy włączone, każde wykluczone entity_id jest logowane osobno podczas początkowej synchronizacji adaptera (wymaga poziomu logowania info lub debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Gdy włączone, każde wykluczone entity_id jest logowane osobno podczas początkowej synchronizacji adaptera (wymaga poziomu logowania info lub debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Po włączeniu, przy każdym uruchomieniu adaptera usuwane są istniejące obiekty w encji.*, których identyfikator encji pasuje do wzorców wykluczeń. Obiekty z niestandardową konfiguracją adaptera (history/influxdb/sql) są zachowywane i rejestrowane."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "IP do assistente doméstico", "Cleanup excluded entities on adapter start": "Limpeza de entidades excluídas na inicialização do adaptador",
"Home assistant WS Port": "Assistente doméstico Porta WS",
"Password repeat": "Repetição de senha",
"Password": "Senha",
"Passwords missmatch!": "As senhas não correspondem!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Padrões de exclusão (um por linha)", "Exclude patterns (one per line)": "Padrões de exclusão (um por linha)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Padrões glob, * como wildcard. Linhas começando com # são comentários. Vazio = filtro desligado.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Padrões glob, * como wildcard. Linhas começando com # são comentários. Vazio = filtro desligado.",
"Home assistant IP": "IP do assistente doméstico",
"Home assistant WS Port": "Assistente doméstico Porta WS",
"Password": "Senha",
"Password repeat": "Repetição de senha",
"Passwords missmatch!": "As senhas não correspondem!",
"Secure": "HTTPS?",
"Verbose filter logging": "Registro detalhado do filtro", "Verbose filter logging": "Registro detalhado do filtro",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Quando ativado, cada entity_id excluído é registrado individualmente durante a sincronização inicial do adaptador (requer loglevel info ou debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Quando ativado, cada entity_id excluído é registrado individualmente durante a sincronização inicial do adaptador (requer loglevel info ou debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Quando ativado, a cada inicialização do adaptador, os objetos existentes em entities.* cujo entity_id corresponde aos padrões de exclusão são excluídos. Os objetos com configuração de adaptador personalizada (history/influxdb/sql) são mantidos e registrados."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "Домашний помощник IP", "Cleanup excluded entities on adapter start": "Удалите исключенные объекты при запуске адаптера.",
"Home assistant WS Port": "Домашний помощник WS Порт",
"Password repeat": "Повтор пароля",
"Password": "Пароль",
"Passwords missmatch!": "Пароли не совпадают!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Шаблоны исключения (по одному на строку)", "Exclude patterns (one per line)": "Шаблоны исключения (по одному на строку)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-шаблоны, * — подстановочный знак. Строки, начинающиеся с #, являются комментариями. Пусто = фильтр выключен.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-шаблоны, * — подстановочный знак. Строки, начинающиеся с #, являются комментариями. Пусто = фильтр выключен.",
"Home assistant IP": "Домашний помощник IP",
"Home assistant WS Port": "Домашний помощник WS Порт",
"Password": "Пароль",
"Password repeat": "Повтор пароля",
"Passwords missmatch!": "Пароли не совпадают!",
"Secure": "HTTPS?",
"Verbose filter logging": "Подробное логирование фильтра", "Verbose filter logging": "Подробное логирование фильтра",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Когда включено, каждый исключённый entity_id логируется отдельно во время начальной синхронизации адаптера (требуется loglevel info или debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Когда включено, каждый исключённый entity_id логируется отдельно во время начальной синхронизации адаптера (требуется loglevel info или debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "При включении этой функции при каждом запуске адаптера удалять существующие объекты в папке entities.*, чей entity_id соответствует шаблонам исключения. Объекты с пользовательской конфигурацией адаптера (history/influxdb/sql) сохраняются и записываются в журнал."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "Домашній помічник IP", "Cleanup excluded entities on adapter start": "Очищення виключених об'єктів під час запуску адаптера",
"Home assistant WS Port": "Домашній помічник WS Порт",
"Password repeat": "Повторення пароля",
"Password": "Пароль",
"Passwords missmatch!": "Паролі не збігаються!",
"Secure": "HTTPS?",
"Exclude patterns (one per line)": "Шаблони виключення (по одному в рядку)", "Exclude patterns (one per line)": "Шаблони виключення (по одному в рядку)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-шаблони, * — підстановочний знак. Рядки, що починаються з #, є коментарями. Порожньо = фільтр вимкнено.", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob-шаблони, * — підстановочний знак. Рядки, що починаються з #, є коментарями. Порожньо = фільтр вимкнено.",
"Home assistant IP": "Домашній помічник IP",
"Home assistant WS Port": "Домашній помічник WS Порт",
"Password": "Пароль",
"Password repeat": "Повторення пароля",
"Passwords missmatch!": "Паролі не збігаються!",
"Secure": "HTTPS?",
"Verbose filter logging": "Детальне логування фільтра", "Verbose filter logging": "Детальне логування фільтра",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Коли увімкнено, кожен виключений entity_id логується окремо під час початкової синхронізації адаптера (потрібен loglevel info або debug).", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "Коли увімкнено, кожен виключений entity_id логується окремо під час початкової синхронізації адаптера (потрібен loglevel info або debug).",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "Якщо ввімкнено, на кожному адаптері починається видалення існуючих об'єктів у розділі entity.*, чий entity_id відповідає шаблонам виключення. Об'єкти з власною конфігурацією адаптера (history/influxdb/sql) зберігаються та реєструються."
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+8 -8
View File
@@ -1,14 +1,14 @@
{ {
"Home assistant IP": "家庭助理IP", "Cleanup excluded entities on adapter start": "适配器启动时清理排除的实体",
"Home assistant WS Port": "家庭助理 WS 端口:",
"Password repeat": "密码重复:",
"Password": "密码:",
"Passwords missmatch!": "密码不匹配!",
"Secure": "HTTPS",
"Exclude patterns (one per line)": "排除模式(每行一个)", "Exclude patterns (one per line)": "排除模式(每行一个)",
"Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob 模式,* 通配符。以 # 开头的行是注释。为空 = 过滤器关闭。", "Glob patterns, * wildcard. Lines starting with # are comments. Empty = filter off.": "Glob 模式,* 通配符。以 # 开头的行是注释。为空 = 过滤器关闭。",
"Home assistant IP": "家庭助理IP",
"Home assistant WS Port": "家庭助理 WS 端口:",
"Password": "密码:",
"Password repeat": "密码重复:",
"Passwords missmatch!": "密码不匹配!",
"Secure": "HTTPS",
"Verbose filter logging": "详细过滤器日志", "Verbose filter logging": "详细过滤器日志",
"When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "启用后,在适配器初始同步期间单独记录每个被排除的 entity_id(需要日志级别 info 或 debug)。", "When enabled, log each excluded entity_id individually during the initial adapter sync (requires loglevel info or debug).": "启用后,在适配器初始同步期间单独记录每个被排除的 entity_id(需要日志级别 info 或 debug)。",
"Cleanup excluded entities on adapter start": "Cleanup excluded entities on adapter start", "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "启用此功能后,每次适配器启动时,都会删除 entities.* 下 entity_id 与排除模式匹配的现有对象。具有自定义适配器配置(history/influxdb/sql)的对象将被保留并记录。"
"When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged.": "When enabled, on each adapter start delete existing objects under entities.* whose entity_id matches the exclude patterns. Objects with custom adapter config (history/influxdb/sql) are kept and logged."
} }
+34
View File
@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isExcluded = isExcluded;
exports.buildExcludeRegexps = buildExcludeRegexps;
/**
* Returns true if entityId matches any of the supplied glob patterns.
* Glob syntax: `*` is the only wildcard and matches any sequence of characters
* (including dots). All other characters are matched literally (regex
* metacharacters are escaped). Matching is case-sensitive and anchored to
* the full entity_id.
*
* An empty patterns array always returns false.
*/
function isExcluded(entityId, patterns) {
if (!patterns?.length) {
return false;
}
for (const pattern of patterns) {
if (pattern.test(entityId)) {
return true;
}
}
return false;
}
function buildExcludeRegexps(patterns) {
if (!patterns?.length) {
return [];
}
return patterns.map(pattern => {
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
return new RegExp(`^${escaped}$`);
});
}
//# sourceMappingURL=entityFilter.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"entityFilter.js","sourceRoot":"","sources":["../../src/lib/entityFilter.ts"],"names":[],"mappings":";;AASA,gCAUC;AAED,kDAQC;AA7BD;;;;;;;;GAQG;AACH,SAAgB,UAAU,CAAC,QAAgB,EAAE,QAAkB;IAC3D,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAgB,mBAAmB,CAAC,QAAkB;IAClD,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,CAAC;IACd,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;QAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnF,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACP,CAAC"}
+130
View File
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const adapter_core_1 = require("@iobroker/adapter-core"); const adapter_core_1 = require("@iobroker/adapter-core");
const hass_1 = __importDefault(require("./lib/hass")); const hass_1 = __importDefault(require("./lib/hass"));
const entityFilter_1 = require("./lib/entityFilter");
const knownAttributes = { const knownAttributes = {
azimuth: { write: false, read: true, unit: '°' }, azimuth: { write: false, read: true, unit: '°' },
elevation: { write: false, read: true, unit: '°' }, elevation: { write: false, read: true, unit: '°' },
@@ -156,6 +157,8 @@ class HassAdapter extends adapter_core_1.Adapter {
delayTimeout = null; delayTimeout = null;
syncDebounceTimeout = null; syncDebounceTimeout = null;
stopped = false; stopped = false;
excludePatterns = [];
initialSyncCompleted = false;
constructor(options = {}) { constructor(options = {}) {
super({ super({
...options, ...options,
@@ -381,11 +384,20 @@ class HassAdapter extends adapter_core_1.Adapter {
const objs = []; const objs = [];
const states = []; const states = [];
const expectedObjects = new Set(); const expectedObjects = new Set();
let excludedCount = 0;
const excludedIds = [];
for (let e = 0; e < entities.length; e++) { for (let e = 0; e < entities.length; e++) {
const entity = entities[e]; const entity = entities[e];
if (!entity) { if (!entity) {
continue; continue;
} }
if ((0, entityFilter_1.isExcluded)(entity.entity_id, this.excludePatterns)) {
excludedCount++;
if (this.config.verboseFilterLog && !this.initialSyncCompleted) {
excludedIds.push(entity.entity_id);
}
continue;
}
const name = entity.name || entity.attributes?.friendly_name || entity.entity_id; const name = entity.name || entity.attributes?.friendly_name || entity.entity_id;
const desc = entity.attributes?.attribution || undefined; const desc = entity.attributes?.attribution || undefined;
const channelId = `${this.namespace}.entities.${entity.entity_id}`; const channelId = `${this.namespace}.entities.${entity.entity_id}`;
@@ -555,10 +567,124 @@ class HassAdapter extends adapter_core_1.Adapter {
} }
this.log.info(`Synchronization completed: ${changes.join(', ')}`); this.log.info(`Synchronization completed: ${changes.join(', ')}`);
} }
if (excludedCount > 0) {
this.log.info(`Entity filter excluded ${excludedCount} entit${excludedCount === 1 ? 'y' : 'ies'} from sync`);
}
if (excludedIds.length > 0) {
for (const id of excludedIds) {
this.log.info(`Entity filter excluded: ${id}`);
}
}
this.initialSyncCompleted = true;
}
async cleanupExcludedObjects() {
if (!this.config.cleanupExcludedOnStart) {
return;
}
if (this.excludePatterns.length === 0) {
this.log.info('Cleanup skipped: no exclude patterns configured');
return;
}
let allObjects;
try {
allObjects = await this.getAdapterObjectsAsync();
}
catch (err) {
this.log.error(`Cleanup: failed to load adapter objects: ${err}`);
return;
}
const prefix = `${this.namespace}.entities.`;
// Group every object under entities.* by its derived entity_id. We extract
// the entity_id from the object id (first two path components after the
// prefix) instead of native.entity_id — older objects from previous adapter
// versions may have been written as flat states without a parent channel
// and without native.entity_id, but their id still encodes the entity.
const matchedByEntity = new Map();
for (const id in allObjects) {
if (!Object.prototype.hasOwnProperty.call(allObjects, id) || !id.startsWith(prefix)) {
continue;
}
const rest = id.substring(prefix.length);
const parts = rest.split('.');
if (parts.length < 2) {
continue;
}
const entityId = `${parts[0]}.${parts[1]}`;
if (!(0, entityFilter_1.isExcluded)(entityId, this.excludePatterns)) {
continue;
}
const ids = matchedByEntity.get(entityId);
if (ids) {
ids.push(id);
}
else {
matchedByEntity.set(entityId, [id]);
}
}
if (matchedByEntity.size === 0) {
this.log.info('Cleanup: no existing objects matched exclude patterns');
return;
}
let deletedEntityCount = 0;
let deletedIdCount = 0;
let keptForCustomCount = 0;
for (const [entityId, ids] of matchedByEntity) {
// Custom-config protection: scan all ids of the group; if any holds
// common.custom (history/influxdb/sql) keep the whole entity.
let hasCustom = false;
for (const id of ids) {
const custom = allObjects[id].common?.custom;
if (custom && Object.keys(custom).length) {
hasCustom = true;
break;
}
}
if (hasCustom) {
keptForCustomCount++;
this.log.warn(`Cleanup: keeping entity "${entityId}" — has custom adapter config (history/influxdb/sql); remove it manually if you really want to drop it`);
continue;
}
// Delete sub-states first (longest ids), then any parent channel last.
const sortedIds = [...ids].sort((a, b) => b.length - a.length);
let entityFullyDeleted = true;
for (const id of sortedIds) {
try {
await this.delObjectAsync(id);
delete this.hassObjects[id];
deletedIdCount++;
}
catch (err) {
entityFullyDeleted = false;
this.log.error(`Cleanup: failed to delete "${id}": ${err}`);
}
}
if (entityFullyDeleted) {
deletedEntityCount++;
if (this.config.verboseFilterLog) {
this.log.info(`Cleanup: deleted entity "${entityId}" (${ids.length} object${ids.length === 1 ? '' : 's'})`);
}
}
}
this.log.info(`Cleanup: deleted ${deletedEntityCount} excluded entit${deletedEntityCount === 1 ? 'y' : 'ies'} (${deletedIdCount} object${deletedIdCount === 1 ? '' : 's'} total)${keptForCustomCount > 0
? `, kept ${keptForCustomCount} entit${keptForCustomCount === 1 ? 'y' : 'ies'} with custom config (see warnings above)`
: ''}`);
} }
async main() { async main() {
this.config.host ||= '127.0.0.1'; this.config.host ||= '127.0.0.1';
this.config.port = parseInt(String(this.config.port), 10) || 8123; this.config.port = parseInt(String(this.config.port), 10) || 8123;
const rawPatterns = (this.config.excludePatterns || '').toString();
const stringPatterns = rawPatterns
.split(/\r?\n/)
.map(s => s.trim())
.filter(line => line.length > 0 && !line.startsWith('#'));
this.excludePatterns = (0, entityFilter_1.buildExcludeRegexps)(stringPatterns);
if (this.excludePatterns.length === 0) {
this.log.info('Entity filter inactive (no exclude patterns configured)');
}
else {
this.log.info(`Entity filter active: ${this.excludePatterns.length} pattern(s) loaded: ${stringPatterns.join(', ')}`);
}
await this.cleanupExcludedObjects();
await this.setStateAsync('info.connection', false, true); await this.setStateAsync('info.connection', false, true);
this.hass = new hass_1.default(this.config, this.log); this.hass = new hass_1.default(this.config, this.log);
this.hass.on('error', err => this.log.error(err)); this.hass.on('error', err => this.log.error(err));
@@ -567,6 +693,10 @@ class HassAdapter extends adapter_core_1.Adapter {
if (!entity || typeof entity.entity_id !== 'string') { if (!entity || typeof entity.entity_id !== 'string') {
return; return;
} }
if ((0, entityFilter_1.isExcluded)(entity.entity_id, this.excludePatterns)) {
this.log.debug(`Entity filter: ignored state_changed for ${entity.entity_id}`);
return;
}
const id = `entities.${entity.entity_id}.`; const id = `entities.${entity.entity_id}.`;
const lc = entity.last_changed ? new Date(entity.last_changed).getTime() : undefined; const lc = entity.last_changed ? new Date(entity.last_changed).getTime() : undefined;
const ts = entity.last_updated ? new Date(entity.last_updated).getTime() : undefined; const ts = entity.last_updated ? new Date(entity.last_updated).getTime() : undefined;
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -8
View File
@@ -231,8 +231,6 @@
"buttons": ["agree", "cancel"] "buttons": ["agree", "cancel"]
} }
] ]
}
]
}, },
"native": { "native": {
"host": "127.0.0.1", "host": "127.0.0.1",
@@ -243,12 +241,8 @@
"verboseFilterLog": false, "verboseFilterLog": false,
"cleanupExcludedOnStart": false "cleanupExcludedOnStart": false
}, },
"protectedNative": [ "protectedNative": ["password"],
"password" "encryptedNative": ["password"],
],
"encryptedNative": [
"password"
],
"objects": [], "objects": [],
"instanceObjects": [ "instanceObjects": [
{ {
-3
View File
@@ -23,9 +23,6 @@
"@iobroker/testing": "^5.2.2", "@iobroker/testing": "^5.2.2",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"typescript": "^6.0.3" "typescript": "^6.0.3"
},
"engines": {
"node": ">=20"
} }
}, },
"node_modules/@alcalzone/esbuild-register": { "node_modules/@alcalzone/esbuild-register": {
+10 -6
View File
@@ -6,6 +6,9 @@
"name": "bluefox", "name": "bluefox",
"email": "dogafox@gmail.com" "email": "dogafox@gmail.com"
}, },
"engines": {
"node": ">=20"
},
"contributors": [ "contributors": [
{ {
"name": "bluefox", "name": "bluefox",
@@ -25,19 +28,20 @@
"url": "https://github.com/ioBroker/ioBroker.hass" "url": "https://github.com/ioBroker/ioBroker.hass"
}, },
"dependencies": { "dependencies": {
"ws": "^8.20.0", "ws": "^8.20.1",
"@iobroker/adapter-core": "^3.3.2" "@iobroker/adapter-core": "^3.3.2"
}, },
"devDependencies": { "devDependencies": {
"@alcalzone/release-script": "^5.1.1", "@alcalzone/release-script": "^5.2.0",
"@alcalzone/release-script-plugin-iobroker": "^5.1.2", "@alcalzone/release-script-plugin-iobroker": "^5.2.0",
"@alcalzone/release-script-plugin-license": "^5.1.1", "@alcalzone/release-script-plugin-license": "^5.2.0",
"@iobroker/adapter-dev": "^1.5.0", "@iobroker/adapter-dev": "^1.5.0",
"@iobroker/build-tools": "^3.0.1", "@iobroker/build-tools": "^3.0.1",
"@iobroker/eslint-config": "^2.2.0", "@iobroker/eslint-config": "^2.3.4",
"@iobroker/legacy-testing": "^2.0.2", "@iobroker/legacy-testing": "^2.0.2",
"@iobroker/testing": "^5.2.2", "@iobroker/testing": "^5.2.2",
"@types/ws": "^8.18.1" "@types/ws": "^8.18.1",
"typescript": "^6.0.3"
}, },
"main": "build/main.js", "main": "build/main.js",
"files": [ "files": [
+9 -4
View File
@@ -7,19 +7,24 @@
* *
* An empty patterns array always returns false. * An empty patterns array always returns false.
*/ */
export function isExcluded(entityId: string, patterns: string[]): boolean { export function isExcluded(entityId: string, patterns: RegExp[]): boolean {
if (!patterns || patterns.length === 0) { if (!patterns?.length) {
return false; return false;
} }
for (const pattern of patterns) { for (const pattern of patterns) {
if (globToRegex(pattern).test(entityId)) { if (pattern.test(entityId)) {
return true; return true;
} }
} }
return false; return false;
} }
function globToRegex(pattern: string): RegExp { export function buildExcludeRegexps(patterns: string[]): RegExp[] {
if (!patterns?.length) {
return [];
}
return patterns.map(pattern => {
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*'); const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
return new RegExp(`^${escaped}$`); return new RegExp(`^${escaped}$`);
});
} }
+13 -11
View File
@@ -1,13 +1,13 @@
import { Adapter, type AdapterOptions } from '@iobroker/adapter-core'; import { Adapter, type AdapterOptions } from '@iobroker/adapter-core';
import HASS from './lib/hass'; import HASS from './lib/hass';
import { isExcluded } from './lib/entityFilter'; import { isExcluded, buildExcludeRegexps } from './lib/entityFilter';
interface HassAdapterConfig { interface HassAdapterConfig {
host: string; host: string;
port: number; port: number;
password: string; password: string;
secure: boolean; secure: boolean;
excludePatterns: string; excludePatterns: RegExp[];
verboseFilterLog: boolean; verboseFilterLog: boolean;
cleanupExcludedOnStart: boolean; cleanupExcludedOnStart: boolean;
} }
@@ -192,7 +192,7 @@ class HassAdapter extends Adapter {
private delayTimeout: ReturnType<typeof setTimeout> | null = null; private delayTimeout: ReturnType<typeof setTimeout> | null = null;
private syncDebounceTimeout: ReturnType<typeof setTimeout> | null = null; private syncDebounceTimeout: ReturnType<typeof setTimeout> | null = null;
private stopped: boolean = false; private stopped: boolean = false;
private excludePatterns: string[] = []; private excludePatterns: RegExp[] = [];
private initialSyncCompleted: boolean = false; private initialSyncCompleted: boolean = false;
public constructor(options: Partial<AdapterOptions> = {}) { public constructor(options: Partial<AdapterOptions> = {}) {
@@ -695,7 +695,7 @@ class HassAdapter extends Adapter {
let allObjects: Record<string, ioBroker.Object>; let allObjects: Record<string, ioBroker.Object>;
try { try {
allObjects = (await this.getAdapterObjectsAsync()) as Record<string, ioBroker.Object>; allObjects = await this.getAdapterObjectsAsync();
} catch (err) { } catch (err) {
this.log.error(`Cleanup: failed to load adapter objects: ${err}`); this.log.error(`Cleanup: failed to load adapter objects: ${err}`);
return; return;
@@ -745,8 +745,7 @@ class HassAdapter extends Adapter {
// common.custom (history/influxdb/sql) keep the whole entity. // common.custom (history/influxdb/sql) keep the whole entity.
let hasCustom = false; let hasCustom = false;
for (const id of ids) { for (const id of ids) {
const custom = (allObjects[id].common as { custom?: Record<string, unknown> } | undefined) const custom = (allObjects[id].common as { custom?: Record<string, unknown> } | undefined)?.custom;
?.custom;
if (custom && Object.keys(custom).length) { if (custom && Object.keys(custom).length) {
hasCustom = true; hasCustom = true;
break; break;
@@ -784,10 +783,11 @@ class HassAdapter extends Adapter {
} }
this.log.info( this.log.info(
`Cleanup: deleted ${deletedEntityCount} excluded entit${deletedEntityCount === 1 ? 'y' : 'ies'} (${deletedIdCount} object${deletedIdCount === 1 ? '' : 's'} total)` + `Cleanup: deleted ${deletedEntityCount} excluded entit${deletedEntityCount === 1 ? 'y' : 'ies'} (${deletedIdCount} object${deletedIdCount === 1 ? '' : 's'} total)${
(keptForCustomCount > 0 keptForCustomCount > 0
? `, kept ${keptForCustomCount} entit${keptForCustomCount === 1 ? 'y' : 'ies'} with custom config (see warnings above)` ? `, kept ${keptForCustomCount} entit${keptForCustomCount === 1 ? 'y' : 'ies'} with custom config (see warnings above)`
: ''), : ''
}`,
); );
} }
@@ -796,16 +796,18 @@ class HassAdapter extends Adapter {
this.config.port = parseInt(String(this.config.port), 10) || 8123; this.config.port = parseInt(String(this.config.port), 10) || 8123;
const rawPatterns = (this.config.excludePatterns || '').toString(); const rawPatterns = (this.config.excludePatterns || '').toString();
this.excludePatterns = rawPatterns const stringPatterns = rawPatterns
.split(/\r?\n/) .split(/\r?\n/)
.map(s => s.trim()) .map(s => s.trim())
.filter(line => line.length > 0 && !line.startsWith('#')); .filter(line => line.length > 0 && !line.startsWith('#'));
this.excludePatterns = buildExcludeRegexps(stringPatterns);
if (this.excludePatterns.length === 0) { if (this.excludePatterns.length === 0) {
this.log.info('Entity filter inactive (no exclude patterns configured)'); this.log.info('Entity filter inactive (no exclude patterns configured)');
} else { } else {
this.log.info( this.log.info(
`Entity filter active: ${this.excludePatterns.length} pattern(s) loaded: ${this.excludePatterns.join(', ')}`, `Entity filter active: ${this.excludePatterns.length} pattern(s) loaded: ${stringPatterns.join(', ')}`,
); );
} }
+13 -12
View File
@@ -1,5 +1,5 @@
const { expect } = require('chai'); const { expect } = require('chai');
const { isExcluded } = require('../build/lib/entityFilter'); const { isExcluded, buildExcludeRegexps } = require('../build/lib/entityFilter');
describe('entityFilter.isExcluded', () => { describe('entityFilter.isExcluded', () => {
it('returns false for an empty pattern list', () => { it('returns false for an empty pattern list', () => {
@@ -7,41 +7,42 @@ describe('entityFilter.isExcluded', () => {
}); });
it('matches a simple glob with leading wildcard', () => { it('matches a simple glob with leading wildcard', () => {
expect(isExcluded('switch.iob_shelly_xyz', ['*.iob_*'])).to.equal(true); expect(isExcluded('switch.iob_shelly_xyz', buildExcludeRegexps(['*.iob_*']))).to.equal(true);
}); });
it('matches the bridge mirror naming pattern', () => { it('matches the bridge mirror naming pattern', () => {
expect(isExcluded('light.iob_ha_eg_wz1__e_licht_decke', ['*.iob_*__*'])).to.equal(true); expect(isExcluded('light.iob_ha_eg_wz1__e_licht_decke', buildExcludeRegexps(['*.iob_*__*']))).to.equal(true);
}); });
it('handles entities that miss a required literal segment', () => { it('handles entities that miss a required literal segment', () => {
// *.iob_*__* requires the literal `__` segment — entities without it do not match // *.iob_*__* requires the literal `__` segment — entities without it do not match
expect(isExcluded('switch.iob_', ['*.iob_*__*'])).to.equal(false); const regexps = buildExcludeRegexps(['*.iob_*__*']);
expect(isExcluded('switch.iob_foo', ['*.iob_*__*'])).to.equal(false); expect(isExcluded('switch.iob_', regexps)).to.equal(false);
expect(isExcluded('switch.iob_foo', regexps)).to.equal(false);
// Naming-Convention safety: similarly named entities without `__` are safe // Naming-Convention safety: similarly named entities without `__` are safe
expect(isExcluded('sensor.scheune_temperatur', ['*.sc_*__*'])).to.equal(false); expect(isExcluded('sensor.scheune_temperatur', buildExcludeRegexps(['*.sc_*__*']))).to.equal(false);
}); });
it('matches multiple patterns (OR semantic)', () => { it('matches multiple patterns (OR semantic)', () => {
const patterns = ['*.iob_*__*', '*.knx_*', '*.ha_*__*']; const patterns = buildExcludeRegexps(['*.iob_*__*', '*.knx_*', '*.ha_*__*']);
expect(isExcluded('switch.knx_foo', patterns)).to.equal(true); expect(isExcluded('switch.knx_foo', patterns)).to.equal(true);
expect(isExcluded('switch.ha_eg_wz1__e_licht_decke', patterns)).to.equal(true); expect(isExcluded('switch.ha_eg_wz1__e_licht_decke', patterns)).to.equal(true);
expect(isExcluded('switch.something_else', patterns)).to.equal(false); expect(isExcluded('switch.something_else', patterns)).to.equal(false);
}); });
it('is case sensitive', () => { it('is case sensitive', () => {
expect(isExcluded('switch.IOB_foo', ['*.iob_*'])).to.equal(false); expect(isExcluded('switch.IOB_foo', buildExcludeRegexps(['*.iob_*']))).to.equal(false);
expect(isExcluded('switch.iob_foo', ['*.IOB_*'])).to.equal(false); expect(isExcluded('switch.iob_foo', buildExcludeRegexps(['*.IOB_*']))).to.equal(false);
}); });
it('treats * as matching any characters including dots', () => { it('treats * as matching any characters including dots', () => {
// We use full entity_id including the leading domain, so this is fine. // We use full entity_id including the leading domain, so this is fine.
expect(isExcluded('switch.iob_foo', ['*foo*'])).to.equal(true); expect(isExcluded('switch.iob_foo', buildExcludeRegexps(['*foo*']))).to.equal(true);
}); });
it('escapes regex metacharacters in patterns', () => { it('escapes regex metacharacters in patterns', () => {
// `.` in pattern matches literal `.`, not "any char" // `.` in pattern matches literal `.`, not "any char"
expect(isExcluded('switch.iob_foo', ['switch.iob_foo'])).to.equal(true); expect(isExcluded('switch.iob_foo', buildExcludeRegexps(['switch.iob_foo']))).to.equal(true);
expect(isExcluded('switchXiob_foo', ['switch.iob_foo'])).to.equal(false); expect(isExcluded('switchXiob_foo', buildExcludeRegexps(['switch.iob_foo']))).to.equal(false);
}); });
}); });