homebridge-lib 8.0.0 → 8.0.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.
- package/lib/MdnsClient.js +12 -0
- package/lib/Platform.js +37 -105
- package/package.json +4 -4
- package/lib/Bonjour.js +0 -12
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// homebridge-lib/lib/MdnsClient.js
|
|
2
|
+
//
|
|
3
|
+
// Library for Homebridge plugins.
|
|
4
|
+
// Copyright © 2020-2026 Erik Baauw. All rights reserved.
|
|
5
|
+
|
|
6
|
+
/** Multicat DNS (Bonjour) client.
|
|
7
|
+
* <br>See {@link MdnsClient}.
|
|
8
|
+
* @name MdnsClient
|
|
9
|
+
* @type {Class}
|
|
10
|
+
* @memberof module:homebridge-lib
|
|
11
|
+
*/
|
|
12
|
+
export { MdnsClient } from 'hb-lib-tools/MdnsClient'
|
package/lib/Platform.js
CHANGED
|
@@ -5,14 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import { once } from 'node:events'
|
|
7
7
|
import { writeFile, unlink } from 'node:fs/promises'
|
|
8
|
-
import { Server, STATUS_CODES } from 'node:http'
|
|
9
8
|
import { createRequire } from 'node:module'
|
|
10
9
|
import { format, promisify } from 'node:util'
|
|
11
|
-
import zlib from 'node:zlib'
|
|
12
10
|
|
|
13
11
|
import { HttpClient } from 'hb-lib-tools/HttpClient'
|
|
14
12
|
import { SystemInfo } from 'hb-lib-tools/SystemInfo'
|
|
15
|
-
import { UpnpClient } from 'hb-lib-tools/UpnpClient'
|
|
16
13
|
|
|
17
14
|
import { Delegate } from 'homebridge-lib/Delegate'
|
|
18
15
|
import { EveHomeKitTypes } from 'homebridge-lib/EveHomeKitTypes'
|
|
@@ -25,15 +22,16 @@ const require = createRequire(import.meta.url)
|
|
|
25
22
|
const libPackageJson = require('../package.json')
|
|
26
23
|
|
|
27
24
|
const uuid = /^[0-9A-F]{8}-[0-9A-F]{4}-[1-5][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/
|
|
28
|
-
|
|
25
|
+
|
|
26
|
+
let gzip
|
|
29
27
|
|
|
30
28
|
const context = {
|
|
31
29
|
libName: libPackageJson.name,
|
|
32
30
|
libVersion: libPackageJson.version,
|
|
33
31
|
nodeVersion: process.version.slice(1),
|
|
34
32
|
recommendedNodeVersion: recommendedNodeVersion(libPackageJson),
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
recommendedHomebridgeVersion:
|
|
34
|
+
semver.minVersion(libPackageJson.engines.homebridge).toString(),
|
|
37
35
|
saveInterval: 3600,
|
|
38
36
|
checkInterval: 7 * 24 * 3600,
|
|
39
37
|
driftDebugThreshold: 250,
|
|
@@ -80,8 +78,8 @@ class Platform extends Delegate {
|
|
|
80
78
|
context.homebridge = homebridge
|
|
81
79
|
context.homebridgeVersion = homebridge.serverVersion
|
|
82
80
|
context.PlatformAccessory = homebridge.platformAccessory
|
|
83
|
-
context.recommendedHomebridgeVersion =
|
|
84
|
-
|
|
81
|
+
// context.recommendedHomebridgeVersion =
|
|
82
|
+
// semver.minVersion(libPackageJson.engines.homebridge).toString()
|
|
85
83
|
|
|
86
84
|
const hap = {
|
|
87
85
|
Services: {},
|
|
@@ -178,52 +176,41 @@ class Platform extends Delegate {
|
|
|
178
176
|
)
|
|
179
177
|
}
|
|
180
178
|
this._myContext.platform = this
|
|
181
|
-
this
|
|
179
|
+
this.#identify()
|
|
182
180
|
|
|
183
181
|
this._homebridge
|
|
184
|
-
.on('didFinishLaunching', this.
|
|
185
|
-
.on('shutdown', this.
|
|
186
|
-
process.on('exit', this.
|
|
182
|
+
.on('didFinishLaunching', this.#main.bind(this))
|
|
183
|
+
.on('shutdown', this.#shutdown.bind(this))
|
|
184
|
+
process.on('exit', this.#exit.bind(this))
|
|
187
185
|
}
|
|
188
186
|
|
|
189
187
|
// ===== Main ================================================================
|
|
190
188
|
|
|
191
189
|
// Main platform function.
|
|
192
190
|
// Called by homebridge after restoring accessories from cache.
|
|
193
|
-
async
|
|
191
|
+
async #main () {
|
|
194
192
|
/** System information.
|
|
195
193
|
* @type {SystemInfo}
|
|
196
194
|
* @readonly
|
|
197
195
|
*/
|
|
198
|
-
this.systemInfo = new SystemInfo()
|
|
199
|
-
this.systemInfo
|
|
200
|
-
.on('error', (error) => { this.warn(error) })
|
|
201
|
-
.on('exec', (command) => { this.debug('exec: %s', command) })
|
|
202
|
-
.on('readFile', (filename) => { this.debug('read file: %s', filename) })
|
|
196
|
+
this.systemInfo = new SystemInfo({ logger: this })
|
|
203
197
|
await this.systemInfo.init()
|
|
204
198
|
this.log('hardware: %s', this.systemInfo.hwInfo.prettyName)
|
|
205
199
|
this.log('os: %s', this.systemInfo.osInfo.prettyName)
|
|
206
200
|
this._heartbeatStart = new Date()
|
|
207
|
-
setTimeout(() => { this
|
|
208
|
-
this.on('exit', () => { this._flushCachedAccessories() })
|
|
201
|
+
setTimeout(() => { this.#beat(-1) }, 1000)
|
|
209
202
|
|
|
210
203
|
const n = Object.keys(this._accessories).length
|
|
211
204
|
if (n > 0) {
|
|
212
205
|
this.log('restored %d accessories from cache', n)
|
|
213
206
|
}
|
|
214
|
-
if (this.listenerCount('upnpDeviceAlive') > 0) {
|
|
215
|
-
this._upnpMonitor.listen()
|
|
216
|
-
}
|
|
217
|
-
if (this.listenerCount('upnpDeviceFound') > 0) {
|
|
218
|
-
this._upnpMonitor.search()
|
|
219
|
-
}
|
|
220
207
|
if (typeof this.onUiRequest === 'function') {
|
|
221
208
|
try {
|
|
222
|
-
await this
|
|
209
|
+
await this.#createUiServer()
|
|
223
210
|
} catch (error) { this.error(error) }
|
|
224
211
|
}
|
|
225
212
|
await once(this, 'initialised')
|
|
226
|
-
this
|
|
213
|
+
this.#flushCachedAccessories()
|
|
227
214
|
for (const id in this._accessories) {
|
|
228
215
|
if (this._accessoryDelegates[id] == null) {
|
|
229
216
|
const accessory = this._accessories[id]
|
|
@@ -253,6 +240,10 @@ class Platform extends Delegate {
|
|
|
253
240
|
* @param {*} dumpInfo - Plugin-specific information.
|
|
254
241
|
*/
|
|
255
242
|
async createDumpFile (dumpInfo = {}) {
|
|
243
|
+
if (gzip == null) {
|
|
244
|
+
const zlib = await import('node:zlib')
|
|
245
|
+
gzip = promisify(zlib.gzip)
|
|
246
|
+
}
|
|
256
247
|
const result = {
|
|
257
248
|
hardware: this.systemInfo.hwInfo.prettyName,
|
|
258
249
|
os: this.systemInfo.osInfo.prettyName,
|
|
@@ -274,13 +265,13 @@ class Platform extends Delegate {
|
|
|
274
265
|
}
|
|
275
266
|
|
|
276
267
|
// Write `cachedAccessories` to disk.
|
|
277
|
-
|
|
268
|
+
#flushCachedAccessories () {
|
|
278
269
|
this.debug('flush cachedAccessories')
|
|
279
270
|
this._homebridge.updatePlatformAccessories([])
|
|
280
271
|
}
|
|
281
272
|
|
|
282
273
|
// Called every second.
|
|
283
|
-
|
|
274
|
+
#beat (beat) {
|
|
284
275
|
beat += 1
|
|
285
276
|
const drift = new Date() - this._heartbeatStart - 1000 * (beat + 1)
|
|
286
277
|
if (this._shuttingDown) {
|
|
@@ -295,16 +286,16 @@ class Platform extends Delegate {
|
|
|
295
286
|
}
|
|
296
287
|
}
|
|
297
288
|
setTimeout(() => {
|
|
298
|
-
this
|
|
289
|
+
this.#beat(beat)
|
|
299
290
|
}, drift < 1000 ? 1000 - drift : 1)
|
|
300
291
|
|
|
301
292
|
if (beat % context.saveInterval === 30) {
|
|
302
|
-
this
|
|
293
|
+
this.#flushCachedAccessories()
|
|
303
294
|
}
|
|
304
295
|
|
|
305
296
|
if (beat % context.checkInterval === 0) {
|
|
306
|
-
this
|
|
307
|
-
// this
|
|
297
|
+
this.#checkLatest(this._pluginName, this._pluginVersion)
|
|
298
|
+
// this.#checkLatest(context.libName, context.libVersion)
|
|
308
299
|
}
|
|
309
300
|
|
|
310
301
|
/** Emitted every second.
|
|
@@ -324,46 +315,40 @@ class Platform extends Delegate {
|
|
|
324
315
|
}
|
|
325
316
|
|
|
326
317
|
// Called by homebridge when shutting down.
|
|
327
|
-
|
|
318
|
+
#shutdown () {
|
|
328
319
|
if (this._shuttingDown) {
|
|
329
320
|
return
|
|
330
321
|
}
|
|
331
322
|
this._shuttingDown = true
|
|
332
|
-
this.removeAllListeners('upnpDeviceAlive')
|
|
333
|
-
this.removeAllListeners('upnpDeviceFound')
|
|
334
323
|
if (this._ui?.abortController != null) {
|
|
335
324
|
this._ui.abortController.abort()
|
|
336
325
|
}
|
|
337
326
|
for (const id in this._accessoryDelegates) {
|
|
338
327
|
/** Emitted when Homebridge is shutting down.
|
|
339
328
|
*
|
|
340
|
-
* On receiving this event, the
|
|
341
|
-
* flush peristent storage, ...).
|
|
329
|
+
* On receiving this event, the accessory delegate should cleanup
|
|
330
|
+
* (close connections, flush peristent storage, ...).
|
|
342
331
|
* @event AccessoryDelegate#shutdown
|
|
343
332
|
*/
|
|
344
333
|
this._accessoryDelegates[id].emit('shutdown')
|
|
345
334
|
}
|
|
346
335
|
/** Emitted when Homebridge is shutting down.
|
|
347
336
|
*
|
|
348
|
-
* On receiving this event, the plugin should cleanup
|
|
349
|
-
* flush peristent storage, ...).
|
|
337
|
+
* On receiving this event, the plugin should cleanup
|
|
338
|
+
* (close connections, flush peristent storage, ...).
|
|
350
339
|
* @event Platform#shutdown
|
|
351
340
|
*/
|
|
352
341
|
this.emit('shutdown')
|
|
353
342
|
}
|
|
354
343
|
|
|
355
344
|
// Called by NodeJS when process is exiting.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
* Note: asynchronous calls made when handling this event will not be executed.
|
|
360
|
-
* @event Platform#exit
|
|
361
|
-
*/
|
|
362
|
-
this.emit('exit')
|
|
345
|
+
#exit () {
|
|
346
|
+
this.#flushCachedAccessories()
|
|
347
|
+
this.log('goodbye')
|
|
363
348
|
}
|
|
364
349
|
|
|
365
350
|
// Issue an identity message.
|
|
366
|
-
|
|
351
|
+
#identify () {
|
|
367
352
|
this.log(
|
|
368
353
|
'%s v%s, node v%s, homebridge v%s, %s v%s',
|
|
369
354
|
this._pluginName, this._pluginVersion, context.nodeVersion,
|
|
@@ -388,7 +373,7 @@ class Platform extends Delegate {
|
|
|
388
373
|
}
|
|
389
374
|
|
|
390
375
|
// Check the NPM registry for the latest version of this plugin.
|
|
391
|
-
async
|
|
376
|
+
async #checkLatest (name, version) {
|
|
392
377
|
try {
|
|
393
378
|
if (this.npmRegistry == null) {
|
|
394
379
|
this.npmRegistry = new HttpClient({
|
|
@@ -534,60 +519,6 @@ class Platform extends Delegate {
|
|
|
534
519
|
delete this._accessories[id]
|
|
535
520
|
}
|
|
536
521
|
|
|
537
|
-
// ===== UPnP Device Discovery ===============================================
|
|
538
|
-
|
|
539
|
-
/** Configure UPnP discovery.
|
|
540
|
-
*
|
|
541
|
-
* @param {!object} config - ...
|
|
542
|
-
* @param {?string} config.class - Filter on UPnP device class.
|
|
543
|
-
* Default `upnp:rootdevice`. Use `ssdp:all` for all device classes.
|
|
544
|
-
* @param {?string} config.host - UPnP address and port.
|
|
545
|
-
* Default: `239.255.255.250:1900`.
|
|
546
|
-
* @param {function} config.filter - Filter on UPnP message content.
|
|
547
|
-
* The function takes the message as argument and returns a boolean.
|
|
548
|
-
* Default: `(message) => { return true }`, return all messages.
|
|
549
|
-
* @param {integer} config.timeout - Timeout (in seconds) for UPnP search.
|
|
550
|
-
* Default: `5`.
|
|
551
|
-
*/
|
|
552
|
-
upnpConfig (config) {
|
|
553
|
-
if (this._upnpMonitor != null) {
|
|
554
|
-
throw new SyntaxError('upnpConfig(): already called')
|
|
555
|
-
}
|
|
556
|
-
this._upnpMonitor = new UpnpClient(config)
|
|
557
|
-
this._upnpMonitor
|
|
558
|
-
.on('error', (error) => {
|
|
559
|
-
this.error('upnp: error')
|
|
560
|
-
this.error(error)
|
|
561
|
-
})
|
|
562
|
-
.on('listening', (host) => {
|
|
563
|
-
this.debug('upnp: listening on %s', host)
|
|
564
|
-
})
|
|
565
|
-
.on('searching', (host) => {
|
|
566
|
-
this.debug('upnp: searching on %s', host)
|
|
567
|
-
})
|
|
568
|
-
.on('searchDone', () => {
|
|
569
|
-
this.debug('upnp: search done')
|
|
570
|
-
})
|
|
571
|
-
.on('deviceAlive', (address, message) => {
|
|
572
|
-
// this.debug('upnp: device %s is alive: %j', address, message)
|
|
573
|
-
/** Emitted when a UPnP device sends an alive message.
|
|
574
|
-
* @event Platform#upnpDeviceAlive
|
|
575
|
-
* @param {string} address - The device's IP address.
|
|
576
|
-
* @param {object} message - The contents of the alive message.
|
|
577
|
-
*/
|
|
578
|
-
this.emit('upnpDeviceAlive', address, message)
|
|
579
|
-
})
|
|
580
|
-
.on('deviceFound', (address, message) => {
|
|
581
|
-
// this.debug('upnp: found device %s: %j', address, message)
|
|
582
|
-
/** Emitted when a UPnP device responds to a search request.
|
|
583
|
-
* @event Platform#upnpDeviceFound
|
|
584
|
-
* @param {string} address - The device's IP address.
|
|
585
|
-
* @param {object} message - The contents of the search response message.
|
|
586
|
-
*/
|
|
587
|
-
this.emit('upnpDeviceFound', address, message)
|
|
588
|
-
})
|
|
589
|
-
}
|
|
590
|
-
|
|
591
522
|
// ===== Dynamic Configuration through Homebridge UI =========================
|
|
592
523
|
|
|
593
524
|
/** Handler for requests from the Homebridge Plugin UI Server.
|
|
@@ -601,7 +532,8 @@ class Platform extends Delegate {
|
|
|
601
532
|
*/
|
|
602
533
|
|
|
603
534
|
// Create HTTP server for Homebridge Plugin UI Settings.
|
|
604
|
-
async
|
|
535
|
+
async #createUiServer () {
|
|
536
|
+
const { Server, STATUS_CODES } = await import('node:http')
|
|
605
537
|
this._ui = {}
|
|
606
538
|
this._ui.server = new Server()
|
|
607
539
|
this._ui.server
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"ebaauw"
|
|
7
7
|
],
|
|
8
8
|
"license": "Apache-2.0",
|
|
9
|
-
"version": "8.0.
|
|
9
|
+
"version": "8.0.1",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"homekit",
|
|
12
12
|
"homebridge"
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"upnp": "cli/upnp.js"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
|
-
"homebridge": "^
|
|
33
|
-
"node": "24.
|
|
32
|
+
"homebridge": "^2.0.0",
|
|
33
|
+
"node": "24.15.0||^24||^22"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@homebridge/plugin-ui-utils": "~2.2.3",
|
|
37
|
-
"hb-lib-tools": "~3.0.
|
|
37
|
+
"hb-lib-tools": "~3.0.4"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"prepare": "standard && rm -rf out && jsdoc -c jsdoc.json",
|
package/lib/Bonjour.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// homebridge-lib/lib/Bonjour.js
|
|
2
|
-
//
|
|
3
|
-
// Library for Homebridge plugins.
|
|
4
|
-
// Copyright © 2020-2026 Erik Baauw. All rights reserved.
|
|
5
|
-
|
|
6
|
-
/** Return the `Bonjour` class from [`bonjour-hap`](https://github.com/homebridge/bonjour),
|
|
7
|
-
* so plugins don't have to list this as a separate dependency.
|
|
8
|
-
* @name Bonjour
|
|
9
|
-
* @type {Class}
|
|
10
|
-
* @memberof module:homebridge-lib
|
|
11
|
-
*/
|
|
12
|
-
export { Bonjour } from 'hb-lib-tools/Bonjour'
|