homebridge-lib 6.3.6 → 6.3.8
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/README.md +1 -6
- package/cli/hap.js +2 -80
- package/cli/json.js +2 -154
- package/cli/sysinfo.js +2 -69
- package/cli/upnp.js +2 -138
- package/index.js +41 -99
- package/lib/AccessoryDelegate.js +1 -1
- package/lib/AdaptiveLighting.js +3 -3
- package/lib/Delegate.js +2 -1
- package/lib/EveHomeKitTypes.js +2 -2
- package/lib/MyHomeKitTypes.js +2 -2
- package/lib/Platform.js +7 -5
- package/lib/ServiceDelegate/AccessoryInformation.js +1 -3
- package/lib/ServiceDelegate/Battery.js +1 -3
- package/lib/ServiceDelegate/Dummy.js +1 -3
- package/lib/ServiceDelegate/History.js +6 -11
- package/lib/ServiceDelegate/ServiceLabel.js +1 -3
- package/lib/UiServer.js +1 -2
- package/package.json +4 -6
- package/lib/Colour.js +0 -323
- package/lib/CommandLineParser.js +0 -311
- package/lib/CommandLineTool.js +0 -328
- package/lib/HttpClient.js +0 -478
- package/lib/JsonFormatter.js +0 -200
- package/lib/OptionParser.js +0 -886
- package/lib/SystemInfo.js +0 -522
- package/lib/UpnpClient.js +0 -217
package/lib/HttpClient.js
DELETED
|
@@ -1,478 +0,0 @@
|
|
|
1
|
-
// homebridge-lib/lib/HttpClient.js
|
|
2
|
-
//
|
|
3
|
-
// Library for Homebridge plugins.
|
|
4
|
-
// Copyright © 2018-2023 Erik Baauw. All rights reserved.
|
|
5
|
-
|
|
6
|
-
'use strict'
|
|
7
|
-
|
|
8
|
-
const homebridgeLib = require('../index')
|
|
9
|
-
|
|
10
|
-
const events = require('events')
|
|
11
|
-
const http = require('http')
|
|
12
|
-
const https = require('https')
|
|
13
|
-
|
|
14
|
-
/** HTTP error.
|
|
15
|
-
* @hideconstructor
|
|
16
|
-
* @extends Error
|
|
17
|
-
* @memberof HttpClient
|
|
18
|
-
*/
|
|
19
|
-
class HttpError extends Error {
|
|
20
|
-
constructor (message, request, statusCode, statusMessage) {
|
|
21
|
-
super(message)
|
|
22
|
-
/** @member {HttpClient.HttpRequest} - The request that caused the error.
|
|
23
|
-
*/
|
|
24
|
-
this.request = request
|
|
25
|
-
|
|
26
|
-
/** @member {?integer} - The HTTP status code.
|
|
27
|
-
*/
|
|
28
|
-
this.statusCode = statusCode
|
|
29
|
-
|
|
30
|
-
/** @member {?string} - The HTTP status message.
|
|
31
|
-
*/
|
|
32
|
-
this.statusMessage = statusMessage
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** HTTP request.
|
|
37
|
-
* @hideconstructor
|
|
38
|
-
* @memberof HttpClient
|
|
39
|
-
*/
|
|
40
|
-
class HttpRequest {
|
|
41
|
-
constuctor (name, id, method, resource, headers, body, url) {
|
|
42
|
-
/** @member {string} - The server name.
|
|
43
|
-
*/
|
|
44
|
-
this.name = name
|
|
45
|
-
|
|
46
|
-
/** @member {integer} - The request ID.
|
|
47
|
-
*/
|
|
48
|
-
this.id = id
|
|
49
|
-
|
|
50
|
-
/** @member {string} - The request method.
|
|
51
|
-
*/
|
|
52
|
-
this.method = method
|
|
53
|
-
|
|
54
|
-
/** @member {string} - The requested resource.
|
|
55
|
-
*/
|
|
56
|
-
this.resource = resource
|
|
57
|
-
|
|
58
|
-
/** @member {object} - The request headers.
|
|
59
|
-
*/
|
|
60
|
-
this.headers = headers
|
|
61
|
-
|
|
62
|
-
/** @member {?string} - The request body.
|
|
63
|
-
*/
|
|
64
|
-
this.body = body
|
|
65
|
-
|
|
66
|
-
/** @member {string} - The full request URL.
|
|
67
|
-
*/
|
|
68
|
-
this.url = url
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** HTTP response.
|
|
73
|
-
* @hideconstructor
|
|
74
|
-
* @memberof HttpClient
|
|
75
|
-
*/
|
|
76
|
-
class HttpResponse {
|
|
77
|
-
constructor (request, statusCode, statusMessage, headers, body, parsedBody) {
|
|
78
|
-
/** @member {HttpClient.HttpRequest} - The request that generated the response.
|
|
79
|
-
*/
|
|
80
|
-
this.request = request
|
|
81
|
-
|
|
82
|
-
/** @member {integer} - The HTTP status code.
|
|
83
|
-
*/
|
|
84
|
-
this.statusCode = statusCode
|
|
85
|
-
|
|
86
|
-
/** @member {string} - The HTTP status message.
|
|
87
|
-
*/
|
|
88
|
-
this.statusMessage = statusMessage
|
|
89
|
-
|
|
90
|
-
/** @member {object} - The response headers.
|
|
91
|
-
*/
|
|
92
|
-
this.headers = headers
|
|
93
|
-
|
|
94
|
-
/** @member {?*} - The response body.
|
|
95
|
-
*/
|
|
96
|
-
this.body = body
|
|
97
|
-
|
|
98
|
-
/** @member {?*} - The parsed response body
|
|
99
|
-
* (in case of an XML response body).
|
|
100
|
-
*/
|
|
101
|
-
this.parsedBody = parsedBody
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** HTTP client.
|
|
106
|
-
* @extends EventEmitter
|
|
107
|
-
*/
|
|
108
|
-
class HttpClient extends events.EventEmitter {
|
|
109
|
-
static get HttpError () { return HttpError }
|
|
110
|
-
static get HttpRequest () { return HttpRequest }
|
|
111
|
-
static get HttpResponse () { return HttpResponse }
|
|
112
|
-
|
|
113
|
-
/** Create a new instance of a client to an HTTP server.
|
|
114
|
-
*
|
|
115
|
-
* @param {object} params - Parameters.
|
|
116
|
-
* @param {object} [params.headers={}] - Default HTTP headers for each request.
|
|
117
|
-
* @param {string} [params.host='localhost:80'] - Server hostname and port.
|
|
118
|
-
* @param {boolean} [params.https=false] - Use HTTPS (instead of HTTP).
|
|
119
|
-
* @param {boolean} [params.ipv6=false] - Use IPv6 (instead of IPv4).
|
|
120
|
-
* @param {boolean} [params.json=false] - Use JSON, i.e. request and response
|
|
121
|
-
* bodies are JSON strings.
|
|
122
|
-
* @param {boolean} [params.keepAlive=false] - Keep server connection(s) open.
|
|
123
|
-
* @param {integer} [params.maxSockets=Infinity] - Throttle requests to
|
|
124
|
-
* maximum number of parallel connections.
|
|
125
|
-
* @param {?string} params.name - The name of the server. Defaults to hostname.
|
|
126
|
-
* @param {string} [params.path=''] - Server base path.
|
|
127
|
-
* @param {boolean} [params.selfSignedCertificate=false] - Server uses a
|
|
128
|
-
* self-signed SSL certificate.
|
|
129
|
-
* @param {string} [params.suffix=''] - Base suffix to append after resource
|
|
130
|
-
* e.g. for authentication of the request.
|
|
131
|
-
* @param {boolean} [params.text=false] - Convert response body to text.
|
|
132
|
-
* @param {integer} [params.timeout=5] - Request timeout (in seconds).
|
|
133
|
-
* @param {integer[]} [params.validStatusCodes=[200]] - List of valid HTTP status codes.
|
|
134
|
-
* @param {?function} params.xmlParser - Parser for XML response body.
|
|
135
|
-
*/
|
|
136
|
-
constructor (params) {
|
|
137
|
-
super()
|
|
138
|
-
this.__params = {
|
|
139
|
-
headers: {},
|
|
140
|
-
hostname: 'localhost',
|
|
141
|
-
keepAlive: false,
|
|
142
|
-
maxSockets: Infinity,
|
|
143
|
-
path: '',
|
|
144
|
-
suffix: '',
|
|
145
|
-
timeout: 5,
|
|
146
|
-
validStatusCodes: [200]
|
|
147
|
-
}
|
|
148
|
-
const optionParser = new homebridgeLib.OptionParser(this.__params)
|
|
149
|
-
optionParser
|
|
150
|
-
.hostKey()
|
|
151
|
-
.boolKey('https')
|
|
152
|
-
.boolKey('ipv6')
|
|
153
|
-
.objectKey('headers')
|
|
154
|
-
.boolKey('json')
|
|
155
|
-
.boolKey('keepAlive')
|
|
156
|
-
.intKey('maxSockets', 1)
|
|
157
|
-
.stringKey('name', true)
|
|
158
|
-
.stringKey('path')
|
|
159
|
-
.boolKey('selfSignedCertificate')
|
|
160
|
-
.stringKey('suffix')
|
|
161
|
-
.boolKey('text', true)
|
|
162
|
-
.intKey('timeout', 1, 60)
|
|
163
|
-
.arrayKey('validStatusCodes')
|
|
164
|
-
.asyncFunctionKey('xmlParser')
|
|
165
|
-
.parse(params)
|
|
166
|
-
if (this.__params.selfSignedCertificate) {
|
|
167
|
-
this.__params.https = true
|
|
168
|
-
}
|
|
169
|
-
this._http = this.__params.https ? https : http
|
|
170
|
-
this.__options = {
|
|
171
|
-
agent: new this._http.Agent({
|
|
172
|
-
keepAlive: this.__params.keepAlive,
|
|
173
|
-
maxSockets: this.__params.maxSockets
|
|
174
|
-
}),
|
|
175
|
-
family: this.__params.ipv6 ? 6 : 4,
|
|
176
|
-
headers: Object.assign({}, this.__params.headers),
|
|
177
|
-
timeout: 1000 * this.__params.timeout
|
|
178
|
-
}
|
|
179
|
-
if (this.__params.selfSignedCertificate) {
|
|
180
|
-
this.__options.rejectUnauthorized = false
|
|
181
|
-
}
|
|
182
|
-
if (this.__params.json) {
|
|
183
|
-
const json = 'application/json;charset=utf-8'
|
|
184
|
-
if (this.__options.headers == null) {
|
|
185
|
-
this.__options.headers = {}
|
|
186
|
-
}
|
|
187
|
-
this.__options.headers['Content-Type'] = json
|
|
188
|
-
if (this.__options.headers.Accept == null) {
|
|
189
|
-
this.__options.headers.Accept = json
|
|
190
|
-
} else {
|
|
191
|
-
this.__options.headers.Accept += ',' + json
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
this._setUrl()
|
|
195
|
-
this.__requestId = 0
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
_setUrl () {
|
|
199
|
-
this.__params.url = this.__params.https ? 'https://' : 'http://'
|
|
200
|
-
this.__params.url += this.__params.hostname
|
|
201
|
-
if (this.__params.port != null) {
|
|
202
|
-
this.__params.url += ':' + this.__params.port
|
|
203
|
-
}
|
|
204
|
-
this.__params.url += this.__params.path
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/** Server IP address.
|
|
208
|
-
* @type {string}
|
|
209
|
-
* @readonly
|
|
210
|
-
*/
|
|
211
|
-
get address () { return this.__params.address }
|
|
212
|
-
|
|
213
|
-
/** Server hostname and port.
|
|
214
|
-
* @type {string}
|
|
215
|
-
*/
|
|
216
|
-
get host () {
|
|
217
|
-
let host = this.__params.hostname
|
|
218
|
-
if (this.__params.port != null) {
|
|
219
|
-
host += ':' + this.__params.port
|
|
220
|
-
}
|
|
221
|
-
return host
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
set host (value) {
|
|
225
|
-
const obj = homebridgeLib.OptionParser.toHost('host', value)
|
|
226
|
-
this.__params.hostname = obj.hostname
|
|
227
|
-
this.__params.port = obj.port
|
|
228
|
-
this._setUrl()
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/** Local IP address used for the connection.
|
|
232
|
-
* @type {string}
|
|
233
|
-
* @readonly
|
|
234
|
-
*/
|
|
235
|
-
get localAddress () { return this.__params.localAddress }
|
|
236
|
-
|
|
237
|
-
/** Server frienly name.
|
|
238
|
-
* Defaults to the hostname.
|
|
239
|
-
* @type {string}
|
|
240
|
-
*/
|
|
241
|
-
get name () {
|
|
242
|
-
return this.__params.name == null
|
|
243
|
-
? this.__params.hostname
|
|
244
|
-
: this.__params.name
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
set name (name) {
|
|
248
|
-
this.__params.name = name
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/** Server (base) path.
|
|
252
|
-
* @type {string}
|
|
253
|
-
*/
|
|
254
|
-
get path () { return this.__params.path }
|
|
255
|
-
set path (value) {
|
|
256
|
-
this.__params.path = value == null
|
|
257
|
-
? ''
|
|
258
|
-
: homebridgeLib.OptionParser.toPath('path', value)
|
|
259
|
-
this._setUrl()
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/** Server (base) url.
|
|
263
|
-
* @type {string}
|
|
264
|
-
* @readonly
|
|
265
|
-
*/
|
|
266
|
-
get url () { return this.__params.url }
|
|
267
|
-
|
|
268
|
-
/** GET request.
|
|
269
|
-
* @param {string} [resource='/'] - The resource.
|
|
270
|
-
* @param {?object} headers - Additional headers for the request.
|
|
271
|
-
* @param {?string} suffix - Additional suffix to append after resource
|
|
272
|
-
* e.g. for authentication of the request.
|
|
273
|
-
* @return {HttpResponse} response - The response.
|
|
274
|
-
* @throws {HttpError} In case of error.
|
|
275
|
-
*/
|
|
276
|
-
async get (resource = '/', headers, suffix) {
|
|
277
|
-
return this.request('GET', resource, undefined, headers, suffix)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/** PUT request.
|
|
281
|
-
* @param {!string} resource - The resource.
|
|
282
|
-
* @param {?*} body - The body for the request.
|
|
283
|
-
* @param {?object} headers - Additional headers for the request.
|
|
284
|
-
* @param {?string} suffix - Additional suffix to append after resource
|
|
285
|
-
* e.g. for authentication of the request.
|
|
286
|
-
* @return {HttpResponse} response - The response.
|
|
287
|
-
* @throws {HttpError} In case of error.
|
|
288
|
-
*/
|
|
289
|
-
async put (resource, body, headers, suffix) {
|
|
290
|
-
return this.request('PUT', resource, body, headers, suffix)
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/** POST request.
|
|
294
|
-
* @param {!string} resource - The resource.
|
|
295
|
-
* @param {?*} body - The body for the request.
|
|
296
|
-
* @param {?object} headers - Additional headers for the request.
|
|
297
|
-
* @param {?string} suffix - Additional suffix to append after resource
|
|
298
|
-
* e.g. for authentication of the request.
|
|
299
|
-
* @return {HttpResponse} response - The response.
|
|
300
|
-
* @throws {HttpError} In case of error.
|
|
301
|
-
*/
|
|
302
|
-
async post (resource, body, headers, suffix) {
|
|
303
|
-
return this.request('POST', resource, body, headers, suffix)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/** DELETE request.
|
|
307
|
-
* @param {!string} resource - The resource.
|
|
308
|
-
* @param {?*} body - The body for the request.
|
|
309
|
-
* @param {?object} headers - Additional headers for the request.
|
|
310
|
-
* @param {?string} suffix - Additional suffix to append after resource
|
|
311
|
-
* e.g. for authentication of the request.
|
|
312
|
-
* @return {object} response - The response.
|
|
313
|
-
* @throws {HttpError} In case of error.
|
|
314
|
-
*/
|
|
315
|
-
async delete (resource, body, headers, suffix) {
|
|
316
|
-
return this.request('DELETE', resource, body, headers, suffix)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/** Check self-signed SSL certificate.
|
|
320
|
-
* @abstract
|
|
321
|
-
* @throws {Error} For invalid SSL certificate.
|
|
322
|
-
*/
|
|
323
|
-
checkCertificate (cert) {}
|
|
324
|
-
|
|
325
|
-
/** Issue an HTTP request.
|
|
326
|
-
* @param {string} method - The method for the request.
|
|
327
|
-
* @param {!string} resource - The resource for the request.
|
|
328
|
-
* @param {?*} body - The body for the request.
|
|
329
|
-
* @param {?object} headers - Additional headers for the request.
|
|
330
|
-
* @param {?string} suffix - Additional suffix to append after resource
|
|
331
|
-
* e.g. for authentication of the request.
|
|
332
|
-
* @param {?object} info - Additional key/value pairs to include in the
|
|
333
|
-
* for the `HttpRequest` of the `request`, `response`, and `error` events.
|
|
334
|
-
* @return {HttpResponse} response - The response.
|
|
335
|
-
* @throws {HttpError} In case of error.
|
|
336
|
-
*/
|
|
337
|
-
async request (method, resource, body, headers = {}, suffix = '', info = {}) {
|
|
338
|
-
return new Promise((resolve, reject) => {
|
|
339
|
-
method = homebridgeLib.OptionParser.toString('method', method, true)
|
|
340
|
-
if (!http.METHODS.includes(method)) {
|
|
341
|
-
throw new TypeError(`${method}: invalid method`)
|
|
342
|
-
}
|
|
343
|
-
resource = homebridgeLib.OptionParser.toString('resource', resource, true)
|
|
344
|
-
if (body != null && !Buffer.isBuffer(body)) {
|
|
345
|
-
body = this.__params.json
|
|
346
|
-
? JSON.stringify(body)
|
|
347
|
-
: homebridgeLib.OptionParser.toString('body', body)
|
|
348
|
-
}
|
|
349
|
-
const requestId = ++this.__requestId
|
|
350
|
-
const url = this.__params.url + (resource === '/' ? '' : resource) +
|
|
351
|
-
this.__params.suffix + suffix
|
|
352
|
-
const options = Object.assign({ method }, this.__options)
|
|
353
|
-
const requestInfo = Object.assign({
|
|
354
|
-
name: this.name,
|
|
355
|
-
id: requestId,
|
|
356
|
-
method,
|
|
357
|
-
resource,
|
|
358
|
-
body,
|
|
359
|
-
url
|
|
360
|
-
}, info)
|
|
361
|
-
const request = this._http.request(url, options)
|
|
362
|
-
request
|
|
363
|
-
.on('error', (error) => {
|
|
364
|
-
if (!(error instanceof HttpError)) {
|
|
365
|
-
error = new HttpError(error.message, requestInfo)
|
|
366
|
-
}
|
|
367
|
-
/** Emitted in case of error.
|
|
368
|
-
* @event HttpClient#error
|
|
369
|
-
* @param {HttpClient.HttpError} error - The error.
|
|
370
|
-
*/
|
|
371
|
-
this.emit('error', error)
|
|
372
|
-
reject(error)
|
|
373
|
-
})
|
|
374
|
-
.on('timeout', () => {
|
|
375
|
-
const error = new HttpError(
|
|
376
|
-
`timeout after ${this.__params.timeout} seconds`,
|
|
377
|
-
requestInfo, 408, 'Request Timeout'
|
|
378
|
-
)
|
|
379
|
-
request.destroy(error)
|
|
380
|
-
})
|
|
381
|
-
.on('socket', (socket) => {
|
|
382
|
-
if (
|
|
383
|
-
this.__params.address == null || this.__params.localAddress == null
|
|
384
|
-
) {
|
|
385
|
-
socket.once('connect', () => {
|
|
386
|
-
this.__params.address = socket.remoteAddress
|
|
387
|
-
this.__params.localAddress = socket.localAddress
|
|
388
|
-
})
|
|
389
|
-
}
|
|
390
|
-
/** Emitted when a request has been sent to the HTTP server.
|
|
391
|
-
* @event HttpClient#request
|
|
392
|
-
* @param {HttpClient.HttpRequest} request - The request.
|
|
393
|
-
*/
|
|
394
|
-
this.emit('request', requestInfo)
|
|
395
|
-
})
|
|
396
|
-
.on('response', (response) => {
|
|
397
|
-
if (this.__params.selfSignedCertificate) {
|
|
398
|
-
try {
|
|
399
|
-
this.checkCertificate(response.socket.getPeerCertificate())
|
|
400
|
-
} catch (error) {
|
|
401
|
-
request.destroy(error)
|
|
402
|
-
return
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
const a = []
|
|
406
|
-
response
|
|
407
|
-
.on('data', (chunk) => { a.push(chunk) })
|
|
408
|
-
.on('end', async () => {
|
|
409
|
-
const buffer = Buffer.concat(a)
|
|
410
|
-
|
|
411
|
-
if (!this.__params.validStatusCodes.includes(response.statusCode)) {
|
|
412
|
-
request.emit('error',
|
|
413
|
-
new HttpError(
|
|
414
|
-
`http status ${response.statusCode} ${response.statusMessage}`,
|
|
415
|
-
requestInfo, response.statusCode, response.statusMessage
|
|
416
|
-
)
|
|
417
|
-
)
|
|
418
|
-
return
|
|
419
|
-
}
|
|
420
|
-
const responseInfo = {
|
|
421
|
-
request: requestInfo,
|
|
422
|
-
headers: response.headers,
|
|
423
|
-
statusCode: response.statusCode,
|
|
424
|
-
statusMessage: response.statusMessage,
|
|
425
|
-
body: null
|
|
426
|
-
}
|
|
427
|
-
if (buffer != null && buffer.length > 0) {
|
|
428
|
-
responseInfo.body = buffer
|
|
429
|
-
if (response.headers['content-type'] != null) {
|
|
430
|
-
if (
|
|
431
|
-
response.headers['content-type'].startsWith('application/json')
|
|
432
|
-
) {
|
|
433
|
-
try {
|
|
434
|
-
responseInfo.body = JSON.parse(buffer.toString('utf-8'))
|
|
435
|
-
} catch (error) {
|
|
436
|
-
request.emit('error', error)
|
|
437
|
-
return
|
|
438
|
-
}
|
|
439
|
-
} else if (response.headers['content-type'].startsWith('text')) {
|
|
440
|
-
responseInfo.body = buffer.toString('utf-8')
|
|
441
|
-
if (
|
|
442
|
-
response.headers['content-type'].startsWith('text/xml') &&
|
|
443
|
-
this.__params.xmlParser != null
|
|
444
|
-
) {
|
|
445
|
-
try {
|
|
446
|
-
responseInfo.parsedBody = await this.__params.xmlParser(
|
|
447
|
-
responseInfo.body
|
|
448
|
-
)
|
|
449
|
-
} catch (error) {
|
|
450
|
-
request.emit('error', error)
|
|
451
|
-
return
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
/** Emitted when a valid response has been received from the HTTP server.
|
|
458
|
-
* @event HttpClient#response
|
|
459
|
-
* @param {HttpClient.HttpResponse} response - The response.
|
|
460
|
-
*/
|
|
461
|
-
this.emit('response', responseInfo)
|
|
462
|
-
resolve(responseInfo)
|
|
463
|
-
})
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
if (headers != null) {
|
|
467
|
-
headers = homebridgeLib.OptionParser.toObject('headers', headers)
|
|
468
|
-
for (const header in headers) {
|
|
469
|
-
request.setHeader(header, headers[header])
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
requestInfo.headers = request.getHeaders()
|
|
473
|
-
request.end(body)
|
|
474
|
-
})
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
module.exports = HttpClient
|
package/lib/JsonFormatter.js
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
// homebridge-lib/lib/JsonFormatter.js
|
|
2
|
-
//
|
|
3
|
-
// Library for Homebridge plugins.
|
|
4
|
-
// Copyright © 2018-2023 Erik Baauw. All rights reserved.
|
|
5
|
-
|
|
6
|
-
'use strict'
|
|
7
|
-
|
|
8
|
-
const homebridgeLib = require('../index')
|
|
9
|
-
|
|
10
|
-
/** JSON formatter.
|
|
11
|
-
*
|
|
12
|
-
* Class to format (pretty-print) JavaScript types to formatted JSON strings.
|
|
13
|
-
* This class is the engine under the `json` command-line tool.
|
|
14
|
-
*/
|
|
15
|
-
class JsonFormatter {
|
|
16
|
-
/** Create a new instance of a JSON formatter.
|
|
17
|
-
*
|
|
18
|
-
* The parameters configure how the JSON should be formatted.
|
|
19
|
-
* @param {object} params - Parameters.
|
|
20
|
-
* @param {boolean} [params.ascii=false] - Output path:value in plain text
|
|
21
|
-
* instead of JSON.
|
|
22
|
-
* <br>Command-line equivalent: `json -a`
|
|
23
|
-
* @param {string} params.fromPath - Limit output to key/values under path.
|
|
24
|
-
* <br>Set top level below path.
|
|
25
|
-
* <br>Command-line equivalent: `json -p `_path_
|
|
26
|
-
* @param {boolean} [params.jsonArray=false] - Output JSON array of objects
|
|
27
|
-
* for each key/value pair.<br>
|
|
28
|
-
* Each object contains two key/value pairs: key `keys` with an array
|
|
29
|
-
* of keys as value and key `value` with the value as value.
|
|
30
|
-
* <br>Command-line equivalent: `json -j`
|
|
31
|
-
* @param {boolean} [params.joinKeys=false] - Output JSON array of objects
|
|
32
|
-
* for each key/value pair.<br>
|
|
33
|
-
* Each object contains one key/value pair: the path (concatenated
|
|
34
|
-
* keys separated by '/') as key and the value as value.
|
|
35
|
-
* <br>Command-line equivalent: `json -u`
|
|
36
|
-
* @param {boolean} [params.keysOnly=false] - Limit output to keys.
|
|
37
|
-
* <br>With `joinKeys` output JSON array of paths.
|
|
38
|
-
* <br>Command-line equivalent: `json -k`
|
|
39
|
-
* @param {boolean} [params.leavesOnly=false] - Limit output to leaf
|
|
40
|
-
* (non-array, non-object) key/values.
|
|
41
|
-
* <br>Command-line equivalent: `json -l`
|
|
42
|
-
* @param {?integer} params.maxDepth - Limit output to levels above depth.
|
|
43
|
-
* <br>Command-line equivalent: `json -d `_depth_
|
|
44
|
-
* @param {boolean} [params.noWhiteSpace=false] - Do not include spaces nor
|
|
45
|
-
* newlines in the output.
|
|
46
|
-
* <br>Command-line equivalent: `json -n`
|
|
47
|
-
* @param {boolean} [params.sortKeys=false] - Sort object key/value pairs
|
|
48
|
-
* alphabetically on key.
|
|
49
|
-
* <br>Command-line equivalent: `json -s`
|
|
50
|
-
* @param {boolean} [params.topOnly=false] - Limit output to top-level key/values.
|
|
51
|
-
* <br>Command-line equivalent: `json -t`
|
|
52
|
-
* @param {boolean} [params.valuesOnly=false] - Limit output to values.
|
|
53
|
-
* <br>With `joinKeys` output JSON array of values.
|
|
54
|
-
* <br>Command-line equivalent: `json -v`
|
|
55
|
-
*/
|
|
56
|
-
constructor (params = {}) {
|
|
57
|
-
this.options = {}
|
|
58
|
-
const optionParser = new homebridgeLib.OptionParser(this.options)
|
|
59
|
-
optionParser.boolKey('sortKeys')
|
|
60
|
-
optionParser.boolKey('noWhiteSpace')
|
|
61
|
-
optionParser.boolKey('jsonArray')
|
|
62
|
-
optionParser.boolKey('joinKeys')
|
|
63
|
-
optionParser.boolKey('ascii')
|
|
64
|
-
optionParser.boolKey('topOnly')
|
|
65
|
-
optionParser.intKey('maxDepth', 0)
|
|
66
|
-
optionParser.pathKey('fromPath')
|
|
67
|
-
optionParser.boolKey('leavesOnly')
|
|
68
|
-
optionParser.boolKey('keysOnly')
|
|
69
|
-
optionParser.boolKey('valuesOnly')
|
|
70
|
-
optionParser.parse(params)
|
|
71
|
-
if (this.options.ascii) {
|
|
72
|
-
this.options.noWhiteSpace = true
|
|
73
|
-
this.options.joinKeys = true
|
|
74
|
-
}
|
|
75
|
-
if (
|
|
76
|
-
this.options.joinKeys || this.options.topOnly || this.options.leavesOnly ||
|
|
77
|
-
this.options.keysOnly || this.options.valuesOnly
|
|
78
|
-
) {
|
|
79
|
-
this.options.jsonArray = true
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
_forEach (value, callback) {
|
|
84
|
-
function forEach (keys, value, depth) {
|
|
85
|
-
const isCollection = (typeof (value) === 'object' && value != null)
|
|
86
|
-
|
|
87
|
-
if (value === undefined) {
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
if (
|
|
91
|
-
!isCollection ||
|
|
92
|
-
(!this.options.leavesOnly && (!this.options.topOnly || depth === 1))
|
|
93
|
-
) {
|
|
94
|
-
callback(keys, value)
|
|
95
|
-
}
|
|
96
|
-
if (
|
|
97
|
-
isCollection && (!this.options.topOnly || depth === 0) &&
|
|
98
|
-
depth !== this.options.maxDepth
|
|
99
|
-
) {
|
|
100
|
-
const list = Object.keys(value)
|
|
101
|
-
if (this.options.sortKeys && !Array.isArray(value)) {
|
|
102
|
-
list.sort()
|
|
103
|
-
}
|
|
104
|
-
for (const key of list) {
|
|
105
|
-
forEach.call(this, keys.concat([key]), value[key], depth + 1)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
forEach.call(this, [], value, 0)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
_map (value, callback) {
|
|
114
|
-
const array = []
|
|
115
|
-
this._forEach(value, (keys, value) => {
|
|
116
|
-
array.push(callback(keys, value))
|
|
117
|
-
})
|
|
118
|
-
return array
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
_format (value, maxDepth = this.options.maxDepth, withIndent = ' ') {
|
|
122
|
-
function format (value, depth, indent) {
|
|
123
|
-
const noNewline = this.options.noWhiteSpace || depth >= maxDepth
|
|
124
|
-
const nl = this.options.noWhiteSpace ? '' : noNewline ? ' ' : '\n'
|
|
125
|
-
const sp = this.options.noWhiteSpace ? '' : ' '
|
|
126
|
-
const nlsp = noNewline ? '' : '\n'
|
|
127
|
-
const wi = noNewline ? '' : withIndent
|
|
128
|
-
const id = noNewline ? '' : indent
|
|
129
|
-
|
|
130
|
-
if (value === undefined) {
|
|
131
|
-
return ''
|
|
132
|
-
}
|
|
133
|
-
if (typeof (value) !== 'object' || value == null) {
|
|
134
|
-
return JSON.stringify(value)
|
|
135
|
-
}
|
|
136
|
-
const array = []
|
|
137
|
-
const list = Object.keys(value)
|
|
138
|
-
if (this.options.sortKeys && !Array.isArray(value)) {
|
|
139
|
-
list.sort()
|
|
140
|
-
}
|
|
141
|
-
for (const key of list) {
|
|
142
|
-
if (value[key] !== undefined) {
|
|
143
|
-
const k = Array.isArray(value) ? '' : `"${key}":${sp}`
|
|
144
|
-
const v = format.call(this, value[key], depth + 1, `${wi}${id}`)
|
|
145
|
-
array.push(k + v)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
let s = array.join(`,${nl}${wi}${id}`)
|
|
149
|
-
if (s !== '') {
|
|
150
|
-
s = `${nlsp}${wi}${id}${s}${nlsp}${id}`
|
|
151
|
-
}
|
|
152
|
-
return Array.isArray(value) ? `[${s}]` : `{${s}}`
|
|
153
|
-
}
|
|
154
|
-
return format.call(this, value, 0, '')
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** Transform javascript value into a formatted JSON string.
|
|
158
|
-
*
|
|
159
|
-
* @param {*} value - The JavaScript value.
|
|
160
|
-
* @return {string} json - The formatted JSON string.
|
|
161
|
-
*/
|
|
162
|
-
stringify (value) {
|
|
163
|
-
if (this.options.fromPath != null) {
|
|
164
|
-
const a = this.options.fromPath.slice(1).split('/')
|
|
165
|
-
for (const key of a) {
|
|
166
|
-
if (typeof (value) === 'object' && value != null) {
|
|
167
|
-
value = value[key]
|
|
168
|
-
} else {
|
|
169
|
-
value = undefined
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
if (!this.options.jsonArray) {
|
|
174
|
-
return this._format(value)
|
|
175
|
-
}
|
|
176
|
-
const array = this._map(value, (keys, value) => {
|
|
177
|
-
if (this.options.ascii) {
|
|
178
|
-
if (this.options.keysOnly) { return `/${keys.join('/')}` }
|
|
179
|
-
if (this.options.valuesOnly) { return this._format(value) }
|
|
180
|
-
return `/${keys.join('/')}:${this._format(value)}`
|
|
181
|
-
} else if (this.options.joinKeys) {
|
|
182
|
-
if (this.options.keysOnly) { return `/${keys.join('/')}` }
|
|
183
|
-
if (this.options.valuesOnly) { return value }
|
|
184
|
-
const obj = {}
|
|
185
|
-
obj[`/${keys.join('/')}`] = value
|
|
186
|
-
return obj
|
|
187
|
-
} else {
|
|
188
|
-
if (this.options.keysOnly) { return { keys } }
|
|
189
|
-
if (this.options.valuesOnly) { return { value } }
|
|
190
|
-
return { keys, value }
|
|
191
|
-
}
|
|
192
|
-
})
|
|
193
|
-
if (this.options.ascii) {
|
|
194
|
-
return array.join('\n')
|
|
195
|
-
}
|
|
196
|
-
return this._format(array, 1)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
module.exports = JsonFormatter
|