@homebridge-plugins/homebridge-govee 11.32.2-beta.2 → 11.32.2-beta.21

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 (73) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/config.schema.json +1 -1
  3. package/lib/connection/ble.js +26 -1
  4. package/lib/connection/openapi.js +18 -0
  5. package/lib/device/appliance-basic.js +377 -0
  6. package/lib/device/dehumidifier-H7150.js +9 -3
  7. package/lib/device/diffuser-H7161.js +7 -1
  8. package/lib/device/fan-H1370.js +7 -1
  9. package/lib/device/fan-H7100.js +36 -4
  10. package/lib/device/fan-H7105.js +7 -1
  11. package/lib/device/fan-H7107.js +7 -1
  12. package/lib/device/heater1a.js +52 -12
  13. package/lib/device/heater1b.js +14 -3
  14. package/lib/device/heater2.js +7 -1
  15. package/lib/device/humidifier-H7140.js +7 -1
  16. package/lib/device/humidifier-H7141.js +7 -1
  17. package/lib/device/humidifier-H7142.js +7 -1
  18. package/lib/device/humidifier-H7143.js +7 -1
  19. package/lib/device/humidifier-H7160.js +7 -1
  20. package/lib/device/ice-maker-H7172.js +19 -7
  21. package/lib/device/index.js +44 -51
  22. package/lib/device/kettle.js +7 -1
  23. package/lib/device/outlet-single.js +7 -1
  24. package/lib/device/purifier-H7120.js +14 -25
  25. package/lib/device/purifier-H7122.js +7 -1
  26. package/lib/device/purifier-H7123.js +9 -3
  27. package/lib/device/purifier-H7126.js +7 -1
  28. package/lib/device/{sensor-button.js → sensor-basic.js} +14 -2
  29. package/lib/device/sensor-presence.js +7 -1
  30. package/lib/device/sensor-thermo4.js +13 -2
  31. package/lib/device/switch-double.js +34 -15
  32. package/lib/device/switch-triple.js +36 -17
  33. package/lib/device/template.js +15 -2
  34. package/lib/platform.js +83 -799
  35. package/lib/utils/capabilities.js +220 -0
  36. package/lib/utils/command-builder.js +6 -4
  37. package/lib/utils/config-parser.js +280 -0
  38. package/lib/utils/constants.js +120 -8
  39. package/lib/utils/decode.js +4 -1
  40. package/lib/utils/device-capabilities.js +77 -3
  41. package/lib/utils/device-context.js +179 -0
  42. package/lib/utils/device-merge.js +251 -0
  43. package/lib/utils/dispatch.js +159 -0
  44. package/lib/utils/functions.js +9 -0
  45. package/lib/utils/lang-en.js +0 -2
  46. package/lib/utils/report-unknown.js +156 -0
  47. package/package.json +1 -1
  48. package/lib/device/dehumidifier-H7151.js +0 -189
  49. package/lib/device/dehumidifier-H7152.js +0 -180
  50. package/lib/device/diffuser-H7162.js +0 -123
  51. package/lib/device/fan-H1310.js +0 -284
  52. package/lib/device/fan-H7101.js +0 -340
  53. package/lib/device/fan-H7102.js +0 -284
  54. package/lib/device/fan-H7106.js +0 -284
  55. package/lib/device/fan-H7111.js +0 -345
  56. package/lib/device/humidifier-H7145.js +0 -187
  57. package/lib/device/humidifier-H7147.js +0 -187
  58. package/lib/device/humidifier-H7148.js +0 -187
  59. package/lib/device/humidifier-H7149.js +0 -187
  60. package/lib/device/humidifier-H714E.js +0 -187
  61. package/lib/device/ice-maker-H717D.js +0 -218
  62. package/lib/device/ice-maker-H8120.js +0 -218
  63. package/lib/device/outlet-double.js +0 -188
  64. package/lib/device/outlet-triple.js +0 -160
  65. package/lib/device/purifier-H7121.js +0 -406
  66. package/lib/device/purifier-H7124.js +0 -504
  67. package/lib/device/purifier-H7127.js +0 -359
  68. package/lib/device/purifier-H7128.js +0 -359
  69. package/lib/device/purifier-H7129.js +0 -359
  70. package/lib/device/purifier-H712C.js +0 -359
  71. package/lib/device/sensor-contact.js +0 -22
  72. package/lib/utils/ble-timeouts.test.js +0 -44
  73. package/lib/utils/functions.test.js +0 -292
