@homebridge-plugins/homebridge-ewelink 12.15.3-beta.2 → 12.15.3-beta.4
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 +13 -0
- package/lib/connection/http.js +1 -0
- package/lib/device/fan-gear.js +234 -0
- package/lib/device/index.js +2 -0
- package/lib/device/light-dimmer.js +46 -0
- package/lib/device/sensor-temp-humi.js +37 -0
- package/lib/device/switch-single.js +37 -1
- package/lib/device/zigbee/sensor-motion.js +20 -0
- package/lib/device/zigbee/sensor-occupancy.js +20 -0
- package/lib/device/zigbee/thermostat.js +45 -0
- package/lib/platform.js +9 -0
- package/lib/utils/constants.js +12 -8
- package/lib/utils/lang-en.js +5 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,20 @@ All notable changes to `@homebridge-plugins/homebridge-ewelink` will be document
|
|
|
7
7
|
### Changed
|
|
8
8
|
|
|
9
9
|
- chore: remove the looking-for-maintainers message
|
|
10
|
+
- feat: add support for the snzb-01m zigbee button (7039)
|
|
11
|
+
- feat: add support for the wireless temperature and humidity sensor (242)
|
|
12
|
+
- feat: add support for the three gear fan (17)
|
|
13
|
+
- feat: expose the light level on the snzb-03p and snzb-06p sensors (7002/7016)
|
|
14
|
+
- feat: add power readings for outlets reporting phase values (226)
|
|
15
|
+
- feat: add power readings for the mini dimmer (277)
|
|
16
|
+
- feat: expose the spm-4relay sub units as multi channel outlets with power readings (130)
|
|
17
|
+
- feat: expose the dust and light levels on the sonoff sc (18)
|
|
18
|
+
- feat: add a child lock control for the trvzb radiator valve (7017)
|
|
19
|
+
- feat: expose the wall button presses on the mini r3/r4 for detached relay setups (138)
|
|
20
|
+
- fix: correct the multi channel format and add power readings for the s60zbtpf zigbee outlet (7032)
|
|
21
|
+
- fix: stop treating the dualr3 lite as a power monitoring device (165)
|
|
10
22
|
- fix: time out stalled http requests so device fetching cannot hang forever (#788) (@glenmarks-hub)
|
|
23
|
+
- fix: apply the request timeout to the login request too (#788)
|
|
11
24
|
- fix: use the switch_00 param for the swv-zne irrigation valve (7047) (#778)
|
|
12
25
|
- chore(github): update the setup-node action to v7
|
|
13
26
|
|
package/lib/connection/http.js
CHANGED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { generateRandomString, sleep } from '../utils/functions.js'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
UIID 17: the "three gear fan", controlled with named params rather than
|
|
5
|
+
switch channels (matching AlexxIT/SonoffLAN's XFan17):
|
|
6
|
+
- fan: 'on'/'off'
|
|
7
|
+
- speed: 'slow'/'moderate'/'fast'
|
|
8
|
+
- shake: 'on'/'off' (oscillation)
|
|
9
|
+
*/
|
|
10
|
+
export default class {
|
|
11
|
+
constructor(platform, accessory) {
|
|
12
|
+
// Set up variables from the platform
|
|
13
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
14
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
15
|
+
this.hapServ = platform.api.hap.Service
|
|
16
|
+
this.lang = platform.lang
|
|
17
|
+
this.log = platform.log
|
|
18
|
+
this.platform = platform
|
|
19
|
+
|
|
20
|
+
// Set up variables from the accessory
|
|
21
|
+
this.name = accessory.displayName
|
|
22
|
+
this.accessory = accessory
|
|
23
|
+
|
|
24
|
+
// Initially set the online flag as true (to be then updated as false if necessary)
|
|
25
|
+
this.isOnline = true
|
|
26
|
+
|
|
27
|
+
// Set up custom variables for this device type
|
|
28
|
+
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
29
|
+
|
|
30
|
+
// Set the correct logging variables for this accessory
|
|
31
|
+
switch (deviceConf.overrideLogging) {
|
|
32
|
+
case 'standard':
|
|
33
|
+
this.enableLogging = true
|
|
34
|
+
this.enableDebugLogging = false
|
|
35
|
+
break
|
|
36
|
+
case 'debug':
|
|
37
|
+
this.enableLogging = true
|
|
38
|
+
this.enableDebugLogging = true
|
|
39
|
+
break
|
|
40
|
+
case 'disable':
|
|
41
|
+
this.enableLogging = false
|
|
42
|
+
this.enableDebugLogging = false
|
|
43
|
+
break
|
|
44
|
+
default:
|
|
45
|
+
this.enableLogging = !platform.config.disableDeviceLogging
|
|
46
|
+
this.enableDebugLogging = platform.config.debug
|
|
47
|
+
break
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Conversion objects between eWeLink speed labels and HomeKit values
|
|
51
|
+
this.speed2value = {
|
|
52
|
+
slow: 1,
|
|
53
|
+
moderate: 2,
|
|
54
|
+
fast: 3,
|
|
55
|
+
}
|
|
56
|
+
this.value2speed = {
|
|
57
|
+
1: 'slow',
|
|
58
|
+
2: 'moderate',
|
|
59
|
+
3: 'fast',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Add the fan service if it doesn't already exist
|
|
63
|
+
this.service = this.accessory.getService(this.hapServ.Fan) || this.accessory.addService(this.hapServ.Fan)
|
|
64
|
+
|
|
65
|
+
// Add the set handler to the fan on/off characteristic
|
|
66
|
+
this.service
|
|
67
|
+
.getCharacteristic(this.hapChar.On)
|
|
68
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
69
|
+
|
|
70
|
+
// Add the set handler to the fan rotation speed characteristic
|
|
71
|
+
this.service
|
|
72
|
+
.getCharacteristic(this.hapChar.RotationSpeed)
|
|
73
|
+
.setProps({
|
|
74
|
+
maxValue: 3,
|
|
75
|
+
minStep: 1,
|
|
76
|
+
minValue: 0,
|
|
77
|
+
unit: 'unitless', // This is actually from HAP for Bluetooth LE Specification, but fits
|
|
78
|
+
})
|
|
79
|
+
.onSet(async value => this.internalSpeedUpdate(value))
|
|
80
|
+
|
|
81
|
+
// Add the set handler to the fan swing mode (oscillation) characteristic
|
|
82
|
+
this.service
|
|
83
|
+
.getCharacteristic(this.hapChar.SwingMode)
|
|
84
|
+
.onSet(async value => this.internalShakeUpdate(value))
|
|
85
|
+
|
|
86
|
+
// Add the get handlers only if the user hasn't disabled the disableNoResponse setting
|
|
87
|
+
if (!platform.config.disableNoResponse) {
|
|
88
|
+
this.service.getCharacteristic(this.hapChar.On).onGet(() => {
|
|
89
|
+
if (!this.isOnline) {
|
|
90
|
+
throw new this.hapErr(-70402)
|
|
91
|
+
}
|
|
92
|
+
return this.service.getCharacteristic(this.hapChar.On).value
|
|
93
|
+
})
|
|
94
|
+
this.service.getCharacteristic(this.hapChar.RotationSpeed).onGet(() => {
|
|
95
|
+
if (!this.isOnline) {
|
|
96
|
+
throw new this.hapErr(-70402)
|
|
97
|
+
}
|
|
98
|
+
return this.service.getCharacteristic(this.hapChar.RotationSpeed).value
|
|
99
|
+
})
|
|
100
|
+
this.service.getCharacteristic(this.hapChar.SwingMode).onGet(() => {
|
|
101
|
+
if (!this.isOnline) {
|
|
102
|
+
throw new this.hapErr(-70402)
|
|
103
|
+
}
|
|
104
|
+
return this.service.getCharacteristic(this.hapChar.SwingMode).value
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Output the customised options to the log
|
|
109
|
+
const normalLogging = this.enableLogging ? 'standard' : 'disable'
|
|
110
|
+
const opts = JSON.stringify({
|
|
111
|
+
logging: this.enableDebugLogging ? 'debug' : normalLogging,
|
|
112
|
+
})
|
|
113
|
+
this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async internalStateUpdate(value) {
|
|
117
|
+
try {
|
|
118
|
+
const newState = value ? 'on' : 'off'
|
|
119
|
+
if (newState === this.cacheState) {
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
await this.platform.sendDeviceUpdate(this.accessory, { fan: newState })
|
|
123
|
+
this.cacheState = newState
|
|
124
|
+
if (this.enableLogging) {
|
|
125
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, newState)
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
131
|
+
}, 2000)
|
|
132
|
+
throw new this.hapErr(-70402)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async internalSpeedUpdate(value) {
|
|
137
|
+
try {
|
|
138
|
+
// This acts like a debounce function when endlessly sliding the slider
|
|
139
|
+
const updateKey = generateRandomString(5)
|
|
140
|
+
this.updateKey = updateKey
|
|
141
|
+
await sleep(500)
|
|
142
|
+
if (updateKey !== this.updateKey) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
if (value === 0) {
|
|
146
|
+
// A zero speed value means turning the fan off
|
|
147
|
+
await this.platform.sendDeviceUpdate(this.accessory, { fan: 'off' })
|
|
148
|
+
this.cacheState = 'off'
|
|
149
|
+
if (this.enableLogging) {
|
|
150
|
+
this.log('[%s] %s [off].', this.name, this.lang.curState)
|
|
151
|
+
}
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
const newSpeed = this.value2speed[value]
|
|
155
|
+
if (newSpeed === this.cacheSpeed) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
await this.platform.sendDeviceUpdate(this.accessory, { fan: 'on', speed: newSpeed })
|
|
159
|
+
this.cacheState = 'on'
|
|
160
|
+
this.cacheSpeed = newSpeed
|
|
161
|
+
if (this.enableLogging) {
|
|
162
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curSpeed, newSpeed)
|
|
163
|
+
}
|
|
164
|
+
} catch (err) {
|
|
165
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
166
|
+
setTimeout(() => {
|
|
167
|
+
this.service.updateCharacteristic(
|
|
168
|
+
this.hapChar.RotationSpeed,
|
|
169
|
+
this.speed2value[this.cacheSpeed] || 0,
|
|
170
|
+
)
|
|
171
|
+
}, 2000)
|
|
172
|
+
throw new this.hapErr(-70402)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async internalShakeUpdate(value) {
|
|
177
|
+
try {
|
|
178
|
+
const newShake = value === this.hapChar.SwingMode.SWING_ENABLED ? 'on' : 'off'
|
|
179
|
+
if (newShake === this.cacheShake) {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
await this.platform.sendDeviceUpdate(this.accessory, { shake: newShake })
|
|
183
|
+
this.cacheShake = newShake
|
|
184
|
+
if (this.enableLogging) {
|
|
185
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curOscillation, newShake)
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
this.service.updateCharacteristic(
|
|
191
|
+
this.hapChar.SwingMode,
|
|
192
|
+
this.cacheShake === 'on'
|
|
193
|
+
? this.hapChar.SwingMode.SWING_ENABLED
|
|
194
|
+
: this.hapChar.SwingMode.SWING_DISABLED,
|
|
195
|
+
)
|
|
196
|
+
}, 2000)
|
|
197
|
+
throw new this.hapErr(-70402)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async externalUpdate(params) {
|
|
202
|
+
try {
|
|
203
|
+
if (params.fan && params.fan !== this.cacheState) {
|
|
204
|
+
this.cacheState = params.fan
|
|
205
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
206
|
+
if (params.updateSource && this.enableLogging) {
|
|
207
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (params.speed && params.speed !== this.cacheSpeed && this.speed2value[params.speed]) {
|
|
211
|
+
this.cacheSpeed = params.speed
|
|
212
|
+
this.service.updateCharacteristic(this.hapChar.RotationSpeed, this.speed2value[this.cacheSpeed])
|
|
213
|
+
if (params.updateSource && this.enableLogging) {
|
|
214
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curSpeed, this.cacheSpeed)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (params.shake && params.shake !== this.cacheShake) {
|
|
218
|
+
this.cacheShake = params.shake
|
|
219
|
+
this.service.updateCharacteristic(
|
|
220
|
+
this.hapChar.SwingMode,
|
|
221
|
+
this.cacheShake === 'on'
|
|
222
|
+
? this.hapChar.SwingMode.SWING_ENABLED
|
|
223
|
+
: this.hapChar.SwingMode.SWING_DISABLED,
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
} catch (err) {
|
|
227
|
+
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
markStatus(isOnline) {
|
|
232
|
+
this.isOnline = isOnline
|
|
233
|
+
}
|
|
234
|
+
}
|
package/lib/device/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import deviceAirConditioner from './air-conditioner.js'
|
|
2
2
|
import deviceCurtain from './curtain.js'
|
|
3
3
|
import deviceDiffuser from './diffuser.js'
|
|
4
|
+
import deviceFanGear from './fan-gear.js'
|
|
4
5
|
import deviceFan from './fan.js'
|
|
5
6
|
import deviceGroup from './group.js'
|
|
6
7
|
import deviceHumidifier from './humidifier.js'
|
|
@@ -36,6 +37,7 @@ export default {
|
|
|
36
37
|
deviceCurtain,
|
|
37
38
|
deviceDiffuser,
|
|
38
39
|
deviceFan,
|
|
40
|
+
deviceFanGear,
|
|
39
41
|
deviceGroup,
|
|
40
42
|
deviceHumidifier,
|
|
41
43
|
deviceLightCCT,
|
|
@@ -4,6 +4,7 @@ import { generateRandomString, hasProperty, sleep } from '../utils/functions.js'
|
|
|
4
4
|
export default class {
|
|
5
5
|
constructor(platform, accessory) {
|
|
6
6
|
// Set up variables from the platform
|
|
7
|
+
this.eveChar = platform.eveChar
|
|
7
8
|
this.hapChar = platform.api.hap.Characteristic
|
|
8
9
|
this.hapErr = platform.api.hap.HapStatusError
|
|
9
10
|
this.hapServ = platform.api.hap.Service
|
|
@@ -54,6 +55,20 @@ export default class {
|
|
|
54
55
|
this.service = this.accessory.getService(this.hapServ.Lightbulb)
|
|
55
56
|
|| this.accessory.addService(this.hapServ.Lightbulb)
|
|
56
57
|
|
|
58
|
+
// The MINI dimmer also reports power readings
|
|
59
|
+
if (this.accessory.context.eweUIID === 277) {
|
|
60
|
+
this.powerReadings = true
|
|
61
|
+
if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
62
|
+
this.service.addCharacteristic(this.eveChar.CurrentConsumption)
|
|
63
|
+
}
|
|
64
|
+
if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
65
|
+
this.service.addCharacteristic(this.eveChar.ElectricCurrent)
|
|
66
|
+
}
|
|
67
|
+
if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
68
|
+
this.service.addCharacteristic(this.eveChar.Voltage)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
// Add the set handler to the lightbulb on/off characteristic
|
|
58
73
|
this.service
|
|
59
74
|
.getCharacteristic(this.hapChar.On)
|
|
@@ -232,6 +247,37 @@ export default class {
|
|
|
232
247
|
break
|
|
233
248
|
default:
|
|
234
249
|
}
|
|
250
|
+
|
|
251
|
+
if (this.powerReadings) {
|
|
252
|
+
let logger = false
|
|
253
|
+
let power
|
|
254
|
+
let voltage
|
|
255
|
+
let current
|
|
256
|
+
if (hasProperty(params, 'power')) {
|
|
257
|
+
power = Number.parseFloat(params.power) / 100
|
|
258
|
+
this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
|
|
259
|
+
logger = true
|
|
260
|
+
}
|
|
261
|
+
if (hasProperty(params, 'voltage')) {
|
|
262
|
+
voltage = Number.parseFloat(params.voltage) / 100
|
|
263
|
+
this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
|
|
264
|
+
logger = true
|
|
265
|
+
}
|
|
266
|
+
if (hasProperty(params, 'current')) {
|
|
267
|
+
current = Number.parseFloat(params.current) / 100
|
|
268
|
+
this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
|
|
269
|
+
logger = true
|
|
270
|
+
}
|
|
271
|
+
if (params.updateSource && logger && this.enableLogging) {
|
|
272
|
+
this.log(
|
|
273
|
+
'[%s] %s%s%s.',
|
|
274
|
+
this.name,
|
|
275
|
+
power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
|
|
276
|
+
voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
|
|
277
|
+
current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
235
281
|
} catch (err) {
|
|
236
282
|
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
237
283
|
}
|
|
@@ -55,6 +55,14 @@ export default class {
|
|
|
55
55
|
minStep: 0.1,
|
|
56
56
|
})
|
|
57
57
|
this.cacheTemp = this.tempService.getCharacteristic(this.hapChar.CurrentTemperature).value
|
|
58
|
+
|
|
59
|
+
// The Sonoff SC also reports dust, light and noise levels (uiid 18)
|
|
60
|
+
if (this.accessory.context.eweUIID === 18) {
|
|
61
|
+
this.airService = this.accessory.getService(this.hapServ.AirQualitySensor)
|
|
62
|
+
|| this.accessory.addService(this.hapServ.AirQualitySensor)
|
|
63
|
+
this.lightService = this.accessory.getService(this.hapServ.LightSensor)
|
|
64
|
+
|| this.accessory.addService(this.hapServ.LightSensor)
|
|
65
|
+
}
|
|
58
66
|
this.updateCache()
|
|
59
67
|
|
|
60
68
|
// Add the humidity sensor service if it doesn't already exist
|
|
@@ -101,6 +109,35 @@ export default class {
|
|
|
101
109
|
|
|
102
110
|
async externalUpdate(params) {
|
|
103
111
|
try {
|
|
112
|
+
if (this.airService && hasProperty(params, 'dusty')) {
|
|
113
|
+
// The device reports a level from 1 (best) to 10 (worst)
|
|
114
|
+
const rawDust = Number.parseInt(params.dusty, 10)
|
|
115
|
+
if (!Number.isNaN(rawDust)) {
|
|
116
|
+
const newQuality = Math.max(Math.min(Math.ceil(rawDust / 2), 5), 1)
|
|
117
|
+
if (newQuality !== this.cacheDust) {
|
|
118
|
+
this.cacheDust = newQuality
|
|
119
|
+
this.airService.updateCharacteristic(this.hapChar.AirQuality, this.cacheDust)
|
|
120
|
+
if (params.updateSource && this.enableLogging) {
|
|
121
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curAirQuality, rawDust)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (this.lightService && hasProperty(params, 'light')) {
|
|
127
|
+
// The device reports a level from 1 (darkest) to 10 (brightest),
|
|
128
|
+
// mapped to a nominal lux value for HomeKit
|
|
129
|
+
const rawLight = Number.parseInt(params.light, 10)
|
|
130
|
+
if (!Number.isNaN(rawLight)) {
|
|
131
|
+
const newLux = Math.max(rawLight * 10, 0.0001)
|
|
132
|
+
if (newLux !== this.cacheLux) {
|
|
133
|
+
this.cacheLux = newLux
|
|
134
|
+
this.lightService.updateCharacteristic(this.hapChar.CurrentAmbientLightLevel, this.cacheLux)
|
|
135
|
+
if (params.updateSource && this.enableLogging) {
|
|
136
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curLightLevel, rawLight)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
104
141
|
if (hasProperty(params, 'temperature')) {
|
|
105
142
|
const rawTemp = Number(params.temperature)
|
|
106
143
|
if (Number.isNaN(rawTemp) || rawTemp < -270 || rawTemp > 270) {
|
|
@@ -88,7 +88,9 @@ export default class {
|
|
|
88
88
|
case 32:
|
|
89
89
|
case 182:
|
|
90
90
|
case 190:
|
|
91
|
+
case 226:
|
|
91
92
|
case 276:
|
|
93
|
+
case 7032:
|
|
92
94
|
// Add Eve power characteristics
|
|
93
95
|
this.powerReadings = true
|
|
94
96
|
if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
|
|
@@ -104,7 +106,7 @@ export default class {
|
|
|
104
106
|
this.service.addCharacteristic(this.eveChar.Voltage)
|
|
105
107
|
}
|
|
106
108
|
|
|
107
|
-
if ([190, 276].includes(this.accessory.context.eweUIID)) {
|
|
109
|
+
if ([190, 276, 7032].includes(this.accessory.context.eweUIID)) {
|
|
108
110
|
this.divisor = 100
|
|
109
111
|
}
|
|
110
112
|
break
|
|
@@ -134,6 +136,14 @@ export default class {
|
|
|
134
136
|
this.isSCM = true
|
|
135
137
|
}
|
|
136
138
|
|
|
139
|
+
// The MINI R3/R4 family reports wall button presses, useful in detached relay mode
|
|
140
|
+
if (this.accessory.context.eweUIID === 138) {
|
|
141
|
+
this.buttonService = this.accessory.getService(this.hapServ.StatelessProgrammableSwitch)
|
|
142
|
+
|| this.accessory.addService(this.hapServ.StatelessProgrammableSwitch)
|
|
143
|
+
} else if (this.accessory.getService(this.hapServ.StatelessProgrammableSwitch)) {
|
|
144
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.StatelessProgrammableSwitch))
|
|
145
|
+
}
|
|
146
|
+
|
|
137
147
|
// Add the set handler to the switch on/off characteristic
|
|
138
148
|
this.service
|
|
139
149
|
.getCharacteristic(this.hapChar.On)
|
|
@@ -225,6 +235,21 @@ export default class {
|
|
|
225
235
|
|
|
226
236
|
async externalUpdate(params) {
|
|
227
237
|
try {
|
|
238
|
+
if (
|
|
239
|
+
this.buttonService
|
|
240
|
+
&& hasProperty(params, 'key')
|
|
241
|
+
&& [0, 1, 2].includes(params.key)
|
|
242
|
+
&& params.trigTime
|
|
243
|
+
&& params.trigTime !== this.lastTrigTime
|
|
244
|
+
) {
|
|
245
|
+
this.lastTrigTime = params.trigTime
|
|
246
|
+
this.buttonService.updateCharacteristic(this.hapChar.ProgrammableSwitchEvent, params.key)
|
|
247
|
+
if (params.updateSource && this.enableLogging) {
|
|
248
|
+
const doubleLong = params.key === 1 ? this.lang.buttonDouble : this.lang.buttonLong
|
|
249
|
+
const textLabel = params.key === 0 ? this.lang.buttonSingle : doubleLong
|
|
250
|
+
this.log('[%s] %s [%s].', this.name, this.lang.buttonEvent, textLabel)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
228
253
|
if (!this.isSCM && hasProperty(params, 'switch')) {
|
|
229
254
|
// Normalize boolean switch values (UIID 226 uses true/false)
|
|
230
255
|
const newState = typeof params.switch === 'boolean'
|
|
@@ -250,6 +275,17 @@ export default class {
|
|
|
250
275
|
if (!this.powerReadings) {
|
|
251
276
|
return
|
|
252
277
|
}
|
|
278
|
+
|
|
279
|
+
// Some models (e.g. uiid 226) report their readings under phase names
|
|
280
|
+
if (hasProperty(params, 'phase_0_p')) {
|
|
281
|
+
params.power = params.phase_0_p
|
|
282
|
+
}
|
|
283
|
+
if (hasProperty(params, 'phase_0_v')) {
|
|
284
|
+
params.voltage = params.phase_0_v
|
|
285
|
+
}
|
|
286
|
+
if (hasProperty(params, 'phase_0_c')) {
|
|
287
|
+
params.current = params.phase_0_c
|
|
288
|
+
}
|
|
253
289
|
let logger = false
|
|
254
290
|
let power
|
|
255
291
|
let voltage
|
|
@@ -53,6 +53,14 @@ export default class {
|
|
|
53
53
|
this.battService = this.accessory.getService(this.hapServ.Battery)
|
|
54
54
|
|| this.accessory.addService(this.hapServ.Battery)
|
|
55
55
|
|
|
56
|
+
// SNZB-03P models also report a binary bright/dark light level (uiid 7002)
|
|
57
|
+
if (accessory.context.eweUIID === 7002) {
|
|
58
|
+
this.lightService = this.accessory.getService(this.hapServ.LightSensor)
|
|
59
|
+
|| this.accessory.addService(this.hapServ.LightSensor)
|
|
60
|
+
} else if (this.accessory.getService(this.hapServ.LightSensor)) {
|
|
61
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.LightSensor))
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
// Pass the accessory to Fakegato to set up with Eve
|
|
57
65
|
this.accessory.eveService = new platform.eveService('motion', this.accessory, {
|
|
58
66
|
log: () => {},
|
|
@@ -70,6 +78,18 @@ export default class {
|
|
|
70
78
|
|
|
71
79
|
async externalUpdate(params) {
|
|
72
80
|
try {
|
|
81
|
+
if (this.lightService && hasProperty(params, 'brState')) {
|
|
82
|
+
// The device reports a binary state, so map brighter/darker to a
|
|
83
|
+
// nominal high/low lux value for HomeKit
|
|
84
|
+
const newLux = params.brState === 'brighter' ? 100 : 0.0001
|
|
85
|
+
if (newLux !== this.cacheLux) {
|
|
86
|
+
this.cacheLux = newLux
|
|
87
|
+
this.lightService.updateCharacteristic(this.hapChar.CurrentAmbientLightLevel, this.cacheLux)
|
|
88
|
+
if (params.updateSource && this.enableLogging) {
|
|
89
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curLightLevel, params.brState)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
73
93
|
if (hasProperty(params, 'battery') && params.battery !== this.cacheBatt) {
|
|
74
94
|
this.cacheBatt = params.battery
|
|
75
95
|
this.cacheBattScaled = Math.max(Math.min(this.cacheBatt, 100), 0)
|
|
@@ -53,6 +53,14 @@ export default class {
|
|
|
53
53
|
this.battService = this.accessory.getService(this.hapServ.Battery)
|
|
54
54
|
|| this.accessory.addService(this.hapServ.Battery)
|
|
55
55
|
|
|
56
|
+
// SNZB-06P models also report a binary bright/dark light level (uiid 7016)
|
|
57
|
+
if (accessory.context.eweUIID === 7016) {
|
|
58
|
+
this.lightService = this.accessory.getService(this.hapServ.LightSensor)
|
|
59
|
+
|| this.accessory.addService(this.hapServ.LightSensor)
|
|
60
|
+
} else if (this.accessory.getService(this.hapServ.LightSensor)) {
|
|
61
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.LightSensor))
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
// Pass the accessory to Fakegato to set up with Eve
|
|
57
65
|
this.accessory.eveService = new platform.eveService('motion', this.accessory, {
|
|
58
66
|
log: () => {},
|
|
@@ -70,6 +78,18 @@ export default class {
|
|
|
70
78
|
|
|
71
79
|
async externalUpdate(params) {
|
|
72
80
|
try {
|
|
81
|
+
if (this.lightService && hasProperty(params, 'brState')) {
|
|
82
|
+
// The device reports a binary state, so map brighter/darker to a
|
|
83
|
+
// nominal high/low lux value for HomeKit
|
|
84
|
+
const newLux = params.brState === 'brighter' ? 100 : 0.0001
|
|
85
|
+
if (newLux !== this.cacheLux) {
|
|
86
|
+
this.cacheLux = newLux
|
|
87
|
+
this.lightService.updateCharacteristic(this.hapChar.CurrentAmbientLightLevel, this.cacheLux)
|
|
88
|
+
if (params.updateSource && this.enableLogging) {
|
|
89
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curLightLevel, params.brState)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
73
93
|
if (hasProperty(params, 'battery') && params.battery !== this.cacheBatt) {
|
|
74
94
|
this.cacheBatt = params.battery
|
|
75
95
|
this.cacheBattScaled = Math.max(Math.min(this.cacheBatt, 100), 0)
|
|
@@ -77,6 +77,14 @@ export default class {
|
|
|
77
77
|
this.service = this.accessory.getService(this.hapServ.Thermostat)
|
|
78
78
|
|| this.accessory.addService(this.hapServ.Thermostat)
|
|
79
79
|
|
|
80
|
+
// The TRVZB supports a child lock, exposed as the lock physical controls characteristic
|
|
81
|
+
if (!this.service.testCharacteristic(this.hapChar.LockPhysicalControls)) {
|
|
82
|
+
this.service.addCharacteristic(this.hapChar.LockPhysicalControls)
|
|
83
|
+
}
|
|
84
|
+
this.service
|
|
85
|
+
.getCharacteristic(this.hapChar.LockPhysicalControls)
|
|
86
|
+
.onSet(async value => this.internalLockUpdate(value))
|
|
87
|
+
|
|
80
88
|
// Set custom properties of the current temperature characteristic
|
|
81
89
|
this.service.getCharacteristic(this.hapChar.CurrentTemperature).setProps({
|
|
82
90
|
minStep: 0.1,
|
|
@@ -223,8 +231,45 @@ export default class {
|
|
|
223
231
|
}
|
|
224
232
|
}
|
|
225
233
|
|
|
234
|
+
async internalLockUpdate(value) {
|
|
235
|
+
try {
|
|
236
|
+
const newLock = value === this.hapChar.LockPhysicalControls.CONTROL_LOCK_ENABLED
|
|
237
|
+
if (newLock === this.cacheLock) {
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
await this.platform.sendDeviceUpdate(this.accessory, { childLock: newLock })
|
|
241
|
+
this.cacheLock = newLock
|
|
242
|
+
if (this.enableLogging) {
|
|
243
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curChildLock, newLock ? 'on' : 'off')
|
|
244
|
+
}
|
|
245
|
+
} catch (err) {
|
|
246
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
247
|
+
setTimeout(() => {
|
|
248
|
+
this.service.updateCharacteristic(
|
|
249
|
+
this.hapChar.LockPhysicalControls,
|
|
250
|
+
this.cacheLock
|
|
251
|
+
? this.hapChar.LockPhysicalControls.CONTROL_LOCK_ENABLED
|
|
252
|
+
: this.hapChar.LockPhysicalControls.CONTROL_LOCK_DISABLED,
|
|
253
|
+
)
|
|
254
|
+
}, 2000)
|
|
255
|
+
throw new this.hapErr(-70402)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
226
259
|
async externalUpdate(params) {
|
|
227
260
|
try {
|
|
261
|
+
if (hasProperty(params, 'childLock') && params.childLock !== this.cacheLock) {
|
|
262
|
+
this.cacheLock = params.childLock
|
|
263
|
+
this.service.updateCharacteristic(
|
|
264
|
+
this.hapChar.LockPhysicalControls,
|
|
265
|
+
this.cacheLock
|
|
266
|
+
? this.hapChar.LockPhysicalControls.CONTROL_LOCK_ENABLED
|
|
267
|
+
: this.hapChar.LockPhysicalControls.CONTROL_LOCK_DISABLED,
|
|
268
|
+
)
|
|
269
|
+
if (params.updateSource && this.enableLogging) {
|
|
270
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curChildLock, this.cacheLock ? 'on' : 'off')
|
|
271
|
+
}
|
|
272
|
+
}
|
|
228
273
|
if (hasProperty(params, 'workMode')) {
|
|
229
274
|
let workMode
|
|
230
275
|
let hkWorkMode
|
package/lib/platform.js
CHANGED
|
@@ -1107,6 +1107,15 @@ export default class {
|
|
|
1107
1107
|
this.applyAccessoryLogging(accessory)
|
|
1108
1108
|
accessory.control = new deviceTypes.deviceFan(this, accessory)
|
|
1109
1109
|
/**/
|
|
1110
|
+
} else if (platformConsts.devices.fanGear.includes(uiid)) {
|
|
1111
|
+
/**
|
|
1112
|
+
FANS [GEAR]
|
|
1113
|
+
*
|
|
1114
|
+
*/
|
|
1115
|
+
accessory = devicesInHB.get(uuid) || this.addAccessory(device, `${device.deviceid}SWX`)
|
|
1116
|
+
this.applyAccessoryLogging(accessory)
|
|
1117
|
+
accessory.control = new deviceTypes.deviceFanGear(this, accessory)
|
|
1118
|
+
/**/
|
|
1110
1119
|
} else if (platformConsts.devices.diffuser.includes(uiid)) {
|
|
1111
1120
|
/**
|
|
1112
1121
|
******
|
package/lib/utils/constants.js
CHANGED
|
@@ -291,9 +291,9 @@ export default {
|
|
|
291
291
|
275,
|
|
292
292
|
277,
|
|
293
293
|
],
|
|
294
|
-
switchSingle: [1, 6, 14, 24, 27, 216, 225, 226, 268, 1009, 1256, 7004, 7005, 7010, 7028,
|
|
294
|
+
switchSingle: [1, 6, 14, 24, 27, 216, 225, 226, 268, 1009, 1256, 7004, 7005, 7010, 7028, 20001],
|
|
295
295
|
switchSCM: [77, 78, 81, 107, 112, 138, 160, 191, 209, 264],
|
|
296
|
-
switchSCMPower: [182, 190, 276],
|
|
296
|
+
switchSCMPower: [182, 190, 276, 7032],
|
|
297
297
|
switchSinglePower: [5, 32],
|
|
298
298
|
switchMulti: [
|
|
299
299
|
2,
|
|
@@ -316,6 +316,7 @@ export default {
|
|
|
316
316
|
141,
|
|
317
317
|
161,
|
|
318
318
|
162,
|
|
319
|
+
165,
|
|
319
320
|
210,
|
|
320
321
|
211,
|
|
321
322
|
212,
|
|
@@ -329,7 +330,7 @@ export default {
|
|
|
329
330
|
7040,
|
|
330
331
|
20004,
|
|
331
332
|
],
|
|
332
|
-
switchMultiPower: [126,
|
|
333
|
+
switchMultiPower: [126, 130, 262],
|
|
333
334
|
switchMate: [177],
|
|
334
335
|
switchMan: [174],
|
|
335
336
|
lightDimmer: [36, 44, 57, 277, 20006],
|
|
@@ -346,17 +347,18 @@ export default {
|
|
|
346
347
|
panel: [133, 195, 228],
|
|
347
348
|
virtual: [265],
|
|
348
349
|
fan: [34],
|
|
350
|
+
fanGear: [17],
|
|
349
351
|
humidifier: [19],
|
|
350
352
|
diffuser: [25],
|
|
351
353
|
camera: [87],
|
|
352
354
|
rfBridge: [28, 98],
|
|
353
355
|
zbBridge: [66, 128, 168, 243], // 128 not technically Zigbee, but same data structure
|
|
354
|
-
zbSwitchStateless: [1000, 7000],
|
|
356
|
+
zbSwitchStateless: [1000, 7000, 7039],
|
|
355
357
|
zbLightDimmer: [1257],
|
|
356
358
|
zbLightCCT: [1258],
|
|
357
359
|
zbLightRGBCCT: [3258, 7009],
|
|
358
360
|
zbMotor: [1514, 7006, 7034],
|
|
359
|
-
zbSensorAmbient: [1770, 1771, 7014, 7033, 7038],
|
|
361
|
+
zbSensorAmbient: [242, 1770, 1771, 7014, 7033, 7038],
|
|
360
362
|
zbSensorMotion: [2026, 7002],
|
|
361
363
|
zbSensorOccupancy: [7016],
|
|
362
364
|
zbSensorContact: [3026, 7003],
|
|
@@ -365,7 +367,7 @@ export default {
|
|
|
365
367
|
zbThermostat: [7017],
|
|
366
368
|
zbWaterValve: [7027, 7047],
|
|
367
369
|
group: [5000],
|
|
368
|
-
template: [
|
|
370
|
+
template: [], // Currently unused
|
|
369
371
|
cannotSupport: [0, 65, 118, 119, 120, 121, 204, 223, 20000],
|
|
370
372
|
garageSensors: [102, 154, 3026],
|
|
371
373
|
skipUpdateRequest: [1000],
|
|
@@ -464,6 +466,7 @@ export default {
|
|
|
464
466
|
11: 1, // "CURTAIN" (King Q4 Cover)
|
|
465
467
|
14: 1, // "SWITCH_CHANGE" (Sonoff SV)
|
|
466
468
|
15: 1, // "THERMOSTAT" (TH10, TH16)
|
|
469
|
+
17: 1, // Three gear fan
|
|
467
470
|
18: 1, // "SENSORS_CENTER" (Sonoff SC)
|
|
468
471
|
19: 1, // "HUMIDIFIER"
|
|
469
472
|
22: 1, // "RGB_BALL_LIGHT" (B1, B1_R2)
|
|
@@ -504,7 +507,8 @@ export default {
|
|
|
504
507
|
126: 2, // "DUALR3"
|
|
505
508
|
127: 1, // "GTTA127"
|
|
506
509
|
128: 1, // SPM Main Unit (same data structure as zigbee bridge)
|
|
507
|
-
130:
|
|
510
|
+
130: 4, // SPM-4Relay sub unit
|
|
511
|
+
242: 1, // Wireless temperature and humidity sensor
|
|
508
512
|
133: 1, // "NSPANEL"
|
|
509
513
|
135: 1, // ""
|
|
510
514
|
136: 1, // RGB Five-Color Light_Support 2.4G eWeLink-Remote
|
|
@@ -561,6 +565,7 @@ export default {
|
|
|
561
565
|
5000: 1, // Custom uiid for groups
|
|
562
566
|
5026: 1, // Zigbee Smoke Sensor
|
|
563
567
|
7000: 1, // Zigbee Button?
|
|
568
|
+
7039: 1, // SNZB-01M Zigbee Button
|
|
564
569
|
7002: 1, // Zigbee Human Body Sensor_Support OTA
|
|
565
570
|
7003: 1, // Zigbee Door Magnet_Support OTA
|
|
566
571
|
7004: 1, // Zigbee Single-Channel Switch _Support OTA
|
|
@@ -605,7 +610,6 @@ export default {
|
|
|
605
610
|
12: 0, // "EW-RE"
|
|
606
611
|
13: 0, // "FIREPLACE"
|
|
607
612
|
16: 0, // "COLD_WARM_LED"
|
|
608
|
-
17: 0, // "THREE_GEAR_FAN"
|
|
609
613
|
23: 0, // "NEST_THERMOSTAT"
|
|
610
614
|
26: 0, // "RuiMiTeWenKongQi"
|
|
611
615
|
35: 0, // "EZVIZ_CAMERA",
|
package/lib/utils/lang-en.js
CHANGED
|
@@ -37,6 +37,11 @@ export default {
|
|
|
37
37
|
curPur: 'current purifying',
|
|
38
38
|
curScene: 'current scene',
|
|
39
39
|
curSpeed: 'current speed',
|
|
40
|
+
curOscillation: 'current oscillation',
|
|
41
|
+
curAirQuality: 'current air quality level',
|
|
42
|
+
curChildLock: 'current child lock',
|
|
43
|
+
buttonEvent: 'button event',
|
|
44
|
+
curLightLevel: 'current light level',
|
|
40
45
|
curState: 'current state',
|
|
41
46
|
curTarg: 'current target',
|
|
42
47
|
curTemp: 'current temperature',
|
package/package.json
CHANGED