@homebridge-plugins/homebridge-govee 10.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG.md +1937 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -0
  4. package/config.schema.json +1727 -0
  5. package/eslint.config.js +49 -0
  6. package/lib/connection/aws.js +174 -0
  7. package/lib/connection/ble.js +208 -0
  8. package/lib/connection/cert/AmazonRootCA1.pem +20 -0
  9. package/lib/connection/http.js +240 -0
  10. package/lib/connection/lan.js +284 -0
  11. package/lib/device/cooler-single.js +300 -0
  12. package/lib/device/dehumidifier-H7150.js +182 -0
  13. package/lib/device/dehumidifier-H7151.js +157 -0
  14. package/lib/device/diffuser-H7161.js +117 -0
  15. package/lib/device/diffuser-H7162.js +117 -0
  16. package/lib/device/fan-H7100.js +274 -0
  17. package/lib/device/fan-H7101.js +330 -0
  18. package/lib/device/fan-H7102.js +274 -0
  19. package/lib/device/fan-H7105.js +503 -0
  20. package/lib/device/fan-H7106.js +274 -0
  21. package/lib/device/fan-H7111.js +335 -0
  22. package/lib/device/heater-single.js +300 -0
  23. package/lib/device/heater1a.js +353 -0
  24. package/lib/device/heater1b.js +616 -0
  25. package/lib/device/heater2.js +838 -0
  26. package/lib/device/humidifier-H7140.js +224 -0
  27. package/lib/device/humidifier-H7141.js +257 -0
  28. package/lib/device/humidifier-H7142.js +522 -0
  29. package/lib/device/humidifier-H7143.js +157 -0
  30. package/lib/device/humidifier-H7148.js +157 -0
  31. package/lib/device/humidifier-H7160.js +446 -0
  32. package/lib/device/ice-maker-H7162.js +46 -0
  33. package/lib/device/index.js +105 -0
  34. package/lib/device/kettle.js +269 -0
  35. package/lib/device/light-switch.js +86 -0
  36. package/lib/device/light.js +617 -0
  37. package/lib/device/outlet-double.js +121 -0
  38. package/lib/device/outlet-single.js +172 -0
  39. package/lib/device/outlet-triple.js +160 -0
  40. package/lib/device/purifier-H7120.js +336 -0
  41. package/lib/device/purifier-H7121.js +336 -0
  42. package/lib/device/purifier-H7122.js +449 -0
  43. package/lib/device/purifier-H7123.js +411 -0
  44. package/lib/device/purifier-H7124.js +411 -0
  45. package/lib/device/purifier-H7126.js +296 -0
  46. package/lib/device/purifier-H7127.js +296 -0
  47. package/lib/device/purifier-H712C.js +296 -0
  48. package/lib/device/purifier-single.js +119 -0
  49. package/lib/device/sensor-button.js +22 -0
  50. package/lib/device/sensor-contact.js +22 -0
  51. package/lib/device/sensor-leak.js +87 -0
  52. package/lib/device/sensor-monitor.js +190 -0
  53. package/lib/device/sensor-presence.js +53 -0
  54. package/lib/device/sensor-thermo.js +144 -0
  55. package/lib/device/sensor-thermo4.js +55 -0
  56. package/lib/device/switch-double.js +121 -0
  57. package/lib/device/switch-single.js +95 -0
  58. package/lib/device/switch-triple.js +160 -0
  59. package/lib/device/tap-single.js +108 -0
  60. package/lib/device/template.js +43 -0
  61. package/lib/device/tv-single.js +84 -0
  62. package/lib/device/valve-single.js +155 -0
  63. package/lib/fakegato/LICENSE +21 -0
  64. package/lib/fakegato/fakegato-history.js +814 -0
  65. package/lib/fakegato/fakegato-storage.js +108 -0
  66. package/lib/fakegato/fakegato-timer.js +125 -0
  67. package/lib/fakegato/uuid.js +27 -0
  68. package/lib/homebridge-ui/public/index.html +433 -0
  69. package/lib/homebridge-ui/server.js +10 -0
  70. package/lib/index.js +8 -0
  71. package/lib/platform.js +1967 -0
  72. package/lib/utils/colour.js +564 -0
  73. package/lib/utils/constants.js +579 -0
  74. package/lib/utils/custom-chars.js +225 -0
  75. package/lib/utils/eve-chars.js +68 -0
  76. package/lib/utils/functions.js +117 -0
  77. package/lib/utils/lang-en.js +131 -0
  78. package/package.json +75 -0
