@homebridge-plugins/homebridge-govee 11.2.0 → 11.2.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/config.schema.json +10 -1
- package/lib/device/fan-H7105.js +116 -152
- package/lib/platform.js +1 -0
- package/lib/utils/constants.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/config.schema.json
CHANGED
|
@@ -1153,6 +1153,14 @@
|
|
|
1153
1153
|
"condition": {
|
|
1154
1154
|
"functionBody": "return (model.fanDevices && model.fanDevices[arrayIndices] && model.fanDevices[arrayIndices].deviceId && model.fanDevices[arrayIndices].deviceId.length === 23);"
|
|
1155
1155
|
}
|
|
1156
|
+
},
|
|
1157
|
+
"hideLight": {
|
|
1158
|
+
"type": "boolean",
|
|
1159
|
+
"title": "Hide Light",
|
|
1160
|
+
"description": "Enable this to not expose the light service in Homebridge/Homekit if your fan has one.",
|
|
1161
|
+
"condition": {
|
|
1162
|
+
"functionBody": "return (model.fanDevices && model.fanDevices[arrayIndices] && model.fanDevices[arrayIndices].deviceId && model.fanDevices[arrayIndices].deviceId.length === 23 && !model.fanDevices[arrayIndices].ignoreDevice);"
|
|
1163
|
+
}
|
|
1156
1164
|
}
|
|
1157
1165
|
}
|
|
1158
1166
|
}
|
|
@@ -1604,7 +1612,8 @@
|
|
|
1604
1612
|
"items": [
|
|
1605
1613
|
"fanDevices[].label",
|
|
1606
1614
|
"fanDevices[].deviceId",
|
|
1607
|
-
"fanDevices[].ignoreDevice"
|
|
1615
|
+
"fanDevices[].ignoreDevice",
|
|
1616
|
+
"fanDevices[].hideLight"
|
|
1608
1617
|
]
|
|
1609
1618
|
}
|
|
1610
1619
|
]
|
package/lib/device/fan-H7105.js
CHANGED
|
@@ -22,20 +22,24 @@ export default class {
|
|
|
22
22
|
// Set up variables from the accessory
|
|
23
23
|
this.accessory = accessory
|
|
24
24
|
|
|
25
|
+
// Set up custom variables for this device type
|
|
26
|
+
const deviceConf = platform.deviceConf[accessory.context.gvDeviceId]
|
|
27
|
+
this.hideLight = deviceConf && deviceConf.hideLight
|
|
28
|
+
|
|
25
29
|
// Codes etc
|
|
26
30
|
this.speedCodes = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
1: 'MwUBAQAAAAAAAAAAAAAAAAAAADY=',
|
|
32
|
+
2: 'MwUBAgAAAAAAAAAAAAAAAAAAADU=',
|
|
33
|
+
3: 'MwUBAwAAAAAAAAAAAAAAAAAAADQ=',
|
|
34
|
+
4: 'MwUBBAAAAAAAAAAAAAAAAAAAADM=',
|
|
35
|
+
5: 'MwUBBQAAAAAAAAAAAAAAAAAAADI=',
|
|
36
|
+
6: 'MwUBBgAAAAAAAAAAAAAAAAAAADE=',
|
|
37
|
+
7: 'MwUBBwAAAAAAAAAAAAAAAAAAADA=',
|
|
38
|
+
8: 'MwUBCAAAAAAAAAAAAAAAAAAAAD8=',
|
|
39
|
+
9: 'MwUBCQAAAAAAAAAAAAAAAAAAAD4=',
|
|
40
|
+
10: 'MwUBCgAAAAAAAAAAAAAAAAAAAD0=',
|
|
41
|
+
11: 'MwUBCwAAAAAAAAAAAAAAAAAAADw=',
|
|
42
|
+
12: 'MwUBDAAAAAAAAAAAAAAAAAAAADs=',
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
// Remove any old original Fan services
|
|
@@ -46,9 +50,6 @@ export default class {
|
|
|
46
50
|
// Add the fan service for the fan if it doesn't already exist
|
|
47
51
|
this.service = this.accessory.getService(this.hapServ.Fanv2) || this.accessory.addService(this.hapServ.Fanv2)
|
|
48
52
|
|
|
49
|
-
// Add the night light service if it doesn't already exist
|
|
50
|
-
this.lightService = this.accessory.getService(this.hapServ.Lightbulb) || this.accessory.addService(this.hapServ.Lightbulb)
|
|
51
|
-
|
|
52
53
|
// Add the set handler to the fan on/off characteristic
|
|
53
54
|
this.service
|
|
54
55
|
.getCharacteristic(this.hapChar.Active)
|
|
@@ -59,43 +60,56 @@ export default class {
|
|
|
59
60
|
this.service
|
|
60
61
|
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
61
62
|
.setProps({
|
|
62
|
-
|
|
63
|
+
maxValue: 12,
|
|
64
|
+
minStep: 1,
|
|
63
65
|
minValue: 0,
|
|
64
|
-
|
|
66
|
+
unit: 'unitless', // This is actually from HAP for Bluetooth LE Specification, but fits
|
|
65
67
|
})
|
|
66
68
|
.onSet(async value => this.internalSpeedUpdate(value))
|
|
67
69
|
this.cacheSpeed = this.service.getCharacteristic(this.hapChar.RotationSpeed).value
|
|
68
|
-
this.cacheMode = this.cacheSpeed >= 91 ? 'auto' : 'manual'
|
|
69
70
|
|
|
70
71
|
// Add the set handler to the fan swing mode
|
|
71
72
|
this.service
|
|
72
73
|
.getCharacteristic(this.hapChar.SwingMode)
|
|
73
74
|
.onSet(async value => this.internalSwingUpdate(value))
|
|
74
75
|
this.cacheSwing = this.service.getCharacteristic(this.hapChar.SwingMode).value === 1 ? 'on' : 'off'
|
|
76
|
+
this.cacheSwingCode = ''
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
if (this.hideLight) {
|
|
79
|
+
if (this.accessory.getService(this.hapServ.Lightbulb)) {
|
|
80
|
+
// Remove the light service if it exists
|
|
81
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Lightbulb))
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
// Add the night light service if it doesn't already exist
|
|
85
|
+
this.lightService = this.accessory.getService(this.hapServ.Lightbulb) || this.accessory.addService(this.hapServ.Lightbulb)
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.onSet(async (value) => {
|
|
86
|
-
await this.internalBrightnessUpdate(value)
|
|
87
|
+
// Add the set handler to the lightbulb on/off characteristic
|
|
88
|
+
this.lightService.getCharacteristic(this.hapChar.On).onSet(async (value) => {
|
|
89
|
+
await this.internalLightStateUpdate(value)
|
|
87
90
|
})
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
this.cacheLightState = this.lightService.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
92
|
+
|
|
93
|
+
// Add the set handler to the lightbulb brightness characteristic
|
|
94
|
+
this.lightService
|
|
95
|
+
.getCharacteristic(this.hapChar.Brightness)
|
|
96
|
+
.onSet(async (value) => {
|
|
97
|
+
await this.internalBrightnessUpdate(value)
|
|
98
|
+
})
|
|
99
|
+
this.cacheBright = this.lightService.getCharacteristic(this.hapChar.Brightness).value
|
|
100
|
+
|
|
101
|
+
// Add the set handler to the lightbulb hue characteristic
|
|
102
|
+
this.lightService.getCharacteristic(this.hapChar.Hue).onSet(async (value) => {
|
|
103
|
+
await this.internalColourUpdate(value)
|
|
104
|
+
})
|
|
105
|
+
this.cacheHue = this.lightService.getCharacteristic(this.hapChar.Hue).value
|
|
106
|
+
this.cacheSat = this.lightService.getCharacteristic(this.hapChar.Saturation).value
|
|
107
|
+
}
|
|
96
108
|
|
|
97
109
|
// Output the customised options to the log
|
|
98
|
-
const opts = JSON.stringify({
|
|
110
|
+
const opts = JSON.stringify({
|
|
111
|
+
hideLight: this.hideLight,
|
|
112
|
+
})
|
|
99
113
|
platform.log('[%s] %s %s.', accessory.displayName, platformLang.devInitOpts, opts)
|
|
100
114
|
}
|
|
101
115
|
|
|
@@ -133,74 +147,20 @@ export default class {
|
|
|
133
147
|
|
|
134
148
|
async internalSpeedUpdate(value) {
|
|
135
149
|
try {
|
|
136
|
-
if (value < 3) {
|
|
137
|
-
return
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
let newValue
|
|
141
|
-
if (value < 10) {
|
|
142
|
-
newValue = 7
|
|
143
|
-
} else if (value < 17) {
|
|
144
|
-
newValue = 14
|
|
145
|
-
} else if (value < 24) {
|
|
146
|
-
newValue = 21
|
|
147
|
-
} else if (value < 31) {
|
|
148
|
-
newValue = 28
|
|
149
|
-
} else if (value < 38) {
|
|
150
|
-
newValue = 35
|
|
151
|
-
} else if (value < 45) {
|
|
152
|
-
newValue = 42
|
|
153
|
-
} else if (value < 52) {
|
|
154
|
-
newValue = 49
|
|
155
|
-
} else if (value < 59) {
|
|
156
|
-
newValue = 56
|
|
157
|
-
} else if (value < 66) {
|
|
158
|
-
newValue = 63
|
|
159
|
-
} else if (value < 73) {
|
|
160
|
-
newValue = 70
|
|
161
|
-
} else if (value < 80) {
|
|
162
|
-
newValue = 77
|
|
163
|
-
} else if (value < 87) {
|
|
164
|
-
newValue = 84
|
|
165
|
-
} else {
|
|
166
|
-
newValue = 91
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
let newMode = value === 91 ? 'auto' : 'manual'
|
|
170
|
-
|
|
171
150
|
// Don't continue if the new value is the same as before
|
|
172
|
-
if (this.cacheSpeed ===
|
|
151
|
+
if (this.cacheSpeed === value || value === 0) {
|
|
173
152
|
return
|
|
174
153
|
}
|
|
175
154
|
|
|
176
|
-
// Don't continue if trying to access auto mode but there is no sensor attached
|
|
177
|
-
let codeToSend
|
|
178
|
-
if (newMode === 'auto') {
|
|
179
|
-
if (!this.accessory.context.sensorAttached || !this.cacheAutoCode) {
|
|
180
|
-
this.accessory.logWarn('auto mode not supported without a linked sensor')
|
|
181
|
-
codeToSend = this.speedCodes[84]
|
|
182
|
-
newMode = 'manual'
|
|
183
|
-
newValue = 84
|
|
184
|
-
} else {
|
|
185
|
-
codeToSend = this.cacheAutoCode
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
codeToSend = this.speedCodes[newValue]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
155
|
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
192
156
|
cmd: 'ptReal',
|
|
193
|
-
value:
|
|
157
|
+
value: this.speedCodes[value],
|
|
194
158
|
})
|
|
195
159
|
|
|
196
160
|
// Cache the new state and log if appropriate
|
|
197
|
-
if (this.
|
|
198
|
-
this.
|
|
199
|
-
this.accessory.log(`${platformLang.
|
|
200
|
-
}
|
|
201
|
-
if (this.cacheSpeed !== newValue) {
|
|
202
|
-
this.cacheSpeed = newValue
|
|
203
|
-
this.accessory.log(`${platformLang.curSpeed} [${newValue}%]`)
|
|
161
|
+
if (this.cacheSpeed !== value) {
|
|
162
|
+
this.cacheSpeed = value
|
|
163
|
+
this.accessory.log(`${platformLang.curSpeed} [${value}]`)
|
|
204
164
|
}
|
|
205
165
|
} catch (err) {
|
|
206
166
|
// Catch any errors during the process
|
|
@@ -218,13 +178,22 @@ export default class {
|
|
|
218
178
|
try {
|
|
219
179
|
const newValue = value ? 'on' : 'off'
|
|
220
180
|
// Don't continue if the new value is the same as before
|
|
221
|
-
if (this.cacheSwing === value) {
|
|
181
|
+
if (this.cacheSwing === value || !this.cacheSwingCode) {
|
|
222
182
|
return
|
|
223
183
|
}
|
|
224
184
|
|
|
185
|
+
// The existing cacheSwingCode might be something like aa1d0101960384000000000000000000000000a6
|
|
186
|
+
// We need to change the third hex value to 00 for off or 01 for on
|
|
187
|
+
const hexValues = [
|
|
188
|
+
0x3A,
|
|
189
|
+
0x1D,
|
|
190
|
+
value ? 0x01 : 0x00,
|
|
191
|
+
...this.cacheSwingCode.slice(6, 14).match(/.{1,2}/g).map(byte => Number.parseInt(byte, 16)),
|
|
192
|
+
]
|
|
193
|
+
|
|
225
194
|
await this.platform.sendDeviceUpdate(this.accessory, {
|
|
226
195
|
cmd: 'ptReal',
|
|
227
|
-
value:
|
|
196
|
+
value: generateCodeFromHexValues(hexValues),
|
|
228
197
|
})
|
|
229
198
|
|
|
230
199
|
// Cache the new state and log if appropriate
|
|
@@ -408,16 +377,11 @@ export default class {
|
|
|
408
377
|
case '0501': {
|
|
409
378
|
// Fan speed
|
|
410
379
|
const newSpeed = getTwoItemPosition(hexParts, 4)
|
|
411
|
-
const newSpeedInt = Number.parseInt(newSpeed, 16)
|
|
412
|
-
const newMode = 'manual'
|
|
413
|
-
if (this.cacheMode !== newMode) {
|
|
414
|
-
this.cacheMode = newMode
|
|
415
|
-
this.accessory.log(`${platformLang.curMode} [${this.cacheMode}]`)
|
|
416
|
-
}
|
|
380
|
+
const newSpeedInt = Number.parseInt(newSpeed, 16)
|
|
417
381
|
if (this.cacheSpeed !== newSpeedInt) {
|
|
418
382
|
this.cacheSpeed = newSpeedInt
|
|
419
383
|
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
420
|
-
this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}
|
|
384
|
+
this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}]`)
|
|
421
385
|
}
|
|
422
386
|
break
|
|
423
387
|
}
|
|
@@ -430,66 +394,66 @@ export default class {
|
|
|
430
394
|
// Sleep: 5
|
|
431
395
|
// Nature: 6
|
|
432
396
|
// Turbo: 7
|
|
433
|
-
const newMode = getTwoItemPosition(hexParts, 4) === '03' ? 'auto' : 'manual'
|
|
434
|
-
if (this.cacheMode !== newMode) {
|
|
435
|
-
this.cacheMode = newMode
|
|
436
|
-
this.accessory.log(`${platformLang.curMode} [${this.cacheMode}]`)
|
|
437
|
-
|
|
438
|
-
if (this.cacheMode === 'auto' && this.cacheSpeed < 91) {
|
|
439
|
-
this.cacheSpeed = 91
|
|
440
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
441
|
-
this.accessory.log(`${platformLang.curSpeed} [${this.cacheSpeed}%]`)
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
break
|
|
445
|
-
}
|
|
446
|
-
case '0503': {
|
|
447
|
-
// Auto mode, we need to keep this code to send it back to the device
|
|
448
|
-
const code = hexToTwoItems(`33${hexString.substring(2, hexString.length - 2)}`)
|
|
449
|
-
this.cacheAutoCode = generateCodeFromHexValues(code.map(p => Number.parseInt(p, 16)))
|
|
450
397
|
break
|
|
451
398
|
}
|
|
452
399
|
case '1b01': {
|
|
453
|
-
|
|
454
|
-
if (this.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
400
|
+
// Night light on/off
|
|
401
|
+
if (!this.hideLight) {
|
|
402
|
+
const newLightState = getTwoItemPosition(hexParts, 4) === '01' ? 'on' : 'off'
|
|
403
|
+
if (this.cacheLightState !== newLightState) {
|
|
404
|
+
this.cacheLightState = newLightState
|
|
405
|
+
this.lightService.updateCharacteristic(this.hapChar.On, this.cacheLightState === 'on')
|
|
406
|
+
this.accessory.log(`${platformLang.curLight} [${this.cacheLightState}]`)
|
|
407
|
+
}
|
|
408
|
+
const newBrightness = hexToDecimal(getTwoItemPosition(hexParts, 5))
|
|
409
|
+
if (this.cacheBright !== newBrightness) {
|
|
410
|
+
this.cacheBright = newBrightness
|
|
411
|
+
this.lightService.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
|
|
412
|
+
this.accessory.log(`${platformLang.curBright} [${this.cacheBright}%]`)
|
|
413
|
+
}
|
|
464
414
|
}
|
|
465
415
|
break
|
|
466
416
|
}
|
|
467
417
|
case '1b05': {
|
|
468
418
|
// Night light colour
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
419
|
+
if (!this.hideLight) {
|
|
420
|
+
const newR = hexToDecimal(getTwoItemPosition(hexParts, 5))
|
|
421
|
+
const newG = hexToDecimal(getTwoItemPosition(hexParts, 6))
|
|
422
|
+
const newB = hexToDecimal(getTwoItemPosition(hexParts, 7))
|
|
423
|
+
|
|
424
|
+
const hs = rgb2hs(newR, newG, newB)
|
|
425
|
+
|
|
426
|
+
// Check for a colour change
|
|
427
|
+
if (hs[0] !== this.cacheHue) {
|
|
428
|
+
// Colour is different so update Homebridge with new values
|
|
429
|
+
this.lightService.updateCharacteristic(this.hapChar.Hue, hs[0])
|
|
430
|
+
this.lightService.updateCharacteristic(this.hapChar.Saturation, hs[1]);
|
|
431
|
+
[this.cacheHue] = hs
|
|
432
|
+
|
|
433
|
+
// Log the change
|
|
434
|
+
this.accessory.log(`${platformLang.curColour} [rgb ${newR} ${newG} ${newB}]`)
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
break
|
|
438
|
+
}
|
|
439
|
+
case '1d00':{
|
|
440
|
+
// Swing Mode Off
|
|
441
|
+
const newSwing = 'off'
|
|
442
|
+
this.cacheSwingCode = hexParts
|
|
443
|
+
if (this.cacheSwing !== newSwing) {
|
|
444
|
+
this.cacheSwing = newSwing
|
|
445
|
+
this.service.updateCharacteristic(this.hapChar.SwingMode, 0)
|
|
446
|
+
this.accessory.log(`${platformLang.curSwing} [${this.cacheSwing}]`)
|
|
484
447
|
}
|
|
485
448
|
break
|
|
486
449
|
}
|
|
487
|
-
case '
|
|
488
|
-
// Swing Mode
|
|
489
|
-
const newSwing =
|
|
450
|
+
case '1d01':{
|
|
451
|
+
// Swing Mode On
|
|
452
|
+
const newSwing = 'on'
|
|
453
|
+
this.cacheSwingCode = hexParts
|
|
490
454
|
if (this.cacheSwing !== newSwing) {
|
|
491
455
|
this.cacheSwing = newSwing
|
|
492
|
-
this.service.updateCharacteristic(this.hapChar.SwingMode,
|
|
456
|
+
this.service.updateCharacteristic(this.hapChar.SwingMode, 1)
|
|
493
457
|
this.accessory.log(`${platformLang.curSwing} [${this.cacheSwing}]`)
|
|
494
458
|
}
|
|
495
459
|
break
|
package/lib/platform.js
CHANGED
package/lib/utils/constants.js
CHANGED
|
@@ -91,7 +91,7 @@ export default {
|
|
|
91
91
|
],
|
|
92
92
|
leakDevices: ['label', 'deviceId', 'ignoreDevice', 'lowBattThreshold'],
|
|
93
93
|
thermoDevices: ['label', 'deviceId', 'ignoreDevice', 'lowBattThreshold', 'showExtraSwitch'],
|
|
94
|
-
fanDevices: ['label', 'deviceId', 'ignoreDevice'],
|
|
94
|
+
fanDevices: ['label', 'deviceId', 'ignoreDevice', 'hideLight'],
|
|
95
95
|
heaterDevices: ['label', 'deviceId', 'ignoreDevice', 'tempReporting'],
|
|
96
96
|
humidifierDevices: ['label', 'deviceId', 'ignoreDevice'],
|
|
97
97
|
dehumidifierDevices: ['label', 'deviceId', 'ignoreDevice'],
|
package/package.json
CHANGED