homebridge-deconz 0.0.6 → 0.0.11

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.
Files changed (58) hide show
  1. package/README.md +8 -0
  2. package/cli/deconz.js +980 -0
  3. package/config.schema.json +1 -1
  4. package/lib/Client/ApiError.js +42 -0
  5. package/lib/{DeconzClient.js → Deconz/ApiClient.js} +82 -158
  6. package/lib/Deconz/ApiError.js +42 -0
  7. package/lib/Deconz/ApiResponse.js +54 -0
  8. package/lib/Deconz/Device.js +100 -0
  9. package/lib/{DeconzDiscovery.js → Deconz/Discovery.js} +4 -3
  10. package/lib/Deconz/Resource.js +1206 -0
  11. package/lib/{DeconzWsClient.js → Deconz/WsClient.js} +59 -44
  12. package/lib/Deconz/index.js +21 -0
  13. package/lib/DeconzAccessory/Contact.js +54 -0
  14. package/lib/DeconzAccessory/Gateway.js +316 -374
  15. package/lib/DeconzAccessory/Light.js +72 -0
  16. package/lib/DeconzAccessory/Motion.js +51 -0
  17. package/lib/DeconzAccessory/Sensor.js +35 -0
  18. package/lib/DeconzAccessory/Temperature.js +63 -0
  19. package/lib/DeconzAccessory/Thermostat.js +50 -0
  20. package/lib/DeconzAccessory/WarningDevice.js +56 -0
  21. package/lib/DeconzAccessory/WindowCovering.js +47 -0
  22. package/lib/DeconzAccessory/index.js +216 -0
  23. package/lib/DeconzPlatform.js +8 -3
  24. package/lib/DeconzService/AirPressure.js +43 -0
  25. package/lib/DeconzService/AirQuality.js +20 -10
  26. package/lib/DeconzService/Alarm.js +16 -9
  27. package/lib/DeconzService/Battery.js +43 -0
  28. package/lib/DeconzService/Button.js +12 -2
  29. package/lib/DeconzService/CarbonMonoxide.js +38 -0
  30. package/lib/DeconzService/Consumption.js +65 -0
  31. package/lib/DeconzService/Contact.js +60 -0
  32. package/lib/DeconzService/Daylight.js +132 -0
  33. package/lib/DeconzService/DeviceSettings.js +13 -5
  34. package/lib/DeconzService/Flag.js +52 -0
  35. package/lib/DeconzService/GatewaySettings.js +8 -58
  36. package/lib/DeconzService/Humidity.js +37 -0
  37. package/lib/DeconzService/Leak.js +38 -0
  38. package/lib/DeconzService/Light.js +376 -0
  39. package/lib/DeconzService/LightLevel.js +54 -0
  40. package/lib/DeconzService/LightsResource.js +112 -0
  41. package/lib/DeconzService/Motion.js +101 -0
  42. package/lib/DeconzService/Outlet.js +76 -0
  43. package/lib/DeconzService/Power.js +83 -0
  44. package/lib/DeconzService/SensorsResource.js +96 -0
  45. package/lib/DeconzService/Smoke.js +38 -0
  46. package/lib/DeconzService/Status.js +53 -0
  47. package/lib/DeconzService/Switch.js +93 -0
  48. package/lib/DeconzService/Temperature.js +63 -0
  49. package/lib/DeconzService/Thermostat.js +175 -0
  50. package/lib/DeconzService/WarningDevice.js +68 -0
  51. package/lib/DeconzService/WindowCovering.js +139 -0
  52. package/lib/DeconzService/index.js +94 -0
  53. package/package.json +7 -4
  54. package/lib/DeconzAccessory/Device.js +0 -91
  55. package/lib/DeconzAccessory.js +0 -16
  56. package/lib/DeconzDevice.js +0 -245
  57. package/lib/DeconzService/Sensor.js +0 -58
  58. package/lib/DeconzService.js +0 -43
@@ -69,7 +69,7 @@
69
69
  "maximum": 2000
70
70
  },
