@homebridge-plugins/homebridge-govee 11.32.0-beta.24 → 11.32.0-beta.26
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.
- package/CHANGELOG.md +3 -0
- package/lib/device/fan-H1310.js +4 -2
- package/lib/device/fan-H1370.js +10 -0
- package/lib/device/fan-H7100.js +4 -2
- package/lib/device/fan-H7101.js +4 -2
- package/lib/device/fan-H7102.js +4 -2
- package/lib/device/fan-H7105.js +4 -2
- package/lib/device/fan-H7106.js +4 -2
- package/lib/device/fan-H7107.js +4 -2
- package/lib/device/fan-H7111.js +4 -2
- package/lib/device/ice-maker-H7172.js +28 -3
- package/lib/device/ice-maker-H717D.js +28 -3
- package/lib/device/ice-maker-H8120.js +28 -3
- package/lib/device/outlet-double.js +15 -83
- package/lib/device/switch-double.js +15 -83
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -27,6 +27,9 @@ All notable changes to `@homebridge-plugins/homebridge-govee` will be documented
|
|
|
27
27
|
- fix: hide the H5089 night light tile until its commands are worked out, rather than showing a control that does nothing (#1323)
|
|
28
28
|
- feat: add support for the H3001 solar string lights as an rgb light (#1328)
|
|
29
29
|
- docs(changelog): list every unreleased commit in the pending section
|
|
30
|
+
- fix(fans): rebuild the fan service once when it was cached under the old speed scale, so the top of the slider stops setting the slowest speed (#1326) (@cwhett)
|
|
31
|
+
- fix: show the H5089 night light tile again, but refuse control commands, so it reports the real state instead of quietly ignoring taps (#1323)
|
|
32
|
+
- fix(ice-makers): stop the home app showing an ice size of 0 when one has never been set (#1329) (@cwhett)
|
|
30
33
|
|
|
31
34
|
## v11.31.0 (2026-07-24)
|
|
32
35
|
|
package/lib/device/fan-H1310.js
CHANGED
|
@@ -40,10 +40,12 @@ export default class {
|
|
|
40
40
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
44
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
44
45
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
45
46
|
if (existingService) {
|
|
46
|
-
|
|
47
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
48
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
47
49
|
this.accessory.removeService(existingService)
|
|
48
50
|
}
|
|
49
51
|
}
|
package/lib/device/fan-H1370.js
CHANGED
|
@@ -34,6 +34,16 @@ export default class {
|
|
|
34
34
|
const deviceConf = platform.deviceConf[accessory.context.gvDeviceId]
|
|
35
35
|
this.hideLight = deviceConf && deviceConf.hideLight
|
|
36
36
|
|
|
37
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
38
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
39
|
+
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
40
|
+
if (existingService) {
|
|
41
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
42
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
43
|
+
this.accessory.removeService(existingService)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
// Add the fan service if it doesn't already exist
|
|
38
48
|
this.service = this.accessory.getService(this.hapServ.Fanv2) || this.accessory.addService(this.hapServ.Fanv2)
|
|
39
49
|
|
package/lib/device/fan-H7100.js
CHANGED
|
@@ -40,10 +40,12 @@ export default class {
|
|
|
40
40
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
44
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
44
45
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
45
46
|
if (existingService) {
|
|
46
|
-
|
|
47
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
48
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
47
49
|
this.accessory.removeService(existingService)
|
|
48
50
|
}
|
|
49
51
|
}
|
package/lib/device/fan-H7101.js
CHANGED
|
@@ -96,10 +96,12 @@ export default class {
|
|
|
96
96
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
//
|
|
99
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
100
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
100
101
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
101
102
|
if (existingService) {
|
|
102
|
-
|
|
103
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
104
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
103
105
|
this.accessory.removeService(existingService)
|
|
104
106
|
}
|
|
105
107
|
}
|
package/lib/device/fan-H7102.js
CHANGED
|
@@ -40,10 +40,12 @@ export default class {
|
|
|
40
40
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
44
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
44
45
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
45
46
|
if (existingService) {
|
|
46
|
-
|
|
47
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
48
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
47
49
|
this.accessory.removeService(existingService)
|
|
48
50
|
}
|
|
49
51
|
}
|
package/lib/device/fan-H7105.js
CHANGED
|
@@ -53,10 +53,12 @@ export default class {
|
|
|
53
53
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
//
|
|
56
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
57
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
57
58
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
58
59
|
if (existingService) {
|
|
59
|
-
|
|
60
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
61
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
60
62
|
this.accessory.removeService(existingService)
|
|
61
63
|
}
|
|
62
64
|
}
|
package/lib/device/fan-H7106.js
CHANGED
|
@@ -40,10 +40,12 @@ export default class {
|
|
|
40
40
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
44
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
44
45
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
45
46
|
if (existingService) {
|
|
46
|
-
|
|
47
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
48
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
47
49
|
this.accessory.removeService(existingService)
|
|
48
50
|
}
|
|
49
51
|
}
|
package/lib/device/fan-H7107.js
CHANGED
|
@@ -53,10 +53,12 @@ export default class {
|
|
|
53
53
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
//
|
|
56
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
57
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
57
58
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
58
59
|
if (existingService) {
|
|
59
|
-
|
|
60
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
61
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
60
62
|
this.accessory.removeService(existingService)
|
|
61
63
|
}
|
|
62
64
|
}
|
package/lib/device/fan-H7111.js
CHANGED
|
@@ -101,10 +101,12 @@ export default class {
|
|
|
101
101
|
this.accessory.removeService(this.accessory.getService(this.hapServ.Fan))
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
//
|
|
104
|
+
// Rebuild the fan service if it was cached under an older speed scale, as
|
|
105
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
105
106
|
const existingService = this.accessory.getService(this.hapServ.Fanv2)
|
|
106
107
|
if (existingService) {
|
|
107
|
-
|
|
108
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
109
|
+
if (props.unit === 'percentage' || props.maxValue !== 100) {
|
|
108
110
|
this.accessory.removeService(existingService)
|
|
109
111
|
}
|
|
110
112
|
}
|
|
@@ -52,6 +52,13 @@ export default class {
|
|
|
52
52
|
.onSet(async value => this.internalSizeUpdate(value))
|
|
53
53
|
this.cacheSize = this.service.getCharacteristic(this.hapChar.RotationSpeed).value || 2
|
|
54
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
|
+
|
|
55
62
|
// Output the customised options to the log
|
|
56
63
|
const opts = JSON.stringify({})
|
|
57
64
|
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
@@ -105,8 +112,15 @@ export default class {
|
|
|
105
112
|
|
|
106
113
|
async internalSizeUpdate(value) {
|
|
107
114
|
try {
|
|
108
|
-
//
|
|
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.
|
|
109
120
|
if (value === 0) {
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
123
|
+
}, 1000)
|
|
110
124
|
return
|
|
111
125
|
}
|
|
112
126
|
|
|
@@ -145,8 +159,11 @@ export default class {
|
|
|
145
159
|
if (params.workMode) {
|
|
146
160
|
const mode = params.workMode.workMode
|
|
147
161
|
if (mode > 0 && mode <= 3) {
|
|
148
|
-
this.cacheState
|
|
149
|
-
|
|
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
|
+
}
|
|
150
167
|
if (this.cacheSize !== mode) {
|
|
151
168
|
this.cacheSize = mode
|
|
152
169
|
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
@@ -184,6 +201,14 @@ export default class {
|
|
|
184
201
|
}
|
|
185
202
|
break
|
|
186
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
|
+
}
|
|
187
212
|
default:
|
|
188
213
|
this.accessory.logWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
|
|
189
214
|
break
|
|
@@ -52,6 +52,13 @@ export default class {
|
|
|
52
52
|
.onSet(async value => this.internalSizeUpdate(value))
|
|
53
53
|
this.cacheSize = this.service.getCharacteristic(this.hapChar.RotationSpeed).value || 2
|
|
54
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
|
+
|
|
55
62
|
// Output the customised options to the log
|
|
56
63
|
const opts = JSON.stringify({})
|
|
57
64
|
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
@@ -105,8 +112,15 @@ export default class {
|
|
|
105
112
|
|
|
106
113
|
async internalSizeUpdate(value) {
|
|
107
114
|
try {
|
|
108
|
-
//
|
|
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.
|
|
109
120
|
if (value === 0) {
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
123
|
+
}, 1000)
|
|
110
124
|
return
|
|
111
125
|
}
|
|
112
126
|
|
|
@@ -145,8 +159,11 @@ export default class {
|
|
|
145
159
|
if (params.workMode) {
|
|
146
160
|
const mode = params.workMode.workMode
|
|
147
161
|
if (mode > 0 && mode <= 3) {
|
|
148
|
-
this.cacheState
|
|
149
|
-
|
|
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
|
+
}
|
|
150
167
|
if (this.cacheSize !== mode) {
|
|
151
168
|
this.cacheSize = mode
|
|
152
169
|
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
@@ -184,6 +201,14 @@ export default class {
|
|
|
184
201
|
}
|
|
185
202
|
break
|
|
186
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
|
+
}
|
|
187
212
|
default:
|
|
188
213
|
this.accessory.logWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
|
|
189
214
|
break
|
|
@@ -52,6 +52,13 @@ export default class {
|
|
|
52
52
|
.onSet(async value => this.internalSizeUpdate(value))
|
|
53
53
|
this.cacheSize = this.service.getCharacteristic(this.hapChar.RotationSpeed).value || 2
|
|
54
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
|
+
|
|
55
62
|
// Output the customised options to the log
|
|
56
63
|
const opts = JSON.stringify({})
|
|
57
64
|
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
@@ -105,8 +112,15 @@ export default class {
|
|
|
105
112
|
|
|
106
113
|
async internalSizeUpdate(value) {
|
|
107
114
|
try {
|
|
108
|
-
//
|
|
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.
|
|
109
120
|
if (value === 0) {
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
123
|
+
}, 1000)
|
|
110
124
|
return
|
|
111
125
|
}
|
|
112
126
|
|
|
@@ -145,8 +159,11 @@ export default class {
|
|
|
145
159
|
if (params.workMode) {
|
|
146
160
|
const mode = params.workMode.workMode
|
|
147
161
|
if (mode > 0 && mode <= 3) {
|
|
148
|
-
this.cacheState
|
|
149
|
-
|
|
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
|
+
}
|
|
150
167
|
if (this.cacheSize !== mode) {
|
|
151
168
|
this.cacheSize = mode
|
|
152
169
|
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSize)
|
|
@@ -184,6 +201,14 @@ export default class {
|
|
|
184
201
|
}
|
|
185
202
|
break
|
|
186
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
|
+
}
|
|
187
212
|
default:
|
|
188
213
|
this.accessory.logWarn(`${platformLang.newScene}: [${command}] [${hexString}]`)
|
|
189
214
|
break
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { base64ToHex,
|
|
1
|
+
import { base64ToHex, parseError } from '../utils/functions.js'
|
|
2
2
|
import platformLang from '../utils/lang-en.js'
|
|
3
3
|
|
|
4
|
-
// Models
|
|
5
|
-
// `aa 1b 01 [on] [level]` and that reading is correct,
|
|
6
|
-
//
|
|
7
|
-
// `33 18` the purifiers use - so
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
const nightLightModels = []
|
|
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']
|
|
11
10
|
|
|
12
11
|
export default class {
|
|
13
12
|
constructor(platform, accessory) {
|
|
@@ -77,13 +76,9 @@ export default class {
|
|
|
77
76
|
this.lightService.addCharacteristic(this.hapChar.ConfiguredName)
|
|
78
77
|
this.lightService.updateCharacteristic(this.hapChar.ConfiguredName, 'Night Light')
|
|
79
78
|
}
|
|
80
|
-
this.lightService.getCharacteristic(this.hapChar.On).onSet(
|
|
81
|
-
await this.internalLightStateUpdate(value)
|
|
82
|
-
})
|
|
79
|
+
this.lightService.getCharacteristic(this.hapChar.On).onSet(() => this.refuseLightControl())
|
|
83
80
|
this.cacheLightState = this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
84
|
-
this.lightService.getCharacteristic(this.hapChar.Brightness).onSet(
|
|
85
|
-
await this.internalLightBrightnessUpdate(value)
|
|
86
|
-
})
|
|
81
|
+
this.lightService.getCharacteristic(this.hapChar.Brightness).onSet(() => this.refuseLightControl())
|
|
87
82
|
this.cacheLightBright = this.lightService.getCharacteristic(this.hapChar.Brightness).value || 100
|
|
88
83
|
} else if (this.accessory.getService('Night Light')) {
|
|
89
84
|
this.accessory.removeService(this.accessory.getService('Night Light'))
|
|
@@ -136,75 +131,12 @@ export default class {
|
|
|
136
131
|
}
|
|
137
132
|
}
|
|
138
133
|
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
async sendLightCommand(state, level) {
|
|
146
|
-
const shapes = [
|
|
147
|
-
[0x33, 0x1B, 0x01, state, level],
|
|
148
|
-
[0x33, 0x18, state, level],
|
|
149
|
-
]
|
|
150
|
-
let lastError
|
|
151
|
-
for (const hexValues of shapes) {
|
|
152
|
-
try {
|
|
153
|
-
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
154
|
-
// This plug acks the app's commands as `ptIot` and ignores `ptReal`
|
|
155
|
-
cmd: 'ptIot',
|
|
156
|
-
value: generateCodeFromHexValues(hexValues),
|
|
157
|
-
})
|
|
158
|
-
} catch (err) {
|
|
159
|
-
lastError = err
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (lastError) {
|
|
163
|
-
throw lastError
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async internalLightStateUpdate(value) {
|
|
168
|
-
try {
|
|
169
|
-
const newValue = value ? 'on' : 'off'
|
|
170
|
-
if (this.cacheLightState === newValue) {
|
|
171
|
-
return
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Keep the current level when toggling, so turning the light on does not
|
|
175
|
-
// also change how bright it is
|
|
176
|
-
await this.sendLightCommand(value ? 0x01 : 0x00, this.cacheLightBright)
|
|
177
|
-
|
|
178
|
-
this.cacheLightState = newValue
|
|
179
|
-
this.accessory.log(`[Night Light] ${platformLang.curState} [${newValue}]`)
|
|
180
|
-
} catch (err) {
|
|
181
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
182
|
-
setTimeout(() => {
|
|
183
|
-
this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLightState === 'on')
|
|
184
|
-
}, 2000)
|
|
185
|
-
throw new this.hapErr(-70402)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async internalLightBrightnessUpdate(value) {
|
|
190
|
-
try {
|
|
191
|
-
if (value === 0 || this.cacheLightBright === value) {
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Setting a level also turns the light on, matching how HomeKit sends
|
|
196
|
-
// brightness changes alongside an on command
|
|
197
|
-
await this.sendLightCommand(0x01, value)
|
|
198
|
-
|
|
199
|
-
this.cacheLightBright = value
|
|
200
|
-
this.accessory.log(`[Night Light] ${platformLang.curBright} [${value}%]`)
|
|
201
|
-
} catch (err) {
|
|
202
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
203
|
-
setTimeout(() => {
|
|
204
|
-
this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheLightBright)
|
|
205
|
-
}, 2000)
|
|
206
|
-
throw new this.hapErr(-70402)
|
|
207
|
-
}
|
|
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)
|
|
208
140
|
}
|
|
209
141
|
|
|
210
142
|
externalUpdate(params) {
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { base64ToHex,
|
|
1
|
+
import { base64ToHex, parseError } from '../utils/functions.js'
|
|
2
2
|
import platformLang from '../utils/lang-en.js'
|
|
3
3
|
|
|
4
|
-
// Models
|
|
5
|
-
// `aa 1b 01 [on] [level]` and that reading is correct,
|
|
6
|
-
//
|
|
7
|
-
// `33 18` the purifiers use - so
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
const nightLightModels = []
|
|
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']
|
|
11
10
|
|
|
12
11
|
export default class {
|
|
13
12
|
constructor(platform, accessory) {
|
|
@@ -77,13 +76,9 @@ export default class {
|
|
|
77
76
|
this.lightService.addCharacteristic(this.hapChar.ConfiguredName)
|
|
78
77
|
this.lightService.updateCharacteristic(this.hapChar.ConfiguredName, 'Night Light')
|
|
79
78
|
}
|
|
80
|
-
this.lightService.getCharacteristic(this.hapChar.On).onSet(
|
|
81
|
-
await this.internalLightStateUpdate(value)
|
|
82
|
-
})
|
|
79
|
+
this.lightService.getCharacteristic(this.hapChar.On).onSet(() => this.refuseLightControl())
|
|
83
80
|
this.cacheLightState = this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
84
|
-
this.lightService.getCharacteristic(this.hapChar.Brightness).onSet(
|
|
85
|
-
await this.internalLightBrightnessUpdate(value)
|
|
86
|
-
})
|
|
81
|
+
this.lightService.getCharacteristic(this.hapChar.Brightness).onSet(() => this.refuseLightControl())
|
|
87
82
|
this.cacheLightBright = this.lightService.getCharacteristic(this.hapChar.Brightness).value || 100
|
|
88
83
|
} else if (this.accessory.getService('Night Light')) {
|
|
89
84
|
this.accessory.removeService(this.accessory.getService('Night Light'))
|
|
@@ -136,75 +131,12 @@ export default class {
|
|
|
136
131
|
}
|
|
137
132
|
}
|
|
138
133
|
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
async sendLightCommand(state, level) {
|
|
146
|
-
const shapes = [
|
|
147
|
-
[0x33, 0x1B, 0x01, state, level],
|
|
148
|
-
[0x33, 0x18, state, level],
|
|
149
|
-
]
|
|
150
|
-
let lastError
|
|
151
|
-
for (const hexValues of shapes) {
|
|
152
|
-
try {
|
|
153
|
-
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
154
|
-
// This plug acks the app's commands as `ptIot` and ignores `ptReal`
|
|
155
|
-
cmd: 'ptIot',
|
|
156
|
-
value: generateCodeFromHexValues(hexValues),
|
|
157
|
-
})
|
|
158
|
-
} catch (err) {
|
|
159
|
-
lastError = err
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (lastError) {
|
|
163
|
-
throw lastError
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async internalLightStateUpdate(value) {
|
|
168
|
-
try {
|
|
169
|
-
const newValue = value ? 'on' : 'off'
|
|
170
|
-
if (this.cacheLightState === newValue) {
|
|
171
|
-
return
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Keep the current level when toggling, so turning the light on does not
|
|
175
|
-
// also change how bright it is
|
|
176
|
-
await this.sendLightCommand(value ? 0x01 : 0x00, this.cacheLightBright)
|
|
177
|
-
|
|
178
|
-
this.cacheLightState = newValue
|
|
179
|
-
this.accessory.log(`[Night Light] ${platformLang.curState} [${newValue}]`)
|
|
180
|
-
} catch (err) {
|
|
181
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
182
|
-
setTimeout(() => {
|
|
183
|
-
this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLightState === 'on')
|
|
184
|
-
}, 2000)
|
|
185
|
-
throw new this.hapErr(-70402)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async internalLightBrightnessUpdate(value) {
|
|
190
|
-
try {
|
|
191
|
-
if (value === 0 || this.cacheLightBright === value) {
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Setting a level also turns the light on, matching how HomeKit sends
|
|
196
|
-
// brightness changes alongside an on command
|
|
197
|
-
await this.sendLightCommand(0x01, value)
|
|
198
|
-
|
|
199
|
-
this.cacheLightBright = value
|
|
200
|
-
this.accessory.log(`[Night Light] ${platformLang.curBright} [${value}%]`)
|
|
201
|
-
} catch (err) {
|
|
202
|
-
this.accessory.logWarn(`${platformLang.devNotUpdated} ${parseError(err)}`)
|
|
203
|
-
setTimeout(() => {
|
|
204
|
-
this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheLightBright)
|
|
205
|
-
}, 2000)
|
|
206
|
-
throw new this.hapErr(-70402)
|
|
207
|
-
}
|
|
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)
|
|
208
140
|
}
|
|
209
141
|
|
|
210
142
|
externalUpdate(params) {
|
package/package.json
CHANGED