iobroker.nuki-local 0.1.2 → 0.1.3
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/LICENSE +1 -1
- package/README.md +24 -8
- package/admin/i18n/de.json +0 -1
- package/admin/i18n/en.json +0 -1
- package/admin/i18n/es.json +9 -10
- package/admin/i18n/fr.json +9 -10
- package/admin/i18n/it.json +9 -10
- package/admin/i18n/nl.json +9 -10
- package/admin/i18n/pl.json +9 -10
- package/admin/i18n/pt.json +9 -10
- package/admin/i18n/ru.json +9 -10
- package/admin/i18n/uk.json +9 -10
- package/admin/i18n/zh-cn.json +9 -10
- package/admin/nuki-local.png +0 -0
- package/build/main.js +69 -44
- package/build/main.js.map +2 -2
- package/io-package.json +18 -4
- package/package.json +5 -6
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 helfi9999 <
|
|
3
|
+
Copyright (c) 2026 helfi9999 <helfi9999@gmail.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Local Nuki Smart Lock integration for ioBroker using an integrated MQTT broker.
|
|
4
4
|
|
|
5
|
+
Manufacturer and product information: [Nuki](https://nuki.io/).
|
|
6
|
+
|
|
5
7
|
The adapter is designed for direct local communication with compatible Nuki Smart Locks over MQTT.
|
|
6
8
|
Optionally, the Nuki Web API can be enabled to enrich the local MQTT data with authorization names and activity information.
|
|
7
9
|
|
|
@@ -391,15 +393,27 @@ Restart:
|
|
|
391
393
|
iobroker restart nuki-local.0
|
|
392
394
|
```
|
|
393
395
|
|
|
394
|
-
##
|
|
396
|
+
## Changelog
|
|
397
|
+
### 0.1.3 (2026-10-04)
|
|
395
398
|
|
|
396
|
-
|
|
399
|
+
- (helfi9999) Replaced the default adapter icon with a custom Nuki icon.
|
|
400
|
+
- (helfi9999) Limited the Web API polling interval to 60–86400 seconds and prevented overlapping updates.
|
|
401
|
+
- (helfi9999) Corrected access and activity date roles and removed an unused translation key.
|
|
402
|
+
- (helfi9999) Reset the code ID when importing Web API activity data.
|
|
397
403
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
404
|
+
- (helfi9999) Changed state texts to English and completed configuration label translations.
|
|
405
|
+
- (helfi9999) Corrected command button, authorization JSON and timestamp roles.
|
|
406
|
+
- (helfi9999) Added a Web API request timeout and MQTT device ID validation.
|
|
407
|
+
- (helfi9999) Updated Aedes, Node.js types, testing tools and transitive dependencies.
|
|
408
|
+
- (helfi9999) Added Node.js 26 testing and updated the workflow check action.
|
|
409
|
+
- (helfi9999) Updated documentation, keywords and maintainer contact information.
|
|
410
|
+
- (helfi9999) Adapter requires admin >= 7.8.23 now.
|
|
401
411
|
|
|
402
|
-
|
|
412
|
+
### 0.1.2
|
|
413
|
+
|
|
414
|
+
- Improved publishing workflow
|
|
415
|
+
- Added npm trusted publishing support
|
|
416
|
+
- Updated package metadata and repository checks
|
|
403
417
|
|
|
404
418
|
### 0.1.0
|
|
405
419
|
|
|
@@ -420,8 +434,10 @@ Initial functional development version.
|
|
|
420
434
|
- Activity information
|
|
421
435
|
- Dynamic status icons
|
|
422
436
|
|
|
437
|
+
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
|
438
|
+
|
|
423
439
|
## License
|
|
424
440
|
|
|
425
|
-
MIT License
|
|
441
|
+
[MIT License](LICENSE)
|
|
426
442
|
|
|
427
|
-
Copyright (c) 2026 helfi9999
|
|
443
|
+
Copyright (c) 2026 helfi9999 <helfi9999@gmail.com>
|
package/admin/i18n/de.json
CHANGED
package/admin/i18n/en.json
CHANGED
package/admin/i18n/es.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "Puerto MQTT",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Puerto utilizado por el broker MQTT integrado",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "Nombre de usuario MQTT",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Nombre de usuario utilizado por la cerradura Nuki Smart Lock",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "Contraseña MQTT",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Contraseña utilizada por la cerradura Nuki Smart Lock",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Activar la API web de Nuki",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Habilitar datos adicionales de la nube como usuarios, autorizaciones y registros de actividad",
|
|
11
|
-
"Nuki Web API token": "
|
|
10
|
+
"Nuki Web API token": "Token de la API web de Nuki",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Token API personal utilizado para acceder a la API web de Nuki",
|
|
13
|
-
"Web API update interval": "
|
|
12
|
+
"Web API update interval": "Intervalo de actualización de la API web",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Intervalo en segundos para actualizar los datos de la API web de Nuki",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Usuarios del teclado / de huella dactilar",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Asigne los ID de código de Nuki a nombres de usuario. El ID de código se encuentra en el mensaje MQTT lockActionEvent.",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "ID del código",
|
|
17
|
+
"Name": "Nombre"
|
|
19
18
|
}
|
package/admin/i18n/fr.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "Port MQTT",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Port utilisé par le broker MQTT intégré",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "Nom d’utilisateur MQTT",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Nom d’utilisateur utilisé par la serrure Nuki Smart Lock",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "Mot de passe MQTT",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Mot de passe utilisé par la serrure Nuki Smart Lock",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Activer l’API Web Nuki",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Activer des données cloud supplémentaires comme les utilisateurs, les autorisations et les journaux d’activité",
|
|
11
|
-
"Nuki Web API token": "
|
|
10
|
+
"Nuki Web API token": "Jeton de l’API Web Nuki",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Jeton API personnel utilisé pour accéder à l’API Web Nuki",
|
|
13
|
-
"Web API update interval": "
|
|
12
|
+
"Web API update interval": "Intervalle de mise à jour de l’API Web",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Intervalle en secondes pour la mise à jour des données de l’API Web Nuki",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Utilisateurs du clavier / des empreintes digitales",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Associez les identifiants de code Nuki aux noms d’utilisateur. L’identifiant de code se trouve dans le message MQTT lockActionEvent.",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "Identifiant du code",
|
|
17
|
+
"Name": "Nom"
|
|
19
18
|
}
|
package/admin/i18n/it.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "Porta MQTT",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Porta utilizzata dal broker MQTT integrato",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "Nome utente MQTT",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Nome utente utilizzato dalla serratura Nuki Smart Lock",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "Password MQTT",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Password utilizzata dalla serratura Nuki Smart Lock",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Abilita l’API web Nuki",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Abilita dati cloud aggiuntivi come utenti, autorizzazioni e registri attività",
|
|
11
|
-
"Nuki Web API token": "
|
|
10
|
+
"Nuki Web API token": "Token dell’API web Nuki",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Token API personale utilizzato per accedere alla Nuki Web API",
|
|
13
|
-
"Web API update interval": "
|
|
12
|
+
"Web API update interval": "Intervallo di aggiornamento dell’API web",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Intervallo in secondi per aggiornare i dati della Nuki Web API",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Utenti del tastierino / delle impronte digitali",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Assegna gli ID codice Nuki ai nomi utente. L’ID codice è disponibile nel messaggio MQTT lockActionEvent.",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "ID del codice",
|
|
17
|
+
"Name": "Nome"
|
|
19
18
|
}
|
package/admin/i18n/nl.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "MQTT-poort",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Poort die door de geïntegreerde MQTT-broker wordt gebruikt",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "MQTT-gebruikersnaam",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Gebruikersnaam die door het Nuki Smart Lock wordt gebruikt",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "MQTT-wachtwoord",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Wachtwoord dat door het Nuki Smart Lock wordt gebruikt",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Nuki Web API inschakelen",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Aanvullende cloudgegevens inschakelen, zoals gebruikers, autorisaties en activiteitenlogboeken",
|
|
11
|
-
"Nuki Web API token": "Nuki Web API
|
|
10
|
+
"Nuki Web API token": "Nuki Web API-token",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Persoonlijk API-token voor toegang tot de Nuki Web API",
|
|
13
|
-
"Web API update interval": "Web API
|
|
12
|
+
"Web API update interval": "Update-interval voor de Web API",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Interval in seconden voor het bijwerken van Nuki Web API-gegevens",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Gebruikers van toetsenblok / vingerafdruk",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Koppel Nuki-code-ID's aan gebruikersnamen. De code-ID staat in het MQTT-bericht lockActionEvent.",
|
|
17
|
-
"Code ID": "Code
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "Code-ID",
|
|
17
|
+
"Name": "Naam"
|
|
19
18
|
}
|
package/admin/i18n/pl.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "Port MQTT",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Port używany przez zintegrowany broker MQTT",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "Nazwa użytkownika MQTT",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Nazwa użytkownika używana przez zamek Nuki Smart Lock",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "Hasło MQTT",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Hasło używane przez zamek Nuki Smart Lock",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Włącz interfejs API Nuki",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Włącz dodatkowe dane z chmury, takie jak użytkownicy, uprawnienia i dzienniki aktywności",
|
|
11
|
-
"Nuki Web API token": "
|
|
10
|
+
"Nuki Web API token": "Token interfejsu API Nuki",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Osobisty token API używany do dostępu do Nuki Web API",
|
|
13
|
-
"Web API update interval": "
|
|
12
|
+
"Web API update interval": "Interwał aktualizacji interfejsu API",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Interwał w sekundach dla aktualizacji danych Nuki Web API",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Użytkownicy klawiatury / odcisków palców",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Przypisz identyfikatory kodów Nuki do nazw użytkowników. Identyfikator kodu znajduje się w wiadomości MQTT lockActionEvent.",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "Identyfikator kodu",
|
|
17
|
+
"Name": "Nazwa"
|
|
19
18
|
}
|
package/admin/i18n/pt.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "Porta MQTT",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Porta utilizada pelo broker MQTT integrado",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "Nome de utilizador MQTT",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Nome de utilizador usado pela fechadura Nuki Smart Lock",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "Palavra-passe MQTT",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Palavra-passe usada pela fechadura Nuki Smart Lock",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Ativar a API Web da Nuki",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Ativar dados adicionais da cloud, como utilizadores, autorizações e registos de atividade",
|
|
11
|
-
"Nuki Web API token": "
|
|
10
|
+
"Nuki Web API token": "Token da API Web da Nuki",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Token API pessoal utilizado para aceder à Nuki Web API",
|
|
13
|
-
"Web API update interval": "
|
|
12
|
+
"Web API update interval": "Intervalo de atualização da API Web",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Intervalo em segundos para atualizar os dados da Nuki Web API",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Utilizadores do teclado / de impressão digital",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Associe IDs de código Nuki a nomes de utilizador. O ID do código pode ser encontrado na mensagem MQTT lockActionEvent.",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "ID do código",
|
|
17
|
+
"Name": "Nome"
|
|
19
18
|
}
|
package/admin/i18n/ru.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "Порт MQTT",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Порт, используемый встроенным MQTT-брокером",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "Имя пользователя MQTT",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Имя пользователя, используемое замком Nuki Smart Lock",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "Пароль MQTT",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Пароль, используемый замком Nuki Smart Lock",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Включить веб-API Nuki",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Включить дополнительные облачные данные, такие как пользователи, авторизации и журналы активности",
|
|
11
|
-
"Nuki Web API token": "
|
|
10
|
+
"Nuki Web API token": "Токен веб-API Nuki",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Персональный API-токен для доступа к Nuki Web API",
|
|
13
|
-
"Web API update interval": "
|
|
12
|
+
"Web API update interval": "Интервал обновления веб-API",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Интервал в секундах для обновления данных Nuki Web API",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Пользователи клавиатуры / отпечатков пальцев",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Назначьте идентификаторы кодов Nuki именам пользователей. ID кода можно найти в MQTT-сообщении lockActionEvent.",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "Идентификатор кода",
|
|
17
|
+
"Name": "Имя"
|
|
19
18
|
}
|
package/admin/i18n/uk.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "Порт MQTT",
|
|
4
3
|
"Port used by the integrated MQTT broker": "Порт, який використовується вбудованим MQTT-брокером",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "Ім’я користувача MQTT",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Ім’я користувача, яке використовується замком Nuki Smart Lock",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "Пароль MQTT",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Пароль, який використовується замком Nuki Smart Lock",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "Увімкнути веб-API Nuki",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "Увімкнути додаткові хмарні дані, такі як користувачі, авторизації та журнали активності",
|
|
11
|
-
"Nuki Web API token": "
|
|
10
|
+
"Nuki Web API token": "Токен веб-API Nuki",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "Персональний API-токен для доступу до Nuki Web API",
|
|
13
|
-
"Web API update interval": "
|
|
12
|
+
"Web API update interval": "Інтервал оновлення веб-API",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "Інтервал у секундах для оновлення даних Nuki Web API",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "Користувачі клавіатури / відбитків пальців",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "Призначте ID кодів Nuki іменам користувачів. ID коду можна знайти в MQTT-повідомленні lockActionEvent.",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "Ідентифікатор коду",
|
|
17
|
+
"Name": "Ім’я"
|
|
19
18
|
}
|
package/admin/i18n/zh-cn.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"MQTT port": "MQTT port",
|
|
2
|
+
"MQTT port": "MQTT 端口",
|
|
4
3
|
"Port used by the integrated MQTT broker": "集成 MQTT 代理使用的端口",
|
|
5
|
-
"MQTT username": "MQTT
|
|
4
|
+
"MQTT username": "MQTT 用户名",
|
|
6
5
|
"Username used by the Nuki Smart Lock": "Nuki 智能锁使用的用户名",
|
|
7
|
-
"MQTT password": "MQTT
|
|
6
|
+
"MQTT password": "MQTT 密码",
|
|
8
7
|
"Password used by the Nuki Smart Lock": "Nuki 智能锁使用的密码",
|
|
9
|
-
"Enable Nuki Web API": "
|
|
8
|
+
"Enable Nuki Web API": "启用 Nuki Web API",
|
|
10
9
|
"Enable additional cloud data such as users, authorizations and activity logs": "启用额外的云端数据,例如用户、授权和活动日志",
|
|
11
|
-
"Nuki Web API token": "Nuki Web API
|
|
10
|
+
"Nuki Web API token": "Nuki Web API 令牌",
|
|
12
11
|
"Personal API token used to access the Nuki Web API": "用于访问 Nuki Web API 的个人 API 令牌",
|
|
13
|
-
"Web API update interval": "Web API
|
|
12
|
+
"Web API update interval": "Web API 更新间隔",
|
|
14
13
|
"Interval in seconds for updating Nuki Web API data": "更新 Nuki Web API 数据的时间间隔(秒)",
|
|
15
|
-
"Keypad / Fingerprint users": "
|
|
14
|
+
"Keypad / Fingerprint users": "键盘 / 指纹用户",
|
|
16
15
|
"Assign Nuki Code IDs to user names. The Code ID can be found in the lockActionEvent MQTT message.": "将 Nuki Code ID 分配给用户名。Code ID 可在 MQTT 消息 lockActionEvent 中找到。",
|
|
17
|
-
"Code ID": "
|
|
18
|
-
"Name": "
|
|
16
|
+
"Code ID": "代码 ID",
|
|
17
|
+
"Name": "名称"
|
|
19
18
|
}
|
package/admin/nuki-local.png
CHANGED
|
Binary file
|
package/build/main.js
CHANGED
|
@@ -40,37 +40,37 @@ const MODES = {
|
|
|
40
40
|
3: "Continuous mode"
|
|
41
41
|
};
|
|
42
42
|
const LOCK_STATES = {
|
|
43
|
-
0: "
|
|
44
|
-
1: "
|
|
45
|
-
2: "
|
|
46
|
-
3: "
|
|
47
|
-
4: "
|
|
48
|
-
5: "
|
|
49
|
-
6: "
|
|
50
|
-
7: "
|
|
43
|
+
0: "Uncalibrated",
|
|
44
|
+
1: "Locked",
|
|
45
|
+
2: "Unlocking",
|
|
46
|
+
3: "Unlocked",
|
|
47
|
+
4: "Locking",
|
|
48
|
+
5: "Unlatched",
|
|
49
|
+
6: "Unlocked (Lock'n'Go)",
|
|
50
|
+
7: "Unlatching",
|
|
51
51
|
253: "Boot run",
|
|
52
|
-
254: "Motor
|
|
53
|
-
255: "
|
|
52
|
+
254: "Motor blocked",
|
|
53
|
+
255: "Unknown state"
|
|
54
54
|
};
|
|
55
55
|
const DOOR_STATES = {
|
|
56
|
-
1: "
|
|
57
|
-
2: "
|
|
58
|
-
3: "
|
|
59
|
-
4: "
|
|
60
|
-
5: "
|
|
61
|
-
16: "
|
|
62
|
-
240: "
|
|
63
|
-
255: "
|
|
56
|
+
1: "Door sensor disabled",
|
|
57
|
+
2: "Door closed",
|
|
58
|
+
3: "Door open",
|
|
59
|
+
4: "Door state unknown",
|
|
60
|
+
5: "Door sensor calibrating",
|
|
61
|
+
16: "Door sensor uncalibrated",
|
|
62
|
+
240: "Door sensor tampered",
|
|
63
|
+
255: "Unknown"
|
|
64
64
|
};
|
|
65
65
|
const LOCK_ACTIONS = {
|
|
66
|
-
1: "
|
|
67
|
-
2: "
|
|
68
|
-
3: "
|
|
66
|
+
1: "Unlock",
|
|
67
|
+
2: "Lock",
|
|
68
|
+
3: "Unlatch",
|
|
69
69
|
4: "Lock'n'Go",
|
|
70
|
-
5: "Lock'n'Go
|
|
71
|
-
6: "
|
|
72
|
-
80: "Fob
|
|
73
|
-
90: "Button
|
|
70
|
+
5: "Lock'n'Go with unlatch",
|
|
71
|
+
6: "Full lock",
|
|
72
|
+
80: "Fob without action",
|
|
73
|
+
90: "Button without action"
|
|
74
74
|
};
|
|
75
75
|
const NUKI_ICONS = {
|
|
76
76
|
locked: "/adapter/nuki-local/icons/Nuki_Vis/nuki_locked.png",
|
|
@@ -87,6 +87,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
87
87
|
mqttDatabase = null;
|
|
88
88
|
mqttPersistence = null;
|
|
89
89
|
webApiTimer = null;
|
|
90
|
+
webApiStopping = false;
|
|
90
91
|
initializedDevices = /* @__PURE__ */ new Set();
|
|
91
92
|
initializingDevices = /* @__PURE__ */ new Map();
|
|
92
93
|
webSmartlockIds = /* @__PURE__ */ new Map();
|
|
@@ -236,6 +237,10 @@ class NukiLocal extends utils.Adapter {
|
|
|
236
237
|
if (!deviceId || !property) {
|
|
237
238
|
return;
|
|
238
239
|
}
|
|
240
|
+
if (this.sanitizeId(deviceId) !== deviceId) {
|
|
241
|
+
this.log.debug("Ignoring MQTT message with invalid device ID");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
239
244
|
await this.ensureDevice(deviceId);
|
|
240
245
|
switch (property) {
|
|
241
246
|
case "name":
|
|
@@ -260,7 +265,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
260
265
|
await this.setBooleanState(`${deviceId}.device.online`, "Online", payload, "indicator.connected");
|
|
261
266
|
break;
|
|
262
267
|
case "timestamp":
|
|
263
|
-
await this.setStringState(`${deviceId}.status.timestamp`, "Timestamp", payload);
|
|
268
|
+
await this.setStringState(`${deviceId}.status.timestamp`, "Timestamp", payload, "date");
|
|
264
269
|
break;
|
|
265
270
|
case "batteryChargeState":
|
|
266
271
|
await this.setNumberState(`${deviceId}.battery.percent`, "Battery", payload, "%", "value.battery");
|
|
@@ -381,7 +386,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
381
386
|
await this.ensureMappedStateObject(`${deviceId}.status.doorState`, "Door state", DOOR_STATES);
|
|
382
387
|
await this.ensureStateObject(`${deviceId}.status.doorStateText`, "Door state text", "string", "text");
|
|
383
388
|
await this.ensureStateObject(`${deviceId}.status.doorOpen`, "Door open", "boolean", "sensor.door");
|
|
384
|
-
await this.ensureStateObject(`${deviceId}.status.timestamp`, "Timestamp", "string", "
|
|
389
|
+
await this.ensureStateObject(`${deviceId}.status.timestamp`, "Timestamp", "string", "date");
|
|
385
390
|
await this.ensureStateObject(`${deviceId}.status.iconState`, "Icon state", "string", "text");
|
|
386
391
|
await this.ensureStateObject(`${deviceId}.status.icon`, "Icon", "string", "text");
|
|
387
392
|
await this.ensureStateObject(`${deviceId}.battery.percent`, "Battery", "number", "value.battery", false, "%");
|
|
@@ -406,11 +411,11 @@ class NukiLocal extends utils.Adapter {
|
|
|
406
411
|
);
|
|
407
412
|
await this.ensureStateObject(`${deviceId}.keypad.lastType`, "Last access type", "string", "text");
|
|
408
413
|
await this.ensureStateObject(`${deviceId}.keypad.lastUser`, "Last user", "string", "text");
|
|
409
|
-
await this.ensureStateObject(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", "string", "
|
|
414
|
+
await this.ensureStateObject(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", "string", "date");
|
|
410
415
|
await this.ensureStateObject(`${deviceId}.activity.lastAction`, "Last action", "number", "value");
|
|
411
416
|
await this.ensureStateObject(`${deviceId}.activity.lastActionText`, "Last action text", "string", "text");
|
|
412
417
|
await this.ensureStateObject(`${deviceId}.activity.lastUser`, "Last user", "string", "text");
|
|
413
|
-
await this.ensureStateObject(`${deviceId}.activity.lastDate`, "Last date", "string", "
|
|
418
|
+
await this.ensureStateObject(`${deviceId}.activity.lastDate`, "Last date", "string", "date");
|
|
414
419
|
await this.ensureStateObject(`${deviceId}.device.name`, "Name", "string", "text");
|
|
415
420
|
await this.ensureStateObject(`${deviceId}.device.firmware`, "Firmware", "string", "text");
|
|
416
421
|
await this.ensureMappedStateObject(`${deviceId}.device.deviceType`, "Device type", DEVICE_TYPES);
|
|
@@ -640,20 +645,20 @@ class NukiLocal extends utils.Adapter {
|
|
|
640
645
|
"Last action text",
|
|
641
646
|
this.getMappedText(LOCK_ACTIONS, action)
|
|
642
647
|
);
|
|
643
|
-
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", now);
|
|
648
|
+
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", now, "date");
|
|
644
649
|
await this.setStateAsync(`${deviceId}.advanced.authId`, authId, true);
|
|
645
650
|
await this.setStateAsync(`${deviceId}.advanced.codeId`, codeId, true);
|
|
646
651
|
await this.setStateAsync(`${deviceId}.advanced.source`, source, true);
|
|
647
652
|
await this.setStateAsync(`${deviceId}.advanced.trigger`, trigger, true);
|
|
648
653
|
const configuredKeypadUser = codeId > 0 ? this.findConfiguredKeypadUser(codeId) : void 0;
|
|
649
654
|
const resolvedUserName = this.findAuthorizationName(deviceId, authId);
|
|
650
|
-
const displayUser = (_a = configuredKeypadUser != null ? configuredKeypadUser : resolvedUserName) != null ? _a : codeId > 0 ? `
|
|
655
|
+
const displayUser = (_a = configuredKeypadUser != null ? configuredKeypadUser : resolvedUserName) != null ? _a : codeId > 0 ? `Unknown (Code-ID ${codeId})` : `Unknown (Auth-ID ${authId})`;
|
|
651
656
|
await this.setStringState(`${deviceId}.activity.lastUser`, "Last user", displayUser);
|
|
652
657
|
if (codeId > 0) {
|
|
653
|
-
let type = `
|
|
658
|
+
let type = `Source ${source}`;
|
|
654
659
|
switch (source) {
|
|
655
660
|
case 0:
|
|
656
|
-
type = "Back
|
|
661
|
+
type = "Back button";
|
|
657
662
|
break;
|
|
658
663
|
case 1:
|
|
659
664
|
type = "Code";
|
|
@@ -664,7 +669,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
664
669
|
}
|
|
665
670
|
await this.setStringState(`${deviceId}.keypad.lastType`, "Last access type", type);
|
|
666
671
|
await this.setStringState(`${deviceId}.keypad.lastUser`, "Last user", displayUser);
|
|
667
|
-
await this.setStringState(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", now);
|
|
672
|
+
await this.setStringState(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", now, "date");
|
|
668
673
|
if (source === 2) {
|
|
669
674
|
this.log.info(`Fingerprint detected: authId=${authId}, codeId=${codeId}, user=${displayUser}`);
|
|
670
675
|
} else if (source === 1) {
|
|
@@ -704,7 +709,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
704
709
|
name,
|
|
705
710
|
type: "boolean",
|
|
706
711
|
role: "button",
|
|
707
|
-
read:
|
|
712
|
+
read: false,
|
|
708
713
|
write: true,
|
|
709
714
|
def: false
|
|
710
715
|
},
|
|
@@ -795,7 +800,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
795
800
|
await this.setStringState(
|
|
796
801
|
`${deviceId}.commands.lastResultText`,
|
|
797
802
|
"Last command result text",
|
|
798
|
-
result === 0 ? "
|
|
803
|
+
result === 0 ? "Successful" : `Error ${result}`
|
|
799
804
|
);
|
|
800
805
|
}
|
|
801
806
|
/*
|
|
@@ -810,7 +815,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
810
815
|
this.log.warn("Nuki Web API is enabled but no API token is configured");
|
|
811
816
|
return;
|
|
812
817
|
}
|
|
813
|
-
const intervalSeconds = Math.max(60, Number(this.config.webApiInterval) || 300);
|
|
818
|
+
const intervalSeconds = Math.min(86400, Math.max(60, Number(this.config.webApiInterval) || 300));
|
|
814
819
|
this.log.info(`Nuki Web API enabled, update interval: ${intervalSeconds} seconds`);
|
|
815
820
|
try {
|
|
816
821
|
await this.updateNukiWebData();
|
|
@@ -818,10 +823,22 @@ class NukiLocal extends utils.Adapter {
|
|
|
818
823
|
const message = error instanceof Error ? error.message : String(error);
|
|
819
824
|
this.log.warn(`Initial Nuki Web API update failed: ${message}`);
|
|
820
825
|
}
|
|
821
|
-
this.
|
|
826
|
+
this.scheduleWebApiUpdate(intervalSeconds);
|
|
827
|
+
}
|
|
828
|
+
scheduleWebApiUpdate(intervalSeconds) {
|
|
829
|
+
if (this.webApiStopping) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
this.webApiTimer = this.setTimeout(() => {
|
|
833
|
+
this.webApiTimer = null;
|
|
834
|
+
if (this.webApiStopping) {
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
822
837
|
void this.updateNukiWebData().catch((error) => {
|
|
823
838
|
const message = error instanceof Error ? error.message : String(error);
|
|
824
839
|
this.log.warn(`Nuki Web API update failed: ${message}`);
|
|
840
|
+
}).finally(() => {
|
|
841
|
+
this.scheduleWebApiUpdate(intervalSeconds);
|
|
825
842
|
});
|
|
826
843
|
}, intervalSeconds * 1e3);
|
|
827
844
|
}
|
|
@@ -873,6 +890,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
873
890
|
throw new Error("Nuki Web API token missing");
|
|
874
891
|
}
|
|
875
892
|
const response = await fetch(`https://api.nuki.io${path}`, {
|
|
893
|
+
signal: AbortSignal.timeout(15e3),
|
|
876
894
|
method: "GET",
|
|
877
895
|
headers: {
|
|
878
896
|
Accept: "application/json",
|
|
@@ -981,7 +999,12 @@ class NukiLocal extends utils.Adapter {
|
|
|
981
999
|
enabled: auth.enabled,
|
|
982
1000
|
remoteAllowed: auth.remoteAllowed
|
|
983
1001
|
}));
|
|
984
|
-
await this.setStringState(
|
|
1002
|
+
await this.setStringState(
|
|
1003
|
+
`${deviceId}.advanced.authorizations`,
|
|
1004
|
+
"Authorizations",
|
|
1005
|
+
JSON.stringify(safe),
|
|
1006
|
+
"json"
|
|
1007
|
+
);
|
|
985
1008
|
}
|
|
986
1009
|
async storeWebLogs(deviceId, logs) {
|
|
987
1010
|
if (logs.length === 0) {
|
|
@@ -1009,8 +1032,9 @@ class NukiLocal extends utils.Adapter {
|
|
|
1009
1032
|
await this.setStringState(`${deviceId}.activity.lastUser`, "Last user", last.name);
|
|
1010
1033
|
}
|
|
1011
1034
|
if (typeof last.date === "string") {
|
|
1012
|
-
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", last.date);
|
|
1035
|
+
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", last.date, "date");
|
|
1013
1036
|
}
|
|
1037
|
+
await this.setStateAsync(`${deviceId}.advanced.codeId`, 0, true);
|
|
1014
1038
|
if (typeof last.authId === "number") {
|
|
1015
1039
|
await this.setStateAsync(`${deviceId}.advanced.authId`, last.authId, true);
|
|
1016
1040
|
}
|
|
@@ -1022,7 +1046,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
1022
1046
|
*/
|
|
1023
1047
|
getMappedText(map, value) {
|
|
1024
1048
|
var _a;
|
|
1025
|
-
return (_a = map[value]) != null ? _a : `
|
|
1049
|
+
return (_a = map[value]) != null ? _a : `Unknown (${value})`;
|
|
1026
1050
|
}
|
|
1027
1051
|
async ensureStateObject(id, name, type, role, write = false, unit) {
|
|
1028
1052
|
await this.extendObjectAsync(id, {
|
|
@@ -1058,8 +1082,8 @@ class NukiLocal extends utils.Adapter {
|
|
|
1058
1082
|
native: {}
|
|
1059
1083
|
});
|
|
1060
1084
|
}
|
|
1061
|
-
async setStringState(id, name, value) {
|
|
1062
|
-
await this.ensureStateObject(id, name, "string",
|
|
1085
|
+
async setStringState(id, name, value, role = "text") {
|
|
1086
|
+
await this.ensureStateObject(id, name, "string", role);
|
|
1063
1087
|
await this.setStateAsync(id, value, true);
|
|
1064
1088
|
}
|
|
1065
1089
|
async setNumberState(id, name, value, unit, role = "value") {
|
|
@@ -1092,10 +1116,11 @@ class NukiLocal extends utils.Adapter {
|
|
|
1092
1116
|
* ============================================================
|
|
1093
1117
|
*/
|
|
1094
1118
|
onUnload(callback) {
|
|
1119
|
+
this.webApiStopping = true;
|
|
1095
1120
|
void (async () => {
|
|
1096
1121
|
try {
|
|
1097
1122
|
if (this.webApiTimer) {
|
|
1098
|
-
|
|
1123
|
+
this.clearTimeout(this.webApiTimer);
|
|
1099
1124
|
this.webApiTimer = null;
|
|
1100
1125
|
}
|
|
1101
1126
|
await this.setStateAsync("info.connection", false, true);
|
package/build/main.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { createServer, type Server } from \"node:net\";\nimport { join } from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { Level } from \"level\";\nimport { Aedes, type Aedes as AedesInstance } from \"aedes\";\nimport type { AedesPersistence } from \"aedes-persistence\";\n\nimport aedesPersistenceLevel from \"aedes-persistence-level\";\n\nconst DEVICE_TYPES: Record<number, string> = {\n 0: \"Smart Lock\",\n 2: \"Opener\",\n 3: \"Smart Door\",\n 4: \"Smart Lock 3.0 / 4th Generation\",\n 5: \"Smart Lock Ultra / 5th Generation / Go\",\n};\n\nconst MODES: Record<number, string> = {\n 2: \"Door mode\",\n 3: \"Continuous mode\",\n};\n\nconst LOCK_STATES: Record<number, string> = {\n 0: \"Nicht kalibriert\",\n 1: \"Zugesperrt\",\n 2: \"Wird aufgesperrt\",\n 3: \"Aufgesperrt\",\n 4: \"Wird zugesperrt\",\n 5: \"Falle gezogen\",\n 6: \"Aufgesperrt (Lock'n'Go)\",\n 7: \"Falle wird gezogen\",\n 253: \"Boot run\",\n 254: \"Motor blockiert\",\n 255: \"Unbekannter Zustand\",\n};\n\nconst DOOR_STATES: Record<number, string> = {\n 1: \"T\u00FCrsensor deaktiviert\",\n 2: \"T\u00FCr geschlossen\",\n 3: \"T\u00FCr ge\u00F6ffnet\",\n 4: \"T\u00FCrstatus unbekannt\",\n 5: \"T\u00FCrsensor wird kalibriert\",\n 16: \"T\u00FCrsensor nicht kalibriert\",\n 240: \"T\u00FCrsensor manipuliert\",\n 255: \"Unbekannt\",\n};\n\nconst LOCK_ACTIONS: Record<number, string> = {\n 1: \"Aufsperren\",\n 2: \"Zusperren\",\n 3: \"Falle ziehen\",\n 4: \"Lock'n'Go\",\n 5: \"Lock'n'Go mit Falle ziehen\",\n 6: \"Vollst\u00E4ndig zusperren\",\n 80: \"Fob ohne Aktion\",\n 90: \"Button ohne Aktion\",\n};\n\nconst NUKI_ICONS = {\n locked: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_locked.png\",\n unlocked: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_unlocked.png\",\n doorOpen: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_door_open.png\",\n doorClosed: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_door_closed.png\",\n charging: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_charging.png\",\n pairing: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_pairing.png\",\n unknown: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_unknown.png\",\n} as const;\n\ntype NukiIconState = \"locked\" | \"unlocked\" | \"door_open\" | \"door_closed\" | \"charging\" | \"pairing\" | \"unknown\";\n\ninterface NukiWebState {\n mode?: number;\n state?: number;\n trigger?: number;\n lastAction?: number;\n batteryCritical?: boolean;\n batteryCharging?: boolean;\n batteryCharge?: number;\n keypadBatteryCritical?: boolean;\n doorsensorBatteryCritical?: boolean;\n doorState?: number;\n}\n\ninterface NukiWebDevice {\n smartlockId: number;\n type?: number;\n name?: string;\n serverState?: number;\n state?: NukiWebState;\n}\n\ninterface NukiWebAuthorization {\n id?: string;\n smartlockId?: number;\n authId?: number;\n code?: number;\n type?: number;\n name?: string;\n enabled?: boolean;\n remoteAllowed?: boolean;\n}\n\ninterface NukiWebLog {\n id?: string;\n smartlockId?: number;\n authId?: number;\n name?: string;\n action?: number;\n state?: number;\n trigger?: number;\n date?: string;\n}\n\nclass NukiLocal extends utils.Adapter {\n private mqttBroker: AedesInstance | null = null;\n private mqttServer: Server | null = null;\n private mqttDatabase: Level | null = null;\n\n private mqttPersistence: AedesPersistence | null = null;\n\n private webApiTimer: ioBroker.Interval | undefined | null = null;\n\n private readonly initializedDevices = new Set<string>();\n\n private readonly initializingDevices = new Map<string, Promise<void>>();\n\n private readonly webSmartlockIds = new Map<string, number>();\n\n private readonly authorizationNames = new Map<number, Map<number, string>>();\n\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"nuki-local\",\n });\n\n this.on(\"ready\", this.onReady.bind(this));\n\n this.on(\"stateChange\", (id, state) => {\n void this.onStateChange(id, state);\n });\n\n this.on(\"unload\", this.onUnload.bind(this));\n }\n\n private async onReady(): Promise<void> {\n await this.ensureInstanceObjects();\n\n await this.setStateAsync(\"info.connection\", false, true);\n\n this.subscribeStates(\"*\");\n\n try {\n await this.startMqttBroker();\n\n await this.restoreRetainedNukiStates();\n\n if (this.config.webApiEnabled) {\n await this.startWebApi();\n }\n\n await this.setStateAsync(\"info.connection\", true, true);\n\n this.log.info(\"Nuki Local adapter started\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not start adapter: ${message}`);\n\n await this.setStateAsync(\"info.connection\", false, true);\n }\n }\n\n private async ensureInstanceObjects(): Promise<void> {\n await this.extendObjectAsync(\"info.connection\", {\n type: \"state\",\n common: {\n name: \"Connection\",\n type: \"boolean\",\n role: \"indicator.connected\",\n read: true,\n write: false,\n def: false,\n },\n native: {},\n });\n }\n\n /*\n * ============================================================\n * MQTT\n * ============================================================\n */\n\n private async startMqttBroker(): Promise<void> {\n const port = Number(this.config.mqttPort) || 1883;\n\n const username = String(this.config.mqttUsername ?? \"nuki\");\n\n const password = String(this.config.mqttPassword ?? \"\");\n\n const instanceDataDir = utils.getAbsoluteInstanceDataDir(this);\n\n const mqttDataDir = join(instanceDataDir, \"mqtt-leveldb\");\n\n await mkdir(mqttDataDir, {\n recursive: true,\n });\n\n this.log.info(`MQTT persistence directory: ${mqttDataDir}`);\n\n this.mqttDatabase = new Level(mqttDataDir);\n\n this.mqttPersistence = aedesPersistenceLevel(this.mqttDatabase);\n\n this.mqttBroker = await Aedes.createBroker({\n persistence: this.mqttPersistence,\n });\n\n this.mqttBroker.authenticate = (client, mqttUsername, mqttPassword, callback): void => {\n const receivedUsername = mqttUsername ?? \"\";\n\n const receivedPassword = mqttPassword?.toString() ?? \"\";\n\n const valid = receivedUsername === username && receivedPassword === password;\n\n if (valid) {\n this.log.info(`MQTT client authenticated: ${client?.id ?? \"unknown\"}`);\n } else {\n this.log.warn(`MQTT authentication rejected for client: ${client?.id ?? \"unknown\"}`);\n }\n\n callback(null, valid);\n };\n\n this.mqttBroker.on(\"client\", client => {\n this.log.info(`MQTT client connected: ${client.id}`);\n });\n\n this.mqttBroker.on(\"clientDisconnect\", client => {\n this.log.info(`MQTT client disconnected: ${client.id}`);\n });\n\n this.mqttBroker.on(\"publish\", (packet, client) => {\n if (!client) {\n return;\n }\n\n if (!packet.topic.startsWith(\"nuki/\")) {\n return;\n }\n\n const value = packet.payload.toString();\n\n this.log.info(`Nuki MQTT: ${packet.topic} = ${value}`);\n\n void this.handleNukiMessage(packet.topic, value);\n });\n\n this.mqttServer = createServer(this.mqttBroker.handle);\n\n await new Promise<void>((resolve, reject) => {\n if (!this.mqttServer) {\n reject(new Error(\"MQTT server was not created\"));\n\n return;\n }\n\n const onError = (error: Error): void => {\n reject(error);\n };\n\n const onListening = (): void => {\n this.mqttServer?.off(\"error\", onError);\n resolve();\n };\n\n this.mqttServer.once(\"error\", onError);\n\n this.mqttServer.once(\"listening\", onListening);\n\n this.mqttServer.listen(port, \"0.0.0.0\");\n });\n\n this.log.info(`Integrated MQTT broker listening on port ${port}`);\n }\n\n private async restoreRetainedNukiStates(): Promise<void> {\n if (!this.mqttPersistence) {\n return;\n }\n\n this.log.info(\"Restoring retained Nuki MQTT states...\");\n\n let restored = 0;\n\n const stream = this.mqttPersistence.createRetainedStreamCombi([\"nuki/#\"]);\n\n for await (const packet of stream) {\n if (!packet || typeof packet.topic !== \"string\") {\n continue;\n }\n\n if (!packet.topic.startsWith(\"nuki/\")) {\n continue;\n }\n\n const payload = Buffer.isBuffer(packet.payload) ? packet.payload.toString() : String(packet.payload ?? \"\");\n\n await this.handleNukiMessage(packet.topic, payload);\n\n restored++;\n }\n\n this.log.info(`Restored ${restored} retained Nuki MQTT state(s)`);\n }\n\n private async handleNukiMessage(topic: string, payload: string): Promise<void> {\n try {\n const parts = topic.split(\"/\");\n\n if (parts.length !== 3 || parts[0] !== \"nuki\") {\n return;\n }\n\n const deviceId = parts[1];\n\n const property = parts[2];\n\n if (!deviceId || !property) {\n return;\n }\n\n await this.ensureDevice(deviceId);\n\n switch (property) {\n case \"name\":\n await this.setStringState(`${deviceId}.device.name`, \"Name\", payload);\n break;\n\n case \"firmware\":\n await this.setStringState(`${deviceId}.device.firmware`, \"Firmware\", payload);\n break;\n\n case \"deviceType\":\n await this.setMappedNumberState(\n `${deviceId}.device.deviceType`,\n \"Device type\",\n payload,\n DEVICE_TYPES,\n );\n break;\n\n case \"mode\":\n await this.setMappedNumberState(`${deviceId}.device.mode`, \"Mode\", payload, MODES);\n break;\n\n case \"connected\":\n case \"serverConnected\":\n await this.setBooleanState(`${deviceId}.device.online`, \"Online\", payload, \"indicator.connected\");\n break;\n\n case \"timestamp\":\n await this.setStringState(`${deviceId}.status.timestamp`, \"Timestamp\", payload);\n break;\n\n case \"batteryChargeState\":\n await this.setNumberState(`${deviceId}.battery.percent`, \"Battery\", payload, \"%\", \"value.battery\");\n break;\n\n case \"batteryCharging\":\n await this.setBooleanState(`${deviceId}.battery.charging`, \"Battery charging\", payload);\n\n await this.updateDeviceIcon(deviceId);\n break;\n\n case \"batteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.critical`,\n \"Battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"doorsensorBatteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.doorSensorCritical`,\n \"Door sensor battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"keypadBatteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.keypadCritical`,\n \"Keypad battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"state\":\n await this.handleLockState(deviceId, payload);\n break;\n\n case \"doorsensorState\":\n await this.handleDoorState(deviceId, payload);\n break;\n\n case \"lockActionEvent\":\n await this.handleLockActionEvent(deviceId, payload);\n break;\n\n case \"commandResponse\":\n await this.handleCommandResponse(deviceId, payload);\n break;\n\n case \"lock\":\n case \"unlock\":\n case \"lockAction\":\n break;\n\n default:\n await this.setStringState(`${deviceId}.raw.${this.sanitizeId(property)}`, property, payload);\n break;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not process Nuki MQTT message ${topic}: ${message}`);\n }\n }\n\n /*\n * ============================================================\n * DEVICE INITIALIZATION\n * ============================================================\n */\n\n private async ensureDevice(deviceId: string): Promise<void> {\n const normalized = deviceId.toUpperCase();\n\n if (this.initializedDevices.has(normalized)) {\n return;\n }\n\n const existingPromise = this.initializingDevices.get(normalized);\n\n if (existingPromise) {\n await existingPromise;\n return;\n }\n\n const initialization = this.initializeDevice(deviceId, normalized);\n\n this.initializingDevices.set(normalized, initialization);\n\n try {\n await initialization;\n } finally {\n this.initializingDevices.delete(normalized);\n }\n }\n\n private async initializeDevice(deviceId: string, normalized: string): Promise<void> {\n await this.setObjectNotExistsAsync(deviceId, {\n type: \"device\",\n common: {\n name: `Nuki ${deviceId}`,\n },\n native: {},\n });\n\n const channels = [\n [\"status\", \"Status\"],\n [\"battery\", \"Battery\"],\n [\"keypad\", \"Keypad\"],\n [\"activity\", \"Activity\"],\n [\"device\", \"Device\"],\n [\"commands\", \"Commands\"],\n [\"advanced\", \"Advanced\"],\n [\"raw\", \"Raw MQTT\"],\n ] as const;\n\n for (const [id, name] of channels) {\n await this.setObjectNotExistsAsync(`${deviceId}.${id}`, {\n type: \"channel\",\n common: {\n name,\n },\n native: {},\n });\n }\n\n await this.ensureKnownDeviceObjects(deviceId);\n\n await this.cleanupLegacyObjects(deviceId);\n\n this.initializedDevices.add(normalized);\n\n await this.updateDeviceIcon(deviceId);\n\n this.log.info(`Nuki device initialized: ${deviceId}`);\n }\n\n private async ensureKnownDeviceObjects(deviceId: string): Promise<void> {\n await this.ensureMappedStateObject(`${deviceId}.status.lockState`, \"Lock state\", LOCK_STATES);\n\n await this.ensureStateObject(`${deviceId}.status.lockStateText`, \"Lock state text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.locked`, \"Locked\", \"boolean\", \"indicator\");\n\n await this.ensureMappedStateObject(`${deviceId}.status.doorState`, \"Door state\", DOOR_STATES);\n\n await this.ensureStateObject(`${deviceId}.status.doorStateText`, \"Door state text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.doorOpen`, \"Door open\", \"boolean\", \"sensor.door\");\n\n await this.ensureStateObject(`${deviceId}.status.timestamp`, \"Timestamp\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.iconState`, \"Icon state\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.icon`, \"Icon\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.battery.percent`, \"Battery\", \"number\", \"value.battery\", false, \"%\");\n\n await this.ensureStateObject(\n `${deviceId}.battery.critical`,\n \"Battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(`${deviceId}.battery.charging`, \"Battery charging\", \"boolean\", \"indicator\");\n\n await this.ensureStateObject(\n `${deviceId}.battery.keypadCritical`,\n \"Keypad battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(\n `${deviceId}.battery.doorSensorCritical`,\n \"Door sensor battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(`${deviceId}.keypad.lastType`, \"Last access type\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.keypad.lastUser`, \"Last user\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.keypad.lastTimestamp`, \"Last access timestamp\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastAction`, \"Last action\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastActionText`, \"Last action text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastUser`, \"Last user\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastDate`, \"Last date\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.device.name`, \"Name\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.device.firmware`, \"Firmware\", \"string\", \"text\");\n\n await this.ensureMappedStateObject(`${deviceId}.device.deviceType`, \"Device type\", DEVICE_TYPES);\n\n await this.ensureMappedStateObject(`${deviceId}.device.mode`, \"Mode\", MODES);\n\n await this.ensureStateObject(`${deviceId}.device.online`, \"Online\", \"boolean\", \"indicator.connected\");\n\n await this.ensureStateObject(`${deviceId}.advanced.authId`, \"Authorization ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.codeId`, \"Code ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.source`, \"Source\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.trigger`, \"Trigger\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.smartlockId`, \"Web API Smartlock ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.serverState`, \"Nuki server state\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.authorizations`, \"Authorizations\", \"string\", \"json\");\n\n await this.createCommandState(`${deviceId}.commands.lock`, \"Lock\");\n\n await this.createCommandState(`${deviceId}.commands.unlock`, \"Unlock\");\n\n await this.createCommandState(`${deviceId}.commands.unlatch`, \"Unlatch\");\n\n await this.createCommandState(`${deviceId}.commands.lockNgo`, \"Lock'n'Go\");\n\n await this.createCommandState(`${deviceId}.commands.lockNgoUnlatch`, \"Lock'n'Go with unlatch\");\n\n await this.createCommandState(`${deviceId}.commands.fullLock`, \"Full lock\");\n\n await this.ensureStateObject(`${deviceId}.commands.lastResult`, \"Last command result\", \"number\", \"value\");\n\n await this.ensureStateObject(\n `${deviceId}.commands.lastResultText`,\n \"Last command result text\",\n \"string\",\n \"text\",\n );\n\n await this.initializeDefaultValues(deviceId);\n }\n\n private async initializeDefaultValues(deviceId: string): Promise<void> {\n const defaults: Array<{\n id: string;\n value: string | boolean;\n }> = [\n {\n id: `${deviceId}.device.online`,\n value: false,\n },\n {\n id: `${deviceId}.battery.charging`,\n value: false,\n },\n {\n id: `${deviceId}.battery.critical`,\n value: false,\n },\n {\n id: `${deviceId}.battery.keypadCritical`,\n value: false,\n },\n {\n id: `${deviceId}.battery.doorSensorCritical`,\n value: false,\n },\n {\n id: `${deviceId}.device.firmware`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastUser`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastType`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastTimestamp`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastUser`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastActionText`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastDate`,\n value: \"\",\n },\n {\n id: `${deviceId}.commands.lastResultText`,\n value: \"\",\n },\n {\n id: `${deviceId}.status.iconState`,\n value: \"unknown\",\n },\n {\n id: `${deviceId}.status.icon`,\n value: NUKI_ICONS.unknown,\n },\n ];\n\n for (const item of defaults) {\n const state = await this.getStateAsync(item.id);\n\n if (!state) {\n await this.setStateAsync(item.id, item.value, true);\n }\n }\n }\n\n private async cleanupLegacyObjects(deviceId: string): Promise<void> {\n const legacy = [\n `${deviceId}.info`,\n `${deviceId}.lock`,\n `${deviceId}.door`,\n `${deviceId}.events`,\n `${deviceId}.web`,\n ];\n\n for (const id of legacy) {\n const object = await this.getObjectAsync(id);\n\n if (!object) {\n continue;\n }\n\n try {\n await this.delObjectAsync(id, {\n recursive: true,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not remove old object ${id}: ${message}`);\n }\n }\n }\n\n private async cleanupOldWebDeviceId(oldDeviceId: string, realDeviceId: string): Promise<void> {\n if (oldDeviceId === realDeviceId) {\n return;\n }\n\n const object = await this.getObjectAsync(oldDeviceId);\n\n if (!object) {\n return;\n }\n\n try {\n await this.delObjectAsync(oldDeviceId, {\n recursive: true,\n });\n\n this.initializedDevices.delete(oldDeviceId.toUpperCase());\n\n this.log.info(`Removed old Web API device ID ${oldDeviceId}; using MQTT ID ${realDeviceId}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not remove old Web API device ${oldDeviceId}: ${message}`);\n }\n }\n\n /*\n * ============================================================\n * ICON\n * ============================================================\n */\n\n private async updateDeviceIcon(deviceId: string): Promise<void> {\n const chargingState = await this.getStateAsync(`${deviceId}.battery.charging`);\n\n const doorOpenState = await this.getStateAsync(`${deviceId}.status.doorOpen`);\n\n const lockState = await this.getStateAsync(`${deviceId}.status.lockState`);\n\n const charging = chargingState?.val === true;\n\n const hasDoorState =\n doorOpenState !== null &&\n doorOpenState !== undefined &&\n doorOpenState.val !== null &&\n doorOpenState.val !== undefined;\n\n const doorOpen = doorOpenState?.val === true;\n\n const lockStateValue = typeof lockState?.val === \"number\" ? lockState.val : Number(lockState?.val);\n\n let iconState: NukiIconState = \"unknown\";\n\n let icon: string = NUKI_ICONS.unknown;\n\n /*\n * Priorit\u00E4t:\n *\n * 1. Akku l\u00E4dt\n * 2. T\u00FCr offen\n * 3. Schloss zugesperrt\n * 4. Schloss aufgesperrt\n * 5. T\u00FCr geschlossen\n * 6. unbekannt\n */\n\n if (charging) {\n iconState = \"charging\";\n\n icon = NUKI_ICONS.charging;\n } else if (doorOpen) {\n iconState = \"door_open\";\n\n icon = NUKI_ICONS.doorOpen;\n } else if (lockStateValue === 1) {\n iconState = \"locked\";\n\n icon = NUKI_ICONS.locked;\n } else if (lockStateValue === 3 || lockStateValue === 5 || lockStateValue === 6) {\n iconState = \"unlocked\";\n\n icon = NUKI_ICONS.unlocked;\n } else if (hasDoorState && !doorOpen) {\n iconState = \"door_closed\";\n\n icon = NUKI_ICONS.doorClosed;\n }\n\n await this.setStateAsync(`${deviceId}.status.iconState`, iconState, true);\n\n await this.setStateAsync(`${deviceId}.status.icon`, icon, true);\n }\n\n /*\n * ============================================================\n * STATUS / EVENTS\n * ============================================================\n */\n\n private async handleLockState(deviceId: string, payload: string): Promise<void> {\n const state = Number(payload);\n\n if (!Number.isFinite(state)) {\n return;\n }\n\n await this.setMappedNumberState(`${deviceId}.status.lockState`, \"Lock state\", payload, LOCK_STATES);\n\n await this.setStringState(\n `${deviceId}.status.lockStateText`,\n \"Lock state text\",\n this.getMappedText(LOCK_STATES, state),\n );\n\n await this.setStateAsync(`${deviceId}.status.locked`, state === 1, true);\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async handleDoorState(deviceId: string, payload: string): Promise<void> {\n const state = Number(payload);\n\n if (!Number.isFinite(state)) {\n return;\n }\n\n await this.setMappedNumberState(`${deviceId}.status.doorState`, \"Door state\", payload, DOOR_STATES);\n\n await this.setStringState(\n `${deviceId}.status.doorStateText`,\n \"Door state text\",\n this.getMappedText(DOOR_STATES, state),\n );\n\n await this.setStateAsync(`${deviceId}.status.doorOpen`, state === 3, true);\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async handleLockActionEvent(deviceId: string, payload: string): Promise<void> {\n const parts = payload.split(\",\");\n\n if (parts.length < 5) {\n this.log.warn(`Invalid lockActionEvent: ${payload}`);\n\n return;\n }\n\n const action = Number(parts[0]);\n\n const trigger = Number(parts[1]);\n\n const authId = Number(parts[2]);\n\n const codeId = Number(parts[3]);\n\n const source = Number(parts[4]);\n\n if (\n !Number.isFinite(action) ||\n !Number.isFinite(trigger) ||\n !Number.isFinite(authId) ||\n !Number.isFinite(codeId) ||\n !Number.isFinite(source)\n ) {\n this.log.warn(`Invalid numeric values in lockActionEvent: ${payload}`);\n\n return;\n }\n\n const now = new Date().toISOString();\n\n await this.setStateAsync(`${deviceId}.activity.lastAction`, action, true);\n\n await this.setStringState(\n `${deviceId}.activity.lastActionText`,\n \"Last action text\",\n this.getMappedText(LOCK_ACTIONS, action),\n );\n\n await this.setStringState(`${deviceId}.activity.lastDate`, \"Last date\", now);\n\n await this.setStateAsync(`${deviceId}.advanced.authId`, authId, true);\n\n await this.setStateAsync(`${deviceId}.advanced.codeId`, codeId, true);\n\n await this.setStateAsync(`${deviceId}.advanced.source`, source, true);\n\n await this.setStateAsync(`${deviceId}.advanced.trigger`, trigger, true);\n\n const configuredKeypadUser = codeId > 0 ? this.findConfiguredKeypadUser(codeId) : undefined;\n\n const resolvedUserName = this.findAuthorizationName(deviceId, authId);\n\n const displayUser =\n configuredKeypadUser ??\n resolvedUserName ??\n (codeId > 0 ? `Unbekannt (Code-ID ${codeId})` : `Unbekannt (Auth-ID ${authId})`);\n\n await this.setStringState(`${deviceId}.activity.lastUser`, \"Last user\", displayUser);\n\n if (codeId > 0) {\n let type = `Quelle ${source}`;\n\n switch (source) {\n case 0:\n type = \"Back-Taste\";\n break;\n\n case 1:\n type = \"Code\";\n break;\n\n case 2:\n type = \"Fingerprint\";\n break;\n }\n\n await this.setStringState(`${deviceId}.keypad.lastType`, \"Last access type\", type);\n\n await this.setStringState(`${deviceId}.keypad.lastUser`, \"Last user\", displayUser);\n\n await this.setStringState(`${deviceId}.keypad.lastTimestamp`, \"Last access timestamp\", now);\n\n if (source === 2) {\n this.log.info(`Fingerprint detected: authId=${authId}, codeId=${codeId}, user=${displayUser}`);\n } else if (source === 1) {\n this.log.info(`Keypad code detected: authId=${authId}, codeId=${codeId}, user=${displayUser}`);\n }\n }\n\n this.log.debug(\n `Lock action processed: action=${action}, trigger=${trigger}, authId=${authId}, codeId=${codeId}, source=${source}`,\n );\n }\n\n private findConfiguredKeypadUser(codeId: number): string | undefined {\n const users = Array.isArray(this.config.keypadUsers) ? this.config.keypadUsers : [];\n\n const match = users.find(user => Number(user.codeId) === codeId);\n\n if (!match || typeof match.name !== \"string\") {\n return undefined;\n }\n\n const name = match.name.trim();\n\n return name || undefined;\n }\n\n private findAuthorizationName(deviceId: string, authId: number): string | undefined {\n const smartlockId = this.webSmartlockIds.get(this.normalizeDeviceId(deviceId));\n\n if (smartlockId === undefined) {\n return undefined;\n }\n\n return this.authorizationNames.get(smartlockId)?.get(authId);\n }\n\n /*\n * ============================================================\n * COMMANDS\n * ============================================================\n */\n\n private async createCommandState(id: string, name: string): Promise<void> {\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type: \"boolean\",\n role: \"button\",\n read: true,\n write: true,\n def: false,\n },\n native: {},\n });\n\n const state = await this.getStateAsync(id);\n\n if (!state) {\n await this.setStateAsync(id, false, true);\n }\n }\n\n private async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n if (!state || state.ack || state.val !== true) {\n return;\n }\n\n const prefix = `${this.namespace}.`;\n\n if (!id.startsWith(prefix)) {\n return;\n }\n\n const relativeId = id.substring(prefix.length);\n\n const parts = relativeId.split(\".\");\n\n if (parts.length !== 3 || parts[1] !== \"commands\") {\n return;\n }\n\n const deviceId = parts[0];\n\n const command = parts[2];\n\n try {\n switch (command) {\n case \"unlock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"1\");\n break;\n\n case \"lock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"2\");\n break;\n\n case \"unlatch\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"3\");\n break;\n\n case \"lockNgo\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"4\");\n break;\n\n case \"lockNgoUnlatch\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"5\");\n break;\n\n case \"fullLock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"6\");\n break;\n\n default:\n return;\n }\n\n await this.setStateAsync(relativeId, false, true);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not execute command ${command}: ${message}`);\n\n await this.setStateAsync(relativeId, false, true);\n }\n }\n\n private async publishNukiCommand(topic: string, payload: string): Promise<void> {\n if (!this.mqttBroker) {\n throw new Error(\"MQTT broker is not running\");\n }\n\n await new Promise<void>((resolve, reject) => {\n this.mqttBroker?.publish(\n {\n cmd: \"publish\",\n topic,\n payload: Buffer.from(payload),\n qos: 2,\n dup: false,\n retain: false,\n },\n error => {\n if (error) {\n reject(error);\n\n return;\n }\n\n resolve();\n },\n );\n });\n\n this.log.info(`Nuki command published: ${topic} = ${payload}`);\n }\n\n private async handleCommandResponse(deviceId: string, payload: string): Promise<void> {\n const result = Number(payload);\n\n if (!Number.isFinite(result)) {\n return;\n }\n\n await this.setNumberState(`${deviceId}.commands.lastResult`, \"Last command result\", payload);\n\n await this.setStringState(\n `${deviceId}.commands.lastResultText`,\n \"Last command result text\",\n result === 0 ? \"Erfolgreich\" : `Fehler ${result}`,\n );\n }\n\n /*\n * ============================================================\n * WEB API\n * ============================================================\n */\n\n private async startWebApi(): Promise<void> {\n const token = String(this.config.webApiToken ?? \"\").trim();\n\n if (!token) {\n this.log.warn(\"Nuki Web API is enabled but no API token is configured\");\n\n return;\n }\n\n const intervalSeconds = Math.max(60, Number(this.config.webApiInterval) || 300);\n\n this.log.info(`Nuki Web API enabled, update interval: ${intervalSeconds} seconds`);\n\n try {\n await this.updateNukiWebData();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Initial Nuki Web API update failed: ${message}`);\n }\n\n this.webApiTimer = this.setInterval(() => {\n void this.updateNukiWebData().catch(error => {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Nuki Web API update failed: ${message}`);\n });\n }, intervalSeconds * 1000);\n }\n\n private async updateNukiWebData(): Promise<void> {\n const devices = await this.nukiWebRequest<NukiWebDevice[]>(\"/smartlock\");\n\n if (!Array.isArray(devices)) {\n throw new Error(\"Invalid response from /smartlock\");\n }\n\n let authorizations: NukiWebAuthorization[] = [];\n\n try {\n const result = await this.nukiWebRequest<NukiWebAuthorization[]>(\"/smartlock/auth\");\n\n if (Array.isArray(result)) {\n authorizations = result;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not load Nuki authorizations: ${message}`);\n }\n\n for (const device of devices) {\n const smartlockId = Number(device.smartlockId);\n\n if (!Number.isFinite(smartlockId)) {\n continue;\n }\n\n const conversion = this.getMqttDeviceIdFromWebDevice(device);\n\n const deviceId = conversion.deviceId;\n\n if (conversion.originalHex !== conversion.deviceId) {\n await this.cleanupOldWebDeviceId(conversion.originalHex, conversion.deviceId);\n }\n\n this.webSmartlockIds.set(this.normalizeDeviceId(deviceId), smartlockId);\n\n await this.ensureDevice(deviceId);\n\n await this.storeWebDeviceData(deviceId, device);\n\n const deviceAuths = authorizations.filter(auth => Number(auth.smartlockId) === smartlockId);\n\n await this.storeWebAuthorizations(deviceId, smartlockId, deviceAuths);\n\n try {\n const logs = await this.nukiWebRequest<NukiWebLog[]>(`/smartlock/${smartlockId}/log?limit=20`);\n\n if (Array.isArray(logs)) {\n await this.storeWebLogs(deviceId, logs);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not load activity logs for ${deviceId}: ${message}`);\n }\n }\n }\n\n private async nukiWebRequest<T>(path: string): Promise<T> {\n const token = String(this.config.webApiToken ?? \"\").trim();\n\n if (!token) {\n throw new Error(\"Nuki Web API token missing\");\n }\n\n const response = await fetch(`https://api.nuki.io${path}`, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n\n throw new Error(\n `HTTP ${response.status} ${response.statusText}${body ? `: ${body.substring(0, 300)}` : \"\"}`,\n );\n }\n\n return (await response.json()) as T;\n }\n\n private getMqttDeviceIdFromWebDevice(device: NukiWebDevice): {\n deviceId: string;\n originalHex: string;\n } {\n try {\n const originalHex = BigInt(device.smartlockId).toString(16).toUpperCase();\n\n const deviceId = originalHex.slice(-8).padStart(8, \"0\");\n\n return {\n deviceId,\n originalHex,\n };\n } catch {\n const value = String(device.smartlockId);\n\n return {\n deviceId: value,\n originalHex: value,\n };\n }\n }\n\n private normalizeDeviceId(deviceId: string): string {\n const normalized = deviceId.trim().toUpperCase().replace(/^0X/, \"\");\n\n return normalized.slice(-8).padStart(8, \"0\");\n }\n\n private async storeWebDeviceData(deviceId: string, device: NukiWebDevice): Promise<void> {\n await this.setStateAsync(`${deviceId}.advanced.smartlockId`, Number(device.smartlockId), true);\n\n if (typeof device.name === \"string\") {\n await this.setStringState(`${deviceId}.device.name`, \"Name\", device.name);\n }\n\n if (typeof device.type === \"number\") {\n await this.setMappedNumberState(\n `${deviceId}.device.deviceType`,\n \"Device type\",\n String(device.type),\n DEVICE_TYPES,\n );\n }\n\n if (typeof device.serverState === \"number\") {\n await this.setStateAsync(`${deviceId}.advanced.serverState`, device.serverState, true);\n\n await this.setStateAsync(`${deviceId}.device.online`, device.serverState === 0, true);\n }\n\n const state = device.state;\n\n if (!state) {\n await this.updateDeviceIcon(deviceId);\n\n return;\n }\n\n if (typeof state.batteryCharge === \"number\") {\n await this.setStateAsync(`${deviceId}.battery.percent`, state.batteryCharge, true);\n }\n\n if (typeof state.batteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.critical`, state.batteryCritical, true);\n }\n\n if (typeof state.batteryCharging === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.charging`, state.batteryCharging, true);\n }\n\n if (typeof state.keypadBatteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.keypadCritical`, state.keypadBatteryCritical, true);\n }\n\n if (typeof state.doorsensorBatteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.doorSensorCritical`, state.doorsensorBatteryCritical, true);\n }\n\n if (typeof state.state === \"number\") {\n await this.setStateAsync(`${deviceId}.status.lockState`, state.state, true);\n\n await this.setStringState(\n `${deviceId}.status.lockStateText`,\n \"Lock state text\",\n this.getMappedText(LOCK_STATES, state.state),\n );\n\n await this.setStateAsync(`${deviceId}.status.locked`, state.state === 1, true);\n }\n\n if (typeof state.doorState === \"number\") {\n await this.setStateAsync(`${deviceId}.status.doorState`, state.doorState, true);\n\n await this.setStringState(\n `${deviceId}.status.doorStateText`,\n \"Door state text\",\n this.getMappedText(DOOR_STATES, state.doorState),\n );\n\n await this.setStateAsync(`${deviceId}.status.doorOpen`, state.doorState === 3, true);\n }\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async storeWebAuthorizations(\n deviceId: string,\n smartlockId: number,\n authorizations: NukiWebAuthorization[],\n ): Promise<void> {\n const authNames = new Map<number, string>();\n\n for (const auth of authorizations) {\n if (typeof auth.authId === \"number\" && typeof auth.name === \"string\") {\n authNames.set(auth.authId, auth.name);\n }\n }\n\n this.authorizationNames.set(smartlockId, authNames);\n\n const safe = authorizations.map(auth => ({\n id: auth.id,\n smartlockId: auth.smartlockId,\n authId: auth.authId,\n type: auth.type,\n name: auth.name,\n enabled: auth.enabled,\n remoteAllowed: auth.remoteAllowed,\n }));\n\n await this.setStringState(`${deviceId}.advanced.authorizations`, \"Authorizations\", JSON.stringify(safe));\n }\n\n private async storeWebLogs(deviceId: string, logs: NukiWebLog[]): Promise<void> {\n if (logs.length === 0) {\n return;\n }\n\n const sorted = [...logs].sort((a, b) => {\n const bTime = Date.parse(b.date ?? \"\");\n\n const aTime = Date.parse(a.date ?? \"\");\n\n return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);\n });\n\n const last = sorted[0];\n\n if (!last) {\n return;\n }\n\n if (typeof last.action === \"number\") {\n await this.setStateAsync(`${deviceId}.activity.lastAction`, last.action, true);\n\n await this.setStringState(\n `${deviceId}.activity.lastActionText`,\n \"Last action text\",\n this.getMappedText(LOCK_ACTIONS, last.action),\n );\n }\n\n if (typeof last.name === \"string\") {\n await this.setStringState(`${deviceId}.activity.lastUser`, \"Last user\", last.name);\n }\n\n if (typeof last.date === \"string\") {\n await this.setStringState(`${deviceId}.activity.lastDate`, \"Last date\", last.date);\n }\n\n if (typeof last.authId === \"number\") {\n await this.setStateAsync(`${deviceId}.advanced.authId`, last.authId, true);\n }\n }\n\n /*\n * ============================================================\n * HELPERS\n * ============================================================\n */\n\n private getMappedText(map: Record<number, string>, value: number): string {\n return map[value] ?? `Unbekannt (${value})`;\n }\n\n private async ensureStateObject(\n id: string,\n name: string,\n type: \"string\" | \"number\" | \"boolean\",\n role: string,\n write = false,\n unit?: string,\n ): Promise<void> {\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type,\n role,\n read: true,\n write,\n ...(unit\n ? {\n unit,\n }\n : {}),\n },\n native: {},\n });\n }\n\n private async ensureMappedStateObject(id: string, name: string, states: Record<number, string>): Promise<void> {\n const ioBrokerStates: Record<string, string> = {};\n\n for (const [key, text] of Object.entries(states)) {\n ioBrokerStates[key] = text;\n }\n\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n states: ioBrokerStates,\n },\n native: {},\n });\n }\n\n private async setStringState(id: string, name: string, value: string): Promise<void> {\n await this.ensureStateObject(id, name, \"string\", \"text\");\n\n await this.setStateAsync(id, value, true);\n }\n\n private async setNumberState(\n id: string,\n name: string,\n value: string,\n unit?: string,\n role = \"value\",\n ): Promise<void> {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n return;\n }\n\n await this.ensureStateObject(id, name, \"number\", role, false, unit);\n\n await this.setStateAsync(id, numberValue, true);\n }\n\n private async setMappedNumberState(\n id: string,\n name: string,\n value: string,\n states: Record<number, string>,\n ): Promise<void> {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n return;\n }\n\n await this.ensureMappedStateObject(id, name, states);\n\n await this.setStateAsync(id, numberValue, true);\n }\n\n private async setBooleanState(id: string, name: string, value: string, role = \"indicator\"): Promise<void> {\n await this.ensureStateObject(id, name, \"boolean\", role);\n\n const boolValue = value.toLowerCase() === \"true\" || value === \"1\";\n\n await this.setStateAsync(id, boolValue, true);\n }\n\n private sanitizeId(value: string): string {\n return value.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n }\n\n /*\n * ============================================================\n * UNLOAD\n * ============================================================\n */\n\n private onUnload(callback: () => void): void {\n void (async () => {\n try {\n if (this.webApiTimer) {\n clearInterval(this.webApiTimer);\n\n this.webApiTimer = null;\n }\n\n await this.setStateAsync(\"info.connection\", false, true);\n\n if (this.mqttServer) {\n await new Promise<void>(resolve => {\n this.mqttServer?.close(() => resolve());\n });\n\n this.mqttServer = null;\n }\n\n if (this.mqttBroker) {\n await new Promise<void>(resolve => {\n this.mqttBroker?.close(() => resolve());\n });\n\n this.mqttBroker = null;\n }\n\n this.mqttPersistence = null;\n\n this.mqttDatabase = null;\n\n this.log.info(\"Nuki Local adapter stopped\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Error while stopping adapter: ${message}`);\n } finally {\n callback();\n }\n })();\n }\n}\n\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new NukiLocal(options);\n} else {\n (() => new NukiLocal())();\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { createServer, type Server } from \"node:net\";\nimport { join } from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { Level } from \"level\";\nimport { Aedes, type Aedes as AedesInstance } from \"aedes\";\nimport type { AedesPersistence } from \"aedes-persistence\";\n\nimport aedesPersistenceLevel from \"aedes-persistence-level\";\n\nconst DEVICE_TYPES: Record<number, string> = {\n 0: \"Smart Lock\",\n 2: \"Opener\",\n 3: \"Smart Door\",\n 4: \"Smart Lock 3.0 / 4th Generation\",\n 5: \"Smart Lock Ultra / 5th Generation / Go\",\n};\n\nconst MODES: Record<number, string> = {\n 2: \"Door mode\",\n 3: \"Continuous mode\",\n};\n\nconst LOCK_STATES: Record<number, string> = {\n 0: \"Uncalibrated\",\n 1: \"Locked\",\n 2: \"Unlocking\",\n 3: \"Unlocked\",\n 4: \"Locking\",\n 5: \"Unlatched\",\n 6: \"Unlocked (Lock'n'Go)\",\n 7: \"Unlatching\",\n 253: \"Boot run\",\n 254: \"Motor blocked\",\n 255: \"Unknown state\",\n};\n\nconst DOOR_STATES: Record<number, string> = {\n 1: \"Door sensor disabled\",\n 2: \"Door closed\",\n 3: \"Door open\",\n 4: \"Door state unknown\",\n 5: \"Door sensor calibrating\",\n 16: \"Door sensor uncalibrated\",\n 240: \"Door sensor tampered\",\n 255: \"Unknown\",\n};\n\nconst LOCK_ACTIONS: Record<number, string> = {\n 1: \"Unlock\",\n 2: \"Lock\",\n 3: \"Unlatch\",\n 4: \"Lock'n'Go\",\n 5: \"Lock'n'Go with unlatch\",\n 6: \"Full lock\",\n 80: \"Fob without action\",\n 90: \"Button without action\",\n};\n\nconst NUKI_ICONS = {\n locked: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_locked.png\",\n unlocked: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_unlocked.png\",\n doorOpen: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_door_open.png\",\n doorClosed: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_door_closed.png\",\n charging: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_charging.png\",\n pairing: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_pairing.png\",\n unknown: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_unknown.png\",\n} as const;\n\ntype NukiIconState = \"locked\" | \"unlocked\" | \"door_open\" | \"door_closed\" | \"charging\" | \"pairing\" | \"unknown\";\n\ninterface NukiWebState {\n mode?: number;\n state?: number;\n trigger?: number;\n lastAction?: number;\n batteryCritical?: boolean;\n batteryCharging?: boolean;\n batteryCharge?: number;\n keypadBatteryCritical?: boolean;\n doorsensorBatteryCritical?: boolean;\n doorState?: number;\n}\n\ninterface NukiWebDevice {\n smartlockId: number;\n type?: number;\n name?: string;\n serverState?: number;\n state?: NukiWebState;\n}\n\ninterface NukiWebAuthorization {\n id?: string;\n smartlockId?: number;\n authId?: number;\n code?: number;\n type?: number;\n name?: string;\n enabled?: boolean;\n remoteAllowed?: boolean;\n}\n\ninterface NukiWebLog {\n id?: string;\n smartlockId?: number;\n authId?: number;\n name?: string;\n action?: number;\n state?: number;\n trigger?: number;\n date?: string;\n}\n\nclass NukiLocal extends utils.Adapter {\n private mqttBroker: AedesInstance | null = null;\n private mqttServer: Server | null = null;\n private mqttDatabase: Level | null = null;\n\n private mqttPersistence: AedesPersistence | null = null;\n\n private webApiTimer: ioBroker.Timeout | undefined | null = null;\n\n private webApiStopping = false;\n\n private readonly initializedDevices = new Set<string>();\n\n private readonly initializingDevices = new Map<string, Promise<void>>();\n\n private readonly webSmartlockIds = new Map<string, number>();\n\n private readonly authorizationNames = new Map<number, Map<number, string>>();\n\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"nuki-local\",\n });\n\n this.on(\"ready\", this.onReady.bind(this));\n\n this.on(\"stateChange\", (id, state) => {\n void this.onStateChange(id, state);\n });\n\n this.on(\"unload\", this.onUnload.bind(this));\n }\n\n private async onReady(): Promise<void> {\n await this.ensureInstanceObjects();\n\n await this.setStateAsync(\"info.connection\", false, true);\n\n this.subscribeStates(\"*\");\n\n try {\n await this.startMqttBroker();\n\n await this.restoreRetainedNukiStates();\n\n if (this.config.webApiEnabled) {\n await this.startWebApi();\n }\n\n await this.setStateAsync(\"info.connection\", true, true);\n\n this.log.info(\"Nuki Local adapter started\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not start adapter: ${message}`);\n\n await this.setStateAsync(\"info.connection\", false, true);\n }\n }\n\n private async ensureInstanceObjects(): Promise<void> {\n await this.extendObjectAsync(\"info.connection\", {\n type: \"state\",\n common: {\n name: \"Connection\",\n type: \"boolean\",\n role: \"indicator.connected\",\n read: true,\n write: false,\n def: false,\n },\n native: {},\n });\n }\n\n /*\n * ============================================================\n * MQTT\n * ============================================================\n */\n\n private async startMqttBroker(): Promise<void> {\n const port = Number(this.config.mqttPort) || 1883;\n\n const username = String(this.config.mqttUsername ?? \"nuki\");\n\n const password = String(this.config.mqttPassword ?? \"\");\n\n const instanceDataDir = utils.getAbsoluteInstanceDataDir(this);\n\n const mqttDataDir = join(instanceDataDir, \"mqtt-leveldb\");\n\n await mkdir(mqttDataDir, {\n recursive: true,\n });\n\n this.log.info(`MQTT persistence directory: ${mqttDataDir}`);\n\n this.mqttDatabase = new Level(mqttDataDir);\n\n this.mqttPersistence = aedesPersistenceLevel(this.mqttDatabase);\n\n this.mqttBroker = await Aedes.createBroker({\n persistence: this.mqttPersistence,\n });\n\n this.mqttBroker.authenticate = (client, mqttUsername, mqttPassword, callback): void => {\n const receivedUsername = mqttUsername ?? \"\";\n\n const receivedPassword = mqttPassword?.toString() ?? \"\";\n\n const valid = receivedUsername === username && receivedPassword === password;\n\n if (valid) {\n this.log.info(`MQTT client authenticated: ${client?.id ?? \"unknown\"}`);\n } else {\n this.log.warn(`MQTT authentication rejected for client: ${client?.id ?? \"unknown\"}`);\n }\n\n callback(null, valid);\n };\n\n this.mqttBroker.on(\"client\", client => {\n this.log.info(`MQTT client connected: ${client.id}`);\n });\n\n this.mqttBroker.on(\"clientDisconnect\", client => {\n this.log.info(`MQTT client disconnected: ${client.id}`);\n });\n\n this.mqttBroker.on(\"publish\", (packet, client) => {\n if (!client) {\n return;\n }\n\n if (!packet.topic.startsWith(\"nuki/\")) {\n return;\n }\n\n const value = packet.payload.toString();\n\n this.log.info(`Nuki MQTT: ${packet.topic} = ${value}`);\n\n void this.handleNukiMessage(packet.topic, value);\n });\n\n this.mqttServer = createServer(this.mqttBroker.handle);\n\n await new Promise<void>((resolve, reject) => {\n if (!this.mqttServer) {\n reject(new Error(\"MQTT server was not created\"));\n\n return;\n }\n\n const onError = (error: Error): void => {\n reject(error);\n };\n\n const onListening = (): void => {\n this.mqttServer?.off(\"error\", onError);\n resolve();\n };\n\n this.mqttServer.once(\"error\", onError);\n\n this.mqttServer.once(\"listening\", onListening);\n\n this.mqttServer.listen(port, \"0.0.0.0\");\n });\n\n this.log.info(`Integrated MQTT broker listening on port ${port}`);\n }\n\n private async restoreRetainedNukiStates(): Promise<void> {\n if (!this.mqttPersistence) {\n return;\n }\n\n this.log.info(\"Restoring retained Nuki MQTT states...\");\n\n let restored = 0;\n\n const stream = this.mqttPersistence.createRetainedStreamCombi([\"nuki/#\"]);\n\n for await (const packet of stream) {\n if (!packet || typeof packet.topic !== \"string\") {\n continue;\n }\n\n if (!packet.topic.startsWith(\"nuki/\")) {\n continue;\n }\n\n const payload = Buffer.isBuffer(packet.payload) ? packet.payload.toString() : String(packet.payload ?? \"\");\n\n await this.handleNukiMessage(packet.topic, payload);\n\n restored++;\n }\n\n this.log.info(`Restored ${restored} retained Nuki MQTT state(s)`);\n }\n\n private async handleNukiMessage(topic: string, payload: string): Promise<void> {\n try {\n const parts = topic.split(\"/\");\n\n if (parts.length !== 3 || parts[0] !== \"nuki\") {\n return;\n }\n\n const deviceId = parts[1];\n\n const property = parts[2];\n\n if (!deviceId || !property) {\n return;\n }\n\n if (this.sanitizeId(deviceId) !== deviceId) {\n this.log.debug(\"Ignoring MQTT message with invalid device ID\");\n return;\n }\n\n await this.ensureDevice(deviceId);\n\n switch (property) {\n case \"name\":\n await this.setStringState(`${deviceId}.device.name`, \"Name\", payload);\n break;\n\n case \"firmware\":\n await this.setStringState(`${deviceId}.device.firmware`, \"Firmware\", payload);\n break;\n\n case \"deviceType\":\n await this.setMappedNumberState(\n `${deviceId}.device.deviceType`,\n \"Device type\",\n payload,\n DEVICE_TYPES,\n );\n break;\n\n case \"mode\":\n await this.setMappedNumberState(`${deviceId}.device.mode`, \"Mode\", payload, MODES);\n break;\n\n case \"connected\":\n case \"serverConnected\":\n await this.setBooleanState(`${deviceId}.device.online`, \"Online\", payload, \"indicator.connected\");\n break;\n\n case \"timestamp\":\n await this.setStringState(`${deviceId}.status.timestamp`, \"Timestamp\", payload, \"date\");\n break;\n\n case \"batteryChargeState\":\n await this.setNumberState(`${deviceId}.battery.percent`, \"Battery\", payload, \"%\", \"value.battery\");\n break;\n\n case \"batteryCharging\":\n await this.setBooleanState(`${deviceId}.battery.charging`, \"Battery charging\", payload);\n\n await this.updateDeviceIcon(deviceId);\n break;\n\n case \"batteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.critical`,\n \"Battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"doorsensorBatteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.doorSensorCritical`,\n \"Door sensor battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"keypadBatteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.keypadCritical`,\n \"Keypad battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"state\":\n await this.handleLockState(deviceId, payload);\n break;\n\n case \"doorsensorState\":\n await this.handleDoorState(deviceId, payload);\n break;\n\n case \"lockActionEvent\":\n await this.handleLockActionEvent(deviceId, payload);\n break;\n\n case \"commandResponse\":\n await this.handleCommandResponse(deviceId, payload);\n break;\n\n case \"lock\":\n case \"unlock\":\n case \"lockAction\":\n break;\n\n default:\n await this.setStringState(`${deviceId}.raw.${this.sanitizeId(property)}`, property, payload);\n break;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not process Nuki MQTT message ${topic}: ${message}`);\n }\n }\n\n /*\n * ============================================================\n * DEVICE INITIALIZATION\n * ============================================================\n */\n\n private async ensureDevice(deviceId: string): Promise<void> {\n const normalized = deviceId.toUpperCase();\n\n if (this.initializedDevices.has(normalized)) {\n return;\n }\n\n const existingPromise = this.initializingDevices.get(normalized);\n\n if (existingPromise) {\n await existingPromise;\n return;\n }\n\n const initialization = this.initializeDevice(deviceId, normalized);\n\n this.initializingDevices.set(normalized, initialization);\n\n try {\n await initialization;\n } finally {\n this.initializingDevices.delete(normalized);\n }\n }\n\n private async initializeDevice(deviceId: string, normalized: string): Promise<void> {\n await this.setObjectNotExistsAsync(deviceId, {\n type: \"device\",\n common: {\n name: `Nuki ${deviceId}`,\n },\n native: {},\n });\n\n const channels = [\n [\"status\", \"Status\"],\n [\"battery\", \"Battery\"],\n [\"keypad\", \"Keypad\"],\n [\"activity\", \"Activity\"],\n [\"device\", \"Device\"],\n [\"commands\", \"Commands\"],\n [\"advanced\", \"Advanced\"],\n [\"raw\", \"Raw MQTT\"],\n ] as const;\n\n for (const [id, name] of channels) {\n await this.setObjectNotExistsAsync(`${deviceId}.${id}`, {\n type: \"channel\",\n common: {\n name,\n },\n native: {},\n });\n }\n\n await this.ensureKnownDeviceObjects(deviceId);\n\n await this.cleanupLegacyObjects(deviceId);\n\n this.initializedDevices.add(normalized);\n\n await this.updateDeviceIcon(deviceId);\n\n this.log.info(`Nuki device initialized: ${deviceId}`);\n }\n\n private async ensureKnownDeviceObjects(deviceId: string): Promise<void> {\n await this.ensureMappedStateObject(`${deviceId}.status.lockState`, \"Lock state\", LOCK_STATES);\n\n await this.ensureStateObject(`${deviceId}.status.lockStateText`, \"Lock state text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.locked`, \"Locked\", \"boolean\", \"indicator\");\n\n await this.ensureMappedStateObject(`${deviceId}.status.doorState`, \"Door state\", DOOR_STATES);\n\n await this.ensureStateObject(`${deviceId}.status.doorStateText`, \"Door state text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.doorOpen`, \"Door open\", \"boolean\", \"sensor.door\");\n\n await this.ensureStateObject(`${deviceId}.status.timestamp`, \"Timestamp\", \"string\", \"date\");\n\n await this.ensureStateObject(`${deviceId}.status.iconState`, \"Icon state\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.icon`, \"Icon\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.battery.percent`, \"Battery\", \"number\", \"value.battery\", false, \"%\");\n\n await this.ensureStateObject(\n `${deviceId}.battery.critical`,\n \"Battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(`${deviceId}.battery.charging`, \"Battery charging\", \"boolean\", \"indicator\");\n\n await this.ensureStateObject(\n `${deviceId}.battery.keypadCritical`,\n \"Keypad battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(\n `${deviceId}.battery.doorSensorCritical`,\n \"Door sensor battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(`${deviceId}.keypad.lastType`, \"Last access type\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.keypad.lastUser`, \"Last user\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.keypad.lastTimestamp`, \"Last access timestamp\", \"string\", \"date\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastAction`, \"Last action\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastActionText`, \"Last action text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastUser`, \"Last user\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastDate`, \"Last date\", \"string\", \"date\");\n\n await this.ensureStateObject(`${deviceId}.device.name`, \"Name\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.device.firmware`, \"Firmware\", \"string\", \"text\");\n\n await this.ensureMappedStateObject(`${deviceId}.device.deviceType`, \"Device type\", DEVICE_TYPES);\n\n await this.ensureMappedStateObject(`${deviceId}.device.mode`, \"Mode\", MODES);\n\n await this.ensureStateObject(`${deviceId}.device.online`, \"Online\", \"boolean\", \"indicator.connected\");\n\n await this.ensureStateObject(`${deviceId}.advanced.authId`, \"Authorization ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.codeId`, \"Code ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.source`, \"Source\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.trigger`, \"Trigger\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.smartlockId`, \"Web API Smartlock ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.serverState`, \"Nuki server state\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.authorizations`, \"Authorizations\", \"string\", \"json\");\n\n await this.createCommandState(`${deviceId}.commands.lock`, \"Lock\");\n\n await this.createCommandState(`${deviceId}.commands.unlock`, \"Unlock\");\n\n await this.createCommandState(`${deviceId}.commands.unlatch`, \"Unlatch\");\n\n await this.createCommandState(`${deviceId}.commands.lockNgo`, \"Lock'n'Go\");\n\n await this.createCommandState(`${deviceId}.commands.lockNgoUnlatch`, \"Lock'n'Go with unlatch\");\n\n await this.createCommandState(`${deviceId}.commands.fullLock`, \"Full lock\");\n\n await this.ensureStateObject(`${deviceId}.commands.lastResult`, \"Last command result\", \"number\", \"value\");\n\n await this.ensureStateObject(\n `${deviceId}.commands.lastResultText`,\n \"Last command result text\",\n \"string\",\n \"text\",\n );\n\n await this.initializeDefaultValues(deviceId);\n }\n\n private async initializeDefaultValues(deviceId: string): Promise<void> {\n const defaults: Array<{\n id: string;\n value: string | boolean;\n }> = [\n {\n id: `${deviceId}.device.online`,\n value: false,\n },\n {\n id: `${deviceId}.battery.charging`,\n value: false,\n },\n {\n id: `${deviceId}.battery.critical`,\n value: false,\n },\n {\n id: `${deviceId}.battery.keypadCritical`,\n value: false,\n },\n {\n id: `${deviceId}.battery.doorSensorCritical`,\n value: false,\n },\n {\n id: `${deviceId}.device.firmware`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastUser`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastType`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastTimestamp`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastUser`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastActionText`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastDate`,\n value: \"\",\n },\n {\n id: `${deviceId}.commands.lastResultText`,\n value: \"\",\n },\n {\n id: `${deviceId}.status.iconState`,\n value: \"unknown\",\n },\n {\n id: `${deviceId}.status.icon`,\n value: NUKI_ICONS.unknown,\n },\n ];\n\n for (const item of defaults) {\n const state = await this.getStateAsync(item.id);\n\n if (!state) {\n await this.setStateAsync(item.id, item.value, true);\n }\n }\n }\n\n private async cleanupLegacyObjects(deviceId: string): Promise<void> {\n const legacy = [\n `${deviceId}.info`,\n `${deviceId}.lock`,\n `${deviceId}.door`,\n `${deviceId}.events`,\n `${deviceId}.web`,\n ];\n\n for (const id of legacy) {\n const object = await this.getObjectAsync(id);\n\n if (!object) {\n continue;\n }\n\n try {\n await this.delObjectAsync(id, {\n recursive: true,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not remove old object ${id}: ${message}`);\n }\n }\n }\n\n private async cleanupOldWebDeviceId(oldDeviceId: string, realDeviceId: string): Promise<void> {\n if (oldDeviceId === realDeviceId) {\n return;\n }\n\n const object = await this.getObjectAsync(oldDeviceId);\n\n if (!object) {\n return;\n }\n\n try {\n await this.delObjectAsync(oldDeviceId, {\n recursive: true,\n });\n\n this.initializedDevices.delete(oldDeviceId.toUpperCase());\n\n this.log.info(`Removed old Web API device ID ${oldDeviceId}; using MQTT ID ${realDeviceId}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not remove old Web API device ${oldDeviceId}: ${message}`);\n }\n }\n\n /*\n * ============================================================\n * ICON\n * ============================================================\n */\n\n private async updateDeviceIcon(deviceId: string): Promise<void> {\n const chargingState = await this.getStateAsync(`${deviceId}.battery.charging`);\n\n const doorOpenState = await this.getStateAsync(`${deviceId}.status.doorOpen`);\n\n const lockState = await this.getStateAsync(`${deviceId}.status.lockState`);\n\n const charging = chargingState?.val === true;\n\n const hasDoorState =\n doorOpenState !== null &&\n doorOpenState !== undefined &&\n doorOpenState.val !== null &&\n doorOpenState.val !== undefined;\n\n const doorOpen = doorOpenState?.val === true;\n\n const lockStateValue = typeof lockState?.val === \"number\" ? lockState.val : Number(lockState?.val);\n\n let iconState: NukiIconState = \"unknown\";\n\n let icon: string = NUKI_ICONS.unknown;\n\n /*\n * Priorit\u00E4t:\n *\n * 1. Akku l\u00E4dt\n * 2. T\u00FCr offen\n * 3. Schloss zugesperrt\n * 4. Schloss aufgesperrt\n * 5. T\u00FCr geschlossen\n * 6. unbekannt\n */\n\n if (charging) {\n iconState = \"charging\";\n\n icon = NUKI_ICONS.charging;\n } else if (doorOpen) {\n iconState = \"door_open\";\n\n icon = NUKI_ICONS.doorOpen;\n } else if (lockStateValue === 1) {\n iconState = \"locked\";\n\n icon = NUKI_ICONS.locked;\n } else if (lockStateValue === 3 || lockStateValue === 5 || lockStateValue === 6) {\n iconState = \"unlocked\";\n\n icon = NUKI_ICONS.unlocked;\n } else if (hasDoorState && !doorOpen) {\n iconState = \"door_closed\";\n\n icon = NUKI_ICONS.doorClosed;\n }\n\n await this.setStateAsync(`${deviceId}.status.iconState`, iconState, true);\n\n await this.setStateAsync(`${deviceId}.status.icon`, icon, true);\n }\n\n /*\n * ============================================================\n * STATUS / EVENTS\n * ============================================================\n */\n\n private async handleLockState(deviceId: string, payload: string): Promise<void> {\n const state = Number(payload);\n\n if (!Number.isFinite(state)) {\n return;\n }\n\n await this.setMappedNumberState(`${deviceId}.status.lockState`, \"Lock state\", payload, LOCK_STATES);\n\n await this.setStringState(\n `${deviceId}.status.lockStateText`,\n \"Lock state text\",\n this.getMappedText(LOCK_STATES, state),\n );\n\n await this.setStateAsync(`${deviceId}.status.locked`, state === 1, true);\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async handleDoorState(deviceId: string, payload: string): Promise<void> {\n const state = Number(payload);\n\n if (!Number.isFinite(state)) {\n return;\n }\n\n await this.setMappedNumberState(`${deviceId}.status.doorState`, \"Door state\", payload, DOOR_STATES);\n\n await this.setStringState(\n `${deviceId}.status.doorStateText`,\n \"Door state text\",\n this.getMappedText(DOOR_STATES, state),\n );\n\n await this.setStateAsync(`${deviceId}.status.doorOpen`, state === 3, true);\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async handleLockActionEvent(deviceId: string, payload: string): Promise<void> {\n const parts = payload.split(\",\");\n\n if (parts.length < 5) {\n this.log.warn(`Invalid lockActionEvent: ${payload}`);\n\n return;\n }\n\n const action = Number(parts[0]);\n\n const trigger = Number(parts[1]);\n\n const authId = Number(parts[2]);\n\n const codeId = Number(parts[3]);\n\n const source = Number(parts[4]);\n\n if (\n !Number.isFinite(action) ||\n !Number.isFinite(trigger) ||\n !Number.isFinite(authId) ||\n !Number.isFinite(codeId) ||\n !Number.isFinite(source)\n ) {\n this.log.warn(`Invalid numeric values in lockActionEvent: ${payload}`);\n\n return;\n }\n\n const now = new Date().toISOString();\n\n await this.setStateAsync(`${deviceId}.activity.lastAction`, action, true);\n\n await this.setStringState(\n `${deviceId}.activity.lastActionText`,\n \"Last action text\",\n this.getMappedText(LOCK_ACTIONS, action),\n );\n\n await this.setStringState(`${deviceId}.activity.lastDate`, \"Last date\", now, \"date\");\n\n await this.setStateAsync(`${deviceId}.advanced.authId`, authId, true);\n\n await this.setStateAsync(`${deviceId}.advanced.codeId`, codeId, true);\n\n await this.setStateAsync(`${deviceId}.advanced.source`, source, true);\n\n await this.setStateAsync(`${deviceId}.advanced.trigger`, trigger, true);\n\n const configuredKeypadUser = codeId > 0 ? this.findConfiguredKeypadUser(codeId) : undefined;\n\n const resolvedUserName = this.findAuthorizationName(deviceId, authId);\n\n const displayUser =\n configuredKeypadUser ??\n resolvedUserName ??\n (codeId > 0 ? `Unknown (Code-ID ${codeId})` : `Unknown (Auth-ID ${authId})`);\n\n await this.setStringState(`${deviceId}.activity.lastUser`, \"Last user\", displayUser);\n\n if (codeId > 0) {\n let type = `Source ${source}`;\n\n switch (source) {\n case 0:\n type = \"Back button\";\n break;\n\n case 1:\n type = \"Code\";\n break;\n\n case 2:\n type = \"Fingerprint\";\n break;\n }\n\n await this.setStringState(`${deviceId}.keypad.lastType`, \"Last access type\", type);\n\n await this.setStringState(`${deviceId}.keypad.lastUser`, \"Last user\", displayUser);\n\n await this.setStringState(`${deviceId}.keypad.lastTimestamp`, \"Last access timestamp\", now, \"date\");\n\n if (source === 2) {\n this.log.info(`Fingerprint detected: authId=${authId}, codeId=${codeId}, user=${displayUser}`);\n } else if (source === 1) {\n this.log.info(`Keypad code detected: authId=${authId}, codeId=${codeId}, user=${displayUser}`);\n }\n }\n\n this.log.debug(\n `Lock action processed: action=${action}, trigger=${trigger}, authId=${authId}, codeId=${codeId}, source=${source}`,\n );\n }\n\n private findConfiguredKeypadUser(codeId: number): string | undefined {\n const users = Array.isArray(this.config.keypadUsers) ? this.config.keypadUsers : [];\n\n const match = users.find(user => Number(user.codeId) === codeId);\n\n if (!match || typeof match.name !== \"string\") {\n return undefined;\n }\n\n const name = match.name.trim();\n\n return name || undefined;\n }\n\n private findAuthorizationName(deviceId: string, authId: number): string | undefined {\n const smartlockId = this.webSmartlockIds.get(this.normalizeDeviceId(deviceId));\n\n if (smartlockId === undefined) {\n return undefined;\n }\n\n return this.authorizationNames.get(smartlockId)?.get(authId);\n }\n\n /*\n * ============================================================\n * COMMANDS\n * ============================================================\n */\n\n private async createCommandState(id: string, name: string): Promise<void> {\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type: \"boolean\",\n role: \"button\",\n read: false,\n write: true,\n def: false,\n },\n native: {},\n });\n\n const state = await this.getStateAsync(id);\n\n if (!state) {\n await this.setStateAsync(id, false, true);\n }\n }\n\n private async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n if (!state || state.ack || state.val !== true) {\n return;\n }\n\n const prefix = `${this.namespace}.`;\n\n if (!id.startsWith(prefix)) {\n return;\n }\n\n const relativeId = id.substring(prefix.length);\n\n const parts = relativeId.split(\".\");\n\n if (parts.length !== 3 || parts[1] !== \"commands\") {\n return;\n }\n\n const deviceId = parts[0];\n\n const command = parts[2];\n\n try {\n switch (command) {\n case \"unlock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"1\");\n break;\n\n case \"lock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"2\");\n break;\n\n case \"unlatch\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"3\");\n break;\n\n case \"lockNgo\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"4\");\n break;\n\n case \"lockNgoUnlatch\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"5\");\n break;\n\n case \"fullLock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"6\");\n break;\n\n default:\n return;\n }\n\n await this.setStateAsync(relativeId, false, true);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not execute command ${command}: ${message}`);\n\n await this.setStateAsync(relativeId, false, true);\n }\n }\n\n private async publishNukiCommand(topic: string, payload: string): Promise<void> {\n if (!this.mqttBroker) {\n throw new Error(\"MQTT broker is not running\");\n }\n\n await new Promise<void>((resolve, reject) => {\n this.mqttBroker?.publish(\n {\n cmd: \"publish\",\n topic,\n payload: Buffer.from(payload),\n qos: 2,\n dup: false,\n retain: false,\n },\n error => {\n if (error) {\n reject(error);\n\n return;\n }\n\n resolve();\n },\n );\n });\n\n this.log.info(`Nuki command published: ${topic} = ${payload}`);\n }\n\n private async handleCommandResponse(deviceId: string, payload: string): Promise<void> {\n const result = Number(payload);\n\n if (!Number.isFinite(result)) {\n return;\n }\n\n await this.setNumberState(`${deviceId}.commands.lastResult`, \"Last command result\", payload);\n\n await this.setStringState(\n `${deviceId}.commands.lastResultText`,\n \"Last command result text\",\n result === 0 ? \"Successful\" : `Error ${result}`,\n );\n }\n\n /*\n * ============================================================\n * WEB API\n * ============================================================\n */\n\n private async startWebApi(): Promise<void> {\n const token = String(this.config.webApiToken ?? \"\").trim();\n\n if (!token) {\n this.log.warn(\"Nuki Web API is enabled but no API token is configured\");\n\n return;\n }\n\n const intervalSeconds = Math.min(86400, Math.max(60, Number(this.config.webApiInterval) || 300));\n\n this.log.info(`Nuki Web API enabled, update interval: ${intervalSeconds} seconds`);\n\n try {\n await this.updateNukiWebData();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Initial Nuki Web API update failed: ${message}`);\n }\n\n this.scheduleWebApiUpdate(intervalSeconds);\n }\n\n private scheduleWebApiUpdate(intervalSeconds: number): void {\n if (this.webApiStopping) {\n return;\n }\n\n this.webApiTimer = this.setTimeout(() => {\n this.webApiTimer = null;\n\n if (this.webApiStopping) {\n return;\n }\n\n void this.updateNukiWebData()\n .catch(error => {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Nuki Web API update failed: ${message}`);\n })\n .finally(() => {\n this.scheduleWebApiUpdate(intervalSeconds);\n });\n }, intervalSeconds * 1000);\n }\n\n private async updateNukiWebData(): Promise<void> {\n const devices = await this.nukiWebRequest<NukiWebDevice[]>(\"/smartlock\");\n\n if (!Array.isArray(devices)) {\n throw new Error(\"Invalid response from /smartlock\");\n }\n\n let authorizations: NukiWebAuthorization[] = [];\n\n try {\n const result = await this.nukiWebRequest<NukiWebAuthorization[]>(\"/smartlock/auth\");\n\n if (Array.isArray(result)) {\n authorizations = result;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not load Nuki authorizations: ${message}`);\n }\n\n for (const device of devices) {\n const smartlockId = Number(device.smartlockId);\n\n if (!Number.isFinite(smartlockId)) {\n continue;\n }\n\n const conversion = this.getMqttDeviceIdFromWebDevice(device);\n\n const deviceId = conversion.deviceId;\n\n if (conversion.originalHex !== conversion.deviceId) {\n await this.cleanupOldWebDeviceId(conversion.originalHex, conversion.deviceId);\n }\n\n this.webSmartlockIds.set(this.normalizeDeviceId(deviceId), smartlockId);\n\n await this.ensureDevice(deviceId);\n\n await this.storeWebDeviceData(deviceId, device);\n\n const deviceAuths = authorizations.filter(auth => Number(auth.smartlockId) === smartlockId);\n\n await this.storeWebAuthorizations(deviceId, smartlockId, deviceAuths);\n\n try {\n const logs = await this.nukiWebRequest<NukiWebLog[]>(`/smartlock/${smartlockId}/log?limit=20`);\n\n if (Array.isArray(logs)) {\n await this.storeWebLogs(deviceId, logs);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not load activity logs for ${deviceId}: ${message}`);\n }\n }\n }\n\n private async nukiWebRequest<T>(path: string): Promise<T> {\n const token = String(this.config.webApiToken ?? \"\").trim();\n\n if (!token) {\n throw new Error(\"Nuki Web API token missing\");\n }\n\n const response = await fetch(`https://api.nuki.io${path}`, {\n signal: AbortSignal.timeout(15_000),\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n\n throw new Error(\n `HTTP ${response.status} ${response.statusText}${body ? `: ${body.substring(0, 300)}` : \"\"}`,\n );\n }\n\n return (await response.json()) as T;\n }\n\n private getMqttDeviceIdFromWebDevice(device: NukiWebDevice): {\n deviceId: string;\n originalHex: string;\n } {\n try {\n const originalHex = BigInt(device.smartlockId).toString(16).toUpperCase();\n\n const deviceId = originalHex.slice(-8).padStart(8, \"0\");\n\n return {\n deviceId,\n originalHex,\n };\n } catch {\n const value = String(device.smartlockId);\n\n return {\n deviceId: value,\n originalHex: value,\n };\n }\n }\n\n private normalizeDeviceId(deviceId: string): string {\n const normalized = deviceId.trim().toUpperCase().replace(/^0X/, \"\");\n\n return normalized.slice(-8).padStart(8, \"0\");\n }\n\n private async storeWebDeviceData(deviceId: string, device: NukiWebDevice): Promise<void> {\n await this.setStateAsync(`${deviceId}.advanced.smartlockId`, Number(device.smartlockId), true);\n\n if (typeof device.name === \"string\") {\n await this.setStringState(`${deviceId}.device.name`, \"Name\", device.name);\n }\n\n if (typeof device.type === \"number\") {\n await this.setMappedNumberState(\n `${deviceId}.device.deviceType`,\n \"Device type\",\n String(device.type),\n DEVICE_TYPES,\n );\n }\n\n if (typeof device.serverState === \"number\") {\n await this.setStateAsync(`${deviceId}.advanced.serverState`, device.serverState, true);\n\n await this.setStateAsync(`${deviceId}.device.online`, device.serverState === 0, true);\n }\n\n const state = device.state;\n\n if (!state) {\n await this.updateDeviceIcon(deviceId);\n\n return;\n }\n\n if (typeof state.batteryCharge === \"number\") {\n await this.setStateAsync(`${deviceId}.battery.percent`, state.batteryCharge, true);\n }\n\n if (typeof state.batteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.critical`, state.batteryCritical, true);\n }\n\n if (typeof state.batteryCharging === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.charging`, state.batteryCharging, true);\n }\n\n if (typeof state.keypadBatteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.keypadCritical`, state.keypadBatteryCritical, true);\n }\n\n if (typeof state.doorsensorBatteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.doorSensorCritical`, state.doorsensorBatteryCritical, true);\n }\n\n if (typeof state.state === \"number\") {\n await this.setStateAsync(`${deviceId}.status.lockState`, state.state, true);\n\n await this.setStringState(\n `${deviceId}.status.lockStateText`,\n \"Lock state text\",\n this.getMappedText(LOCK_STATES, state.state),\n );\n\n await this.setStateAsync(`${deviceId}.status.locked`, state.state === 1, true);\n }\n\n if (typeof state.doorState === \"number\") {\n await this.setStateAsync(`${deviceId}.status.doorState`, state.doorState, true);\n\n await this.setStringState(\n `${deviceId}.status.doorStateText`,\n \"Door state text\",\n this.getMappedText(DOOR_STATES, state.doorState),\n );\n\n await this.setStateAsync(`${deviceId}.status.doorOpen`, state.doorState === 3, true);\n }\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async storeWebAuthorizations(\n deviceId: string,\n smartlockId: number,\n authorizations: NukiWebAuthorization[],\n ): Promise<void> {\n const authNames = new Map<number, string>();\n\n for (const auth of authorizations) {\n if (typeof auth.authId === \"number\" && typeof auth.name === \"string\") {\n authNames.set(auth.authId, auth.name);\n }\n }\n\n this.authorizationNames.set(smartlockId, authNames);\n\n const safe = authorizations.map(auth => ({\n id: auth.id,\n smartlockId: auth.smartlockId,\n authId: auth.authId,\n type: auth.type,\n name: auth.name,\n enabled: auth.enabled,\n remoteAllowed: auth.remoteAllowed,\n }));\n\n await this.setStringState(\n `${deviceId}.advanced.authorizations`,\n \"Authorizations\",\n JSON.stringify(safe),\n \"json\",\n );\n }\n\n private async storeWebLogs(deviceId: string, logs: NukiWebLog[]): Promise<void> {\n if (logs.length === 0) {\n return;\n }\n\n const sorted = [...logs].sort((a, b) => {\n const bTime = Date.parse(b.date ?? \"\");\n\n const aTime = Date.parse(a.date ?? \"\");\n\n return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);\n });\n\n const last = sorted[0];\n\n if (!last) {\n return;\n }\n\n if (typeof last.action === \"number\") {\n await this.setStateAsync(`${deviceId}.activity.lastAction`, last.action, true);\n\n await this.setStringState(\n `${deviceId}.activity.lastActionText`,\n \"Last action text\",\n this.getMappedText(LOCK_ACTIONS, last.action),\n );\n }\n\n if (typeof last.name === \"string\") {\n await this.setStringState(`${deviceId}.activity.lastUser`, \"Last user\", last.name);\n }\n\n if (typeof last.date === \"string\") {\n await this.setStringState(`${deviceId}.activity.lastDate`, \"Last date\", last.date, \"date\");\n }\n\n await this.setStateAsync(`${deviceId}.advanced.codeId`, 0, true);\n\n if (typeof last.authId === \"number\") {\n await this.setStateAsync(`${deviceId}.advanced.authId`, last.authId, true);\n }\n }\n\n /*\n * ============================================================\n * HELPERS\n * ============================================================\n */\n\n private getMappedText(map: Record<number, string>, value: number): string {\n return map[value] ?? `Unknown (${value})`;\n }\n\n private async ensureStateObject(\n id: string,\n name: string,\n type: \"string\" | \"number\" | \"boolean\",\n role: string,\n write = false,\n unit?: string,\n ): Promise<void> {\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type,\n role,\n read: true,\n write,\n ...(unit\n ? {\n unit,\n }\n : {}),\n },\n native: {},\n });\n }\n\n private async ensureMappedStateObject(id: string, name: string, states: Record<number, string>): Promise<void> {\n const ioBrokerStates: Record<string, string> = {};\n\n for (const [key, text] of Object.entries(states)) {\n ioBrokerStates[key] = text;\n }\n\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n states: ioBrokerStates,\n },\n native: {},\n });\n }\n\n private async setStringState(id: string, name: string, value: string, role = \"text\"): Promise<void> {\n await this.ensureStateObject(id, name, \"string\", role);\n\n await this.setStateAsync(id, value, true);\n }\n\n private async setNumberState(\n id: string,\n name: string,\n value: string,\n unit?: string,\n role = \"value\",\n ): Promise<void> {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n return;\n }\n\n await this.ensureStateObject(id, name, \"number\", role, false, unit);\n\n await this.setStateAsync(id, numberValue, true);\n }\n\n private async setMappedNumberState(\n id: string,\n name: string,\n value: string,\n states: Record<number, string>,\n ): Promise<void> {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n return;\n }\n\n await this.ensureMappedStateObject(id, name, states);\n\n await this.setStateAsync(id, numberValue, true);\n }\n\n private async setBooleanState(id: string, name: string, value: string, role = \"indicator\"): Promise<void> {\n await this.ensureStateObject(id, name, \"boolean\", role);\n\n const boolValue = value.toLowerCase() === \"true\" || value === \"1\";\n\n await this.setStateAsync(id, boolValue, true);\n }\n\n private sanitizeId(value: string): string {\n return value.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n }\n\n /*\n * ============================================================\n * UNLOAD\n * ============================================================\n */\n\n private onUnload(callback: () => void): void {\n this.webApiStopping = true;\n\n void (async () => {\n try {\n if (this.webApiTimer) {\n this.clearTimeout(this.webApiTimer);\n\n this.webApiTimer = null;\n }\n\n await this.setStateAsync(\"info.connection\", false, true);\n\n if (this.mqttServer) {\n await new Promise<void>(resolve => {\n this.mqttServer?.close(() => resolve());\n });\n\n this.mqttServer = null;\n }\n\n if (this.mqttBroker) {\n await new Promise<void>(resolve => {\n this.mqttBroker?.close(() => resolve());\n });\n\n this.mqttBroker = null;\n }\n\n this.mqttPersistence = null;\n\n this.mqttDatabase = null;\n\n this.log.info(\"Nuki Local adapter stopped\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Error while stopping adapter: ${message}`);\n } finally {\n callback();\n }\n })();\n }\n}\n\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new NukiLocal(options);\n} else {\n (() => new NukiLocal())();\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["aedesPersistenceLevel", "_a", "_b"]
|
|
7
7
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "nuki-local",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.1.3": {
|
|
7
|
+
"en": "New adapter icon. Web API polling limited to 60–86400 seconds without overlapping updates. Corrected state roles, English state texts and configuration translations. Added request timeout and MQTT device ID validation. Reset code IDs when importing Web API activity data. Updated dependencies, tests and documentation. Requires admin >= 7.8.23.",
|
|
8
|
+
"de": "Neues Adapter-Icon. Web-API-Abfrageintervall auf 60–86400 Sekunden begrenzt und überlappende Abfragen verhindert. Datenpunktrollen, englische Zustandstexte und Konfigurationsübersetzungen korrigiert. Anfrage-Timeout und MQTT-Geräte-ID-Prüfung ergänzt. Code-IDs beim Import von Web-API-Aktivitätsdaten zurückgesetzt. Abhängigkeiten, Tests und Dokumentation aktualisiert. Benötigt admin >= 7.8.23.",
|
|
9
|
+
"ru": "Новый значок адаптера. Интервал опроса Web API ограничен 60–86400 секундами; одновременные опросы исключены. Исправлены роли состояний, английские тексты состояний и переводы настроек. Добавлены тайм-аут запросов и проверка идентификаторов устройств MQTT. Идентификаторы кодов сбрасываются при импорте активности Web API. Обновлены зависимости, тесты и документация. Требуется admin >= 7.8.23.",
|
|
10
|
+
"pt": "Novo ícone do adaptador. Intervalo de consulta da Web API limitado a 60–86400 segundos, sem consultas sobrepostas. Corrigidos os papéis dos estados, textos dos estados em inglês e traduções da configuração. Adicionados tempo limite de requisição e validação de IDs de dispositivos MQTT. IDs de códigos redefinidos ao importar atividades da Web API. Dependências, testes e documentação atualizados. Requer admin >= 7.8.23.",
|
|
11
|
+
"nl": "Nieuw adapterpictogram. Web API-interval beperkt tot 60–86400 seconden zonder overlappende aanvragen. Statusrollen, Engelse statusteksten en configuratievertalingen gecorrigeerd. Time-out voor aanvragen en validatie van MQTT-apparaat-ID's toegevoegd. Code-ID's worden teruggezet bij het importeren van Web API-activiteit. Afhankelijkheden, tests en documentatie bijgewerkt. Vereist admin >= 7.8.23.",
|
|
12
|
+
"fr": "Nouvelle icône de l’adaptateur. Intervalle d’interrogation de la Web API limité à 60–86400 secondes, sans requêtes simultanées. Rôles des états, textes des états en anglais et traductions de configuration corrigés. Ajout d’un délai maximal de requête et de la validation des identifiants des appareils MQTT. Identifiants de code réinitialisés lors de l’import des activités de la Web API. Dépendances, tests et documentation mis à jour. Nécessite admin >= 7.8.23.",
|
|
13
|
+
"it": "Nuova icona dell’adattatore. Intervallo di interrogazione della Web API limitato a 60–86400 secondi, senza richieste sovrapposte. Corretti ruoli degli stati, testi degli stati in inglese e traduzioni della configurazione. Aggiunti timeout delle richieste e validazione degli ID dei dispositivi MQTT. ID dei codici azzerati durante l’importazione delle attività della Web API. Aggiornati dipendenze, test e documentazione. Richiede admin >= 7.8.23.",
|
|
14
|
+
"es": "Nuevo icono del adaptador. Intervalo de consulta de la Web API limitado a 60–86400 segundos, sin consultas superpuestas. Corregidos los roles de los estados, los textos de estado en inglés y las traducciones de configuración. Añadidos tiempo límite de solicitudes y validación de IDs de dispositivos MQTT. IDs de códigos restablecidos al importar actividades de la Web API. Dependencias, pruebas y documentación actualizadas. Requiere admin >= 7.8.23.",
|
|
15
|
+
"pl": "Nowa ikona adaptera. Interwał odpytywania Web API ograniczony do 60–86400 sekund, bez nakładających się zapytań. Poprawiono role stanów, angielskie teksty stanów i tłumaczenia konfiguracji. Dodano limit czasu zapytań i walidację identyfikatorów urządzeń MQTT. Identyfikatory kodów są zerowane przy imporcie aktywności Web API. Zaktualizowano zależności, testy i dokumentację. Wymaga admin >= 7.8.23.",
|
|
16
|
+
"uk": "Нова піктограма адаптера. Інтервал опитування Web API обмежено 60–86400 секундами без одночасних запитів. Виправлено ролі станів, англійські тексти станів і переклади налаштувань. Додано тайм-аут запитів і перевірку ідентифікаторів пристроїв MQTT. Ідентифікатори кодів скидаються під час імпорту активності Web API. Оновлено залежності, тести й документацію. Потрібен admin >= 7.8.23.",
|
|
17
|
+
"zh-cn": "更新适配器图标。Web API 轮询间隔限制为 60–86400 秒,避免请求重叠。修正状态角色、英文状态文本和配置翻译。添加请求超时和 MQTT 设备 ID 验证。导入 Web API 活动数据时重置代码 ID。更新依赖项、测试和文档。需要 admin >= 7.8.23。"
|
|
18
|
+
},
|
|
6
19
|
"0.1.0": {
|
|
7
20
|
"en": "Initial public release of the Nuki Local adapter",
|
|
8
21
|
"de": "Erste öffentliche Version des Nuki Local Adapters",
|
|
@@ -70,14 +83,15 @@
|
|
|
70
83
|
"zh-cn": "Nuki 智能锁的本地 MQTT 集成"
|
|
71
84
|
},
|
|
72
85
|
"authors": [
|
|
73
|
-
"helfi9999 <
|
|
86
|
+
"helfi9999 <helfi9999@gmail.com>"
|
|
74
87
|
],
|
|
75
88
|
"keywords": [
|
|
76
89
|
"nuki",
|
|
77
90
|
"smart-lock",
|
|
78
91
|
"mqtt",
|
|
79
92
|
"local",
|
|
80
|
-
"door-lock"
|
|
93
|
+
"door-lock",
|
|
94
|
+
"smarthome"
|
|
81
95
|
],
|
|
82
96
|
"licenseInformation": {
|
|
83
97
|
"type": "free",
|
|
@@ -105,7 +119,7 @@
|
|
|
105
119
|
],
|
|
106
120
|
"globalDependencies": [
|
|
107
121
|
{
|
|
108
|
-
"admin": ">=7.
|
|
122
|
+
"admin": ">=7.8.23"
|
|
109
123
|
}
|
|
110
124
|
]
|
|
111
125
|
},
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.nuki-local",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Local MQTT integration for Nuki Smart Locks",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "helfi9999",
|
|
7
|
-
"email": "
|
|
7
|
+
"email": "helfi9999@gmail.com"
|
|
8
8
|
},
|
|
9
9
|
"contributors": [],
|
|
10
10
|
"homepage": "https://github.com/helfi9999/ioBroker.nuki-local",
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"local",
|
|
17
17
|
"door-lock",
|
|
18
18
|
"smarthome",
|
|
19
|
-
"iobroker",
|
|
20
19
|
"ioBroker"
|
|
21
20
|
],
|
|
22
21
|
"repository": {
|
|
@@ -28,7 +27,7 @@
|
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
29
|
"@iobroker/adapter-core": "^3.4.3",
|
|
31
|
-
"aedes": "^1.
|
|
30
|
+
"aedes": "^1.2.0",
|
|
32
31
|
"aedes-persistence-level": "^9.1.3",
|
|
33
32
|
"level": "^10.0.0"
|
|
34
33
|
},
|
|
@@ -40,10 +39,10 @@
|
|
|
40
39
|
"@iobroker/adapter-dev": "^1.5.0",
|
|
41
40
|
"@iobroker/dev-server": "^0.8.0",
|
|
42
41
|
"@iobroker/eslint-config": "^2.3.4",
|
|
43
|
-
"@iobroker/testing": "^
|
|
42
|
+
"@iobroker/testing": "^6.3.0",
|
|
44
43
|
"@tsconfig/node22": "^22.0.6",
|
|
45
44
|
"@types/iobroker": "npm:@iobroker/types@^7.2.2",
|
|
46
|
-
"@types/node": "^20.
|
|
45
|
+
"@types/node": "^22.20.4",
|
|
47
46
|
"nyc": "^18.0.0",
|
|
48
47
|
"rimraf": "^6.1.3",
|
|
49
48
|
"source-map-support": "^0.5.21",
|