homebridge-lib 5.1.24-3 → 5.2.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/index.js CHANGED
@@ -175,6 +175,13 @@ class homebridgeLib {
175
175
  */
176
176
  static get Platform () { return require('./lib/Platform') }
177
177
 
178
+ /** Delegate of a property of a HomeKit accessory or service.
179
+ * <br>See {@link PropertyDelegate}.
180
+ * @type {Class}
181
+ * @memberof module:homebridgeLib
182
+ */
183
+ static get PropertyDelegate () { return require('./lib/PropertyDelegate') }
184
+
178
185
  /** Delegate of a HomeKit service.
179
186
  * <br>See {@link ServiceDelegate}.
180
187
  * @type {Class}
@@ -7,7 +7,6 @@
7
7
 
8
8
  const homebridgeLib = require('../index')
9
9
 
10
- const maxLogLevel = 4
11
10
  const startsWithUuid = /^[0-9A-F]{8}-[0-9A-F]{4}-[1-5][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/
12
11
 
13
12
  /** Delegate of a HomeKit accessory.
@@ -35,8 +34,6 @@ class AccessoryDelegate extends homebridgeLib.Delegate {
35
34
  * @param {!string} params.firmware - The accessory firmware revision.
36
35
  * @param {?string} params.hardware - The accessory hardware revision.
37
36
  * @param {?string} params.software - The accessory software revision.
38
- * @param {?boolean} [params.inheritLogLevel] - Inherit `logLevel` from
39
- * `platform` instead of maintaining it oneself.
40
37
  */
41
38
  constructor (platform, params = {}) {
42
39
  if (params.name == null) {
@@ -50,19 +47,25 @@ class AccessoryDelegate extends homebridgeLib.Delegate {
50
47
  throw new RangeError('params.id: invalid id')
51
48
  }
52
49
 
53
- if (params.inheritLogLevel != null) {
54
- if (params.inheritLogLevel) {
55
- Object.defineProperty(this, 'logLevel', {
56
- get () { return platform.logLevel }
57
- })
58
- }
59
- delete params.inheritLogLevel
60
- }
61
-
62
50
  // Link or create associated PlatformAccessory.
63
51
  this._accessory = this._platform._getAccessory(this, params)
52
+
53
+ if (params.logLevel != null) {
54
+ this.warn('params.logLevel: deprecated')
55
+ }
56
+ if (params.inheritLogLevel != null) {
57
+ this.warn('params.inheritLogLevel: deprecated')
58
+ }
64
59
  this._context = this._accessory.context
65
60
 
61
+ delete this._context.logLevel
62
+
63
+ // Setup shortcut for property values and values of the characteristics
64
+ // of the _Accessory Information_ service.
65
+ this._values = {} // by key
66
+
67
+ this._propertyDelegates = {}
68
+
66
69
  // Create delegate for AccessoryInformation service.
67
70
  this._serviceDelegates = {}
68
71
  this._accessoryInformationDelegate =
@@ -135,16 +138,88 @@ class AccessoryDelegate extends homebridgeLib.Delegate {
135
138
  }
136
139
  }
137
140
 
141
+ /** Creates a new {@link PropertyDelegate} instance, for a property of the
142
+ * associated HomeKit accessory.
143
+ *
144
+ * The property value is accessed through
145
+ * {@link AccessoryDelegate#values values}.
146
+ * The delegate is returned, but can also be accessed through
147
+ * {@link AccessoryDelegate#propertyDelegate propertyDelegate()}.
148
+ * @param {!object} params - Parameters of the property delegate.
149
+ * @param {!string} params.key - The key for the property delegate.<br>
150
+ * Needs to be unique with parent delegate.
151
+ // * @param {!type} params.type - The type of the property value.
152
+ * @param {?*} params.value - The initial value of the property.<br>
153
+ * Only used when the property delegate is created for the first time.
154
+ * Otherwise, the value is restored from persistent storage.
155
+ * @param {?boolean} params.logLevel - Level for homebridge log messages
156
+ * when property was set or has been changed.
157
+ * @param {?string} params.unit - The unit of the value of the property.
158
+ * @throws {TypeError} When a parameter has an invalid type.
159
+ * @throws {RangeError} When a parameter has an invalid value.
160
+ * @throws {SyntaxError} When a mandatory parameter is missing or an
161
+ * optional parameter is not applicable.
162
+ * @returns {PropertyDelegate}
163
+ * @throws {TypeError} When a parameter has an invalid type.
164
+ * @throws {RangeError} When a parameter has an invalid value.
165
+ * @throws {SyntaxError} When a mandatory parameter is missing or an
166
+ * optional parameter is not applicable.
167
+ */
168
+ addPropertyDelegate (params = {}) {
169
+ if (typeof params.key !== 'string') {
170
+ throw new TypeError(`params.key: ${params.key}: invalid key`)
171
+ }
172
+ if (params.key === '') {
173
+ throw new RangeError(`params.key: ${params.key}: invalid key`)
174
+ }
175
+ const key = params.key
176
+ if (this.values[key] !== undefined) {
177
+ throw new SyntaxError(`${key}: duplicate key`)
178
+ }
179
+
180
+ const delegate = new homebridgeLib.PropertyDelegate(this, params)
181
+ this._propertyDelegates[key] = delegate
182
+
183
+ // Create shortcut for characteristic value.
184
+ Object.defineProperty(this.values, key, {
185
+ configurable: true, // make sure we can delete it again
186
+ writeable: true,
187
+ get () { return delegate.value },
188
+ set (value) { delegate.value = value }
189
+ })
190
+
191
+ return delegate
192
+ }
193
+
194
+ removePropertyDelegate (key) {
195
+ if (this._accessoryInformationDelegate.values[key] != null) {
196
+ throw new RangeError('%s: invalid key')
197
+ }
198
+ delete this.values[key]
199
+ const delegate = this._propertyDelegates[key]
200
+ delegate._destroy()
201
+ delete this._propertyDelegates[key]
202
+ delete this._context[key]
203
+ }
204
+
205
+ /** Returns the property delegate correspondig to the property key.
206
+ * @param {!string} key - The key for the property.
207
+ * returns {PropertyDelegate}
208
+ */
209
+ propertyDelegate (key) {
210
+ return this._propertyDelegates[key]
211
+ }
212
+
138
213
  /** Values of the HomeKit characteristics for the `AccessoryInformation` service.
139
214
  *
140
- * Contains the key of each characteristic in
215
+ * Contains the key of each property and of each characteristic in
141
216
  * {@link ServiceDelegate.AccessoryInformation AccessoryInformation}.
142
217
  * When the value is written, the value of the corresponding HomeKit
143
218
  * characteristic is updated.
144
219
  * @type {object}
145
220
  */
146
221
  get values () {
147
- return this._accessoryInformationDelegate._values
222
+ return this._values
148
223
  }
149
224
 
150
225
  /** Enable `heartbeat` events for this accessory delegate.
@@ -189,16 +264,7 @@ class AccessoryDelegate extends homebridgeLib.Delegate {
189
264
  * @type {!integer}
190
265
  */
191
266
  get logLevel () {
192
- return this._context.logLevel
193
- }
194
-
195
- set logLevel (value) {
196
- const oldLogLevel = this._context.logLevel
197
- this._context.logLevel = Math.max(0, Math.min(value, maxLogLevel))
198
- this._platform._message(
199
- 'log', 2, this.name + ': ',
200
- 'set loglevel from %d to %d', oldLogLevel, this.logLevel
201
- )
267
+ return this.platform.logLevel
202
268
  }
203
269
 
204
270
  /** Inherit `logLevel` from another accessory delegate.
@@ -217,6 +283,19 @@ class AccessoryDelegate extends homebridgeLib.Delegate {
217
283
  })
218
284
  }
219
285
 
286
+ /** Manage `logLevel` from characteristic delegate.
287
+ * @param {CharacteristicDelegate} delegate - The delegate of the `logLevel`
288
+ * characteristic.
289
+ */
290
+ manageLogLevel (delegate) {
291
+ if (!(delegate instanceof homebridgeLib.CharacteristicDelegate)) {
292
+ throw new TypeError('delegate: not an CharacteristicDelegate')
293
+ }
294
+ Object.defineProperty(this, 'logLevel', {
295
+ get () { return delegate.value }
296
+ })
297
+ }
298
+
220
299
  // Called by homebridge when Identify is selected.
221
300
  _identify () {
222
301
  this.emit('identify')
@@ -5,6 +5,8 @@
5
5
 
6
6
  'use strict'
7
7
 
8
+ const homebridgeLib = require('../index')
9
+
8
10
  /* global BigInt */
9
11
 
10
12
  const epoch = (new Date('2001-01-01T00:00:00Z')).valueOf()
@@ -170,8 +172,12 @@ class AdaptiveLighting {
170
172
  * @param {integer} ct - The IID of the _Color Temperature_ characteristic.
171
173
  */
172
174
  constructor (bri, ct) {
173
- this.bri = bri
174
- this.ct = ct
175
+ this.bri = bri instanceof homebridgeLib.CharacteristicDelegate
176
+ ? bri._characteristic.iid
177
+ : bri
178
+ this.ct = ct instanceof homebridgeLib.CharacteristicDelegate
179
+ ? ct._characteristic.iid
180
+ : ct
175
181
  this._active = false
176
182
  }
177
183
 
@@ -200,6 +200,10 @@ class CharacteristicDelegate extends homebridgeLib.Delegate {
200
200
  return this._hasPerm(this.Characteristic.Perms.NOTIFY)
201
201
  }
202
202
 
203
+ get _writeResponse () {
204
+ return this._hasPerm(this.Characteristic.Perms.WRITE_RESPONSE)
205
+ }
206
+
203
207
  get _writeOnly () {
204
208
  return this._canWrite && !this._canRead && !this._canNotifiy
205
209
  }
@@ -375,6 +379,7 @@ class CharacteristicDelegate extends homebridgeLib.Delegate {
375
379
  // Called when characteristic is updated from HomeKit.
376
380
  async _onSet (v, callback) {
377
381
  const { value } = this.validate(v)
382
+ let result
378
383
 
379
384
  // Issue info message that Characteristic value was updated from HomeKit.
380
385
  if (this._writeOnly || this.value == null) {
@@ -407,7 +412,7 @@ class CharacteristicDelegate extends homebridgeLib.Delegate {
407
412
  callback(new Error('timed out'))
408
413
  }, this._timeout)
409
414
  try {
410
- await this._setter(value)
415
+ result = await this._setter(value)
411
416
  } catch (error) {
412
417
  clearTimeout(timeout)
413
418
  this.error(error)
@@ -423,7 +428,11 @@ class CharacteristicDelegate extends homebridgeLib.Delegate {
423
428
  }
424
429
 
425
430
  // Return status to HomeKit.
426
- callback()
431
+ if (this._writeResponse) {
432
+ callback(null, result)
433
+ } else {
434
+ callback()
435
+ }
427
436
 
428
437
  // Update persisted value in ~/.homebridge/accessories/cachedAccessories.
429
438
  this._serviceDelegate._context[this._key] = value
@@ -188,10 +188,11 @@ class EveHomeKitTypes extends homebridgeLib.CustomHomeKitTypes {
188
188
  }, 'Current Consumption')
189
189
 
190
190
  this.createCharacteristicClass('AirPressure', uuid('10F'), {
191
- format: this.Formats.UINT16,
191
+ format: this.Formats.FLOAT,
192
192
  unit: 'hPa',
193
193
  minValue: 700,
194
194
  maxValue: 1100,
195
+ minStep: 0.1,
195
196
  perms: [this.Perms.READ, this.Perms.NOTIFY]
196
197
  }, 'Air Pressure')
197
198
 
@@ -83,8 +83,16 @@ class MyHomeKitTypes extends homebridgeLib.CustomHomeKitTypes {
83
83
  * <br>Used by: homebridge-hue.
84
84
  * @property {Class} Enabled - Enable/disable service.
85
85
  * <br>Used by: homebridge-hue.
86
- * @property {Class} Expose - Expose to HomeKit.
87
- * <br>Used by: homebridge-deconz.
86
+ * @property {Class} Expose - Expose device to HomeKit.
87
+ * <br>Used by: homebridge-deconz, homebridge-hue2.
88
+ * @property {Class} ExposeGroups - Expose groups to HomeKit.
89
+ * <br>Used by: homebridge-deconz, homebridge-hue2.
90
+ * @property {Class} ExposeLights - Expose lights devices to HomeKit.
91
+ * <br>Used by: homebridge-deconz, homebridge-hue2.
92
+ * @property {Class} ExposeSchedules - Expose schdules to HomeKit.
93
+ * <br>Used by: homebridge-deconz, homebridge-hue2.
94
+ * @property {Class} ExposeSensors - Expose sensors devices to HomeKit.
95
+ * <br>Used by: homebridge-deconz, homebridge-hue2.
88
96
  * @property {Class} Heartrate - Refresh rate.
89
97
  * <br>Used by: homebridge-hue in HueBridge service,
90
98
  * by Homebridge-soma, by Homebridge-rpi, by Homebridge-ws.
@@ -581,7 +589,7 @@ class MyHomeKitTypes extends homebridgeLib.CustomHomeKitTypes {
581
589
  this.createCharacteristicClass('LogLevel', uuid('065'), {
582
590
  format: this.Formats.UINT8,
583
591
  minValue: 0,
584
- maxValue: 3,
592
+ maxValue: 3, // 4 for homebridge-zp
585
593
  minStep: 1, // Force Down|Up control in Eve
586
594
  perms: [this.Perms.READ, this.Perms.NOTIFY, this.Perms.WRITE],
587
595
  adminOnlyAccess: [this.Access.WRITE]
@@ -590,6 +598,7 @@ class MyHomeKitTypes extends homebridgeLib.CustomHomeKitTypes {
590
598
  this.Characteristics.LogLevel.LOG = 1
591
599
  this.Characteristics.LogLevel.DEBUG = 2
592
600
  this.Characteristics.LogLevel.VDEBUG = 3
601
+ this.Characteristics.LogLevel.VVDEBUG = 4
593
602
 
594
603
  this.createCharacteristicClass('Repeat', uuid('066'), {
595
604
  format: this.Formats.UINT8,
@@ -714,6 +723,30 @@ class MyHomeKitTypes extends homebridgeLib.CustomHomeKitTypes {
714
723
  adminOnlyAccess: [this.Access.WRITE]
715
724
  })
716
725
 
726
+ this.createCharacteristicClass('ExposeLights', uuid('079'), {
727
+ format: this.Formats.BOOL,
728
+ perms: [this.Perms.READ, this.Perms.NOTIFY, this.Perms.WRITE],
729
+ adminOnlyAccess: [this.Access.WRITE]
730
+ }, 'Expose Lights')
731
+
732
+ this.createCharacteristicClass('ExposeSensors', uuid('07A'), {
733
+ format: this.Formats.BOOL,
734
+ perms: [this.Perms.READ, this.Perms.NOTIFY, this.Perms.WRITE],
735
+ adminOnlyAccess: [this.Access.WRITE]
736
+ }, 'Expose Sensors')
737
+
738
+ this.createCharacteristicClass('ExposeGroups', uuid('07B'), {
739
+ format: this.Formats.BOOL,
740
+ perms: [this.Perms.READ, this.Perms.NOTIFY, this.Perms.WRITE],
741
+ adminOnlyAccess: [this.Access.WRITE]
742
+ }, 'Expose Groups')
743
+
744
+ this.createCharacteristicClass('ExposeSchedules', uuid('07C'), {
745
+ format: this.Formats.BOOL,
746
+ perms: [this.Perms.READ, this.Perms.NOTIFY, this.Perms.WRITE],
747
+ adminOnlyAccess: [this.Access.WRITE]
748
+ }, 'Expose Schedules')
749
+
717
750
  // Characteristic for Unique ID, used by homebridge-hue.
718
751
  // Source: as exposed by the Philips Hue bridge. This characteristic is
719
752
  // used by the Hue app to select the accessories when syncing Hue bridge
@@ -782,13 +815,17 @@ class MyHomeKitTypes extends homebridgeLib.CustomHomeKitTypes {
782
815
  ])
783
816
 
784
817
  this.createServiceClass('DeconzGateway', uuid('014'), [
785
- this.Characteristics.LogLevel,
786
- this.Characteristics.Expose,
787
818
  this.Characteristics.Heartrate,
788
819
  this.Characteristics.LastUpdated,
820
+ this.Characteristics.TransitionTime,
821
+ this.Characteristics.Expose,
822
+ this.Characteristics.ExposeLights,
823
+ this.Characteristics.ExposeSensors,
824
+ this.Characteristics.ExposeGroups,
825
+ this.Characteristics.ExposeSchedules,
826
+ this.Characteristics.LogLevel,
789
827
  this.Characteristics.Restart,
790
828
  this.Characteristics.Search,
791
- this.Characteristics.TransitionTime,
792
829
  this.Characteristics.Unlock
793
830
  ])
794
831
 
package/lib/Platform.js CHANGED
@@ -27,8 +27,6 @@ const context = {
27
27
  checkInterval: 7 * 24 * 3600
28
28
  }
29
29
 
30
- const globalKey = context.libName // + '@' + context.libVersion
31
-
32
30
  /** Homebridge dynamic platform plugin.
33
31
  *
34
32
  * `Platform` provides the following features to a platform plugin:
@@ -59,6 +57,8 @@ class Platform extends homebridgeLib.Delegate {
59
57
  static loadPlatform (homebridge, packageJson, platformName, Platform) {
60
58
  if (context.homebridge == null) {
61
59
  context.homebridge = homebridge
60
+ context.packageJson = packageJson
61
+ context.platformName = platformName
62
62
  context.homebridgeVersion = homebridge.serverVersion
63
63
  context.PlatformAccessory = homebridge.platformAccessory
64
64
  const hap = {
@@ -98,25 +98,6 @@ class Platform extends homebridgeLib.Delegate {
98
98
  my: Object.freeze(my.Characteristics)
99
99
  })
100
100
  }
101
- if (global[globalKey] == null) {
102
- global[globalKey] = {
103
- Platform: {
104
- platformName: context.libName,
105
- packageJson: libPackageJson,
106
- UpnpClient: homebridgeLib.UpnpClient
107
- }
108
- }
109
- homebridge.registerPlatform(
110
- // libPackageJson.name, 'Lib', homebridgeLib.Platform, false
111
- packageJson.name, 'Lib', homebridgeLib.Platform, false
112
- )
113
- // } else {
114
- // TODO: check compatible homebridge-lib version.
115
- }
116
- global[globalKey][Platform.name] = {
117
- platformName: platformName,
118
- packageJson: packageJson
119
- }
120
101
  homebridge.registerPlatform(
121
102
  packageJson.name, platformName, Platform, true
122
103
  )
@@ -148,50 +129,19 @@ class Platform extends homebridgeLib.Delegate {
148
129
  this._log = log
149
130
  this._configJson = configJson
150
131
  this._homebridge = homebridge
151
- const myContext = global[globalKey][this.className]
152
- this._platformName = myContext.platformName
153
- this._pluginName = myContext.packageJson.name
154
- this._pluginVersion = myContext.packageJson.version
155
- if (myContext.packageJson.name === context.libName) {
156
- this._isHomebridgeLib = true
157
- myContext.heartbeat = this
158
- // Delay start of Homebridge's HAP server until all plugins have
159
- // initialised.
160
- this.accessories = (f) => {
161
- this.on('initialised', () => { f([]) })
162
- }
163
- // this.debug('Categories: %j', this.Accessory.Categories)
164
- // for (const type of ['Access', 'Formats', 'Perms', 'Units']) {
165
- // this.debug('%s: %j', type, this.Characteristic[type])
166
- // }
167
- // for (const module of ['hap', 'eve', 'my']) {
168
- // this.debug('Services.%s: %j', module, Object.keys(this.Services[module]))
169
- // this.debug('Characteristics.%s: %j', module, Object.keys(this.Characteristics[module]))
170
- // }
171
- } else {
172
- /** Configure an accessory, after it has been restored from peristent
173
- * storage.
174
- *
175
- * Called by homebridge when restoring peristed accessories, typically from
176
- * `~/.homebridge/accessories/cachedAccessories`.
177
- * @method
178
- * @param {!PlatformAccessory} accessory - The restored Homebridge
179
- * [PlatformAccessory](https://github.com/nfarina/homebridge/blob/master/lib/platformAccessory.js).
180
- */
181
- this.configureAccessory = this._configureAccessory
182
- }
132
+ this._platformName = context.platformName
133
+ this._pluginName = context.packageJson.name
134
+ this._pluginVersion = context.packageJson.version
135
+
183
136
  this._accessories = {}
184
137
  this._accessoryDelegates = {}
185
138
 
186
- if (myContext.platform != null) {
139
+ if (context.platform != null) {
187
140
  this.fatal(
188
- 'config.json: duplicate entry for %s platform', myContext.platformName
141
+ 'config.json: duplicate entry for %s platform', context.platformName
189
142
  )
190
143
  }
191
- myContext.platform = this
192
- if (global[globalKey].Platform == null) {
193
- this.fatal('%s platform not registered', context.libName)
194
- }
144
+ context.platform = this
195
145
  this._identify()
196
146
 
197
147
  this._homebridge
@@ -205,54 +155,30 @@ class Platform extends homebridgeLib.Delegate {
205
155
  // Main platform function.
206
156
  // Called by homebridge after restoring accessories from cache.
207
157
  async _main () {
208
- if (this._isHomebridgeLib || global[globalKey].Platform.heartbeat == null) {
209
- // process.on('unhandledRejection', (error) => {
210
- // this.error('unhandled rejection: %s', error.stack)
211
- // })
212
- global[globalKey].Platform.heartbeat = this
213
- /** System information.
214
- * @type {SystemInfo}
215
- * @readonly
216
- */
217
- this.systemInfo = new homebridgeLib.SystemInfo()
218
- this.systemInfo
219
- .on('error', (error) => { this.warn(error) })
220
- .on('exec', (command) => { this.debug('exec: %s', command) })
221
- .on('readFile', (filename) => { this.debug('read file: %s', filename) })
222
- await this.systemInfo.init()
223
- this.log('hardware: %s', this.systemInfo.hwInfo.prettyName)
224
- this.log('os: %s', this.systemInfo.osInfo.prettyName)
225
- this.heartbeatClients = Object.keys(global[globalKey]).filter((key) => {
226
- return global[globalKey][key].platform != null
227
- })
228
- this.debug('starting heartbeat for %j', this.heartbeatClients)
229
- this._heartbeatStart = new Date()
230
- setTimeout(() => { this._beat(-1) }, 1000)
231
- this.on('exit', () => {
232
- this.debug('flush cachedAccessories')
233
- this._homebridge.updatePlatformAccessories()
234
- this.log('goodbye')
235
- })
236
- } else {
237
- this.systemInfo = global[globalKey].Platform.heartbeat.systemInfo
238
- }
239
- if (this._isHomebridgeLib) {
240
- const jobs = []
241
- for (const plugin in global[globalKey]) {
242
- const platform = global[globalKey][plugin].platform
243
- if (
244
- platform != null && platform !== this &&
245
- Object.keys(platform._accessories).length === 0
246
- ) {
247
- this.warn('waiting on %s', plugin)
248
- jobs.push(events.once(platform, 'initialised'))
249
- }
250
- }
251
- for (const job of jobs) {
252
- await job
253
- }
254
- this.emit('initialised')
255
- }
158
+ // process.on('unhandledRejection', (error) => {
159
+ // this.error('unhandled rejection: %s', error.stack)
160
+ // })
161
+ /** System information.
162
+ * @type {SystemInfo}
163
+ * @readonly
164
+ */
165
+ this.systemInfo = new homebridgeLib.SystemInfo()
166
+ this.systemInfo
167
+ .on('error', (error) => { this.warn(error) })
168
+ .on('exec', (command) => { this.debug('exec: %s', command) })
169
+ .on('readFile', (filename) => { this.debug('read file: %s', filename) })
170
+ await this.systemInfo.init()
171
+ this.log('hardware: %s', this.systemInfo.hwInfo.prettyName)
172
+ this.log('os: %s', this.systemInfo.osInfo.prettyName)
173
+ this.debug('starting heartbeat for %j', this.heartbeatClients)
174
+ this._heartbeatStart = new Date()
175
+ setTimeout(() => { this._beat(-1) }, 1000)
176
+ this.on('exit', () => {
177
+ this.debug('flush cachedAccessories')
178
+ this._homebridge.updatePlatformAccessories()
179
+ this.log('goodbye')
180
+ })
181
+
256
182
  const n = Object.keys(this._accessories).length
257
183
  if (n > 0) {
258
184
  this.log('restored %d accessories from cache', n)
@@ -301,15 +227,11 @@ class Platform extends homebridgeLib.Delegate {
301
227
  this._homebridge.updatePlatformAccessories()
302
228
  }
303
229
 
304
- for (const plugin of this.heartbeatClients) {
305
- global[globalKey][plugin].platform._heartbeat(beat)
306
- }
307
- }
308
-
309
- _heartbeat (beat) {
310
230
  if (beat % context.checkInterval === 0) {
311
- this._checkLatest()
231
+ this._checkLatest(this._pluginName, this._pluginVersion)
232
+ this._checkLatest(context.libName, context.libVersion)
312
233
  }
234
+
313
235
  /** Emitted every second.
314
236
  * @event Platform#heartbeat
315
237
  * @param {number} beat - The sequence number of this heartbeat.
@@ -364,13 +286,10 @@ class Platform extends homebridgeLib.Delegate {
364
286
 
365
287
  // Issue an identity message.
366
288
  _identify () {
367
- const s = this._isHomebridgeLib
368
- ? ''
369
- : ', ' + context.libName + ' v' + context.libVersion
370
289
  this.log(
371
- '%s v%s, node v%s, homebridge v%s%s',
372
- this._pluginName, this._pluginVersion,
373
- context.nodeVersion, context.homebridgeVersion, s
290
+ '%s v%s, node v%s, homebridge v%s, %s v%s',
291
+ this._pluginName, this._pluginVersion, context.nodeVersion,
292
+ context.homebridgeVersion, context.libName, context.libVersion
374
293
  )
375
294
  if (context.nodeVersion !== context.recommendedNodeVersion) {
376
295
  this.warn(
@@ -391,16 +310,16 @@ class Platform extends homebridgeLib.Delegate {
391
310
  }
392
311
 
393
312
  // Check the NPM registry for the latest version of this plugin.
394
- async _checkLatest () {
313
+ async _checkLatest (name, version) {
395
314
  try {
396
- if (global[globalKey].Platform.npmRegistry == null) {
397
- const npmRegistry = new homebridgeLib.HttpClient({
315
+ if (this.npmRegistry == null) {
316
+ this.npmRegistry = new homebridgeLib.HttpClient({
398
317
  https: true,
399
318
  host: 'registry.npmjs.org',
400
319
  json: true,
401
320
  maxSockets: 1
402
321
  })
403
- npmRegistry
322
+ this.npmRegistry
404
323
  .on('error', (error) => {
405
324
  this.log(
406
325
  'npm registry: request %d: %s %s', error.request.id,
@@ -428,15 +347,14 @@ class Platform extends homebridgeLib.Delegate {
428
347
  response.statusCode, response.statusMessage
429
348
  )
430
349
  })
431
- global[globalKey].Platform.npmRegistry = npmRegistry
432
350
  }
433
- const { body } = await global[globalKey].Platform.npmRegistry.get(
434
- '/' + this._pluginName + '/latest', { Accept: 'application/json' })
351
+ const { body } = await this.npmRegistry.get(
352
+ '/' + name + '/latest', { Accept: 'application/json' })
435
353
  if (body != null && body.version != null) {
436
- if (body.version !== this._pluginVersion) {
437
- this.warn('latest version: %s v%s', this._pluginName, body.version)
354
+ if (body.version !== version) {
355
+ this.warn('latest version: %s v%s', name, body.version)
438
356
  } else {
439
- this.debug('latest version: %s v%s', this._pluginName, body.version)
357
+ this.debug('latest version: %s v%s', name, body.version)
440
358
  }
441
359
  }
442
360
  } catch (error) {
@@ -448,7 +366,16 @@ class Platform extends homebridgeLib.Delegate {
448
366
 
449
367
  // ===== Handle Accessories ==================================================
450
368
 
451
- _configureAccessory (accessory) {
369
+ /** Configure an accessory, after it has been restored from peristent
370
+ * storage.
371
+ *
372
+ * Called by homebridge when restoring peristed accessories, typically from
373
+ * `~/.homebridge/accessories/cachedAccessories`.
374
+ * @method
375
+ * @param {!PlatformAccessory} accessory - The restored Homebridge
376
+ * [PlatformAccessory](https://github.com/nfarina/homebridge/blob/master/lib/platformAccessory.js).
377
+ */
378
+ configureAccessory (accessory) {
452
379
  const className = accessory.context.className
453
380
  const version = accessory.context.version
454
381
  const id = accessory.context.id
@@ -584,7 +511,7 @@ class Platform extends homebridgeLib.Delegate {
584
511
  if (this._upnpMonitor != null) {
585
512
  throw new SyntaxError('upnpConfig(): already called')
586
513
  }
587
- this._upnpMonitor = new global[globalKey].Platform.UpnpClient(config)
514
+ this._upnpMonitor = new homebridgeLib.UpnpClient(config)
588
515
  this._upnpMonitor
589
516
  .on('error', (error) => {
590
517
  this.error('upnp: error')
@@ -0,0 +1,135 @@
1
+ // homebridge-lib/lib/PropertyDelegate.js
2
+ //
3
+ // Library for Homebridge plugins.
4
+ // Copyright © 2017-2022 Erik Baauw. All rights reserved.
5
+
6
+ 'use strict'
7
+
8
+ const homebridgeLib = require('../index')
9
+
10
+ /** Delegate of a property of a delegate of a HomeKit accessory or HomeKit
11
+ * service.
12
+ *
13
+ * A property delegate manages a property value that:
14
+ * - Is persisted across homebridge restarts;
15
+ * - Can be monitored through homebridge's log output;
16
+ * - Can be monitored programmatically, through
17
+ * {@link PropertyDelegate#event:didSet didSet} events.
18
+ *
19
+ * @extends Delegate
20
+ */
21
+ class PropertyDelegate extends homebridgeLib.Delegate {
22
+ /** Instantiate a property delegate.
23
+ *
24
+ * Note that instances are normally created by invoking
25
+ * {@link AccessoryDelegate#addPropertyDelegate addPropertyDelegate()} or
26
+ * {@link ServiceDelegate#addPropertyDelegate addPropertyDelegate()}.
27
+ * @param {!AccessoryDelegate|ServiceDelegate} delegate - Reference to the
28
+ * delegate of the corresponding HomeKit accessory or service.
29
+ * @param {!object} params - Parameters of the property delegate.
30
+ * @param {!string} params.key - The key for the property delegate.<br>
31
+ * Needs to be unique with parent delegate.
32
+ * @param {?boolean} params.silent - Suppress set log messages.
33
+ // * @param {!type} params.type - The type of the property value.
34
+ * @param {?*} params.value - The initial value of the property.<br>
35
+ * Only used when the property delegate is created for the first time.
36
+ * Otherwise, the value is restored from persistent storage.
37
+ * @param {?string} params.unit - The unit of the value of the property.
38
+ * @throws {TypeError} When a parameter has an invalid type.
39
+ * @throws {RangeError} When a parameter has an invalid value.
40
+ * @throws {SyntaxError} When a mandatory parameter is missing or an
41
+ * optional parameter is not applicable.
42
+ */
43
+ constructor (parent, params = {}) {
44
+ if (!(
45
+ parent instanceof homebridgeLib.AccessoryDelegate ||
46
+ parent instanceof homebridgeLib.ServiceDelegate
47
+ )) {
48
+ throw new TypeError('parent: not an AccessoryDelegate')
49
+ }
50
+ super(parent.platform, parent.name + ': ' + params.key)
51
+ if (typeof params.key !== 'string') {
52
+ throw new TypeError('params.key: not a string')
53
+ }
54
+ this._parent = parent
55
+ this._key = params.key
56
+ this._log = params.silent ? this.debug : this.log
57
+ // this._type = params.type
58
+ this._unit = (params.unit != null) ? params.unit : ''
59
+
60
+ // Set initial value.
61
+ if (this.value == null && params.value != null) {
62
+ this.value = params.value
63
+ }
64
+
65
+ this.vdebug('created')
66
+ }
67
+
68
+ /** Destroy the propery delegate.
69
+ */
70
+ _destroy () {
71
+ this.vdebug('destroy')
72
+ this.removeAllListeners()
73
+ delete this._parent._context[this.key]
74
+ }
75
+
76
+ /** Current log level (of the associated accessory or service delegate).
77
+ *
78
+ * The log level determines what type of messages are printed:
79
+ *
80
+ * 0. Print error and warning messages.
81
+ * 1. Print error, warning, and log messages.
82
+ * 2. Print error, warning, log, and debug messages.
83
+ * 3. Print error, warning, log, debug, and verbose debug messages.
84
+ *
85
+ * Note that debug messages (level 2 and 3) are only printed when
86
+ * Homebridge was started with the `-D` or `--debug` command line option.
87
+ *
88
+ * @type {!integer}
89
+ * @readonly
90
+ */
91
+ get logLevel () {
92
+ return this._parent.logLevel
93
+ }
94
+
95
+ validate (value) {
96
+ // Todo: check value against type.
97
+ return { value: value, s: '' }
98
+ }
99
+
100
+ /** Value of associated Characteristic.
101
+ */
102
+ get value () {
103
+ return this._parent._context[this._key]
104
+ }
105
+
106
+ set value (v) {
107
+ const { value, s } = this.validate(v)
108
+
109
+ // Check for actual change.
110
+ if (value === this.value) {
111
+ return
112
+ }
113
+
114
+ // Issue info message that property value has been set.
115
+ if (this.value == null) {
116
+ this._log('set to %j%s%s', value, this._unit, s)
117
+ } else {
118
+ this._log(
119
+ 'set to %j%s%s (from %j%s)', value, this._unit, s,
120
+ this.value, this._unit
121
+ )
122
+ }
123
+
124
+ // Update persisted value in ~/.homebridge/accessories/cachedAccessories.
125
+ this._parent._context[this._key] = value
126
+
127
+ /** Emitted when property value has changed.
128
+ * @event PropertyDelegate#didSet
129
+ * @param {*} value - The new property value.
130
+ */
131
+ this.emit('didSet', value)
132
+ }
133
+ }
134
+
135
+ module.exports = PropertyDelegate
@@ -416,6 +416,22 @@ class AccessoryInformation extends ServiceDelegate {
416
416
  })
417
417
  }
418
418
  }
419
+
420
+ addCharacteristicDelegate (params = {}) {
421
+ const delegate = super.addCharacteristicDelegate(params)
422
+ Object.defineProperty(this.accessoryDelegate.values, params.key, {
423
+ configurable: true, // make sure we can delete it again
424
+ writeable: true,
425
+ get () { return delegate.value },
426
+ set (value) { delegate.value = value }
427
+ })
428
+ return delegate
429
+ }
430
+
431
+ removeCharacteristicDelegate (key) {
432
+ delete this.accessoryDelegate.values[key]
433
+ super.removeCharacteristicDelegate(key)
434
+ }
419
435
  }
420
436
 
421
437
  /** Class for a _Battery_ service delegate.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Library for homebridge plugins",
4
4
  "author": "Erik Baauw",
5
5
  "license": "Apache-2.0",
6
- "version": "5.1.24-3",
6
+ "version": "5.2.0",
7
7
  "keywords": [
8
8
  "homekit",
9
9
  "homebridge"
@@ -21,8 +21,8 @@
21
21
  "upnp": "cli/upnp.js"
22
22
  },
23
23
  "engines": {
24
- "homebridge": "^1.3.9",
25
- "node": "^16.13.1"
24
+ "homebridge": "^1.4.0",
25
+ "node": "^16.13.2"
26
26
  },
27
27
  "dependencies": {
28
28
  "bonjour-hap": "^3.6.3",