iobroker.zigbee2mqtt 3.2.2 → 3.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/admin/i18n/de/translations.json +12 -2
- package/admin/i18n/en/translations.json +17 -6
- package/admin/i18n/es/translations.json +15 -1
- package/admin/i18n/fr/translations.json +14 -1
- package/admin/i18n/it/translations.json +14 -1
- package/admin/i18n/nl/translations.json +14 -1
- package/admin/i18n/pl/translations.json +14 -1
- package/admin/i18n/pt/translations.json +14 -1
- package/admin/i18n/ru/translations.json +14 -1
- package/admin/i18n/uk/translations.json +14 -1
- package/admin/i18n/zh-cn/translations.json +12 -1
- package/admin/jsonConfig.json +4 -4
- package/io-package.json +31 -4
- package/lib/deviceController.js +32 -21
- package/lib/exposes.js +13 -14
- package/lib/imageController.js +1 -1
- package/lib/mqttServerController.js +25 -5
- package/lib/rgb.js +5 -5
- package/lib/states.js +22 -0
- package/lib/statesController.js +43 -8
- package/lib/z2mController.js +9 -1
- package/main.js +38 -12
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -17,11 +17,29 @@
|
|
|
17
17
|
|
|
18
18
|
This adapter allows to control the data points of the devices of a Zigbee2MQTT instance in ioBroker.
|
|
19
19
|
|
|
20
|
+
## Troubleshooting
|
|
21
|
+
if you see in Log a message with Caught by controller[1]: /opt/iobroker/node_modules/iobroker.zigbee2mqtt/node_modules/sharp/lib/sharp.js
|
|
22
|
+
check you VM Settings
|
|
23
|
+
|
|
24
|
+
<img width="619" height="238" alt="grafik" src="https://github.com/user-attachments/assets/83879925-96f8-4c33-a6cd-2b68e4b41780" />
|
|
25
|
+
<img width="618" height="216" alt="grafik" src="https://github.com/user-attachments/assets/30c33952-b055-4d6f-99d9-f7cc49831db3" />
|
|
26
|
+
<img width="711" height="497" alt="grafik" src="https://github.com/user-attachments/assets/803340a6-f000-4e64-b53f-8e80f2a13127" />
|
|
27
|
+
|
|
20
28
|
## Adapter Documentation
|
|
21
29
|
|
|
22
30
|
[Adapter Documentation](https://github.com/arteck/ioBroker.zigbee2mqtt/blob/main/docs/wiki.md)
|
|
23
31
|
|
|
24
32
|
## Changelog
|
|
33
|
+
### 3.2.4 (2026-06-26)
|
|
34
|
+
* (arteck) Dependencies have been updated
|
|
35
|
+
*
|
|
36
|
+
|
|
37
|
+
### 3.2.3 (2026-06-25)
|
|
38
|
+
* (arteck) typo
|
|
39
|
+
* (arteck) fix some warnings
|
|
40
|
+
* (arteck) fix internal mqtt
|
|
41
|
+
* (arteck) fix languages
|
|
42
|
+
|
|
25
43
|
### 3.2.2 (2026-05-26)
|
|
26
44
|
* (arteck) Dependencies have been updated
|
|
27
45
|
|
|
@@ -34,12 +52,6 @@ This adapter allows to control the data points of the devices of a Zigbee2MQTT i
|
|
|
34
52
|
### 3.2.0 (2026-04-26)
|
|
35
53
|
* (arteck) del deprectated setStateAsync
|
|
36
54
|
|
|
37
|
-
### 3.1.9 (2026-04-26)
|
|
38
|
-
* (arteck) fix WS close
|
|
39
|
-
|
|
40
|
-
### 3.1.8 (2026-04-26)
|
|
41
|
-
* (arteck) back to old dependency aedes
|
|
42
|
-
|
|
43
55
|
## License
|
|
44
56
|
|
|
45
57
|
MIT License
|
|
@@ -48,6 +48,16 @@
|
|
|
48
48
|
"More information": "Mehr Informationen",
|
|
49
49
|
"Expert Settings. Please only use if you know what you're doing": "Expert Einstellungen. Bitte nur verwenden, wenn Du weisst was du machst",
|
|
50
50
|
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System" : "Aktualisiert immer den Status für die Bewegung, wenn eine Nachricht vom zigbee2mqtt-Server eintrifft (nur bei true). Erhöht die Last auf dem ioBroker",
|
|
51
|
-
"Always update available state when message included last_seen status": "Aktualisiert immer den available Status, wenn die Nachricht last_seen Status enthält"
|
|
52
|
-
|
|
51
|
+
"Always update available state when message included last_seen status": "Aktualisiert immer den available Status, wenn die Nachricht last_seen Status enthält",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Haupteinstellungen",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Willkommen beim Zigbee2MQTT-Adapter",
|
|
58
|
+
"Device image configurations": "Gerätebild-Konfigurationen",
|
|
59
|
+
"Size of the object icons in pixels": "Größe der Objekt-Symbole in Pixeln",
|
|
60
|
+
"Expert configurations": "Experteneinstellungen",
|
|
61
|
+
"coming soon": "kommt bald",
|
|
62
|
+
"Delete old / unused states": "Alte / ungenutzte Zustände löschen"
|
|
53
63
|
}
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
"Configure your Zigbee2MQTT connection": "Configure your Zigbee2MQTT connection",
|
|
11
11
|
"Websocket IP-Address": "Websocket IP-Address",
|
|
12
12
|
"Websocket Port": "Websocket Port",
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
"Use Auth-Token": "Use Auth-Token",
|
|
14
|
+
"Auth-Token (special characters are not supported)": "Auth-Token (special characters are not supported)",
|
|
15
|
+
"Create a dummy MQTT-Server for Zigbee2MQTT": "Create a dummy MQTT-Server for Zigbee2MQTT",
|
|
16
|
+
"Base MQTT topic": "Base MQTT topic",
|
|
17
|
+
"External MQTT-Server IP-Address": "External MQTT-Server IP-Address",
|
|
18
18
|
"External MQTT-Server Port": "External MQTT-Server Port",
|
|
19
19
|
"MQTT-Server IP-Address bind": "MQTT-Server IP-Address bind",
|
|
20
20
|
"MQTT-Server Port": "MQTT-Server Port",
|
|
@@ -48,5 +48,16 @@
|
|
|
48
48
|
"More information": "More information",
|
|
49
49
|
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
50
|
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System" : "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
-
"Always update available state when message included last_seen status": "Always update available state when message included last_seen status"
|
|
51
|
+
"Always update available state when message included last_seen status": "Always update available state when message included last_seen status",
|
|
52
|
+
"Main Settings": "Main Settings",
|
|
53
|
+
"Welcome to Zigbee2MQTT Adapter": "Welcome to Zigbee2MQTT Adapter",
|
|
54
|
+
"WS": "WS",
|
|
55
|
+
"WS (SSL)": "WS (SSL)",
|
|
56
|
+
"HTTP": "HTTP",
|
|
57
|
+
"HTTPS": "HTTPS",
|
|
58
|
+
"Device image configurations" : "Device image configurations",
|
|
59
|
+
"Size of the object icons in pixels": "Size of the object icons in pixels",
|
|
60
|
+
"Expert configurations": "Expert configurations",
|
|
61
|
+
"coming soon": "coming soon ",
|
|
62
|
+
"Delete old / unused states": "Delete old / unused states"
|
|
52
63
|
}
|
|
@@ -45,5 +45,19 @@
|
|
|
45
45
|
"Automatic check for missing routers in the coordinator memory.": "Comprobación automática de enrutadores faltantes en la memoria del coordinador.",
|
|
46
46
|
"With which log level should a negative search be logged?": "¿Con qué nivel de registro se debe registrar una búsqueda negativa?",
|
|
47
47
|
"Time of the automatic check": "Hora del control automático",
|
|
48
|
-
"More information": "Más información"
|
|
48
|
+
"More information": "Más información",
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Siempre actualizar el estado disponible cuando el mensaje incluya el estado last_seen",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Configuración principal",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Bienvenido al adaptador Zigbee2MQTT",
|
|
58
|
+
"Device image configurations": "Configuración de imágenes de dispositivos",
|
|
59
|
+
"Size of the object icons in pixels": "Tamaño de los iconos de objetos en píxeles",
|
|
60
|
+
"Expert configurations": "Configuraciones avanzadas",
|
|
61
|
+
"coming soon": "próximamente",
|
|
62
|
+
"Delete old / unused states": "Eliminar estados antiguos / no utilizados"
|
|
49
63
|
}
|
|
@@ -46,5 +46,18 @@
|
|
|
46
46
|
"With which log level should a negative search be logged?": "Avec quel niveau de journalisation une recherche négative doit-elle être enregistrée ?",
|
|
47
47
|
"Time of the automatic check": "Heure du contrôle automatique",
|
|
48
48
|
"More information": "Plus d'information",
|
|
49
|
-
"
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Mettre à jour l'état disponible lorsque le message inclut le statut last_seen",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Paramètres principaux",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Bienvenue dans l'adaptateur Zigbee2MQTT",
|
|
58
|
+
"Device image configurations": "Configuration des images des appareils",
|
|
59
|
+
"Size of the object icons in pixels": "Taille des icônes d'objet en pixels",
|
|
60
|
+
"Expert configurations": "Configurations avancées",
|
|
61
|
+
"coming soon": "bientôt disponible",
|
|
62
|
+
"Delete old / unused states": "Supprimer les états anciens / inutilisés"
|
|
50
63
|
}
|
|
@@ -46,5 +46,18 @@
|
|
|
46
46
|
"With which log level should a negative search be logged?": "Con quale livello di registro deve essere registrata una ricerca negativa?",
|
|
47
47
|
"Time of the automatic check": "Orario del controllo automatico",
|
|
48
48
|
"More information": "Maggiori informazioni",
|
|
49
|
-
"
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Aggiorna sempre lo stato disponibile quando il messaggio include lo stato last_seen",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Impostazioni principali",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Benvenuto nell'adattatore Zigbee2MQTT",
|
|
58
|
+
"Device image configurations": "Configurazioni delle immagini dei dispositivi",
|
|
59
|
+
"Size of the object icons in pixels": "Dimensione delle icone degli oggetti in pixel",
|
|
60
|
+
"Expert configurations": "Configurazioni avanzate",
|
|
61
|
+
"coming soon": "prossimamente",
|
|
62
|
+
"Delete old / unused states": "Elimina stati vecchi / inutilizzati"
|
|
50
63
|
}
|
|
@@ -46,5 +46,18 @@
|
|
|
46
46
|
"With which log level should a negative search be logged?": "Met welk logniveau moet een negatieve zoekopdracht worden geregistreerd?",
|
|
47
47
|
"Time of the automatic check": "Tijdstip van de automatische controle",
|
|
48
48
|
"More information": "Meer informatie",
|
|
49
|
-
"
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Altijd de beschikbare status bijwerken wanneer het bericht de last_seen-status bevat",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Hoofdinstellingen",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Welkom bij de Zigbee2MQTT-adapter",
|
|
58
|
+
"Device image configurations": "Configuratie van apparaatafbeeldingen",
|
|
59
|
+
"Size of the object icons in pixels": "Grootte van de objectpictogrammen in pixels",
|
|
60
|
+
"Expert configurations": "Geavanceerde configuraties",
|
|
61
|
+
"coming soon": "binnenkort beschikbaar",
|
|
62
|
+
"Delete old / unused states": "Oude / ongebruikte statussen verwijderen"
|
|
50
63
|
}
|
|
@@ -46,5 +46,18 @@
|
|
|
46
46
|
"With which log level should a negative search be logged?": "Na jakim poziomie rejestrowania należy rejestrować wyszukiwanie wykluczające?",
|
|
47
47
|
"Time of the automatic check": "Czas automatycznego sprawdzenia",
|
|
48
48
|
"More information": "Więcej informacji",
|
|
49
|
-
"
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Zawsze aktualizuj available stan, gdy wiadomość zawiera status last_seen",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Ustawienia główne",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Witamy w adapterze Zigbee2MQTT",
|
|
58
|
+
"Device image configurations": "Konfiguracja obrazów urządzeń",
|
|
59
|
+
"Size of the object icons in pixels": "Rozmiar ikon obiektów w pikselach",
|
|
60
|
+
"Expert configurations": "Konfiguracje zaawansowane",
|
|
61
|
+
"coming soon": "wkrótce dostępne",
|
|
62
|
+
"Delete old / unused states": "Usuń stare / nieużywane stany"
|
|
50
63
|
}
|
|
@@ -46,5 +46,18 @@
|
|
|
46
46
|
"With which log level should a negative search be logged?": "Com qual nível de log uma pesquisa negativa deve ser registrada?",
|
|
47
47
|
"Time of the automatic check": "Hora da verificação automática",
|
|
48
48
|
"More information": "Mais Informações",
|
|
49
|
-
"
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Sempre atualize o estado disponível quando a mensagem incluir o status last_seen",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Configurações principais",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Bem-vindo ao adaptador Zigbee2MQTT",
|
|
58
|
+
"Device image configurations": "Configurações de imagens dos dispositivos",
|
|
59
|
+
"Size of the object icons in pixels": "Tamanho dos ícones dos objetos em pixels",
|
|
60
|
+
"Expert configurations": "Configurações avançadas",
|
|
61
|
+
"coming soon": "em breve",
|
|
62
|
+
"Delete old / unused states": "Excluir estados antigos / não utilizados"
|
|
50
63
|
}
|
|
@@ -46,5 +46,18 @@
|
|
|
46
46
|
"With which log level should a negative search be logged?": "На каком уровне журнала следует регистрировать отрицательный поиск?",
|
|
47
47
|
"Time of the automatic check": "Время автоматической проверки",
|
|
48
48
|
"More information": "Больше информации",
|
|
49
|
-
"
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Всегда обновляйте доступное состояние, когда сообщение включает статус last_seen",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Основные настройки",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Добро пожаловать в адаптер Zigbee2MQTT",
|
|
58
|
+
"Device image configurations": "Настройки изображений устройств",
|
|
59
|
+
"Size of the object icons in pixels": "Размер значков объектов в пикселях",
|
|
60
|
+
"Expert configurations": "Расширенные настройки",
|
|
61
|
+
"coming soon": "скоро будет доступно",
|
|
62
|
+
"Delete old / unused states": "Удалить старые / неиспользуемые состояния"
|
|
50
63
|
}
|
|
@@ -46,5 +46,18 @@
|
|
|
46
46
|
"With which log level should a negative search be logged?": "На якому рівні журналу слід реєструвати негативний пошук?",
|
|
47
47
|
"Time of the automatic check": "Час автоматичної перевірки",
|
|
48
48
|
"More information": "Більше інформації",
|
|
49
|
-
"
|
|
49
|
+
"Expert Settings. Please only use if you know what you're doing": "Expert Settings. Please only use if you know what you're doing",
|
|
50
|
+
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
51
|
+
"Always update available state when message included last_seen status": "Завжди оновлюйте доступний стан, коли повідомлення містить статус last_seen",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "Основні налаштування",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "Ласкаво просимо до адаптера Zigbee2MQTT",
|
|
58
|
+
"Device image configurations": "Налаштування зображень пристроїв",
|
|
59
|
+
"Size of the object icons in pixels": "Розмір значків об'єктів у пікселях",
|
|
60
|
+
"Expert configurations": "Розширені налаштування",
|
|
61
|
+
"coming soon": "незабаром",
|
|
62
|
+
"Delete old / unused states": "Видалити старі / невикористовувані стани"
|
|
50
63
|
}
|
|
@@ -48,5 +48,16 @@
|
|
|
48
48
|
"More information": "更多信息",
|
|
49
49
|
"Expert Settings. Please only use if you know what you're doing": "专家设置。请仅在您清楚操作的情况下使用",
|
|
50
50
|
"Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System": "当来自 zigbee2mqtt 服务器的消息到达时始终更新占用状态(仅限为 true 的状态)。会增加 ioBroker 系统负载",
|
|
51
|
-
"Always update available state when message included last_seen status": "当消息包含 last_seen 状态时始终更新可用状态"
|
|
51
|
+
"Always update available state when message included last_seen status": "当消息包含 last_seen 状态时始终更新可用状态",
|
|
52
|
+
"WS": "WS",
|
|
53
|
+
"WS (SSL)": "WS (SSL)",
|
|
54
|
+
"HTTP": "HTTP",
|
|
55
|
+
"HTTPS": "HTTPS",
|
|
56
|
+
"Main Settings": "主要设置",
|
|
57
|
+
"Welcome to Zigbee2MQTT Adapter": "欢迎使用 Zigbee2MQTT 适配器",
|
|
58
|
+
"Device image configurations": "设备图片配置",
|
|
59
|
+
"Size of the object icons in pixels": "对象图标大小(像素)",
|
|
60
|
+
"Expert configurations": "高级配置",
|
|
61
|
+
"coming soon": "即将推出",
|
|
62
|
+
"Delete old / unused states": "删除旧的/未使用的状态"
|
|
52
63
|
}
|
package/admin/jsonConfig.json
CHANGED
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"value": "ws"
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
|
-
"label": "
|
|
115
|
+
"label": "WS (SSL)",
|
|
116
116
|
"value": "wss"
|
|
117
117
|
}
|
|
118
118
|
],
|
|
@@ -546,7 +546,7 @@
|
|
|
546
546
|
"hidden": "data.coordinatorCheck != true"
|
|
547
547
|
},
|
|
548
548
|
|
|
549
|
-
"
|
|
549
|
+
"alwaysUpdateOccupancyState": {
|
|
550
550
|
"type": "checkbox",
|
|
551
551
|
"label": "Always update state for occupancy when message arrives from zigbee2mqtt server (Only for true state). Increases load on ioBroker System",
|
|
552
552
|
"xs": 12,
|
|
@@ -556,7 +556,7 @@
|
|
|
556
556
|
"xl": 12,
|
|
557
557
|
"newLine": true
|
|
558
558
|
},
|
|
559
|
-
"
|
|
559
|
+
"alwaysUpdateAvailableState": {
|
|
560
560
|
"type": "checkbox",
|
|
561
561
|
"label": "Always update available state when message included last_seen status",
|
|
562
562
|
"xs": 12,
|
|
@@ -578,7 +578,7 @@
|
|
|
578
578
|
},
|
|
579
579
|
"headerDeleteStates": {
|
|
580
580
|
"type": "header",
|
|
581
|
-
"text": "coming soon
|
|
581
|
+
"text": "coming soon",
|
|
582
582
|
"size": 4,
|
|
583
583
|
"newLine": true
|
|
584
584
|
},
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "zigbee2mqtt",
|
|
4
|
-
"version": "3.2.
|
|
4
|
+
"version": "3.2.4",
|
|
5
5
|
"news": {
|
|
6
|
+
"3.2.4": {
|
|
7
|
+
"en": "Dependencies have been updated\n",
|
|
8
|
+
"de": "Die Abhängigkeiten wurden aktualisiert.\n",
|
|
9
|
+
"ru": "Зависимости обновлены.\n",
|
|
10
|
+
"pt": "As dependências foram atualizadas.\n",
|
|
11
|
+
"nl": "Afhankelijkheden zijn bijgewerkt.\n",
|
|
12
|
+
"fr": "Les dépendances ont été mises à jour\n",
|
|
13
|
+
"it": "Le dipendenze sono state aggiornate\n",
|
|
14
|
+
"es": "Se han actualizado las dependencias.\n",
|
|
15
|
+
"pl": "Zaktualizowano zależności\n",
|
|
16
|
+
"uk": "Залежності оновлено\n",
|
|
17
|
+
"zh-cn": "依赖项已更新\n"
|
|
18
|
+
},
|
|
19
|
+
"3.2.3": {
|
|
20
|
+
"en": "typo\nfix some warnings\nfix internal mqtt\nfix languages",
|
|
21
|
+
"de": "Tippfehler\nEinige Warnungen beheben\nInternes MQTT reparieren\nSprachen korrigieren",
|
|
22
|
+
"ru": "опечатка\nИсправлены некоторые предупреждения\nисправление внутреннего MQTT\nисправить языки",
|
|
23
|
+
"pt": "erro de digitação\nCorrigir alguns avisos\ncorrigir MQTT interno\ncorrigir idiomas",
|
|
24
|
+
"nl": "typfout\nEnkele waarschuwingen verhelpen\ninterne MQTT-problemen oplossen\ntalen corrigeren",
|
|
25
|
+
"fr": "faute de frappe\nCorrection de certains avertissements\ncorrection du MQTT interne\ncorriger les langues",
|
|
26
|
+
"it": "errore di battitura\ncorreggere alcuni avvisi\ncorrezione interna MQTT\nlinguaggi corretti",
|
|
27
|
+
"es": "error de tipografía\ncorregir algunas advertencias\ncorregir mqtt interno\ncorregir idiomas",
|
|
28
|
+
"pl": "literówka\nnapraw niektóre ostrzeżenia\nnapraw wewnętrzny MQTT\nnaprawić języki",
|
|
29
|
+
"uk": "друкарська помилка\nвиправити деякі попередження\nвиправити внутрішній mqtt\nвиправити мови",
|
|
30
|
+
"zh-cn": "拼写错误\n修复一些警告\n修复内部 MQTT\n修复语言"
|
|
31
|
+
},
|
|
6
32
|
"3.2.2": {
|
|
7
33
|
"en": "Dependencies have been updated",
|
|
8
34
|
"de": "Abhängigkeiten wurden aktualisiert",
|
|
@@ -170,7 +196,8 @@
|
|
|
170
196
|
"it": "Zigbee2MQTT",
|
|
171
197
|
"es": "Zigbee2MQTT",
|
|
172
198
|
"pl": "Zigbee2MQTT",
|
|
173
|
-
"zh-cn": "Zigbee2MQTT"
|
|
199
|
+
"zh-cn": "Zigbee2MQTT",
|
|
200
|
+
"uk": "Zigbee2MQTT"
|
|
174
201
|
},
|
|
175
202
|
"link": "%webUIScheme%://%webUIServer%:%webUIPort%"
|
|
176
203
|
},
|
|
@@ -224,8 +251,8 @@
|
|
|
224
251
|
"coordinatorCheck": false,
|
|
225
252
|
"coordinatorCheckLogLvl": "info",
|
|
226
253
|
"coordinatorCheckCron": "0 3 * * *",
|
|
227
|
-
"
|
|
228
|
-
"
|
|
254
|
+
"alwaysUpdateOccupancyState": false,
|
|
255
|
+
"alwaysUpdateAvailableState": false
|
|
229
256
|
},
|
|
230
257
|
"protectedNative": [
|
|
231
258
|
"externalMqttServerPassword",
|
package/lib/deviceController.js
CHANGED
|
@@ -6,19 +6,6 @@ const rgb = require('./rgb.js');
|
|
|
6
6
|
const ImageController = require('./imageController').ImageController;
|
|
7
7
|
const dmUtils = require('@iobroker/dm-utils');
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* Prüft ob eine ieee_address in der kommaseparierten debugDevices-Liste enthalten ist.
|
|
11
|
-
* String.includes() würde Substring-Matches liefern (z.B. "0x12" trifft "0x1234").
|
|
12
|
-
*
|
|
13
|
-
* @param {string} debugDevices
|
|
14
|
-
* @param {string} address
|
|
15
|
-
* @returns {boolean}
|
|
16
|
-
*/
|
|
17
|
-
function isDebugDevice(debugDevices, address) {
|
|
18
|
-
if (!debugDevices || !address) { return false; }
|
|
19
|
-
return String(debugDevices).split(',').map((s) => s.trim()).includes(address);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
9
|
/**
|
|
23
10
|
* Verwaltet das Erstellen und Aktualisieren von Geräte- und Gruppenobjekten in ioBroker
|
|
24
11
|
* basierend auf den Zigbee2MQTT-Exposes.
|
|
@@ -43,6 +30,31 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
43
30
|
this.logCustomizations = logCustomizations;
|
|
44
31
|
this.createCache = createCache;
|
|
45
32
|
this.imageController = new ImageController(adapter);
|
|
33
|
+
|
|
34
|
+
// Debug-Device Cache - wird lazy befüllt (siehe isDebugDevice)
|
|
35
|
+
this._debugDeviceCache = null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks if the given device is a debug device.
|
|
40
|
+
* Der CSV-String wird einmalig geparst und in einem Set gecached (O(1) Lookup).
|
|
41
|
+
* Case-insensitive Vergleich.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} ieeeAddr - IEEE address of the device
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
isDebugDevice(ieeeAddr) {
|
|
47
|
+
if (!ieeeAddr) { return false; }
|
|
48
|
+
if (this._debugDeviceCache === null) {
|
|
49
|
+
const debugDeviceList = this.logCustomizations.debugDevices || '';
|
|
50
|
+
this._debugDeviceCache = new Set(
|
|
51
|
+
debugDeviceList
|
|
52
|
+
.split(',')
|
|
53
|
+
.map(s => s.trim().toLowerCase())
|
|
54
|
+
.filter(s => s.length > 0)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return this._debugDeviceCache.has(ieeeAddr.toLowerCase());
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
/**
|
|
@@ -63,7 +75,7 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
63
75
|
continue;
|
|
64
76
|
}
|
|
65
77
|
|
|
66
|
-
if (isDebugDevice(
|
|
78
|
+
if (this.isDebugDevice(devicesMessag.ieee_address)) {
|
|
67
79
|
this.adapter.log.warn(
|
|
68
80
|
`--->>> fromZ2M -> ${devicesMessag.ieee_address} exposes: ${JSON.stringify(devicesMessag)}`
|
|
69
81
|
);
|
|
@@ -76,7 +88,11 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
76
88
|
if (Array.isArray(devicesMessag.definition.exposes)) {
|
|
77
89
|
try {
|
|
78
90
|
const newDevice = await createDeviceFromExposes(devicesMessag, this.adapter);
|
|
79
|
-
|
|
91
|
+
try {
|
|
92
|
+
newDevice.icon = await this.imageController.getDeviceIcon(devicesMessag);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
this.adapter.log.warn(`[deviceController] Could not load icon for device ${newDevice.common?.name || 'unknown'}: ${e.message}`);
|
|
95
|
+
}
|
|
80
96
|
newDevice.manufacturer = devicesMessag.definition.vendor || '';
|
|
81
97
|
newDevice.model = devicesMessag.definition.model || '';
|
|
82
98
|
newDevice.modelDescription = devicesMessag.definition.description || '';
|
|
@@ -297,7 +313,7 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
297
313
|
if (!groupMessage || groupMessage.id == null || !groupMessage.friendly_name) {
|
|
298
314
|
continue;
|
|
299
315
|
}
|
|
300
|
-
if (isDebugDevice(
|
|
316
|
+
if (this.isDebugDevice(String(groupMessage.id))) {
|
|
301
317
|
this.adapter.log.warn(`--->>> fromZ2M -> ${groupMessage.id} exposes: ${JSON.stringify(groupMessage)}`);
|
|
302
318
|
}
|
|
303
319
|
this.defineGroupDevice(groupMessage.friendly_name, `group_${groupMessage.id}`, groupMessage.scenes || []);
|
|
@@ -765,11 +781,9 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
765
781
|
const detailItems = device._detailItems || {};
|
|
766
782
|
const hasDetails = Object.keys(detailItems).length > 0;
|
|
767
783
|
|
|
768
|
-
/** @type {Record<string, any>} */
|
|
769
784
|
const data = {};
|
|
770
785
|
|
|
771
786
|
// ── Tab 1: Gerät ─────────────────────────────────────────────────────
|
|
772
|
-
/** @type {Record<string, any>} */
|
|
773
787
|
const infoItems = {};
|
|
774
788
|
infoItems['_h1'] = { type: 'header', text: 'Device Identity', size: 4, newLine: true };
|
|
775
789
|
infoItems['_d1'] = { type: 'divider', color: 'primary' };
|
|
@@ -781,7 +795,6 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
781
795
|
infoItems['pwr'] = { type: 'staticInfo', label: 'Power Source', data: device.powerSource || '—', size: 16, addColon: true, newLine: true };
|
|
782
796
|
|
|
783
797
|
// ── Tab 2: Verbindung ─────────────────────────────────────────────────
|
|
784
|
-
/** @type {Record<string, any>} */
|
|
785
798
|
const connItems = {};
|
|
786
799
|
connItems['_h1'] = { type: 'header', text: 'Zigbee Connection', size: 4, newLine: true };
|
|
787
800
|
connItems['_d1'] = { type: 'divider', color: 'primary' };
|
|
@@ -793,7 +806,6 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
793
806
|
}
|
|
794
807
|
|
|
795
808
|
// ── Tab 3: Technisch ──────────────────────────────────────────────────
|
|
796
|
-
/** @type {Record<string, any>} */
|
|
797
809
|
const techItems = {};
|
|
798
810
|
techItems['_h1'] = { type: 'header', text: 'Zigbee Status', size: 4, newLine: true };
|
|
799
811
|
techItems['_d1'] = { type: 'divider', color: 'primary' };
|
|
@@ -806,7 +818,6 @@ class DeviceController extends dmUtils.DeviceManagement {
|
|
|
806
818
|
data.disabled = !!device.disabled;
|
|
807
819
|
|
|
808
820
|
// ── Tabs zusammenbauen ────────────────────────────────────────────────
|
|
809
|
-
/** @type {Record<string, any>} */
|
|
810
821
|
const tabs = {
|
|
811
822
|
_tab_info: { type: 'panel', label: 'Device', items: infoItems },
|
|
812
823
|
_tab_conn: { type: 'panel', label: 'Connection', items: connItems },
|
package/lib/exposes.js
CHANGED
|
@@ -111,6 +111,7 @@ function genState(expose, role, name, desc) {
|
|
|
111
111
|
states: {},
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
+
state.type = 'string';
|
|
114
115
|
for (const val of expose.values) {
|
|
115
116
|
// if a definition of a button (eg. Aqara presence detector FP1)
|
|
116
117
|
if (val === '') {
|
|
@@ -118,7 +119,6 @@ function genState(expose, role, name, desc) {
|
|
|
118
119
|
} else {
|
|
119
120
|
state.states[val] = val;
|
|
120
121
|
}
|
|
121
|
-
state.type = typeof val;
|
|
122
122
|
}
|
|
123
123
|
// Fix 4: wenn values leer → type bleibt undefined → Fallback auf 'string'
|
|
124
124
|
if (!state.type) {
|
|
@@ -153,6 +153,7 @@ function genState(expose, role, name, desc) {
|
|
|
153
153
|
break;
|
|
154
154
|
|
|
155
155
|
default:
|
|
156
|
+
console.warn(`[genState] Unknown expose type: ${expose.type}`);
|
|
156
157
|
break;
|
|
157
158
|
}
|
|
158
159
|
|
|
@@ -203,13 +204,12 @@ async function createDeviceFromExposes(devicesMessag, adapter) {
|
|
|
203
204
|
state = Object.assign({}, state);
|
|
204
205
|
|
|
205
206
|
if (access === undefined) {access = z2mAccess.ALL;}
|
|
206
|
-
state.
|
|
207
|
-
state.
|
|
207
|
+
state.read = (access & z2mAccess.STATE) > 0;
|
|
208
|
+
state.write = (access & z2mAccess.SET) > 0;
|
|
208
209
|
const stateExists = states.findIndex((x, _index, _array) => x.id === state.id);
|
|
209
210
|
|
|
210
211
|
if (stateExists < 0) {
|
|
211
|
-
state.write
|
|
212
|
-
if (!state.writable) {
|
|
212
|
+
if (!state.write) {
|
|
213
213
|
if (state.hasOwnProperty('setter')) {
|
|
214
214
|
delete state.setter;
|
|
215
215
|
}
|
|
@@ -219,7 +219,7 @@ async function createDeviceFromExposes(devicesMessag, adapter) {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
if (!state.
|
|
222
|
+
if (!state.read) {
|
|
223
223
|
if (state.hasOwnProperty('getter')) {
|
|
224
224
|
//to awid some worning on unprocessed data
|
|
225
225
|
state.getter = (_payload) => undefined;
|
|
@@ -228,7 +228,7 @@ async function createDeviceFromExposes(devicesMessag, adapter) {
|
|
|
228
228
|
|
|
229
229
|
return states.push(state);
|
|
230
230
|
}
|
|
231
|
-
if (state.
|
|
231
|
+
if (state.read && !states[stateExists].read) {
|
|
232
232
|
states[stateExists].read = state.read;
|
|
233
233
|
// as state is readable, it can't be button or event
|
|
234
234
|
if (states[stateExists].role === 'button') {
|
|
@@ -259,11 +259,11 @@ async function createDeviceFromExposes(devicesMessag, adapter) {
|
|
|
259
259
|
} else if (state.hasOwnProperty('prop')) {
|
|
260
260
|
states[stateExists].prop = state.prop;
|
|
261
261
|
}
|
|
262
|
-
states[stateExists].
|
|
262
|
+
states[stateExists].read = true;
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
if (state.
|
|
266
|
-
states[stateExists].write = state.
|
|
265
|
+
if (state.write && !states[stateExists].write) {
|
|
266
|
+
states[stateExists].write = state.write;
|
|
267
267
|
// use new state `setter`
|
|
268
268
|
if (state.hasOwnProperty('setter')) {
|
|
269
269
|
states[stateExists].setter = state.setter;
|
|
@@ -303,7 +303,7 @@ async function createDeviceFromExposes(devicesMessag, adapter) {
|
|
|
303
303
|
if (state.hasOwnProperty('epname')) {
|
|
304
304
|
states[stateExists].epname = state.epname;
|
|
305
305
|
}
|
|
306
|
-
states[stateExists].
|
|
306
|
+
states[stateExists].write = true;
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
return states.length;
|
|
@@ -1289,9 +1289,8 @@ async function createDeviceFromExposes(devicesMessag, adapter) {
|
|
|
1289
1289
|
return !isNaN(payload[expose.property][prop.property])
|
|
1290
1290
|
? payload[expose.property][prop.property]
|
|
1291
1291
|
: undefined;
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1292
|
+
}
|
|
1293
|
+
return undefined;
|
|
1295
1294
|
};
|
|
1296
1295
|
} else {
|
|
1297
1296
|
state.getter = (payload) => {
|
package/lib/imageController.js
CHANGED
|
@@ -106,7 +106,7 @@ class ImageController {
|
|
|
106
106
|
return '';
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
const imageSize = this.adapter.config.deviceIconsSize;
|
|
109
|
+
const imageSize = this.adapter.config.deviceIconsSize || 120;
|
|
110
110
|
const z2mModel = device.definition.model || '';
|
|
111
111
|
const z2mIconFileNameJPG = `${this.sanitizeZ2MDeviceName(z2mModel)}.jpg`;
|
|
112
112
|
const z2mIconFileNamePNG = `${this.sanitizeZ2MDeviceName(z2mModel)}.png`;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Aedes = require('aedes');
|
|
4
4
|
const net = require('node:net');
|
|
5
|
+
const MemoryPersistence = require('aedes-persistence');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Verwaltet den internen oder Dummy-MQTT-Server (Aedes) für den Zigbee2MQTT-Adapter.
|
|
@@ -58,10 +59,9 @@ class MqttServerController {
|
|
|
58
59
|
*/
|
|
59
60
|
async createMQTTServer() {
|
|
60
61
|
if (!this._checkConfig()) {
|
|
61
|
-
return;
|
|
62
|
+
return false;
|
|
62
63
|
}
|
|
63
64
|
try {
|
|
64
|
-
const MemoryPersistence = require('aedes-persistence');
|
|
65
65
|
const db = MemoryPersistence();
|
|
66
66
|
const aedes = Aedes({ persistence: db });
|
|
67
67
|
this.aedesBroker = aedes;
|
|
@@ -82,10 +82,20 @@ class MqttServerController {
|
|
|
82
82
|
}
|
|
83
83
|
);
|
|
84
84
|
});
|
|
85
|
+
return true;
|
|
85
86
|
} catch (err) {
|
|
86
|
-
|
|
87
|
+
const code = err && err.code ? err.code : '';
|
|
88
|
+
if (code === 'EACCES' || code === 'EADDRINUSE') {
|
|
89
|
+
this.adapter.log.error(
|
|
90
|
+
`Port ${this.adapter.config.mqttServerPort} is already in use or requires admin privileges. ` +
|
|
91
|
+
`Please change the port in the adapter settings or stop the other service.`
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
this.adapter.log.error(`createMQTTServer error: ${err && err.message ? err.message : String(err)}`);
|
|
95
|
+
}
|
|
87
96
|
// Sicherstellen dass kein halboffener Server übrig bleibt
|
|
88
97
|
this.closeServer();
|
|
98
|
+
return false;
|
|
89
99
|
}
|
|
90
100
|
}
|
|
91
101
|
|
|
@@ -95,7 +105,7 @@ class MqttServerController {
|
|
|
95
105
|
*/
|
|
96
106
|
async createDummyMQTTServer() {
|
|
97
107
|
if (!this._checkConfig()) {
|
|
98
|
-
return;
|
|
108
|
+
return false;
|
|
99
109
|
}
|
|
100
110
|
try {
|
|
101
111
|
const aedes = Aedes();
|
|
@@ -117,9 +127,19 @@ class MqttServerController {
|
|
|
117
127
|
}
|
|
118
128
|
);
|
|
119
129
|
});
|
|
130
|
+
return true;
|
|
120
131
|
} catch (err) {
|
|
121
|
-
|
|
132
|
+
const code = err && err.code ? err.code : '';
|
|
133
|
+
if (code === 'EACCES' || code === 'EADDRINUSE') {
|
|
134
|
+
this.adapter.log.error(
|
|
135
|
+
`Port ${this.adapter.config.mqttServerPort} is already in use or requires admin privileges. ` +
|
|
136
|
+
`Please change the port in the adapter settings or stop the other service.`
|
|
137
|
+
);
|
|
138
|
+
} else {
|
|
139
|
+
this.adapter.log.error(`createDummyMQTTServer error: ${err && err.message ? err.message : String(err)}`);
|
|
140
|
+
}
|
|
122
141
|
this.closeServer();
|
|
142
|
+
return false;
|
|
123
143
|
}
|
|
124
144
|
}
|
|
125
145
|
|
package/lib/rgb.js
CHANGED
|
@@ -152,6 +152,11 @@ function rgb_to_cie(red, green, blue) {
|
|
|
152
152
|
* @param v
|
|
153
153
|
*/
|
|
154
154
|
function hsvToRGB(h, s, v) {
|
|
155
|
+
if (arguments.length === 1) {
|
|
156
|
+
s = h.s;
|
|
157
|
+
v = h.v;
|
|
158
|
+
h = h.h;
|
|
159
|
+
}
|
|
155
160
|
h = (h % 360) / 360;
|
|
156
161
|
s = s / 100;
|
|
157
162
|
v = v / 100;
|
|
@@ -159,11 +164,6 @@ function hsvToRGB(h, s, v) {
|
|
|
159
164
|
let r;
|
|
160
165
|
let g;
|
|
161
166
|
let b;
|
|
162
|
-
if (arguments.length === 1) {
|
|
163
|
-
s = h.s;
|
|
164
|
-
v = h.v;
|
|
165
|
-
h = h.h;
|
|
166
|
-
}
|
|
167
167
|
const i = Math.floor(h * 6);
|
|
168
168
|
const f = h * 6 - i;
|
|
169
169
|
const p = v * (1 - s);
|
package/lib/states.js
CHANGED
|
@@ -37,6 +37,17 @@ const nameLookup = {
|
|
|
37
37
|
Hz: 'frequency',
|
|
38
38
|
pf: 'power_factor',
|
|
39
39
|
lx: 'illuminance_lux',
|
|
40
|
+
'°C': 'temperature_celsius',
|
|
41
|
+
'°F': 'temperature_fahrenheit',
|
|
42
|
+
mA: 'milliampere',
|
|
43
|
+
mV: 'millivolt',
|
|
44
|
+
'μg/m³': 'microgram_per_cubic_meter',
|
|
45
|
+
dB: 'decibel',
|
|
46
|
+
'%rH': 'percent_rh',
|
|
47
|
+
'km/h': 'kilometer_per_hour',
|
|
48
|
+
'm/s': 'meter_per_second',
|
|
49
|
+
'mg/m³': 'milligram_per_cubic_meter',
|
|
50
|
+
K: 'kelvin',
|
|
40
51
|
};
|
|
41
52
|
|
|
42
53
|
const unitLookup = {
|
|
@@ -53,6 +64,17 @@ const unitLookup = {
|
|
|
53
64
|
frequency: 'Hz',
|
|
54
65
|
power_factor: 'pf',
|
|
55
66
|
illuminance_lux: 'lx',
|
|
67
|
+
temperature_celsius: '°C',
|
|
68
|
+
temperature_fahrenheit: '°F',
|
|
69
|
+
milliampere: 'mA',
|
|
70
|
+
millivolt: 'mV',
|
|
71
|
+
microgram_per_cubic_meter: 'μg/m³',
|
|
72
|
+
decibel: 'dB',
|
|
73
|
+
percent_rh: '%rH',
|
|
74
|
+
kilometer_per_hour: 'km/h',
|
|
75
|
+
meter_per_second: 'm/s',
|
|
76
|
+
milligram_per_cubic_meter: 'mg/m³',
|
|
77
|
+
kelvin: 'K',
|
|
56
78
|
};
|
|
57
79
|
|
|
58
80
|
const states = {
|
package/lib/statesController.js
CHANGED
|
@@ -116,6 +116,30 @@ class StatesController {
|
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// Fix 3: Vorab prüfen, ob überhaupt ein State des Devices existiert.
|
|
120
|
+
// Nur wenn KEIN EINZIGER State existiert → requeue.
|
|
121
|
+
// Wenn mindestens einer existiert → verarbeite vorhandene, ignoriere fehlende.
|
|
122
|
+
let hasAnyExistingState = false;
|
|
123
|
+
for (const key of Object.keys(messageObj.payload)) {
|
|
124
|
+
if (hasAnyExistingState) { break; }
|
|
125
|
+
let states = device.states.filter(state => state.prop && state.prop === key);
|
|
126
|
+
if (states.length === 0) { states = device.states.filter(x => x.id === key); }
|
|
127
|
+
for (const state of states) {
|
|
128
|
+
if (this.createCache[device.ieee_address]
|
|
129
|
+
&& this.createCache[device.ieee_address][state.id]
|
|
130
|
+
&& this.createCache[device.ieee_address][state.id].created === true) {
|
|
131
|
+
hasAnyExistingState = true;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!hasAnyExistingState) {
|
|
138
|
+
// Kein einziger State existiert → Nachricht in Queue stellen
|
|
139
|
+
pushToQueue(messageObj);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
119
143
|
for (let [key, value] of Object.entries(messageObj.payload)) {
|
|
120
144
|
if (value === undefined || value === null) {
|
|
121
145
|
continue;
|
|
@@ -166,15 +190,15 @@ class StatesController {
|
|
|
166
190
|
const stateName = `${device.ieee_address}.${state.id}`;
|
|
167
191
|
|
|
168
192
|
// set available status if last_seen is set
|
|
169
|
-
if (state.id === 'last_seen' && this.adapter.config.
|
|
193
|
+
if (state.id === 'last_seen' && this.adapter.config.alwaysUpdateAvailableState === true) {
|
|
170
194
|
await this.setStateSafelyAsync(`${device.ieee_address}.available`, true);
|
|
171
195
|
}
|
|
172
196
|
|
|
173
|
-
// State noch nicht erstellt? →
|
|
197
|
+
// State noch nicht erstellt? → überspringen (Requeue erfolgt nur,
|
|
198
|
+
// wenn GAR KEIN State des Devices existiert – siehe Pre-Check oben)
|
|
174
199
|
if (!this.createCache[device.ieee_address]
|
|
175
200
|
|| !this.createCache[device.ieee_address][state.id]
|
|
176
201
|
|| this.createCache[device.ieee_address][state.id].created !== true) {
|
|
177
|
-
pushToQueue(messageObj);
|
|
178
202
|
continue;
|
|
179
203
|
}
|
|
180
204
|
|
|
@@ -183,7 +207,7 @@ class StatesController {
|
|
|
183
207
|
if (state.prop && state.prop === 'action') {
|
|
184
208
|
actionStates.push(state);
|
|
185
209
|
}
|
|
186
|
-
else if (this.adapter.config.
|
|
210
|
+
else if (this.adapter.config.alwaysUpdateOccupancyState === true && state.id === 'occupancy' && value === true) {
|
|
187
211
|
await this.setStateSafelyAsync(stateName, value);
|
|
188
212
|
}
|
|
189
213
|
else {
|
|
@@ -264,22 +288,33 @@ class StatesController {
|
|
|
264
288
|
return;
|
|
265
289
|
}
|
|
266
290
|
|
|
267
|
-
|
|
291
|
+
// Prüfen, ob bereits ein Timer läuft (State ist dann schon auf true)
|
|
292
|
+
const wasAlreadyTrue = !!this.timeOutCache[stateName];
|
|
268
293
|
|
|
269
|
-
//
|
|
270
|
-
// Wenn value=false (z.B. Stop-Aktion im simpleMoveStopState-Modus),
|
|
271
|
-
// soll der State dauerhaft false bleiben und NICHT nach timeout auf true springen.
|
|
294
|
+
// Timer immer clearen, wenn vorhanden
|
|
272
295
|
if (this.timeOutCache[stateName]) {
|
|
273
296
|
this.adapter.clearTimeout(this.timeOutCache[stateName]);
|
|
274
297
|
delete this.timeOutCache[stateName];
|
|
275
298
|
}
|
|
299
|
+
|
|
276
300
|
if (value === true) {
|
|
301
|
+
// Nur setState aufrufen, wenn der State NICHT bereits auf true war.
|
|
302
|
+
// Bei Dauerfeuer (value=true→true schneller als timeout) spart das
|
|
303
|
+
// den unnötigen setState-Call und verhindert, dass der Timer
|
|
304
|
+
// nie ausgelöst wird (Timer-Leak).
|
|
305
|
+
if (!wasAlreadyTrue) {
|
|
306
|
+
await this.setStateSafelyAsync(stateName, true);
|
|
307
|
+
}
|
|
308
|
+
// Neuen Timer für Auto-Reset setzen
|
|
277
309
|
this.timeOutCache[stateName] = this.adapter.setTimeout(() => {
|
|
278
310
|
delete this.timeOutCache[stateName];
|
|
279
311
|
this.adapter.setState(stateName, false, true).catch((err) => {
|
|
280
312
|
this.adapter.log.debug(`setStateWithTimeout reset error for ${stateName}: ${err}`);
|
|
281
313
|
});
|
|
282
314
|
}, timeout);
|
|
315
|
+
} else {
|
|
316
|
+
// value === false: State setzen, Timer bereits gelöscht
|
|
317
|
+
await this.setStateSafelyAsync(stateName, false);
|
|
283
318
|
}
|
|
284
319
|
}
|
|
285
320
|
|
package/lib/z2mController.js
CHANGED
|
@@ -105,7 +105,15 @@ class Z2mController {
|
|
|
105
105
|
for (const option of deviceState.options) {
|
|
106
106
|
// Fix: "in"-Prüfung statt === undefined, damit null als gültiger gecachter Wert behandelt wird
|
|
107
107
|
if (!(option in device.optionsValues)) {
|
|
108
|
-
|
|
108
|
+
let optState;
|
|
109
|
+
try {
|
|
110
|
+
optState = await this.adapter.getStateAsync(`${splitedID[0]}.${splitedID[1]}.${splitedID[2]}.${option}`);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
this.adapter.log.warn(
|
|
113
|
+
`Cannot read state for option "${option}": ${e.message}`
|
|
114
|
+
);
|
|
115
|
+
optState = null;
|
|
116
|
+
}
|
|
109
117
|
device.optionsValues[option] = optState ? optState.val : null;
|
|
110
118
|
}
|
|
111
119
|
|
package/main.js
CHANGED
|
@@ -40,6 +40,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
40
40
|
this.websocketController = null;
|
|
41
41
|
this.mqttServerController = null;
|
|
42
42
|
this.messageParseMutex = Promise.resolve();
|
|
43
|
+
this.mqttReconnectAttempts = 0;
|
|
43
44
|
|
|
44
45
|
this.on('ready', () => {
|
|
45
46
|
this.onReady().catch((e) => this.log.error(`onReady error: ${e}`));
|
|
@@ -124,7 +125,12 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
124
125
|
} else {
|
|
125
126
|
// Internal MQTT-Server
|
|
126
127
|
this.mqttServerController = new MqttServerController(this);
|
|
127
|
-
await this.mqttServerController.createMQTTServer();
|
|
128
|
+
const serverStarted = await this.mqttServerController.createMQTTServer();
|
|
129
|
+
if (!serverStarted) {
|
|
130
|
+
this.log.error('Internal MQTT server could not be started. The adapter cannot connect to Zigbee2MQTT.');
|
|
131
|
+
this.setState('info.connection', false, true);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
128
134
|
// Kurze Pause damit der OS-Socket tatsächlich bereit ist (createMQTTServer wartet bereits auf listen)
|
|
129
135
|
await this.delay(200);
|
|
130
136
|
this.mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, {
|
|
@@ -137,6 +143,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
137
143
|
|
|
138
144
|
// MQTT Client Events
|
|
139
145
|
this.mqttClient.on('connect', () => {
|
|
146
|
+
this.mqttReconnectAttempts = 0;
|
|
140
147
|
this.log.info(
|
|
141
148
|
`Connect to Zigbee2MQTT over ${this.config.connectionType === 'exmqtt' ? 'external mqtt' : 'internal mqtt'} connection.`
|
|
142
149
|
);
|
|
@@ -155,7 +162,20 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
155
162
|
});
|
|
156
163
|
|
|
157
164
|
this.mqttClient.on('reconnect', () => {
|
|
158
|
-
this.
|
|
165
|
+
this.mqttReconnectAttempts++;
|
|
166
|
+
if (this.mqttReconnectAttempts > 20) {
|
|
167
|
+
this.log.error(
|
|
168
|
+
`MQTT client gave up after ${this.mqttReconnectAttempts} reconnect attempts (10 seconds). ` +
|
|
169
|
+
`Please check whether Zigbee2MQTT is running and the MQTT connection settings are correct.`
|
|
170
|
+
);
|
|
171
|
+
if (this.mqttClient) {
|
|
172
|
+
this.mqttClient.end(true);
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.log.info(
|
|
177
|
+
`MQTT client reconnecting to Zigbee2MQTT... (attempt ${this.mqttReconnectAttempts}/20)`
|
|
178
|
+
);
|
|
159
179
|
});
|
|
160
180
|
|
|
161
181
|
this.mqttClient.on('offline', async () => {
|
|
@@ -214,7 +234,12 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
214
234
|
|
|
215
235
|
if (this.config.dummyMqtt === true) {
|
|
216
236
|
this.mqttServerController = new MqttServerController(this);
|
|
217
|
-
await this.mqttServerController.createDummyMQTTServer();
|
|
237
|
+
const serverStarted = await this.mqttServerController.createDummyMQTTServer();
|
|
238
|
+
if (!serverStarted) {
|
|
239
|
+
this.log.error('Dummy MQTT server could not be started – WebSocket mode cannot continue.');
|
|
240
|
+
this.setState('info.connection', false, true);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
218
243
|
await this.delay(200);
|
|
219
244
|
}
|
|
220
245
|
|
|
@@ -527,7 +552,7 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
527
552
|
this.mqttClient.removeAllListeners();
|
|
528
553
|
this.mqttClient.end(true);
|
|
529
554
|
} catch (e) {
|
|
530
|
-
this.log.error(e);
|
|
555
|
+
this.log.error(`[onUnload] Fehler beim Schließen des MQTT-Clients: ${e.message}`);
|
|
531
556
|
}
|
|
532
557
|
}
|
|
533
558
|
}
|
|
@@ -537,15 +562,16 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
537
562
|
this.mqttServerController.closeServer();
|
|
538
563
|
}
|
|
539
564
|
} catch (e) {
|
|
540
|
-
this.log.error(e);
|
|
565
|
+
this.log.error(`[onUnload] Fehler beim Schließen des MQTT-Servers: ${e.message}`);
|
|
541
566
|
}
|
|
542
|
-
}
|
|
567
|
+
}
|
|
568
|
+
if (this.config.connectionType === 'ws') {
|
|
543
569
|
try {
|
|
544
570
|
if (this.websocketController) {
|
|
545
571
|
this.websocketController.closeConnection();
|
|
546
572
|
}
|
|
547
573
|
} catch (e) {
|
|
548
|
-
this.log.error(e);
|
|
574
|
+
this.log.error(`[onUnload] Fehler beim Schließen der WebSocket-Verbindung: ${e.message}`);
|
|
549
575
|
}
|
|
550
576
|
}
|
|
551
577
|
try {
|
|
@@ -553,21 +579,21 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
553
579
|
await this.statesController.setAllAvailableToFalse();
|
|
554
580
|
}
|
|
555
581
|
} catch (e) {
|
|
556
|
-
this.log.error(e);
|
|
582
|
+
this.log.error(`[onUnload] Fehler beim Setzen aller States auf false: ${e.message}`);
|
|
557
583
|
}
|
|
558
584
|
try {
|
|
559
585
|
if (this.websocketController) {
|
|
560
586
|
this.websocketController.allTimerClear();
|
|
561
587
|
}
|
|
562
588
|
} catch (e) {
|
|
563
|
-
this.log.error(e);
|
|
589
|
+
this.log.error(`[onUnload] Fehler beim Zurücksetzen der WebSocket-Timer: ${e.message}`);
|
|
564
590
|
}
|
|
565
591
|
try {
|
|
566
592
|
if (this.statesController) {
|
|
567
593
|
this.statesController.allTimerClear();
|
|
568
594
|
}
|
|
569
595
|
} catch (e) {
|
|
570
|
-
this.log.error(e);
|
|
596
|
+
this.log.error(`[onUnload] Fehler beim Zurücksetzen der States-Timer: ${e.message}`);
|
|
571
597
|
}
|
|
572
598
|
|
|
573
599
|
this.setState('info.connection', false, true);
|
|
@@ -595,14 +621,14 @@ class Zigbee2mqtt extends core.Adapter {
|
|
|
595
621
|
if (state && state.ack === false) {
|
|
596
622
|
if (id.endsWith('info.debugmessages')) {
|
|
597
623
|
this.logCustomizations.debugDevices = state.val != null ? String(state.val) : '';
|
|
598
|
-
this.setState(id, state.val, true);
|
|
624
|
+
await this.setState(id, state.val, true).catch(e => this.log.error(`[stateChange] Fehler beim Setzen von ${id}: ${e.message}`));
|
|
599
625
|
return;
|
|
600
626
|
}
|
|
601
627
|
if (id.endsWith('info.logfilter')) {
|
|
602
628
|
this.logCustomizations.logfilter = state.val != null
|
|
603
629
|
? String(state.val).split(';').filter((x) => x)
|
|
604
630
|
: [];
|
|
605
|
-
this.setState(id, state.val, true);
|
|
631
|
+
await this.setState(id, state.val, true).catch(e => this.log.error(`[stateChange] Fehler beim Setzen von ${id}: ${e.message}`));
|
|
606
632
|
return;
|
|
607
633
|
}
|
|
608
634
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.zigbee2mqtt",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.4",
|
|
4
4
|
"description": "Zigbee2MQTT adapter for ioBroker",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Dennis Rathjen and Arthur Rupp",
|
|
@@ -24,27 +24,27 @@
|
|
|
24
24
|
"node": ">= 22"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@iobroker/adapter-core": "^3.
|
|
27
|
+
"@iobroker/adapter-core": "^3.4.1",
|
|
28
28
|
"@iobroker/dm-utils": "^3.0.17",
|
|
29
29
|
"aedes": "^0.51.3",
|
|
30
30
|
"aedes-persistence": "^9.1.2",
|
|
31
|
-
"axios": "^1.
|
|
31
|
+
"axios": "^1.17.0",
|
|
32
32
|
"mqtt": "^5.15.1",
|
|
33
33
|
"net": "^1.0.2",
|
|
34
34
|
"node-schedule": "^2.1.1",
|
|
35
35
|
"sharp": "^0.34.5",
|
|
36
|
-
"ws": "^8.
|
|
36
|
+
"ws": "^8.21.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@alcalzone/release-script": "^5.2.
|
|
39
|
+
"@alcalzone/release-script": "^5.2.1",
|
|
40
40
|
"@alcalzone/release-script-plugin-iobroker": "^5.2.0",
|
|
41
41
|
"@alcalzone/release-script-plugin-license": "^5.2.0",
|
|
42
42
|
"@alcalzone/release-script-plugin-manual-review": "^5.2.0",
|
|
43
43
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
44
44
|
"@iobroker/eslint-config": "^2.3.4",
|
|
45
|
-
"@iobroker/testing": "^5.
|
|
46
|
-
"@tsconfig/
|
|
47
|
-
"@types/node": "^25.
|
|
45
|
+
"@iobroker/testing": "^5.2.2",
|
|
46
|
+
"@tsconfig/node22": "^22.0.5",
|
|
47
|
+
"@types/node": "^25.9.3",
|
|
48
48
|
"@types/node-schedule": "^2.1.8",
|
|
49
49
|
"typescript": "~6.0.3"
|
|
50
50
|
},
|