@homebridge-plugins/homebridge-meross 10.8.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +1346 -0
  2. package/LICENSE +21 -0
  3. package/README.md +68 -0
  4. package/config.schema.json +2066 -0
  5. package/eslint.config.js +49 -0
  6. package/lib/connection/http.js +345 -0
  7. package/lib/connection/mqtt.js +174 -0
  8. package/lib/device/baby.js +532 -0
  9. package/lib/device/cooler-single.js +447 -0
  10. package/lib/device/diffuser.js +730 -0
  11. package/lib/device/fan.js +530 -0
  12. package/lib/device/garage-main.js +225 -0
  13. package/lib/device/garage-single.js +495 -0
  14. package/lib/device/garage-sub.js +376 -0
  15. package/lib/device/heater-single.js +445 -0
  16. package/lib/device/hub-contact.js +56 -0
  17. package/lib/device/hub-leak.js +86 -0
  18. package/lib/device/hub-main.js +403 -0
  19. package/lib/device/hub-sensor.js +115 -0
  20. package/lib/device/hub-smoke.js +40 -0
  21. package/lib/device/hub-valve.js +377 -0
  22. package/lib/device/humidifier.js +521 -0
  23. package/lib/device/index.js +63 -0
  24. package/lib/device/light-cct.js +474 -0
  25. package/lib/device/light-dimmer.js +312 -0
  26. package/lib/device/light-rgb.js +528 -0
  27. package/lib/device/outlet-multi.js +383 -0
  28. package/lib/device/outlet-single.js +405 -0
  29. package/lib/device/power-strip.js +282 -0
  30. package/lib/device/purifier-single.js +372 -0
  31. package/lib/device/purifier.js +403 -0
  32. package/lib/device/roller-location.js +317 -0
  33. package/lib/device/roller.js +234 -0
  34. package/lib/device/sensor-presence.js +201 -0
  35. package/lib/device/switch-multi.js +403 -0
  36. package/lib/device/switch-single.js +371 -0
  37. package/lib/device/template.js +177 -0
  38. package/lib/device/thermostat.js +493 -0
  39. package/lib/fakegato/LICENSE +21 -0
  40. package/lib/fakegato/fakegato-history.js +814 -0
  41. package/lib/fakegato/fakegato-storage.js +108 -0
  42. package/lib/fakegato/fakegato-timer.js +125 -0
  43. package/lib/fakegato/uuid.js +27 -0
  44. package/lib/homebridge-ui/public/index.html +316 -0
  45. package/lib/homebridge-ui/server.js +10 -0
  46. package/lib/index.js +8 -0
  47. package/lib/platform.js +1256 -0
  48. package/lib/utils/colour.js +581 -0
  49. package/lib/utils/constants.js +377 -0
  50. package/lib/utils/custom-chars.js +165 -0
  51. package/lib/utils/eve-chars.js +130 -0
  52. package/lib/utils/functions.js +39 -0
  53. package/lib/utils/lang-en.js +114 -0
  54. package/package.json +70 -0
