homebridge-lib 8.0.0 → 8.1.0
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/AccessoryDelegate.js +0 -5
- package/lib/MdnsClient.js +12 -0
- package/lib/Platform.js +47 -108
- package/package.json +4 -4
- package/lib/Bonjour.js +0 -12
package/lib/AccessoryDelegate.js
CHANGED
|
@@ -249,11 +249,6 @@ class AccessoryDelegate extends Delegate {
|
|
|
249
249
|
|
|
250
250
|
set heartbeatEnabled (value) { this._heartbeatEnabled = !!value }
|
|
251
251
|
|
|
252
|
-
setAlive () {
|
|
253
|
-
this.warn('setAlive() has been deprecated, use heartbeatEnabled instead')
|
|
254
|
-
this._heartbeatEnabled = true
|
|
255
|
-
}
|
|
256
|
-
|
|
257
252
|
/** Plugin-specific context to be persisted across Homebridge restarts.
|
|
258
253
|
*
|
|
259
254
|
* After restart, this object is passed back to the plugin through the
|
|
@@ -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.log('goodbye') })
|
|
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,47 @@ class Platform extends Delegate {
|
|
|
324
315
|
}
|
|
325
316
|
|
|
326
317
|
// Called by homebridge when shutting down.
|
|
327
|
-
|
|
318
|
+
async #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
|
}
|
|
326
|
+
const jobs = []
|
|
337
327
|
for (const id in this._accessoryDelegates) {
|
|
338
328
|
/** Emitted when Homebridge is shutting down.
|
|
339
329
|
*
|
|
340
|
-
* On receiving this event, the
|
|
341
|
-
* flush peristent storage, ...).
|
|
330
|
+
* On receiving this event, the accessory delegate should cleanup
|
|
331
|
+
* (close connections, flush peristent storage, ...).
|
|
342
332
|
* @event AccessoryDelegate#shutdown
|
|
343
333
|
*/
|
|
344
334
|
this._accessoryDelegates[id].emit('shutdown')
|
|
335
|
+
if (typeof this._accessoryDelegates[id].shutdown === 'function') {
|
|
336
|
+
jobs.push(this._accessoryDelegates[id].shutdown())
|
|
337
|
+
}
|
|
345
338
|
}
|
|
346
339
|
/** Emitted when Homebridge is shutting down.
|
|
347
340
|
*
|
|
348
|
-
* On receiving this event, the plugin should cleanup
|
|
349
|
-
* flush peristent storage, ...).
|
|
341
|
+
* On receiving this event, the plugin should cleanup
|
|
342
|
+
* (close connections, flush peristent storage, ...).
|
|
350
343
|
* @event Platform#shutdown
|
|
351
344
|
*/
|
|
352
345
|
this.emit('shutdown')
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
this.emit('exit')
|
|
346
|
+
if (typeof this.shutdown === 'function') {
|
|
347
|
+
jobs.push(this.shutdown())
|
|
348
|
+
}
|
|
349
|
+
for (const job of jobs) {
|
|
350
|
+
try {
|
|
351
|
+
await job
|
|
352
|
+
} catch (error) { this.warn(error) }
|
|
353
|
+
}
|
|
354
|
+
this.#flushCachedAccessories()
|
|
363
355
|
}
|
|
364
356
|
|
|
365
357
|
// Issue an identity message.
|
|
366
|
-
|
|
358
|
+
#identify () {
|
|
367
359
|
this.log(
|
|
368
360
|
'%s v%s, node v%s, homebridge v%s, %s v%s',
|
|
369
361
|
this._pluginName, this._pluginVersion, context.nodeVersion,
|
|
@@ -388,7 +380,7 @@ class Platform extends Delegate {
|
|
|
388
380
|
}
|
|
389
381
|
|
|
390
382
|
// Check the NPM registry for the latest version of this plugin.
|
|
391
|
-
async
|
|
383
|
+
async #checkLatest (name, version) {
|
|
392
384
|
try {
|
|
393
385
|
if (this.npmRegistry == null) {
|
|
394
386
|
this.npmRegistry = new HttpClient({
|
|
@@ -534,60 +526,6 @@ class Platform extends Delegate {
|
|
|
534
526
|
delete this._accessories[id]
|
|
535
527
|
}
|
|
536
528
|
|
|
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
529
|
// ===== Dynamic Configuration through Homebridge UI =========================
|
|
592
530
|
|
|
593
531
|
/** Handler for requests from the Homebridge Plugin UI Server.
|
|
@@ -601,7 +539,8 @@ class Platform extends Delegate {
|
|
|
601
539
|
*/
|
|
602
540
|
|
|
603
541
|
// Create HTTP server for Homebridge Plugin UI Settings.
|
|
604
|
-
async
|
|
542
|
+
async #createUiServer () {
|
|
543
|
+
const { Server, STATUS_CODES } = await import('node:http')
|
|
605
544
|
this._ui = {}
|
|
606
545
|
this._ui.server = new Server()
|
|
607
546
|
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.
|
|
9
|
+
"version": "8.1.0",
|
|
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.1",
|
|
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.5"
|
|
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'
|