homebridge-nb 1.4.4 → 1.4.5

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.
@@ -17,16 +17,15 @@ class NbAccessory extends homebridgeLib.AccessoryDelegate {
17
17
  category: params.category,
18
18
  manufacturer: 'Nuki Home Solutions GmbH',
19
19
  model: params.model,
20
- firmware: params.firmware
20
+ firmware: params.device.firmwareVersion
21
21
  })
22
22
  this.inheritLogLevel(bridge)
23
23
  this.id = params.id
24
24
  this.bridge = bridge
25
25
  this.client = this.bridge.client
26
26
  this.context.bridgeId = this.bridge.id
27
- this.context.firmware = params.firmware
28
- this.context.deviceType = params.deviceType
29
- this.log('Nuki %s v%s %s', params.model, params.firmware, params.id)
27
+ this.deviceType = params.device
28
+ this.log('Nuki %s v%s %s', params.model, params.device.firmwareVersion, params.id)
30
29
  this.on('identify', this.identify)
31
30
  setImmediate(() => { this.emit('initialised') })
32
31
  }
@@ -39,15 +38,13 @@ class NbAccessory extends homebridgeLib.AccessoryDelegate {
39
38
 
40
39
  class Bridge extends homebridgeLib.AccessoryDelegate {
41
40
  constructor (platform, context) {
42
- const params = {
41
+ super(platform, {
43
42
  id: context.id,
44
43
  name: context.name,
45
44
  category: platform.Accessory.Categories.RANGE_EXTENDER,
46
- manufacturer: 'Nuki Home Solutions GmbH',
47
45
  model: 'Bridge',
48
46
  firmware: context.firmware
49
- }
50
- super(platform, params)
47
+ })
51
48
  this.id = context.id
52
49
  this.context.host = context.host
53
50
  this.context.token = context.token
@@ -245,72 +242,58 @@ class Bridge extends homebridgeLib.AccessoryDelegate {
245
242
  this.service.update(response.body)
246
243
  response = await this.client.list()
247
244
  for (const device of response.body) {
248
- this.debug('device: %j', device)
249
- if (device.firmwareVersion == null) { // Issue 93.
250
- continue
251
- }
252
- const id = device.nukiId.toString(16).toUpperCase()
253
- if (!this.platform.isWhitelisted(id)) {
254
- continue
255
- }
256
- switch (device.deviceType) {
257
- case NbClient.DeviceTypes.SMARTLOCK:
258
- case NbClient.DeviceTypes.SMARTDOOR:
259
- case NbClient.DeviceTypes.SMARTLOCK3:
260
- if (this.smartLocks[id] == null) {
261
- this.addSmartLock(id, {
262
- id,
263
- name: device.name,
264
- firmware: device.firmwareVersion,
265
- deviceType: device.deviceType
266
- })
267
- }
268
- this.smartLocks[id].values.firmware = device.firmwareVersion
269
- this.smartLocks[id].context.deviceType = device.deviceType
270
- if (device.lastKnownState == null) {
271
- this.smartLocks[id].warn('no last known state')
272
- continue
273
- }
274
- this.smartLocks[id].update(device.lastKnownState)
275
- if (
276
- device.lastKnownState.doorsensorState != null &&
277
- device.lastKnownState.doorsensorState !== NbClient.DoorSensorStates.DEACTIVATED
278
- ) {
279
- if (this.doorSensors[id + '-S'] == null) {
280
- this.addDoorSensor(id + '-S', {
281
- id: id + '-S',
282
- name: device.name + ' Sensor',
283
- firmware: device.firmwareVersion,
284
- deviceType: device.deviceType
285
- })
245
+ try {
246
+ this.debug('device: %j', device)
247
+ if (device.firmwareVersion == null) { // Issue 93.
248
+ continue
249
+ }
250
+ const id = device.nukiId.toString(16).toUpperCase()
251
+ if (!this.platform.isWhitelisted(id)) {
252
+ continue
253
+ }
254
+ switch (device.deviceType) {
255
+ case NbClient.DeviceTypes.SMARTLOCK:
256
+ case NbClient.DeviceTypes.SMARTDOOR:
257
+ case NbClient.DeviceTypes.SMARTLOCK3:
258
+ if (device.lastKnownState == null) {
259
+ this.warn('%s: no last known state', id)
260
+ continue
261
+ }
262
+ if (this.smartLocks[id] == null) {
263
+ this.addSmartLock(id, { id, device })
264
+ }
265
+ this.smartLocks[id].context.device = device
266
+ this.smartLocks[id].update(device.lastKnownState)
267
+ if (
268
+ device.lastKnownState.doorsensorState != null &&
269
+ device.lastKnownState.doorsensorState !== NbClient.DoorSensorStates.DEACTIVATED
270
+ ) {
271
+ if (this.doorSensors[id + '-S'] == null) {
272
+ this.addDoorSensor(id + '-S', { id: id + '-S', device })
273
+ }
274
+ this.doorSensors[id + '-S'].context.device = device
275
+ this.doorSensors[id + '-S'].update(device.lastKnownState)
276
+ } else if (this.doorSensors[id + '-S'] != null) {
277
+ this.doorSensors[id + '-S'].destroy()
278
+ delete this.doorSensors[id + '-S']
286
279
  }
287
- this.doorSensors[id + '-S'].values.firmware = device.firmwareVersion
288
- this.doorSensors[id + '-S'].context.deviceType = device.deviceType
289
- this.doorSensors[id + '-S'].update(device.lastKnownState)
290
- } else if (this.doorSensors[id + '-S'] != null) {
291
- this.doorSensors[id + '-S'].destroy()
292
- delete this.doorSensors[id + '-S']
293
- }
294
- break
295
- case NbClient.DeviceTypes.OPENER:
296
- if (this.openers[id] == null) {
297
- this.addOpener(id, {
298
- id,
299
- name: device.name,
300
- firmware: device.firmwareVersion,
301
- deviceType: device.deviceType
302
- })
303
- }
304
- this.openers[id].values.firmware = device.firmwareVersion
305
- this.openers[id].context.deviceType = device.deviceType
306
- if (device.lastKnownState == null) {
307
- this.openers[id].warn('no last known state')
308
- continue
309
- }
310
- this.openers[id].update(device.lastKnownState)
311
- break
312
- default:
313
- break
280
+ break
281
+ case NbClient.DeviceTypes.OPENER:
282
+ if (device.lastKnownState == null) {
283
+ this.warn('%s: no last known state', id)
284
+ continue
285
+ }
286
+ if (this.openers[id] == null) {
287
+ this.addOpener(id, { id, device })
288
+ }
289
+ this.openers[id].context.device = device
290
+ this.openers[id].update(device.lastKnownState)
291
+ break
292
+ default:
293
+ break
294
+ }
295
+ } catch (error) {
296
+ this.warn('heartbeat error: %s', error)
314
297
  }
315
298
  }
316
299
  } catch (error) {
@@ -322,14 +305,24 @@ class Bridge extends homebridgeLib.AccessoryDelegate {
322
305
 
323
306
  class SmartLock extends NbAccessory {
324
307
  constructor (bridge, params) {
325
- params.category = bridge.Accessory.Categories.DOOR_LOCK
326
- params.model = NbClient.modelName(params.deviceType, params.firmware)
327
- super(bridge, params)
328
- this.batteryService = new homebridgeLib.ServiceDelegate.Battery(this)
308
+ super(bridge, {
309
+ id: params.id,
310
+ name: params.device.name,
311
+ device: params.device,
312
+ category: bridge.Accessory.Categories.DOOR_LOCK,
313
+ model: NbClient.modelName(params.device.deviceType, params.device.firmwareVersion)
314
+ })
329
315
  this.service = new NbService.SmartLock(this)
330
316
  if (this.platform.config.latch) {
331
317
  this.latchService = new NbService.Latch(this)
332
318
  }
319
+ this.batteryService = new homebridgeLib.ServiceDelegate.Battery(this, {
320
+ batteryLevel: params.device.lastKnownState.batteryChargeState,
321
+ chargingState: params.device.lastKnownState.batteryCharging,
322
+ statusLowBattery: params.device.lastKnownState.batteryCritical
323
+ ? this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_LOW
324
+ : this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_NORMAL
325
+ })
333
326
  }
334
327
 
335
328
  update (state) {
@@ -340,6 +333,11 @@ class SmartLock extends NbAccessory {
340
333
  if (state.batteryChargeState) {
341
334
  this.batteryService.values.batteryLevel = state.batteryChargeState
342
335
  }
336
+ if (state.batteryCharging != null) {
337
+ this.batteryService.values.chargingState = state.batteryCharging
338
+ ? this.Characteristics.hap.ChargingState.CHARGING
339
+ : this.Characteristics.hap.ChargingState.NOT_CHARGING
340
+ }
343
341
  if (state.batteryCritical != null) {
344
342
  this.batteryService.values.statusLowBattery = state.batteryCritical
345
343
  ? this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_LOW
@@ -361,8 +359,21 @@ class DoorSensor extends NbAccessory {
361
359
  constructor (bridge, params) {
362
360
  params.category = bridge.Accessory.Categories.DOOR
363
361
  params.model = 'Door Sensor'
364
- super(bridge, params)
362
+ super(bridge, {
363
+ id: params.id,
364
+ name: params.device.name + ' Sensor',
365
+ device: params.device,
366
+ category: bridge.Accessory.Categories.DOOR,
367
+ model: 'Door Sensor'
368
+ })
365
369
  this.service = new NbService.DoorSensor(this)
370
+ if (params.device.lastKnownState.doorsensorBatteryCritical) {
371
+ this.batteryService = new homebridgeLib.ServiceDelegate.Battery(this, {
372
+ statusLowBattery: params.device.lastKnownState.doorsensorBatteryCritical
373
+ ? this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_LOW
374
+ : this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_NORMAL
375
+ })
376
+ }
366
377
  this.historyService = new homebridgeLib.ServiceDelegate.History(this, {
367
378
  contactDelegate: this.service.characteristicDelegate('contact'),
368
379
  lastContactDelegate: this.service.characteristicDelegate('lastActivation'),
@@ -372,6 +383,11 @@ class DoorSensor extends NbAccessory {
372
383
 
373
384
  update (state) {
374
385
  this.service.update(state)
386
+ if (state.doorsensorBatteryCritical != null) {
387
+ this.batteryService.values.statusLowBattery = state.doorsensorBatteryCritical
388
+ ? this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_LOW
389
+ : this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_NORMAL
390
+ }
375
391
  }
376
392
 
377
393
  async identify () {
@@ -387,26 +403,30 @@ class DoorSensor extends NbAccessory {
387
403
  class Opener extends NbAccessory {
388
404
  constructor (bridge, params) {
389
405
  params.category = bridge.Accessory.Categories.DOOR_LOCK
390
- params.model = NbClient.modelName(params.deviceType, params.firmware)
391
- super(bridge, params)
392
- this.batteryService = new homebridgeLib.ServiceDelegate.Battery(this)
393
- this.doorBellService = new NbService.DoorBell(this)
406
+ params.model = NbClient.modelName(params.device.deviceType, params.device.firmwareVersion)
407
+ super(bridge, {
408
+ id: params.id,
409
+ name: params.device.name,
410
+ device: params.device,
411
+ category: bridge.Accessory.Categories.DOOR_LOCK,
412
+ model: NbClient.modelName(params.device.deviceType, params.device.firmwareVersion)
413
+ })
394
414
  this.openerService = new NbService.Opener(this)
415
+ this.doorBellService = new NbService.DoorBell(this)
416
+ this.batteryService = new homebridgeLib.ServiceDelegate.Battery(this, {
417
+ statusLowBattery: params.device.lastKnownState.doorsensorBatteryCritical
418
+ ? this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_LOW
419
+ : this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_NORMAL
420
+ })
395
421
  }
396
422
 
397
423
  update (state) {
398
424
  this.doorBellService.update(state)
399
425
  this.openerService.update(state)
400
426
  if (state.batteryCritical != null) {
401
- if (state.batteryCritical) {
402
- this.batteryService.values.statusLowBattery =
403
- this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_LOW
404
- this.batteryService.values.batteryLevel = 10
405
- } else {
406
- this.batteryService.values.statusLowBattery =
407
- this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_NORMAL
408
- this.batteryService.values.batteryLevel = 90
409
- }
427
+ this.batteryService.values.statusLowBattery = state.batteryCritical
428
+ ? this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_LOW
429
+ : this.Characteristics.hap.StatusLowBattery.BATTERY_LEVEL_NORMAL
410
430
  }
411
431
  }
412
432
 
package/lib/NbPlatform.js CHANGED
@@ -96,20 +96,19 @@ class NbPlatform extends homebridgeLib.Platform {
96
96
 
97
97
  async init (beat) {
98
98
  try {
99
- if (Object.keys(this.bridges).length === 0) {
100
- const jobs = []
101
- const bridges = await this.discovery.discover()
102
- this.debug('discovery: %j', bridges)
103
- for (const bridge of bridges) {
104
- jobs.push(this.foundBridge(bridge))
105
- }
106
- for (const job of jobs) {
107
- try {
108
- await job
109
- } catch (error) {
110
- if (!(error instanceof homebridgeLib.HttpClient.HttpError)) {
111
- this.error(error)
112
- }
99
+ const jobs = []
100
+ for (const id in this.bridges) {
101
+ jobs.push(events.once(this.bridges[id], 'initialised'))
102
+ }
103
+ if (jobs.length === 0) {
104
+ jobs.push(this.discover())
105
+ }
106
+ for (const job of jobs) {
107
+ try {
108
+ await job
109
+ } catch (error) {
110
+ if (!(error instanceof homebridgeLib.HttpClient.HttpError)) {
111
+ this.error(error)
113
112
  }
114
113
  }
115
114
  }
@@ -123,24 +122,28 @@ class NbPlatform extends homebridgeLib.Platform {
123
122
  this.emit('initialised')
124
123
  }
125
124
 
125
+ async discover () {
126
+ const bridges = await this.discovery.discover()
127
+ this.debug('discovery: %j', bridges)
128
+ const jobs = []
129
+ for (const bridge of bridges) {
130
+ jobs.push(this.foundBridge(bridge))
131
+ }
132
+ for (const job of jobs) {
133
+ try {
134
+ await job
135
+ } catch (error) {
136
+ if (!(error instanceof homebridgeLib.HttpClient.HttpError)) {
137
+ this.error(error)
138
+ }
139
+ }
140
+ }
141
+ }
142
+
126
143
  async heartbeat (beat) {
127
144
  if (beat % discoveryInterval === discoveryInterval - 5) {
128
145
  try {
129
- const bridges = await this.discovery.discover()
130
- this.debug('discovery: %j', bridges)
131
- const jobs = []
132
- for (const bridge of bridges) {
133
- jobs.push(this.foundBridge(bridge))
134
- }
135
- for (const job of jobs) {
136
- try {
137
- await job
138
- } catch (error) {
139
- if (!(error instanceof homebridgeLib.HttpClient.HttpError)) {
140
- this.error(error)
141
- }
142
- }
143
- }
146
+ await this.discover()
144
147
  } catch (error) {
145
148
  if (!(error instanceof homebridgeLib.HttpClient.HttpError)) {
146
149
  this.error(error)
@@ -247,6 +250,10 @@ class NbPlatform extends homebridgeLib.Platform {
247
250
  )
248
251
  break
249
252
  }
253
+ if (context.device == null) {
254
+ // Old plugin version - re-create accessory delegate on bridge initialisation
255
+ break
256
+ }
250
257
  context.id = id
251
258
  context.name = name
252
259
  bridge['add' + className](context.id, context)
@@ -254,7 +261,7 @@ class NbPlatform extends homebridgeLib.Platform {
254
261
  break
255
262
  default:
256
263
  this.warn(
257
- '%s: ignore unknown %s %v accesssory', name, className, version
264
+ '%s: ignore unknown %s v%s accesssory', name, className, version
258
265
  )
259
266
  break
260
267
  }
package/lib/NbService.js CHANGED
@@ -105,7 +105,7 @@ class SmartLock extends homebridgeLib.ServiceDelegate {
105
105
  return
106
106
  }
107
107
  const response = await nbAccessory.client.lockAction(
108
- nbAccessory.id, nbAccessory.context.deviceType,
108
+ nbAccessory.id, nbAccessory.deviceType,
109
109
  value === this.Characteristics.hap.LockTargetState.UNSECURED
110
110
  ? NbClient.LockActions.UNLOCK
111
111
  : NbClient.LockActions.LOCK
@@ -122,7 +122,7 @@ class SmartLock extends homebridgeLib.ServiceDelegate {
122
122
  }
123
123
  if (value === this.Characteristics.hap.LockTargetState.UNSECURED) {
124
124
  await nbAccessory.client.lockAction(
125
- nbAccessory.id, nbAccessory.context.deviceType,
125
+ nbAccessory.id, nbAccessory.deviceType,
126
126
  NbClient.LockActions.UNLATCH
127
127
  )
128
128
  }
@@ -143,7 +143,7 @@ class SmartLock extends homebridgeLib.ServiceDelegate {
143
143
  nbAccessory.update({ state: NbClient.LockStates.UNLATCHED })
144
144
  }, 3000)
145
145
  const response = await nbAccessory.client.lockAction(
146
- nbAccessory.id, nbAccessory.context.deviceType,
146
+ nbAccessory.id, nbAccessory.deviceType,
147
147
  NbClient.LockActions.UNLATCH
148
148
  )
149
149
  if (response != null && response.body.success) {
@@ -162,6 +162,13 @@ class SmartLock extends homebridgeLib.ServiceDelegate {
162
162
  Characteristic: this.Characteristics.hap.StatusFault,
163
163
  value: this.Characteristics.hap.StatusFault.NO_FAULT
164
164
  })
165
+ if (this.platform.config.latch) {
166
+ this.addCharacteristicDelegate({
167
+ key: 'label',
168
+ Characteristic: this.Characteristics.hap.ServiceLabelIndex,
169
+ value: 1
170
+ })
171
+ }
165
172
  }
166
173
 
167
174
  update (state) {
@@ -233,7 +240,7 @@ class Latch extends homebridgeLib.ServiceDelegate {
233
240
  nbAccessory.update({ state: NbClient.LockStates.UNLATCHED })
234
241
  }, 3000)
235
242
  const response = await nbAccessory.client.lockAction(
236
- nbAccessory.id, nbAccessory.context.deviceType,
243
+ nbAccessory.id, nbAccessory.deviceType,
237
244
  NbClient.LockActions.UNLATCH
238
245
  )
239
246
  if (response != null && response.body.success) {
@@ -243,6 +250,11 @@ class Latch extends homebridgeLib.ServiceDelegate {
243
250
  }
244
251
  } catch (error) { this.error(error) }
245
252
  })
253
+ this.addCharacteristicDelegate({
254
+ key: 'label',
255
+ Characteristic: this.Characteristics.hap.ServiceLabelIndex,
256
+ value: 2
257
+ })
246
258
  }
247
259
 
248
260
  update (state) {
@@ -280,6 +292,7 @@ class DoorSensor extends homebridgeLib.ServiceDelegate {
280
292
  constructor (nbAccessory, params = {}) {
281
293
  params.name = nbAccessory.name
282
294
  params.Service = nbAccessory.Services.hap.ContactSensor
295
+ params.primaryService = true
283
296
  super(nbAccessory, params)
284
297
 
285
298
  this.addCharacteristicDelegate({
@@ -374,6 +387,7 @@ class Opener extends homebridgeLib.ServiceDelegate {
374
387
  constructor (nbAccessory, params = {}) {
375
388
  params.name = nbAccessory.name
376
389
  params.Service = nbAccessory.Services.hap.LockMechanism
390
+ params.primaryService = true
377
391
  super(nbAccessory, params)
378
392
 
379
393
  this.addCharacteristicDelegate({
@@ -392,7 +406,7 @@ class Opener extends homebridgeLib.ServiceDelegate {
392
406
  }
393
407
  if (value === this.Characteristics.hap.LockTargetState.UNSECURED) {
394
408
  const response = await nbAccessory.client.lockAction(
395
- nbAccessory.id, nbAccessory.context.deviceType,
409
+ nbAccessory.id, nbAccessory.deviceType,
396
410
  NbClient.OpenerActions.OPEN
397
411
  )
398
412
  if (response != null && response.body.success) {
@@ -417,7 +431,7 @@ class Opener extends homebridgeLib.ServiceDelegate {
417
431
  return
418
432
  }
419
433
  await nbAccessory.client.lockAction(
420
- nbAccessory.id, nbAccessory.context.deviceType, value
434
+ nbAccessory.id, nbAccessory.deviceType, value
421
435
  ? NbClient.OpenerActions.ACTIVATE_RTO
422
436
  : NbClient.OpenerActions.DEACTIVATE_RTO
423
437
  )
@@ -432,7 +446,7 @@ class Opener extends homebridgeLib.ServiceDelegate {
432
446
  return
433
447
  }
434
448
  await nbAccessory.client.lockAction(
435
- nbAccessory.id, nbAccessory.context.deviceType, value
449
+ nbAccessory.id, nbAccessory.deviceType, value
436
450
  ? NbClient.OpenerActions.ACTIVATE_CM
437
451
  : NbClient.OpenerActions.DEACTIVATE_CM
438
452
  )
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "displayName": "Homebridge NB",
5
5
  "author": "Erik Baauw",
6
6
  "license": "Apache-2.0",
7
- "version": "1.4.4",
7
+ "version": "1.4.5",
8
8
  "keywords": [
9
9
  "homebridge-plugin",
10
10
  "homekit",
@@ -19,12 +19,12 @@
19
19
  },
20
20
  "engines": {
21
21
  "homebridge": "^1.6.1",
22
- "node": "^18.16.1",
22
+ "node": "18.17.0||^18||^16",
23
23
  "nuki": "2.15.0"
24
24
  },
25
25
  "dependencies": {
26
- "homebridge-lib": "~6.3.18",
27
- "hb-nb-tools": "~1.1.3"
26
+ "homebridge-lib": "~6.4.0",
27
+ "hb-nb-tools": "~1.1.4"
28
28
  },
29
29
  "scripts": {
30
30
  "prepare": "standard",