@homebridge-plugins/homebridge-ewelink 12.3.3
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 +1363 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/config.schema.json +1757 -0
- package/eslint.config.js +15 -0
- package/lib/connection/api.js +508 -0
- package/lib/connection/http.js +304 -0
- package/lib/connection/lan.js +318 -0
- package/lib/connection/ws.js +414 -0
- package/lib/device/curtain.js +324 -0
- package/lib/device/diffuser.js +473 -0
- package/lib/device/fan.js +349 -0
- package/lib/device/group.js +106 -0
- package/lib/device/humidifier.js +199 -0
- package/lib/device/index.js +61 -0
- package/lib/device/light-cct.js +333 -0
- package/lib/device/light-dimmer.js +257 -0
- package/lib/device/light-rgb-cct.js +809 -0
- package/lib/device/light-rgb.js +272 -0
- package/lib/device/motor.js +363 -0
- package/lib/device/outlet-multi.js +417 -0
- package/lib/device/outlet-single-inched.js +409 -0
- package/lib/device/outlet-single.js +384 -0
- package/lib/device/panel.js +382 -0
- package/lib/device/rf-bridge.js +321 -0
- package/lib/device/rf-button.js +125 -0
- package/lib/device/rf-sensor.js +173 -0
- package/lib/device/sensor-ambient.js +311 -0
- package/lib/device/sensor-contact.js +140 -0
- package/lib/device/sensor-temp-humi.js +182 -0
- package/lib/device/simulation/blind.js +253 -0
- package/lib/device/simulation/cooler.js +312 -0
- package/lib/device/simulation/door.js +253 -0
- package/lib/device/simulation/doorbell.js +190 -0
- package/lib/device/simulation/garage-eachen.js +235 -0
- package/lib/device/simulation/garage-four.js +176 -0
- package/lib/device/simulation/garage-od-switch.js +36 -0
- package/lib/device/simulation/garage-one.js +395 -0
- package/lib/device/simulation/garage-two.js +394 -0
- package/lib/device/simulation/gate-one.js +327 -0
- package/lib/device/simulation/heater.js +389 -0
- package/lib/device/simulation/index.js +75 -0
- package/lib/device/simulation/light-fan.js +258 -0
- package/lib/device/simulation/lock-eachen.js +163 -0
- package/lib/device/simulation/lock-one.js +366 -0
- package/lib/device/simulation/p-button.js +223 -0
- package/lib/device/simulation/purifier.js +289 -0
- package/lib/device/simulation/rf-blind.js +192 -0
- package/lib/device/simulation/rf-door.js +180 -0
- package/lib/device/simulation/rf-window.js +180 -0
- package/lib/device/simulation/sensor-hidden.js +105 -0
- package/lib/device/simulation/sensor-leak.js +123 -0
- package/lib/device/simulation/sensor-visible.js +231 -0
- package/lib/device/simulation/sensor.js +300 -0
- package/lib/device/simulation/switch-valve.js +351 -0
- package/lib/device/simulation/tap-one.js +283 -0
- package/lib/device/simulation/tap-two.js +266 -0
- package/lib/device/simulation/th-cooler.js +443 -0
- package/lib/device/simulation/th-dehumidifier.js +424 -0
- package/lib/device/simulation/th-heater.js +449 -0
- package/lib/device/simulation/th-humidifier.js +424 -0
- package/lib/device/simulation/th-thermostat.js +611 -0
- package/lib/device/simulation/tv.js +252 -0
- package/lib/device/simulation/valve-four.js +232 -0
- package/lib/device/simulation/valve-one.js +345 -0
- package/lib/device/simulation/valve-two.js +340 -0
- package/lib/device/simulation/window.js +253 -0
- package/lib/device/switch-man.js +106 -0
- package/lib/device/switch-mate.js +82 -0
- package/lib/device/switch-multi.js +411 -0
- package/lib/device/switch-single-inched.js +332 -0
- package/lib/device/switch-single.js +307 -0
- package/lib/device/template.js +43 -0
- package/lib/device/thermostat.js +286 -0
- package/lib/device/zigbee/index.js +27 -0
- package/lib/device/zigbee/light-cct.js +263 -0
- package/lib/device/zigbee/light-dimmer.js +148 -0
- package/lib/device/zigbee/light-rgb-cct.js +400 -0
- package/lib/device/zigbee/motor.js +301 -0
- package/lib/device/zigbee/sensor-ambient.js +177 -0
- package/lib/device/zigbee/sensor-contact.js +137 -0
- package/lib/device/zigbee/sensor-leak.js +119 -0
- package/lib/device/zigbee/sensor-motion.js +119 -0
- package/lib/device/zigbee/sensor-occupancy.js +119 -0
- package/lib/device/zigbee/sensor-smoke.js +96 -0
- package/lib/device/zigbee/switch-stateless.js +104 -0
- package/lib/device/zigbee/thermostat.js +323 -0
- package/lib/fakegato/LICENSE +21 -0
- package/lib/fakegato/fakegato-history.js +813 -0
- package/lib/fakegato/fakegato-storage.js +108 -0
- package/lib/fakegato/fakegato-timer.js +125 -0
- package/lib/fakegato/uuid.js +27 -0
- package/lib/homebridge-ui/public/index.html +365 -0
- package/lib/homebridge-ui/server.js +46 -0
- package/lib/index.js +7 -0
- package/lib/node-dns-sd/LICENSE +21 -0
- package/lib/node-dns-sd/README.md +613 -0
- package/lib/node-dns-sd/lib/dns-sd-classes.json +6 -0
- package/lib/node-dns-sd/lib/dns-sd-composer.js +83 -0
- package/lib/node-dns-sd/lib/dns-sd-parser.js +344 -0
- package/lib/node-dns-sd/lib/dns-sd-types.json +52 -0
- package/lib/node-dns-sd/lib/dns-sd.js +580 -0
- package/lib/platform.js +2156 -0
- package/lib/utils/colour.js +552 -0
- package/lib/utils/constants.js +589 -0
- package/lib/utils/custom-chars.js +33 -0
- package/lib/utils/eve-chars.js +130 -0
- package/lib/utils/functions.js +42 -0
- package/lib/utils/lang-en.js +160 -0
- package/lib/utils/lang-fr.js +163 -0
- package/lib/utils/lang-th.js +160 -0
- package/lib/utils/lang.js +5 -0
- package/package.json +75 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import platformConsts from '../utils/constants.js'
|
|
2
|
+
import { hasProperty } from '../utils/functions.js'
|
|
3
|
+
|
|
4
|
+
export default class {
|
|
5
|
+
constructor(platform, accessory) {
|
|
6
|
+
// Set up variables from the platform
|
|
7
|
+
this.cusChar = platform.cusChar
|
|
8
|
+
this.eveChar = platform.eveChar
|
|
9
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
10
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
11
|
+
this.hapServ = platform.api.hap.Service
|
|
12
|
+
this.lang = platform.lang
|
|
13
|
+
this.log = platform.log
|
|
14
|
+
this.platform = platform
|
|
15
|
+
|
|
16
|
+
// Set up variables from the accessory
|
|
17
|
+
this.name = accessory.displayName
|
|
18
|
+
this.accessory = accessory
|
|
19
|
+
|
|
20
|
+
// Initially set the online flag as true (to be then updated as false if necessary)
|
|
21
|
+
this.isOnline = true
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
UIID 1/6/14/24/27/112/1009/1256/7004: single switch, no power readings
|
|
25
|
+
UIID 5: single switch, with wattage readings
|
|
26
|
+
UIID 32: single switch, with wattage, voltage and amp readings
|
|
27
|
+
UIID 77/78/81/107/112/138/160: single switch but firmware uses multiple channels (only need CH0)
|
|
28
|
+
UIID 182/190: single switch but multiple channels with wattage, voltage and amp readings
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// Set up custom variables for this device type
|
|
32
|
+
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
33
|
+
this.inUsePowerThreshold = deviceConf.inUsePowerThreshold || platformConsts.defaultValues.inUsePowerThreshold
|
|
34
|
+
|
|
35
|
+
// Set the correct logging variables for this accessory
|
|
36
|
+
switch (deviceConf.overrideLogging) {
|
|
37
|
+
case 'standard':
|
|
38
|
+
this.enableLogging = true
|
|
39
|
+
this.enableDebugLogging = false
|
|
40
|
+
break
|
|
41
|
+
case 'debug':
|
|
42
|
+
this.enableLogging = true
|
|
43
|
+
this.enableDebugLogging = true
|
|
44
|
+
break
|
|
45
|
+
case 'disable':
|
|
46
|
+
this.enableLogging = false
|
|
47
|
+
this.enableDebugLogging = false
|
|
48
|
+
break
|
|
49
|
+
default:
|
|
50
|
+
this.enableLogging = !platform.config.disableDeviceLogging
|
|
51
|
+
this.enableDebugLogging = platform.config.debug
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If the accessory has a outlet service then remove it
|
|
56
|
+
if (this.accessory.getService(this.hapServ.Outlet)) {
|
|
57
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Outlet))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Add the switch service if it doesn't already exist
|
|
61
|
+
this.service = this.accessory.getService(this.hapServ.Switch)
|
|
62
|
+
|| this.accessory.addService(this.hapServ.Switch)
|
|
63
|
+
|
|
64
|
+
switch (this.accessory.context.eweUIID) {
|
|
65
|
+
case 5:
|
|
66
|
+
// Add Eve power characteristics
|
|
67
|
+
this.powerReadings = true
|
|
68
|
+
if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
|
|
69
|
+
this.service.addCharacteristic(this.hapChar.OutletInUse)
|
|
70
|
+
}
|
|
71
|
+
if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
72
|
+
this.service.addCharacteristic(this.eveChar.CurrentConsumption)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Remove unused Eve characteristics
|
|
76
|
+
if (this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
77
|
+
this.service.removeCharacteristic(
|
|
78
|
+
this.service.getCharacteristic(this.eveChar.ElectricCurrent),
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
if (this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
82
|
+
this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.Voltage))
|
|
83
|
+
}
|
|
84
|
+
break
|
|
85
|
+
case 32:
|
|
86
|
+
case 182:
|
|
87
|
+
case 190:
|
|
88
|
+
// Add Eve power characteristics
|
|
89
|
+
this.powerReadings = true
|
|
90
|
+
if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
|
|
91
|
+
this.service.addCharacteristic(this.hapChar.OutletInUse)
|
|
92
|
+
}
|
|
93
|
+
if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
94
|
+
this.service.addCharacteristic(this.eveChar.CurrentConsumption)
|
|
95
|
+
}
|
|
96
|
+
if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
97
|
+
this.service.addCharacteristic(this.eveChar.ElectricCurrent)
|
|
98
|
+
}
|
|
99
|
+
if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
100
|
+
this.service.addCharacteristic(this.eveChar.Voltage)
|
|
101
|
+
}
|
|
102
|
+
break
|
|
103
|
+
default:
|
|
104
|
+
// Remove unused Eve characteristics
|
|
105
|
+
if (this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
106
|
+
this.service.removeCharacteristic(
|
|
107
|
+
this.service.getCharacteristic(this.eveChar.CurrentConsumption),
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
if (this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
111
|
+
this.service.removeCharacteristic(
|
|
112
|
+
this.service.getCharacteristic(this.eveChar.ElectricCurrent),
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
if (this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
116
|
+
this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.Voltage))
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Set a flag for devices with hardware that use multi-channel format
|
|
122
|
+
if (
|
|
123
|
+
platformConsts.devices.switchSCM.includes(this.accessory.context.eweUIID)
|
|
124
|
+
|| platformConsts.devices.switchSCMPower.includes(this.accessory.context.eweUIID)
|
|
125
|
+
) {
|
|
126
|
+
this.isSCM = true
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Add the set handler to the switch on/off characteristic
|
|
130
|
+
this.service
|
|
131
|
+
.getCharacteristic(this.hapChar.On)
|
|
132
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
133
|
+
this.cacheState = this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
|
|
134
|
+
|
|
135
|
+
// Add a custom characteristic for inverting the switch
|
|
136
|
+
if (!this.service.testCharacteristic(this.cusChar.InvertSwitch)) {
|
|
137
|
+
this.service.addCharacteristic(this.cusChar.InvertSwitch)
|
|
138
|
+
}
|
|
139
|
+
this.service.updateCharacteristic(this.cusChar.InvertSwitch, this.cacheState === 'on')
|
|
140
|
+
|
|
141
|
+
// Add the set handler to invert the switch
|
|
142
|
+
this.service.getCharacteristic(this.cusChar.InvertSwitch).onSet((value) => {
|
|
143
|
+
this.service.updateCharacteristic(this.hapChar.On, value)
|
|
144
|
+
this.cacheState = value ? 'on' : 'off'
|
|
145
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
// Add the get handlers only if the user hasn't disabled the disableNoResponse setting
|
|
149
|
+
if (!platform.config.disableNoResponse) {
|
|
150
|
+
this.service.getCharacteristic(this.hapChar.On).onGet(() => {
|
|
151
|
+
if (!this.isOnline) {
|
|
152
|
+
throw new this.hapErr(-70402)
|
|
153
|
+
}
|
|
154
|
+
return this.service.getCharacteristic(this.hapChar.On).value
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Pass the accessory to Fakegato to set up with Eve
|
|
159
|
+
this.accessory.eveService = new platform.eveService('switch', this.accessory, {
|
|
160
|
+
log: () => {},
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// Set up extra features for outlets that provide power readings
|
|
164
|
+
if (this.powerReadings) {
|
|
165
|
+
// Set up an interval to get eWeLink to send power updates
|
|
166
|
+
setTimeout(() => {
|
|
167
|
+
this.internalUIUpdate()
|
|
168
|
+
this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
|
|
169
|
+
}, 5000)
|
|
170
|
+
|
|
171
|
+
// Stop the intervals on Homebridge shutdown
|
|
172
|
+
platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Output the customised options to the log
|
|
176
|
+
const normalLogging = this.enableLogging ? 'standard' : 'disable'
|
|
177
|
+
const opts = JSON.stringify({
|
|
178
|
+
inUsePowerThreshold: this.inUsePowerThreshold,
|
|
179
|
+
isInched: true,
|
|
180
|
+
logging: this.enableDebugLogging ? 'debug' : normalLogging,
|
|
181
|
+
showAs: 'default',
|
|
182
|
+
})
|
|
183
|
+
this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async internalStateUpdate(value) {
|
|
187
|
+
try {
|
|
188
|
+
const newValue = this.cacheState === 'on' ? 'off' : 'on'
|
|
189
|
+
const params = {}
|
|
190
|
+
if (this.isSCM) {
|
|
191
|
+
params.switches = [{ switch: 'on', outlet: 0 }]
|
|
192
|
+
} else {
|
|
193
|
+
params.switch = 'on'
|
|
194
|
+
}
|
|
195
|
+
this.ignoreUpdates = true
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
this.ignoreUpdates = false
|
|
198
|
+
}, 1500)
|
|
199
|
+
await this.platform.sendDeviceUpdate(this.accessory, params)
|
|
200
|
+
this.cacheState = newValue
|
|
201
|
+
this.service.updateCharacteristic(this.cusChar.InvertSwitch, value)
|
|
202
|
+
this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
|
|
203
|
+
if (this.enableLogging) {
|
|
204
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
208
|
+
setTimeout(() => {
|
|
209
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
210
|
+
}, 2000)
|
|
211
|
+
throw new this.hapErr(-70402)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async internalUIUpdate() {
|
|
216
|
+
try {
|
|
217
|
+
// Skip polling if device isn't online
|
|
218
|
+
if (!this.isOnline) {
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Send the params to request the updates
|
|
223
|
+
await this.platform.sendDeviceUpdate(this.accessory, { uiActive: 120 })
|
|
224
|
+
} catch (err) {
|
|
225
|
+
// Suppress errors here
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async externalUpdate(params) {
|
|
230
|
+
try {
|
|
231
|
+
if (!this.isSCM && params.switch === 'on') {
|
|
232
|
+
if (!this.ignoreUpdates) {
|
|
233
|
+
this.ignoreUpdates = true
|
|
234
|
+
setTimeout(() => {
|
|
235
|
+
this.ignoreUpdates = false
|
|
236
|
+
}, 1500)
|
|
237
|
+
this.cacheState = this.cacheState === 'on' ? 'off' : 'on'
|
|
238
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
239
|
+
this.service.updateCharacteristic(this.cusChar.InvertSwitch, this.cacheState === 'on')
|
|
240
|
+
this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
|
|
241
|
+
if (params.updateSource && this.enableLogging) {
|
|
242
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (this.isSCM && params.switches && params.switches[0].switch === 'on') {
|
|
247
|
+
if (!this.ignoreUpdates) {
|
|
248
|
+
this.ignoreUpdates = true
|
|
249
|
+
setTimeout(() => {
|
|
250
|
+
this.ignoreUpdates = false
|
|
251
|
+
}, 1500)
|
|
252
|
+
this.cacheState = this.cacheState === 'on' ? 'off' : 'on'
|
|
253
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
254
|
+
this.service.updateCharacteristic(this.cusChar.InvertSwitch, this.cacheState === 'on')
|
|
255
|
+
this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
|
|
256
|
+
if (params.updateSource && this.enableLogging) {
|
|
257
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (!this.powerReadings) {
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
let logger = false
|
|
265
|
+
if (hasProperty(params, 'power')) {
|
|
266
|
+
this.service.updateCharacteristic(this.eveChar.CurrentConsumption, Number.parseFloat(params.power))
|
|
267
|
+
this.service.updateCharacteristic(
|
|
268
|
+
this.hapChar.OutletInUse,
|
|
269
|
+
this.cacheState === 'on' && Number.parseFloat(params.power) > this.inUsePowerThreshold,
|
|
270
|
+
)
|
|
271
|
+
logger = true
|
|
272
|
+
}
|
|
273
|
+
if (hasProperty(params, 'voltage')) {
|
|
274
|
+
this.service.updateCharacteristic(this.eveChar.Voltage, Number.parseFloat(params.voltage))
|
|
275
|
+
logger = true
|
|
276
|
+
}
|
|
277
|
+
if (hasProperty(params, 'current')) {
|
|
278
|
+
this.service.updateCharacteristic(this.eveChar.ElectricCurrent, Number.parseFloat(params.current))
|
|
279
|
+
logger = true
|
|
280
|
+
}
|
|
281
|
+
if (params.updateSource && logger && this.enableLogging) {
|
|
282
|
+
this.log(
|
|
283
|
+
'[%s] %s%s%s.',
|
|
284
|
+
this.name,
|
|
285
|
+
hasProperty(params, 'power')
|
|
286
|
+
? `${this.lang.curPower} [${params.power}W]`
|
|
287
|
+
: '',
|
|
288
|
+
hasProperty(params, 'voltage')
|
|
289
|
+
? ` ${this.lang.curVolt} [${params.voltage}V]`
|
|
290
|
+
: '',
|
|
291
|
+
hasProperty(params, 'current')
|
|
292
|
+
? ` ${this.lang.curCurr} [${params.current}A]`
|
|
293
|
+
: '',
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
} catch (err) {
|
|
297
|
+
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
markStatus(isOnline) {
|
|
302
|
+
this.isOnline = isOnline
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async currentState() {
|
|
306
|
+
const toReturn = {}
|
|
307
|
+
toReturn.services = ['switch']
|
|
308
|
+
toReturn.switch = {
|
|
309
|
+
state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
|
|
310
|
+
}
|
|
311
|
+
if (this.powerReadings) {
|
|
312
|
+
try {
|
|
313
|
+
toReturn.services.push('power')
|
|
314
|
+
toReturn.power = {
|
|
315
|
+
state: await this.platform.sendDeviceUpdate(this.accessory, { hundredDaysKwh: 'get' }),
|
|
316
|
+
voltage: this.service.testCharacteristic(this.eveChar.Voltage)
|
|
317
|
+
? this.service.getCharacteristic(this.eveChar.Voltage).value
|
|
318
|
+
: undefined,
|
|
319
|
+
power: this.service.testCharacteristic(this.eveChar.CurrentConsumption)
|
|
320
|
+
? this.service.getCharacteristic(this.eveChar.CurrentConsumption).value
|
|
321
|
+
: undefined,
|
|
322
|
+
current: this.service.testCharacteristic(this.eveChar.ElectricCurrent)
|
|
323
|
+
? this.service.getCharacteristic(this.eveChar.ElectricCurrent).value
|
|
324
|
+
: undefined,
|
|
325
|
+
}
|
|
326
|
+
} catch (err) {
|
|
327
|
+
// Suppress errors here
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return toReturn
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import platformConsts from '../utils/constants.js'
|
|
2
|
+
import { hasProperty } from '../utils/functions.js'
|
|
3
|
+
|
|
4
|
+
export default class {
|
|
5
|
+
constructor(platform, accessory) {
|
|
6
|
+
// Set up variables from the platform
|
|
7
|
+
this.eveChar = platform.eveChar
|
|
8
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
9
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
10
|
+
this.hapServ = platform.api.hap.Service
|
|
11
|
+
this.lang = platform.lang
|
|
12
|
+
this.log = platform.log
|
|
13
|
+
this.platform = platform
|
|
14
|
+
|
|
15
|
+
// Set up variables from the accessory
|
|
16
|
+
this.name = accessory.displayName
|
|
17
|
+
this.accessory = accessory
|
|
18
|
+
|
|
19
|
+
// Initially set the online flag as true (to be then updated as false if necessary)
|
|
20
|
+
this.isOnline = true
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
UIID 1/6/14/24/27/112/1009/1256/7004: single switch, no power readings
|
|
24
|
+
UIID 5: single switch, with wattage readings
|
|
25
|
+
UIID 32: single switch, with wattage, voltage and amp readings
|
|
26
|
+
UIID 77/78/81/107/112/138/160: single switch but firmware uses multiple channels (only need CH0)
|
|
27
|
+
UIID 182/190: single switch but multiple channels with wattage, voltage and amp readings
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// Set up custom variables for this device type
|
|
31
|
+
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
32
|
+
this.inUsePowerThreshold = deviceConf.inUsePowerThreshold || platformConsts.defaultValues.inUsePowerThreshold
|
|
33
|
+
|
|
34
|
+
// Set the correct logging variables for this accessory
|
|
35
|
+
switch (deviceConf.overrideLogging) {
|
|
36
|
+
case 'standard':
|
|
37
|
+
this.enableLogging = true
|
|
38
|
+
this.enableDebugLogging = false
|
|
39
|
+
break
|
|
40
|
+
case 'debug':
|
|
41
|
+
this.enableLogging = true
|
|
42
|
+
this.enableDebugLogging = true
|
|
43
|
+
break
|
|
44
|
+
case 'disable':
|
|
45
|
+
this.enableLogging = false
|
|
46
|
+
this.enableDebugLogging = false
|
|
47
|
+
break
|
|
48
|
+
default:
|
|
49
|
+
this.enableLogging = !platform.config.disableDeviceLogging
|
|
50
|
+
this.enableDebugLogging = platform.config.debug
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// If the accessory has a outlet service then remove it
|
|
55
|
+
if (this.accessory.getService(this.hapServ.Outlet)) {
|
|
56
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Outlet))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add the switch service if it doesn't already exist
|
|
60
|
+
this.service = this.accessory.getService(this.hapServ.Switch)
|
|
61
|
+
|| this.accessory.addService(this.hapServ.Switch)
|
|
62
|
+
|
|
63
|
+
// Some models provide a power reading in different decimals
|
|
64
|
+
this.divisor = 1
|
|
65
|
+
|
|
66
|
+
switch (this.accessory.context.eweUIID) {
|
|
67
|
+
case 5:
|
|
68
|
+
// Add Eve power characteristics
|
|
69
|
+
this.powerReadings = true
|
|
70
|
+
if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
|
|
71
|
+
this.service.addCharacteristic(this.hapChar.OutletInUse)
|
|
72
|
+
}
|
|
73
|
+
if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
74
|
+
this.service.addCharacteristic(this.eveChar.CurrentConsumption)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Remove unused Eve characteristics
|
|
78
|
+
if (this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
79
|
+
this.service.removeCharacteristic(
|
|
80
|
+
this.service.getCharacteristic(this.eveChar.ElectricCurrent),
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
if (this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
84
|
+
this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.Voltage))
|
|
85
|
+
}
|
|
86
|
+
break
|
|
87
|
+
case 32:
|
|
88
|
+
case 182:
|
|
89
|
+
case 190:
|
|
90
|
+
// Add Eve power characteristics
|
|
91
|
+
this.powerReadings = true
|
|
92
|
+
if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
|
|
93
|
+
this.service.addCharacteristic(this.hapChar.OutletInUse)
|
|
94
|
+
}
|
|
95
|
+
if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
96
|
+
this.service.addCharacteristic(this.eveChar.CurrentConsumption)
|
|
97
|
+
}
|
|
98
|
+
if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
99
|
+
this.service.addCharacteristic(this.eveChar.ElectricCurrent)
|
|
100
|
+
}
|
|
101
|
+
if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
102
|
+
this.service.addCharacteristic(this.eveChar.Voltage)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (this.accessory.context.eweUIID === 190) {
|
|
106
|
+
this.divisor = 100
|
|
107
|
+
}
|
|
108
|
+
break
|
|
109
|
+
default:
|
|
110
|
+
// Remove unused Eve characteristics
|
|
111
|
+
if (this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
112
|
+
this.service.removeCharacteristic(
|
|
113
|
+
this.service.getCharacteristic(this.eveChar.CurrentConsumption),
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
if (this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
117
|
+
this.service.removeCharacteristic(
|
|
118
|
+
this.service.getCharacteristic(this.eveChar.ElectricCurrent),
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
if (this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
122
|
+
this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.Voltage))
|
|
123
|
+
}
|
|
124
|
+
break
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Set a flag for devices with hardware that use multi-channel format
|
|
128
|
+
if (
|
|
129
|
+
platformConsts.devices.switchSCM.includes(this.accessory.context.eweUIID)
|
|
130
|
+
|| platformConsts.devices.switchSCMPower.includes(this.accessory.context.eweUIID)
|
|
131
|
+
) {
|
|
132
|
+
this.isSCM = true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add the set handler to the switch on/off characteristic
|
|
136
|
+
this.service
|
|
137
|
+
.getCharacteristic(this.hapChar.On)
|
|
138
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
139
|
+
|
|
140
|
+
// Add the get handlers only if the user hasn't disabled the disableNoResponse setting
|
|
141
|
+
if (!platform.config.disableNoResponse) {
|
|
142
|
+
this.service.getCharacteristic(this.hapChar.On).onGet(() => {
|
|
143
|
+
if (!this.isOnline) {
|
|
144
|
+
throw new this.hapErr(-70402)
|
|
145
|
+
}
|
|
146
|
+
return this.service.getCharacteristic(this.hapChar.On).value
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Pass the accessory to Fakegato to set up with Eve
|
|
151
|
+
this.accessory.eveService = new platform.eveService('switch', this.accessory, {
|
|
152
|
+
log: () => {},
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Set up extra features for outlets that provide power readings
|
|
156
|
+
if (this.powerReadings) {
|
|
157
|
+
// Set up an interval to get eWeLink to send power updates
|
|
158
|
+
setTimeout(() => {
|
|
159
|
+
this.internalUIUpdate()
|
|
160
|
+
this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
|
|
161
|
+
}, 5000)
|
|
162
|
+
|
|
163
|
+
// Stop the intervals on Homebridge shutdown
|
|
164
|
+
platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Output the customised options to the log
|
|
168
|
+
const normalLogging = this.enableLogging ? 'standard' : 'disable'
|
|
169
|
+
const opts = JSON.stringify({
|
|
170
|
+
inUsePowerThreshold: this.inUsePowerThreshold,
|
|
171
|
+
logging: this.enableDebugLogging ? 'debug' : normalLogging,
|
|
172
|
+
showAs: 'default',
|
|
173
|
+
})
|
|
174
|
+
this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async internalStateUpdate(value) {
|
|
178
|
+
try {
|
|
179
|
+
const newValue = value ? 'on' : 'off'
|
|
180
|
+
if (newValue === this.cacheState) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
const params = {}
|
|
184
|
+
if (this.isSCM) {
|
|
185
|
+
params.switches = [{ switch: newValue, outlet: 0 }]
|
|
186
|
+
} else {
|
|
187
|
+
params.switch = newValue
|
|
188
|
+
}
|
|
189
|
+
await this.platform.sendDeviceUpdate(this.accessory, params)
|
|
190
|
+
this.cacheState = newValue
|
|
191
|
+
this.accessory.eveService.addEntry({ status: value ? 1 : 0 })
|
|
192
|
+
if (this.enableLogging) {
|
|
193
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
199
|
+
}, 2000)
|
|
200
|
+
throw new this.hapErr(-70402)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async internalUIUpdate() {
|
|
205
|
+
try {
|
|
206
|
+
// Skip polling if device isn't online
|
|
207
|
+
if (!this.isOnline) {
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Send the params to request the updates
|
|
212
|
+
await this.platform.sendDeviceUpdate(this.accessory, { uiActive: 120 })
|
|
213
|
+
} catch (err) {
|
|
214
|
+
// Suppress errors here
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async externalUpdate(params) {
|
|
219
|
+
try {
|
|
220
|
+
if (!this.isSCM && params.switch && params.switch !== this.cacheState) {
|
|
221
|
+
this.cacheState = params.switch
|
|
222
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
223
|
+
this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
|
|
224
|
+
if (params.updateSource && this.enableLogging) {
|
|
225
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (this.isSCM && params.switches && params.switches[0].switch !== this.cacheState) {
|
|
229
|
+
this.cacheState = params.switches[0].switch
|
|
230
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
231
|
+
this.accessory.eveService.addEntry({ status: this.cacheState === 'on' ? 1 : 0 })
|
|
232
|
+
if (params.updateSource && this.enableLogging) {
|
|
233
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (!this.powerReadings) {
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
let logger = false
|
|
240
|
+
let power
|
|
241
|
+
let voltage
|
|
242
|
+
let current
|
|
243
|
+
if (hasProperty(params, 'power')) {
|
|
244
|
+
power = Math.round((Number.parseFloat(params.power) * 100) / this.divisor) / 100
|
|
245
|
+
this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
|
|
246
|
+
this.service.updateCharacteristic(
|
|
247
|
+
this.hapChar.OutletInUse,
|
|
248
|
+
this.cacheState === 'on' && Number.parseFloat(params.power) > this.inUsePowerThreshold,
|
|
249
|
+
)
|
|
250
|
+
logger = true
|
|
251
|
+
}
|
|
252
|
+
if (hasProperty(params, 'voltage')) {
|
|
253
|
+
voltage = Math.round((Number.parseFloat(params.voltage) * 100) / this.divisor) / 100
|
|
254
|
+
this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
|
|
255
|
+
logger = true
|
|
256
|
+
}
|
|
257
|
+
if (hasProperty(params, 'current')) {
|
|
258
|
+
current = Math.round((Number.parseFloat(params.current) * 100) / this.divisor) / 100
|
|
259
|
+
this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
|
|
260
|
+
logger = true
|
|
261
|
+
}
|
|
262
|
+
if (params.updateSource && logger && this.enableLogging) {
|
|
263
|
+
this.log(
|
|
264
|
+
'[%s] %s%s%s.',
|
|
265
|
+
this.name,
|
|
266
|
+
power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
|
|
267
|
+
voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
|
|
268
|
+
current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
} catch (err) {
|
|
272
|
+
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
markStatus(isOnline) {
|
|
277
|
+
this.isOnline = isOnline
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async currentState() {
|
|
281
|
+
const toReturn = {}
|
|
282
|
+
toReturn.services = ['switch']
|
|
283
|
+
toReturn.switch = {
|
|
284
|
+
state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
|
|
285
|
+
}
|
|
286
|
+
if (this.powerReadings) {
|
|
287
|
+
try {
|
|
288
|
+
toReturn.services.push('power')
|
|
289
|
+
toReturn.power = {
|
|
290
|
+
state: await this.platform.sendDeviceUpdate(this.accessory, { hundredDaysKwh: 'get' }),
|
|
291
|
+
voltage: this.service.testCharacteristic(this.eveChar.Voltage)
|
|
292
|
+
? this.service.getCharacteristic(this.eveChar.Voltage).value
|
|
293
|
+
: undefined,
|
|
294
|
+
power: this.service.testCharacteristic(this.eveChar.CurrentConsumption)
|
|
295
|
+
? this.service.getCharacteristic(this.eveChar.CurrentConsumption).value
|
|
296
|
+
: undefined,
|
|
297
|
+
current: this.service.testCharacteristic(this.eveChar.ElectricCurrent)
|
|
298
|
+
? this.service.getCharacteristic(this.eveChar.ElectricCurrent).value
|
|
299
|
+
: undefined,
|
|
300
|
+
}
|
|
301
|
+
} catch (err) {
|
|
302
|
+
// Suppress errors here
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return toReturn
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export default class {
|
|
2
|
+
constructor(platform, accessory) {
|
|
3
|
+
// Set up variables from the platform
|
|
4
|
+
this.eveChar = platform.eveChar
|
|
5
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
6
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
7
|
+
this.hapServ = platform.api.hap.Service
|
|
8
|
+
this.lang = platform.lang
|
|
9
|
+
this.log = platform.log
|
|
10
|
+
this.platform = platform
|
|
11
|
+
|
|
12
|
+
// Set up variables from the accessory
|
|
13
|
+
this.name = accessory.displayName
|
|
14
|
+
this.accessory = accessory
|
|
15
|
+
|
|
16
|
+
// Initially set the online flag as true (to be then updated as false if necessary)
|
|
17
|
+
this.isOnline = true
|
|
18
|
+
|
|
19
|
+
// Set the correct logging variables for this accessory
|
|
20
|
+
this.enableLogging = true
|
|
21
|
+
this.enableDebugLogging = true
|
|
22
|
+
|
|
23
|
+
// Output the customised options to the log
|
|
24
|
+
const normalLogging = this.enableLogging ? 'standard' : 'disable'
|
|
25
|
+
const opts = JSON.stringify({
|
|
26
|
+
logging: this.enableDebugLogging ? 'debug' : normalLogging,
|
|
27
|
+
})
|
|
28
|
+
this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async externalUpdate(params) {
|
|
32
|
+
try {
|
|
33
|
+
this.log.warn('New Command Received:')
|
|
34
|
+
this.log(JSON.stringify(params))
|
|
35
|
+
} catch (err) {
|
|
36
|
+
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
markStatus(isOnline) {
|
|
41
|
+
this.isOnline = isOnline
|
|
42
|
+
}
|
|
43
|
+
}
|