@homebridge-plugins/homebridge-ewelink 12.18.2-beta.2 → 12.18.2
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 +9 -1
- package/config.schema.json +9 -0
- package/lib/device/group.js +21 -2
- package/lib/device/rf-bridge.js +172 -170
- package/lib/device/zigbee/motor.js +0 -4
- package/lib/device/zigbee/sensor-ambient.js +16 -4
- package/lib/device/zigbee/zigbee-water-valve.js +21 -1
- package/lib/platform.js +9 -0
- package/lib/utils/constants.js +12 -0
- package/lib/utils/lang-en.js +1 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@homebridge-plugins/homebridge-ewelink` will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## v12.18.2 (
|
|
5
|
+
## v12.18.2 (2026-08-09)
|
|
6
6
|
|
|
7
7
|
### Changed
|
|
8
8
|
|
|
@@ -17,6 +17,14 @@ All notable changes to `@homebridge-plugins/homebridge-ewelink` will be document
|
|
|
17
17
|
- fix: make the per-device logging override settable, and drop the debug option that never was
|
|
18
18
|
- fix: move the two-door garage one door at a time, so the second is not cut short
|
|
19
19
|
- fix: stop the lan discovery printing straight to the console, whatever the log level
|
|
20
|
+
- fix: trigger rf sensor channels one at a time, so the reset delay is actually waited on
|
|
21
|
+
- fix: reject an operation or sensor time so large it would make the reset timer fire immediately
|
|
22
|
+
- fix(zigbee): stop forcing debug logs on zigbee motors, which ignored the logging setting
|
|
23
|
+
- test: record what every device handler builds, so a refactor can be shown to change nothing
|
|
24
|
+
- fix: honour the per-device logging setting on groups and the zigbee water valve
|
|
25
|
+
- fix(zigbee): read the snzb-02ld temperature as degrees, so 18.4c stops showing as 0.18c
|
|
26
|
+
- test: check the device handlers against payloads real devices sent
|
|
27
|
+
- chore(deps): dependency updates
|
|
20
28
|
|
|
21
29
|
## v12.18.1 (2026-08-02)
|
|
22
30
|
|
package/config.schema.json
CHANGED
|
@@ -421,6 +421,7 @@
|
|
|
421
421
|
"description": "Garage doors: the time to open/close the device. Locks: the time to show as unlocked. This setting is in deciseconds - count the time in seconds and multiply by 10, for example 75 for 7.5 seconds. Must be 20 or more.",
|
|
422
422
|
"placeholder": 100,
|
|
423
423
|
"minimum": 20,
|
|
424
|
+
"maximum": 2073600,
|
|
424
425
|
"condition": {
|
|
425
426
|
"functionBody": "return (model.singleDevices && model.singleDevices[arrayIndices] && ((model.singleDevices[arrayIndices].deviceModel === 'gddc5' && model.singleDevices[arrayIndices].showAsEachen !== 'lock') || ['garage', 'lock', 'gate'].includes(model.singleDevices[arrayIndices].showAs)) && !model.singleDevices[arrayIndices].ignoreDevice);"
|
|
426
427
|
}
|
|
@@ -431,6 +432,7 @@
|
|
|
431
432
|
"description": "If your garage door has a different operation time for up and down, use this setting to specify the operation time DOWN (in deciseconds) and the other setting to specify the operation time UP. Otherwise leave blank. Must be 20 or more.",
|
|
432
433
|
"placeholder": 100,
|
|
433
434
|
"minimum": 20,
|
|
435
|
+
"maximum": 2073600,
|
|
434
436
|
"condition": {
|
|
435
437
|
"functionBody": "return (model.singleDevices && model.singleDevices[arrayIndices] && ['garage'].includes(model.singleDevices[arrayIndices].showAs) && !model.singleDevices[arrayIndices].ignoreDevice);"
|
|
436
438
|
}
|
|
@@ -760,6 +762,7 @@
|
|
|
760
762
|
"description": "Blinds/garage doors: the time to open/close the device. Locks: the time to show as unlocked. This setting is in deciseconds - count the time in seconds and multiply by 10, for example 75 for 7.5 seconds. Must be 20 or more.",
|
|
761
763
|
"placeholder": 100,
|
|
762
764
|
"minimum": 20,
|
|
765
|
+
"maximum": 2073600,
|
|
763
766
|
"condition": {
|
|
764
767
|
"functionBody": "return (model.multiDevices && model.multiDevices[arrayIndices] && ['garage', 'garage_two', 'garage_four', 'blind', 'door', 'window', 'lock', 'gate'].includes(model.multiDevices[arrayIndices].showAs) && !model.multiDevices[arrayIndices].ignoreDevice);"
|
|
765
768
|
}
|
|
@@ -770,6 +773,7 @@
|
|
|
770
773
|
"description": "If your garage door or blind has a different operation time for up and down, use this setting to specify the operation time DOWN (in deciseconds) and the other setting to specify the operation time UP. Otherwise leave blank. Must be 20 or more.",
|
|
771
774
|
"placeholder": 100,
|
|
772
775
|
"minimum": 20,
|
|
776
|
+
"maximum": 2073600,
|
|
773
777
|
"condition": {
|
|
774
778
|
"functionBody": "return (model.multiDevices && model.multiDevices[arrayIndices] && ['garage', 'garage_two', 'garage_four', 'blind', 'door', 'window'].includes(model.multiDevices[arrayIndices].showAs) && !model.multiDevices[arrayIndices].ignoreDevice);"
|
|
775
779
|
}
|
|
@@ -1383,6 +1387,7 @@
|
|
|
1383
1387
|
"description": "An offset in seconds to ignore notifications if the delay between this Zigbee Motion/Leak activating or Zigbee Button press and the plugin receiving the notification is greater than this number. Must be 5 or more.",
|
|
1384
1388
|
"placeholder": 60,
|
|
1385
1389
|
"minimum": 5,
|
|
1390
|
+
"maximum": 2073600,
|
|
1386
1391
|
"condition": {
|
|
1387
1392
|
"functionBody": "return (model.sensorDevices && model.sensorDevices[arrayIndices] && ['snzb01', 'snzb03', 'leak'].includes(model.sensorDevices[arrayIndices].deviceModel) && !model.sensorDevices[arrayIndices].ignoreDevice);"
|
|
1388
1393
|
}
|
|
@@ -1522,6 +1527,7 @@
|
|
|
1522
1527
|
"description": "The time to open/close the device. This setting is in deciseconds - count the time in seconds and multiply by 10, for example 75 for 7.5 seconds. Must be 20 or more.",
|
|
1523
1528
|
"placeholder": 100,
|
|
1524
1529
|
"minimum": 20,
|
|
1530
|
+
"maximum": 2073600,
|
|
1525
1531
|
"condition": {
|
|
1526
1532
|
"functionBody": "return (model.bridgeSensors && model.bridgeSensors[arrayIndices] && model.bridgeSensors[arrayIndices].fullDeviceId && model.bridgeSensors[arrayIndices].fullDeviceId.length >= 13 && model.bridgeSensors[arrayIndices].deviceType === 'curtain' && ['blind', 'door', 'window'].includes(model.bridgeSensors[arrayIndices].curtainType));"
|
|
1527
1533
|
}
|
|
@@ -1532,6 +1538,7 @@
|
|
|
1532
1538
|
"description": "If your device has a different operation time for up and down, use this setting to specify the operation time DOWN (in deciseconds) and the other setting to specify the operation time UP. Otherwise leave blank. Must be 20 or more.",
|
|
1533
1539
|
"placeholder": 100,
|
|
1534
1540
|
"minimum": 20,
|
|
1541
|
+
"maximum": 2073600,
|
|
1535
1542
|
"condition": {
|
|
1536
1543
|
"functionBody": "return (model.bridgeSensors && model.bridgeSensors[arrayIndices] && model.bridgeSensors[arrayIndices].fullDeviceId && model.bridgeSensors[arrayIndices].fullDeviceId.length >= 13 && model.bridgeSensors[arrayIndices].deviceType === 'curtain' && ['blind', 'door', 'window'].includes(model.bridgeSensors[arrayIndices].curtainType));"
|
|
1537
1544
|
}
|
|
@@ -1588,6 +1595,7 @@
|
|
|
1588
1595
|
"description": "The number of seconds which the sensor tile in the Home app will light up for if this sensor is activated. Must be 1 or more.",
|
|
1589
1596
|
"placeholder": 60,
|
|
1590
1597
|
"minimum": 1,
|
|
1598
|
+
"maximum": 2073600,
|
|
1591
1599
|
"condition": {
|
|
1592
1600
|
"functionBody": "return (model.bridgeSensors && model.bridgeSensors[arrayIndices] && model.bridgeSensors[arrayIndices].type && model.bridgeSensors[arrayIndices].deviceType === 'sensor' && ['motion', 'smoke', 'water', 'co', 'co2', 'occupancy', 'contact'].includes(model.bridgeSensors[arrayIndices].type));"
|
|
1593
1601
|
}
|
|
@@ -1598,6 +1606,7 @@
|
|
|
1598
1606
|
"description": "An offset in seconds to ignore notifications if the delay between this sensor activating and the plugin receiving the notification is greater than this number. Must be 5 or more.",
|
|
1599
1607
|
"placeholder": 60,
|
|
1600
1608
|
"minimum": 5,
|
|
1609
|
+
"maximum": 2073600,
|
|
1601
1610
|
"condition": {
|
|
1602
1611
|
"functionBody": "return (model.bridgeSensors && model.bridgeSensors[arrayIndices] && model.bridgeSensors[arrayIndices].type && model.bridgeSensors[arrayIndices].deviceType === 'sensor' && ['motion', 'smoke', 'water', 'co', 'co2', 'occupancy', 'contact'].includes(model.bridgeSensors[arrayIndices].type));"
|
|
1603
1612
|
}
|
package/lib/device/group.js
CHANGED
|
@@ -15,9 +15,28 @@ export default class {
|
|
|
15
15
|
this.name = accessory.displayName
|
|
16
16
|
this.accessory = accessory
|
|
17
17
|
|
|
18
|
+
// Set up custom variables for this device type
|
|
19
|
+
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
20
|
+
|
|
18
21
|
// Set the correct logging variables for this accessory
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
switch (deviceConf.overrideLogging) {
|
|
23
|
+
case 'standard':
|
|
24
|
+
this.enableLogging = true
|
|
25
|
+
this.enableDebugLogging = false
|
|
26
|
+
break
|
|
27
|
+
case 'debug':
|
|
28
|
+
this.enableLogging = true
|
|
29
|
+
this.enableDebugLogging = true
|
|
30
|
+
break
|
|
31
|
+
case 'disable':
|
|
32
|
+
this.enableLogging = false
|
|
33
|
+
this.enableDebugLogging = false
|
|
34
|
+
break
|
|
35
|
+
default:
|
|
36
|
+
this.enableLogging = !platform.config.disableDeviceLogging
|
|
37
|
+
this.enableDebugLogging = false
|
|
38
|
+
break
|
|
39
|
+
}
|
|
21
40
|
|
|
22
41
|
if (platformConsts.devices.switchSCM.includes(this.accessory.context.groupUIID)) {
|
|
23
42
|
this.isSCM = true
|
package/lib/device/rf-bridge.js
CHANGED
|
@@ -116,202 +116,204 @@ export default class {
|
|
|
116
116
|
|| params.updateSource === 'LAN'
|
|
117
117
|
) {
|
|
118
118
|
// RF Sensor
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
// One channel at a time. This was a `forEach(async ...)`, so every
|
|
120
|
+
// triggered channel ran at once and the promise each returned was thrown
|
|
121
|
+
// away - the `sleep` calls below held nothing back, and a rejection
|
|
122
|
+
// escaped the try in externalUpdate rather than being caught by it.
|
|
123
|
+
for (const chan of Object.keys(params).filter(name => rfTrigRegex.test(name))) {
|
|
124
|
+
try {
|
|
125
|
+
this.devicesInHB.forEach((acc) => {
|
|
126
|
+
if (
|
|
127
|
+
acc.context.eweDeviceId === this.accessory.context.eweDeviceId
|
|
128
|
+
&& hasProperty(acc.context, 'buttons')
|
|
129
|
+
&& hasProperty(acc.context.buttons, chan.split('g')[1].toString())
|
|
130
|
+
) {
|
|
131
|
+
subAccessory = acc
|
|
132
|
+
channel = chan.split('g')[1].toString()
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
if (subAccessory) {
|
|
136
|
+
// Avoid duplicate triggers from LAN and WS
|
|
137
|
+
if (params[chan] === subAccessory.context.cacheLastAct) {
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
const deviceConf = this.platform.rfSubdevices[subAccessory.context.hbDeviceId] || {}
|
|
141
|
+
let enableLogging
|
|
142
|
+
switch (deviceConf.overrideLogging) {
|
|
143
|
+
case 'standard':
|
|
144
|
+
case 'debug':
|
|
145
|
+
enableLogging = true
|
|
146
|
+
break
|
|
147
|
+
case 'disable':
|
|
148
|
+
enableLogging = false
|
|
149
|
+
break
|
|
150
|
+
default:
|
|
151
|
+
enableLogging = !this.disableDeviceLogging
|
|
152
|
+
break
|
|
153
|
+
}
|
|
154
|
+
switch (subAccessory.context.subType) {
|
|
155
|
+
case 'blind':
|
|
156
|
+
case 'door':
|
|
157
|
+
case 'window':
|
|
158
|
+
// Simulations don't support external updates
|
|
159
|
+
continue
|
|
160
|
+
case 'button':
|
|
161
|
+
case 'curtain': {
|
|
162
|
+
const service = subAccessory.context.buttons[channel]
|
|
163
|
+
subAccessory.getService(service).updateCharacteristic(this.hapChar.On, true)
|
|
164
|
+
this.log(
|
|
165
|
+
'[%s] %s [%s].',
|
|
166
|
+
subAccessory.displayName,
|
|
167
|
+
this.lang.curState,
|
|
168
|
+
this.lang.buttonTrig,
|
|
169
|
+
)
|
|
170
|
+
await sleep(3000)
|
|
171
|
+
subAccessory.getService(service).updateCharacteristic(this.hapChar.On, false)
|
|
172
|
+
continue
|
|
137
173
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
174
|
+
default:
|
|
175
|
+
break
|
|
176
|
+
}
|
|
177
|
+
const sensorTimeLength = deviceConf.sensorTimeLength || platformConsts.defaultValues.sensorTimeLength
|
|
178
|
+
const sensorTimeDifference = deviceConf.sensorTimeDifference || platformConsts.defaultValues.sensorTimeDifference
|
|
179
|
+
subAccessory.context.cacheLastAct = params[chan]
|
|
180
|
+
const timeOfMotion = new Date(params[chan])
|
|
181
|
+
const diff = (timeNow.getTime() - timeOfMotion.getTime()) / 1000
|
|
182
|
+
let serv
|
|
183
|
+
let char
|
|
184
|
+
if (diff < sensorTimeDifference) {
|
|
185
|
+
switch (deviceConf.type) {
|
|
186
|
+
case 'water':
|
|
187
|
+
serv = this.hapServ.LeakSensor
|
|
188
|
+
char = this.hapChar.LeakDetected
|
|
144
189
|
break
|
|
145
|
-
case '
|
|
146
|
-
|
|
190
|
+
case 'fire':
|
|
191
|
+
case 'smoke':
|
|
192
|
+
serv = this.hapServ.SmokeSensor
|
|
193
|
+
char = this.hapChar.SmokeDetected
|
|
147
194
|
break
|
|
148
|
-
|
|
149
|
-
|
|
195
|
+
case 'co':
|
|
196
|
+
serv = this.hapServ.CarbonMonoxideSensor
|
|
197
|
+
char = this.hapChar.CarbonMonoxideDetected
|
|
198
|
+
break
|
|
199
|
+
case 'co2':
|
|
200
|
+
serv = this.hapServ.CarbonDioxideSensor
|
|
201
|
+
char = this.hapChar.CarbonDioxideDetected
|
|
202
|
+
break
|
|
203
|
+
case 'contact': {
|
|
204
|
+
serv = this.hapServ.ContactSensor
|
|
205
|
+
char = this.hapChar.ContactSensorState
|
|
206
|
+
const initialTime = subAccessory.eveService.getInitialTime()
|
|
207
|
+
subAccessory
|
|
208
|
+
.getService(serv)
|
|
209
|
+
.updateCharacteristic(
|
|
210
|
+
this.eveChar.LastActivation,
|
|
211
|
+
Math.round(new Date().valueOf() / 1000) - initialTime,
|
|
212
|
+
)
|
|
150
213
|
break
|
|
151
|
-
}
|
|
152
|
-
switch (subAccessory.context.subType) {
|
|
153
|
-
case 'blind':
|
|
154
|
-
case 'door':
|
|
155
|
-
case 'window':
|
|
156
|
-
// Simulations don't support external updates
|
|
157
|
-
return
|
|
158
|
-
case 'button':
|
|
159
|
-
case 'curtain': {
|
|
160
|
-
const service = subAccessory.context.buttons[channel]
|
|
161
|
-
subAccessory.getService(service).updateCharacteristic(this.hapChar.On, true)
|
|
162
|
-
this.log(
|
|
163
|
-
'[%s] %s [%s].',
|
|
164
|
-
subAccessory.displayName,
|
|
165
|
-
this.lang.curState,
|
|
166
|
-
this.lang.buttonTrig,
|
|
167
|
-
)
|
|
168
|
-
await sleep(3000)
|
|
169
|
-
subAccessory.getService(service).updateCharacteristic(this.hapChar.On, false)
|
|
170
|
-
return
|
|
171
214
|
}
|
|
172
|
-
|
|
215
|
+
case 'occupancy':
|
|
216
|
+
serv = this.hapServ.OccupancySensor
|
|
217
|
+
char = this.hapChar.OccupancyDetected
|
|
218
|
+
break
|
|
219
|
+
case 'p_button':
|
|
220
|
+
serv = this.hapServ.StatelessProgrammableSwitch
|
|
221
|
+
char = this.hapChar.ProgrammableSwitchEvent
|
|
222
|
+
break
|
|
223
|
+
case 'doorbell':
|
|
224
|
+
serv = this.hapServ.Doorbell
|
|
225
|
+
char = this.hapChar.ProgrammableSwitchEvent
|
|
226
|
+
break
|
|
227
|
+
default: {
|
|
228
|
+
serv = this.hapServ.MotionSensor
|
|
229
|
+
char = this.hapChar.MotionDetected
|
|
230
|
+
const initialTime = subAccessory.eveService.getInitialTime()
|
|
231
|
+
subAccessory
|
|
232
|
+
.getService(serv)
|
|
233
|
+
.updateCharacteristic(
|
|
234
|
+
this.eveChar.LastActivation,
|
|
235
|
+
Math.round(new Date().valueOf() / 1000) - initialTime,
|
|
236
|
+
)
|
|
173
237
|
break
|
|
174
|
-
}
|
|
175
|
-
const sensorTimeLength = deviceConf.sensorTimeLength || platformConsts.defaultValues.sensorTimeLength
|
|
176
|
-
const sensorTimeDifference = deviceConf.sensorTimeDifference || platformConsts.defaultValues.sensorTimeDifference
|
|
177
|
-
subAccessory.context.cacheLastAct = params[chan]
|
|
178
|
-
const timeOfMotion = new Date(params[chan])
|
|
179
|
-
const diff = (timeNow.getTime() - timeOfMotion.getTime()) / 1000
|
|
180
|
-
let serv
|
|
181
|
-
let char
|
|
182
|
-
if (diff < sensorTimeDifference) {
|
|
183
|
-
switch (deviceConf.type) {
|
|
184
|
-
case 'water':
|
|
185
|
-
serv = this.hapServ.LeakSensor
|
|
186
|
-
char = this.hapChar.LeakDetected
|
|
187
|
-
break
|
|
188
|
-
case 'fire':
|
|
189
|
-
case 'smoke':
|
|
190
|
-
serv = this.hapServ.SmokeSensor
|
|
191
|
-
char = this.hapChar.SmokeDetected
|
|
192
|
-
break
|
|
193
|
-
case 'co':
|
|
194
|
-
serv = this.hapServ.CarbonMonoxideSensor
|
|
195
|
-
char = this.hapChar.CarbonMonoxideDetected
|
|
196
|
-
break
|
|
197
|
-
case 'co2':
|
|
198
|
-
serv = this.hapServ.CarbonDioxideSensor
|
|
199
|
-
char = this.hapChar.CarbonDioxideDetected
|
|
200
|
-
break
|
|
201
|
-
case 'contact': {
|
|
202
|
-
serv = this.hapServ.ContactSensor
|
|
203
|
-
char = this.hapChar.ContactSensorState
|
|
204
|
-
const initialTime = subAccessory.eveService.getInitialTime()
|
|
205
|
-
subAccessory
|
|
206
|
-
.getService(serv)
|
|
207
|
-
.updateCharacteristic(
|
|
208
|
-
this.eveChar.LastActivation,
|
|
209
|
-
Math.round(new Date().valueOf() / 1000) - initialTime,
|
|
210
|
-
)
|
|
211
|
-
break
|
|
212
|
-
}
|
|
213
|
-
case 'occupancy':
|
|
214
|
-
serv = this.hapServ.OccupancySensor
|
|
215
|
-
char = this.hapChar.OccupancyDetected
|
|
216
|
-
break
|
|
217
|
-
case 'p_button':
|
|
218
|
-
serv = this.hapServ.StatelessProgrammableSwitch
|
|
219
|
-
char = this.hapChar.ProgrammableSwitchEvent
|
|
220
|
-
break
|
|
221
|
-
case 'doorbell':
|
|
222
|
-
serv = this.hapServ.Doorbell
|
|
223
|
-
char = this.hapChar.ProgrammableSwitchEvent
|
|
224
|
-
break
|
|
225
|
-
default: {
|
|
226
|
-
serv = this.hapServ.MotionSensor
|
|
227
|
-
char = this.hapChar.MotionDetected
|
|
228
|
-
const initialTime = subAccessory.eveService.getInitialTime()
|
|
229
|
-
subAccessory
|
|
230
|
-
.getService(serv)
|
|
231
|
-
.updateCharacteristic(
|
|
232
|
-
this.eveChar.LastActivation,
|
|
233
|
-
Math.round(new Date().valueOf() / 1000) - initialTime,
|
|
234
|
-
)
|
|
235
|
-
break
|
|
236
|
-
}
|
|
237
238
|
}
|
|
239
|
+
}
|
|
238
240
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
} catch (e) {
|
|
254
|
-
if (enableLogging) {
|
|
255
|
-
this.log(
|
|
256
|
-
'[%s] %s %s.',
|
|
257
|
-
subAccessory.displayName,
|
|
258
|
-
'http webhook request failed as',
|
|
259
|
-
e.message,
|
|
260
|
-
)
|
|
261
|
-
}
|
|
241
|
+
// Call any webhook defined in the config
|
|
242
|
+
if (deviceConf.sensorWebHook) {
|
|
243
|
+
try {
|
|
244
|
+
await axios.get(deviceConf.sensorWebHook, {
|
|
245
|
+
timeout: 5000,
|
|
246
|
+
})
|
|
247
|
+
if (enableLogging) {
|
|
248
|
+
this.log(
|
|
249
|
+
'[%s] %s [GET %s].',
|
|
250
|
+
subAccessory.displayName,
|
|
251
|
+
'http webhook request successful',
|
|
252
|
+
deviceConf.sensorWebHook,
|
|
253
|
+
)
|
|
262
254
|
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Programmable button and doorbell activate differently
|
|
266
|
-
if (char === this.hapChar.ProgrammableSwitchEvent) {
|
|
267
|
-
subAccessory.getService(serv).updateCharacteristic(char, 0)
|
|
268
|
-
if (params.updateSource && enableLogging) {
|
|
255
|
+
} catch (e) {
|
|
256
|
+
if (enableLogging) {
|
|
269
257
|
this.log(
|
|
270
|
-
'[%s] %s
|
|
258
|
+
'[%s] %s %s.',
|
|
271
259
|
subAccessory.displayName,
|
|
272
|
-
|
|
273
|
-
|
|
260
|
+
'http webhook request failed as',
|
|
261
|
+
e.message,
|
|
274
262
|
)
|
|
275
263
|
}
|
|
276
|
-
return
|
|
277
|
-
}
|
|
278
|
-
subAccessory.getService(serv).updateCharacteristic(char, 1)
|
|
279
|
-
subAccessory.eveService.addEntry({ status: 1 })
|
|
280
|
-
if (params.updateSource && enableLogging) {
|
|
281
|
-
this.log(
|
|
282
|
-
'[%s] %s [%s].',
|
|
283
|
-
subAccessory.displayName,
|
|
284
|
-
this.lang.curState,
|
|
285
|
-
this.lang.rfTrigYes,
|
|
286
|
-
)
|
|
287
264
|
}
|
|
265
|
+
}
|
|
288
266
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
.substring(2, 10)
|
|
292
|
-
subAccessory.context.updateKey = updateKey
|
|
293
|
-
await sleep(sensorTimeLength * 1000)
|
|
294
|
-
if (updateKey !== subAccessory.context.updateKey) {
|
|
295
|
-
return
|
|
296
|
-
}
|
|
267
|
+
// Programmable button and doorbell activate differently
|
|
268
|
+
if (char === this.hapChar.ProgrammableSwitchEvent) {
|
|
297
269
|
subAccessory.getService(serv).updateCharacteristic(char, 0)
|
|
298
|
-
subAccessory.eveService.addEntry({ status: 0 })
|
|
299
270
|
if (params.updateSource && enableLogging) {
|
|
300
271
|
this.log(
|
|
301
272
|
'[%s] %s [%s].',
|
|
302
273
|
subAccessory.displayName,
|
|
303
274
|
this.lang.curState,
|
|
304
|
-
this.lang.
|
|
275
|
+
this.lang.buttonSingle,
|
|
305
276
|
)
|
|
306
277
|
}
|
|
278
|
+
continue
|
|
279
|
+
}
|
|
280
|
+
subAccessory.getService(serv).updateCharacteristic(char, 1)
|
|
281
|
+
subAccessory.eveService.addEntry({ status: 1 })
|
|
282
|
+
if (params.updateSource && enableLogging) {
|
|
283
|
+
this.log(
|
|
284
|
+
'[%s] %s [%s].',
|
|
285
|
+
subAccessory.displayName,
|
|
286
|
+
this.lang.curState,
|
|
287
|
+
this.lang.rfTrigYes,
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const updateKey = Math.random()
|
|
292
|
+
.toString(36)
|
|
293
|
+
.substring(2, 10)
|
|
294
|
+
subAccessory.context.updateKey = updateKey
|
|
295
|
+
await sleep(sensorTimeLength * 1000)
|
|
296
|
+
if (updateKey !== subAccessory.context.updateKey) {
|
|
297
|
+
continue
|
|
298
|
+
}
|
|
299
|
+
subAccessory.getService(serv).updateCharacteristic(char, 0)
|
|
300
|
+
subAccessory.eveService.addEntry({ status: 0 })
|
|
301
|
+
if (params.updateSource && enableLogging) {
|
|
302
|
+
this.log(
|
|
303
|
+
'[%s] %s [%s].',
|
|
304
|
+
subAccessory.displayName,
|
|
305
|
+
this.lang.curState,
|
|
306
|
+
this.lang.rfTrigNo,
|
|
307
|
+
)
|
|
307
308
|
}
|
|
308
|
-
} else {
|
|
309
|
-
throw new Error(`${this.lang.rfNotFound}[${chan}]`)
|
|
310
309
|
}
|
|
311
|
-
}
|
|
312
|
-
|
|
310
|
+
} else {
|
|
311
|
+
throw new Error(`${this.lang.rfNotFound}[${chan}]`)
|
|
313
312
|
}
|
|
314
|
-
})
|
|
313
|
+
} catch (err) {
|
|
314
|
+
this.log.warn('[%s] %s %s.', this.name, this.lang.devNotRf, parseError(err))
|
|
315
|
+
}
|
|
316
|
+
}
|
|
315
317
|
}
|
|
316
318
|
} catch (err) {
|
|
317
319
|
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
@@ -42,10 +42,6 @@ export default class {
|
|
|
42
42
|
break
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// Temp override for debugging
|
|
46
|
-
this.enableLogging = true
|
|
47
|
-
this.enableDebugLogging = true
|
|
48
|
-
|
|
49
45
|
// Add the battery service if it doesn't already exist
|
|
50
46
|
this.battService = this.accessory.getService(this.hapServ.Battery)
|
|
51
47
|
|| this.accessory.addService(this.hapServ.Battery)
|
|
@@ -98,11 +98,19 @@ export default class {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
if (hasProperty(params, 'temperature')) {
|
|
101
|
-
|
|
101
|
+
// Two conventions arrive here. Most of these sensors send hundredths of
|
|
102
|
+
// a degree as a whole number - 2620 for 26.20C. The SNZB-02LD and 02WD
|
|
103
|
+
// send the degrees themselves, with a decimal point ("18.4"), and
|
|
104
|
+
// reading that as hundredths showed 18.4C in HomeKit as 0.18C (#701).
|
|
105
|
+
// The decimal point is the only thing that tells them apart.
|
|
106
|
+
const sendsDegrees = String(params.temperature).includes('.')
|
|
107
|
+
const rawTemp = sendsDegrees
|
|
108
|
+
? Number.parseFloat(params.temperature)
|
|
109
|
+
: Number.parseInt(params.temperature, 10)
|
|
102
110
|
if (Number.isNaN(rawTemp) || rawTemp < -27000 || rawTemp > 27000) {
|
|
103
111
|
return
|
|
104
112
|
}
|
|
105
|
-
let newTemp = rawTemp / 100
|
|
113
|
+
let newTemp = sendsDegrees ? rawTemp : rawTemp / 100
|
|
106
114
|
if (this.tempOffsetFactor) {
|
|
107
115
|
newTemp *= this.tempOffset
|
|
108
116
|
} else {
|
|
@@ -121,11 +129,15 @@ export default class {
|
|
|
121
129
|
}
|
|
122
130
|
}
|
|
123
131
|
if (hasProperty(params, 'humidity')) {
|
|
124
|
-
|
|
132
|
+
// Same two conventions as the temperature above, handled the same way
|
|
133
|
+
const sendsPercent = String(params.humidity).includes('.')
|
|
134
|
+
const rawHumi = sendsPercent
|
|
135
|
+
? Number.parseFloat(params.humidity)
|
|
136
|
+
: Number.parseInt(params.humidity, 10)
|
|
125
137
|
if (rawHumi === 0) {
|
|
126
138
|
return
|
|
127
139
|
}
|
|
128
|
-
let newHumi = rawHumi / 100
|
|
140
|
+
let newHumi = sendsPercent ? rawHumi : rawHumi / 100
|
|
129
141
|
if (this.humiOffsetFactor) {
|
|
130
142
|
newHumi *= this.humiOffset
|
|
131
143
|
} else {
|
|
@@ -12,7 +12,27 @@ export default class {
|
|
|
12
12
|
this.accessory = accessory
|
|
13
13
|
|
|
14
14
|
// Set up custom variables for this device type
|
|
15
|
-
|
|
15
|
+
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
16
|
+
|
|
17
|
+
// Set the correct logging variables for this accessory
|
|
18
|
+
switch (deviceConf.overrideLogging) {
|
|
19
|
+
case 'standard':
|
|
20
|
+
this.enableLogging = true
|
|
21
|
+
this.enableDebugLogging = false
|
|
22
|
+
break
|
|
23
|
+
case 'debug':
|
|
24
|
+
this.enableLogging = true
|
|
25
|
+
this.enableDebugLogging = true
|
|
26
|
+
break
|
|
27
|
+
case 'disable':
|
|
28
|
+
this.enableLogging = false
|
|
29
|
+
this.enableDebugLogging = false
|
|
30
|
+
break
|
|
31
|
+
default:
|
|
32
|
+
this.enableLogging = !platform.config.disableDeviceLogging
|
|
33
|
+
this.enableDebugLogging = false
|
|
34
|
+
break
|
|
35
|
+
}
|
|
16
36
|
|
|
17
37
|
// The newer SWV-ZNE irrigation valve (uiid 7047) reports and accepts its
|
|
18
38
|
// state under 'switch_00' rather than 'switch' (#778)
|
package/lib/platform.js
CHANGED
|
@@ -113,6 +113,9 @@ export default class {
|
|
|
113
113
|
const logIncrease = (k, min) => {
|
|
114
114
|
this.log.warn('%s [%s] %s %s.', this.lang.cfgItem, k, this.lang.cfgLow, min)
|
|
115
115
|
}
|
|
116
|
+
const logDecrease = (k, max) => {
|
|
117
|
+
this.log.warn('%s [%s] %s %s.', this.lang.cfgItem, k, this.lang.cfgHigh, max)
|
|
118
|
+
}
|
|
116
119
|
const logQuotes = (k) => {
|
|
117
120
|
this.log.warn('%s [%s] %s.', this.lang.cfgItem, k, this.lang.cfgQts)
|
|
118
121
|
}
|
|
@@ -200,6 +203,9 @@ export default class {
|
|
|
200
203
|
} else if (intVal < platformConsts.minValues[k]) {
|
|
201
204
|
logIncrease(`${key}.${id}.${k}`, platformConsts.minValues[k])
|
|
202
205
|
this.rfSubdevices[id][k] = platformConsts.minValues[k]
|
|
206
|
+
} else if (platformConsts.maxValues[k] !== undefined && intVal > platformConsts.maxValues[k]) {
|
|
207
|
+
logDecrease(`${key}.${id}.${k}`, platformConsts.maxValues[k])
|
|
208
|
+
this.rfSubdevices[id][k] = platformConsts.maxValues[k]
|
|
203
209
|
} else {
|
|
204
210
|
this.rfSubdevices[id][k] = intVal
|
|
205
211
|
}
|
|
@@ -282,6 +288,9 @@ export default class {
|
|
|
282
288
|
} else if (intVal < platformConsts.minValues[k]) {
|
|
283
289
|
logIncrease(`${key}.${id}.${k}`, platformConsts.minValues[k])
|
|
284
290
|
this.deviceConf[id][k] = platformConsts.minValues[k]
|
|
291
|
+
} else if (platformConsts.maxValues[k] !== undefined && intVal > platformConsts.maxValues[k]) {
|
|
292
|
+
logDecrease(`${key}.${id}.${k}`, platformConsts.maxValues[k])
|
|
293
|
+
this.deviceConf[id][k] = platformConsts.maxValues[k]
|
|
285
294
|
} else {
|
|
286
295
|
this.deviceConf[id][k] = intVal
|
|
287
296
|
}
|
package/lib/utils/constants.js
CHANGED
|
@@ -61,6 +61,18 @@ export default {
|
|
|
61
61
|
targetTempThreshold: 0,
|
|
62
62
|
},
|
|
63
63
|
|
|
64
|
+
// A Node timer holds its delay in a signed 32-bit integer. Over 2147483647 ms
|
|
65
|
+
// it does not throw - it quietly becomes 1 ms, so a device set past that point
|
|
66
|
+
// would run its reset timer immediately and repeatedly. operationTime is
|
|
67
|
+
// multiplied by 100 and sensorTimeLength by 1000, so both are capped well
|
|
68
|
+
// inside the limit.
|
|
69
|
+
maxValues: {
|
|
70
|
+
operationTime: 2073600,
|
|
71
|
+
operationTimeDown: 2073600,
|
|
72
|
+
sensorTimeDifference: 2073600,
|
|
73
|
+
sensorTimeLength: 2073600,
|
|
74
|
+
},
|
|
75
|
+
|
|
64
76
|
appId: 'Uw83EKZFxdif7XFXEsrpduz5YyjP7nTl',
|
|
65
77
|
|
|
66
78
|
appSecret: 'mXLOjea0woSMvK9gw7Fjsy7YlFO4iSu6',
|
package/lib/utils/lang-en.js
CHANGED
|
@@ -14,6 +14,7 @@ export default {
|
|
|
14
14
|
cfgIgn: 'is not configured correctly so ignoring',
|
|
15
15
|
cfgIgnItem: 'has an invalid entry which will be ignored',
|
|
16
16
|
cfgItem: 'Config entry',
|
|
17
|
+
cfgHigh: 'is set too high so reducing to',
|
|
17
18
|
cfgLow: 'is set too low so increasing to',
|
|
18
19
|
cfgRmv: 'is unused and can be removed',
|
|
19
20
|
cfgQts: 'should not have quotes around its entry',
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"displayName": "Homebridge eWeLink",
|
|
4
4
|
"alias": "eWeLink",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"version": "12.18.2
|
|
6
|
+
"version": "12.18.2",
|
|
7
7
|
"description": "Homebridge plugin to integrate eWeLink devices into HomeKit.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "bwp91",
|
|
@@ -67,14 +67,14 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@homebridge/plugin-ui-utils": "^2.2.5",
|
|
70
|
-
"axios": "^1.
|
|
70
|
+
"axios": "^1.19.0",
|
|
71
71
|
"node-persist": "^4.0.4",
|
|
72
72
|
"p-queue": "^9.3.3",
|
|
73
73
|
"websocket-as-promised": "^3.0.1",
|
|
74
|
-
"ws": "^8.21.
|
|
74
|
+
"ws": "^8.21.3"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@antfu/eslint-config": "^9.
|
|
77
|
+
"@antfu/eslint-config": "^9.3.0",
|
|
78
78
|
"@vitest/coverage-v8": "^4.1.10",
|
|
79
79
|
"vitest": "^4.1.10"
|
|
80
80
|
}
|