71
71
  "waitTimeUpdate": {
72
- "description": "The time, in milliseconds, to wait for a change from HomeKit to another characteristic for the same light or group, before updating the deCONZ gateway. Default: 20.",
72
+ "description": "The time, in milliseconds, to wait for a change from HomeKit to another characteristic for the same light or group, before updating the deCONZ gateway. Default: 100.",
73
73
  "type": "integer",
74
74
  "minimum": 0,
75
75
  "maximum": 500
@@ -0,0 +1,42 @@
1
+ // homebridge-deconz/lib/Deconz/ApiError.js
2
+ //
3
+ // Homebridge plug-in for deCONZ.
4
+ // Copyright © 2018-2022 Erik Baauw. All rights reserved.
5
+
6
+ 'use strict'
7
+
8
+ const homebridgeLib = require('homebridge-lib')
9
+
10
+ // API errors that could still cause (part of) the PUT command to be executed.
11
+ const nonCriticalApiErrorTypes = [
12
+ 6, // parameter not available
13
+ 7, // invalid value for parameter
14
+ 8, // paramater not modifiable
15
+ 201 // paramater not modifiable, device is set to off
16
+ ]
17
+
18
+ /** Deconz API error.
19
+ * @hideconstructor
20
+ * @extends HttpClient.HttpError
21
+ * @memberof Deconz
22
+ */
23
+ class ApiError extends homebridgeLib.HttpClient.HttpError {
24
+ constructor (message, response, type, description) {
25
+ super(message, response.request, response.statusCode, response.statusMessage)
26
+
27
+ /** @member {integer} - The API error type.
28
+ */
29
+ this.type = type
30
+
31
+ /** @member {string} - The API error description.
32
+ */
33
+ this.description = description
34
+
35
+ /** @member {boolean} - Indication that the request might still succeed
36
+ * for other attributes.
37
+ */
38
+ this.nonCritical = nonCriticalApiErrorTypes.includes(type)
39
+ }
40
+ }
41
+
42
+ module.exports = ApiError
@@ -1,4 +1,4 @@
1
- // homebridge-deconz/lib/DeconzClient.js
1
+ // homebridge-deconz/lib/Deconz/ApiClient.js
2
2
  //
3
3
  // Homebridge plug-in for deCONZ.
4
4
  // Copyright © 2018-2022 Erik Baauw. All rights reserved.
@@ -9,15 +9,9 @@ const events = require('events')
9
9
  const homebridgeLib = require('homebridge-lib')
10
10
  const os = require('os')
11
11
 
12
- // API errors that could still cause (part of) the PUT command to be executed.
13
- const nonCriticalApiErrorTypes = [
14
- 6, // parameter not available
15
- 7, // invalid value for parameter
16
- 8, // paramater not modifiable
17
- 201 // paramater not modifiable, device is set to off
18
- ]
12
+ const Deconz = require('../Deconz')
19
13
 
20
- // Estmate the number of Zigbee messages resulting from PUTting body.
14
+ // Estmate the number of Zigbee messages resulting from PUT body.
21
15
  function numberOfZigbeeMessages (body = {}) {
22
16
  let n = 0
23
17
  if (Object.keys(body).includes('on')) {
@@ -41,82 +35,37 @@ function numberOfZigbeeMessages (body = {}) {
41
35
  return n === 0 ? 1 : n
42
36
  }
43
37
 
44
- /** Deconz API error.
45
- * @hideconstructor
46
- * @extends HttpClient.HttpError
47
- * @memberof DeconzClient
48
- */
49
- class DeconzError extends homebridgeLib.HttpClient.HttpError {
50
- constructor (message, response, type, description) {
51
- super(message, response.request, response.statusCode, response.statusMessage)
52
-
53
- /** @member {integer} - The API error type.
54
- */
55
- this.type = type
56
-
57
- /** @member {string} - The API error description.
58
- */
59
- this.description = description
60
-
61
- /** @member {boolean} - Indication that the request might still succeed
62
- * for other attributes.
63
- */
64
- this.nonCritical = nonCriticalApiErrorTypes.includes(type)
65
- }
66
- }
67
-
68
- /** Deconz API response.
69
- * @hideconstructor
70
- * @extends HttpClient.HttpResponse
71
- * @memberof DeconzClient
72
- */
73
- class DeconzResponse extends homebridgeLib.HttpClient.HttpResponse {
74
- constructor (response) {
75
- super(
76
- response.request, response.statusCode, response.statusMessage,
77
- response.headers, response.body, response.parsedBody
78
- )
79
-
80
- /** @member {object} - An object with the `"success"` API responses.
81
- */
82
- this.success = {}
83
-
84
- /** @member {DeconzClient.DeconzError[]} - A list of `"error"` API responses.
85
- */
86
- this.errors = []
87
-
88
- if (Array.isArray(response.body)) {
89
- for (const id in response.body) {
90
- const e = response.body[id].error
91
- if (e != null && typeof e === 'object') {
92
- this.errors.push(new DeconzError(
93
- `api error ${e.type}: ${e.description}`,
94
- response, e.type, e.description
95
- ))
96
- }
97
- const s = response.body[id].success
98
- if (s != null && typeof s === 'object') {
99
- for (const path of Object.keys(s)) {
100
- const a = path.split('/')
101
- const key = a[a.length - 1]
102
- this.success[key] = s[path]
103
- }
104
- }
105
- }
106
- }
107
- }
108
- }
109
-
110
- /** REST API client for deCONZ gateway.
38
+ /** REST API client for a deCONZ gateway.
111
39
  *
112
40
  * See the [deCONZ API](https://dresden-elektronik.github.io/deconz-rest-doc/)
113
41
  * documentation for a better understanding of the API.
114
42
  * @extends HttpClient
43
+ * @memberof Deconz
115
44
  */
116
- class DeconzClient extends homebridgeLib.HttpClient {
117
- static get DeconzError () { return DeconzError }
118
- static get DeconzResponse () { return DeconzResponse }
45
+ class ApiClient extends homebridgeLib.HttpClient {
46
+ /** Events reported through `buttonevent`.
47
+ * @type {Object<string, integer>}
48
+ */
49
+ static get buttonEvent () {
50
+ return {
51
+ PRESS: 0,
52
+ HOLD: 1,
53
+ SHORT_RELEASE: 2,
54
+ LONG_RELEASE: 3,
55
+ DOUBLE_PRESS: 4,
56
+ TRIPLE_PRESS: 5,
57
+ QUADRUPLE_PRESS: 6,
58
+ SHAKE: 7,
59
+ DROP: 8,
60
+ TILT: 9
61
+ }
62
+ }
119
63
 
64
+ /** Convert date as reported by deCONZ to human readable string.
65
+ * @param {string} date - The ISO-8601 date string.
66
+ * @param {boolean} [utc=true] - Treat date as UTC, even with missing `Z`.
67
+ * @param {string} date - The human readable date string.
68
+ */
120
69
  static dateToString (date, utc = true) {
121
70
  if (date == null || date === 'none') {
122
71
  return 'n/a'
@@ -127,33 +76,30 @@ class DeconzClient extends homebridgeLib.HttpClient {
127
76
  return String(new Date(date)).slice(0, 24)
128
77
  }
129
78
 
130
- static parseUniqueid (uniqueid) {
131
- // TODO sanity check
132
- const a = uniqueid.replace(/:/g, '').toUpperCase().split('-')
133
- const o = {
134
- mac: a[0],
135
- endpoint: a[1]
136
- }
137
- if (a.length > 2) {
138
- o.cluster = a[2]
139
- }
140
- return o
79
+ /** Convert `lightlevel` to lux.
80
+ * @param {integer} lightLevel - The `lightlevel` as reported by deCONZ.
81
+ * @return {integer} lux - The value in lux.
82
+ */
83
+ static lightLevelToLux (v) {
84
+ let lux = v ? Math.pow(10, (v - 1) / 10000) : 0.0001
85
+ lux = Math.round(lux * 10000) / 10000
86
+ return Math.max(0.0001, Math.min(lux, 100000))
141
87
  }
142
88
 
143
- /** Create a new instance of a DeconzClient.
89
+ /** Create a new instance of a Deconz.Client.
144
90
  *
145
91
  * The caller is expected to verify that the given host is a reachable
146
92
  * deCONZ gateway, by calling
147
93
  * {@link DeconzDiscovery#config DeconzDiscovery#config()} and passing the
148
94
  * response as `params.config`.<br>
149
- * The caller is expected to persist the username (API key),
150
- * passing it as `params.username`.
151
- * If no API key is known {@link DeconzClient#createuser createuser()} can
95
+ * The caller is expected to persist the API key, passing it as
96
+ * `params.apiKey`.
97
+ * If no API key is known {@link Deconz.ApiClient#getApiKey getApiKey()} can
152
98
  * be called to create one.<br>
153
99
  *
154
100
  * @param {object} params - Parameters.
155
101
  * @param {?string} params.config - The bridge/gateway public configuration,
156
- * i.e. the response of {@link DeconzDiscovery#config DeconzDiscovery#config()}.
102
+ * i.e. the response of {@link Deconz.Discovery#config config()}.
157
103
  * @param {!string} params.host - Hostname/IP address and port of the
158
104
  * deCONZ gateway.
159
105
  * @param {boolean} [params.keepAlive=false] - Keep server connection(s)
@@ -163,7 +109,7 @@ class DeconzClient extends homebridgeLib.HttpClient {
163
109
  * @param {boolean} [params.phoscon=false] - Mimic Phoscon web app to use
164
110
  * deCONZ gateway API extensions.
165
111
  * @param {integer} [params.timeout=5] - Request timeout (in seconds).
166
- * @param {?string} params.username - The API key of the deCONZ gateway.
112
+ * @param {?string} params.apiKey - The API key of the deCONZ gateway.
167
113
  * @param {integer} [params.waitTimePut=50] - The time (in milliseconds),
168
114
  * after sending a PUT request, to wait before sending another PUT request.
169
115
  * @param {integer} [params.waitTimePutGroup=1000] - The time (in
@@ -190,7 +136,7 @@ class DeconzClient extends homebridgeLib.HttpClient {
190
136
  .intKey('maxSockets', 1, 20)
191
137
  .boolKey('phoscon')
192
138
  .intKey('timeout', 1, 60)
193
- .stringKey('username')
139
+ .stringKey('apiKey')
194
140
  .intKey('waitTimePut', 0, 50)
195
141
  .intKey('waitTimePutGroup', 0, 1000)
196
142
  .intKey('waitTimeResend', 0, 1000)
@@ -209,8 +155,8 @@ class DeconzClient extends homebridgeLib.HttpClient {
209
155
  // options.headers = { Accept: 'application/vnd.ddel.v1' }
210
156
  options.headers = { Accept: 'application/vnd.ddel.v1.1,vnd.ddel.v1.1' }
211
157
  }
212
- if (_options.username) {
213
- options.path += '/' + _options.username
158
+ if (_options.apiKey) {
159
+ options.path += '/' + _options.apiKey
214
160
  }
215
161
  super(options)
216
162
  this._options = _options
@@ -224,7 +170,7 @@ class DeconzClient extends homebridgeLib.HttpClient {
224
170
  */
225
171
  get bridgeid () { return this._options.config.bridgeid }
226
172
 
227
- /** Server (base) path, `/api/`_username_.
173
+ /** Server (base) path, `/api/`_API_key_.
228
174
  * @type {string}
229
175
  * @readonly
230
176
  */
@@ -233,9 +179,9 @@ class DeconzClient extends homebridgeLib.HttpClient {
233
179
  /** The API key.
234
180
  * @type {string}
235
181
  */
236
- get username () { return this._options.username }
237
- set username (value) {
238
- this._options.username = value
182
+ get apiKey () { return this._options.apiKey }
183
+ set apiKey (value) {
184
+ this._options.apiKey = value
239
185
  let path = '/api'
240
186
  if (value != null) {
241
187
  path += '/' + value
@@ -245,7 +191,7 @@ class DeconzClient extends homebridgeLib.HttpClient {
245
191
 
246
192
  // ===========================================================================
247
193
 
248
- /** Issue a GET request of `/api/`_username_`/`_resource_.
194
+ /** Issue a GET request of `/api/`_API_key_`/`_resource_.
249
195
  *
250
196
  * @param {string} resource - The resource.<br>
251
197
  * This might be a resource as exposed by the API, e.g. `/lights/1/state`,
@@ -309,9 +255,9 @@ class DeconzClient extends homebridgeLib.HttpClient {
309
255
  return body
310
256
  }
311
257
 
312
- /** Issue a PUT request to `/api/`_username_`/`_resource_.
258
+ /** Issue a PUT request to `/api/`_API_key_`/`_resource_.
313
259
  *
314
- * DeconzClient throttles the number of PUT requests to limit the Zigbee traffic
260
+ * ApiClient throttles the number of PUT requests to limit the Zigbee traffic
315
261
  * to 20 unicast messsages per seconds, or 1 broadcast message per second,
316
262
  * delaying the request when needed.
317
263
  * @param {string} resource - The resource.
@@ -342,7 +288,7 @@ class DeconzClient extends homebridgeLib.HttpClient {
342
288
  return this.request('PUT', resource, body)
343
289
  }
344
290
 
345
- /** Issue a POST request to `/api/`_username_`/`_resource_.
291
+ /** Issue a POST request to `/api/`_API_key_`/`_resource_.
346
292
  *
347
293
  * @param {string} resource - The resource.
348
294
  * @param {*} body - The body, which will be converted to JSON.
@@ -353,7 +299,7 @@ class DeconzClient extends homebridgeLib.HttpClient {
353
299
  return this.request('POST', resource, body)
354
300
  }
355
301
 
356
- /** Issue a DELETE request of `/api/`_username_`/`_resource_.
302
+ /** Issue a DELETE request of `/api/`_API_key_`/`_resource_.
357
303
  * @param {string} resource - The resource.
358
304
  * @param {*} body - The body, which will be converted to JSON.
359
305
  * @return {DeconzResponse} response - The response.
@@ -365,48 +311,48 @@ class DeconzClient extends homebridgeLib.HttpClient {
365
311
 
366
312
  // ===========================================================================
367
313
 
368
- /** Create an API key and set {@link DeconzClient#username username}.
314
+ /** Obtain an API key and set {@link Deconz.ApiClient#apiKey apiKey}.
369
315
  *
370
- * Calls {@link DeconzClient#post post()} to issue a POST request to `/api`.
316
+ * Calls {@link Deconz.ApiClient#post post()} to issue a POST request to `/api`.
371
317
  *
372
- * Before calling `createUser`, the deCONZ gateway must be unlocked, unless
318
+ * Before calling `getApiKey`, the deCONZ gateway must be unlocked, unless
373
319
  * the client is running on the same host as the gateway.
374
- * @return {string} username - The newly created API key.
320
+ * @return {string} apiKey - The newly created API key.
375
321
  * @throws {HttpError} In case of HTTP error.
376
322
  * @throws {DeconzError} In case of API error.
377
323
  */
378
- async createUser (application) {
324
+ async getApiKey (application) {
379
325
  if (typeof application !== 'string' || application === '') {
380
326
  throw new TypeError(`${application}: invalid application name`)
381
327
  }
382
- const username = this._options.username
328
+ const apiKey = this._options.apiKey
383
329
  const body = { devicetype: `${application}#${os.hostname().split('.')[0]}` }
384
- this.username = null
330
+ this.apiKey = null
385
331
  try {
386
332
  const response = await this.post('/', body)
387
- this.username = response.success.username
388
- return this.username
333
+ this.apiKey = response.success.username
334
+ return this.apiKey
389
335
  } catch (error) {
390
- this.username = username
336
+ this.apiKey = apiKey
391
337
  throw (error)
392
338
  }
393
339
  }
394
340
 
395
- /** Delete the API key and clear {@link DeconzClient#username username}.
341
+ /** Delete the API key and clear {@link Deconz.ApiClient#apiKey apiKey}.
396
342
  * @throws {HttpError} In case of HTTP error.
397
343
  * @throws {DeconzError} In case of API error.
398
344
  */
399
- async deleteUser () {
345
+ async deleteApiKey () {
400
346
  try {
401
- await this.delete('/config/whitelist/' + this.username)
347
+ await this.delete('/config/whitelist/' + this.apiKey)
402
348
  } catch (error) {}
403
- this.username = null
349
+ this.apiKey = null
404
350
  }
405
351
 
406
352
  /** Unlock the gateway to allow creating a new API key.
407
353
  *
408
- * Calls {@link DeconzClient#put put()} to issue a PUT request to
409
- * `/api/`_username_`/config`.
354
+ * Calls {@link Deconz.ApiClient#put put()} to issue a PUT request to
355
+ * `/api/`_API_key_`/config`.
410
356
  *
411
357
  * @return {DeconzResponse} response - The response.
412
358
  * @throws {HttpError} In case of HTTP error.
@@ -416,35 +362,13 @@ class DeconzClient extends homebridgeLib.HttpClient {
416
362
  return this.put('/config', { unlock: 60 })
417
363
  }
418
364
 
419
- /** Initiate a touchlink scan.
420
- *
421
- * Calls {@link DeconzClient#post post()} to issue a POST request to
422
- * `/api/`_username_`/touchlink/scan`, to initiate a touchlink scan.
423
- * As the ConBee II and RaspBee II firmware lack support for touchlink,
424
- * this will only work for the original ConBee and RaspBee.
425
- * To see the results of the scan, issue a GET request of
426
- * `/api/`_username_`/touchlink/scan`.
427
- * The ID returned in the scan results is needed to touchlink identify or
428
- * reset the device.
429
- * To issue a touchlink identify, issue a POST request of
430
- * `/api/`_username_`/touchlink/`_ID_`/identify`.
431
- * To issue a touchlink reset, issue a POST request to
432
- * `/api/`_username_`/touchlink/`_ID_`/reset`.
433
- * @return {DeconzResponse} response - The response.
434
- * @throws {HttpError} In case of HTTP error.
435
- * @throws {DeconzError} In case of API error.
436
- */
437
- async touchlink () {
438
- return this.post('/touchlink/scan')
439
- }
440
-
441
365
  /** Search for new devices.
442
366
  *
443
- * Calls {@link DeconzClient#put put()} to issue a PUT request to
444
- * `/api/`_username_`/config`, to enable pairing of new Zigbee devices.
367
+ * Calls {@link Deconz.ApiClient#put put()} to issue a PUT request to
368
+ * `/api/`_API_key_`/config`, to enable pairing of new Zigbee devices.
445
369
  *
446
370
  * To see the newly paired devices, issue a GET request of
447
- * `/api/`_username_`/lights/new` and/or `/api/`_username_`/sensor/new`
371
+ * `/api/`_API_key_`/lights/new` and/or `/api/`_API_key_`/sensor/new`
448
372
  * @return {DeconzResponse} response - The response.
449
373
  * @throws {HttpError} In case of HTTP error.
450
374
  * @throws {DeconzError} In case of API error.
@@ -455,8 +379,8 @@ class DeconzClient extends homebridgeLib.HttpClient {
455
379
 
456
380
  /** Restart the gateway.
457
381
  *
458
- * Calls {@link DeconzClient#post post()} to issue a POST request to
459
- * `/api/`_username_`/config/restartapp`, to restart the deCONZ gateway.
382
+ * Calls {@link Deconz.ApiClient#post post()} to issue a POST request to
383
+ * `/api/`_API_key_`/config/restartapp`, to restart the deCONZ gateway.
460
384
  *
461
385
  * @return {DeconzResponse} response - The response.
462
386
  * @throws {HttpError} In case of HTTP error.
@@ -470,9 +394,9 @@ class DeconzClient extends homebridgeLib.HttpClient {
470
394
 
471
395
  /** Issue an HTTP request to the deCONZ gateway.
472
396
  *
473
- * This method does the heavy lifting for {@link DeconzClient#get get()},
474
- * {@link DeconzClient#put put()}, {@link DeconzClient#post post()}, and
475
- * {@link DeconzClient#delete delete()}.
397
+ * This method does the heavy lifting for {@link Deconz.AplClient#get get()},
398
+ * {@link Deconz.ApiClient#put put()}, {@link Deconz.ApiClient#post post()},
399
+ * and {@link Deconz.ApiClient#delete delete()}.
476
400
  * It shouldn't be called directly.
477
401
  *
478
402
  * @param {string} method - The method for the request.
@@ -485,12 +409,12 @@ class DeconzClient extends homebridgeLib.HttpClient {
485
409
  async request (method, resource, body = null, retry = 0) {
486
410
  try {
487
411
  const httpResponse = await super.request(method, resource, body)
488
- const response = new DeconzResponse(httpResponse)
412
+ const response = new Deconz.ApiResponse(httpResponse)
489
413
  for (const error of response.errors) {
490
414
  /** Emitted for each API error returned by the or deCONZ gateway.
491
415
  *
492
- * @event DeconzClient#error
493
- * @param {HttpClient.HttpError|HttpClient.DeconzError} error - The error.
416
+ * @event Deconz.ApiClient#error
417
+ * @param {HttpClient.HttpError|Deconz.ApiError} error - The error.
494
418
  */
495
419
  this.emit('error', error)
496
420
  if (!error.nonCritical) {
@@ -516,4 +440,4 @@ class DeconzClient extends homebridgeLib.HttpClient {
516
440
  }
517
441
  }
518
442
 
519
- module.exports = DeconzClient
443
+ module.exports = ApiClient
@@ -0,0 +1,42 @@
1
+ // homebridge-deconz/lib/Deconz/ApiError.js
2
+ //
3
+ // Homebridge plug-in for deCONZ.
4
+ // Copyright © 2018-2022 Erik Baauw. All rights reserved.
5
+
6
+ 'use strict'
7
+
8
+ const homebridgeLib = require('homebridge-lib')
9
+
10
+ // API errors that could still cause (part of) the PUT command to be executed.
11
+ const nonCriticalApiErrorTypes = [
12
+ 6, // parameter not available
13
+ 7, // invalid value for parameter
14
+ 8, // paramater not modifiable
15
+ 201 // paramater not modifiable, device is set to off
16
+ ]
17
+
18
+ /** Deconz API error.
19
+ * @hideconstructor
20
+ * @extends HttpClient.HttpError
21
+ * @memberof Deconz
22
+ */
23
+ class ApiError extends homebridgeLib.HttpClient.HttpError {
24
+ constructor (message, response, type, description) {
25
+ super(message, response.request, response.statusCode, response.statusMessage)
26
+
27
+ /** @member {integer} - The API error type.
28
+ */
29
+ this.type = type
30
+
31
+ /** @member {string} - The API error description.
32
+ */
33
+ this.description = description
34
+
35
+ /** @member {boolean} - Indication that the request might still succeed
36
+ * for other attributes.
37
+ */
38
+ this.nonCritical = nonCriticalApiErrorTypes.includes(type)
39
+ }
40
+ }
41
+
42
+ module.exports = ApiError
@@ -0,0 +1,54 @@
1
+ // homebridge-deconz/lib/Deconz/ApiResponse.js
2
+ //
3
+ // Homebridge plug-in for deCONZ.
4
+ // Copyright © 2018-2022 Erik Baauw. All rights reserved.
5
+
6
+ 'use strict'
7
+
8
+ const homebridgeLib = require('homebridge-lib')
9
+
10
+ const Deconz = require('../Deconz')
11
+
12
+ /** Deconz API response.
13
+ * @hideconstructor
14
+ * @extends HttpClient.HttpResponse
15
+ * @memberof Deconz
16
+ */
17
+ class ApiResponse extends homebridgeLib.HttpClient.HttpResponse {
18
+ constructor (response) {
19
+ super(
20
+ response.request, response.statusCode, response.statusMessage,
21
+ response.headers, response.body, response.parsedBody
22
+ )
23
+
24
+ /** @member {object} - An object with the `"success"` API responses.
25
+ */
26
+ this.success = {}
27
+
28
+ /** @member {Deconz.ApiError[]} - A list of `"error"` API responses.
29
+ */
30
+ this.errors = []
31
+
32
+ if (Array.isArray(response.body)) {
33
+ for (const id in response.body) {
34
+ const e = response.body[id].error
35
+ if (e != null && typeof e === 'object') {
36
+ this.errors.push(new Deconz.ApiError(
37
+ `api error ${e.type}: ${e.description}`,
38
+ response, e.type, e.description
39
+ ))
40
+ }
41
+ const s = response.body[id].success
42
+ if (s != null && typeof s === 'object') {
43
+ for (const path of Object.keys(s)) {
44
+ const a = path.split('/')
45
+ const key = a[a.length - 1]
46
+ this.success[key] = s[path]
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ module.exports = ApiResponse
@@ -0,0 +1,100 @@
1
+ // homebridge-deconz/lib/Deconz/Device.js
2
+ // Copyright © 2022 Erik Baauw. All rights reserved.
3
+ //
4
+ // Homebridge plugin for deCONZ.
5
+
6
+ 'use strict'
7
+
8
+ /** Delegate class for a Zigbee or virtual device on a deCONZ gateway.
9
+ *
10
+ * The deCONZ REST API exposes a Zigbee device using one or more resources.
11
+ * These resources are linked to the device through the `uniqueid` in the
12
+ * resource body.
13
+ * Each supported device corresponds to a HomeKit accessory.
14
+ * Each supported resource corresponds to a HomeKit service.
15
+ * @memberof Deconz
16
+ */
17
+ class Device {
18
+ /** Create a new instance of a delegate of a device, from a resource.
19
+ *
20
+ * @param {Deconz.Resource} resource - The resource.
21
+ */
22
+ constructor (resource) {
23
+ /** The device ID.
24
+ *
25
+ * This is the {@link Deconz.Resource#id id} of the delegates
26
+ * of all resources for the device.
27
+ * @type {string}
28
+ */
29
+ this.id = resource.id
30
+
31
+ /** Zigbee device vs virtual device.
32
+ *
33
+ * This is the {@link Deconz.Resource#zigbee zigbee} of the
34
+ * delegates of all resources for the device.
35
+ * @type {boolean}
36
+ */
37
+ this.zigbee = resource.zigbee
38
+
39
+ /** The delegates of the resources for the device, by subtype of the
40
+ * corresponding HomeKit service.
41
+ * @type {Object.<string, Deconz.Resource>}
42
+ */
43
+ this.resourceBySubtype = {}
44
+ this.resourceBySubtype[resource.subtype] = resource
45
+
46
+ /** The key of the delegate for the primary resource for the device in
47
+ * {@link DeconzDevice#resourceBySubtype resourceBySubtype}
48
+ *
49
+ * This is the {@link DeconzDevice.Resource#subtype subtype} of the
50
+ * HomeKit service corresponding to the primary resource.
51
+ * @type {string}
52
+ */
53
+ this.primary = resource.subtype
54
+ }
55
+
56
+ /** The delegate of the primary resource of the device.
57
+ * @type {Deconz.Resource}
58
+ */
59
+ get resource () { return this.resourceBySubtype[this.primary] }
60
+
61
+ /** List of resource paths of the resources for the device.
62
+ * @type {string[]}
63
+ */
64
+ get rpaths () {
65
+ return Object.keys(this.resourceBySubtype || {}).map((subtype) => {
66
+ return this.resourceBySubtype[subtype].rpath
67
+ })
68
+ }
69
+
70
+ /** Add a {@link Deconz.Resource Resource}.
71
+ *
72
+ * Updates {@link Deconz.Device#resourceBySubtype resourceBySubtype},
73
+ * {@link Deconz.Device#rpaths rpaths}, and, when the added resource
74
+ * has a higher priority, {@link Deconz.Device#primary primary} and
75
+ * {@link Deconz.Device#resource resource}.
76
+ * @param {Deconz.Resource} resource - The resource.
77
+ */
78
+ addResource (resource) {
79
+ const { body, id, prio, rtype, subtype, zigbee } = resource
80
+ if (this.resourceBySubtype[subtype] != null) {
81
+ const r = this.resourceBySubtype[subtype]
82
+ throw new Error(
83
+ `${resource.rpath}: duplicate uniqueid ${body.uniqueid} in ${r.rpath}`
84
+ )
85
+ }
86
+ if (zigbee !== this.zigbee || (zigbee && id !== this.id)) {
87
+ const r = this.resourceBySubtype[subtype]
88
+ throw new SyntaxError(
89
+ `${resource.rpath}: cannot combine ${r.rpath}`
90
+ )
91
+ }
92
+ this.resourceBySubtype[subtype] = resource
93
+ const p = this.resourceBySubtype[this.primary]
94
+ if (p.rtype === rtype && p.prio < prio) {
95
+ this.primary = resource.subtype
96
+ }
97
+ }
98
+ }
99
+
100
+ module.exports = Device