@homebridge-plugins/homebridge-wemo 7.0.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 (41) hide show
  1. package/CHANGELOG.md +809 -0
  2. package/LICENSE +21 -0
  3. package/README.md +66 -0
  4. package/config.schema.json +814 -0
  5. package/eslint.config.js +50 -0
  6. package/lib/connection/http.js +174 -0
  7. package/lib/connection/upnp.js +155 -0
  8. package/lib/device/coffee.js +167 -0
  9. package/lib/device/crockpot.js +380 -0
  10. package/lib/device/dimmer.js +280 -0
  11. package/lib/device/heater.js +416 -0
  12. package/lib/device/humidifier.js +379 -0
  13. package/lib/device/index.js +39 -0
  14. package/lib/device/insight.js +353 -0
  15. package/lib/device/lightswitch.js +154 -0
  16. package/lib/device/link-bulb.js +467 -0
  17. package/lib/device/link-hub.js +63 -0
  18. package/lib/device/maker-garage.js +426 -0
  19. package/lib/device/maker-switch.js +202 -0
  20. package/lib/device/motion.js +148 -0
  21. package/lib/device/outlet.js +148 -0
  22. package/lib/device/purifier.js +468 -0
  23. package/lib/device/simulation/purifier-insight.js +357 -0
  24. package/lib/device/simulation/purifier.js +159 -0
  25. package/lib/device/simulation/switch-insight.js +315 -0
  26. package/lib/device/simulation/switch.js +154 -0
  27. package/lib/fakegato/LICENSE +21 -0
  28. package/lib/fakegato/fakegato-history.js +814 -0
  29. package/lib/fakegato/fakegato-storage.js +108 -0
  30. package/lib/fakegato/fakegato-timer.js +125 -0
  31. package/lib/fakegato/uuid.js +27 -0
  32. package/lib/homebridge-ui/public/index.html +315 -0
  33. package/lib/homebridge-ui/server.js +10 -0
  34. package/lib/index.js +8 -0
  35. package/lib/platform.js +1423 -0
  36. package/lib/utils/colour.js +135 -0
  37. package/lib/utils/constants.js +129 -0
  38. package/lib/utils/eve-chars.js +130 -0
  39. package/lib/utils/functions.js +52 -0
  40. package/lib/utils/lang-en.js +125 -0
  41. package/package.json +70 -0
