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