@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,272 @@
|
|
|
1
|
+
import { hs2rgb, rgb2hs } from '../utils/colour.js'
|
|
2
|
+
import { generateRandomString, hasProperty, sleep } from '../utils/functions.js'
|
|
3
|
+
|
|
4
|
+
export default class {
|
|
5
|
+
constructor(platform, accessory) {
|
|
6
|
+
// Set up variables from the platform
|
|
7
|
+
this.hapChar = platform.api.hap.Characteristic
|
|
8
|
+
this.hapErr = platform.api.hap.HapStatusError
|
|
9
|
+
this.hapServ = platform.api.hap.Service
|
|
10
|
+
this.lang = platform.lang
|
|
11
|
+
this.log = platform.log
|
|
12
|
+
this.platform = platform
|
|
13
|
+
|
|
14
|
+
// Set up variables from the accessory
|
|
15
|
+
this.name = accessory.displayName
|
|
16
|
+
this.accessory = accessory
|
|
17
|
+
|
|
18
|
+
// Initially set the online flag as true (to be then updated as false if necessary)
|
|
19
|
+
this.isOnline = true
|
|
20
|
+
|
|
21
|
+
// Set up custom variables for this device type
|
|
22
|
+
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
23
|
+
this.offlineAsOff = deviceConf.offlineAsOff
|
|
24
|
+
|
|
25
|
+
// Set the correct logging variables for this accessory
|
|
26
|
+
switch (deviceConf.overrideLogging) {
|
|
27
|
+
case 'standard':
|
|
28
|
+
this.enableLogging = true
|
|
29
|
+
this.enableDebugLogging = false
|
|
30
|
+
break
|
|
31
|
+
case 'debug':
|
|
32
|
+
this.enableLogging = true
|
|
33
|
+
this.enableDebugLogging = true
|
|
34
|
+
break
|
|
35
|
+
case 'disable':
|
|
36
|
+
this.enableLogging = false
|
|
37
|
+
this.enableDebugLogging = false
|
|
38
|
+
break
|
|
39
|
+
default:
|
|
40
|
+
this.enableLogging = !platform.config.disableDeviceLogging
|
|
41
|
+
this.enableDebugLogging = platform.config.debug
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Add the lightbulb service if it doesn't already exist
|
|
46
|
+
this.service = this.accessory.getService(this.hapServ.Lightbulb)
|
|
47
|
+
|| this.accessory.addService(this.hapServ.Lightbulb)
|
|
48
|
+
|
|
49
|
+
// Add the set handler to the lightbulb on/off characteristic
|
|
50
|
+
this.service
|
|
51
|
+
.getCharacteristic(this.hapChar.On)
|
|
52
|
+
.onSet(async value => this.internalStateUpdate(value))
|
|
53
|
+
|
|
54
|
+
// Add the set handler to the lightbulb brightness characteristic
|
|
55
|
+
this.service
|
|
56
|
+
.getCharacteristic(this.hapChar.Brightness)
|
|
57
|
+
.setProps({ minStep: 100 })
|
|
58
|
+
.onSet(async value => this.internalBrightnessUpdate(value))
|
|
59
|
+
|
|
60
|
+
// Add the set handler to the lightbulb hue characteristic
|
|
61
|
+
this.service
|
|
62
|
+
.getCharacteristic(this.hapChar.Hue)
|
|
63
|
+
.onSet(async value => this.internalColourUpdate(value))
|
|
64
|
+
this.cacheSat = this.service.getCharacteristic(this.hapChar.Saturation).value
|
|
65
|
+
|
|
66
|
+
// Add the get handlers only if the user hasn't disabled the disableNoResponse setting
|
|
67
|
+
if (!platform.config.disableNoResponse) {
|
|
68
|
+
this.service.getCharacteristic(this.hapChar.On).onGet(() => {
|
|
69
|
+
if (!this.isOnline) {
|
|
70
|
+
throw new this.hapErr(-70402)
|
|
71
|
+
}
|
|
72
|
+
return this.service.getCharacteristic(this.hapChar.On).value
|
|
73
|
+
})
|
|
74
|
+
this.service
|
|
75
|
+
.getCharacteristic(this.hapChar.Brightness)
|
|
76
|
+
.onGet(() => {
|
|
77
|
+
if (!this.isOnline) {
|
|
78
|
+
throw new this.hapErr(-70402)
|
|
79
|
+
}
|
|
80
|
+
return this.service.getCharacteristic(this.hapChar.Brightness).value
|
|
81
|
+
})
|
|
82
|
+
this.service.getCharacteristic(this.hapChar.Hue).onGet(() => {
|
|
83
|
+
if (!this.isOnline) {
|
|
84
|
+
throw new this.hapErr(-70402)
|
|
85
|
+
}
|
|
86
|
+
return this.service.getCharacteristic(this.hapChar.Hue).value
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Output the customised options to the log
|
|
91
|
+
const normalLogging = this.enableLogging ? 'standard' : 'disable'
|
|
92
|
+
const opts = JSON.stringify({
|
|
93
|
+
logging: this.enableDebugLogging ? 'debug' : normalLogging,
|
|
94
|
+
offlineAsOff: !!this.offlineAsOff,
|
|
95
|
+
})
|
|
96
|
+
this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async internalStateUpdate(value) {
|
|
100
|
+
try {
|
|
101
|
+
const params = {}
|
|
102
|
+
const newValue = value ? 'on' : 'off'
|
|
103
|
+
if (this.cacheState === newValue) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
params.state = newValue
|
|
107
|
+
const timerKey = generateRandomString(5)
|
|
108
|
+
this.updateTimeout = timerKey
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
if (this.updateTimeout === timerKey) {
|
|
111
|
+
this.updateTimeout = false
|
|
112
|
+
}
|
|
113
|
+
}, 5000)
|
|
114
|
+
await this.platform.sendDeviceUpdate(this.accessory, params)
|
|
115
|
+
this.cacheState = newValue
|
|
116
|
+
if (this.enableLogging) {
|
|
117
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
118
|
+
}
|
|
119
|
+
} catch (err) {
|
|
120
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
123
|
+
}, 2000)
|
|
124
|
+
throw new this.hapErr(-70402)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async internalBrightnessUpdate(value) {
|
|
129
|
+
try {
|
|
130
|
+
// B1 doesn't support brightness
|
|
131
|
+
this.cacheBright = value
|
|
132
|
+
} catch (err) {
|
|
133
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
this.service.updateCharacteristic(this.hapChar.Brightness, this.cacheBright)
|
|
136
|
+
}, 2000)
|
|
137
|
+
throw new this.hapErr(-70402)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async internalColourUpdate(value) {
|
|
142
|
+
try {
|
|
143
|
+
if (this.cacheHue === value) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
const updateKeyColour = generateRandomString(5)
|
|
147
|
+
this.updateKeyColour = updateKeyColour
|
|
148
|
+
await sleep(400)
|
|
149
|
+
if (updateKeyColour !== this.updateKeyColour) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
this.updateTimeout = updateKeyColour
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
if (this.updateTimeout === updateKeyColour) {
|
|
155
|
+
this.updateTimeout = false
|
|
156
|
+
}
|
|
157
|
+
}, 5000)
|
|
158
|
+
const saturation = this.service.getCharacteristic(this.hapChar.Saturation).value
|
|
159
|
+
const newRGB = hs2rgb(value, saturation)
|
|
160
|
+
const params = {
|
|
161
|
+
zyx_mode: 2,
|
|
162
|
+
type: 'middle',
|
|
163
|
+
channel0: '0',
|
|
164
|
+
channel1: '0',
|
|
165
|
+
channel2: newRGB[0].toString(),
|
|
166
|
+
channel3: newRGB[1].toString(),
|
|
167
|
+
channel4: newRGB[2].toString(),
|
|
168
|
+
}
|
|
169
|
+
await this.platform.sendDeviceUpdate(this.accessory, params)
|
|
170
|
+
this.cacheHue = value;
|
|
171
|
+
[this.cacheR, this.cacheG, this.cacheB] = newRGB
|
|
172
|
+
if (this.enableLogging) {
|
|
173
|
+
this.log(
|
|
174
|
+
'[%s] %s [rgb %s].',
|
|
175
|
+
this.name,
|
|
176
|
+
this.lang.curColour,
|
|
177
|
+
`${this.cacheR} ${this.cacheG} ${this.cacheB}`,
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
this.service.updateCharacteristic(this.hapChar.Hue, this.cacheHue)
|
|
184
|
+
}, 2000)
|
|
185
|
+
throw new this.hapErr(-70402)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async externalUpdate(params) {
|
|
190
|
+
try {
|
|
191
|
+
if (this.updateTimeout) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
if (params.state && params.state !== this.cacheState) {
|
|
195
|
+
this.cacheState = params.state
|
|
196
|
+
this.service.updateCharacteristic(this.hapChar.On, this.cacheState === 'on')
|
|
197
|
+
this.service.updateCharacteristic(
|
|
198
|
+
this.hapChar.Brightness,
|
|
199
|
+
this.cacheState === 'on' ? 100 : 0,
|
|
200
|
+
)
|
|
201
|
+
if (params.updateSource && this.enableLogging) {
|
|
202
|
+
this.log('[%s] %s [%s].', this.name, this.lang.curState, this.cacheState)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
let hs
|
|
206
|
+
let mode
|
|
207
|
+
if (params.zyx_mode) {
|
|
208
|
+
mode = Number.parseInt(params.zyx_mode, 10)
|
|
209
|
+
} else if (
|
|
210
|
+
hasProperty(params, 'channel0')
|
|
211
|
+
&& Number.parseInt(params.channel0, 10) + Number.parseInt(params.channel1, 10) > 0
|
|
212
|
+
) {
|
|
213
|
+
mode = 1
|
|
214
|
+
} else {
|
|
215
|
+
mode = 2
|
|
216
|
+
}
|
|
217
|
+
if (mode === 2) {
|
|
218
|
+
if (hasProperty(params, 'channel2')) {
|
|
219
|
+
if (
|
|
220
|
+
params.channel2 !== (this.cacheR || 0).toString()
|
|
221
|
+
|| params.channel3 !== (this.cacheG || 0).toString()
|
|
222
|
+
|| params.channel4 !== (this.cacheB || 0).toString()
|
|
223
|
+
) {
|
|
224
|
+
this.cacheR = Number.parseInt(params.channel2, 10)
|
|
225
|
+
this.cacheG = Number.parseInt(params.channel3, 10)
|
|
226
|
+
this.cacheB = Number.parseInt(params.channel4, 10)
|
|
227
|
+
hs = rgb2hs(this.cacheR, this.cacheG, this.cacheB);
|
|
228
|
+
[this.cacheHue] = hs
|
|
229
|
+
this.service.updateCharacteristic(this.hapChar.Hue, this.cacheHue)
|
|
230
|
+
this.service.updateCharacteristic(this.hapChar.Saturation, 100)
|
|
231
|
+
this.service.updateCharacteristic(this.hapChar.Brightness, 100)
|
|
232
|
+
if (params.updateSource && this.enableLogging) {
|
|
233
|
+
this.log(
|
|
234
|
+
'[%s] %s [rgb %s].',
|
|
235
|
+
this.name,
|
|
236
|
+
this.lang.curColour,
|
|
237
|
+
`${this.cacheR} ${this.cacheG} ${this.cacheB}`,
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
} catch (err) {
|
|
244
|
+
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
markStatus(isOnline) {
|
|
249
|
+
this.isOnline = isOnline
|
|
250
|
+
|
|
251
|
+
// Show as off if offlineAsOff is enabled
|
|
252
|
+
if (this.offlineAsOff && !isOnline && this.cacheState !== 'off') {
|
|
253
|
+
this.service.updateCharacteristic(this.hapChar.On, false)
|
|
254
|
+
this.cacheState = 'off'
|
|
255
|
+
if (this.enableLogging) {
|
|
256
|
+
this.log('[%s] %s [%s - offline]', this.name, this.lang.curState, this.cacheState)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
currentState() {
|
|
262
|
+
const toReturn = {}
|
|
263
|
+
toReturn.services = ['light']
|
|
264
|
+
toReturn.light = {
|
|
265
|
+
state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
|
|
266
|
+
brightness: this.service.getCharacteristic(this.hapChar.Brightness).value,
|
|
267
|
+
hue: this.service.getCharacteristic(this.hapChar.Hue).value,
|
|
268
|
+
saturation: this.service.getCharacteristic(this.hapChar.Saturation).value,
|
|
269
|
+
}
|
|
270
|
+
return toReturn
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import platformConsts from '../utils/constants.js'
|
|
2
|
+
import { generateRandomString, hasProperty, sleep } 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
|
+
This is for the DUALR3 using motor mode
|
|
24
|
+
This device's parameters are:
|
|
25
|
+
motorTurn: 1=OPEN, 0=STOP, 2=CLOSE (not needed by plugin)
|
|
26
|
+
location: 0=CLOSED, 100=OPEN
|
|
27
|
+
currLocation: 0=CLOSED, 100=OPEN
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// Set up custom variables for this device type
|
|
31
|
+
const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
|
|
32
|
+
const showAsMotor = deviceConf.showAsMotor || platformConsts.defaultValues.showAsMotor
|
|
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
|
+
// Add the window covering service if it doesn't already exist
|
|
55
|
+
let service
|
|
56
|
+
switch (showAsMotor) {
|
|
57
|
+
case 'door':
|
|
58
|
+
service = this.hapServ.Door
|
|
59
|
+
if (this.accessory.getService(this.hapServ.GarageDoorOpener)) {
|
|
60
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.GarageDoorOpener))
|
|
61
|
+
}
|
|
62
|
+
if (this.accessory.getService(this.hapServ.Window)) {
|
|
63
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Window))
|
|
64
|
+
}
|
|
65
|
+
if (this.accessory.getService(this.hapServ.WindowCovering)) {
|
|
66
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.WindowCovering))
|
|
67
|
+
}
|
|
68
|
+
break
|
|
69
|
+
case 'garage':
|
|
70
|
+
service = this.hapServ.GarageDoorOpener
|
|
71
|
+
this.isGarage = true
|
|
72
|
+
if (this.accessory.getService(this.hapServ.Door)) {
|
|
73
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Door))
|
|
74
|
+
}
|
|
75
|
+
if (this.accessory.getService(this.hapServ.Window)) {
|
|
76
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Window))
|
|
77
|
+
}
|
|
78
|
+
if (this.accessory.getService(this.hapServ.WindowCovering)) {
|
|
79
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.WindowCovering))
|
|
80
|
+
}
|
|
81
|
+
break
|
|
82
|
+
case 'window':
|
|
83
|
+
service = this.hapServ.Window
|
|
84
|
+
if (this.accessory.getService(this.hapServ.Door)) {
|
|
85
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Door))
|
|
86
|
+
}
|
|
87
|
+
if (this.accessory.getService(this.hapServ.GarageDoorOpener)) {
|
|
88
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.GarageDoorOpener))
|
|
89
|
+
}
|
|
90
|
+
if (this.accessory.getService(this.hapServ.WindowCovering)) {
|
|
91
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.WindowCovering))
|
|
92
|
+
}
|
|
93
|
+
break
|
|
94
|
+
default:
|
|
95
|
+
service = this.hapServ.WindowCovering
|
|
96
|
+
if (this.accessory.getService(this.hapServ.Door)) {
|
|
97
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Door))
|
|
98
|
+
}
|
|
99
|
+
if (this.accessory.getService(this.hapServ.GarageDoorOpener)) {
|
|
100
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.GarageDoorOpener))
|
|
101
|
+
}
|
|
102
|
+
if (this.accessory.getService(this.hapServ.Window)) {
|
|
103
|
+
this.accessory.removeService(this.accessory.getService(this.hapServ.Window))
|
|
104
|
+
}
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
this.service = this.accessory.getService(service) || this.accessory.addService(service)
|
|
108
|
+
|
|
109
|
+
// Add Eve power characteristics
|
|
110
|
+
if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
|
|
111
|
+
this.service.addCharacteristic(this.eveChar.CurrentConsumption)
|
|
112
|
+
}
|
|
113
|
+
if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
|
|
114
|
+
this.service.addCharacteristic(this.eveChar.ElectricCurrent)
|
|
115
|
+
}
|
|
116
|
+
if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
|
|
117
|
+
this.service.addCharacteristic(this.eveChar.Voltage)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Add the set handler to the target position characteristic
|
|
121
|
+
if (this.isGarage) {
|
|
122
|
+
this.service
|
|
123
|
+
.getCharacteristic(this.hapChar.TargetDoorState)
|
|
124
|
+
.onSet(async value => this.internalTargetUpdate(value))
|
|
125
|
+
} else {
|
|
126
|
+
this.service
|
|
127
|
+
.getCharacteristic(this.hapChar.TargetPosition)
|
|
128
|
+
.onSet(async value => this.internalPositionUpdate(value))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add the get handlers only if the user hasn't disabled the disableNoResponse setting
|
|
132
|
+
if (!platform.config.disableNoResponse) {
|
|
133
|
+
if (this.isGarage) {
|
|
134
|
+
this.service.getCharacteristic(this.hapChar.CurrentDoorState).onGet(() => {
|
|
135
|
+
if (!this.isOnline) {
|
|
136
|
+
throw new this.hapErr(-70402)
|
|
137
|
+
}
|
|
138
|
+
return this.service.getCharacteristic(this.hapChar.CurrentDoorState).value
|
|
139
|
+
})
|
|
140
|
+
this.service.getCharacteristic(this.hapChar.TargetDoorState).onGet(() => {
|
|
141
|
+
if (!this.isOnline) {
|
|
142
|
+
throw new this.hapErr(-70402)
|
|
143
|
+
}
|
|
144
|
+
return this.service.getCharacteristic(this.hapChar.TargetDoorState).value
|
|
145
|
+
})
|
|
146
|
+
} else {
|
|
147
|
+
this.service.getCharacteristic(this.hapChar.CurrentPosition).onGet(() => {
|
|
148
|
+
if (!this.isOnline) {
|
|
149
|
+
throw new this.hapErr(-70402)
|
|
150
|
+
}
|
|
151
|
+
return this.service.getCharacteristic(this.hapChar.CurrentPosition).value
|
|
152
|
+
})
|
|
153
|
+
this.service.getCharacteristic(this.hapChar.TargetPosition).onGet(() => {
|
|
154
|
+
if (!this.isOnline) {
|
|
155
|
+
throw new this.hapErr(-70402)
|
|
156
|
+
}
|
|
157
|
+
return this.service.getCharacteristic(this.hapChar.TargetPosition).value
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (platform.config.mode !== 'lan') {
|
|
163
|
+
// Set up an interval to get eWeLink to send power updates
|
|
164
|
+
setTimeout(() => {
|
|
165
|
+
this.internalUIUpdate()
|
|
166
|
+
this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
|
|
167
|
+
}, 5000)
|
|
168
|
+
|
|
169
|
+
// Stop the intervals on Homebridge shutdown
|
|
170
|
+
platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Output the customised options to the log
|
|
174
|
+
const normalLogging = this.enableLogging ? 'standard' : 'disable'
|
|
175
|
+
const opts = JSON.stringify({
|
|
176
|
+
logging: this.enableDebugLogging ? 'debug' : normalLogging,
|
|
177
|
+
showAsMotor,
|
|
178
|
+
})
|
|
179
|
+
this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async internalPositionUpdate(value) {
|
|
183
|
+
try {
|
|
184
|
+
if (this.cachePos === value) {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
const updateKey = generateRandomString(5)
|
|
188
|
+
this.updateKey = updateKey
|
|
189
|
+
await sleep(500)
|
|
190
|
+
if (updateKey !== this.updateKey) {
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
const params = { location: value }
|
|
194
|
+
await this.platform.sendDeviceUpdate(this.accessory, params)
|
|
195
|
+
this.cacheTarg = value
|
|
196
|
+
if (this.enableLogging) {
|
|
197
|
+
this.log('[%s] %s [%s%].', this.name, this.lang.curTarg, this.cacheTarg)
|
|
198
|
+
}
|
|
199
|
+
} catch (err) {
|
|
200
|
+
// Catch any errors and let the platform display them
|
|
201
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
202
|
+
setTimeout(() => {
|
|
203
|
+
this.service.updateCharacteristic(this.hapChar.TargetPosition, this.cacheTarg)
|
|
204
|
+
}, 2000)
|
|
205
|
+
throw new this.hapErr(-70402)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async internalTargetUpdate(value) {
|
|
210
|
+
try {
|
|
211
|
+
// 0 for open, 1 for closed
|
|
212
|
+
// this.cachePos is 0 for closed and >0 for open
|
|
213
|
+
|
|
214
|
+
const params = { location: value === 1 ? 0 : 100 }
|
|
215
|
+
await this.platform.sendDeviceUpdate(this.accessory, params)
|
|
216
|
+
this.cacheTarg = value === 1 ? 0 : 100
|
|
217
|
+
if (this.enableLogging) {
|
|
218
|
+
this.log('[%s] %s [%s%].', this.name, this.lang.curTarg, this.cacheTarg)
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
// Catch any errors and let the platform display them
|
|
222
|
+
this.platform.deviceUpdateError(this.accessory, err, true)
|
|
223
|
+
setTimeout(() => {
|
|
224
|
+
this.service.updateCharacteristic(this.hapChar.TargetDoorState, this.cacheTarg)
|
|
225
|
+
}, 2000)
|
|
226
|
+
throw new this.hapErr(-70402)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async internalUIUpdate() {
|
|
231
|
+
try {
|
|
232
|
+
// Skip polling if device isn't online
|
|
233
|
+
if (!this.isOnline) {
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Send the params to request the updates
|
|
238
|
+
await this.platform.sendDeviceUpdate(this.accessory, { uiActive: { outlet: 0, time: 120 } })
|
|
239
|
+
} catch (err) {
|
|
240
|
+
this.log.error(err)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async externalUpdate(params) {
|
|
245
|
+
try {
|
|
246
|
+
let locationParams = false
|
|
247
|
+
|
|
248
|
+
// currLocation is the current position of the motor
|
|
249
|
+
if (hasProperty(params, 'currLocation')) {
|
|
250
|
+
locationParams = true
|
|
251
|
+
if (params.currLocation !== this.cachePos) {
|
|
252
|
+
this.cachePos = params.currLocation
|
|
253
|
+
if (!this.isGarage) {
|
|
254
|
+
this.service.updateCharacteristic(this.hapChar.CurrentPosition, this.cachePos)
|
|
255
|
+
}
|
|
256
|
+
if (params.updateSource && this.enableLogging) {
|
|
257
|
+
this.log('[%s] %s [%s%].', this.name, this.lang.curPos, this.cachePos)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// location is the target position of the motor
|
|
263
|
+
if (hasProperty(params, 'location')) {
|
|
264
|
+
locationParams = true
|
|
265
|
+
if (params.location !== this.cacheTarg) {
|
|
266
|
+
this.cacheTarg = params.location
|
|
267
|
+
if (!this.isGarage) {
|
|
268
|
+
this.service.updateCharacteristic(this.hapChar.TargetPosition, this.cacheTarg)
|
|
269
|
+
}
|
|
270
|
+
if (params.updateSource && this.enableLogging) {
|
|
271
|
+
this.log('[%s] %s [%s%].', this.name, this.lang.curTarg, this.cacheTarg)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (locationParams) {
|
|
277
|
+
// Get the target door state characteristic for use later
|
|
278
|
+
const targetChar = this.isGarage
|
|
279
|
+
? this.service.getCharacteristic(this.hapChar.TargetDoorState)
|
|
280
|
+
: this.service.getCharacteristic(this.hapChar.TargetPosition)
|
|
281
|
+
|
|
282
|
+
// If the two are equal then the motor must have stopped at this position
|
|
283
|
+
if (this.cacheTarg === this.cachePos) {
|
|
284
|
+
if (this.isGarage) {
|
|
285
|
+
if (this.cachePos === 0) {
|
|
286
|
+
// CLOSED
|
|
287
|
+
this.service.updateCharacteristic(this.hapChar.CurrentDoorState, 1)
|
|
288
|
+
if (targetChar.value !== 1) {
|
|
289
|
+
targetChar.updateValue(1)
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
// OPEN
|
|
293
|
+
this.service.updateCharacteristic(this.hapChar.CurrentDoorState, 0)
|
|
294
|
+
if (targetChar.value !== 0) {
|
|
295
|
+
targetChar.updateValue(0)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
// PositionState 2 === STOPPED
|
|
300
|
+
this.service.updateCharacteristic(this.hapChar.PositionState, 2)
|
|
301
|
+
}
|
|
302
|
+
} else if (this.cacheTarg > this.cachePos) {
|
|
303
|
+
if (this.isGarage) {
|
|
304
|
+
// OPENING
|
|
305
|
+
this.service.updateCharacteristic(this.hapChar.CurrentDoorState, 2)
|
|
306
|
+
if (targetChar.value !== 0) {
|
|
307
|
+
targetChar.updateValue(0)
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
// PositionState 1 === INCREASING
|
|
311
|
+
this.service.updateCharacteristic(this.hapChar.PositionState, 1)
|
|
312
|
+
}
|
|
313
|
+
} else if (this.isGarage) {
|
|
314
|
+
// CLOSING
|
|
315
|
+
this.service.updateCharacteristic(this.hapChar.CurrentDoorState, 3)
|
|
316
|
+
if (targetChar.value !== 1) {
|
|
317
|
+
targetChar.updateValue(1)
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
// PositionState 0 === DECREASING
|
|
321
|
+
this.service.updateCharacteristic(this.hapChar.PositionState, 0)
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Update power information if given
|
|
326
|
+
let logger = false
|
|
327
|
+
let power
|
|
328
|
+
let voltage
|
|
329
|
+
let current
|
|
330
|
+
if (hasProperty(params, 'actPow_00')) {
|
|
331
|
+
power = Number.parseInt(params.actPow_00, 10) / 100
|
|
332
|
+
this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
|
|
333
|
+
logger = true
|
|
334
|
+
}
|
|
335
|
+
if (hasProperty(params, 'voltage_00')) {
|
|
336
|
+
voltage = Number.parseInt(params.voltage_00, 10) / 100
|
|
337
|
+
this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
|
|
338
|
+
logger = true
|
|
339
|
+
}
|
|
340
|
+
if (hasProperty(params, 'current_00')) {
|
|
341
|
+
current = Number.parseInt(params.current_00, 10) / 100
|
|
342
|
+
this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
|
|
343
|
+
logger = true
|
|
344
|
+
}
|
|
345
|
+
if (params.updateSource && logger && this.enableLogging) {
|
|
346
|
+
this.log(
|
|
347
|
+
'[%s] %s%s%s.',
|
|
348
|
+
this.name,
|
|
349
|
+
power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
|
|
350
|
+
voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
|
|
351
|
+
current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
} catch (err) {
|
|
355
|
+
// Catch any errors and let the platform display them
|
|
356
|
+
this.platform.deviceUpdateError(this.accessory, err, false)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
markStatus(isOnline) {
|
|
361
|
+
this.isOnline = isOnline
|
|
362
|
+
}
|
|
363
|
+
}
|