iobroker.nuki-local 0.1.3 → 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/README.md +8 -0
- package/build/main.js +24 -5
- package/build/main.js.map +2 -2
- package/io-package.json +14 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -394,6 +394,14 @@ iobroker restart nuki-local.0
|
|
|
394
394
|
```
|
|
395
395
|
|
|
396
396
|
## Changelog
|
|
397
|
+
|
|
398
|
+
### 0.1.4 (2026-10-10)
|
|
399
|
+
|
|
400
|
+
- (helfi9999) Added readable fingerprint and keypad access logs with device name, user name and action.
|
|
401
|
+
|
|
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
|
+
|
|
397
405
|
### 0.1.3 (2026-10-04)
|
|
398
406
|
|
|
399
407
|
- (helfi9999) Replaced the default adapter icon with a custom Nuki icon.
|
package/build/main.js
CHANGED
|
@@ -89,6 +89,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
89
89
|
webApiTimer = null;
|
|
90
90
|
webApiStopping = false;
|
|
91
91
|
initializedDevices = /* @__PURE__ */ new Set();
|
|
92
|
+
lastLoggedStates = /* @__PURE__ */ new Map();
|
|
92
93
|
initializingDevices = /* @__PURE__ */ new Map();
|
|
93
94
|
webSmartlockIds = /* @__PURE__ */ new Map();
|
|
94
95
|
authorizationNames = /* @__PURE__ */ new Map();
|
|
@@ -182,7 +183,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
182
183
|
return;
|
|
183
184
|
}
|
|
184
185
|
const value = packet.payload.toString();
|
|
185
|
-
this.log.
|
|
186
|
+
this.log.debug(`Nuki MQTT: ${packet.topic} = ${value}`);
|
|
186
187
|
void this.handleNukiMessage(packet.topic, value);
|
|
187
188
|
});
|
|
188
189
|
this.mqttServer = (0, import_node_net.createServer)(this.mqttBroker.handle);
|
|
@@ -594,11 +595,26 @@ class NukiLocal extends utils.Adapter {
|
|
|
594
595
|
* STATUS / EVENTS
|
|
595
596
|
* ============================================================
|
|
596
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
|
+
}
|
|
597
612
|
async handleLockState(deviceId, payload) {
|
|
598
613
|
const state = Number(payload);
|
|
599
614
|
if (!Number.isFinite(state)) {
|
|
600
615
|
return;
|
|
601
616
|
}
|
|
617
|
+
await this.logStatusChange(deviceId, "lockState", state, LOCK_STATES);
|
|
602
618
|
await this.setMappedNumberState(`${deviceId}.status.lockState`, "Lock state", payload, LOCK_STATES);
|
|
603
619
|
await this.setStringState(
|
|
604
620
|
`${deviceId}.status.lockStateText`,
|
|
@@ -613,6 +629,7 @@ class NukiLocal extends utils.Adapter {
|
|
|
613
629
|
if (!Number.isFinite(state)) {
|
|
614
630
|
return;
|
|
615
631
|
}
|
|
632
|
+
await this.logStatusChange(deviceId, "doorState", state, DOOR_STATES);
|
|
616
633
|
await this.setMappedNumberState(`${deviceId}.status.doorState`, "Door state", payload, DOOR_STATES);
|
|
617
634
|
await this.setStringState(
|
|
618
635
|
`${deviceId}.status.doorStateText`,
|
|
@@ -670,10 +687,12 @@ class NukiLocal extends utils.Adapter {
|
|
|
670
687
|
await this.setStringState(`${deviceId}.keypad.lastType`, "Last access type", type);
|
|
671
688
|
await this.setStringState(`${deviceId}.keypad.lastUser`, "Last user", displayUser);
|
|
672
689
|
await this.setStringState(`${deviceId}.keypad.lastTimestamp`, "Last access timestamp", now, "date");
|
|
673
|
-
if (source === 2) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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})`);
|
|
677
696
|
}
|
|
678
697
|
}
|
|
679
698
|
this.log.debug(
|
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: \"Uncalibrated\",\n 1: \"Locked\",\n 2: \"Unlocking\",\n 3: \"Unlocked\",\n 4: \"Locking\",\n 5: \"Unlatched\",\n 6: \"Unlocked (Lock'n'Go)\",\n 7: \"Unlatching\",\n 253: \"Boot run\",\n 254: \"Motor blocked\",\n 255: \"Unknown state\",\n};\n\nconst DOOR_STATES: Record<number, string> = {\n 1: \"Door sensor disabled\",\n 2: \"Door closed\",\n 3: \"Door open\",\n 4: \"Door state unknown\",\n 5: \"Door sensor calibrating\",\n 16: \"Door sensor uncalibrated\",\n 240: \"Door sensor tampered\",\n 255: \"Unknown\",\n};\n\nconst LOCK_ACTIONS: Record<number, string> = {\n 1: \"Unlock\",\n 2: \"Lock\",\n 3: \"Unlatch\",\n 4: \"Lock'n'Go\",\n 5: \"Lock'n'Go with unlatch\",\n 6: \"Full lock\",\n 80: \"Fob without action\",\n 90: \"Button without action\",\n};\n\nconst NUKI_ICONS = {\n locked: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_locked.png\",\n unlocked: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_unlocked.png\",\n doorOpen: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_door_open.png\",\n doorClosed: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_door_closed.png\",\n charging: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_charging.png\",\n pairing: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_pairing.png\",\n unknown: \"/adapter/nuki-local/icons/Nuki_Vis/nuki_unknown.png\",\n} as const;\n\ntype NukiIconState = \"locked\" | \"unlocked\" | \"door_open\" | \"door_closed\" | \"charging\" | \"pairing\" | \"unknown\";\n\ninterface NukiWebState {\n mode?: number;\n state?: number;\n trigger?: number;\n lastAction?: number;\n batteryCritical?: boolean;\n batteryCharging?: boolean;\n batteryCharge?: number;\n keypadBatteryCritical?: boolean;\n doorsensorBatteryCritical?: boolean;\n doorState?: number;\n}\n\ninterface NukiWebDevice {\n smartlockId: number;\n type?: number;\n name?: string;\n serverState?: number;\n state?: NukiWebState;\n}\n\ninterface NukiWebAuthorization {\n id?: string;\n smartlockId?: number;\n authId?: number;\n code?: number;\n type?: number;\n name?: string;\n enabled?: boolean;\n remoteAllowed?: boolean;\n}\n\ninterface NukiWebLog {\n id?: string;\n smartlockId?: number;\n authId?: number;\n name?: string;\n action?: number;\n state?: number;\n trigger?: number;\n date?: string;\n}\n\nclass NukiLocal extends utils.Adapter {\n private mqttBroker: AedesInstance | null = null;\n private mqttServer: Server | null = null;\n private mqttDatabase: Level | null = null;\n\n private mqttPersistence: AedesPersistence | null = null;\n\n private webApiTimer: ioBroker.Timeout | undefined | null = null;\n\n private webApiStopping = false;\n\n private readonly initializedDevices = new Set<string>();\n\n private readonly initializingDevices = new Map<string, Promise<void>>();\n\n private readonly webSmartlockIds = new Map<string, number>();\n\n private readonly authorizationNames = new Map<number, Map<number, string>>();\n\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({\n ...options,\n name: \"nuki-local\",\n });\n\n this.on(\"ready\", this.onReady.bind(this));\n\n this.on(\"stateChange\", (id, state) => {\n void this.onStateChange(id, state);\n });\n\n this.on(\"unload\", this.onUnload.bind(this));\n }\n\n private async onReady(): Promise<void> {\n await this.ensureInstanceObjects();\n\n await this.setStateAsync(\"info.connection\", false, true);\n\n this.subscribeStates(\"*\");\n\n try {\n await this.startMqttBroker();\n\n await this.restoreRetainedNukiStates();\n\n if (this.config.webApiEnabled) {\n await this.startWebApi();\n }\n\n await this.setStateAsync(\"info.connection\", true, true);\n\n this.log.info(\"Nuki Local adapter started\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not start adapter: ${message}`);\n\n await this.setStateAsync(\"info.connection\", false, true);\n }\n }\n\n private async ensureInstanceObjects(): Promise<void> {\n await this.extendObjectAsync(\"info.connection\", {\n type: \"state\",\n common: {\n name: \"Connection\",\n type: \"boolean\",\n role: \"indicator.connected\",\n read: true,\n write: false,\n def: false,\n },\n native: {},\n });\n }\n\n /*\n * ============================================================\n * MQTT\n * ============================================================\n */\n\n private async startMqttBroker(): Promise<void> {\n const port = Number(this.config.mqttPort) || 1883;\n\n const username = String(this.config.mqttUsername ?? \"nuki\");\n\n const password = String(this.config.mqttPassword ?? \"\");\n\n const instanceDataDir = utils.getAbsoluteInstanceDataDir(this);\n\n const mqttDataDir = join(instanceDataDir, \"mqtt-leveldb\");\n\n await mkdir(mqttDataDir, {\n recursive: true,\n });\n\n this.log.info(`MQTT persistence directory: ${mqttDataDir}`);\n\n this.mqttDatabase = new Level(mqttDataDir);\n\n this.mqttPersistence = aedesPersistenceLevel(this.mqttDatabase);\n\n this.mqttBroker = await Aedes.createBroker({\n persistence: this.mqttPersistence,\n });\n\n this.mqttBroker.authenticate = (client, mqttUsername, mqttPassword, callback): void => {\n const receivedUsername = mqttUsername ?? \"\";\n\n const receivedPassword = mqttPassword?.toString() ?? \"\";\n\n const valid = receivedUsername === username && receivedPassword === password;\n\n if (valid) {\n this.log.info(`MQTT client authenticated: ${client?.id ?? \"unknown\"}`);\n } else {\n this.log.warn(`MQTT authentication rejected for client: ${client?.id ?? \"unknown\"}`);\n }\n\n callback(null, valid);\n };\n\n this.mqttBroker.on(\"client\", client => {\n this.log.info(`MQTT client connected: ${client.id}`);\n });\n\n this.mqttBroker.on(\"clientDisconnect\", client => {\n this.log.info(`MQTT client disconnected: ${client.id}`);\n });\n\n this.mqttBroker.on(\"publish\", (packet, client) => {\n if (!client) {\n return;\n }\n\n if (!packet.topic.startsWith(\"nuki/\")) {\n return;\n }\n\n const value = packet.payload.toString();\n\n this.log.info(`Nuki MQTT: ${packet.topic} = ${value}`);\n\n void this.handleNukiMessage(packet.topic, value);\n });\n\n this.mqttServer = createServer(this.mqttBroker.handle);\n\n await new Promise<void>((resolve, reject) => {\n if (!this.mqttServer) {\n reject(new Error(\"MQTT server was not created\"));\n\n return;\n }\n\n const onError = (error: Error): void => {\n reject(error);\n };\n\n const onListening = (): void => {\n this.mqttServer?.off(\"error\", onError);\n resolve();\n };\n\n this.mqttServer.once(\"error\", onError);\n\n this.mqttServer.once(\"listening\", onListening);\n\n this.mqttServer.listen(port, \"0.0.0.0\");\n });\n\n this.log.info(`Integrated MQTT broker listening on port ${port}`);\n }\n\n private async restoreRetainedNukiStates(): Promise<void> {\n if (!this.mqttPersistence) {\n return;\n }\n\n this.log.info(\"Restoring retained Nuki MQTT states...\");\n\n let restored = 0;\n\n const stream = this.mqttPersistence.createRetainedStreamCombi([\"nuki/#\"]);\n\n for await (const packet of stream) {\n if (!packet || typeof packet.topic !== \"string\") {\n continue;\n }\n\n if (!packet.topic.startsWith(\"nuki/\")) {\n continue;\n }\n\n const payload = Buffer.isBuffer(packet.payload) ? packet.payload.toString() : String(packet.payload ?? \"\");\n\n await this.handleNukiMessage(packet.topic, payload);\n\n restored++;\n }\n\n this.log.info(`Restored ${restored} retained Nuki MQTT state(s)`);\n }\n\n private async handleNukiMessage(topic: string, payload: string): Promise<void> {\n try {\n const parts = topic.split(\"/\");\n\n if (parts.length !== 3 || parts[0] !== \"nuki\") {\n return;\n }\n\n const deviceId = parts[1];\n\n const property = parts[2];\n\n if (!deviceId || !property) {\n return;\n }\n\n if (this.sanitizeId(deviceId) !== deviceId) {\n this.log.debug(\"Ignoring MQTT message with invalid device ID\");\n return;\n }\n\n await this.ensureDevice(deviceId);\n\n switch (property) {\n case \"name\":\n await this.setStringState(`${deviceId}.device.name`, \"Name\", payload);\n break;\n\n case \"firmware\":\n await this.setStringState(`${deviceId}.device.firmware`, \"Firmware\", payload);\n break;\n\n case \"deviceType\":\n await this.setMappedNumberState(\n `${deviceId}.device.deviceType`,\n \"Device type\",\n payload,\n DEVICE_TYPES,\n );\n break;\n\n case \"mode\":\n await this.setMappedNumberState(`${deviceId}.device.mode`, \"Mode\", payload, MODES);\n break;\n\n case \"connected\":\n case \"serverConnected\":\n await this.setBooleanState(`${deviceId}.device.online`, \"Online\", payload, \"indicator.connected\");\n break;\n\n case \"timestamp\":\n await this.setStringState(`${deviceId}.status.timestamp`, \"Timestamp\", payload, \"date\");\n break;\n\n case \"batteryChargeState\":\n await this.setNumberState(`${deviceId}.battery.percent`, \"Battery\", payload, \"%\", \"value.battery\");\n break;\n\n case \"batteryCharging\":\n await this.setBooleanState(`${deviceId}.battery.charging`, \"Battery charging\", payload);\n\n await this.updateDeviceIcon(deviceId);\n break;\n\n case \"batteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.critical`,\n \"Battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"doorsensorBatteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.doorSensorCritical`,\n \"Door sensor battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"keypadBatteryCritical\":\n await this.setBooleanState(\n `${deviceId}.battery.keypadCritical`,\n \"Keypad battery critical\",\n payload,\n \"indicator.maintenance\",\n );\n break;\n\n case \"state\":\n await this.handleLockState(deviceId, payload);\n break;\n\n case \"doorsensorState\":\n await this.handleDoorState(deviceId, payload);\n break;\n\n case \"lockActionEvent\":\n await this.handleLockActionEvent(deviceId, payload);\n break;\n\n case \"commandResponse\":\n await this.handleCommandResponse(deviceId, payload);\n break;\n\n case \"lock\":\n case \"unlock\":\n case \"lockAction\":\n break;\n\n default:\n await this.setStringState(`${deviceId}.raw.${this.sanitizeId(property)}`, property, payload);\n break;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not process Nuki MQTT message ${topic}: ${message}`);\n }\n }\n\n /*\n * ============================================================\n * DEVICE INITIALIZATION\n * ============================================================\n */\n\n private async ensureDevice(deviceId: string): Promise<void> {\n const normalized = deviceId.toUpperCase();\n\n if (this.initializedDevices.has(normalized)) {\n return;\n }\n\n const existingPromise = this.initializingDevices.get(normalized);\n\n if (existingPromise) {\n await existingPromise;\n return;\n }\n\n const initialization = this.initializeDevice(deviceId, normalized);\n\n this.initializingDevices.set(normalized, initialization);\n\n try {\n await initialization;\n } finally {\n this.initializingDevices.delete(normalized);\n }\n }\n\n private async initializeDevice(deviceId: string, normalized: string): Promise<void> {\n await this.setObjectNotExistsAsync(deviceId, {\n type: \"device\",\n common: {\n name: `Nuki ${deviceId}`,\n },\n native: {},\n });\n\n const channels = [\n [\"status\", \"Status\"],\n [\"battery\", \"Battery\"],\n [\"keypad\", \"Keypad\"],\n [\"activity\", \"Activity\"],\n [\"device\", \"Device\"],\n [\"commands\", \"Commands\"],\n [\"advanced\", \"Advanced\"],\n [\"raw\", \"Raw MQTT\"],\n ] as const;\n\n for (const [id, name] of channels) {\n await this.setObjectNotExistsAsync(`${deviceId}.${id}`, {\n type: \"channel\",\n common: {\n name,\n },\n native: {},\n });\n }\n\n await this.ensureKnownDeviceObjects(deviceId);\n\n await this.cleanupLegacyObjects(deviceId);\n\n this.initializedDevices.add(normalized);\n\n await this.updateDeviceIcon(deviceId);\n\n this.log.info(`Nuki device initialized: ${deviceId}`);\n }\n\n private async ensureKnownDeviceObjects(deviceId: string): Promise<void> {\n await this.ensureMappedStateObject(`${deviceId}.status.lockState`, \"Lock state\", LOCK_STATES);\n\n await this.ensureStateObject(`${deviceId}.status.lockStateText`, \"Lock state text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.locked`, \"Locked\", \"boolean\", \"indicator\");\n\n await this.ensureMappedStateObject(`${deviceId}.status.doorState`, \"Door state\", DOOR_STATES);\n\n await this.ensureStateObject(`${deviceId}.status.doorStateText`, \"Door state text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.doorOpen`, \"Door open\", \"boolean\", \"sensor.door\");\n\n await this.ensureStateObject(`${deviceId}.status.timestamp`, \"Timestamp\", \"string\", \"date\");\n\n await this.ensureStateObject(`${deviceId}.status.iconState`, \"Icon state\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.status.icon`, \"Icon\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.battery.percent`, \"Battery\", \"number\", \"value.battery\", false, \"%\");\n\n await this.ensureStateObject(\n `${deviceId}.battery.critical`,\n \"Battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(`${deviceId}.battery.charging`, \"Battery charging\", \"boolean\", \"indicator\");\n\n await this.ensureStateObject(\n `${deviceId}.battery.keypadCritical`,\n \"Keypad battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(\n `${deviceId}.battery.doorSensorCritical`,\n \"Door sensor battery critical\",\n \"boolean\",\n \"indicator.maintenance\",\n );\n\n await this.ensureStateObject(`${deviceId}.keypad.lastType`, \"Last access type\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.keypad.lastUser`, \"Last user\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.keypad.lastTimestamp`, \"Last access timestamp\", \"string\", \"date\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastAction`, \"Last action\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastActionText`, \"Last action text\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastUser`, \"Last user\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.activity.lastDate`, \"Last date\", \"string\", \"date\");\n\n await this.ensureStateObject(`${deviceId}.device.name`, \"Name\", \"string\", \"text\");\n\n await this.ensureStateObject(`${deviceId}.device.firmware`, \"Firmware\", \"string\", \"text\");\n\n await this.ensureMappedStateObject(`${deviceId}.device.deviceType`, \"Device type\", DEVICE_TYPES);\n\n await this.ensureMappedStateObject(`${deviceId}.device.mode`, \"Mode\", MODES);\n\n await this.ensureStateObject(`${deviceId}.device.online`, \"Online\", \"boolean\", \"indicator.connected\");\n\n await this.ensureStateObject(`${deviceId}.advanced.authId`, \"Authorization ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.codeId`, \"Code ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.source`, \"Source\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.trigger`, \"Trigger\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.smartlockId`, \"Web API Smartlock ID\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.serverState`, \"Nuki server state\", \"number\", \"value\");\n\n await this.ensureStateObject(`${deviceId}.advanced.authorizations`, \"Authorizations\", \"string\", \"json\");\n\n await this.createCommandState(`${deviceId}.commands.lock`, \"Lock\");\n\n await this.createCommandState(`${deviceId}.commands.unlock`, \"Unlock\");\n\n await this.createCommandState(`${deviceId}.commands.unlatch`, \"Unlatch\");\n\n await this.createCommandState(`${deviceId}.commands.lockNgo`, \"Lock'n'Go\");\n\n await this.createCommandState(`${deviceId}.commands.lockNgoUnlatch`, \"Lock'n'Go with unlatch\");\n\n await this.createCommandState(`${deviceId}.commands.fullLock`, \"Full lock\");\n\n await this.ensureStateObject(`${deviceId}.commands.lastResult`, \"Last command result\", \"number\", \"value\");\n\n await this.ensureStateObject(\n `${deviceId}.commands.lastResultText`,\n \"Last command result text\",\n \"string\",\n \"text\",\n );\n\n await this.initializeDefaultValues(deviceId);\n }\n\n private async initializeDefaultValues(deviceId: string): Promise<void> {\n const defaults: Array<{\n id: string;\n value: string | boolean;\n }> = [\n {\n id: `${deviceId}.device.online`,\n value: false,\n },\n {\n id: `${deviceId}.battery.charging`,\n value: false,\n },\n {\n id: `${deviceId}.battery.critical`,\n value: false,\n },\n {\n id: `${deviceId}.battery.keypadCritical`,\n value: false,\n },\n {\n id: `${deviceId}.battery.doorSensorCritical`,\n value: false,\n },\n {\n id: `${deviceId}.device.firmware`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastUser`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastType`,\n value: \"\",\n },\n {\n id: `${deviceId}.keypad.lastTimestamp`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastUser`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastActionText`,\n value: \"\",\n },\n {\n id: `${deviceId}.activity.lastDate`,\n value: \"\",\n },\n {\n id: `${deviceId}.commands.lastResultText`,\n value: \"\",\n },\n {\n id: `${deviceId}.status.iconState`,\n value: \"unknown\",\n },\n {\n id: `${deviceId}.status.icon`,\n value: NUKI_ICONS.unknown,\n },\n ];\n\n for (const item of defaults) {\n const state = await this.getStateAsync(item.id);\n\n if (!state) {\n await this.setStateAsync(item.id, item.value, true);\n }\n }\n }\n\n private async cleanupLegacyObjects(deviceId: string): Promise<void> {\n const legacy = [\n `${deviceId}.info`,\n `${deviceId}.lock`,\n `${deviceId}.door`,\n `${deviceId}.events`,\n `${deviceId}.web`,\n ];\n\n for (const id of legacy) {\n const object = await this.getObjectAsync(id);\n\n if (!object) {\n continue;\n }\n\n try {\n await this.delObjectAsync(id, {\n recursive: true,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not remove old object ${id}: ${message}`);\n }\n }\n }\n\n private async cleanupOldWebDeviceId(oldDeviceId: string, realDeviceId: string): Promise<void> {\n if (oldDeviceId === realDeviceId) {\n return;\n }\n\n const object = await this.getObjectAsync(oldDeviceId);\n\n if (!object) {\n return;\n }\n\n try {\n await this.delObjectAsync(oldDeviceId, {\n recursive: true,\n });\n\n this.initializedDevices.delete(oldDeviceId.toUpperCase());\n\n this.log.info(`Removed old Web API device ID ${oldDeviceId}; using MQTT ID ${realDeviceId}`);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not remove old Web API device ${oldDeviceId}: ${message}`);\n }\n }\n\n /*\n * ============================================================\n * ICON\n * ============================================================\n */\n\n private async updateDeviceIcon(deviceId: string): Promise<void> {\n const chargingState = await this.getStateAsync(`${deviceId}.battery.charging`);\n\n const doorOpenState = await this.getStateAsync(`${deviceId}.status.doorOpen`);\n\n const lockState = await this.getStateAsync(`${deviceId}.status.lockState`);\n\n const charging = chargingState?.val === true;\n\n const hasDoorState =\n doorOpenState !== null &&\n doorOpenState !== undefined &&\n doorOpenState.val !== null &&\n doorOpenState.val !== undefined;\n\n const doorOpen = doorOpenState?.val === true;\n\n const lockStateValue = typeof lockState?.val === \"number\" ? lockState.val : Number(lockState?.val);\n\n let iconState: NukiIconState = \"unknown\";\n\n let icon: string = NUKI_ICONS.unknown;\n\n /*\n * Priorit\u00E4t:\n *\n * 1. Akku l\u00E4dt\n * 2. T\u00FCr offen\n * 3. Schloss zugesperrt\n * 4. Schloss aufgesperrt\n * 5. T\u00FCr geschlossen\n * 6. unbekannt\n */\n\n if (charging) {\n iconState = \"charging\";\n\n icon = NUKI_ICONS.charging;\n } else if (doorOpen) {\n iconState = \"door_open\";\n\n icon = NUKI_ICONS.doorOpen;\n } else if (lockStateValue === 1) {\n iconState = \"locked\";\n\n icon = NUKI_ICONS.locked;\n } else if (lockStateValue === 3 || lockStateValue === 5 || lockStateValue === 6) {\n iconState = \"unlocked\";\n\n icon = NUKI_ICONS.unlocked;\n } else if (hasDoorState && !doorOpen) {\n iconState = \"door_closed\";\n\n icon = NUKI_ICONS.doorClosed;\n }\n\n await this.setStateAsync(`${deviceId}.status.iconState`, iconState, true);\n\n await this.setStateAsync(`${deviceId}.status.icon`, icon, true);\n }\n\n /*\n * ============================================================\n * STATUS / EVENTS\n * ============================================================\n */\n\n private async handleLockState(deviceId: string, payload: string): Promise<void> {\n const state = Number(payload);\n\n if (!Number.isFinite(state)) {\n return;\n }\n\n await this.setMappedNumberState(`${deviceId}.status.lockState`, \"Lock state\", payload, LOCK_STATES);\n\n await this.setStringState(\n `${deviceId}.status.lockStateText`,\n \"Lock state text\",\n this.getMappedText(LOCK_STATES, state),\n );\n\n await this.setStateAsync(`${deviceId}.status.locked`, state === 1, true);\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async handleDoorState(deviceId: string, payload: string): Promise<void> {\n const state = Number(payload);\n\n if (!Number.isFinite(state)) {\n return;\n }\n\n await this.setMappedNumberState(`${deviceId}.status.doorState`, \"Door state\", payload, DOOR_STATES);\n\n await this.setStringState(\n `${deviceId}.status.doorStateText`,\n \"Door state text\",\n this.getMappedText(DOOR_STATES, state),\n );\n\n await this.setStateAsync(`${deviceId}.status.doorOpen`, state === 3, true);\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async handleLockActionEvent(deviceId: string, payload: string): Promise<void> {\n const parts = payload.split(\",\");\n\n if (parts.length < 5) {\n this.log.warn(`Invalid lockActionEvent: ${payload}`);\n\n return;\n }\n\n const action = Number(parts[0]);\n\n const trigger = Number(parts[1]);\n\n const authId = Number(parts[2]);\n\n const codeId = Number(parts[3]);\n\n const source = Number(parts[4]);\n\n if (\n !Number.isFinite(action) ||\n !Number.isFinite(trigger) ||\n !Number.isFinite(authId) ||\n !Number.isFinite(codeId) ||\n !Number.isFinite(source)\n ) {\n this.log.warn(`Invalid numeric values in lockActionEvent: ${payload}`);\n\n return;\n }\n\n const now = new Date().toISOString();\n\n await this.setStateAsync(`${deviceId}.activity.lastAction`, action, true);\n\n await this.setStringState(\n `${deviceId}.activity.lastActionText`,\n \"Last action text\",\n this.getMappedText(LOCK_ACTIONS, action),\n );\n\n await this.setStringState(`${deviceId}.activity.lastDate`, \"Last date\", now, \"date\");\n\n await this.setStateAsync(`${deviceId}.advanced.authId`, authId, true);\n\n await this.setStateAsync(`${deviceId}.advanced.codeId`, codeId, true);\n\n await this.setStateAsync(`${deviceId}.advanced.source`, source, true);\n\n await this.setStateAsync(`${deviceId}.advanced.trigger`, trigger, true);\n\n const configuredKeypadUser = codeId > 0 ? this.findConfiguredKeypadUser(codeId) : undefined;\n\n const resolvedUserName = this.findAuthorizationName(deviceId, authId);\n\n const displayUser =\n configuredKeypadUser ??\n resolvedUserName ??\n (codeId > 0 ? `Unknown (Code-ID ${codeId})` : `Unknown (Auth-ID ${authId})`);\n\n await this.setStringState(`${deviceId}.activity.lastUser`, \"Last user\", displayUser);\n\n if (codeId > 0) {\n let type = `Source ${source}`;\n\n switch (source) {\n case 0:\n type = \"Back button\";\n break;\n\n case 1:\n type = \"Code\";\n break;\n\n case 2:\n type = \"Fingerprint\";\n break;\n }\n\n await this.setStringState(`${deviceId}.keypad.lastType`, \"Last access type\", type);\n\n await this.setStringState(`${deviceId}.keypad.lastUser`, \"Last user\", displayUser);\n\n await this.setStringState(`${deviceId}.keypad.lastTimestamp`, \"Last access timestamp\", now, \"date\");\n\n if (source === 2) {\n this.log.info(`Fingerprint detected: authId=${authId}, codeId=${codeId}, user=${displayUser}`);\n } else if (source === 1) {\n this.log.info(`Keypad code detected: authId=${authId}, codeId=${codeId}, user=${displayUser}`);\n }\n }\n\n this.log.debug(\n `Lock action processed: action=${action}, trigger=${trigger}, authId=${authId}, codeId=${codeId}, source=${source}`,\n );\n }\n\n private findConfiguredKeypadUser(codeId: number): string | undefined {\n const users = Array.isArray(this.config.keypadUsers) ? this.config.keypadUsers : [];\n\n const match = users.find(user => Number(user.codeId) === codeId);\n\n if (!match || typeof match.name !== \"string\") {\n return undefined;\n }\n\n const name = match.name.trim();\n\n return name || undefined;\n }\n\n private findAuthorizationName(deviceId: string, authId: number): string | undefined {\n const smartlockId = this.webSmartlockIds.get(this.normalizeDeviceId(deviceId));\n\n if (smartlockId === undefined) {\n return undefined;\n }\n\n return this.authorizationNames.get(smartlockId)?.get(authId);\n }\n\n /*\n * ============================================================\n * COMMANDS\n * ============================================================\n */\n\n private async createCommandState(id: string, name: string): Promise<void> {\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type: \"boolean\",\n role: \"button\",\n read: false,\n write: true,\n def: false,\n },\n native: {},\n });\n\n const state = await this.getStateAsync(id);\n\n if (!state) {\n await this.setStateAsync(id, false, true);\n }\n }\n\n private async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n if (!state || state.ack || state.val !== true) {\n return;\n }\n\n const prefix = `${this.namespace}.`;\n\n if (!id.startsWith(prefix)) {\n return;\n }\n\n const relativeId = id.substring(prefix.length);\n\n const parts = relativeId.split(\".\");\n\n if (parts.length !== 3 || parts[1] !== \"commands\") {\n return;\n }\n\n const deviceId = parts[0];\n\n const command = parts[2];\n\n try {\n switch (command) {\n case \"unlock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"1\");\n break;\n\n case \"lock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"2\");\n break;\n\n case \"unlatch\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"3\");\n break;\n\n case \"lockNgo\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"4\");\n break;\n\n case \"lockNgoUnlatch\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"5\");\n break;\n\n case \"fullLock\":\n await this.publishNukiCommand(`nuki/${deviceId}/lockAction`, \"6\");\n break;\n\n default:\n return;\n }\n\n await this.setStateAsync(relativeId, false, true);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.error(`Could not execute command ${command}: ${message}`);\n\n await this.setStateAsync(relativeId, false, true);\n }\n }\n\n private async publishNukiCommand(topic: string, payload: string): Promise<void> {\n if (!this.mqttBroker) {\n throw new Error(\"MQTT broker is not running\");\n }\n\n await new Promise<void>((resolve, reject) => {\n this.mqttBroker?.publish(\n {\n cmd: \"publish\",\n topic,\n payload: Buffer.from(payload),\n qos: 2,\n dup: false,\n retain: false,\n },\n error => {\n if (error) {\n reject(error);\n\n return;\n }\n\n resolve();\n },\n );\n });\n\n this.log.info(`Nuki command published: ${topic} = ${payload}`);\n }\n\n private async handleCommandResponse(deviceId: string, payload: string): Promise<void> {\n const result = Number(payload);\n\n if (!Number.isFinite(result)) {\n return;\n }\n\n await this.setNumberState(`${deviceId}.commands.lastResult`, \"Last command result\", payload);\n\n await this.setStringState(\n `${deviceId}.commands.lastResultText`,\n \"Last command result text\",\n result === 0 ? \"Successful\" : `Error ${result}`,\n );\n }\n\n /*\n * ============================================================\n * WEB API\n * ============================================================\n */\n\n private async startWebApi(): Promise<void> {\n const token = String(this.config.webApiToken ?? \"\").trim();\n\n if (!token) {\n this.log.warn(\"Nuki Web API is enabled but no API token is configured\");\n\n return;\n }\n\n const intervalSeconds = Math.min(86400, Math.max(60, Number(this.config.webApiInterval) || 300));\n\n this.log.info(`Nuki Web API enabled, update interval: ${intervalSeconds} seconds`);\n\n try {\n await this.updateNukiWebData();\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Initial Nuki Web API update failed: ${message}`);\n }\n\n this.scheduleWebApiUpdate(intervalSeconds);\n }\n\n private scheduleWebApiUpdate(intervalSeconds: number): void {\n if (this.webApiStopping) {\n return;\n }\n\n this.webApiTimer = this.setTimeout(() => {\n this.webApiTimer = null;\n\n if (this.webApiStopping) {\n return;\n }\n\n void this.updateNukiWebData()\n .catch(error => {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Nuki Web API update failed: ${message}`);\n })\n .finally(() => {\n this.scheduleWebApiUpdate(intervalSeconds);\n });\n }, intervalSeconds * 1000);\n }\n\n private async updateNukiWebData(): Promise<void> {\n const devices = await this.nukiWebRequest<NukiWebDevice[]>(\"/smartlock\");\n\n if (!Array.isArray(devices)) {\n throw new Error(\"Invalid response from /smartlock\");\n }\n\n let authorizations: NukiWebAuthorization[] = [];\n\n try {\n const result = await this.nukiWebRequest<NukiWebAuthorization[]>(\"/smartlock/auth\");\n\n if (Array.isArray(result)) {\n authorizations = result;\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not load Nuki authorizations: ${message}`);\n }\n\n for (const device of devices) {\n const smartlockId = Number(device.smartlockId);\n\n if (!Number.isFinite(smartlockId)) {\n continue;\n }\n\n const conversion = this.getMqttDeviceIdFromWebDevice(device);\n\n const deviceId = conversion.deviceId;\n\n if (conversion.originalHex !== conversion.deviceId) {\n await this.cleanupOldWebDeviceId(conversion.originalHex, conversion.deviceId);\n }\n\n this.webSmartlockIds.set(this.normalizeDeviceId(deviceId), smartlockId);\n\n await this.ensureDevice(deviceId);\n\n await this.storeWebDeviceData(deviceId, device);\n\n const deviceAuths = authorizations.filter(auth => Number(auth.smartlockId) === smartlockId);\n\n await this.storeWebAuthorizations(deviceId, smartlockId, deviceAuths);\n\n try {\n const logs = await this.nukiWebRequest<NukiWebLog[]>(`/smartlock/${smartlockId}/log?limit=20`);\n\n if (Array.isArray(logs)) {\n await this.storeWebLogs(deviceId, logs);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Could not load activity logs for ${deviceId}: ${message}`);\n }\n }\n }\n\n private async nukiWebRequest<T>(path: string): Promise<T> {\n const token = String(this.config.webApiToken ?? \"\").trim();\n\n if (!token) {\n throw new Error(\"Nuki Web API token missing\");\n }\n\n const response = await fetch(`https://api.nuki.io${path}`, {\n signal: AbortSignal.timeout(15_000),\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n\n throw new Error(\n `HTTP ${response.status} ${response.statusText}${body ? `: ${body.substring(0, 300)}` : \"\"}`,\n );\n }\n\n return (await response.json()) as T;\n }\n\n private getMqttDeviceIdFromWebDevice(device: NukiWebDevice): {\n deviceId: string;\n originalHex: string;\n } {\n try {\n const originalHex = BigInt(device.smartlockId).toString(16).toUpperCase();\n\n const deviceId = originalHex.slice(-8).padStart(8, \"0\");\n\n return {\n deviceId,\n originalHex,\n };\n } catch {\n const value = String(device.smartlockId);\n\n return {\n deviceId: value,\n originalHex: value,\n };\n }\n }\n\n private normalizeDeviceId(deviceId: string): string {\n const normalized = deviceId.trim().toUpperCase().replace(/^0X/, \"\");\n\n return normalized.slice(-8).padStart(8, \"0\");\n }\n\n private async storeWebDeviceData(deviceId: string, device: NukiWebDevice): Promise<void> {\n await this.setStateAsync(`${deviceId}.advanced.smartlockId`, Number(device.smartlockId), true);\n\n if (typeof device.name === \"string\") {\n await this.setStringState(`${deviceId}.device.name`, \"Name\", device.name);\n }\n\n if (typeof device.type === \"number\") {\n await this.setMappedNumberState(\n `${deviceId}.device.deviceType`,\n \"Device type\",\n String(device.type),\n DEVICE_TYPES,\n );\n }\n\n if (typeof device.serverState === \"number\") {\n await this.setStateAsync(`${deviceId}.advanced.serverState`, device.serverState, true);\n\n await this.setStateAsync(`${deviceId}.device.online`, device.serverState === 0, true);\n }\n\n const state = device.state;\n\n if (!state) {\n await this.updateDeviceIcon(deviceId);\n\n return;\n }\n\n if (typeof state.batteryCharge === \"number\") {\n await this.setStateAsync(`${deviceId}.battery.percent`, state.batteryCharge, true);\n }\n\n if (typeof state.batteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.critical`, state.batteryCritical, true);\n }\n\n if (typeof state.batteryCharging === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.charging`, state.batteryCharging, true);\n }\n\n if (typeof state.keypadBatteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.keypadCritical`, state.keypadBatteryCritical, true);\n }\n\n if (typeof state.doorsensorBatteryCritical === \"boolean\") {\n await this.setStateAsync(`${deviceId}.battery.doorSensorCritical`, state.doorsensorBatteryCritical, true);\n }\n\n if (typeof state.state === \"number\") {\n await this.setStateAsync(`${deviceId}.status.lockState`, state.state, true);\n\n await this.setStringState(\n `${deviceId}.status.lockStateText`,\n \"Lock state text\",\n this.getMappedText(LOCK_STATES, state.state),\n );\n\n await this.setStateAsync(`${deviceId}.status.locked`, state.state === 1, true);\n }\n\n if (typeof state.doorState === \"number\") {\n await this.setStateAsync(`${deviceId}.status.doorState`, state.doorState, true);\n\n await this.setStringState(\n `${deviceId}.status.doorStateText`,\n \"Door state text\",\n this.getMappedText(DOOR_STATES, state.doorState),\n );\n\n await this.setStateAsync(`${deviceId}.status.doorOpen`, state.doorState === 3, true);\n }\n\n await this.updateDeviceIcon(deviceId);\n }\n\n private async storeWebAuthorizations(\n deviceId: string,\n smartlockId: number,\n authorizations: NukiWebAuthorization[],\n ): Promise<void> {\n const authNames = new Map<number, string>();\n\n for (const auth of authorizations) {\n if (typeof auth.authId === \"number\" && typeof auth.name === \"string\") {\n authNames.set(auth.authId, auth.name);\n }\n }\n\n this.authorizationNames.set(smartlockId, authNames);\n\n const safe = authorizations.map(auth => ({\n id: auth.id,\n smartlockId: auth.smartlockId,\n authId: auth.authId,\n type: auth.type,\n name: auth.name,\n enabled: auth.enabled,\n remoteAllowed: auth.remoteAllowed,\n }));\n\n await this.setStringState(\n `${deviceId}.advanced.authorizations`,\n \"Authorizations\",\n JSON.stringify(safe),\n \"json\",\n );\n }\n\n private async storeWebLogs(deviceId: string, logs: NukiWebLog[]): Promise<void> {\n if (logs.length === 0) {\n return;\n }\n\n const sorted = [...logs].sort((a, b) => {\n const bTime = Date.parse(b.date ?? \"\");\n\n const aTime = Date.parse(a.date ?? \"\");\n\n return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);\n });\n\n const last = sorted[0];\n\n if (!last) {\n return;\n }\n\n if (typeof last.action === \"number\") {\n await this.setStateAsync(`${deviceId}.activity.lastAction`, last.action, true);\n\n await this.setStringState(\n `${deviceId}.activity.lastActionText`,\n \"Last action text\",\n this.getMappedText(LOCK_ACTIONS, last.action),\n );\n }\n\n if (typeof last.name === \"string\") {\n await this.setStringState(`${deviceId}.activity.lastUser`, \"Last user\", last.name);\n }\n\n if (typeof last.date === \"string\") {\n await this.setStringState(`${deviceId}.activity.lastDate`, \"Last date\", last.date, \"date\");\n }\n\n await this.setStateAsync(`${deviceId}.advanced.codeId`, 0, true);\n\n if (typeof last.authId === \"number\") {\n await this.setStateAsync(`${deviceId}.advanced.authId`, last.authId, true);\n }\n }\n\n /*\n * ============================================================\n * HELPERS\n * ============================================================\n */\n\n private getMappedText(map: Record<number, string>, value: number): string {\n return map[value] ?? `Unknown (${value})`;\n }\n\n private async ensureStateObject(\n id: string,\n name: string,\n type: \"string\" | \"number\" | \"boolean\",\n role: string,\n write = false,\n unit?: string,\n ): Promise<void> {\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type,\n role,\n read: true,\n write,\n ...(unit\n ? {\n unit,\n }\n : {}),\n },\n native: {},\n });\n }\n\n private async ensureMappedStateObject(id: string, name: string, states: Record<number, string>): Promise<void> {\n const ioBrokerStates: Record<string, string> = {};\n\n for (const [key, text] of Object.entries(states)) {\n ioBrokerStates[key] = text;\n }\n\n await this.extendObjectAsync(id, {\n type: \"state\",\n common: {\n name,\n type: \"number\",\n role: \"value\",\n read: true,\n write: false,\n states: ioBrokerStates,\n },\n native: {},\n });\n }\n\n private async setStringState(id: string, name: string, value: string, role = \"text\"): Promise<void> {\n await this.ensureStateObject(id, name, \"string\", role);\n\n await this.setStateAsync(id, value, true);\n }\n\n private async setNumberState(\n id: string,\n name: string,\n value: string,\n unit?: string,\n role = \"value\",\n ): Promise<void> {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n return;\n }\n\n await this.ensureStateObject(id, name, \"number\", role, false, unit);\n\n await this.setStateAsync(id, numberValue, true);\n }\n\n private async setMappedNumberState(\n id: string,\n name: string,\n value: string,\n states: Record<number, string>,\n ): Promise<void> {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n return;\n }\n\n await this.ensureMappedStateObject(id, name, states);\n\n await this.setStateAsync(id, numberValue, true);\n }\n\n private async setBooleanState(id: string, name: string, value: string, role = \"indicator\"): Promise<void> {\n await this.ensureStateObject(id, name, \"boolean\", role);\n\n const boolValue = value.toLowerCase() === \"true\" || value === \"1\";\n\n await this.setStateAsync(id, boolValue, true);\n }\n\n private sanitizeId(value: string): string {\n return value.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n }\n\n /*\n * ============================================================\n * UNLOAD\n * ============================================================\n */\n\n private onUnload(callback: () => void): void {\n this.webApiStopping = true;\n\n void (async () => {\n try {\n if (this.webApiTimer) {\n this.clearTimeout(this.webApiTimer);\n\n this.webApiTimer = null;\n }\n\n await this.setStateAsync(\"info.connection\", false, true);\n\n if (this.mqttServer) {\n await new Promise<void>(resolve => {\n this.mqttServer?.close(() => resolve());\n });\n\n this.mqttServer = null;\n }\n\n if (this.mqttBroker) {\n await new Promise<void>(resolve => {\n this.mqttBroker?.close(() => resolve());\n });\n\n this.mqttBroker = null;\n }\n\n this.mqttPersistence = null;\n\n this.mqttDatabase = null;\n\n this.log.info(\"Nuki Local adapter stopped\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.log.warn(`Error while stopping adapter: ${message}`);\n } finally {\n callback();\n }\n })();\n }\n}\n\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new NukiLocal(options);\n} else {\n (() => new NukiLocal())();\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
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,21 @@
|
|
|
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
|
+
},
|
|
6
19
|
"0.1.3": {
|
|
7
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.",
|
|
8
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.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@iobroker/testing": "^6.3.0",
|
|
43
43
|
"@tsconfig/node22": "^22.0.6",
|
|
44
44
|
"@types/iobroker": "npm:@iobroker/types@^7.2.2",
|
|
45
|
-
"@types/node": "^22.20.
|
|
45
|
+
"@types/node": "^22.20.5",
|
|
46
46
|
"nyc": "^18.0.0",
|
|
47
47
|
"rimraf": "^6.1.3",
|
|
48
48
|
"source-map-support": "^0.5.21",
|