@@ -0,0 +1,380 @@
1
+ import {
2
+ generateRandomString,
3
+ hasProperty,
4
+ parseError,
5
+ sleep,
6
+ } 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.hapChar = platform.api.hap.Characteristic
13
+ this.hapErr = platform.api.hap.HapStatusError
14
+ this.hapServ = platform.api.hap.Service
15
+ this.platform = platform
16
+
17
+ // Set up variables from the accessory
18
+ this.accessory = accessory
19
+
20
+ // Add the heater service if it doesn't already exist
21
+ this.service = this.accessory.getService(this.hapServ.HeaterCooler)
22
+ || this.accessory.addService(this.hapServ.HeaterCooler)
23
+
24
+ // Add the set handler to the heater active characteristic
25
+ this.service
26
+ .getCharacteristic(this.hapChar.Active)
27
+ .removeOnSet()
28
+ .onSet(async value => this.internalStateUpdate(value))
29
+
30
+ // Add options to the heater target state characteristic
31
+ this.service.getCharacteristic(this.hapChar.TargetHeaterCoolerState).setProps({
32
+ minValue: 0,
33
+ maxValue: 0,
34
+ validValues: [0],
35
+ })
36
+
37
+ // Add the set handler and a range to the heater target temperature characteristic
38
+ this.service
39
+ .getCharacteristic(this.hapChar.HeatingThresholdTemperature)
40
+ .setProps({
41
+ minValue: 0,
42
+ maxValue: 24,
43
+ minStep: 0.5,
44
+ })
45
+ .onSet(async (value) => {
46
+ await this.internalCookingTimeUpdate(value)
47
+ })
48
+
49
+ // Add the set handler to the heater rotation speed characteristic
50
+ this.service
51
+ .getCharacteristic(this.hapChar.RotationSpeed)
52
+ .setProps({ minStep: 33 })
53
+ .onSet(async (value) => {
54
+ await this.internalModeUpdate(value)
55
+ })
56
+
57
+ // Add a range to the heater current temperature characteristic
58
+ this.service.getCharacteristic(this.hapChar.CurrentTemperature).setProps({
59
+ minValue: 0,
60
+ maxValue: 24,
61
+ minStep: 0.5,
62
+ })
63
+
64
+ // Some conversion objects
65
+ this.modeLabels = {
66
+ 0: platformLang.labelOff,
67
+ 50: platformLang.labelWarm,
68
+ 51: platformLang.labelLow,
69
+ 52: platformLang.labelHigh,
70
+ }
71
+
72
+ // Output the customised options to the log
73
+ const opts = JSON.stringify({
74
+ })
75
+ platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
76
+
77
+ // Request a device update immediately
78
+ this.requestDeviceUpdate()
79
+
80
+ // Start a polling interval if the user has disabled upnp
81
+ if (this.accessory.context.connection === 'http') {
82
+ this.pollingInterval = setInterval(
83
+ () => this.requestDeviceUpdate(),
84
+ platform.config.pollingInterval * 1000,
85
+ )
86
+ }
87
+ }
88
+
89
+ async requestDeviceUpdate() {
90
+ try {
91
+ // Request the update
92
+ const data = await this.platform.httpClient.sendDeviceUpdate(
93
+ this.accessory,
94
+ 'urn:Belkin:service:basicevent:1',
95
+ 'GetCrockpotState',
96
+ )
97
+
98
+ // Check for existence since data.mode can be 0
99
+ if (hasProperty(data, 'mode')) {
100
+ // Log the receiving update if debug is enabled
101
+ this.accessory.logDebug(`${platformLang.recUpd} [mode: ${data.mode}]`)
102
+
103
+ // Send the data to the receiver function
104
+ this.externalModeUpdate(Number.parseInt(data.mode, 10))
105
+ }
106
+
107
+ // data.time can be 0 so check for existence
108
+ if (hasProperty(data, 'time')) {
109
+ // Log the receiving update if debug is enabled
110
+ this.accessory.logDebug(`${platformLang.recUpd} [time: ${data.time}]`)
111
+
112
+ // Send the data to the receiver function
113
+ this.externalTimeLeftUpdate(Number.parseInt(data.time, 10))
114
+ }
115
+ } catch (err) {
116
+ const eText = parseError(err, [
117
+ platformLang.timeout,
118
+ platformLang.timeoutUnreach,
119
+ platformLang.noService,
120
+ ])
121
+ this.accessory.logDebugWarn(`${platformLang.rduErr} ${eText}`)
122
+ }
123
+ }
124
+
125
+ receiveDeviceUpdate(attribute) {
126
+ // Log the receiving update if debug is enabled
127
+ this.accessory.logDebug(`${platformLang.recUpd} [${attribute.name}: ${JSON.stringify(attribute.value)}]`)
128
+
129
+ // Send a HomeKit needed true/false argument
130
+ // attribute.value is 0 if and only if the outlet is off
131
+ // this.externalStateUpdate(attribute.value !== 0)
132
+ }
133
+
134
+ async sendDeviceUpdate(mode, time) {
135
+ // Log the sending update if debug is enabled
136
+ this.accessory.logDebug(`${platformLang.senUpd} {"mode": ${mode}, "time": ${time}`)
137
+
138
+ // Send the update
139
+ await this.platform.httpClient.sendDeviceUpdate(
140
+ this.accessory,
141
+ 'urn:Belkin:service:basicevent:1',
142
+ 'SetCrockpotState',
143
+ {
144
+ mode: { '#text': mode },
145
+ time: { '#text': time },
146
+ },
147
+ )
148
+ }
149
+
150
+ async internalStateUpdate(value) {
151
+ const prevState = this.service.getCharacteristic(this.hapChar.Active).value
152
+ try {
153
+ // Don't continue if the new value is the same as before
154
+ if (value === prevState) {
155
+ return
156
+ }
157
+
158
+ // A slight pause seems to make Home app more responsive for characteristic updates later
159
+ await sleep(500)
160
+
161
+ // Note value === 0 is OFF, value === 1 is ON
162
+ if (value === 0) {
163
+ // Turn everything off
164
+ this.service.setCharacteristic(this.hapChar.RotationSpeed, 0)
165
+ this.service.updateCharacteristic(this.hapChar.CurrentTemperature, 0)
166
+ this.service.updateCharacteristic(this.hapChar.HeatingThresholdTemperature, 0)
167
+ this.accessory.context.cacheTime = 0
168
+ } else {
169
+ // Set rotation speed to the lowest ON value
170
+ this.service.setCharacteristic(this.hapChar.RotationSpeed, 33)
171
+ }
172
+ } catch (err) {
173
+ const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
174
+ this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
175
+
176
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
177
+ setTimeout(() => {
178
+ this.service.updateCharacteristic(this.hapChar.Active, prevState)
179
+ }, 2000)
180
+ throw new this.hapErr(-70402)
181
+ }
182
+ }
183
+
184
+ async internalModeUpdate(value) {
185
+ const prevSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
186
+ try {
187
+ // Avoid multiple updates in quick succession
188
+ const updateKeyMode = generateRandomString(5)
189
+ this.updateKeyMode = updateKeyMode
190
+ await sleep(500)
191
+ if (updateKeyMode !== this.updateKeyMode) {
192
+ return
193
+ }
194
+
195
+ // Generate newValue for the needed mode and newSpeed in 33% multiples
196
+ let newValue = 0
197
+ let newSpeed = 0
198
+ if (value > 25 && value <= 50) {
199
+ newValue = 50
200
+ newSpeed = 33
201
+ } else if (value > 50 && value <= 75) {
202
+ newValue = 51
203
+ newSpeed = 66
204
+ } else if (value > 75) {
205
+ newValue = 52
206
+ newSpeed = 99
207
+ }
208
+
209
+ // Don't continue if the speed is the same as before
210
+ if (prevSpeed === newSpeed) {
211
+ return
212
+ }
213
+
214
+ // A slight pause seems to make Home app more responsive for characteristic updates later
215
+ await sleep(500)
216
+ if ([0, 33].includes(newSpeed)) {
217
+ // Reset the cooking times to 0 if turned off or set to warm
218
+ this.service.updateCharacteristic(this.hapChar.CurrentTemperature, 0)
219
+ this.service.updateCharacteristic(this.hapChar.HeatingThresholdTemperature, 0)
220
+ this.accessory.context.cacheTime = 0
221
+
222
+ // Log the change if appropriate
223
+ this.accessory.log(`${platformLang.curTimer} [0:00]`)
224
+ }
225
+
226
+ // Send the update
227
+ await this.sendDeviceUpdate(newValue, this.accessory.context.cacheTime)
228
+
229
+ // Update the cache and log if appropriate
230
+ this.cacheMode = newValue
231
+ this.accessory.log(`${platformLang.curMode} [${this.modeLabels[newValue]}]`)
232
+ } catch (err) {
233
+ const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
234
+ this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
235
+
236
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
237
+ setTimeout(() => {
238
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, prevSpeed)
239
+ }, 2000)
240
+ throw new this.hapErr(-70402)
241
+ }
242
+ }
243
+
244
+ async internalCookingTimeUpdate(value) {
245
+ const prevTemp = this.service.getCharacteristic(this.hapChar.HeatingThresholdTemperature).value
246
+ try {
247
+ // Avoid multiple updates in quick succession
248
+ const updateKeyTemp = generateRandomString(5)
249
+ this.updateKeyTemp = updateKeyTemp
250
+ await sleep(500)
251
+ if (updateKeyTemp !== this.updateKeyTemp) {
252
+ return
253
+ }
254
+
255
+ // The value is cooking hours, I don't think device can be set to cook for 24 hours as max
256
+ if (value === 24) {
257
+ value = 23.5
258
+ }
259
+
260
+ // Don't continue if the value is the same as before
261
+ if (value === prevTemp) {
262
+ return
263
+ }
264
+
265
+ // Find the needed mode based on the new value
266
+ const prevSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
267
+ let modeChange = this.cacheMode
268
+ // If cooking time is changed to above zero and mode is OFF or WARM, then set to LOW
269
+ if (value !== 0 && [0, 33].includes(prevSpeed)) {
270
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, 66)
271
+ modeChange = 51
272
+ this.cacheMode = 51
273
+
274
+ // Log the mode change if appropriate
275
+ this.accessory.log(`${platformLang.curMode} [${this.modeLabels[51]}]`)
276
+ }
277
+
278
+ // Send the update
279
+ const minutes = value * 60
280
+ await this.sendDeviceUpdate(modeChange, minutes)
281
+
282
+ // Log the change of cooking minutes if appropriate
283
+ const modMinutes = minutes % 60
284
+ this.accessory.log(`${platformLang.curTimer} [${Math.floor(value)}:${modMinutes >= 10 ? modMinutes : `0${modMinutes}`}]`)
285
+ } catch (err) {
286
+ const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
287
+ this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
288
+
289
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
290
+ setTimeout(() => {
291
+ this.service.updateCharacteristic(this.hapChar.HeatingThresholdTemperature, prevTemp)
292
+ }, 2000)
293
+ throw new this.hapErr(-70402)
294
+ }
295
+ }
296
+
297
+ externalModeUpdate(value) {
298
+ try {
299
+ // Don't continue if the given mode is the same as before
300
+ if (value === this.cacheMode) {
301
+ return
302
+ }
303
+
304
+ // Find the needed rotation speed based on the given mode
305
+ let rotSpeed = 0
306
+ switch (value) {
307
+ case 0:
308
+ break
309
+ case 50: {
310
+ rotSpeed = 33
311
+ break
312
+ }
313
+ case 51: {
314
+ rotSpeed = 66
315
+ break
316
+ }
317
+ case 52: {
318
+ rotSpeed = 99
319
+ break
320
+ }
321
+ default:
322
+ throw new Error(`Unknown value passed [${value} ${typeof value}]`)
323
+ }
324
+ // Update the HomeKit characteristics
325
+ this.service.updateCharacteristic(this.hapChar.Active, value !== 0 ? 1 : 0)
326
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, rotSpeed)
327
+
328
+ // Update the cache and log if appropriate
329
+ this.cacheMode = value
330
+ this.accessory.log(`${platformLang.curMode} [${this.modeLabels[value]}]`)
331
+
332
+ // If turned off then set the cooking time characteristics to 0
333
+ if (value === 0) {
334
+ this.service.updateCharacteristic(this.hapChar.CurrentTemperature, 0)
335
+ this.service.updateCharacteristic(this.hapChar.HeatingThresholdTemperature, 0)
336
+ }
337
+ } catch (err) {
338
+ this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
339
+ }
340
+ }
341
+
342
+ externalTimeLeftUpdate(value) {
343
+ try {
344
+ // Don't continue if the rounded cooking time is the same as before
345
+ if (value === this.accessory.context.cacheTime) {
346
+ return
347
+ }
348
+
349
+ // The value is passed in minutes (cooking time remaining)
350
+ let hkValue = 0
351
+ if (value > 0) {
352
+ /*
353
+ (1) convert to half-hour units (e.g. 159 -> 5.3)
354
+ (2) round to nearest 0.5 hour unit (e.g. 5.3 -> 5)
355
+ (3) if 0 then raise to 0.5 (as technically still cooking even if 1 minute)
356
+ */
357
+ hkValue = Math.max(Math.round(value / 30) / 2, 0.5)
358
+ }
359
+
360
+ const rotSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
361
+
362
+ // Change to LOW mode if cooking but cache is OFF
363
+ if (hkValue > 0 && rotSpeed === 0) {
364
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, 33)
365
+ this.cacheMode = 50
366
+ }
367
+
368
+ // Update the cooking time HomeKit characteristics
369
+ this.service.updateCharacteristic(this.hapChar.CurrentTemperature, hkValue)
370
+ this.service.updateCharacteristic(this.hapChar.HeatingThresholdTemperature, hkValue)
371
+
372
+ // Update the cache and log if appropriate
373
+ this.accessory.context.cacheTime = value
374
+ const modMinutes = value % 60
375
+ this.accessory.log(`${platformLang.curTimer} [${Math.floor(value / 60)}:${modMinutes >= 10 ? modMinutes : `0${modMinutes}`}]`)
376
+ } catch (err) {
377
+ this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
378
+ }
379
+ }
380
+ }
@@ -0,0 +1,280 @@
1
+ import platformConsts from '../utils/constants.js'
2
+ import {
3
+ generateRandomString,
4
+ hasProperty,
5
+ parseError,
6
+ sleep,
7
+ } from '../utils/functions.js'
8
+ import platformLang from '../utils/lang-en.js'
9
+
10
+ export default class {
11
+ constructor(platform, accessory) {
12
+ // Set up variables from the platform
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
+
21
+ // Set up custom variables for this device type
22
+ const deviceConf = platform.deviceConf[accessory.context.serialNumber] || {}
23
+ this.brightStep = deviceConf.brightnessStep
24
+ ? Math.min(deviceConf.brightnessStep, 100)
25
+ : platformConsts.defaultValues.brightnessStep
26
+
27
+ // Add the lightbulb service if it doesn't already exist
28
+ this.service = accessory.getService(this.hapServ.Lightbulb) || accessory.addService(this.hapServ.Lightbulb)
29
+
30
+ // Add the set handler to the lightbulb on/off characteristic
31
+ this.service
32
+ .getCharacteristic(this.hapChar.On)
33
+ .removeOnSet()
34
+ .onSet(async value => this.internalStateUpdate(value))
35
+
36
+ // Add the set handler to the lightbulb brightness characteristic
37
+ this.service
38
+ .getCharacteristic(this.hapChar.Brightness)
39
+ .setProps({ minStep: this.brightStep })
40
+ .onSet(async (value) => {
41
+ await this.internalBrightnessUpdate(value)
42
+ })
43
+
44
+ // Output the customised options to the log
45
+ const opts = JSON.stringify({
46
+ brightnessStep: this.brightStep,
47
+ })
48
+ platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
49
+
50
+ // Request a device update immediately
51
+ this.requestDeviceUpdate()
52
+
53
+ // Start a polling interval if the user has disabled upnp
54
+ if (this.accessory.context.connection === 'http') {
55
+ this.pollingInterval = setInterval(
56
+ () => this.requestDeviceUpdate(),
57
+ platform.config.pollingInterval * 1000,
58
+ )
59
+ }
60
+ }
61
+
62
+ receiveDeviceUpdate(attribute) {
63
+ // Log the receiving update if debug is enabled
64
+ this.accessory.logDebug(`${platformLang.recUpd} [${attribute.name}: ${JSON.stringify(attribute.value)}]`)
65
+
66
+ // Check which attribute we are getting
67
+ switch (attribute.name) {
68
+ case 'BinaryState': {
69
+ // Send a HomeKit needed true/false argument
70
+ // attribute.value is 0 if and only if the device is off
71
+ const hkValue = attribute.value !== 0
72
+ this.externalSwitchUpdate(hkValue)
73
+ break
74
+ }
75
+ case 'Brightness':
76
+ // Send a HomeKit needed INT argument
77
+ this.externalBrightnessUpdate(attribute.value)
78
+ break
79
+ default:
80
+ }
81
+ }
82
+
83
+ async sendDeviceUpdate(value) {
84
+ // Log the sending update if debug is enabled
85
+ this.accessory.logDebug(`${platformLang.senUpd} ${JSON.stringify(value)}`)
86
+
87
+ // Send the update
88
+ await this.platform.httpClient.sendDeviceUpdate(
89
+ this.accessory,
90
+ 'urn:Belkin:service:basicevent:1',
91
+ 'SetBinaryState',
92
+ value,
93
+ )
94
+ }
95
+
96
+ async requestDeviceUpdate() {
97
+ try {
98
+ // Request the update
99
+ const data = await this.platform.httpClient.sendDeviceUpdate(
100
+ this.accessory,
101
+ 'urn:Belkin:service:basicevent:1',
102
+ 'GetBinaryState',
103
+ )
104
+
105
+ // Check for existence since BinaryState can be int 0
106
+ if (hasProperty(data, 'BinaryState')) {
107
+ this.receiveDeviceUpdate({
108
+ name: 'BinaryState',
109
+ value: Number.parseInt(data.BinaryState, 10),
110
+ })
111
+ }
112
+
113
+ // Check for existence since brightness can be int 0
114
+ if (hasProperty(data, 'brightness')) {
115
+ this.receiveDeviceUpdate({
116
+ name: 'Brightness',
117
+ value: Number.parseInt(data.brightness, 10),
118
+ })
119
+ }
120
+ } catch (err) {
121
+ const eText = parseError(err, [
122
+ platformLang.timeout,
123
+ platformLang.timeoutUnreach,
124
+ platformLang.noService,
125
+ ])
126
+ this.accessory.logDebugWarn(`${platformLang.rduErr} ${eText}`)
127
+ }
128
+ }
129
+
130
+ async getCurrentBrightness() {
131
+ // A quick function to get the current brightness of the device
132
+ const data = await this.platform.httpClient.sendDeviceUpdate(
133
+ this.accessory,
134
+ 'urn:Belkin:service:basicevent:1',
135
+ 'GetBinaryState',
136
+ )
137
+ return Number.parseInt(data.brightness, 10)
138
+ }
139
+
140
+ async internalStateUpdate(value) {
141
+ try {
142
+ // Wait a longer time than the brightness so in scenes brightness is sent first
143
+ await sleep(500)
144
+
145
+ // Send the update
146
+ await this.sendDeviceUpdate({
147
+ BinaryState: value ? 1 : 0,
148
+ })
149
+
150
+ // Update the cache and log if appropriate
151
+ this.cacheState = value
152
+ this.accessory.log(`${platformLang.curState} [${value ? 'on' : 'off'}]`)
153
+
154
+ // Don't continue if turning the device off
155
+ if (!value) {
156
+ return
157
+ }
158
+
159
+ // Wrap the extra brightness request in another try, so it doesn't affect the on/off change
160
+ try {
161
+ // When turning the device on we want to update the HomeKit brightness
162
+ const updatedBrightness = await this.getCurrentBrightness()
163
+
164
+ // Don't continue if the brightness is the same
165
+ if (updatedBrightness === this.cacheBright) {
166
+ return
167
+ }
168
+
169
+ // Update the brightness characteristic
170
+ this.service.updateCharacteristic(this.hapChar.Brightness, updatedBrightness)
171
+
172
+ // Update the cache and log if appropriate
173
+ this.cacheBright = updatedBrightness
174
+ this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
175
+ } catch (err) {
176
+ this.accessory.logWarn(`${platformLang.brightnessFail} ${parseError(err)}`)
177
+ }
178
+ } catch (err) {
179
+ const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
180
+ this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
181
+
182
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
183
+ setTimeout(() => {
184
+ this.service.updateCharacteristic(this.hapChar.On, this.cacheState)
185
+ }, 2000)
186
+ throw new this.hapErr(-70402)
187
+ }
188
+ }
189
+
190
+ async internalBrightnessUpdate(value) {
191
+ try {
192
+ // Avoid multiple updates in quick succession
193
+ const updateKey = generateRandomString(5)
194
+ this.updateKey = updateKey
195
+ await sleep(300)
196
+ if (updateKey !== this.updateKey) {
197
+ return
198
+ }
199
+
200
+ // Send the update
201
+ await this.sendDeviceUpdate({
202
+ BinaryState: value === 0 ? 0 : 1,
203
+ brightness: value,
204
+ })
205
+
206
+ // Update the cache and log if appropriate
207
+ this.cacheBright = value
208
+ this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
209
+ } catch (err) {
210
+ const eText = parseError(err, [platformLang.timeout, platformLang.timeoutUnreach])
211
+ this.accessory.logWarn(`${platformLang.cantCtl} ${eText}`)
212
+
213
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
214
+ setTimeout(() => {
215
+ this.service.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
216
+ }, 2000)
217
+ throw new this.hapErr(-70402)
218
+ }
219
+ }
220
+
221
+ async externalSwitchUpdate(value) {
222
+ try {
223
+ // Don't continue if the value is the same as the cache
224
+ if (value === this.cacheState) {
225
+ return
226
+ }
227
+
228
+ // Update the ON/OFF characteristic
229
+ this.service.updateCharacteristic(this.hapChar.On, value)
230
+
231
+ // Update the cache and log if appropriate
232
+ this.cacheState = value
233
+ this.accessory.log(`${platformLang.curState} [${value ? 'on' : 'off'}]`)
234
+
235
+ // Don't continue if the new state is OFF
236
+ if (!value) {
237
+ return
238
+ }
239
+
240
+ // Wrap the extra brightness request in another try, so it doesn't affect the on/off change
241
+ try {
242
+ // If the new state is ON then we want to update the HomeKit brightness
243
+ const updatedBrightness = await this.getCurrentBrightness()
244
+
245
+ // Don't continue if the brightness is the same
246
+ if (updatedBrightness === this.cacheBright) {
247
+ return
248
+ }
249
+
250
+ // Update the HomeKit brightness characteristic
251
+ this.service.updateCharacteristic(this.hapChar.Brightness, updatedBrightness)
252
+
253
+ // Update the cache and log if appropriate
254
+ this.cacheBright = updatedBrightness
255
+ this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
256
+ } catch (err) {
257
+ this.accessory.logWarn(`${platformLang.brightnessFail} ${parseError(err)}`)
258
+ }
259
+ } catch (err) {
260
+ this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
261
+ }
262
+ }
263
+
264
+ externalBrightnessUpdate(value) {
265
+ try {
266
+ // Don't continue if the brightness is the same as the cache value
267
+ if (value === this.cacheBright) {
268
+ return
269
+ }
270
+ // Update the HomeKit brightness characteristic
271
+ this.service.updateCharacteristic(this.hapChar.Brightness, value)
272
+
273
+ // Update the cache and log if appropriate
274
+ this.cacheBright = value
275
+ this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
276
+ } catch (err) {
277
+ this.accessory.logWarn(`${platformLang.cantUpd} ${parseError(err)}`)
278
+ }
279
+ }
280
+ }