@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,188 +0,0 @@
1
- import { base64ToHex, parseError } from '../utils/functions.js'
2
- import platformLang from '../utils/lang-en.js'
3
-
4
- // Models that expose a night light. The H5089 reports its light as
5
- // `aa 1b 01 [on] [level]` and that reading is correct, so the tile shows the
6
- // real state. No command shape tried moves it - neither mirroring its own
7
- // status code nor the `33 18` the purifiers use - so control is refused
8
- // outright rather than accepted and silently dropped (#1323).
9
- const nightLightModels = ['H5089']
10
-
11
- export default class {
12
- constructor(platform, accessory) {
13
- // Set up variables from the platform
14
- this.hapChar = platform.api.hap.Characteristic
15
- this.hapErr = platform.api.hap.HapStatusError
16
- this.hapServ = platform.api.hap.Service
17
- this.platform = platform
18
-
19
- // Set up variables from the accessory
20
- this.accessory = accessory
21
-
22
- // Remove switch services if they exist
23
- if (this.accessory.getService('Switch 1')) {
24
- this.accessory.removeService(this.accessory.getService('Switch 1'))
25
- }
26
- if (this.accessory.getService('Switch 2')) {
27
- this.accessory.removeService(this.accessory.getService('Switch 2'))
28
- }
29
-
30
- // A model that used to be treated as a single switch or outlet leaves an
31
- // unnumbered service behind, which would sit next to the numbered pair as
32
- // a tile that no longer does anything (#1323)
33
- this.accessory.services
34
- .filter(service => !service.subtype
35
- && [this.hapServ.Switch.UUID, this.hapServ.Outlet.UUID].includes(service.UUID))
36
- .forEach(service => this.accessory.removeService(service))
37
-
38
- // Add the outlet services if they don't already exist
39
- this.service1 = this.accessory.getService('Outlet 1')
40
- || this.accessory.addService(this.hapServ.Outlet, 'Outlet 1', 'outlet1')
41
- this.service2 = this.accessory.getService('Outlet 2')
42
- || this.accessory.addService(this.hapServ.Outlet, 'Outlet 2', 'outlet2')
43
-
44
- if (!this.service1.testCharacteristic(this.hapChar.ConfiguredName)) {
45
- this.service1.addCharacteristic(this.hapChar.ConfiguredName)
46
- this.service1.updateCharacteristic(this.hapChar.ConfiguredName, 'Outlet 1')
47
- }
48
- if (!this.service1.testCharacteristic(this.hapChar.ServiceLabelIndex)) {
49
- this.service1.addCharacteristic(this.hapChar.ServiceLabelIndex)
50
- this.service1.updateCharacteristic(this.hapChar.ServiceLabelIndex, 1)
51
- }
52
- if (!this.service2.testCharacteristic(this.hapChar.ConfiguredName)) {
53
- this.service2.addCharacteristic(this.hapChar.ConfiguredName)
54
- this.service2.updateCharacteristic(this.hapChar.ConfiguredName, 'Outlet 2')
55
- }
56
- if (!this.service2.testCharacteristic(this.hapChar.ServiceLabelIndex)) {
57
- this.service2.addCharacteristic(this.hapChar.ServiceLabelIndex)
58
- this.service2.updateCharacteristic(this.hapChar.ServiceLabelIndex, 2)
59
- }
60
-
61
- // Add the set handler to the switch on/off characteristic
62
- this.service1.getCharacteristic(this.hapChar.On).onSet(async (value) => {
63
- await this.internalStateUpdate(this.service1, value ? 17 : 16)
64
- })
65
- this.service1.cacheState = this.service1.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
66
- this.service2.getCharacteristic(this.hapChar.On).onSet(async (value) => {
67
- await this.internalStateUpdate(this.service2, value ? 34 : 32)
68
- })
69
- this.service2.cacheState = this.service2.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
70
-
71
- // Add a night light service for the models that have one
72
- if (nightLightModels.includes(this.accessory.context.gvModel)) {
73
- this.lightService = this.accessory.getService('Night Light')
74
- || this.accessory.addService(this.hapServ.Lightbulb, 'Night Light', 'nightlight')
75
- if (!this.lightService.testCharacteristic(this.hapChar.ConfiguredName)) {
76
- this.lightService.addCharacteristic(this.hapChar.ConfiguredName)
77
- this.lightService.updateCharacteristic(this.hapChar.ConfiguredName, 'Night Light')
78
- }
79
- this.lightService.getCharacteristic(this.hapChar.On).onSet(() => this.refuseLightControl())
80
- this.cacheLightState = this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
81
- this.lightService.getCharacteristic(this.hapChar.Brightness).onSet(() => this.refuseLightControl())
82
- this.cacheLightBright = this.lightService.getCharacteristic(this.hapChar.Brightness).value || 100
83
- } else if (this.accessory.getService('Night Light')) {
84
- this.accessory.removeService(this.accessory.getService('Night Light'))
85
- }
86
-
87
- // Output the customised options to the log
88
- const opts = JSON.stringify({
89
- showAs: 'outlet',
90
- })
91
- platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
92
-
93
- // The value is (which outlets to change << 4) | (which of those to turn on)
94
- // 51 turns BOTH ON
95
- // 48 turns BOTH OFF
96
- // 17 turns outlet 1 ON
97
- // 16 turns outlet 1 OFF
98
- // 34 turns outlet 2 ON
99
- // 32 turns outlet 2 OFF
100
- }
101
-
102
- async internalStateUpdate(service, value) {
103
- try {
104
- const newValue = value % 16 === 0 ? 'off' : 'on'
105
-
106
- // Don't continue if the new value is the same as before
107
- if (service.cacheState === newValue) {
108
- return
109
- }
110
-
111
- // Send the request to the platform sender function
112
- await this.platform.sendDeviceUpdate(this.accessory, {
113
- cmd: 'stateDual',
114
- value,
115
- })
116
-
117
- // Cache the new state and log if appropriate
118
- if (service.cacheState !== newValue) {
119
- service.cacheState = newValue
120
- this.accessory.log(`[${service.getCharacteristic(this.hapChar.ConfiguredName).value}] ${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
- service.updateCharacteristic(this.hapChar.On, service.cacheState === 'on')
129
- }, 2000)
130
- throw new this.hapErr(-70402)
131
- }
132
- }
133
-
134
- // The night light reading is correct, but every command shape tried has been
135
- // ignored by the plug, so refuse the write. HomeKit reverts the tile rather
136
- // than showing a state the plug was never given (#1323)
137
- refuseLightControl() {
138
- this.accessory.logWarn(`[Night Light] ${platformLang.devNotUpdated} this device does not accept night light commands`)
139
- throw new this.hapErr(-70402)
140
- }
141
-
142
- externalUpdate(params) {
143
- if (Array.isArray(params.state)) {
144
- if (params.state[0] !== this.service1.cacheState) {
145
- this.service1.cacheState = params.state[0]
146
- this.service1.updateCharacteristic(this.hapChar.On, this.service1.cacheState === 'on')
147
-
148
- this.accessory.log(`[${this.service1.getCharacteristic(this.hapChar.ConfiguredName).value}] ${platformLang.curState} [${this.service1.cacheState}]`)
149
- }
150
-
151
- if (params.state[1] !== this.service2.cacheState) {
152
- this.service2.cacheState = params.state[1]
153
- this.service2.updateCharacteristic(this.hapChar.On, this.service2.cacheState === 'on')
154
-
155
- this.accessory.log(`[${this.service2.getCharacteristic(this.hapChar.ConfiguredName).value}] ${platformLang.curState} [${this.service2.cacheState}]`)
156
- }
157
- }
158
-
159
- // The night light reports as `aa 1b 01 [on] [level]` (#1323)
160
- if (this.lightService && Array.isArray(params.commands)) {
161
- params.commands.forEach((command) => {
162
- let hexString
163
- try {
164
- hexString = base64ToHex(command)
165
- } catch {
166
- return
167
- }
168
- if (!hexString.startsWith('aa1b01')) {
169
- return
170
- }
171
-
172
- const newState = hexString.substring(6, 8) === '01' ? 'on' : 'off'
173
- if (newState !== this.cacheLightState) {
174
- this.cacheLightState = newState
175
- this.lightService.updateCharacteristic(this.hapChar.On, newState === 'on')
176
- this.accessory.log(`[Night Light] ${platformLang.curState} [${newState}]`)
177
- }
178
-
179
- const newBright = Number.parseInt(hexString.substring(8, 10), 16)
180
- if (newBright >= 1 && newBright <= 100 && newBright !== this.cacheLightBright) {
181
- this.cacheLightBright = newBright
182
- this.lightService.updateCharacteristic(this.hapChar.Brightness, newBright)
183
- this.accessory.log(`[Night Light] ${platformLang.curBright} [${newBright}%]`)
184
- }
185
- })
186
- }
187
- }
188
- }
@@ -1,160 +0,0 @@
1
- import { parseError } from '../utils/functions.js'
2
- import platformLang from '../utils/lang-en.js'
3
-
4
- export default class {
5
- constructor(platform, accessory) {
6
- // Set up variables from the platform
7
- this.hapChar = platform.api.hap.Characteristic
8
- this.hapErr = platform.api.hap.HapStatusError
9
- this.hapServ = platform.api.hap.Service
10
- this.platform = platform
11
-
12
- // Set up variables from the accessory
13
- this.accessory = accessory
14
-
15
- // Remove switch services if they exist
16
- if (this.accessory.getService('Switch 1')) {
17
- this.accessory.removeService(this.accessory.getService('Switch 1'))
18
- }
19
- if (this.accessory.getService('Switch 2')) {
20
- this.accessory.removeService(this.accessory.getService('Switch 2'))
21
- }
22
- if (this.accessory.getService('Switch 3')) {
23
- this.accessory.removeService(this.accessory.getService('Switch 3'))
24
- }
25
-
26
- // Add the outlet services if they don't already exist
27
- this.service1 = this.accessory.getService('Outlet 1')
28
- || this.accessory.addService(this.hapServ.Outlet, 'Outlet 1', 'outlet1')
29
- this.service2 = this.accessory.getService('Outlet 2')
30
- || this.accessory.addService(this.hapServ.Outlet, 'Outlet 2', 'outlet2')
31
- this.service3 = this.accessory.getService('Outlet 3')
32
- || this.accessory.addService(this.hapServ.Outlet, 'Outlet 3', 'outlet3')
33
-
34
- // Add the set handler to the switch on/off characteristic
35
- this.service1.getCharacteristic(this.hapChar.On).onSet(async (value) => {
36
- await this.internalStateUpdate(this.service1, value ? 17 : 16)
37
- })
38
- this.service1.cacheState = this.service1.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
39
- this.service2.getCharacteristic(this.hapChar.On).onSet(async (value) => {
40
- await this.internalStateUpdate(this.service2, value ? 34 : 32)
41
- })
42
- this.service2.cacheState = this.service2.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
43
- this.service3.getCharacteristic(this.hapChar.On).onSet(async (value) => {
44
- await this.internalStateUpdate(this.service3, value ? 68 : 64)
45
- })
46
- this.service3.cacheState = this.service3.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
47
-
48
- // Output the customised options to the log
49
- const opts = JSON.stringify({
50
- showAs: 'outlet',
51
- })
52
- platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
53
-
54
- // 119 turns BOTH ON
55
- // 112 turns BOTH OFF
56
-
57
- // 68 turns outlet 3 ON
58
- // 64 turns outlet 3 OFF
59
-
60
- // 34 turns outlet 2 ON
61
- // 32 turns outlet 2 OFF
62
-
63
- // 17 turns outlet 1 ON
64
- // 16 turns outlet 1 OFF
65
- }
66
-
67
- async internalStateUpdate(service, value) {
68
- try {
69
- const newValue = value % 16 === 0 ? 'off' : 'on'
70
-
71
- // Don't continue if the new value is the same as before
72
- if (service.cacheState === newValue) {
73
- return
74
- }
75
-
76
- // Send the request to the platform sender function
77
- await this.platform.sendDeviceUpdate(this.accessory, {
78
- cmd: 'stateDual',
79
- value,
80
- })
81
-
82
- // Cache the new state and log if appropriate
83
- service.cacheState = newValue
84
- this.accessory.log(`[${service.displayName}] ${platformLang.curState} [${newValue}]`)
85
- } catch (err) {
86
- // Catch any errors during the process
87
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
88
-
89
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
90
- setTimeout(() => {
91
- service.updateCharacteristic(this.hapChar.On, service.cacheState === 'on')
92
- }, 2000)
93
- throw new this.hapErr(-70402)
94
- }
95
- }
96
-
97
- externalUpdate(params) {
98
- // Check to see if the provided state is different from the cached state
99
- if (!params.stateDual) {
100
- return
101
- }
102
-
103
- if ([112, 119].includes(params.stateDual)) {
104
- // 112 -> both off
105
- // 119 -> both on
106
- const newState = params.stateDual === 119 ? 'on' : 'off'
107
- if (newState !== this.service1.cacheState) {
108
- this.service1.cacheState = newState
109
- this.service1.updateCharacteristic(this.hapChar.On, this.service1.cacheState === 'on')
110
-
111
- // Log the change
112
- this.accessory.log(`[${this.service1.displayName}] ${platformLang.curState} [${this.service1.cacheState}]`)
113
- }
114
- if (newState !== this.service2.cacheState) {
115
- this.service2.cacheState = newState
116
- this.service2.updateCharacteristic(this.hapChar.On, this.service2.cacheState === 'on')
117
-
118
- // Log the change
119
- this.accessory.log(`[${this.service2.displayName}] ${platformLang.curState} [${this.service2.cacheState}]`)
120
- }
121
- if (newState !== this.service3.cacheState) {
122
- this.service3.cacheState = newState
123
- this.service3.updateCharacteristic(this.hapChar.On, this.service3.cacheState === 'on')
124
-
125
- // Log the change
126
- this.accessory.log(`[${this.service3.displayName}] ${platformLang.curState} [${this.service3.cacheState}]`)
127
- }
128
- }
129
- if ([16, 17].includes(params.stateDual)) {
130
- const newState = params.stateDual === 17 ? 'on' : 'off'
131
- if (newState !== this.service1.cacheState) {
132
- this.service1.cacheState = newState
133
- this.service1.updateCharacteristic(this.hapChar.On, this.service1.cacheState === 'on')
134
-
135
- // Log the change
136
- this.accessory.log(`[${this.service1.displayName}] ${platformLang.curState} [${this.service1.cacheState}]`)
137
- }
138
- }
139
- if ([32, 34].includes(params.stateDual)) {
140
- const newState = params.stateDual === 34 ? 'on' : 'off'
141
- if (newState !== this.service2.cacheState) {
142
- this.service2.cacheState = newState
143
- this.service2.updateCharacteristic(this.hapChar.On, this.service2.cacheState === 'on')
144
-
145
- // Log the change
146
- this.accessory.log(`[${this.service2.displayName}] ${platformLang.curState} [${this.service2.cacheState}]`)
147
- }
148
- }
149
- if ([64, 68].includes(params.stateDual)) {
150
- const newState = params.stateDual === 68 ? 'on' : 'off'
151
- if (newState !== this.service3.cacheState) {
152
- this.service3.cacheState = newState
153
- this.service3.updateCharacteristic(this.hapChar.On, this.service3.cacheState === 'on')
154
-
155
- // Log the change
156
- this.accessory.log(`[${this.service3.displayName}] ${platformLang.curState} [${this.service3.cacheState}]`)
157
- }
158
- }
159
- }
160
- }