homebridge-deconz 0.1.17 → 0.1.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.
- package/cli/deconz.js +4 -975
- package/homebridge-ui/server.js +2 -2
- package/lib/Deconz/Resource.js +2 -2
- package/lib/Deconz/index.js +0 -5
- package/lib/DeconzAccessory/Gateway.js +4 -4
- package/lib/DeconzAccessory/index.js +3 -3
- package/lib/DeconzPlatform.js +2 -2
- package/lib/DeconzService/AirQuality.js +2 -2
- package/lib/DeconzService/Battery.js +2 -2
- package/lib/DeconzService/Consumption.js +2 -2
- package/lib/DeconzService/Daylight.js +2 -2
- package/lib/DeconzService/LightLevel.js +2 -2
- package/lib/DeconzService/LightsResource.js +2 -2
- package/lib/DeconzService/Power.js +2 -2
- package/lib/DeconzService/Schedule.js +2 -2
- package/lib/DeconzService/SensorsResource.js +2 -2
- package/lib/DeconzService/index.js +2 -2
- package/package.json +2 -1
- package/lib/Deconz/ApiClient.js +0 -442
- package/lib/Deconz/ApiError.js +0 -49
- package/lib/Deconz/ApiResponse.js +0 -57
- package/lib/Deconz/Discovery.js +0 -238
- package/lib/Deconz/WsClient.js +0 -205
package/lib/Deconz/ApiClient.js
DELETED
@@ -1,442 +0,0 @@
|
|
1
|
-
// homebridge-deconz/lib/Deconz/ApiClient.js
|
2
|
-
//
|
3
|
-
// Homebridge plug-in for deCONZ.
|
4
|
-
// Copyright © 2018-2023 Erik Baauw. All rights reserved.
|
5
|
-
|
6
|
-
'use strict'
|
7
|
-
|
8
|
-
const events = require('events')
|
9
|
-
const { HttpClient, OptionParser, timeout } = require('homebridge-lib')
|
10
|
-
const os = require('os')
|
11
|
-
|
12
|
-
const Deconz = require('../Deconz')
|
13
|
-
|
14
|
-
// Estmate the number of Zigbee messages resulting from PUT body.
|
15
|
-
function numberOfZigbeeMessages (body = {}) {
|
16
|
-
let n = 0
|
17
|
-
if (Object.keys(body).includes('on')) {
|
18
|
-
n++
|
19
|
-
}
|
20
|
-
if (
|
21
|
-
Object.keys(body).includes('bri') ||
|
22
|
-
Object.keys(body).includes('bri_inc')
|
23
|
-
) {
|
24
|
-
n++
|
25
|
-
}
|
26
|
-
if (
|
27
|
-
Object.keys(body).includes('xy') ||
|
28
|
-
Object.keys(body).includes('ct') ||
|
29
|
-
Object.keys(body).includes('hue') ||
|
30
|
-
Object.keys(body).includes('sat') ||
|
31
|
-
Object.keys(body).includes('effect')
|
32
|
-
) {
|
33
|
-
n++
|
34
|
-
}
|
35
|
-
return n === 0 ? 1 : n
|
36
|
-
}
|
37
|
-
|
38
|
-
/** REST API client for a deCONZ gateway.
|
39
|
-
*
|
40
|
-
* See the [deCONZ API](https://dresden-elektronik.github.io/deconz-rest-doc/)
|
41
|
-
* documentation for a better understanding of the API.
|
42
|
-
* @extends HttpClient
|
43
|
-
* @memberof Deconz
|
44
|
-
*/
|
45
|
-
class ApiClient extends 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
|
-
}
|
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
|
-
*/
|
69
|
-
static dateToString (date, utc = true) {
|
70
|
-
if (date == null || date === 'none') {
|
71
|
-
return 'n/a'
|
72
|
-
}
|
73
|
-
if (utc && !date.endsWith('Z')) {
|
74
|
-
date += 'Z'
|
75
|
-
}
|
76
|
-
return String(new Date(date)).slice(0, 24)
|
77
|
-
}
|
78
|
-
|
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
|
-
v = Math.max(0, Math.min(v, 60001))
|
85
|
-
return v ? Math.round(Math.pow(10, (v - 1) / 10000) * 10) / 10 : 0.0001
|
86
|
-
}
|
87
|
-
|
88
|
-
/** Create a new instance of a Deconz.ApiClient.
|
89
|
-
*
|
90
|
-
* The caller is expected to verify that the given host is a reachable
|
91
|
-
* deCONZ gateway, by calling
|
92
|
-
* {@link Deconz.Discovery#config Deconz.Discovery#config()} and passing the
|
93
|
-
* response as `params.config`.<br>
|
94
|
-
* The caller is expected to persist the API key, passing it as
|
95
|
-
* `params.apiKey`.
|
96
|
-
* If no API key is known {@link Deconz.ApiClient#getApiKey getApiKey()} can
|
97
|
-
* be called to create one.<br>
|
98
|
-
*
|
99
|
-
* @param {object} params - Parameters.
|
100
|
-
* @param {?string} params.config - The bridge/gateway public configuration,
|
101
|
-
* i.e. the response of {@link Deconz.Discovery#config config()}.
|
102
|
-
* @param {!string} params.host - Hostname/IP address and port of the
|
103
|
-
* deCONZ gateway.
|
104
|
-
* @param {boolean} [params.keepAlive=false] - Keep server connection(s)
|
105
|
-
* open.
|
106
|
-
* @param {integer} [params.maxSockets=20] - Throttle requests to maximum
|
107
|
-
* number of parallel connections.
|
108
|
-
* @param {boolean} [params.phoscon=false] - Mimic Phoscon web app to use
|
109
|
-
* deCONZ gateway API extensions.
|
110
|
-
* @param {integer} [params.timeout=5] - Request timeout (in seconds).
|
111
|
-
* @param {?string} params.apiKey - The API key of the deCONZ gateway.
|
112
|
-
* @param {integer} [params.waitTimePut=50] - The time (in milliseconds),
|
113
|
-
* after sending a PUT request, to wait before sending another PUT request.
|
114
|
-
* @param {integer} [params.waitTimePutGroup=1000] - The time (in
|
115
|
-
* milliseconds), after sending a PUT request, to wait before sending
|
116
|
-
* another PUT request.
|
117
|
-
* @param {integer} [params.waitTimeResend=300] - The time, in milliseconds,
|
118
|
-
* to wait before resending a request after an ECONNRESET, an http status
|
119
|
-
* 503, or an api 901 error.
|
120
|
-
*/
|
121
|
-
constructor (params = {}) {
|
122
|
-
const _options = {
|
123
|
-
keepAlive: false,
|
124
|
-
maxSockets: 20,
|
125
|
-
timeout: 5,
|
126
|
-
waitTimePut: 50,
|
127
|
-
waitTimePutGroup: 1000,
|
128
|
-
waitTimeResend: 300
|
129
|
-
}
|
130
|
-
const optionParser = new OptionParser(_options)
|
131
|
-
optionParser
|
132
|
-
.objectKey('config', true)
|
133
|
-
.stringKey('host', true)
|
134
|
-
.boolKey('keepAlive')
|
135
|
-
.intKey('maxSockets', 1, 20)
|
136
|
-
.boolKey('phoscon')
|
137
|
-
.intKey('timeout', 1, 60)
|
138
|
-
.stringKey('apiKey')
|
139
|
-
.intKey('waitTimePut', 0, 50)
|
140
|
-
.intKey('waitTimePutGroup', 0, 1000)
|
141
|
-
.intKey('waitTimeResend', 0, 1000)
|
142
|
-
.parse(params)
|
143
|
-
|
144
|
-
const options = {
|
145
|
-
host: _options.host,
|
146
|
-
json: true,
|
147
|
-
keepAlive: _options.keepAlive,
|
148
|
-
maxSockets: _options.maxSockets,
|
149
|
-
path: '/api',
|
150
|
-
timeout: _options.timeout,
|
151
|
-
validStatusCodes: [200, 400, 403] //, 404]
|
152
|
-
}
|
153
|
-
if (_options.phoscon) {
|
154
|
-
// options.headers = { Accept: 'application/vnd.ddel.v1' }
|
155
|
-
options.headers = { Accept: 'application/vnd.ddel.v3,application/vnd.ddel.v2,application/vnd.ddel.v1.1' }
|
156
|
-
}
|
157
|
-
if (_options.apiKey) {
|
158
|
-
options.path += '/' + _options.apiKey
|
159
|
-
}
|
160
|
-
super(options)
|
161
|
-
this._options = _options
|
162
|
-
this.waitForIt = false
|
163
|
-
this.setMaxListeners(30)
|
164
|
-
}
|
165
|
-
|
166
|
-
/** The ID (Zigbee mac address) of the deCONZ gateway.
|
167
|
-
* @type {string}
|
168
|
-
* @readonly
|
169
|
-
*/
|
170
|
-
get bridgeid () { return this._options.config.bridgeid }
|
171
|
-
|
172
|
-
/** Server (base) path, `/api/`_API_key_.
|
173
|
-
* @type {string}
|
174
|
-
* @readonly
|
175
|
-
*/
|
176
|
-
get path () { return super.path }
|
177
|
-
|
178
|
-
/** The API key.
|
179
|
-
* @type {string}
|
180
|
-
*/
|
181
|
-
get apiKey () { return this._options.apiKey }
|
182
|
-
set apiKey (value) {
|
183
|
-
this._options.apiKey = value
|
184
|
-
let path = '/api'
|
185
|
-
if (value != null) {
|
186
|
-
path += '/' + value
|
187
|
-
}
|
188
|
-
super.path = path
|
189
|
-
}
|
190
|
-
|
191
|
-
// ===========================================================================
|
192
|
-
|
193
|
-
/** Issue a GET request of `/api/`_API_key_`/`_resource_.
|
194
|
-
*
|
195
|
-
* @param {string} resource - The resource.<br>
|
196
|
-
* This might be a resource as exposed by the API, e.g. `/lights/1/state`,
|
197
|
-
* or an attribute returned by the API, e.g. `/lights/1/state/on`.
|
198
|
-
* @return {*} response - The JSON response body converted to JavaScript.
|
199
|
-
* @throws {Deconz.ApiError} In case of error.
|
200
|
-
*/
|
201
|
-
async get (resource) {
|
202
|
-
if (typeof resource !== 'string' || resource[0] !== '/') {
|
203
|
-
throw new TypeError(`${resource}: invalid resource`)
|
204
|
-
}
|
205
|
-
let path = resource.slice(1).split('/')
|
206
|
-
switch (path[0]) {
|
207
|
-
case 'lights':
|
208
|
-
if (path.length === 3 && path[2] === 'connectivity2') {
|
209
|
-
path = []
|
210
|
-
break
|
211
|
-
}
|
212
|
-
// falls through
|
213
|
-
case 'groups':
|
214
|
-
if (path.length >= 3 && path[2] === 'scenes') {
|
215
|
-
resource = '/' + path.shift() + '/' + path.shift() + '/' + path.shift()
|
216
|
-
if (path.length >= 1) {
|
217
|
-
resource += '/' + path.shift()
|
218
|
-
}
|
219
|
-
break
|
220
|
-
}
|
221
|
-
// falls through
|
222
|
-
case 'schedules':
|
223
|
-
case 'sensors':
|
224
|
-
case 'rules':
|
225
|
-
case 'resourcelinks':
|
226
|
-
if (path.length > 2) {
|
227
|
-
resource = '/' + path.shift() + '/' + path.shift()
|
228
|
-
break
|
229
|
-
}
|
230
|
-
path = []
|
231
|
-
break
|
232
|
-
case 'config':
|
233
|
-
case 'capabilities':
|
234
|
-
if (path.length > 1) {
|
235
|
-
resource = '/' + path.shift()
|
236
|
-
break
|
237
|
-
}
|
238
|
-
// falls through
|
239
|
-
default:
|
240
|
-
path = []
|
241
|
-
break
|
242
|
-
}
|
243
|
-
let { body } = await this.request('GET', resource)
|
244
|
-
for (const key of path) {
|
245
|
-
if (typeof body === 'object' && body != null) {
|
246
|
-
body = body[key]
|
247
|
-
}
|
248
|
-
}
|
249
|
-
if (body == null && path.length > 0) {
|
250
|
-
throw new Error(
|
251
|
-
`/${path.join('/')}: not found in resource ${resource}`
|
252
|
-
)
|
253
|
-
}
|
254
|
-
return body
|
255
|
-
}
|
256
|
-
|
257
|
-
/** Issue a PUT request to `/api/`_API_key_`/`_resource_.
|
258
|
-
*
|
259
|
-
* ApiClient throttles the number of PUT requests to limit the Zigbee traffic
|
260
|
-
* to 20 unicast messsages per seconds, or 1 broadcast message per second,
|
261
|
-
* delaying the request when needed.
|
262
|
-
* @param {string} resource - The resource.
|
263
|
-
* @param {*} body - The body, which will be converted to JSON.
|
264
|
-
* @return {Deconz.ApiResponse} response - The response.
|
265
|
-
* @throws {Deconz.ApiError} In case of error, except for non-critical API errors.
|
266
|
-
*/
|
267
|
-
async put (resource, body) {
|
268
|
-
if (this.waitForIt) {
|
269
|
-
while (this.waitForIt) {
|
270
|
-
try {
|
271
|
-
await events.once(this, '_go')
|
272
|
-
} catch (error) {}
|
273
|
-
}
|
274
|
-
}
|
275
|
-
const timeout = numberOfZigbeeMessages(body) * (
|
276
|
-
resource.startsWith('/groups')
|
277
|
-
? this._options.waitTimePutGroup
|
278
|
-
: this._options.waitTimePut
|
279
|
-
)
|
280
|
-
if (timeout > 0) {
|
281
|
-
this.waitForIt = true
|
282
|
-
setTimeout(() => {
|
283
|
-
this.waitForIt = false
|
284
|
-
this.emit('_go')
|
285
|
-
}, timeout)
|
286
|
-
}
|
287
|
-
return this.request('PUT', resource, body)
|
288
|
-
}
|
289
|
-
|
290
|
-
/** Issue a POST request to `/api/`_API_key_`/`_resource_.
|
291
|
-
*
|
292
|
-
* @param {string} resource - The resource.
|
293
|
-
* @param {*} body - The body, which will be converted to JSON.
|
294
|
-
* @return {Deconz.ApiResponse} response - The response.
|
295
|
-
* @throws {Deconz.ApiError} In case of error.
|
296
|
-
*/
|
297
|
-
async post (resource, body) {
|
298
|
-
return this.request('POST', resource, body)
|
299
|
-
}
|
300
|
-
|
301
|
-
/** Issue a DELETE request of `/api/`_API_key_`/`_resource_.
|
302
|
-
* @param {string} resource - The resource.
|
303
|
-
* @param {*} body - The body, which will be converted to JSON.
|
304
|
-
* @return {Deconz.ApiResponse} response - The response.
|
305
|
-
* @throws {Deconz.ApiError} In case of error.
|
306
|
-
*/
|
307
|
-
async delete (resource, body) {
|
308
|
-
return this.request('DELETE', resource, body)
|
309
|
-
}
|
310
|
-
|
311
|
-
// ===========================================================================
|
312
|
-
|
313
|
-
/** Obtain an API key and set {@link Deconz.ApiClient#apiKey apiKey}.
|
314
|
-
*
|
315
|
-
* Calls {@link Deconz.ApiClient#post post()} to issue a POST request to `/api`.
|
316
|
-
*
|
317
|
-
* Before calling `getApiKey`, the deCONZ gateway must be unlocked, unless
|
318
|
-
* the client is running on the same host as the gateway.
|
319
|
-
* @return {string} apiKey - The newly created API key.
|
320
|
-
* @throws {HttpError} In case of HTTP error.
|
321
|
-
* @throws {Deconz.ApiError} In case of API error.
|
322
|
-
*/
|
323
|
-
async getApiKey (application) {
|
324
|
-
if (typeof application !== 'string' || application === '') {
|
325
|
-
throw new TypeError(`${application}: invalid application name`)
|
326
|
-
}
|
327
|
-
const apiKey = this._options.apiKey
|
328
|
-
const body = { devicetype: `${application}#${os.hostname().split('.')[0]}` }
|
329
|
-
this.apiKey = null
|
330
|
-
try {
|
331
|
-
const response = await this.post('/', body)
|
332
|
-
this.apiKey = response.success.username
|
333
|
-
return this.apiKey
|
334
|
-
} catch (error) {
|
335
|
-
this.apiKey = apiKey
|
336
|
-
throw (error)
|
337
|
-
}
|
338
|
-
}
|
339
|
-
|
340
|
-
/** Delete the API key and clear {@link Deconz.ApiClient#apiKey apiKey}.
|
341
|
-
* @throws {HttpError} In case of HTTP error.
|
342
|
-
* @throws {Deconz.ApiError} In case of API error.
|
343
|
-
*/
|
344
|
-
async deleteApiKey () {
|
345
|
-
try {
|
346
|
-
await this.delete('/config/whitelist/' + this.apiKey)
|
347
|
-
} catch (error) {}
|
348
|
-
this.apiKey = null
|
349
|
-
}
|
350
|
-
|
351
|
-
/** Unlock the gateway to allow creating a new API key.
|
352
|
-
*
|
353
|
-
* Calls {@link Deconz.ApiClient#put put()} to issue a PUT request to
|
354
|
-
* `/api/`_API_key_`/config`.
|
355
|
-
*
|
356
|
-
* @return {Deconz.ApiResponse} response - The response.
|
357
|
-
* @throws {HttpError} In case of HTTP error.
|
358
|
-
* @throws {Deconz.ApiError} In case of API error.
|
359
|
-
*/
|
360
|
-
async unlock () {
|
361
|
-
return this.put('/config', { unlock: 60 })
|
362
|
-
}
|
363
|
-
|
364
|
-
/** Search for new devices.
|
365
|
-
*
|
366
|
-
* Calls {@link Deconz.ApiClient#put put()} to issue a PUT request to
|
367
|
-
* `/api/`_API_key_`/config`, to enable pairing of new Zigbee devices.
|
368
|
-
*
|
369
|
-
* To see the newly paired devices, issue a GET request of
|
370
|
-
* `/api/`_API_key_`/lights/new` and/or `/api/`_API_key_`/sensor/new`
|
371
|
-
* @return {Deconz.ApiResponse} response - The response.
|
372
|
-
* @throws {HttpError} In case of HTTP error.
|
373
|
-
* @throws {Deconz.ApiError} In case of API error.
|
374
|
-
*/
|
375
|
-
async search () {
|
376
|
-
return this.put('/config', { permitjoin: 120 })
|
377
|
-
}
|
378
|
-
|
379
|
-
/** Restart the gateway.
|
380
|
-
*
|
381
|
-
* Calls {@link Deconz.ApiClient#post post()} to issue a POST request to
|
382
|
-
* `/api/`_API_key_`/config/restartapp`, to restart the deCONZ gateway.
|
383
|
-
*
|
384
|
-
* @return {Deconz.ApiResponse} response - The response.
|
385
|
-
* @throws {HttpError} In case of HTTP error.
|
386
|
-
* @throws {Deconz.ApiError} In case of API error.
|
387
|
-
*/
|
388
|
-
async restart () {
|
389
|
-
return this.post('/config/restartapp')
|
390
|
-
}
|
391
|
-
|
392
|
-
// ===========================================================================
|
393
|
-
|
394
|
-
/** Issue an HTTP request to the deCONZ gateway.
|
395
|
-
*
|
396
|
-
* This method does the heavy lifting for {@link Deconz.AplClient#get get()},
|
397
|
-
* {@link Deconz.ApiClient#put put()}, {@link Deconz.ApiClient#post post()},
|
398
|
-
* and {@link Deconz.ApiClient#delete delete()}.
|
399
|
-
* It shouldn't be called directly.
|
400
|
-
*
|
401
|
-
* @param {string} method - The method for the request.
|
402
|
-
* @param {!string} resource - The resource for the request.
|
403
|
-
* @param {?*} body - The body for the request.
|
404
|
-
* @return {Deconz.ApiResponse} response - The response.
|
405
|
-
* @throws {HttpError} In case of HTTP error.
|
406
|
-
* @throws {Deconz.ApiError} In case of API error.
|
407
|
-
*/
|
408
|
-
async request (method, resource, body = null, retry = 0) {
|
409
|
-
try {
|
410
|
-
const httpResponse = await super.request(method, resource, body)
|
411
|
-
const response = new Deconz.ApiResponse(httpResponse)
|
412
|
-
for (const error of response.errors) {
|
413
|
-
/** Emitted for each API error returned by the or deCONZ gateway.
|
414
|
-
*
|
415
|
-
* @event Deconz.ApiClient#error
|
416
|
-
* @param {HttpClient.HttpError|Deconz.ApiError} error - The error.
|
417
|
-
*/
|
418
|
-
this.emit('error', error)
|
419
|
-
if (!error.nonCritical) {
|
420
|
-
throw error
|
421
|
-
}
|
422
|
-
}
|
423
|
-
return response
|
424
|
-
} catch (error) {
|
425
|
-
if (
|
426
|
-
error.code === 'ECONNRESET' ||
|
427
|
-
error.statusCode === 503 ||
|
428
|
-
error.type === 901
|
429
|
-
) {
|
430
|
-
if (error.request != null && this._options.waitTimeResend > 0 && retry < 5) {
|
431
|
-
error.message += ' - retry in ' + this._options.waitTimeResend + 'ms'
|
432
|
-
this.emit('error', error)
|
433
|
-
await timeout(this._options.waitTimeResend)
|
434
|
-
return this.request(method, resource, body, retry + 1)
|
435
|
-
}
|
436
|
-
}
|
437
|
-
throw error
|
438
|
-
}
|
439
|
-
}
|
440
|
-
}
|
441
|
-
|
442
|
-
module.exports = ApiClient
|
package/lib/Deconz/ApiError.js
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
// homebridge-deconz/lib/Deconz/ApiError.js
|
2
|
-
//
|
3
|
-
// Homebridge plug-in for deCONZ.
|
4
|
-
// Copyright © 2018-2023 Erik Baauw. All rights reserved.
|
5
|
-
|
6
|
-
'use strict'
|
7
|
-
|
8
|
-
const { HttpClient } = 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 HttpClient.HttpError {
|
24
|
-
constructor (e, response) {
|
25
|
-
super(
|
26
|
-
`${e.address}: api error ${e.type}: ${e.description}`,
|
27
|
-
response.request, response.statusCode, response.statusMessage
|
28
|
-
)
|
29
|
-
|
30
|
-
/** @member {integer} - The API error type.
|
31
|
-
*/
|
32
|
-
this.type = e.type
|
33
|
-
|
34
|
-
/** @member {string} - The address causing the error.
|
35
|
-
*/
|
36
|
-
this.address = e.address
|
37
|
-
|
38
|
-
/** @member {string} - The API error description.
|
39
|
-
*/
|
40
|
-
this.description = e.description
|
41
|
-
|
42
|
-
/** @member {boolean} - Indication that the request might still succeed
|
43
|
-
* for other attributes.
|
44
|
-
*/
|
45
|
-
this.nonCritical = nonCriticalApiErrorTypes.includes(e.type)
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
module.exports = ApiError
|
@@ -1,57 +0,0 @@
|
|
1
|
-
// homebridge-deconz/lib/Deconz/ApiResponse.js
|
2
|
-
//
|
3
|
-
// Homebridge plug-in for deCONZ.
|
4
|
-
// Copyright © 2018-2023 Erik Baauw. All rights reserved.
|
5
|
-
|
6
|
-
'use strict'
|
7
|
-
|
8
|
-
const { HttpClient } = 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 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(e, response))
|
37
|
-
}
|
38
|
-
const s = response.body[id].success
|
39
|
-
if (s != null && typeof s === 'object') {
|
40
|
-
for (const path of Object.keys(s)) {
|
41
|
-
const keys = path.split('/')
|
42
|
-
let obj = this.success
|
43
|
-
for (let i = 1; i < keys.length - 1; i++) {
|
44
|
-
if (obj[keys[i]] == null) {
|
45
|
-
obj[keys[i]] = {}
|
46
|
-
}
|
47
|
-
obj = obj[keys[i]]
|
48
|
-
}
|
49
|
-
obj[keys[keys.length - 1]] = s[path]
|
50
|
-
}
|
51
|
-
}
|
52
|
-
}
|
53
|
-
}
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
module.exports = ApiResponse
|