@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,187 +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
- export default class {
10
- constructor(platform, accessory) {
11
- // Set up variables from the platform
12
- this.hapChar = platform.api.hap.Characteristic
13
- this.hapErr = platform.api.hap.HapStatusError
14
- this.hapServ = platform.api.hap.Service
15
- this.platform = platform
16
-
17
- // Set up variables from the accessory
18
- this.accessory = accessory
19
-
20
- // Speed codes
21
- this.value2Code = {
22
- 1: 'MwUBAQAAAAAAAAAAAAAAAAAAADY=',
23
- 2: 'MwUBAgAAAAAAAAAAAAAAAAAAADU=',
24
- 3: 'MwUBAwAAAAAAAAAAAAAAAAAAADQ=',
25
- 4: 'MwUBBAAAAAAAAAAAAAAAAAAAADM=',
26
- 5: 'MwUBBQAAAAAAAAAAAAAAAAAAADI=',
27
- 6: 'MwUBBgAAAAAAAAAAAAAAAAAAADE=',
28
- 7: 'MwUBBwAAAAAAAAAAAAAAAAAAADA=',
29
- 8: 'MwUBCAAAAAAAAAAAAAAAAAAAAD8=',
30
- }
31
-
32
- // Migrate old %-rotation speed to unitless
33
- const existingFanService = this.accessory.getService(this.hapServ.Fan)
34
- if (existingFanService) {
35
- if (existingFanService.getCharacteristic(this.hapChar.RotationSpeed).props.unit === 'percentage') {
36
- this.accessory.removeService(existingFanService)
37
- }
38
- }
39
-
40
- // Add the fan service if it doesn't already exist
41
- this.service = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
42
-
43
- // Add the set handler to the fan on/off characteristic
44
- this.service
45
- .getCharacteristic(this.hapChar.On)
46
- .onSet(async value => this.internalStateUpdate(value))
47
- this.cacheState = this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
48
-
49
- // Add the set handler to the fan rotation speed characteristic
50
- this.service
51
- .getCharacteristic(this.hapChar.RotationSpeed)
52
- .setProps({
53
- maxValue: 8,
54
- minStep: 1,
55
- minValue: 0,
56
- unit: 'unitless',
57
- })
58
- .onSet(async value => this.internalSpeedUpdate(value))
59
- this.cacheSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
60
-
61
- // Output the customised options to the log
62
- const opts = JSON.stringify({})
63
- platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
64
- }
65
-
66
- async internalStateUpdate(value) {
67
- try {
68
- const newValue = value ? 'on' : 'off'
69
-
70
- // Don't continue if the new value is the same as before
71
- if (this.cacheState === newValue) {
72
- return
73
- }
74
-
75
- // Send the request to the platform sender function
76
- await this.platform.sendDeviceUpdate(this.accessory, {
77
- cmd: 'stateHumi',
78
- value: value ? 1 : 0,
79
- })
80
-
81
- // Cache the new state and log if appropriate
82
- this.cacheState = newValue
83
- this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
84
- } catch (err) {
85
- // Catch any errors during the process
86
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
87
-
88
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
89
- setTimeout(() => {
90
- this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
91
- }, 2000)
92
- throw new this.hapErr(-70402)
93
- }
94
- }
95
-
96
- async internalSpeedUpdate(value) {
97
- try {
98
- // Don't continue if the speed is 0
99
- if (value === 0) {
100
- return
101
- }
102
-
103
- // Don't continue if the speed value won't have effect
104
- if (value === this.cacheSpeed) {
105
- return
106
- }
107
-
108
- // Get the scene code for this value
109
- const newCode = this.value2Code[value]
110
-
111
- // Send the request to the platform sender function
112
- await this.platform.sendDeviceUpdate(this.accessory, {
113
- cmd: 'ptReal',
114
- value: newCode,
115
- openApi: this.accessory.context.openApiCapabilities?.workMode
116
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: 1, modeValue: value } }
117
- : undefined,
118
- })
119
-
120
- // Cache the new state and log if appropriate
121
- this.cacheSpeed = value
122
- this.accessory.log(`${platformLang.curSpeed} [${value}]`)
123
- } catch (err) {
124
- // Catch any errors during the process
125
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
126
-
127
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
128
- setTimeout(() => {
129
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
130
- }, 2000)
131
- throw new this.hapErr(-70402)
132
- }
133
- }
134
-
135
- externalUpdate(params) {
136
- // Check for an ON/OFF change
137
- if (params.state && params.state !== this.cacheState) {
138
- this.cacheState = params.state
139
- this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
140
-
141
- // Log the change
142
- this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
143
- }
144
-
145
- // Handle OpenAPI workMode (speed)
146
- if (params.workMode) {
147
- const speed = params.workMode.modeValue
148
- if (speed >= 1 && speed <= 8) {
149
- this.cacheSpeed = speed
150
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
151
- }
152
- }
153
-
154
- // Check for some other scene/mode change
155
- (params.commands || []).forEach((command) => {
156
- const hexString = base64ToHex(command)
157
- const hexParts = hexToTwoItems(hexString)
158
-
159
- // Return now if not a device query update code
160
- if (getTwoItemPosition(hexParts, 1) !== 'aa') {
161
- return
162
- }
163
-
164
- const deviceFunction = `${getTwoItemPosition(hexParts, 2)}${getTwoItemPosition(hexParts, 3)}`
165
-
166
- switch (deviceFunction) {
167
- case '0501':
168
- case '0502': {
169
- // Speed update
170
- const newSpeedRaw = getTwoItemPosition(hexParts, 4)
171
- const newSpeedValue = Number.parseInt(newSpeedRaw, 16)
172
- if (newSpeedValue >= 1 && newSpeedValue <= 8) {
173
- if (newSpeedValue !== this.cacheSpeed) {
174
- this.cacheSpeed = newSpeedValue
175
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
176
- this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}]`)
177
- }
178
- }
179
- break
180
- }
181
- default:
182
- this.accessory.logDebugWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
183
- break
184
- }
185
- })
186
- }
187
- }
@@ -1,218 +0,0 @@
1
- import { base64ToHex, getTwoItemPosition, hexToTwoItems, 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
- // Ice size codes: small=1, medium=2, large=3 (matching OpenAPI workMode values)
16
- this.sizeCodes = {
17
- 1: 'MwUDAAAAAAAAAAAAAAAAAAAAADU=', // small
18
- 2: 'MwUCAAAAAAAAAAAAAAAAAAAAADQ=', // medium
19
- 3: 'MwUBAAAAAAAAAAAAAAAAAAAAADc=', // large
20
- }
21
-
22
- this.sizeLabels = {
23
- 1: 'small',
24
- 2: 'medium',
25
- 3: 'large',
26
- }
27
-
28
- // Remove old switch service if migrating
29
- if (this.accessory.getService(this.hapServ.Switch)) {
30
- this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
31
- }
32
-
33
- // Add the fan service for ice size control
34
- this.service = this.accessory.getService(this.hapServ.Fanv2)
35
- || this.accessory.addService(this.hapServ.Fanv2)
36
-
37
- // Add the set handler to the on/off characteristic
38
- this.service.getCharacteristic(this.hapChar.Active).onSet(async (value) => {
39
- await this.internalStateUpdate(value)
40
- })
41
- this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value === 1 ? 'on' : 'off'
42
-
43
- // Add the set handler to the rotation speed characteristic (ice size)
44
- this.service
45
- .getCharacteristic(this.hapChar.RotationSpeed)
46
- .setProps({
47
- maxValue: 3,
48
- minStep: 1,
49
- minValue: 0,
50
- unit: 'unitless',
51
- })
52
- .onSet(async value => this.internalSizeUpdate(value))
53
- this.cacheSize = this.service.getCharacteristic(this.hapChar.RotationSpeed).value || 2
54
-
55
- // Keep the characteristic in step with the cache. Without this an accessory
56
- // that has never had a size set reports Active=1 with RotationSpeed=0, which
57
- // is contradictory and which the Home app renders as "0".
58
- if (this.service.getCharacteristic(this.hapChar.RotationSpeed).value !== this.cacheSize) {
59
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
60
- }
61
-
62
- // Output the customised options to the log
63
- const opts = JSON.stringify({})
64
- platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
65
- }
66
-
67
- async internalStateUpdate(value) {
68
- try {
69
- const newState = value ? 'on' : 'off'
70
-
71
- // Don't continue if the new value is the same as before
72
- if (newState === this.cacheState) {
73
- return
74
- }
75
-
76
- if (value) {
77
- // Turn on with current cached ice size
78
- await this.platform.sendDeviceUpdate(this.accessory, {
79
- cmd: 'ptReal',
80
- value: this.sizeCodes[this.cacheSize],
81
- openApi: this.accessory.context.openApiCapabilities?.workMode
82
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: this.cacheSize, modeValue: 0 } }
83
- : undefined,
84
- })
85
- } else {
86
- // Turn off
87
- await this.platform.sendDeviceUpdate(this.accessory, {
88
- cmd: 'ptReal',
89
- value: 'MxkAAAAAAAAAAAAAAAAAAAAAACo=',
90
- openApi: this.accessory.context.openApiCapabilities?.workMode
91
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: 0, modeValue: 0 } }
92
- : undefined,
93
- })
94
- }
95
-
96
- // Cache the new state and log if appropriate
97
- if (this.cacheState !== newState) {
98
- this.cacheState = newState
99
- this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
100
- }
101
- } catch (err) {
102
- // Catch any errors during the process
103
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
104
-
105
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
106
- setTimeout(() => {
107
- this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
108
- }, 2000)
109
- throw new this.hapErr(-70402)
110
- }
111
- }
112
-
113
- async internalSizeUpdate(value) {
114
- try {
115
- // 0 is not a valid ice size. Put the slider back, otherwise HomeKit keeps 0
116
- // while the device carries on at cacheSize - and that 0 is what gets written
117
- // to the accessory cache, so it returns on the next restart. Deferred for the
118
- // same reason as the error reverts below: an immediate update would be
119
- // overwritten by the value HomeKit is currently writing.
120
- if (value === 0) {
121
- setTimeout(() => {
122
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
123
- }, 1000)
124
- return
125
- }
126
-
127
- // Don't continue if the new value is the same as before
128
- if (value === this.cacheSize) {
129
- return
130
- }
131
-
132
- await this.platform.sendDeviceUpdate(this.accessory, {
133
- cmd: 'ptReal',
134
- value: this.sizeCodes[value],
135
- openApi: this.accessory.context.openApiCapabilities?.workMode
136
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: value, modeValue: 0 } }
137
- : undefined,
138
- })
139
-
140
- // Cache the new state and log if appropriate
141
- this.cacheSize = value
142
- this.cacheState = 'on'
143
- this.service.updateCharacteristic(this.hapChar.Active, 1)
144
- this.accessory.log(`${platformLang.curSpeed} [${this.sizeLabels[value]}]`)
145
- } catch (err) {
146
- // Catch any errors during the process
147
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
148
-
149
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
150
- setTimeout(() => {
151
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
152
- }, 2000)
153
- throw new this.hapErr(-70402)
154
- }
155
- }
156
-
157
- externalUpdate(params) {
158
- // Handle OpenAPI workMode
159
- if (params.workMode) {
160
- const mode = params.workMode.workMode
161
- if (mode > 0 && mode <= 3) {
162
- if (this.cacheState !== 'on') {
163
- this.cacheState = 'on'
164
- this.service.updateCharacteristic(this.hapChar.Active, 1)
165
- this.accessory.log(`${platformLang.curState} [on]`)
166
- }
167
- if (this.cacheSize !== mode) {
168
- this.cacheSize = mode
169
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
170
- this.accessory.log(`${platformLang.curSpeed} [${this.sizeLabels[this.cacheSize]}]`)
171
- }
172
- } else if (mode === 0) {
173
- if (this.cacheState !== 'off') {
174
- this.cacheState = 'off'
175
- this.service.updateCharacteristic(this.hapChar.Active, 0)
176
- this.accessory.log(`${platformLang.curState} [off]`)
177
- }
178
- }
179
- }
180
-
181
- // Check for some other scene/mode change
182
- (params.commands || []).forEach((command) => {
183
- const hexString = base64ToHex(command)
184
- const hexParts = hexToTwoItems(hexString)
185
-
186
- // Return now if not a device query update code
187
- if (getTwoItemPosition(hexParts, 1) !== 'aa') {
188
- return
189
- }
190
-
191
- const deviceFunction = `${getTwoItemPosition(hexParts, 1)}${getTwoItemPosition(hexParts, 2)}`
192
-
193
- switch (deviceFunction) {
194
- case 'aa19': {
195
- // On/Off
196
- const newState = getTwoItemPosition(hexParts, 3) === '01' ? 'on' : 'off'
197
- if (this.cacheState !== newState) {
198
- this.cacheState = newState
199
- this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
200
- this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
201
- }
202
- break
203
- }
204
- case 'aa05': {
205
- // Ice size report. The payload encoding is deliberately NOT decoded here:
206
- // sizeCodes runs small=0x03..large=0x01 while the OpenAPI path sends
207
- // workMode un-inverted, and which is authoritative is unresolved (#1329).
208
- // Claiming a mapping here could silently swap sizes, so this case exists
209
- // only to stop a routine status report being logged as a new scene code.
210
- break
211
- }
212
- default:
213
- this.accessory.logWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
214
- break
215
- }
216
- })
217
- }
218
- }
@@ -1,218 +0,0 @@
1
- import { base64ToHex, getTwoItemPosition, hexToTwoItems, 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
- // Ice size codes: small=1, medium=2, large=3 (matching OpenAPI workMode values)
16
- this.sizeCodes = {
17
- 1: 'MwUDAAAAAAAAAAAAAAAAAAAAADU=', // small
18
- 2: 'MwUCAAAAAAAAAAAAAAAAAAAAADQ=', // medium
19
- 3: 'MwUBAAAAAAAAAAAAAAAAAAAAADc=', // large
20
- }
21
-
22
- this.sizeLabels = {
23
- 1: 'small',
24
- 2: 'medium',
25
- 3: 'large',
26
- }
27
-
28
- // Remove old switch service if migrating
29
- if (this.accessory.getService(this.hapServ.Switch)) {
30
- this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
31
- }
32
-
33
- // Add the fan service for ice size control
34
- this.service = this.accessory.getService(this.hapServ.Fanv2)
35
- || this.accessory.addService(this.hapServ.Fanv2)
36
-
37
- // Add the set handler to the on/off characteristic
38
- this.service.getCharacteristic(this.hapChar.Active).onSet(async (value) => {
39
- await this.internalStateUpdate(value)
40
- })
41
- this.cacheState = this.service.getCharacteristic(this.hapChar.Active).value === 1 ? 'on' : 'off'
42
-
43
- // Add the set handler to the rotation speed characteristic (ice size)
44
- this.service
45
- .getCharacteristic(this.hapChar.RotationSpeed)
46
- .setProps({
47
- maxValue: 3,
48
- minStep: 1,
49
- minValue: 0,
50
- unit: 'unitless',
51
- })
52
- .onSet(async value => this.internalSizeUpdate(value))
53
- this.cacheSize = this.service.getCharacteristic(this.hapChar.RotationSpeed).value || 2
54
-
55
- // Keep the characteristic in step with the cache. Without this an accessory
56
- // that has never had a size set reports Active=1 with RotationSpeed=0, which
57
- // is contradictory and which the Home app renders as "0".
58
- if (this.service.getCharacteristic(this.hapChar.RotationSpeed).value !== this.cacheSize) {
59
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
60
- }
61
-
62
- // Output the customised options to the log
63
- const opts = JSON.stringify({})
64
- platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
65
- }
66
-
67
- async internalStateUpdate(value) {
68
- try {
69
- const newState = value ? 'on' : 'off'
70
-
71
- // Don't continue if the new value is the same as before
72
- if (newState === this.cacheState) {
73
- return
74
- }
75
-
76
- if (value) {
77
- // Turn on with current cached ice size
78
- await this.platform.sendDeviceUpdate(this.accessory, {
79
- cmd: 'ptReal',
80
- value: this.sizeCodes[this.cacheSize],
81
- openApi: this.accessory.context.openApiCapabilities?.workMode
82
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: this.cacheSize, modeValue: 0 } }
83
- : undefined,
84
- })
85
- } else {
86
- // Turn off
87
- await this.platform.sendDeviceUpdate(this.accessory, {
88
- cmd: 'ptReal',
89
- value: 'MxkAAAAAAAAAAAAAAAAAAAAAACo=',
90
- openApi: this.accessory.context.openApiCapabilities?.workMode
91
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: 0, modeValue: 0 } }
92
- : undefined,
93
- })
94
- }
95
-
96
- // Cache the new state and log if appropriate
97
- if (this.cacheState !== newState) {
98
- this.cacheState = newState
99
- this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
100
- }
101
- } catch (err) {
102
- // Catch any errors during the process
103
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
104
-
105
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
106
- setTimeout(() => {
107
- this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
108
- }, 2000)
109
- throw new this.hapErr(-70402)
110
- }
111
- }
112
-
113
- async internalSizeUpdate(value) {
114
- try {
115
- // 0 is not a valid ice size. Put the slider back, otherwise HomeKit keeps 0
116
- // while the device carries on at cacheSize - and that 0 is what gets written
117
- // to the accessory cache, so it returns on the next restart. Deferred for the
118
- // same reason as the error reverts below: an immediate update would be
119
- // overwritten by the value HomeKit is currently writing.
120
- if (value === 0) {
121
- setTimeout(() => {
122
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
123
- }, 1000)
124
- return
125
- }
126
-
127
- // Don't continue if the new value is the same as before
128
- if (value === this.cacheSize) {
129
- return
130
- }
131
-
132
- await this.platform.sendDeviceUpdate(this.accessory, {
133
- cmd: 'ptReal',
134
- value: this.sizeCodes[value],
135
- openApi: this.accessory.context.openApiCapabilities?.workMode
136
- ? { instance: 'workMode', capabilityType: 'devices.capabilities.work_mode', value: { workMode: value, modeValue: 0 } }
137
- : undefined,
138
- })
139
-
140
- // Cache the new state and log if appropriate
141
- this.cacheSize = value
142
- this.cacheState = 'on'
143
- this.service.updateCharacteristic(this.hapChar.Active, 1)
144
- this.accessory.log(`${platformLang.curSpeed} [${this.sizeLabels[value]}]`)
145
- } catch (err) {
146
- // Catch any errors during the process
147
- this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
148
-
149
- // Throw a 'no response' error and set a timeout to revert this after 2 seconds
150
- setTimeout(() => {
151
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
152
- }, 2000)
153
- throw new this.hapErr(-70402)
154
- }
155
- }
156
-
157
- externalUpdate(params) {
158
- // Handle OpenAPI workMode
159
- if (params.workMode) {
160
- const mode = params.workMode.workMode
161
- if (mode > 0 && mode <= 3) {
162
- if (this.cacheState !== 'on') {
163
- this.cacheState = 'on'
164
- this.service.updateCharacteristic(this.hapChar.Active, 1)
165
- this.accessory.log(`${platformLang.curState} [on]`)
166
- }
167
- if (this.cacheSize !== mode) {
168
- this.cacheSize = mode
169
- this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
170
- this.accessory.log(`${platformLang.curSpeed} [${this.sizeLabels[this.cacheSize]}]`)
171
- }
172
- } else if (mode === 0) {
173
- if (this.cacheState !== 'off') {
174
- this.cacheState = 'off'
175
- this.service.updateCharacteristic(this.hapChar.Active, 0)
176
- this.accessory.log(`${platformLang.curState} [off]`)
177
- }
178
- }
179
- }
180
-
181
- // Check for some other scene/mode change
182
- (params.commands || []).forEach((command) => {
183
- const hexString = base64ToHex(command)
184
- const hexParts = hexToTwoItems(hexString)
185
-
186
- // Return now if not a device query update code
187
- if (getTwoItemPosition(hexParts, 1) !== 'aa') {
188
- return
189
- }
190
-
191
- const deviceFunction = `${getTwoItemPosition(hexParts, 1)}${getTwoItemPosition(hexParts, 2)}`
192
-
193
- switch (deviceFunction) {
194
- case 'aa19': {
195
- // On/Off
196
- const newState = getTwoItemPosition(hexParts, 3) === '01' ? 'on' : 'off'
197
- if (this.cacheState !== newState) {
198
- this.cacheState = newState
199
- this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
200
- this.accessory.log(`${platformLang.curState} [${this.cacheState}]`)
201
- }
202
- break
203
- }
204
- case 'aa05': {
205
- // Ice size report. The payload encoding is deliberately NOT decoded here:
206
- // sizeCodes runs small=0x03..large=0x01 while the OpenAPI path sends
207
- // workMode un-inverted, and which is authoritative is unresolved (#1329).
208
- // Claiming a mapping here could silently swap sizes, so this case exists
209
- // only to stop a routine status report being logged as a new scene code.
210
- break
211
- }
212
- default:
213
- this.accessory.logWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
214
- break
215
- }
216
- })
217
- }
218
- }