@homebridge-plugins/homebridge-ewelink 12.18.4-beta.1 → 12.19.1-beta.0
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 +8 -1
- package/config.schema.json +9 -0
- package/lib/connection/api.js +10 -14
- package/lib/device/fan.js +46 -28
- package/lib/platform.js +1 -0
- package/lib/utils/constants.js +1 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@homebridge-plugins/homebridge-ewelink` will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## v12.
|
|
5
|
+
## v12.19.1 (Pending Release)
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- feat(fan): add an option to show ifan speed as a percentage, so siri's levels work (#551)
|
|
10
|
+
- fix(api): send fan speeds on the fan's own scale, so medium no longer turns it off
|
|
11
|
+
|
|
12
|
+
## v12.19.0 (2026-10-18)
|
|
6
13
|
|
|
7
14
|
### Changed
|
|
8
15
|
|
package/config.schema.json
CHANGED
|
@@ -1218,6 +1218,14 @@
|
|
|
1218
1218
|
"functionBody": "return (model.fanDevices && model.fanDevices[arrayIndices] && ['ifan'].includes(model.fanDevices[arrayIndices].deviceModel) && !model.fanDevices[arrayIndices].ignoreDevice);"
|
|
1219
1219
|
}
|
|
1220
1220
|
},
|
|
1221
|
+
"speedAsPercent": {
|
|
1222
|
+
"type": "boolean",
|
|
1223
|
+
"title": "Show Speed As Percentage",
|
|
1224
|
+
"description": "If true, the fan speed shows as a percentage (low 33%, medium 66%, high 99%) instead of levels 1 to 3. Siri's low, medium and high only work with a percentage. Changing this re-adds the fan to HomeKit, so check any scenes or automations that use it.",
|
|
1225
|
+
"condition": {
|
|
1226
|
+
"functionBody": "return (model.fanDevices && model.fanDevices[arrayIndices] && ['ifan'].includes(model.fanDevices[arrayIndices].deviceModel) && !model.fanDevices[arrayIndices].ignoreDevice);"
|
|
1227
|
+
}
|
|
1228
|
+
},
|
|
1221
1229
|
"ipAddress": {
|
|
1222
1230
|
"type": "string",
|
|
1223
1231
|
"title": "IP Address",
|
|
@@ -1788,6 +1796,7 @@
|
|
|
1788
1796
|
"fanDevices[].overrideLogging",
|
|
1789
1797
|
"fanDevices[].deviceModel",
|
|
1790
1798
|
"fanDevices[].hideLight",
|
|
1799
|
+
"fanDevices[].speedAsPercent",
|
|
1791
1800
|
"fanDevices[].ipAddress"
|
|
1792
1801
|
]
|
|
1793
1802
|
}
|
package/lib/connection/api.js
CHANGED
|
@@ -446,21 +446,16 @@ export default class {
|
|
|
446
446
|
await accessory.control.internalColourUpdate(newHKStatus)
|
|
447
447
|
break
|
|
448
448
|
}
|
|
449
|
-
case 'speed':
|
|
450
|
-
// The new status for speed must be low, medium or high
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
newHKStatus = 66
|
|
457
|
-
break
|
|
458
|
-
case 'high':
|
|
459
|
-
newHKStatus = 99
|
|
460
|
-
break
|
|
461
|
-
default:
|
|
462
|
-
throw new Error('Value must be \'low\', \'medium\' or \'high\' for characteristic:speed')
|
|
449
|
+
case 'speed': {
|
|
450
|
+
// The new status for speed must be low, medium or high. Some fans count
|
|
451
|
+
// their speed in levels 1 to 3 rather than a percentage, and sending
|
|
452
|
+
// those a percentage read as out of range, so medium turned them off
|
|
453
|
+
const levels = { low: 1, medium: 2, high: 3 }
|
|
454
|
+
if (!levels[newValue]) {
|
|
455
|
+
throw new Error('Value must be \'low\', \'medium\' or \'high\' for characteristic:speed')
|
|
463
456
|
}
|
|
457
|
+
const inLevels = accServ.getCharacteristic(this.hapChar.RotationSpeed).props.maxValue <= 3
|
|
458
|
+
newHKStatus = inLevels ? levels[newValue] : levels[newValue] * 33
|
|
464
459
|
|
|
465
460
|
// Check the accessory has a correct set handler for speed
|
|
466
461
|
if (!accessory.control.internalSpeedUpdate) {
|
|
@@ -470,6 +465,7 @@ export default class {
|
|
|
470
465
|
// Call the set handler to send the request to eWeLink
|
|
471
466
|
await accessory.control.internalSpeedUpdate(newHKStatus)
|
|
472
467
|
break
|
|
468
|
+
}
|
|
473
469
|
case 'state':
|
|
474
470
|
// The new status for state must be on, off or toggle
|
|
475
471
|
switch (newValue) {
|
package/lib/device/fan.js
CHANGED
|
@@ -20,6 +20,7 @@ export default class {
|
|
|
20
20
|
// Set up custom variables for this device type
|
|
21
21
|
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
22
22
|
this.hideLight = deviceConf.hideLight
|
|
23
|
+
this.speedAsPercent = deviceConf.speedAsPercent
|
|
23
24
|
|
|
24
25
|
// Set the correct logging variables for this accessory
|
|
25
26
|
switch (deviceConf.overrideLogging) {
|
|
@@ -41,6 +42,16 @@ export default class {
|
|
|
41
42
|
break
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
// Rebuild the fan service if it was cached under the other speed scale, as
|
|
46
|
+
// HomeKit keeps the old props until the service itself is replaced
|
|
47
|
+
const existingService = this.accessory.getService(this.hapServ.Fan)
|
|
48
|
+
if (existingService) {
|
|
49
|
+
const { props } = existingService.getCharacteristic(this.hapChar.RotationSpeed)
|
|
50
|
+
if (props.maxValue !== (this.speedAsPercent ? 100 : 3)) {
|
|
51
|
+
this.accessory.removeService(existingService)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
// Add the fan service if it doesn't already exist
|
|
45
56
|
this.service = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
|
|
46
57
|
|
|
@@ -49,15 +60,19 @@ export default class {
|
|
|
49
60
|
.getCharacteristic(this.hapChar.On)
|
|
50
61
|
.onSet(async value => this.internalStateUpdate(value))
|
|
51
62
|
|
|
52
|
-
// Add the set handler to the fan rotation speed characteristic
|
|
63
|
+
// Add the set handler to the fan rotation speed characteristic. Levels 1-3
|
|
64
|
+
// match the remote, but Siri and a tap on the slider only work with a
|
|
65
|
+
// percentage, so that is offered as an option (#551)
|
|
53
66
|
this.service
|
|
54
67
|
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
55
|
-
.setProps(
|
|
56
|
-
maxValue:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
.setProps(this.speedAsPercent
|
|
69
|
+
? { maxValue: 100, minStep: 33, minValue: 0, unit: 'percentage' }
|
|
70
|
+
: {
|
|
71
|
+
maxValue: 3,
|
|
72
|
+
minStep: 1,
|
|
73
|
+
minValue: 0,
|
|
74
|
+
unit: 'unitless', // This is actually from HAP for Bluetooth LE Specification, but fits
|
|
75
|
+
})
|
|
61
76
|
.onSet(async value => this.internalSpeedUpdate(value))
|
|
62
77
|
|
|
63
78
|
// Check to see if the user has hidden the light channel
|
|
@@ -116,6 +131,7 @@ export default class {
|
|
|
116
131
|
const opts = JSON.stringify({
|
|
117
132
|
hideLight: this.hideLight,
|
|
118
133
|
logging: this.enableDebugLogging ? 'debug' : normalLogging,
|
|
134
|
+
speedAsPercent: this.speedAsPercent,
|
|
119
135
|
})
|
|
120
136
|
this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
|
|
121
137
|
}
|
|
@@ -170,13 +186,7 @@ export default class {
|
|
|
170
186
|
switches: [],
|
|
171
187
|
}
|
|
172
188
|
const newState = value >= 1 ? 'on' : 'off'
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (value < 0 || value > 3) {
|
|
176
|
-
newSpeed = 0
|
|
177
|
-
} else {
|
|
178
|
-
newSpeed = value
|
|
179
|
-
}
|
|
189
|
+
const newSpeed = this.levelFromHK(value)
|
|
180
190
|
if (newSpeed === this.cacheSpeed) {
|
|
181
191
|
return
|
|
182
192
|
}
|
|
@@ -202,7 +212,7 @@ export default class {
|
|
|
202
212
|
if (newSpeed === 0) {
|
|
203
213
|
// Update the rotation speed back to the previous value (with the fan still off)
|
|
204
214
|
setTimeout(() => {
|
|
205
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
215
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.levelToHK(this.cacheSpeed))
|
|
206
216
|
}, 2000)
|
|
207
217
|
return
|
|
208
218
|
}
|
|
@@ -213,7 +223,7 @@ export default class {
|
|
|
213
223
|
} catch (err) {
|
|
214
224
|
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
215
225
|
setTimeout(() => {
|
|
216
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
226
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.levelToHK(this.cacheSpeed))
|
|
217
227
|
}, 2000)
|
|
218
228
|
throw new this.hapErr(-70402)
|
|
219
229
|
}
|
|
@@ -294,7 +304,7 @@ export default class {
|
|
|
294
304
|
}
|
|
295
305
|
if (speed !== this.cacheSpeed && this.cacheState === 'on') {
|
|
296
306
|
this.cacheSpeed = speed
|
|
297
|
-
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.cacheSpeed)
|
|
307
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.levelToHK(this.cacheSpeed))
|
|
298
308
|
if (params.updateSource && this.enableLogging) {
|
|
299
309
|
this.log(
|
|
300
310
|
'[%s] %s [%s].',
|
|
@@ -318,17 +328,8 @@ export default class {
|
|
|
318
328
|
|
|
319
329
|
currentState() {
|
|
320
330
|
const toReturn = {}
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
if (speed === 0) {
|
|
324
|
-
speedLabel = 'off'
|
|
325
|
-
} else if (speed <= 1) {
|
|
326
|
-
speedLabel = 'low'
|
|
327
|
-
} else if (speed <= 2) {
|
|
328
|
-
speedLabel = 'medium'
|
|
329
|
-
} else {
|
|
330
|
-
speedLabel = 'high'
|
|
331
|
-
}
|
|
331
|
+
const level = this.levelFromHK(this.service.getCharacteristic(this.hapChar.RotationSpeed).value)
|
|
332
|
+
const speedLabel = level === 0 ? 'off' : this.speed2label[level]
|
|
332
333
|
toReturn.services = ['fan']
|
|
333
334
|
toReturn.fan = {
|
|
334
335
|
state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
|
|
@@ -343,6 +344,23 @@ export default class {
|
|
|
343
344
|
return toReturn
|
|
344
345
|
}
|
|
345
346
|
|
|
347
|
+
// A HomeKit rotation speed as a fan level, 0 (off) to 3. On the percentage
|
|
348
|
+
// scale 1-33 is low, 34-66 medium and 67-100 high
|
|
349
|
+
levelFromHK(value) {
|
|
350
|
+
if (!this.speedAsPercent) {
|
|
351
|
+
return value < 0 || value > 3 ? 0 : value
|
|
352
|
+
}
|
|
353
|
+
if (value <= 0) {
|
|
354
|
+
return 0
|
|
355
|
+
}
|
|
356
|
+
return value <= 33 ? 1 : value <= 66 ? 2 : 3
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// A fan level as a HomeKit rotation speed, on whichever scale is in use
|
|
360
|
+
levelToHK(level) {
|
|
361
|
+
return this.speedAsPercent && level ? level * 33 : level
|
|
362
|
+
}
|
|
363
|
+
|
|
346
364
|
markStatus(isOnline) {
|
|
347
365
|
this.isOnline = isOnline
|
|
348
366
|
}
|
package/lib/platform.js
CHANGED
package/lib/utils/constants.js
CHANGED
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"displayName": "Homebridge eWeLink",
|
|
4
4
|
"alias": "eWeLink",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"version": "12.
|
|
6
|
+
"version": "12.19.1-beta.0",
|
|
7
7
|
"description": "Homebridge plugin to integrate eWeLink devices into HomeKit.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "bwp91",
|
|
@@ -69,13 +69,13 @@
|
|
|
69
69
|
"@homebridge/plugin-ui-utils": "^2.2.6",
|
|
70
70
|
"axios": "^1.20.0",
|
|
71
71
|
"node-persist": "^4.0.4",
|
|
72
|
-
"p-queue": "^
|
|
72
|
+
"p-queue": "^10.0.0",
|
|
73
73
|
"websocket-as-promised": "^3.0.1",
|
|
74
|
-
"ws": "^8.
|
|
74
|
+
"ws": "^8.22.0"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@antfu/eslint-config": "^9.
|
|
78
|
-
"@vitest/coverage-v8": "^
|
|
79
|
-
"vitest": "^
|
|
77
|
+
"@antfu/eslint-config": "^9.5.1",
|
|
78
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
79
|
+
"vitest": "^5.0.5"
|
|
80
80
|
}
|
|
81
81
|
}
|