homebridge-deconz 1.0.15 → 1.0.18

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.
@@ -135,6 +135,9 @@ class Resource {
135
135
  * @type {string}
136
136
  */
137
137
  this.id = mac
138
+ if (gateway.splitdevice[this.rtype]?.[this.rid]) {
139
+ this.id += '-' + endpoint
140
+ }
138
141
 
139
142
  /** The subtype of the corresponding HomeKit service.
140
143
  *
@@ -665,9 +665,12 @@ class Gateway extends AccessoryDelegate {
665
665
  let { serviceName } = device.resource
666
666
  await this.importAccessoryType(serviceName)
667
667
  if (DeconzAccessory[serviceName] == null) {
668
- // this.warn('%s: %s: not yet supported %s type', body.name, body.type, rtype)
668
+ // this.warn('%s: %s: accessory type not available', body.name, serviceName)
669
669
  serviceName = 'Sensor'
670
670
  }
671
+ if (this.context.settingsById[id]?.serviceName != null) {
672
+ await this.importServiceType(this.context.settingsById[id].serviceName)
673
+ }
671
674
  for (const resourceServiceName of Object.keys(device.subtypesByServiceName)) {
672
675
  await this.importServiceType(resourceServiceName)
673
676
  }
@@ -1007,6 +1010,7 @@ class Gateway extends AccessoryDelegate {
1007
1010
  this.context.fullState.config = config
1008
1011
  this.context.fullState.lights = await this.client.get('/lights')
1009
1012
  this.context.fullState.sensors = await this.client.get('/sensors')
1013
+ this.context.fullState.resourcelinks = await this.client.get('/resourcelinks')
1010
1014
  if (this.nDevicesByRtype.groups > 0) {
1011
1015
  this.context.fullState.groups = await this.client.get('/groups')
1012
1016
  this.context.fullState.groups[0] = await this.client.get('/groups/0')
@@ -1030,6 +1034,54 @@ class Gateway extends AccessoryDelegate {
1030
1034
  }
1031
1035
  }
1032
1036
 
1037
+ /* Analyse blacklist resourcelinks.
1038
+ */
1039
+ analyseResourcelinks (logUnsupported = false) {
1040
+ const warn = (logUnsupported ? this.warn : this.vdebug).bind(this)
1041
+ /** Blacklisted resources.
1042
+ *
1043
+ * Updated by
1044
+ * {@link DeconzAccessory.Gateway#analyseBlacklist analyseBlacklist()}.
1045
+ * @type {Object<string, boolean>}
1046
+ */
1047
+ this.blacklist = {
1048
+ lights: {},
1049
+ sensors: {}
1050
+ }
1051
+ this.splitdevice = {
1052
+ lights: {},
1053
+ sensors: {}
1054
+ }
1055
+ for (const key in this.context.fullState.resourcelinks) {
1056
+ const link = this.context.fullState.resourcelinks[key]
1057
+ if (
1058
+ link.name === 'homebridge-deconz' && link.links != null &&
1059
+ link.description != null
1060
+ ) {
1061
+ const type = link.description.toLowerCase()
1062
+ switch (type) {
1063
+ case 'migration':
1064
+ break
1065
+ case 'splitdevice':
1066
+ case 'blacklist':
1067
+ this.debug('/resourcelinks/%d: %d %s entries', key, link.links.length, type)
1068
+ for (const resource of link.links) {
1069
+ const rtype = resource.split('/')[1]
1070
+ const rid = resource.split('/')[2]
1071
+ if (this[type][rtype] == null) {
1072
+ warn('/resourcelinks/%d: %s: ignoring unsupported %s resource', key, resource, type)
1073
+ continue
1074
+ }
1075
+ this[type][rtype][rid] = true
1076
+ }
1077
+ break
1078
+ default:
1079
+ warn('/resourcelinks/%d: %s: ignoring unsupported resourcelink', key, type)
1080
+ }
1081
+ }
1082
+ }
1083
+ }
1084
+
1033
1085
  /** Analyse the peristed full state of the gateway,
1034
1086
  * adding, re-configuring, and deleting delegates for corresponding HomeKit
1035
1087
  * accessories and services.
@@ -1094,6 +1146,7 @@ class Gateway extends AccessoryDelegate {
1094
1146
  this.nDevicesByRtype = {}
1095
1147
 
1096
1148
  this.vdebug('analysing resources...')
1149
+ this.analyseResourcelinks(params.logUnsupported)
1097
1150
  for (const rtype of rtypes) {
1098
1151
  this.deviceByRidByRtype[rtype] = {}
1099
1152
  for (const rid in fullState[rtype]) {
@@ -1251,10 +1304,16 @@ class Gateway extends AccessoryDelegate {
1251
1304
  * unsupported resources.
1252
1305
  */
1253
1306
  analyseResource (rtype, rid, body, logUnsupported) {
1307
+ const warn = (logUnsupported ? this.warn : this.vdebug).bind(this)
1308
+ const debug = (logUnsupported ? this.debug : this.vdebug).bind(this)
1309
+
1254
1310
  const resource = new Deconz.Resource(this, rtype, rid, body)
1255
1311
  const { id, serviceName } = resource
1312
+ if (this.blacklist[rtype]?.[rid]) {
1313
+ debug('%s: /%s/%d: ignoring blacklisted resource', id, rtype, rid)
1314
+ return
1315
+ }
1256
1316
  if (id === this.id || serviceName === '') {
1257
- const debug = (logUnsupported ? this.debug : this.vdebug).bind(this)
1258
1317
  debug(
1259
1318
  '%s: /%s/%d: %s: ignoring unsupported %s type',
1260
1319
  id, rtype, rid, body.type, rtype
@@ -1262,7 +1321,6 @@ class Gateway extends AccessoryDelegate {
1262
1321
  return
1263
1322
  }
1264
1323
  if (serviceName == null) {
1265
- const warn = (logUnsupported ? this.warn : this.vdebug).bind(this)
1266
1324
  warn(
1267
1325
  '%s: /%s/%d: %s: ignoring unknown %s type',
1268
1326
  id, rtype, rid, body.type, rtype
@@ -26,6 +26,8 @@ class Light extends DeconzAccessory {
26
26
  this.addPropertyDelegate({
27
27
  key: 'serviceName',
28
28
  value: device.resource.serviceName
29
+ }).on('didSet', (value) => {
30
+ gateway.context.settingsById[device.id].serviceName = value
29
31
  })
30
32
 
31
33
  this.service = this.createService(device.resource, {
@@ -139,8 +139,8 @@ class DeconzAccessory extends AccessoryDelegate {
139
139
  }
140
140
  if (DeconzService[params.serviceName] == null) {
141
141
  this.warn(
142
- '%s: %s: not yet supported %s type',
143
- resource.rpath, resource.body.type, resource.rtype
142
+ '%s: %s: service type not available',
143
+ resource.rpath, params.serviceName
144
144
  )
145
145
  return
146
146
  }
@@ -236,7 +236,6 @@ class DeconzAccessory extends AccessoryDelegate {
236
236
  lowBatteryThreshold: this.servicesByServiceName?.Battery?.[0].values.lowBatteryThreshold,
237
237
  // offset: this.servicesByServiceName?.Temperature?.[0].values.offset,
238
238
  serviceName: this.values.serviceName,
239
- splitLight: undefined,
240
239
  venetianBlind: this.service.values.venetianBlind,
241
240
  useExternalTemperature: this.service.values.useExternalTemperature,
242
241
  wallSwitch: this.service.values.wallSwitch
@@ -293,7 +292,7 @@ class DeconzAccessory extends AccessoryDelegate {
293
292
  case 'serviceName':
294
293
  if (this.values.serviceName != null) {
295
294
  value = OptionParser.toString(key, body[key])
296
- if (DeconzService[value] == null) {
295
+ if (['Light', 'Outlet', 'Switch', 'Valve'].includes(value) == null) {
297
296
  throw new Error(`${value}: illegal serviceName`)
298
297
  }
299
298
  this.values.serviceName = value
@@ -15,7 +15,6 @@ class LightsResource extends DeconzService {
15
15
 
16
16
  this.updating = 0
17
17
  this.targetState = {}
18
- this.deferrals = []
19
18
  }
20
19
 
21
20
  addCharacteristicDelegates (params = {}) {
@@ -23,6 +23,15 @@ class Motion extends DeconzService.SensorsResource {
23
23
  value: false
24
24
  })
25
25
 
26
+ if (resource.body.state.distance !== undefined) {
27
+ this.addCharacteristicDelegate({
28
+ key: 'distance',
29
+ Characteristic: this.Characteristics.my.Distance,
30
+ unit: ' cm',
31
+ value: 0
32
+ })
33
+ }
34
+
26
35
  this.addCharacteristicDelegate({
27
36
  key: 'sensitivity',
28
37
  Characteristic: this.Characteristics.eve.Sensitivity
@@ -50,6 +59,32 @@ class Motion extends DeconzService.SensorsResource {
50
59
  }
51
60
  })
52
61
 
62
+ if (resource.body.config.detectionrange !== undefined) {
63
+ this.addCharacteristicDelegate({
64
+ key: 'detectionRange',
65
+ Characteristic: this.Characteristics.my.DetectionRange,
66
+ unit: ' cm',
67
+ value: 600
68
+ }).on('didSet', async (value, fromHomeKit) => {
69
+ if (fromHomeKit) {
70
+ const detectionrange = Math.max(0, Math.min(value, 600))
71
+ await this.putConfig({ detectionrange })
72
+ }
73
+ })
74
+ }
75
+
76
+ if (resource.body.config.resetpresence !== undefined) {
77
+ this.addCharacteristicDelegate({
78
+ key: 'reset',
79
+ Characteristic: this.Characteristics.my.Reset,
80
+ value: false
81
+ }).on('didSet', async (value, fromHomeKit) => {
82
+ if (fromHomeKit) {
83
+ await this.put('/config', { resetpresence: value })
84
+ }
85
+ })
86
+ }
87
+
53
88
  this.addCharacteristicDelegates()
54
89
 
55
90
  this.update(resource.body, resource.rpath)
@@ -59,6 +94,9 @@ class Motion extends DeconzService.SensorsResource {
59
94
  if (state.presence != null) {
60
95
  this.values.motion = state.presence
61
96
  }
97
+ if (state.distance != null) {
98
+ this.values.distance = state.distance
99
+ }
62
100
  if (state.vibration != null) {
63
101
  this.values.motion = state.vibration
64
102
  }
@@ -83,6 +121,12 @@ class Motion extends DeconzService.SensorsResource {
83
121
  ? this.Characteristics.eve.Sensitivity.LOW
84
122
  : this.Characteristics.eve.Sensitivity.MEDIUM
85
123
  }
124
+ if (config.detectionrange != null) {
125
+ this.values.detectionRange = config.detectionrange
126
+ }
127
+ if (config.resetpresence != null) {
128
+ this.values.reset = config.resetpresence
129
+ }
86
130
  super.updateConfig(config)
87
131
  }
88
132
  }
@@ -3,6 +3,8 @@
3
3
  //
4
4
  // Homebridge plugin for deCONZ.
5
5
 
6
+ import { timeout } from 'homebridge-lib'
7
+
6
8
  import { ApiClient } from 'hb-deconz-tools/ApiClient'
7
9
 
8
10
  import { DeconzService } from '../DeconzService/index.js'
@@ -13,9 +15,12 @@ const { dateToString } = ApiClient
13
15
  * @memberof DeconzService
14
16
  */
15
17
  class SensorsResource extends DeconzService {
16
- // constructor (accessory, resource, params) {
17
- // super(accessory, resource, params)
18
- // }
18
+ constructor (accessory, resource, params) {
19
+ super(accessory, resource, params)
20
+
21
+ this.updating = 0
22
+ this.targetConfig = {}
23
+ }
19
24
 
20
25
  addCharacteristicDelegates (params = {}) {
21
26
  if (!params.noLastUpdated) {
@@ -83,6 +88,34 @@ class SensorsResource extends DeconzService {
83
88
  return this.put('/config', { alert: 'select' })
84
89
  }
85
90
  }
91
+
92
+ // Collect config changes into a combined request.
93
+ async putConfig (config) {
94
+ for (const key in config) {
95
+ this.resource.body.config[key] = config[key]
96
+ this.targetConfig[key] = config[key]
97
+ }
98
+ return this._putConfig()
99
+ }
100
+
101
+ // Send the request (for the combined state changes) to the gateway.
102
+ async _putConfig () {
103
+ try {
104
+ if (this.platform.config.waitTimeUpdate > 0) {
105
+ this.updating++
106
+ await timeout(this.platform.config.waitTimeUpdate)
107
+ if (--this.updating > 0) {
108
+ return
109
+ }
110
+ }
111
+ const targetConfig = this.targetConfig
112
+ this.targetConfig = {}
113
+ await this.put('/config', targetConfig)
114
+ // this.recentlyUpdated = true
115
+ // await timeout(500)
116
+ // this.recentlyUpdated = false
117
+ } catch (error) { this.warn(error) }
118
+ }
86
119
  }
87
120
 
88
121
  DeconzService.SensorsResource = SensorsResource
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "ebaauw"
8
8
  ],
9
9
  "license": "Apache-2.0",
10
- "version": "1.0.15",
10
+ "version": "1.0.18",
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.27.6",
29
+ "deCONZ": "2.28.1",
30
30
  "homebridge": "^1.8.4||^2.0.0-beta",
31
31
  "node": "^20||^18"
32
32
  },
33
33
  "dependencies": {
34
34
  "hb-deconz-tools": "~2.0.3",
35
- "homebridge-lib": "~7.0.6"
35
+ "homebridge-lib": "~7.0.8"
36
36
  },
37
37
  "scripts": {
38
38
  "prepare": "standard && rm -rf out && jsdoc -c jsdoc.json",