@@ -0,0 +1,403 @@
1
+ import PQueue from 'p-queue'
2
+ import { TimeoutError } from 'p-timeout'
3
+
4
+ import mqttClient from '../connection/mqtt.js'
5
+ import platformConsts from '../utils/constants.js'
6
+ import { hasProperty, parseError } from '../utils/functions.js'
7
+ import platformLang from '../utils/lang-en.js'
8
+
9
+ export default class {
10
+ constructor(platform, accessory) {
11
+ // Set up variables from the platform
12
+ this.devicesInHB = platform.devicesInHB
13
+ this.hapChar = platform.api.hap.Characteristic
14
+ this.hapErr = platform.api.hap.HapStatusError
15
+ this.hapServ = platform.api.hap.Service
16
+ this.platform = platform
17
+
18
+ // Set up variables from the accessory
19
+ this.accessory = accessory
20
+ this.mtsList = []
21
+ this.name = accessory.displayName
22
+ const cloudRefreshRate = hasProperty(platform.config, 'cloudRefreshRate')
23
+ ? platform.config.cloudRefreshRate
24
+ : platformConsts.defaultValues.cloudRefreshRate
25
+ const localRefreshRate = hasProperty(platform.config, 'refreshRate')
26
+ ? platform.config.refreshRate
27
+ : platformConsts.defaultValues.refreshRate
28
+ this.pollInterval = accessory.context.connection === 'local'
29
+ ? localRefreshRate
30
+ : cloudRefreshRate
31
+
32
+ // Not sure how realtime mqtt updates work with this device, so force enable cloud polling
33
+ if (this.pollInterval === 0) {
34
+ this.pollInterval = 30000
35
+ }
36
+
37
+ // Create the queue used for sending device requests
38
+ this.updateInProgress = false
39
+ this.queue = new PQueue({
40
+ concurrency: 1,
41
+ interval: 250,
42
+ intervalCap: 1,
43
+ timeout: 10000,
44
+ throwOnTimeout: true,
45
+ })
46
+ this.queue.on('idle', () => {
47
+ this.updateInProgress = false
48
+ })
49
+
50
+ // Set up the mqtt client for cloud devices to send and receive device updates
51
+ if (accessory.context.connection !== 'local') {
52
+ this.accessory.mqtt = new mqttClient(platform, this.accessory)
53
+ this.accessory.mqtt.connect()
54
+ }
55
+
56
+ // Always request a device update on startup, then start the interval for polling
57
+ setTimeout(() => this.requestUpdate(true), 5000)
58
+ this.accessory.refreshInterval = setInterval(
59
+ () => this.requestUpdate(),
60
+ this.pollInterval * 1000,
61
+ )
62
+ }
63
+
64
+ async requestUpdate(firstRun = false) {
65
+ try {
66
+ // Don't continue if an update is currently being sent to the device
67
+ if (this.updateInProgress) {
68
+ return
69
+ }
70
+
71
+ // Add the request to the queue so updates are sent apart
72
+ await this.queue.add(async () => {
73
+ // This flag stops the plugin from requesting updates while pending on others
74
+ this.updateInProgress = true
75
+
76
+ // Send the request
77
+ const res = await this.platform.sendUpdate(this.accessory, {
78
+ namespace: 'Appliance.System.All',
79
+ payload: {},
80
+ })
81
+
82
+ // Log the received data
83
+ this.accessory.logDebug(`${platformLang.incPoll}: ${JSON.stringify(res.data)}`)
84
+
85
+ // Check the response is in a useful format
86
+ const data = res.data.payload
87
+ if (data.all) {
88
+ if (
89
+ data.all.digest
90
+ && data.all.digest.hub
91
+ && data.all.digest.hub.subdevice
92
+ && Array.isArray(data.all.digest.hub.subdevice)
93
+ ) {
94
+ data.all.digest.hub.subdevice.forEach((subdevice) => {
95
+ // Check whether the homebridge accessory this relates to exists
96
+ const subAcc = this.devicesInHB.get(
97
+ this.platform.api.hap.uuid.generate(
98
+ this.accessory.context.serialNumber + subdevice.id,
99
+ ),
100
+ )
101
+
102
+ // No need to continue if the accessory doesn't exist nor the receiver function
103
+ if (!subAcc || !subAcc.control || !subAcc.control.applyUpdate) {
104
+ return
105
+ }
106
+
107
+ // Properties we need are in ms100 object
108
+ if (subdevice.ms100) {
109
+ // Apply the update to the accessory
110
+ const update = {}
111
+ if (hasProperty(subdevice.ms100, 'latestTemperature')) {
112
+ update.temperature = subdevice.ms100.latestTemperature
113
+ }
114
+ if (hasProperty(subdevice.ms100, 'latestHumidity')) {
115
+ update.humidity = subdevice.ms100.latestHumidity
116
+ }
117
+ if (hasProperty(subdevice.ms100, 'voltage')) {
118
+ update.voltage = subdevice.ms100.voltage
119
+ }
120
+ subAcc.control.applyUpdate(update)
121
+ } else if (subdevice.waterLeak) {
122
+ // Apply the update to the accessory
123
+ subAcc.control.applyUpdate(subdevice)
124
+ } else if (subdevice.status === 2) {
125
+ // If the status is 2 then has been reported offline - report a battery of 0
126
+ subAcc.control.applyUpdate({ voltage: 0 })
127
+ }
128
+
129
+ // Check to see if any MTS exist
130
+ if (hasProperty(subdevice, 'scheduleBMode')) {
131
+ this.mtsList.push(subdevice.id)
132
+ }
133
+ })
134
+ }
135
+
136
+ // A flag to check if we need to update the accessory context
137
+ let needsUpdate = false
138
+
139
+ // Get the mac address and hardware version of the device
140
+ if (data.all.system) {
141
+ // Mac address and hardware don't change regularly so only get on first poll
142
+ if (firstRun && data.all.system.hardware) {
143
+ this.accessory.context.macAddress = data.all.system.hardware.macAddress.toUpperCase()
144
+ this.accessory.context.hardware = data.all.system.hardware.version
145
+ }
146
+
147
+ // Get the ip address and firmware of the device
148
+ if (data.all.system.firmware) {
149
+ // Check for an IP change each and every time the device is polled
150
+ if (this.accessory.context.ipAddress !== data.all.system.firmware.innerIp) {
151
+ this.accessory.context.ipAddress = data.all.system.firmware.innerIp
152
+ needsUpdate = true
153
+ }
154
+
155
+ // Firmware doesn't change regularly so only get on first poll
156
+ if (firstRun) {
157
+ this.accessory.context.firmware = data.all.system.firmware.version
158
+ }
159
+ }
160
+ }
161
+
162
+ // Get the cloud online status of the device
163
+ if (data.all.system.online) {
164
+ const isOnline = data.all.system.online.status === 1
165
+ if (this.accessory.context.isOnline !== isOnline) {
166
+ this.accessory.context.isOnline = isOnline
167
+ needsUpdate = true
168
+ }
169
+ }
170
+
171
+ // Update the accessory cache if anything has changed
172
+ if (needsUpdate || firstRun) {
173
+ this.devicesInHB.forEach((subAcc) => {
174
+ if (subAcc.context.serialNumber === this.accessory.context.serialNumber) {
175
+ subAcc.context = {
176
+ ...subAcc.context,
177
+ macAddress: this.accessory.context.macAddress,
178
+ hardware: this.accessory.context.hardware,
179
+ ipAddress: this.accessory.context.ipAddress,
180
+ firmware: this.accessory.context.firmware,
181
+ isOnline: this.accessory.context.isOnline,
182
+ }
183
+ this.platform.updateAccessory(subAcc)
184
+ }
185
+ })
186
+ }
187
+ }
188
+
189
+ // Request status for any MTS devices that exist
190
+ if (this.mtsList.length > 0) {
191
+ const payload = { all: [] }
192
+ this.mtsList.forEach(id => payload.all.push({ id }))
193
+
194
+ // Send the request
195
+ const res2 = await this.platform.sendUpdate(this.accessory, {
196
+ namespace: 'Appliance.Hub.Mts100.All',
197
+ payload,
198
+ method: 'GET',
199
+ })
200
+
201
+ // Log the received data
202
+ this.accessory.logDebug(`${platformLang.incPoll}: ${JSON.stringify(res2.data)}`)
203
+
204
+ const data2 = res2.data.payload
205
+ if (data2.all && Array.isArray(data2.all)) {
206
+ data2.all.forEach((entry) => {
207
+ // Check whether the homebridge accessory this relates to exists
208
+ const subAcc = this.devicesInHB.get(
209
+ this.platform.api.hap.uuid.generate(this.accessory.context.serialNumber + entry.id),
210
+ )
211
+
212
+ // No need to continue if the accessory doesn't exist nor the receiver function
213
+ if (!subAcc || !subAcc.control || !subAcc.control.applyUpdate) {
214
+ return
215
+ }
216
+
217
+ const toReturn = {}
218
+ if (entry.togglex && hasProperty(entry.togglex, 'onoff')) {
219
+ toReturn.state = entry.togglex.onoff
220
+ }
221
+ if (entry.temperature) {
222
+ if (hasProperty(entry.temperature, 'room')) {
223
+ toReturn.currTemperature = entry.temperature.room / 10
224
+ }
225
+ if (hasProperty(entry.temperature, 'currentSet')) {
226
+ toReturn.targTemperature = entry.temperature.currentSet / 10
227
+ }
228
+ if (hasProperty(entry.temperature, 'openWindow')) {
229
+ toReturn.openWindow = entry.temperature.openWindow
230
+ }
231
+ }
232
+ if (entry.mode && hasProperty(entry.mode, 'state')) {
233
+ toReturn.mode = entry.mode.state
234
+ }
235
+
236
+ // Apply the update
237
+ if (Object.keys(toReturn).length > 0) {
238
+ subAcc.control.applyUpdate(toReturn)
239
+ }
240
+ })
241
+ }
242
+ }
243
+ })
244
+ } catch (err) {
245
+ const eText = err instanceof TimeoutError ? platformLang.timeout : parseError(err)
246
+ this.accessory.logDebugWarn(`${platformLang.reqFailed}: ${eText}`)
247
+
248
+ // Set the homebridge-ui status of the device to offline if local and error is timeout
249
+ if (
250
+ (this.accessory.context.isOnline || firstRun)
251
+ && ['EHOSTUNREACH', 'timed out'].some(el => eText.includes(el))
252
+ ) {
253
+ this.accessory.context.isOnline = false
254
+ this.platform.updateAccessory(this.accessory)
255
+ }
256
+ }
257
+ }
258
+
259
+ async requestSubdevices() {
260
+ try {
261
+ /*
262
+ This function is unused but would be nice to find the correct payload to
263
+ be able to request a subdevice list from the device itself rather than
264
+ from the cloud.
265
+ */
266
+ // Add the request to the queue so updates are sent apart
267
+ await this.queue.add(async () => {
268
+ // This flag stops the plugin from requesting updates while pending on others
269
+ this.updateInProgress = true
270
+
271
+ // Send the request
272
+ const res = await this.platform.sendUpdate(this.accessory, {
273
+ namespace: 'Appliance.Hub.SubdeviceList',
274
+ payload: {
275
+ all: [],
276
+ },
277
+ })
278
+
279
+ // Log the received data
280
+ this.accessory.logDebug(`${platformLang.incSubDevices}: ${JSON.stringify(res.data)}`)
281
+ })
282
+ } catch (err) {
283
+ const eText = parseError(err)
284
+ this.accessory.logWarn(`${platformLang.reqFailedSubs} ${eText}`)
285
+ }
286
+ }
287
+
288
+ receiveUpdate(params) {
289
+ try {
290
+ // Log the received data
291
+ this.accessory.logDebug(`${platformLang.incMQTT}: ${JSON.stringify(params)}`)
292
+
293
+ // Validate the response, checking for payload property
294
+ if (!params.payload) {
295
+ throw new Error('invalid response received')
296
+ }
297
+ const data = params.payload
298
+
299
+ // Switches
300
+ if (data.togglex && Array.isArray(data.togglex)) {
301
+ data.togglex.forEach((entry) => {
302
+ // Check whether the homebridge accessory this relates to exists
303
+ const subAcc = this.devicesInHB.get(
304
+ this.platform.api.hap.uuid.generate(this.accessory.context.serialNumber + entry.id),
305
+ )
306
+
307
+ // No need to continue if the accessory doesn't exist nor the receiver function
308
+ if (!subAcc || !subAcc.control || !subAcc.control.applyUpdate) {
309
+ return
310
+ }
311
+
312
+ const toReturn = {}
313
+ if (hasProperty(entry, 'onoff')) {
314
+ toReturn.state = entry.onoff
315
+ }
316
+
317
+ // Apply the update
318
+ if (Object.keys(toReturn).length > 0) {
319
+ subAcc.control.applyUpdate(toReturn)
320
+ }
321
+ })
322
+ }
323
+
324
+ // Temperature updates
325
+ if (data.temperature && Array.isArray(data.temperature)) {
326
+ data.temperature.forEach((entry) => {
327
+ // Check whether the homebridge accessory this relates to exists
328
+ const subAcc = this.devicesInHB.get(
329
+ this.platform.api.hap.uuid.generate(this.accessory.context.serialNumber + entry.id),
330
+ )
331
+
332
+ // No need to continue if the accessory doesn't exist nor the receiver function
333
+ if (!subAcc || !subAcc.control || !subAcc.control.applyUpdate) {
334
+ return
335
+ }
336
+
337
+ const toReturn = {}
338
+ if (hasProperty(entry, 'currentSet')) {
339
+ toReturn.targTemperature = entry.currentSet / 10
340
+ }
341
+ if (hasProperty(entry, 'room')) {
342
+ toReturn.currTemperature = entry.room / 10
343
+ }
344
+ if (hasProperty(entry, 'openWindow')) {
345
+ toReturn.openWindow = entry.openWindow
346
+ }
347
+
348
+ // Apply the update
349
+ if (Object.keys(toReturn).length > 0) {
350
+ subAcc.control.applyUpdate(toReturn)
351
+ }
352
+ })
353
+ }
354
+
355
+ // Mode updates
356
+ if (data.mode && Array.isArray(data.mode)) {
357
+ data.mode.forEach((entry) => {
358
+ // Check whether the homebridge accessory this relates to exists
359
+ const subAcc = this.devicesInHB.get(
360
+ this.platform.api.hap.uuid.generate(this.accessory.context.serialNumber + entry.id),
361
+ )
362
+
363
+ // No need to continue if the accessory doesn't exist nor the receiver function
364
+ if (!subAcc || !subAcc.control || !subAcc.control.applyUpdate) {
365
+ return
366
+ }
367
+
368
+ const toReturn = {}
369
+ if (hasProperty(entry, 'state')) {
370
+ toReturn.mode = entry.state
371
+ }
372
+
373
+ // Apply the update
374
+ if (Object.keys(toReturn).length > 0) {
375
+ subAcc.control.applyUpdate(toReturn)
376
+ }
377
+ })
378
+ }
379
+
380
+ // Leak sensor updates
381
+ if (data.waterLeak && Array.isArray(data.waterLeak)) {
382
+ data.waterLeak.forEach((entry) => {
383
+ // Check whether the homebridge accessory this relates to exists
384
+ const subAcc = this.devicesInHB.get(
385
+ this.platform.api.hap.uuid.generate(this.accessory.context.serialNumber + entry.id),
386
+ )
387
+
388
+ // No need to continue if the accessory doesn't exist nor the receiver function
389
+ if (!subAcc || !subAcc.control || !subAcc.control.applyUpdate) {
390
+ return
391
+ }
392
+
393
+ // Apply the update
394
+ if (Object.keys(entry).length > 0) {
395
+ subAcc.control.applyUpdate(entry)
396
+ }
397
+ })
398
+ }
399
+ } catch (err) {
400
+ this.accessory.logWarn(`${platformLang.refFailed} ${parseError(err)}`)
401
+ }
402
+ }
403
+ }
@@ -0,0 +1,115 @@
1
+ import platformConsts from '../utils/constants.js'
2
+ import { hasProperty, parseError } from '../utils/functions.js'
3
+ import platformLang from '../utils/lang-en.js'
4
+
5
+ export default class {
6
+ constructor(platform, accessory) {
7
+ // Set up variables from the platform
8
+ this.hapChar = platform.api.hap.Characteristic
9
+ this.hapErr = platform.api.hap.HapStatusError
10
+ this.hapServ = platform.api.hap.Service
11
+ this.platform = platform
12
+
13
+ // Set up variables from the accessory
14
+ this.accessory = accessory
15
+ this.lowBattThreshold = accessory.context.options.lowBattThreshold
16
+ ? Math.min(accessory.context.options.lowBattThreshold, 100)
17
+ : platformConsts.defaultValues.lowBattThreshold
18
+ this.name = accessory.displayName
19
+
20
+ // Add the temperature service if it doesn't already exist
21
+ this.tempService = this.accessory.getService(this.hapServ.TemperatureSensor)
22
+ || this.accessory.addService(this.hapServ.TemperatureSensor)
23
+ this.cacheTemp = this.tempService.getCharacteristic(this.hapChar.CurrentTemperature).value
24
+ this.updateCache()
25
+
26
+ // Add the humidity service if it doesn't already exist
27
+ this.humiService = this.accessory.getService(this.hapServ.HumiditySensor)
28
+ || this.accessory.addService(this.hapServ.HumiditySensor)
29
+ this.cacheHumi = this.humiService.getCharacteristic(this.hapChar.CurrentRelativeHumidity).value
30
+
31
+ // Add the battery service if it doesn't already exist
32
+ this.battService = this.accessory.getService(this.hapServ.Battery)
33
+ || this.accessory.addService(this.hapServ.Battery)
34
+ this.cacheBatt = this.battService.getCharacteristic(this.hapChar.BatteryLevel).value
35
+
36
+ // Pass the accessory to Fakegato to set up with Eve
37
+ this.accessory.eveService = new platform.eveService('custom', this.accessory, { log: () => {} })
38
+
39
+ // Output the customised options to the log
40
+ const opts = JSON.stringify({
41
+ connection: this.accessory.context.connection,
42
+ lowBattThreshold: this.lowBattThreshold,
43
+ })
44
+ platform.log('[%s] %s %s.', this.name, platformLang.devInitOpts, opts)
45
+ }
46
+
47
+ applyUpdate(data) {
48
+ try {
49
+ // Temperature
50
+ if (hasProperty(data, 'temperature')) {
51
+ // Divide by 10 as reading is given as whole number inc decimal
52
+ const newTemp = data.temperature / 10
53
+ if (newTemp !== this.cacheTemp) {
54
+ this.cacheTemp = newTemp
55
+ this.tempService.updateCharacteristic(this.hapChar.CurrentTemperature, newTemp)
56
+ this.accessory.eveService.addEntry({ temp: newTemp })
57
+ this.accessory.log(`${platformLang.curTemp} [${newTemp}°C]`)
58
+
59
+ // Update the cache file with the new temperature
60
+ this.updateCache()
61
+ }
62
+ }
63
+
64
+ // Humidity
65
+ if (hasProperty(data, 'humidity')) {
66
+ // Divide by 10 and round as reading is given as whole number inc decimal
67
+ const newHumi = Math.round(data.humidity / 10)
68
+ if (newHumi !== this.cacheHumi) {
69
+ this.cacheHumi = newHumi
70
+ this.humiService.updateCharacteristic(this.hapChar.CurrentRelativeHumidity, newHumi)
71
+ this.accessory.eveService.addEntry({ humidity: newHumi })
72
+ this.accessory.log(`${platformLang.curHumi} [${newHumi}%]`)
73
+ }
74
+ }
75
+
76
+ // Battery % from reported voltage
77
+ if (hasProperty(data, 'voltage')) {
78
+ // 1. Reduce/enlarge value so in [2000, 3000]
79
+ let newVoltage = Math.min(Math.max(data.voltage, 2000), 3000)
80
+
81
+ // 2. Scale this from [2000, 3000] to [0, 100] and round to nearest whole number
82
+ newVoltage = Math.round((newVoltage - 2000) / 10)
83
+
84
+ // This should be a rough estimate of the battery %
85
+ if (newVoltage !== this.cacheBatt) {
86
+ this.cacheBatt = newVoltage
87
+ this.battService.updateCharacteristic(this.hapChar.BatteryLevel, this.cacheBatt)
88
+ this.battService.updateCharacteristic(
89
+ this.hapChar.StatusLowBattery,
90
+ this.cacheBatt < this.lowBattThreshold ? 1 : 0,
91
+ )
92
+ }
93
+ }
94
+ } catch (err) {
95
+ this.accessory.logWarn(`${platformLang.refFailed} ${parseError(err)}`)
96
+ }
97
+ }
98
+
99
+ async updateCache() {
100
+ // Don't continue if the storage client hasn't initialised properly
101
+ if (!this.platform.storageClientData) {
102
+ return
103
+ }
104
+
105
+ // Attempt to save the new temperature to the cache
106
+ try {
107
+ await this.platform.storageData.setItem(
108
+ `${this.accessory.context.subSerialNumber}_temp`,
109
+ this.cacheTemp,
110
+ )
111
+ } catch (err) {
112
+ this.accessory.logWarn(`${platformLang.storageWriteErr} ${parseError(err)}`)
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,40 @@
1
+ import platformConsts from '../utils/constants.js'
2
+ import { parseError } from '../utils/functions.js'
3
+ import platformLang from '../utils/lang-en.js'
4
+
5
+ export default class {
6
+ constructor(platform, accessory) {
7
+ // Set up variables from the platform
8
+ this.hapChar = platform.api.hap.Characteristic
9
+ this.hapErr = platform.api.hap.HapStatusError
10
+ this.hapServ = platform.api.hap.Service
11
+ this.platform = platform
12
+
13
+ // Set up variables from the accessory
14
+ this.accessory = accessory
15
+ this.lowBattThreshold = accessory.context.options.lowBattThreshold
16
+ ? Math.min(accessory.context.options.lowBattThreshold, 100)
17
+ : platformConsts.defaultValues.lowBattThreshold
18
+ this.name = accessory.displayName
19
+
20
+ // Add the battery service if it doesn't already exist
21
+ this.battService = this.accessory.getService(this.hapServ.Battery)
22
+ || this.accessory.addService(this.hapServ.Battery)
23
+ this.cacheBatt = this.battService.getCharacteristic(this.hapChar.BatteryLevel).value
24
+
25
+ // Output the customised options to the log
26
+ const opts = JSON.stringify({
27
+ connection: this.accessory.context.connection,
28
+ lowBattThreshold: this.lowBattThreshold,
29
+ })
30
+ platform.log('[%s] %s %s.', this.name, platformLang.devInitOpts, opts)
31
+ }
32
+
33
+ applyUpdate(data) {
34
+ try {
35
+ this.log.warn('[%s]\n%s.', this.name, JSON.stringify(data))
36
+ } catch (err) {
37
+ this.accessory.logWarn(`${platformLang.refFailed} ${parseError(err)}`)
38
+ }
39
+ }
40
+ }