iobroker.nuki-local 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +32 -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 +93 -49
- package/build/main.js.map +2 -2
- package/io-package.json +31 -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,35 @@ Restart:
|
|
|
391
393
|
iobroker restart nuki-local.0
|
|
392
394
|
```
|
|
393
395
|
|
|
394
|
-
##
|
|
396
|
+
## Changelog
|
|
395
397
|
|
|
396
|
-
|
|
398
|
+
### 0.1.4 (2026-10-10)
|
|
397
399
|
|
|
398
|
-
|
|
399
|
-
0.1.0
|
|
400
|
-
```
|
|
400
|
+
- (helfi9999) Added readable fingerprint and keypad access logs with device name, user name and action.
|
|
401
401
|
|
|
402
|
-
|
|
402
|
+
- (helfi9999) Moved raw MQTT messages to debug logging.
|
|
403
|
+
- (helfi9999) Added readable lock and door state change logs using the device name, suppressing duplicate and initial states.
|
|
404
|
+
|
|
405
|
+
### 0.1.3 (2026-10-04)
|
|
406
|
+
|
|
407
|
+
- (helfi9999) Replaced the default adapter icon with a custom Nuki icon.
|
|
408
|
+
- (helfi9999) Limited the Web API polling interval to 60–86400 seconds and prevented overlapping updates.
|
|
409
|
+
- (helfi9999) Corrected access and activity date roles and removed an unused translation key.
|
|
410
|
+
- (helfi9999) Reset the code ID when importing Web API activity data.
|
|
411
|
+
|
|
412
|
+
- (helfi9999) Changed state texts to English and completed configuration label translations.
|
|
413
|
+
- (helfi9999) Corrected command button, authorization JSON and timestamp roles.
|
|
414
|
+
- (helfi9999) Added a Web API request timeout and MQTT device ID validation.
|
|
415
|
+
- (helfi9999) Updated Aedes, Node.js types, testing tools and transitive dependencies.
|
|
416
|
+
- (helfi9999) Added Node.js 26 testing and updated the workflow check action.
|
|
417
|
+
- (helfi9999) Updated documentation, keywords and maintainer contact information.
|
|
418
|
+
- (helfi9999) Adapter requires admin >= 7.8.23 now.
|
|
419
|
+
|
|
420
|
+
### 0.1.2
|
|
421
|
+
|
|
422
|
+
- Improved publishing workflow
|
|
423
|
+
- Added npm trusted publishing support
|
|
424
|
+
- Updated package metadata and repository checks
|
|
403
425
|
|
|
404
426
|
### 0.1.0
|
|
405
427
|
|
|
@@ -420,8 +442,10 @@ Initial functional development version.
|
|
|
420
442
|
- Activity information
|
|
421
443
|
- Dynamic status icons
|
|
422
444
|
|
|
445
|
+
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
|
446
|
+
|
|
423
447
|
## License
|
|
424
448
|
|
|
425
|
-
MIT License
|
|
449
|
+
[MIT License](LICENSE)
|
|
426
450
|
|
|
427
|
-
Copyright (c) 2026 helfi9999
|
|
451
|
+
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,7 +87,9 @@ 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();
|
|
92
|
+
lastLoggedStates = /* @__PURE__ */ new Map();
|
|
91
93
|
initializingDevices = /* @__PURE__ */ new Map();
|
|
92
94
|
webSmartlockIds = /* @__PURE__ */ new Map();
|
|
93
95
|
authorizationNames = /* @__PURE__ */ new Map();
|
|
@@ -181,7 +183,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
181
183
|
return;
|
|
182
184
|
}
|
|
183
185
|
const value = packet.payload.toString();
|
|
184
|
-
this.log.
|
|
186
|
+
this.log.debug(`Nuki MQTT: ${packet.topic} = ${value}`);
|
|
185
187
|
void this.handleNukiMessage(packet.topic, value);
|
|
186
188
|
});
|
|
187
189
|
this.mqttServer = (0, import_node_net.createServer)(this.mqttBroker.handle);
|
|
@@ -236,6 +238,10 @@ class NukiLocal extends utils.Adapter {
|
|
|
236
238
|
if (!deviceId || !property) {
|
|
237
239
|
return;
|
|
238
240
|
}
|
|
241
|
+
if (this.sanitizeId(deviceId) !== deviceId) {
|
|
242
|
+
this.log.debug("Ignoring MQTT message with invalid device ID");
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
239
245
|
await this.ensureDevice(deviceId);
|
|
240
246
|
switch (property) {
|
|
241
247
|
case "name":
|
|
@@ -260,7 +266,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
260
266
|
await this.setBooleanState(`${deviceId}.device.online`, "Online", payload, "indicator.connected");
|
|
261
267
|
break;
|
|
262
268
|
case "timestamp":
|
|
263
|
-
await this.setStringState(`${deviceId}.status.timestamp`, "Timestamp", payload);
|
|
269
|
+
await this.setStringState(`${deviceId}.status.timestamp`, "Timestamp", payload, "date");
|
|
264
270
|
break;
|
|
265
271
|
case "batteryChargeState":
|
|
266
272
|
await this.setNumberState(`${deviceId}.battery.percent`, "Battery", payload, "%", "value.battery");
|
|
@@ -381,7 +387,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
381
387
|
await this.ensureMappedStateObject(`${deviceId}.status.doorState`, "Door state", DOOR_STATES);
|
|
382
388
|
await this.ensureStateObject(`${deviceId}.status.doorStateText`, "Door state text", "string", "text");
|
|
383
389
|
await this.ensureStateObject(`${deviceId}.status.doorOpen`, "Door open", "boolean", "sensor.door");
|
|
384
|
-
await this.ensureStateObject(`${deviceId}.status.timestamp`, "Timestamp", "string", "
|
|
390
|
+
await this.ensureStateObject(`${deviceId}.status.timestamp`, "Timestamp", "string", "date");
|
|
385
391
|
await this.ensureStateObject(`${deviceId}.status.iconState`, "Icon state", "string", "text");
|
|
386
392
|
await this.ensureStateObject(`${deviceId}.status.icon`, "Icon", "string", "text");
|
|
387
393
|
await this.ensureStateObject(`${deviceId}.battery.percent`, "Battery", "number", "value.battery", false, "%");
|
|
@@ -406,11 +412,11 @@ class NukiLocal extends utils.Adapter {
|
|
|
406
412
|
);
|
|
407
413
|
await this.ensureStateObject(`${deviceId}.keypad.lastType`, "Last access type", "string", "text");
|
|
408
414
|
await this.ensureStateObject(`${deviceId}.keypad.lastUser`, "Last user", "string", "text");
|
|
409
|
-
await this.ensureStateObject(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", "string", "
|
|
415
|
+
await this.ensureStateObject(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", "string", "date");
|
|
410
416
|
await this.ensureStateObject(`${deviceId}.activity.lastAction`, "Last action", "number", "value");
|
|
411
417
|
await this.ensureStateObject(`${deviceId}.activity.lastActionText`, "Last action text", "string", "text");
|
|
412
418
|
await this.ensureStateObject(`${deviceId}.activity.lastUser`, "Last user", "string", "text");
|
|
413
|
-
await this.ensureStateObject(`${deviceId}.activity.lastDate`, "Last date", "string", "
|
|
419
|
+
await this.ensureStateObject(`${deviceId}.activity.lastDate`, "Last date", "string", "date");
|
|
414
420
|
await this.ensureStateObject(`${deviceId}.device.name`, "Name", "string", "text");
|
|
415
421
|
await this.ensureStateObject(`${deviceId}.device.firmware`, "Firmware", "string", "text");
|
|
416
422
|
await this.ensureMappedStateObject(`${deviceId}.device.deviceType`, "Device type", DEVICE_TYPES);
|
|
@@ -589,11 +595,26 @@ class NukiLocal extends utils.Adapter {
|
|
|
589
595
|
* STATUS / EVENTS
|
|
590
596
|
* ============================================================
|
|
591
597
|
*/
|
|
598
|
+
async logStatusChange(deviceId, property, value, labels) {
|
|
599
|
+
const key = `${deviceId}.${property}`;
|
|
600
|
+
const previous = this.lastLoggedStates.get(key);
|
|
601
|
+
if (previous === value) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
this.lastLoggedStates.set(key, value);
|
|
605
|
+
if (previous === void 0) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const nameState = await this.getStateAsync(`${deviceId}.device.name`);
|
|
609
|
+
const name = typeof (nameState == null ? void 0 : nameState.val) === "string" && nameState.val.trim() ? nameState.val.trim() : deviceId;
|
|
610
|
+
this.log.info(`${name}: ${this.getMappedText(labels, value)}`);
|
|
611
|
+
}
|
|
592
612
|
async handleLockState(deviceId, payload) {
|
|
593
613
|
const state = Number(payload);
|
|
594
614
|
if (!Number.isFinite(state)) {
|
|
595
615
|
return;
|
|
596
616
|
}
|
|
617
|
+
await this.logStatusChange(deviceId, "lockState", state, LOCK_STATES);
|
|
597
618
|
await this.setMappedNumberState(`${deviceId}.status.lockState`, "Lock state", payload, LOCK_STATES);
|
|
598
619
|
await this.setStringState(
|
|
599
620
|
`${deviceId}.status.lockStateText`,
|
|
@@ -608,6 +629,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
608
629
|
if (!Number.isFinite(state)) {
|
|
609
630
|
return;
|
|
610
631
|
}
|
|
632
|
+
await this.logStatusChange(deviceId, "doorState", state, DOOR_STATES);
|
|
611
633
|
await this.setMappedNumberState(`${deviceId}.status.doorState`, "Door state", payload, DOOR_STATES);
|
|
612
634
|
await this.setStringState(
|
|
613
635
|
`${deviceId}.status.doorStateText`,
|
|
@@ -640,20 +662,20 @@ class NukiLocal extends utils.Adapter {
|
|
|
640
662
|
"Last action text",
|
|
641
663
|
this.getMappedText(LOCK_ACTIONS, action)
|
|
642
664
|
);
|
|
643
|
-
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", now);
|
|
665
|
+
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", now, "date");
|
|
644
666
|
await this.setStateAsync(`${deviceId}.advanced.authId`, authId, true);
|
|
645
667
|
await this.setStateAsync(`${deviceId}.advanced.codeId`, codeId, true);
|
|
646
668
|
await this.setStateAsync(`${deviceId}.advanced.source`, source, true);
|
|
647
669
|
await this.setStateAsync(`${deviceId}.advanced.trigger`, trigger, true);
|
|
648
670
|
const configuredKeypadUser = codeId > 0 ? this.findConfiguredKeypadUser(codeId) : void 0;
|
|
649
671
|
const resolvedUserName = this.findAuthorizationName(deviceId, authId);
|
|
650
|
-
const displayUser = (_a = configuredKeypadUser != null ? configuredKeypadUser : resolvedUserName) != null ? _a : codeId > 0 ? `
|
|
672
|
+
const displayUser = (_a = configuredKeypadUser != null ? configuredKeypadUser : resolvedUserName) != null ? _a : codeId > 0 ? `Unknown (Code-ID ${codeId})` : `Unknown (Auth-ID ${authId})`;
|
|
651
673
|
await this.setStringState(`${deviceId}.activity.lastUser`, "Last user", displayUser);
|
|
652
674
|
if (codeId > 0) {
|
|
653
|
-
let type = `
|
|
675
|
+
let type = `Source ${source}`;
|
|
654
676
|
switch (source) {
|
|
655
677
|
case 0:
|
|
656
|
-
type = "Back
|
|
678
|
+
type = "Back button";
|
|
657
679
|
break;
|
|
658
680
|
case 1:
|
|
659
681
|
type = "Code";
|
|
@@ -664,11 +686,13 @@ class NukiLocal extends utils.Adapter {
|
|
|
664
686
|
}
|
|
665
687
|
await this.setStringState(`${deviceId}.keypad.lastType`, "Last access type", type);
|
|
666
688
|
await this.setStringState(`${deviceId}.keypad.lastUser`, "Last user", displayUser);
|
|
667
|
-
await this.setStringState(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", now);
|
|
668
|
-
if (source === 2) {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
689
|
+
await this.setStringState(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", now, "date");
|
|
690
|
+
if (source === 2 || source === 1) {
|
|
691
|
+
const nameState = await this.getStateAsync(`${deviceId}.device.name`);
|
|
692
|
+
const name = typeof (nameState == null ? void 0 : nameState.val) === "string" && nameState.val.trim() ? nameState.val.trim() : deviceId;
|
|
693
|
+
const accessType = source === 2 ? "Fingerprint" : "Keypad";
|
|
694
|
+
const actionText = this.getMappedText(LOCK_ACTIONS, action);
|
|
695
|
+
this.log.info(`${name}: ${accessType} access by ${displayUser} (${actionText})`);
|
|
672
696
|
}
|
|
673
697
|
}
|
|
674
698
|
this.log.debug(
|
|
@@ -704,7 +728,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
704
728
|
name,
|
|
705
729
|
type: "boolean",
|
|
706
730
|
role: "button",
|
|
707
|
-
read:
|
|
731
|
+
read: false,
|
|
708
732
|
write: true,
|
|
709
733
|
def: false
|
|
710
734
|
},
|
|
@@ -795,7 +819,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
795
819
|
await this.setStringState(
|
|
796
820
|
`${deviceId}.commands.lastResultText`,
|
|
797
821
|
"Last command result text",
|
|
798
|
-
result === 0 ? "
|
|
822
|
+
result === 0 ? "Successful" : `Error ${result}`
|
|
799
823
|
);
|
|
800
824
|
}
|
|
801
825
|
/*
|
|
@@ -810,7 +834,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
810
834
|
this.log.warn("Nuki Web API is enabled but no API token is configured");
|
|
811
835
|
return;
|
|
812
836
|
}
|
|
813
|
-
const intervalSeconds = Math.max(60, Number(this.config.webApiInterval) || 300);
|
|
837
|
+
const intervalSeconds = Math.min(86400, Math.max(60, Number(this.config.webApiInterval) || 300));
|
|
814
838
|
this.log.info(`Nuki Web API enabled, update interval: ${intervalSeconds} seconds`);
|
|
815
839
|
try {
|
|
816
840
|
await this.updateNukiWebData();
|
|
@@ -818,10 +842,22 @@ class NukiLocal extends utils.Adapter {
|
|
|
818
842
|
const message = error instanceof Error ? error.message : String(error);
|
|
819
843
|
this.log.warn(`Initial Nuki Web API update failed: ${message}`);
|
|
820
844
|
}
|
|
821
|
-
this.
|
|
845
|
+
this.scheduleWebApiUpdate(intervalSeconds);
|
|
846
|
+
}
|
|
847
|
+
scheduleWebApiUpdate(intervalSeconds) {
|
|
848
|
+
if (this.webApiStopping) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
this.webApiTimer = this.setTimeout(() => {
|
|
852
|
+
this.webApiTimer = null;
|
|
853
|
+
if (this.webApiStopping) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
822
856
|
void this.updateNukiWebData().catch((error) => {
|
|
823
857
|
const message = error instanceof Error ? error.message : String(error);
|
|
824
858
|
this.log.warn(`Nuki Web API update failed: ${message}`);
|
|
859
|
+
}).finally(() => {
|
|
860
|
+
this.scheduleWebApiUpdate(intervalSeconds);
|
|
825
861
|
});
|
|
826
862
|
}, intervalSeconds * 1e3);
|
|
827
863
|
}
|
|
@@ -873,6 +909,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
873
909
|
throw new Error("Nuki Web API token missing");
|
|
874
910
|
}
|
|
875
911
|
const response = await fetch(`https://api.nuki.io${path}`, {
|
|
912
|
+
signal: AbortSignal.timeout(15e3),
|
|
876
913
|
method: "GET",
|
|
877
914
|
headers: {
|
|
878
915
|
Accept: "application/json",
|
|
@@ -981,7 +1018,12 @@ class NukiLocal extends utils.Adapter {
|
|
|
981
1018
|
enabled: auth.enabled,
|
|
982
1019
|
remoteAllowed: auth.remoteAllowed
|
|
983
1020
|
}));
|
|
984
|
-
await this.setStringState(
|
|
1021
|
+
await this.setStringState(
|
|
1022
|
+
`${deviceId}.advanced.authorizations`,
|
|
1023
|
+
"Authorizations",
|
|
1024
|
+
JSON.stringify(safe),
|
|
1025
|
+
"json"
|
|
1026
|
+
);
|
|
985
1027
|
}
|
|
986
1028
|
async storeWebLogs(deviceId, logs) {
|
|
987
1029
|
if (logs.length === 0) {
|
|
@@ -1009,8 +1051,9 @@ class NukiLocal extends utils.Adapter {
|
|
|
1009
1051
|
await this.setStringState(`${deviceId}.activity.lastUser`, "Last user", last.name);
|
|
1010
1052
|
}
|
|
1011
1053
|
if (typeof last.date === "string") {
|
|
1012
|
-
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", last.date);
|
|
1054
|
+
await this.setStringState(`${deviceId}.activity.lastDate`, "Last date", last.date, "date");
|
|
1013
1055
|
}
|
|
1056
|
+
await this.setStateAsync(`${deviceId}.advanced.codeId`, 0, true);
|
|
1014
1057
|
if (typeof last.authId === "number") {
|
|
1015
1058
|
await this.setStateAsync(`${deviceId}.advanced.authId`, last.authId, true);
|
|
1016
1059
|
}
|
|
@@ -1022,7 +1065,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
1022
1065
|
*/
|
|
1023
1066
|
getMappedText(map, value) {
|
|
1024
1067
|
var _a;
|
|
1025
|
-
return (_a = map[value]) != null ? _a : `
|
|
1068
|
+
return (_a = map[value]) != null ? _a : `Unknown (${value})`;
|
|
1026
1069
|
}
|
|
1027
1070
|
async ensureStateObject(id, name, type, role, write = false, unit) {
|
|
1028
1071
|
await this.extendObjectAsync(id, {
|
|
@@ -1058,8 +1101,8 @@ class NukiLocal extends utils.Adapter {
|
|
|
1058
1101
|
native: {}
|
|
1059
1102
|
});
|
|
1060
1103
|
}
|
|
1061
|
-
async setStringState(id, name, value) {
|
|
1062
|
-
await this.ensureStateObject(id, name, "string",
|
|
1104
|
+
async setStringState(id, name, value, role = "text") {
|
|
1105
|
+
await this.ensureStateObject(id, name, "string", role);
|
|
1063
1106
|
await this.setStateAsync(id, value, true);
|
|
1064
1107
|
}
|
|
1065
1108
|
async setNumberState(id, name, value, unit, role = "value") {
|
|
@@ -1092,10 +1135,11 @@ class NukiLocal extends utils.Adapter {
|
|
|
1092
1135
|
* ============================================================
|
|
1093
1136
|
*/
|
|
1094
1137
|
onUnload(callback) {
|
|
1138
|
+
this.webApiStopping = true;
|
|
1095
1139
|
void (async () => {
|
|
1096
1140
|
try {
|
|
1097
1141
|
if (this.webApiTimer) {
|
|
1098
|
-
|
|
1142
|
+
this.clearTimeout(this.webApiTimer);
|
|
1099
1143
|
this.webApiTimer = null;
|
|
1100
1144
|
}
|
|
1101
1145
|
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 lastLoggedStates = new Map<string, number>();\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.debug(`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 logStatusChange(\n deviceId: string,\n property: string,\n value: number,\n labels: Record<number, string>,\n ): Promise<void> {\n const key = `${deviceId}.${property}`;\n const previous = this.lastLoggedStates.get(key);\n\n if (previous === value) {\n return;\n }\n\n this.lastLoggedStates.set(key, value);\n\n if (previous === undefined) {\n return;\n }\n\n const nameState = await this.getStateAsync(`${deviceId}.device.name`);\n const name = typeof nameState?.val === \"string\" && nameState.val.trim() ? nameState.val.trim() : deviceId;\n\n this.log.info(`${name}: ${this.getMappedText(labels, value)}`);\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.logStatusChange(deviceId, \"lockState\", state, LOCK_STATES);\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.logStatusChange(deviceId, \"doorState\", state, DOOR_STATES);\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 || source === 1) {\n const nameState = await this.getStateAsync(`${deviceId}.device.name`);\n const name =\n typeof nameState?.val === \"string\" && nameState.val.trim() ? nameState.val.trim() : deviceId;\n const accessType = source === 2 ? \"Fingerprint\" : \"Keypad\";\n const actionText = this.getMappedText(LOCK_ACTIONS, action);\n\n this.log.info(`${name}: ${accessType} access by ${displayUser} (${actionText})`);\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,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "nuki-local",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.1.4": {
|
|
7
|
+
"en": "Improved lock, door, fingerprint and keypad logs with device and user names. Raw MQTT messages now use debug logging; duplicate and initial state messages are suppressed.",
|
|
8
|
+
"de": "Lesbarere Schloss-, Tür-, Fingerprint- und Keypad-Meldungen mit Geräte- und Benutzernamen. MQTT-Rohmeldungen erscheinen jetzt im Debug-Log; doppelte und initiale Statusmeldungen werden unterdrückt.",
|
|
9
|
+
"ru": "Улучшены сообщения о замке, двери, отпечатках пальцев и клавиатуре с именами устройств и пользователей. Исходные сообщения MQTT перенесены в журнал отладки; повторные и начальные сообщения о состоянии не выводятся.",
|
|
10
|
+
"pt": "Melhorados os logs da fechadura, porta, impressão digital e teclado com nomes de dispositivos e usuários. Mensagens MQTT brutas usam o nível debug; estados duplicados e iniciais não são registrados.",
|
|
11
|
+
"nl": "Duidelijkere logmeldingen voor slot, deur, vingerafdruk en keypad met apparaat- en gebruikersnamen. Ruwe MQTT-berichten gebruiken nu debug; dubbele en eerste statusmeldingen worden onderdrukt.",
|
|
12
|
+
"fr": "Messages du verrou, de la porte, des empreintes et du clavier améliorés avec les noms des appareils et des utilisateurs. Les messages MQTT bruts utilisent le niveau debug ; les états répétés et initiaux ne sont pas journalisés.",
|
|
13
|
+
"it": "Migliorati i messaggi di serratura, porta, impronta digitale e tastierino con nomi dei dispositivi e degli utenti. I messaggi MQTT grezzi usano il livello debug; gli stati duplicati e iniziali non vengono registrati.",
|
|
14
|
+
"es": "Mejorados los mensajes de cerradura, puerta, huella digital y teclado con nombres de dispositivos y usuarios. Los mensajes MQTT sin procesar usan el nivel debug; se omiten los estados duplicados e iniciales.",
|
|
15
|
+
"pl": "Czytelniejsze komunikaty zamka, drzwi, odcisków palców i klawiatury z nazwami urządzeń i użytkowników. Surowe komunikaty MQTT używają poziomu debug; powtórzone i początkowe stany nie są zapisywane w logu.",
|
|
16
|
+
"uk": "Покращено повідомлення про замок, двері, відбитки пальців і клавіатуру з іменами пристроїв та користувачів. Необроблені повідомлення MQTT перенесено на рівень debug; повторні й початкові стани не виводяться.",
|
|
17
|
+
"zh-cn": "改进锁、门、指纹和键盘日志,显示设备和用户名称。原始 MQTT 消息改为 debug 级别;不再记录重复和初始状态消息。"
|
|
18
|
+
},
|
|
19
|
+
"0.1.3": {
|
|
20
|
+
"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.",
|
|
21
|
+
"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.",
|
|
22
|
+
"ru": "Новый значок адаптера. Интервал опроса Web API ограничен 60–86400 секундами; одновременные опросы исключены. Исправлены роли состояний, английские тексты состояний и переводы настроек. Добавлены тайм-аут запросов и проверка идентификаторов устройств MQTT. Идентификаторы кодов сбрасываются при импорте активности Web API. Обновлены зависимости, тесты и документация. Требуется admin >= 7.8.23.",
|
|
23
|
+
"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.",
|
|
24
|
+
"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.",
|
|
25
|
+
"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.",
|
|
26
|
+
"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.",
|
|
27
|
+
"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.",
|
|
28
|
+
"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.",
|
|
29
|
+
"uk": "Нова піктограма адаптера. Інтервал опитування Web API обмежено 60–86400 секундами без одночасних запитів. Виправлено ролі станів, англійські тексти станів і переклади налаштувань. Додано тайм-аут запитів і перевірку ідентифікаторів пристроїв MQTT. Ідентифікатори кодів скидаються під час імпорту активності Web API. Оновлено залежності, тести й документацію. Потрібен admin >= 7.8.23.",
|
|
30
|
+
"zh-cn": "更新适配器图标。Web API 轮询间隔限制为 60–86400 秒,避免请求重叠。修正状态角色、英文状态文本和配置翻译。添加请求超时和 MQTT 设备 ID 验证。导入 Web API 活动数据时重置代码 ID。更新依赖项、测试和文档。需要 admin >= 7.8.23。"
|
|
31
|
+
},
|
|
6
32
|
"0.1.0": {
|
|
7
33
|
"en": "Initial public release of the Nuki Local adapter",
|
|
8
34
|
"de": "Erste öffentliche Version des Nuki Local Adapters",
|
|
@@ -70,14 +96,15 @@
|
|
|
70
96
|
"zh-cn": "Nuki 智能锁的本地 MQTT 集成"
|
|
71
97
|
},
|
|
72
98
|
"authors": [
|
|
73
|
-
"helfi9999 <
|
|
99
|
+
"helfi9999 <helfi9999@gmail.com>"
|
|
74
100
|
],
|
|
75
101
|
"keywords": [
|
|
76
102
|
"nuki",
|
|
77
103
|
"smart-lock",
|
|
78
104
|
"mqtt",
|
|
79
105
|
"local",
|
|
80
|
-
"door-lock"
|
|
106
|
+
"door-lock",
|
|
107
|
+
"smarthome"
|
|
81
108
|
],
|
|
82
109
|
"licenseInformation": {
|
|
83
110
|
"type": "free",
|
|
@@ -105,7 +132,7 @@
|
|
|
105
132
|
],
|
|
106
133
|
"globalDependencies": [
|
|
107
134
|
{
|
|
108
|
-
"admin": ">=7.
|
|
135
|
+
"admin": ">=7.8.23"
|
|
109
136
|
}
|
|
110
137
|
]
|
|
111
138
|
},
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.nuki-local",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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.5",
|
|
47
46
|
"nyc": "^18.0.0",
|
|
48
47
|
"rimraf": "^6.1.3",
|
|
49
48
|
"source-map-support": "^0.5.21",
|