@@ -1,406 +0,0 @@
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
- openApi: this.accessory.context.openApiCapabilities?.workMode
197
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: 1, modeValue: newValue } }
198
- : undefined,
199
- })
200
-
201
- // Cache the new state and log if appropriate
202
- this.cacheSpeed = newValue * 25
203
- this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
204
- } catch (err) {
205
- // Catch any errors during the process
206
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
207
-
208
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
209
- setTimeout(() => {
210
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
211
- }, 2000)
212
- throw new this.hapErr(-70402)
213
- }
214
- }
215
-
216
- async internalNightLightUpdate(value) {
217
- try {
218
- const newValue = value ? 'on' : 'off'
219
-
220
- // Don't continue if the new value is the same as before
221
- if (this.cacheLight === newValue) {
222
- return
223
- }
224
-
225
- // Send the request to the platform sender function
226
- await this.platform.sendDeviceUpdate(this.accessory, {
227
- cmd: 'ptReal',
228
- value: this.night2Code[newValue],
229
- openApi: this.accessory.context.openApiCapabilities?.nightlightToggle
230
- ? { instance: 'nightlightToggle', capabilityType: 'devices.capabilities.toggle', value: newValue === 'on' ? 1 : 0 }
231
- : undefined,
232
- })
233
-
234
- // Cache the new state and log if appropriate
235
- this.cacheLight = newValue
236
- this.accessory.log(`${platformLang.curLight} [${newValue}]`)
237
- } catch (err) {
238
- // Catch any errors during the process
239
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
240
-
241
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
242
- setTimeout(() => {
243
- this.service.updateCharacteristic(this.cusChar.NightLight, this.cacheLight === 'on')
244
- }, 2000)
245
- throw new this.hapErr(-70402)
246
- }
247
- }
248
-
249
- async internalLockUpdate(value) {
250
- try {
251
- const newValue = value === 1 ? 'on' : 'off'
252
-
253
- // Don't continue if the new value is the same as before
254
- if (this.cacheLock === newValue) {
255
- return
256
- }
257
-
258
- // Send the request to the platform sender function
259
- await this.platform.sendDeviceUpdate(this.accessory, {
260
- cmd: 'ptReal',
261
- value: this.lock2Code[newValue],
262
- openApi: this.accessory.context.openApiCapabilities?.lockToggle
263
- ? { instance: 'lockToggle', capabilityType: 'devices.capabilities.toggle', value: newValue === 'on' ? 1 : 0 }
264
- : undefined,
265
- })
266
-
267
- // Cache the new state and log if appropriate
268
- this.cacheLock = newValue
269
- this.accessory.log(`${platformLang.curLock} [${newValue}]`)
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.service.updateCharacteristic(
277
- this.hapChar.LockPhysicalControls,
278
- this.cacheLock === 'on' ? 1 : 0,
279
- )
280
- }, 2000)
281
- throw new this.hapErr(-70402)
282
- }
283
- }
284
-
285
- async internalDisplayLightUpdate(value) {
286
- try {
287
- const newValue = value ? 'on' : 'off'
288
-
289
- // Don't continue if the new value is the same as before
290
- if (this.cacheDisplay === newValue) {
291
- return
292
- }
293
-
294
- // Send the request to the platform sender function
295
- await this.platform.sendDeviceUpdate(this.accessory, {
296
- cmd: 'ptReal',
297
- value: this.display2Code[newValue],
298
- openApi: this.accessory.context.openApiCapabilities?.displayToggle
299
- ? { instance: 'displayToggle', capabilityType: 'devices.capabilities.toggle', value: newValue === 'on' ? 1 : 0 }
300
- : undefined,
301
- })
302
-
303
- // Cache the new state and log if appropriate
304
- this.cacheDisplay = newValue
305
- this.accessory.log(`${platformLang.curDisplay} [${newValue}]`)
306
- } catch (err) {
307
- // Catch any errors during the process
308
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
309
-
310
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
311
- setTimeout(() => {
312
- this.service.updateCharacteristic(this.cusChar.DisplayLight, this.cacheDisplay === 'on')
313
- }, 2000)
314
- throw new this.hapErr(-70402)
315
- }
316
- }
317
-
318
- externalUpdate(params) {
319
- // Check for an ON/OFF change
320
- if (params.state && params.state !== this.cacheState) {
321
- this.cacheState = params.state
322
- this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on')
323
- this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, this.cacheState === 'on' ? 2 : 0)
324
-
325
- // Log the change
326
- this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
327
- }
328
-
329
- // Handle OpenAPI workMode (speed/mode)
330
- if (params.workMode) {
331
- const speed = params.workMode.modeValue
332
- if (speed >= 1 && speed <= 4) {
333
- const newSpeed = speed * 25
334
- if (newSpeed !== this.cacheSpeed) {
335
- this.cacheSpeed = newSpeed
336
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
337
- this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
338
- }
339
- }
340
- }
341
-
342
- // Handle OpenAPI toggles
343
- if (params.toggles) {
344
- if (params.toggles.lockToggle !== undefined) {
345
- const newLock = params.toggles.lockToggle ? 'on' : 'off'
346
- if (newLock !== this.cacheLock) {
347
- this.cacheLock = newLock
348
- this.service.updateCharacteristic(this.hapChar.LockPhysicalControls, this.cacheLock === 'on' ? 1 : 0)
349
- this.accessory.log(`${platformLang.curLock} [${this.cacheLock}]`)
350
- }
351
- }
352
- if (params.toggles.displayToggle !== undefined) {
353
- const newDisplay = params.toggles.displayToggle ? 'on' : 'off'
354
- if (newDisplay !== this.cacheDisplay) {
355
- this.cacheDisplay = newDisplay
356
- this.service.updateCharacteristic(this.cusChar.DisplayLight, this.cacheDisplay === 'on')
357
- this.accessory.log(`${platformLang.curDisplay} [${this.cacheDisplay}]`)
358
- }
359
- }
360
- }
361
-
362
- // Check for some other scene/mode change
363
- (params.commands || []).forEach((command) => {
364
- const hexString = base64ToHex(command)
365
- const hexParts = hexToTwoItems(hexString)
366
-
367
- // Return now if not a device query update code
368
- if (getTwoItemPosition(hexParts, 1) !== 'aa') {
369
- return
370
- }
371
-
372
- const deviceFunction = `${getTwoItemPosition(hexParts, 2)}${getTwoItemPosition(hexParts, 3)}`
373
-
374
- switch (deviceFunction) {
375
- case '0501':
376
- case '0502': {
377
- // Speed update
378
- const newSpeedValue = Number.parseInt(getTwoItemPosition(hexParts, 4), 16)
379
- if (newSpeedValue >= 1 && newSpeedValue <= 4) {
380
- const newSpeed = newSpeedValue * 25
381
- if (newSpeed !== this.cacheSpeed) {
382
- this.cacheSpeed = newSpeed
383
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
384
- this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
385
- }
386
- }
387
- break
388
- }
389
- case '1000':
390
- case '1001': {
391
- // Lock
392
- const newLock = deviceFunction === '1001' ? 'on' : 'off'
393
- if (newLock !== this.cacheLock) {
394
- this.cacheLock = newLock
395
- this.service.updateCharacteristic(this.hapChar.LockPhysicalControls, this.cacheLock === 'on' ? 1 : 0)
396
- this.accessory.log(`${platformLang.curLock} [${this.cacheLock}]`)
397
- }
398
- break
399
- }
400
- default:
401
- this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
402
- break
403
- }
404
- })
405
- }
406
- }