homebridge-deconz 1.1.1 → 1.2.1

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.
@@ -49,6 +49,25 @@ const hueTapMap = {
49
49
  99: 0 // release 3 and 4
50
50
  }
51
51
 
52
+ const hkEvent = {
53
+ SINGLE: 0x01,
54
+ DOUBLE: 0x02,
55
+ LONG: 0x04
56
+ }
57
+
58
+ const buttonEventMap = {
59
+ 0: 0, // PRESS
60
+ 1: hkEvent.LONG, // HOLD
61
+ 2: hkEvent.SINGLE, // SHORT_RELEASE
62
+ 3: hkEvent.LONG, // LONG_RELEASE
63
+ 4: hkEvent.DOUBLE, // DOUBLE_PRESS
64
+ 5: hkEvent.LONG, // TRIPLE_PRESS
65
+ 6: hkEvent.LONG, // QUADRUPLE_PRESS
66
+ 7: hkEvent.LONG, // SHAKE
67
+ 8: hkEvent.DOUBLE, // DROP
68
+ 9: 0 // TILT
69
+ }
70
+
52
71
  const ancillaryControlMap = {
53
72
  disarmed: 1002,
54
73
  already_disarmed: 1002,
@@ -437,6 +456,30 @@ class Resource {
437
456
  * @param {DeconzAccessory.Gateway} gateway - The gateway.
438
457
  */
439
458
  patchLabel (gateway) {
459
+ // FIX_ME: use introspect until buttons and events are reported as capability
460
+ if (this.body.introspect?.buttons != null) {
461
+ this.capabilities._introspect = true
462
+ this.capabilities._buttons = {}
463
+ let dots = 0
464
+ for (const button in this.body.introspect.buttons) {
465
+ const label = this.body.introspect.buttons[button].name
466
+ this.capabilities._buttons[button] = { label, events: 0 }
467
+ if (label.includes(' Dot')) {
468
+ dots++
469
+ }
470
+ }
471
+ this.capabilities._namespace = dots === this.body.introspect.buttons.length
472
+ ? gateway.Characteristics.hap.ServiceLabelNamespace.DOTS
473
+ : gateway.Characteristics.hap.ServiceLabelNamespace.ARABIC_NUMERALS
474
+ for (const value in this.body.introspect.values) {
475
+ const button = Math.round(value / 1000)
476
+ const event = value % 1000
477
+ if (this.capabilities._buttons[button] != null) {
478
+ this.capabilities._buttons[button].events |= buttonEventMap[event]
479
+ }
480
+ }
481
+ }
482
+ // End FIX_ME
440
483
  const buttons = []
441
484
  let dots = false
442
485
  switch (this.manufacturer) {
@@ -555,11 +598,11 @@ class Resource {
555
598
  buttons.push([1, 'Dim Up', SINGLE | LONG])
556
599
  buttons.push([2, 'Dim Down', SINGLE | LONG])
557
600
  break
558
- case 'SOMRIG shortcut button':
559
- dots = true
560
- buttons.push([1, '1', SINGLE | DOUBLE | LONG])
561
- buttons.push([2, '2', SINGLE | DOUBLE | LONG])
562
- break
601
+ // case 'SOMRIG shortcut button':
602
+ // dots = true
603
+ // buttons.push([1, '1', SINGLE | DOUBLE | LONG])
604
+ // buttons.push([2, '2', SINGLE | DOUBLE | LONG])
605
+ // break
563
606
  case 'SYMFONISK Sound Controller':
564
607
  if (this.cluster === '1000') {
565
608
  buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
@@ -584,17 +627,6 @@ class Resource {
584
627
  buttons.push([6, 'One Dot', SINGLE | DOUBLE | LONG])
585
628
  buttons.push([7, 'Two Dots', SINGLE | DOUBLE | LONG])
586
629
  break
587
- case 'TRADFRI SHORTCUT Button':
588
- buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
589
- break
590
- case 'TRADFRI on/off switch':
591
- buttons.push([1, 'On', SINGLE | LONG])
592
- buttons.push([2, 'Off', SINGLE | LONG])
593
- break
594
- case 'TRADFRI open/close remote':
595
- buttons.push([1, 'Open', SINGLE | LONG])
596
- buttons.push([2, 'Close', SINGLE | LONG])
597
- break
598
630
  case 'TRADFRI remote control':
599
631
  buttons.push([1, 'Power', SINGLE])
600
632
  buttons.push([2, 'Dim Up', SINGLE | LONG])
@@ -751,7 +783,7 @@ class Resource {
751
783
  }
752
784
  break
753
785
  case 'lumi.sensor_switch': // Xiaomi Mi wireless switch
754
- case 'lumi.sensor_switch.aq2': // Xiaomi Aqara smart wireless switch
786
+ // fallthrough
755
787
  case 'lumi.sensor_switch.aq3': // Xiaomi Aqara smart wireless switch with gyro
756
788
  buttons.push([1, 'Button', SINGLE | DOUBLE | LONG])
757
789
  break
@@ -1037,18 +1069,6 @@ class Resource {
1037
1069
  break
1038
1070
  }
1039
1071
  break
1040
- case '_TZ3000_mh9px7cq':
1041
- switch (this.model) {
1042
- case 'TS0044':
1043
- buttons.push([1, 'Button 1', SINGLE | DOUBLE | LONG])
1044
- buttons.push([2, 'Button 2', SINGLE | DOUBLE | LONG])
1045
- buttons.push([3, 'Button 3', SINGLE | DOUBLE | LONG])
1046
- buttons.push([4, 'Button 4', SINGLE | DOUBLE | LONG])
1047
- break
1048
- default:
1049
- break
1050
- }
1051
- break
1052
1072
  case '_TZ3000_pzui3skt':
1053
1073
  switch (this.model) {
1054
1074
  case 'TS0041': // Tuya 1-button switch
@@ -1234,30 +1254,6 @@ class Resource {
1234
1254
  break
1235
1255
  }
1236
1256
  break
1237
- case 'ubisys':
1238
- switch (this.model) {
1239
- case 'C4 (5504)':
1240
- case 'C4-R (5604)':
1241
- buttons.push([1, '1', SINGLE | LONG])
1242
- buttons.push([2, '2', SINGLE | LONG])
1243
- buttons.push([3, '3', SINGLE | LONG])
1244
- buttons.push([4, '4', SINGLE | LONG])
1245
- break
1246
- case 'D1 (5503)':
1247
- case 'D1-R (5603)':
1248
- case 'S1-R (5601)':
1249
- case 'S2 (5502)':
1250
- case 'S2-R (5602)':
1251
- buttons.push([1, '1', SINGLE | LONG])
1252
- buttons.push([2, '2', SINGLE | LONG])
1253
- break
1254
- case 'S1 (5501)':
1255
- buttons.push([1, '1', SINGLE | LONG])
1256
- break
1257
- default:
1258
- break
1259
- }
1260
- break
1261
1257
  default:
1262
1258
  if (this.body.type === 'ZHAAncillaryControl') {
1263
1259
  buttons.push([1, 'Disarm', SINGLE])
@@ -1279,13 +1275,20 @@ class Resource {
1279
1275
  for (const button of buttons) {
1280
1276
  this.capabilities.buttons[button[0]] = {
1281
1277
  label: button[1],
1282
- events: button[2],
1283
- hasRepeat: button[3]
1278
+ events: button[2]
1279
+ }
1280
+ if (button[3]) {
1281
+ this.capabilities.buttons[button[0]].hasRepeat = button[3]
1284
1282
  }
1285
1283
  }
1286
1284
  this.capabilities.namespace = dots
1287
1285
  ? gateway.Characteristics.hap.ServiceLabelNamespace.DOTS
1288
1286
  : gateway.Characteristics.hap.ServiceLabelNamespace.ARABIC_NUMERALS
1287
+ } else if (this.capabilities._buttons != null) {
1288
+ this.capabilities.buttons = this.capabilities._buttons
1289
+ this.capabilities.namespace = this.capabilities._namespace
1290
+ delete this.capabilities._buttons
1291
+ delete this.capabilities._namespace
1289
1292
  }
1290
1293
  }
1291
1294
 
@@ -1304,7 +1307,8 @@ class Resource {
1304
1307
  patchThermostat () {
1305
1308
  if (
1306
1309
  (this.manufacturer === 'Danfoss' && ['eTRV0100', 'eTRV0103'].includes(this.model)) ||
1307
- (this.manufacturer === 'ELKO' && this.model === 'Super TR')
1310
+ (this.manufacturer === 'ELKO' && this.model === 'Super TR') ||
1311
+ (this.manufacturer === 'LUMI' && this.model === 'lumi.airrtc.agl001')
1308
1312
  ) {
1309
1313
  this.capabilities.heatValue = 'heat'
1310
1314
  } else {
@@ -78,12 +78,12 @@ class Gateway extends AccessoryDelegate {
78
78
  if (this.context.settingsById == null) {
79
79
  this.context.settingsById = {}
80
80
  }
81
- if (this.context.fullState != null) {
82
- this.analyseFullState(this.context.fullState, {
83
- analyseOnly: true,
84
- logUnsupported: true
85
- })
86
- }
81
+ // if (this.context.fullState != null) {
82
+ // this.analyseFullState(this.context.fullState, {
83
+ // analyseOnly: true,
84
+ // logUnsupported: true
85
+ // })
86
+ // }
87
87
 
88
88
  this.addPropertyDelegate({
89
89
  key: 'apiKey',
@@ -997,8 +997,22 @@ class Gateway extends AccessoryDelegate {
997
997
  } catch (error) {
998
998
  fullState.alarmsystems = {}
999
999
  }
1000
+ // FIX_ME: use introspect until buttons and events are reported as capability
1001
+ fullState.introspectByRid = {}
1002
+ for (const rid in fullState.sensors) {
1003
+ const sensor = fullState.sensors[rid]
1004
+ if (sensor.type === 'ZHASwitch') {
1005
+ try {
1006
+ fullState.introspectByRid[rid] = await this.client.get(
1007
+ '/devices/' + sensor.uniqueid + '/state/buttonevent/introspect'
1008
+ )
1009
+ } catch (error) { }
1010
+ }
1011
+ }
1012
+ // End FIX_ME
1000
1013
  this.context.fullState = fullState
1001
1014
  this.pollFullState = false
1015
+ await this.analyseFullState(this.context.fullState, { logUnsupported: true })
1002
1016
  } else {
1003
1017
  const config = await this.client.get('/config')
1004
1018
  if (config.bridgeid === this.id && config.UTC == null) {
@@ -1027,8 +1041,8 @@ class Gateway extends AccessoryDelegate {
1027
1041
  if (this.values.exposeSchedules) {
1028
1042
  this.context.fullState.schedules = await this.client.get('/schedules')
1029
1043
  }
1044
+ await this.analyseFullState(this.context.fullState)
1030
1045
  }
1031
- await this.analyseFullState(this.context.fullState)
1032
1046
  } catch (error) {
1033
1047
  this.error('poll error: %s', error)
1034
1048
  } finally {
@@ -1316,8 +1330,39 @@ class Gateway extends AccessoryDelegate {
1316
1330
  const warn = (logUnsupported ? this.warn : this.vdebug).bind(this)
1317
1331
  const debug = (logUnsupported ? this.debug : this.vdebug).bind(this)
1318
1332
 
1333
+ // FIX_ME: use introspect until buttons and events are reported as capability
1334
+ if (this.context.fullState.introspectByRid?.[rid] != null) {
1335
+ body.introspect = this.context.fullState.introspectByRid[rid]
1336
+ }
1337
+ // End FIX_ME
1319
1338
  const resource = new Deconz.Resource(this, rtype, rid, body)
1320
1339
  const { id, serviceName } = resource
1340
+ // FIX_ME: check introspect against whitelist
1341
+ if (logUnsupported && resource.body.type === 'ZHASwitch') {
1342
+ if (!resource.capabilities._introspect) {
1343
+ this.warn(
1344
+ '%s: /sensors/%d: %s by %s: no introspect',
1345
+ id, rid, resource.model, resource.manufacturer
1346
+ )
1347
+ }
1348
+ } else if (resource.capabilities._buttons != null) {
1349
+ if (
1350
+ JSON.stringify(resource.capabilities._buttons) !==
1351
+ JSON.stringify(resource.capabilities.buttons) ||
1352
+ resource.capabilities._namespace !== resource.capabilities.namespace
1353
+ ) {
1354
+ this.debug(
1355
+ '%s: /sensors/%d: %s by %s: whitelist vs introspect mismatch: %j',
1356
+ id, rid, resource.model, resource.manufacturer, resource.capabilities
1357
+ )
1358
+ } else {
1359
+ this.warn(
1360
+ '%s: /sensors/%d: %s by %s: whitelist matches introspect',
1361
+ id, rid, resource.model, resource.manufacturer
1362
+ )
1363
+ }
1364
+ }
1365
+ // End FIX_ME
1321
1366
  if (this.blacklist[rtype]?.[rid]) {
1322
1367
  debug('%s: /%s/%d: ignoring blacklisted resource', id, rtype, rid)
1323
1368
  return
@@ -253,11 +253,6 @@ class DeconzPlatform extends Platform {
253
253
  ) {
254
254
  this.gatewayMap[id] = new DeconzAccessory.Gateway(this, context)
255
255
  }
256
- } else {
257
- const gateway = this.gatewayMap[context.gid]
258
- if (gateway != null) {
259
- gateway.addAccessory(id)
260
- }
261
256
  }
262
257
  } catch (error) { this.error(error) }
263
258
  }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "ebaauw"
8
8
  ],
9
9
  "license": "Apache-2.0",
10
- "version": "1.1.1",
10
+ "version": "1.2.1",
11
11
  "keywords": [
12
12
  "homebridge-plugin",
13
13
  "homekit",
@@ -26,13 +26,13 @@
26
26
  "ui": "cli/ui.js"
27
27
  },
28
28
  "engines": {
29
- "deCONZ": "2.29.5",
30
- "homebridge": "^1.9.0||^2.0.0-beta",
31
- "node": "^22||^20||^18"
29
+ "deCONZ": "2.30.2",
30
+ "homebridge": "^1.10.0||^2.0.0-beta",
31
+ "node": "^22||^20"
32
32
  },
33
33
  "dependencies": {
34
- "hb-deconz-tools": "~2.0.9",
35
- "homebridge-lib": "~7.1.4"
34
+ "hb-deconz-tools": "~2.0.12",
35
+ "homebridge-lib": "~7.1.6"
36
36
  },
37
37
  "scripts": {
38
38
  "prepare": "standard && rm -rf out && jsdoc -c jsdoc.json",