@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,336 @@
1
+ import {
2
+ base64ToHex,
3
+ getTwoItemPosition,
4
+ hexToTwoItems,
5
+ parseError,
6
+ } from '../utils/functions.js'
7
+ import platformLang from '../utils/lang-en.js'
8
+
9
+ /*
10
+ H7120
11
+ {
12
+ "mode": {
13
+ "options": [
14
+ {
15
+ "name": "Low",
16
+ "value": 1
17
+ },
18
+ {
19
+ "name": "Medium",
20
+ "value": 2
21
+ },
22
+ {
23
+ "name": "High",
24
+ "value": 3
25
+ },
26
+ {
27
+ "name": "Sleep mode",
28
+ "value": 5
29
+ }
30
+ ]
31
+ }
32
+ }
33
+ */
34
+
35
+ export default class {
36
+ constructor(platform, accessory) {
37
+ // Set up variables from the platform
38
+ this.cusChar = platform.cusChar
39
+ this.hapChar = platform.api.hap.Characteristic
40
+ this.hapErr = platform.api.hap.HapStatusError
41
+ this.hapServ = platform.api.hap.Service
42
+ this.platform = platform
43
+
44
+ // Set up variables from the accessory
45
+ this.accessory = accessory
46
+
47
+ // Rotation speed to value in {1, 2, 3, 4}
48
+ this.speed2Value = speed => Math.min(Math.max(Number.parseInt(speed / 25, 10), 1), 4)
49
+
50
+ // Speed codes
51
+ this.value2Code = {
52
+ 1: 'MwUQAAAAAAAAAAAAAAAAAAAAACY=',
53
+ 2: 'MwUBAAAAAAAAAAAAAAAAAAAAADc=',
54
+ 3: 'MwUCAAAAAAAAAAAAAAAAAAAAADQ=',
55
+ 4: 'MwUDAAAAAAAAAAAAAAAAAAAAADU=',
56
+ }
57
+
58
+ // Night light codes
59
+ this.night2Code = {
60
+ on: 'MxgBMgAAAAAAAAAAAAAAAAAAABg=',
61
+ off: 'MxgAMgAAAAAAAAAAAAAAAAAAABk=',
62
+ }
63
+
64
+ // Lock codes
65
+ this.lock2Code = {
66
+ on: 'MxABAAAAAAAAAAAAAAAAAAAAACI=',
67
+ off: 'MxAAAAAAAAAAAAAAAAAAAAAAACM=',
68
+ }
69
+
70
+ // Display codes
71
+ this.display2Code = {
72
+ on: 'MxYBAAAAAAAAAAAAAAAAAAAAACQ=',
73
+ off: 'MxYAAAAAAAAAAAAAAAAAAAAAACU=',
74
+ }
75
+
76
+ // Add the purifier service if it doesn't already exist
77
+ this.service = this.accessory.getService(this.hapServ.AirPurifier)
78
+ || this.accessory.addService(this.hapServ.AirPurifier)
79
+
80
+ // Add the set handler to the switch on/off characteristic
81
+ this.service.getCharacteristic(this.hapChar.Active).onSet(async (value) => {
82
+ await this.internalStateUpdate(value)
83
+ })
84
+ this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value === 1 ? 'on' : 'off'
85
+
86
+ // Add options to the purifier target state characteristic
87
+ this.service
88
+ .getCharacteristic(this.hapChar.TargetAirPurifierState)
89
+ .updateValue(1)
90
+ .setProps({
91
+ minValue: 1,
92
+ maxValue: 1,
93
+ validValues: [1],
94
+ })
95
+
96
+ // Add the set handler to the fan rotation speed characteristic
97
+ this.service
98
+ .getCharacteristic(this.hapChar.RotationSpeed)
99
+ .setProps({
100
+ minStep: 25,
101
+ validValues: [0, 25, 50, 75, 100],
102
+ })
103
+ .onSet(async value => this.internalSpeedUpdate(value))
104
+ this.cacheSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
105
+
106
+ // Add the set handler to the lock controls characteristic
107
+ this.service.getCharacteristic(this.hapChar.LockPhysicalControls).onSet(async (value) => {
108
+ await this.internalLockUpdate(value)
109
+ })
110
+ this.cacheLock = this.service.getCharacteristic(this.hapChar.LockPhysicalControls).value === 1 ? 'on' : 'off'
111
+
112
+ // Add night light Eve characteristic if it doesn't exist already
113
+ if (!this.service.testCharacteristic(this.cusChar.NightLight)) {
114
+ this.service.addCharacteristic(this.cusChar.NightLight)
115
+ }
116
+
117
+ // Add the set handler to the custom night light characteristic
118
+ this.service.getCharacteristic(this.cusChar.NightLight).onSet(async (value) => {
119
+ await this.internalNightLightUpdate(value)
120
+ })
121
+ this.cacheLight = this.service.getCharacteristic(this.cusChar.NightLight).value ? 'on' : 'off'
122
+
123
+ // Add display light Eve characteristic if it doesn't exist already
124
+ if (!this.service.testCharacteristic(this.cusChar.DisplayLight)) {
125
+ this.service.addCharacteristic(this.cusChar.DisplayLight)
126
+ }
127
+
128
+ // Add the set handler to the custom display light characteristic
129
+ this.service.getCharacteristic(this.cusChar.DisplayLight).onSet(async (value) => {
130
+ await this.internalDisplayLightUpdate(value)
131
+ })
132
+ this.cacheDisplay = this.service.getCharacteristic(this.cusChar.DisplayLight).value
133
+ ? 'on'
134
+ : 'off'
135
+
136
+ // Output the customised options to the log
137
+ const opts = JSON.stringify({})
138
+ platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
139
+ }
140
+
141
+ async internalStateUpdate(value) {
142
+ try {
143
+ const newValue = value === 1 ? 'on' : 'off'
144
+
145
+ // Don't continue if the new value is the same as before
146
+ if (this.cacheState === newValue) {
147
+ return
148
+ }
149
+
150
+ // Send the request to the platform sender function
151
+ await this.platform.sendDeviceUpdate(this.accessory, {
152
+ cmd: 'statePuri',
153
+ value: value ? 1 : 0,
154
+ })
155
+
156
+ // Update the current state characteristic
157
+ this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, value === 1 ? 2 : 0)
158
+
159
+ // Cache the new state and log if appropriate
160
+ this.cacheState = newValue
161
+ this.accessory.log(`${platformLang.curState} [${newValue}]`)
162
+ } catch (err) {
163
+ // Catch any errors during the process
164
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
165
+
166
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
167
+ setTimeout(() => {
168
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
169
+ }, 2000)
170
+ throw new this.hapErr(-70402)
171
+ }
172
+ }
173
+
174
+ async internalSpeedUpdate(value) {
175
+ try {
176
+ // Don't continue if the speed is 0
177
+ if (value === 0) {
178
+ return
179
+ }
180
+
181
+ // Get the single Govee value {1, 2, ..., 8}
182
+ const newValue = this.speed2Value(value)
183
+
184
+ // Don't continue if the speed value won't have effect
185
+ if (newValue * 25 === this.cacheSpeed) {
186
+ return
187
+ }
188
+
189
+ // Get the scene code for this value
190
+ const newCode = this.value2Code[newValue]
191
+
192
+ // Send the request to the platform sender function
193
+ await this.platform.sendDeviceUpdate(this.accessory, {
194
+ cmd: 'ptReal',
195
+ value: newCode,
196
+ })
197
+
198
+ // Cache the new state and log if appropriate
199
+ this.cacheSpeed = newValue * 25
200
+ this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
201
+ } catch (err) {
202
+ // Catch any errors during the process
203
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
204
+
205
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
206
+ setTimeout(() => {
207
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
208
+ }, 2000)
209
+ throw new this.hapErr(-70402)
210
+ }
211
+ }
212
+
213
+ async internalNightLightUpdate(value) {
214
+ try {
215
+ const newValue = value ? 'on' : 'off'
216
+
217
+ // Don't continue if the new value is the same as before
218
+ if (this.cacheLight === newValue) {
219
+ return
220
+ }
221
+
222
+ // Send the request to the platform sender function
223
+ await this.platform.sendDeviceUpdate(this.accessory, {
224
+ cmd: 'ptReal',
225
+ value: this.night2Code[newValue],
226
+ })
227
+
228
+ // Cache the new state and log if appropriate
229
+ this.cacheLight = newValue
230
+ this.accessory.log(`${platformLang.curLight} [${newValue}]`)
231
+ } catch (err) {
232
+ // Catch any errors during the process
233
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
234
+
235
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
236
+ setTimeout(() => {
237
+ this.service.updateCharacteristic(this.cusChar.NightLight, this.cacheLight === 'on')
238
+ }, 2000)
239
+ throw new this.hapErr(-70402)
240
+ }
241
+ }
242
+
243
+ async internalLockUpdate(value) {
244
+ try {
245
+ const newValue = value === 1 ? 'on' : 'off'
246
+
247
+ // Don't continue if the new value is the same as before
248
+ if (this.cacheLock === newValue) {
249
+ return
250
+ }
251
+
252
+ // Send the request to the platform sender function
253
+ await this.platform.sendDeviceUpdate(this.accessory, {
254
+ cmd: 'ptReal',
255
+ value: this.lock2Code[newValue],
256
+ })
257
+
258
+ // Cache the new state and log if appropriate
259
+ this.cacheLock = newValue
260
+ this.accessory.log(`${platformLang.curLock} [${newValue}]`)
261
+ } catch (err) {
262
+ // Catch any errors during the process
263
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
264
+
265
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
266
+ setTimeout(() => {
267
+ this.service.updateCharacteristic(
268
+ this.hapChar.LockPhysicalControls,
269
+ this.cacheLock === 'on' ? 1 : 0,
270
+ )
271
+ }, 2000)
272
+ throw new this.hapErr(-70402)
273
+ }
274
+ }
275
+
276
+ async internalDisplayLightUpdate(value) {
277
+ try {
278
+ const newValue = value ? 'on' : 'off'
279
+
280
+ // Don't continue if the new value is the same as before
281
+ if (this.cacheDisplay === newValue) {
282
+ return
283
+ }
284
+
285
+ // Send the request to the platform sender function
286
+ await this.platform.sendDeviceUpdate(this.accessory, {
287
+ cmd: 'ptReal',
288
+ value: this.display2Code[newValue],
289
+ })
290
+
291
+ // Cache the new state and log if appropriate
292
+ this.cacheDisplay = newValue
293
+ this.accessory.log(`${platformLang.curDisplay} [${newValue}]`)
294
+ } catch (err) {
295
+ // Catch any errors during the process
296
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
297
+
298
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
299
+ setTimeout(() => {
300
+ this.service.updateCharacteristic(this.cusChar.DisplayLight, this.cacheLight === 'on')
301
+ }, 2000)
302
+ throw new this.hapErr(-70402)
303
+ }
304
+ }
305
+
306
+ externalUpdate(params) {
307
+ // Check for an ON/OFF change
308
+ if (params.state && params.state !== this.cacheState) {
309
+ this.cacheState = params.state
310
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on')
311
+ this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, this.cacheState === 'on' ? 2 : 0)
312
+
313
+ // Log the change
314
+ this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
315
+ }
316
+
317
+ // Check for some other scene/mode change
318
+ (params.commands || []).forEach((command) => {
319
+ const hexString = base64ToHex(command)
320
+ const hexParts = hexToTwoItems(hexString)
321
+
322
+ // Return now if not a device query update code
323
+ if (getTwoItemPosition(hexParts, 1) !== 'aa') {
324
+ return
325
+ }
326
+
327
+ const deviceFunction = `${getTwoItemPosition(hexParts, 1)}${getTwoItemPosition(hexParts, 2)}`
328
+
329
+ switch (deviceFunction) {
330
+ default:
331
+ this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
332
+ break
333
+ }
334
+ })
335
+ }
336
+ }
@@ -0,0 +1,336 @@
1
+ import {
2
+ base64ToHex,
3
+ getTwoItemPosition,
4
+ hexToTwoItems,
5
+ parseError,
6
+ } from '../utils/functions.js'
7
+ import platformLang from '../utils/lang-en.js'
8
+
9
+ /*
10
+ H7121
11
+ {
12
+ "mode": {
13
+ "options": [
14
+ {
15
+ "name": "Low",
16
+ "value": 1
17
+ },
18
+ {
19
+ "name": "Medium",
20
+ "value": 2
21
+ },
22
+ {
23
+ "name": "High",
24
+ "value": 3
25
+ },
26
+ {
27
+ "name": "Sleep",
28
+ "value": 16
29
+ }
30
+ ]
31
+ }
32
+ }
33
+ */
34
+
35
+ export default class {
36
+ constructor(platform, accessory) {
37
+ // Set up variables from the platform
38
+ this.cusChar = platform.cusChar
39
+ this.hapChar = platform.api.hap.Characteristic
40
+ this.hapErr = platform.api.hap.HapStatusError
41
+ this.hapServ = platform.api.hap.Service
42
+ this.platform = platform
43
+
44
+ // Set up variables from the accessory
45
+ this.accessory = accessory
46
+
47
+ // Rotation speed to value in {1, 2, 3, 4}
48
+ this.speed2Value = speed => Math.min(Math.max(Number.parseInt(speed / 25, 10), 1), 4)
49
+
50
+ // Speed codes
51
+ this.value2Code = {
52
+ 1: 'MwUQAAAAAAAAAAAAAAAAAAAAACY=',
53
+ 2: 'MwUBAAAAAAAAAAAAAAAAAAAAADc=',
54
+ 3: 'MwUCAAAAAAAAAAAAAAAAAAAAADQ=',
55
+ 4: 'MwUDAAAAAAAAAAAAAAAAAAAAADU=',
56
+ }
57
+
58
+ // Night light codes
59
+ this.night2Code = {
60
+ on: 'MxgBMgAAAAAAAAAAAAAAAAAAABg=',
61
+ off: 'MxgAMgAAAAAAAAAAAAAAAAAAABk=',
62
+ }
63
+
64
+ // Lock codes
65
+ this.lock2Code = {
66
+ on: 'MxABAAAAAAAAAAAAAAAAAAAAACI=',
67
+ off: 'MxAAAAAAAAAAAAAAAAAAAAAAACM=',
68
+ }
69
+
70
+ // Display codes
71
+ this.display2Code = {
72
+ on: 'MxYBAAAAAAAAAAAAAAAAAAAAACQ=',
73
+ off: 'MxYAAAAAAAAAAAAAAAAAAAAAACU=',
74
+ }
75
+
76
+ // Add the purifier service if it doesn't already exist
77
+ this.service = this.accessory.getService(this.hapServ.AirPurifier)
78
+ || this.accessory.addService(this.hapServ.AirPurifier)
79
+
80
+ // Add the set handler to the switch on/off characteristic
81
+ this.service.getCharacteristic(this.hapChar.Active).onSet(async (value) => {
82
+ await this.internalStateUpdate(value)
83
+ })
84
+ this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value === 1 ? 'on' : 'off'
85
+
86
+ // Add options to the purifier target state characteristic
87
+ this.service
88
+ .getCharacteristic(this.hapChar.TargetAirPurifierState)
89
+ .updateValue(1)
90
+ .setProps({
91
+ minValue: 1,
92
+ maxValue: 1,
93
+ validValues: [1],
94
+ })
95
+
96
+ // Add the set handler to the fan rotation speed characteristic
97
+ this.service
98
+ .getCharacteristic(this.hapChar.RotationSpeed)
99
+ .setProps({
100
+ minStep: 25,
101
+ validValues: [0, 25, 50, 75, 100],
102
+ })
103
+ .onSet(async value => this.internalSpeedUpdate(value))
104
+ this.cacheSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
105
+
106
+ // Add the set handler to the lock controls characteristic
107
+ this.service.getCharacteristic(this.hapChar.LockPhysicalControls).onSet(async (value) => {
108
+ await this.internalLockUpdate(value)
109
+ })
110
+ this.cacheLock = this.service.getCharacteristic(this.hapChar.LockPhysicalControls).value === 1 ? 'on' : 'off'
111
+
112
+ // Add night light Eve characteristic if it doesn't exist already
113
+ if (!this.service.testCharacteristic(this.cusChar.NightLight)) {
114
+ this.service.addCharacteristic(this.cusChar.NightLight)
115
+ }
116
+
117
+ // Add the set handler to the custom night light characteristic
118
+ this.service.getCharacteristic(this.cusChar.NightLight).onSet(async (value) => {
119
+ await this.internalNightLightUpdate(value)
120
+ })
121
+ this.cacheLight = this.service.getCharacteristic(this.cusChar.NightLight).value ? 'on' : 'off'
122
+
123
+ // Add display light Eve characteristic if it doesn't exist already
124
+ if (!this.service.testCharacteristic(this.cusChar.DisplayLight)) {
125
+ this.service.addCharacteristic(this.cusChar.DisplayLight)
126
+ }
127
+
128
+ // Add the set handler to the custom display light characteristic
129
+ this.service.getCharacteristic(this.cusChar.DisplayLight).onSet(async (value) => {
130
+ await this.internalDisplayLightUpdate(value)
131
+ })
132
+ this.cacheDisplay = this.service.getCharacteristic(this.cusChar.DisplayLight).value
133
+ ? 'on'
134
+ : 'off'
135
+
136
+ // Output the customised options to the log
137
+ const opts = JSON.stringify({})
138
+ platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
139
+ }
140
+
141
+ async internalStateUpdate(value) {
142
+ try {
143
+ const newValue = value === 1 ? 'on' : 'off'
144
+
145
+ // Don't continue if the new value is the same as before
146
+ if (this.cacheState === newValue) {
147
+ return
148
+ }
149
+
150
+ // Send the request to the platform sender function
151
+ await this.platform.sendDeviceUpdate(this.accessory, {
152
+ cmd: 'statePuri',
153
+ value: value ? 1 : 0,
154
+ })
155
+
156
+ // Update the current state characteristic
157
+ this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, value === 1 ? 2 : 0)
158
+
159
+ // Cache the new state and log if appropriate
160
+ this.cacheState = newValue
161
+ this.accessory.log(`${platformLang.curState} [${newValue}]`)
162
+ } catch (err) {
163
+ // Catch any errors during the process
164
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
165
+
166
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
167
+ setTimeout(() => {
168
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
169
+ }, 2000)
170
+ throw new this.hapErr(-70402)
171
+ }
172
+ }
173
+
174
+ async internalSpeedUpdate(value) {
175
+ try {
176
+ // Don't continue if the speed is 0
177
+ if (value === 0) {
178
+ return
179
+ }
180
+
181
+ // Get the single Govee value {1, 2, ..., 8}
182
+ const newValue = this.speed2Value(value)
183
+
184
+ // Don't continue if the speed value won't have effect
185
+ if (newValue * 25 === this.cacheSpeed) {
186
+ return
187
+ }
188
+
189
+ // Get the scene code for this value
190
+ const newCode = this.value2Code[newValue]
191
+
192
+ // Send the request to the platform sender function
193
+ await this.platform.sendDeviceUpdate(this.accessory, {
194
+ cmd: 'ptReal',
195
+ value: newCode,
196
+ })
197
+
198
+ // Cache the new state and log if appropriate
199
+ this.cacheSpeed = newValue * 25
200
+ this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
201
+ } catch (err) {
202
+ // Catch any errors during the process
203
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
204
+
205
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
206
+ setTimeout(() => {
207
+ this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
208
+ }, 2000)
209
+ throw new this.hapErr(-70402)
210
+ }
211
+ }
212
+
213
+ async internalNightLightUpdate(value) {
214
+ try {
215
+ const newValue = value ? 'on' : 'off'
216
+
217
+ // Don't continue if the new value is the same as before
218
+ if (this.cacheLight === newValue) {
219
+ return
220
+ }
221
+
222
+ // Send the request to the platform sender function
223
+ await this.platform.sendDeviceUpdate(this.accessory, {
224
+ cmd: 'ptReal',
225
+ value: this.night2Code[newValue],
226
+ })
227
+
228
+ // Cache the new state and log if appropriate
229
+ this.cacheLight = newValue
230
+ this.accessory.log(`${platformLang.curLight} [${newValue}]`)
231
+ } catch (err) {
232
+ // Catch any errors during the process
233
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
234
+
235
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
236
+ setTimeout(() => {
237
+ this.service.updateCharacteristic(this.cusChar.NightLight, this.cacheLight === 'on')
238
+ }, 2000)
239
+ throw new this.hapErr(-70402)
240
+ }
241
+ }
242
+
243
+ async internalLockUpdate(value) {
244
+ try {
245
+ const newValue = value === 1 ? 'on' : 'off'
246
+
247
+ // Don't continue if the new value is the same as before
248
+ if (this.cacheLock === newValue) {
249
+ return
250
+ }
251
+
252
+ // Send the request to the platform sender function
253
+ await this.platform.sendDeviceUpdate(this.accessory, {
254
+ cmd: 'ptReal',
255
+ value: this.lock2Code[newValue],
256
+ })
257
+
258
+ // Cache the new state and log if appropriate
259
+ this.cacheLock = newValue
260
+ this.accessory.log(`${platformLang.curLock} [${newValue}]`)
261
+ } catch (err) {
262
+ // Catch any errors during the process
263
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
264
+
265
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
266
+ setTimeout(() => {
267
+ this.service.updateCharacteristic(
268
+ this.hapChar.LockPhysicalControls,
269
+ this.cacheLock === 'on' ? 1 : 0,
270
+ )
271
+ }, 2000)
272
+ throw new this.hapErr(-70402)
273
+ }
274
+ }
275
+
276
+ async internalDisplayLightUpdate(value) {
277
+ try {
278
+ const newValue = value ? 'on' : 'off'
279
+
280
+ // Don't continue if the new value is the same as before
281
+ if (this.cacheDisplay === newValue) {
282
+ return
283
+ }
284
+
285
+ // Send the request to the platform sender function
286
+ await this.platform.sendDeviceUpdate(this.accessory, {
287
+ cmd: 'ptReal',
288
+ value: this.display2Code[newValue],
289
+ })
290
+
291
+ // Cache the new state and log if appropriate
292
+ this.cacheDisplay = newValue
293
+ this.accessory.log(`${platformLang.curDisplay} [${newValue}]`)
294
+ } catch (err) {
295
+ // Catch any errors during the process
296
+ this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
297
+
298
+ // Throw a 'no response' error and set a timeout to revert this after 2 seconds
299
+ setTimeout(() => {
300
+ this.service.updateCharacteristic(this.cusChar.DisplayLight, this.cacheLight === 'on')
301
+ }, 2000)
302
+ throw new this.hapErr(-70402)
303
+ }
304
+ }
305
+
306
+ externalUpdate(params) {
307
+ // Check for an ON/OFF change
308
+ if (params.state && params.state !== this.cacheState) {
309
+ this.cacheState = params.state
310
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on')
311
+ this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, this.cacheState === 'on' ? 2 : 0)
312
+
313
+ // Log the change
314
+ this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
315
+ }
316
+
317
+ // Check for some other scene/mode change
318
+ (params.commands || []).forEach((command) => {
319
+ const hexString = base64ToHex(command)
320
+ const hexParts = hexToTwoItems(hexString)
321
+
322
+ // Return now if not a device query update code
323
+ if (getTwoItemPosition(hexParts, 1) !== 'aa') {
324
+ return
325
+ }
326
+
327
+ const deviceFunction = `${getTwoItemPosition(hexParts, 1)}${getTwoItemPosition(hexParts, 2)}`
328
+
329
+ switch (deviceFunction) {
330
+ default:
331
+ this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
332
+ break
333
+ }
334
+ })
335
+ }
336
+ }