@@ -0,0 +1,503 @@
1
+ import { hs2rgb, rgb2hs } from '../utils/colour.js'
2
+ import {
3
+ base64ToHex,
4
+ generateCodeFromHexValues,
5
+ generateRandomString,
6
+ getTwoItemPosition,
7
+ hexToDecimal,
8
+ hexToTwoItems,
9
+ parseError,
10
+ sleep,
11
+ } from '../utils/functions.js'
12
+ import platformLang from '../utils/lang-en.js'
13
+
14
+ export default class {
15
+ constructor(platform, accessory) {
16
+ // Set up variables from the platform
17
+ this.hapChar = platform.api.hap.Characteristic
18
+ this.hapErr = platform.api.hap.HapStatusError
19
+ this.hapServ = platform.api.hap.Service
20
+ this.platform = platform
21
+
22
+ // Set up variables from the accessory
23
+ this.accessory = accessory
24
+
25
+ // Codes etc
26
+ this.speedCodes = {
27
+ 7: 'MwUBAQAAAAAAAAAAAAAAAAAAADY=',
28
+ 14: 'MwUBAgAAAAAAAAAAAAAAAAAAADU=',
29
+ 21: 'MwUBAwAAAAAAAAAAAAAAAAAAADQ=',
30
+ 28: 'MwUBBAAAAAAAAAAAAAAAAAAAADM=',
31
+ 35: 'MwUBBQAAAAAAAAAAAAAAAAAAADI=',
32
+ 42: 'MwUBBgAAAAAAAAAAAAAAAAAAADE=',
33
+ 49: 'MwUBBwAAAAAAAAAAAAAAAAAAADA=',
34
+ 56: 'MwUBCAAAAAAAAAAAAAAAAAAAAD8=',
35
+ 63: 'MwUBCQAAAAAAAAAAAAAAAAAAAD4=',
36
+ 70: 'MwUBCgAAAAAAAAAAAAAAAAAAAD0=', // guessed
37
+ 77: 'MwUBCwAAAAAAAAAAAAAAAAAAADw=', // guessed
38
+ 84: 'MwUBDAAAAAAAAAAAAAAAAAAAADs=', // guessed
39
+ }
40
+
41
+ // Remove any old original Fan services
42
+ if (this.accessory.getService(this.hapServ.Fan)) {
43
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
44
+ }
45
+
46
+ // Add the fan service for the fan if it doesn't already exist
47
+ this.service = this.accessory.getService(this.hapServ.Fanv2) || this.accessory.addService(this.hapServ.Fanv2)
48
+
49
+ // Add the night light service if it doesn't already exist
50
+ this.lightService = this.accessory.getService(this.hapServ.Lightbulb) || this.accessory.addService(this.hapServ.Lightbulb)
51
+
52
+ // Add the set handler to the fan on/off characteristic
53
+ this.service
54
+ .getCharacteristic(this.hapChar.Active)
55
+ .onSet(async value => this.internalStateUpdate(value))
56
+ this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value ? 'on' : 'off'
57
+
58
+ // Add the set handler to the fan rotation speed characteristic
59
+ this.service
60
+ .getCharacteristic(this.hapChar.RotationSpeed)
61
+ .setProps({
62
+ minStep: 7,
63
+ minValue: 0,
64
+ validValues: [0, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91],
65
+ })
66
+ .onSet(async value => this.internalSpeedUpdate(value))
67
+ this.cacheSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
68
+ this.cacheMode = this.cacheSpeed >= 91 ? 'auto' : 'manual'
69
+
70
+ // Add the set handler to the fan swing mode
71
+ this.service
72
+ .getCharacteristic(this.hapChar.SwingMode)
73
+ .onSet(async value => this.internalSwingUpdate(value))
74
+ this.cacheSwing = this.service.getCharacteristic(this.hapChar.SwingMode).value === 1 ? 'on' : 'off'
75
+
76
+ // Add the set handler to the lightbulb on/off characteristic
77
+ this.lightService.getCharacteristic(this.hapChar.On).onSet(async (value) => {
78
+ await this.internalLightStateUpdate(value)
79
+ })
80
+ this.cacheLightState = this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
81
+
82
+ // Add the set handler to the lightbulb brightness characteristic
83
+ this.lightService
84
+ .getCharacteristic(this.hapChar.Brightness)
85
+ .onSet(async (value) => {
86
+ await this.internalBrightnessUpdate(value)
87
+ })
88
+ this.cacheBright = this.lightService.getCharacteristic(this.hapChar.Brightness).value
89
+
90
+ // Add the set handler to the lightbulb hue characteristic
91
+ this.lightService.getCharacteristic(this.hapChar.Hue).onSet(async (value) => {
92
+ await this.internalColourUpdate(value)
93
+ })
94
+ this.cacheHue = this.lightService.getCharacteristic(this.hapChar.Hue).value
95
+ this.cacheSat = this.lightService.getCharacteristic(this.hapChar.Saturation).value
96
+
97
+ // Output the customised options to the log
98
+ const opts = JSON.stringify({})
99
+ platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
100
+ }
101
+
102
+ async internalStateUpdate(value) {
103
+ try {
104
+ const newValue = value ? 'on' : 'off'
105
+
106
+ // Don't continue if the new value is the same as before
107
+ if (this.cacheState === newValue) {
108
+ return
109
+ }
110
+
111
+ // Send the request to the platform sender function
112
+ await this.platform.sendDeviceUpdate(this.accessory, {
113
+ cmd: 'ptReal',
114
+ value: value ? 'MwEBAAAAAAAAAAAAAAAAAAAAADM=' : 'MwEAAAAAAAAAAAAAAAAAAAAAADI=',
115
+ })
116
+
117
+ // Cache the new state and log if appropriate
118
+ if (this.cacheState !== newValue) {
119
+ this.cacheState = newValue
120
+ this.accessory.log(`${platformLang.curState} [${newValue}]`)
121
+ }
122
+ } catch (err) {
123
+ // Catch any errors during the process
124
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
125
+
126
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
127
+ setTimeout(() => {
128
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
129
+ }, 2000)
130
+ throw new this.hapErr(-70402)
131
+ }
132
+ }
133
+
134
+ async internalSpeedUpdate(value) {
135
+ try {
136
+ if (value < 3) {
137
+ return
138
+ }
139
+
140
+ let newValue
141
+ if (value < 10) {
142
+ newValue = 7
143
+ } else if (value < 17) {
144
+ newValue = 14
145
+ } else if (value < 24) {
146
+ newValue = 21
147
+ } else if (value < 31) {
148
+ newValue = 28
149
+ } else if (value < 38) {
150
+ newValue = 35
151
+ } else if (value < 45) {
152
+ newValue = 42
153
+ } else if (value < 52) {
154
+ newValue = 49
155
+ } else if (value < 59) {
156
+ newValue = 56
157
+ } else if (value < 66) {
158
+ newValue = 63
159
+ } else if (value < 73) {
160
+ newValue = 70
161
+ } else if (value < 80) {
162
+ newValue = 77
163
+ } else if (value < 87) {
164
+ newValue = 84
165
+ } else {
166
+ newValue = 91
167
+ }
168
+
169
+ let newMode = value === 91 ? 'auto' : 'manual'
170
+
171
+ // Don't continue if the new value is the same as before
172
+ if (this.cacheSpeed === newValue) {
173
+ return
174
+ }
175
+
176
+ // Don't continue if trying to access auto mode but there is no sensor attached
177
+ let codeToSend
178
+ if (newMode === 'auto') {
179
+ if (!this.accessory.context.sensorAttached || !this.cacheAutoCode) {
180
+ this.accessory.logWarn('auto mode not supported without a linked sensor')
181
+ codeToSend = this.speedCodes[84]
182
+ newMode = 'manual'
183
+ newValue = 84
184
+ } else {
185
+ codeToSend = this.cacheAutoCode
186
+ }
187
+ } else {
188
+ codeToSend = this.speedCodes[newValue]
189
+ }
190
+
191
+ await this.platform.sendDeviceUpdate(this.accessory, {
192
+ cmd: 'ptReal',
193
+ value: codeToSend,
194
+ })
195
+
196
+ // Cache the new state and log if appropriate
197
+ if (this.cacheMode !== newMode) {
198
+ this.cacheMode = newMode
199
+ this.accessory.log(`${platformLang.curMode} [${this.cacheMode}]`)
200
+ }
201
+ if (this.cacheSpeed !== newValue) {
202
+ this.cacheSpeed = newValue
203
+ this.accessory.log(`${platformLang.curSpeed} [${newValue}%]`)
204
+ }
205
+ } catch (err) {
206
+ // Catch any errors during the process
207
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
208
+
209
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
210
+ setTimeout(() => {
211
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
212
+ }, 2000)
213
+ throw new this.hapErr(-70402)
214
+ }
215
+ }
216
+
217
+ async internalSwingUpdate(value) {
218
+ try {
219
+ const newValue = value ? 'on' : 'off'
220
+ // Don't continue if the new value is the same as before
221
+ if (this.cacheSwing === value) {
222
+ return
223
+ }
224
+
225
+ await this.platform.sendDeviceUpdate(this.accessory, {
226
+ cmd: 'ptReal',
227
+ value: value ? 'Mx8BAQAAAAAAAAAAAAAAAAAAACw=' : 'Mx8BAAAAAAAAAAAAAAAAAAAAAC0=',
228
+ })
229
+
230
+ // Cache the new state and log if appropriate
231
+ if (this.cacheSwing !== newValue) {
232
+ this.cacheSwing = newValue
233
+ this.accessory.log(`${platformLang.curSwing} [${newValue}]`)
234
+ }
235
+ } catch (err) {
236
+ // Catch any errors during the process
237
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
238
+
239
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
240
+ setTimeout(() => {
241
+ this.service.updateCharacteristic(this.hapChar.SwingMode, this.cacheSwing === 'on' ? 1 : 0)
242
+ }, 2000)
243
+ throw new this.hapErr(-70402)
244
+ }
245
+ }
246
+
247
+ async internalLightStateUpdate(value) {
248
+ try {
249
+ const newValue = value ? 'on' : 'off'
250
+
251
+ // Don't continue if the new value is the same as before
252
+ if (this.cacheLightState === newValue) {
253
+ return
254
+ }
255
+
256
+ // Generate the hex values for the code
257
+ const hexValues = [0x3A, 0x1B, 0x01, 0x01, `0x0${value ? '1' : '0'}`]
258
+
259
+ // Send the request to the platform sender function
260
+ await this.platform.sendDeviceUpdate(this.accessory, {
261
+ cmd: 'multiSync',
262
+ value: generateCodeFromHexValues(hexValues),
263
+ })
264
+
265
+ // Cache the new state and log if appropriate
266
+ if (this.cacheLightState !== newValue) {
267
+ this.cacheLightState = newValue
268
+ this.accessory.log(`${platformLang.curLight} [${newValue}]`)
269
+ }
270
+ } catch (err) {
271
+ // Catch any errors during the process
272
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
273
+
274
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
275
+ setTimeout(() => {
276
+ this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLightState === 'on')
277
+ }, 2000)
278
+ throw new this.hapErr(-70402)
279
+ }
280
+ }
281
+
282
+ async internalBrightnessUpdate(value) {
283
+ try {
284
+ // This acts like a debounce function when endlessly sliding the brightness scale
285
+ const updateKeyBright = generateRandomString(5)
286
+ this.updateKeyBright = updateKeyBright
287
+ await sleep(350)
288
+ if (updateKeyBright !== this.updateKeyBright) {
289
+ return
290
+ }
291
+
292
+ // Don't continue if the new value is the same as before
293
+ if (value === this.cacheBright) {
294
+ return
295
+ }
296
+
297
+ // Generate the hex values for the code
298
+ const hexValues = [0x3A, 0x1B, 0x01, 0x02, value]
299
+
300
+ // Send the request to the platform sender function
301
+ await this.platform.sendDeviceUpdate(this.accessory, {
302
+ cmd: 'multiSync',
303
+ value: generateCodeFromHexValues(hexValues),
304
+ })
305
+
306
+ // Govee considers 0% brightness to be off
307
+ if (value === 0) {
308
+ setTimeout(() => {
309
+ this.cacheLightState = 'off'
310
+ if (this.lightService.getCharacteristic(this.hapChar.On).value) {
311
+ this.lightService.updateCharacteristic(this.hapChar.On, false)
312
+ this.accessory.log(`${platformLang.curLight} [${this.cacheLightState}]`)
313
+ }
314
+ this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
315
+ }, 1500)
316
+ return
317
+ }
318
+
319
+ // Cache the new state and log if appropriate
320
+ if (this.cacheBright !== value) {
321
+ this.cacheBright = value
322
+ this.accessory.log(`${platformLang.curBright} [${value}%]`)
323
+ }
324
+ } catch (err) {
325
+ // Catch any errors during the process
326
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
327
+
328
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
329
+ setTimeout(() => {
330
+ this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
331
+ }, 2000)
332
+ throw new this.hapErr(-70402)
333
+ }
334
+ }
335
+
336
+ async internalColourUpdate(value) {
337
+ try {
338
+ // This acts like a debounce function when endlessly sliding the colour wheel
339
+ const updateKeyColour = generateRandomString(5)
340
+ this.updateKeyColour = updateKeyColour
341
+ await sleep(300)
342
+ if (updateKeyColour !== this.updateKeyColour) {
343
+ return
344
+ }
345
+
346
+ // Don't continue if the new value is the same as before
347
+ if (value === this.cacheHue) {
348
+ return
349
+ }
350
+
351
+ // Calculate RGB values
352
+ const newRGB = hs2rgb(value, this.lightService.getCharacteristic(this.hapChar.Saturation).value)
353
+
354
+ // Generate the hex values for the code
355
+ const hexValues = [0x3A, 0x1B, 0x05, 0x0D, ...newRGB]
356
+
357
+ // Send the request to the platform sender function
358
+ await this.platform.sendDeviceUpdate(this.accessory, {
359
+ cmd: 'multiSync',
360
+ value: generateCodeFromHexValues(hexValues),
361
+ })
362
+
363
+ // Cache the new state and log if appropriate
364
+ if (this.cacheHue !== value) {
365
+ this.cacheHue = value
366
+ this.accessory.log(`${platformLang.curColour} [rgb ${newRGB.join(' ')}]`)
367
+ }
368
+ } catch (err) {
369
+ // Catch any errors during the process
370
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
371
+
372
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
373
+ setTimeout(() => {
374
+ this.lightService.updateCharacteristic(this.hapChar.Hue, this.cacheHue)
375
+ }, 2000)
376
+ throw new this.hapErr(-70402)
377
+ }
378
+ }
379
+
380
+ externalUpdate(params) {
381
+ // Update the active characteristic
382
+ if (params.state && params.state !== this.cacheState) {
383
+ this.cacheState = params.state
384
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
385
+ this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
386
+ }
387
+
388
+ // Check for some other scene/mode change
389
+ (params.commands || []).forEach((command) => {
390
+ const hexString = base64ToHex(command)
391
+ const hexParts = hexToTwoItems(hexString)
392
+
393
+ // Return now if not a device query update code
394
+ if (getTwoItemPosition(hexParts, 1) !== 'aa') {
395
+ return
396
+ }
397
+
398
+ if (getTwoItemPosition(hexParts, 2) === '08') {
399
+ // Sensor Attached?
400
+ const dev = hexString.substring(4, hexString.length - 24)
401
+ this.accessory.context.sensorAttached = dev !== '000000000000'
402
+ return
403
+ }
404
+
405
+ const deviceFunction = `${getTwoItemPosition(hexParts, 2)}${getTwoItemPosition(hexParts, 3)}`
406
+
407
+ switch (deviceFunction) {
408
+ case '0501': {
409
+ // Fan speed
410
+ const newSpeed = getTwoItemPosition(hexParts, 4)
411
+ const newSpeedInt = Number.parseInt(newSpeed, 16) * 7
412
+ const newMode = 'manual'
413
+ if (this.cacheMode !== newMode) {
414
+ this.cacheMode = newMode
415
+ this.accessory.log(`${platformLang.curMode} [${this.cacheMode}]`)
416
+ }
417
+ if (this.cacheSpeed !== newSpeedInt) {
418
+ this.cacheSpeed = newSpeedInt
419
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
420
+ this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
421
+ }
422
+ break
423
+ }
424
+ case '0500': {
425
+ // Auto mode on/off
426
+ // Maybe this relates to
427
+ // (Guess) Fixed Speed: 1
428
+ // Custom: 2
429
+ // Auto: 3
430
+ // Sleep: 5
431
+ // Nature: 6
432
+ // Turbo: 7
433
+ const newMode = getTwoItemPosition(hexParts, 4) === '03' ? 'auto' : 'manual'
434
+ if (this.cacheMode !== newMode) {
435
+ this.cacheMode = newMode
436
+ this.accessory.log(`${platformLang.curMode} [${this.cacheMode}]`)
437
+
438
+ if (this.cacheMode === 'auto' && this.cacheSpeed < 91) {
439
+ this.cacheSpeed = 91
440
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
441
+ this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
442
+ }
443
+ }
444
+ break
445
+ }
446
+ case '0503': {
447
+ // Auto mode, we need to keep this code to send it back to the device
448
+ const code = hexToTwoItems(`33${hexString.substring(2, hexString.length - 2)}`)
449
+ this.cacheAutoCode = generateCodeFromHexValues(code.map(p => Number.parseInt(p, 16)))
450
+ break
451
+ }
452
+ case '1b01': {
453
+ const newLightState = getTwoItemPosition(hexParts, 4) === '01' ? 'on' : 'off'
454
+ if (this.cacheLightState !== newLightState) {
455
+ this.cacheLightState = newLightState
456
+ this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLightState === 'on')
457
+ this.accessory.log(`${platformLang.curLight} [${this.cacheLightState}]`)
458
+ }
459
+ const newBrightness = hexToDecimal(getTwoItemPosition(hexParts, 5))
460
+ if (this.cacheBright !== newBrightness) {
461
+ this.cacheBright = newBrightness
462
+ this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
463
+ this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
464
+ }
465
+ break
466
+ }
467
+ case '1b05': {
468
+ // Night light colour
469
+ const newR = hexToDecimal(getTwoItemPosition(hexParts, 5))
470
+ const newG = hexToDecimal(getTwoItemPosition(hexParts, 6))
471
+ const newB = hexToDecimal(getTwoItemPosition(hexParts, 7))
472
+
473
+ const hs = rgb2hs(newR, newG, newB)
474
+
475
+ // Check for a colour change
476
+ if (hs[0] !== this.cacheHue) {
477
+ // Colour is different so update Homebridge with new values
478
+ this.lightService.updateCharacteristic(this.hapChar.Hue, hs[0])
479
+ this.lightService.updateCharacteristic(this.hapChar.Saturation, hs[1]);
480
+ [this.cacheHue] = hs
481
+
482
+ // Log the change
483
+ this.accessory.log(`${platformLang.curColour} [rgb ${newR} ${newG} ${newB}]`)
484
+ }
485
+ break
486
+ }
487
+ case '1f01': {
488
+ // Swing Mode
489
+ const newSwing = getTwoItemPosition(hexParts, 4) === '01' ? 'on' : 'off'
490
+ if (this.cacheSwing !== newSwing) {
491
+ this.cacheSwing = newSwing
492
+ this.service.updateCharacteristic(this.hapChar.SwingMode, this.cacheSwing === 'on' ? 1 : 0)
493
+ this.accessory.log(`${platformLang.curSwing} [${this.cacheSwing}]`)
494
+ }
495
+ break
496
+ }
497
+ default:
498
+ this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
499
+ break
500
+ }
501
+ })
502
+ }
503
+ }