@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.
Files changed (113) hide show
  1. package/CHANGELOG.md +1363 -0
  2. package/LICENSE +21 -0
  3. package/README.md +68 -0
  4. package/config.schema.json +1757 -0
  5. package/eslint.config.js +15 -0
  6. package/lib/connection/api.js +508 -0
  7. package/lib/connection/http.js +304 -0
  8. package/lib/connection/lan.js +318 -0
  9. package/lib/connection/ws.js +414 -0
  10. package/lib/device/curtain.js +324 -0
  11. package/lib/device/diffuser.js +473 -0
  12. package/lib/device/fan.js +349 -0
  13. package/lib/device/group.js +106 -0
  14. package/lib/device/humidifier.js +199 -0
  15. package/lib/device/index.js +61 -0
  16. package/lib/device/light-cct.js +333 -0
  17. package/lib/device/light-dimmer.js +257 -0
  18. package/lib/device/light-rgb-cct.js +809 -0
  19. package/lib/device/light-rgb.js +272 -0
  20. package/lib/device/motor.js +363 -0
  21. package/lib/device/outlet-multi.js +417 -0
  22. package/lib/device/outlet-single-inched.js +409 -0
  23. package/lib/device/outlet-single.js +384 -0
  24. package/lib/device/panel.js +382 -0
  25. package/lib/device/rf-bridge.js +321 -0
  26. package/lib/device/rf-button.js +125 -0
  27. package/lib/device/rf-sensor.js +173 -0
  28. package/lib/device/sensor-ambient.js +311 -0
  29. package/lib/device/sensor-contact.js +140 -0
  30. package/lib/device/sensor-temp-humi.js +182 -0
  31. package/lib/device/simulation/blind.js +253 -0
  32. package/lib/device/simulation/cooler.js +312 -0
  33. package/lib/device/simulation/door.js +253 -0
  34. package/lib/device/simulation/doorbell.js +190 -0
  35. package/lib/device/simulation/garage-eachen.js +235 -0
  36. package/lib/device/simulation/garage-four.js +176 -0
  37. package/lib/device/simulation/garage-od-switch.js +36 -0
  38. package/lib/device/simulation/garage-one.js +395 -0
  39. package/lib/device/simulation/garage-two.js +394 -0
  40. package/lib/device/simulation/gate-one.js +327 -0
  41. package/lib/device/simulation/heater.js +389 -0
  42. package/lib/device/simulation/index.js +75 -0
  43. package/lib/device/simulation/light-fan.js +258 -0
  44. package/lib/device/simulation/lock-eachen.js +163 -0
  45. package/lib/device/simulation/lock-one.js +366 -0
  46. package/lib/device/simulation/p-button.js +223 -0
  47. package/lib/device/simulation/purifier.js +289 -0
  48. package/lib/device/simulation/rf-blind.js +192 -0
  49. package/lib/device/simulation/rf-door.js +180 -0
  50. package/lib/device/simulation/rf-window.js +180 -0
  51. package/lib/device/simulation/sensor-hidden.js +105 -0
  52. package/lib/device/simulation/sensor-leak.js +123 -0
  53. package/lib/device/simulation/sensor-visible.js +231 -0
  54. package/lib/device/simulation/sensor.js +300 -0
  55. package/lib/device/simulation/switch-valve.js +351 -0
  56. package/lib/device/simulation/tap-one.js +283 -0
  57. package/lib/device/simulation/tap-two.js +266 -0
  58. package/lib/device/simulation/th-cooler.js +443 -0
  59. package/lib/device/simulation/th-dehumidifier.js +424 -0
  60. package/lib/device/simulation/th-heater.js +449 -0
  61. package/lib/device/simulation/th-humidifier.js +424 -0
  62. package/lib/device/simulation/th-thermostat.js +611 -0
  63. package/lib/device/simulation/tv.js +252 -0
  64. package/lib/device/simulation/valve-four.js +232 -0
  65. package/lib/device/simulation/valve-one.js +345 -0
  66. package/lib/device/simulation/valve-two.js +340 -0
  67. package/lib/device/simulation/window.js +253 -0
  68. package/lib/device/switch-man.js +106 -0
  69. package/lib/device/switch-mate.js +82 -0
  70. package/lib/device/switch-multi.js +411 -0
  71. package/lib/device/switch-single-inched.js +332 -0
  72. package/lib/device/switch-single.js +307 -0
  73. package/lib/device/template.js +43 -0
  74. package/lib/device/thermostat.js +286 -0
  75. package/lib/device/zigbee/index.js +27 -0
  76. package/lib/device/zigbee/light-cct.js +263 -0
  77. package/lib/device/zigbee/light-dimmer.js +148 -0
  78. package/lib/device/zigbee/light-rgb-cct.js +400 -0
  79. package/lib/device/zigbee/motor.js +301 -0
  80. package/lib/device/zigbee/sensor-ambient.js +177 -0
  81. package/lib/device/zigbee/sensor-contact.js +137 -0
  82. package/lib/device/zigbee/sensor-leak.js +119 -0
  83. package/lib/device/zigbee/sensor-motion.js +119 -0
  84. package/lib/device/zigbee/sensor-occupancy.js +119 -0
  85. package/lib/device/zigbee/sensor-smoke.js +96 -0
  86. package/lib/device/zigbee/switch-stateless.js +104 -0
  87. package/lib/device/zigbee/thermostat.js +323 -0
  88. package/lib/fakegato/LICENSE +21 -0
  89. package/lib/fakegato/fakegato-history.js +813 -0
  90. package/lib/fakegato/fakegato-storage.js +108 -0
  91. package/lib/fakegato/fakegato-timer.js +125 -0
  92. package/lib/fakegato/uuid.js +27 -0
  93. package/lib/homebridge-ui/public/index.html +365 -0
  94. package/lib/homebridge-ui/server.js +46 -0
  95. package/lib/index.js +7 -0
  96. package/lib/node-dns-sd/LICENSE +21 -0
  97. package/lib/node-dns-sd/README.md +613 -0
  98. package/lib/node-dns-sd/lib/dns-sd-classes.json +6 -0
  99. package/lib/node-dns-sd/lib/dns-sd-composer.js +83 -0
  100. package/lib/node-dns-sd/lib/dns-sd-parser.js +344 -0
  101. package/lib/node-dns-sd/lib/dns-sd-types.json +52 -0
  102. package/lib/node-dns-sd/lib/dns-sd.js +580 -0
  103. package/lib/platform.js +2156 -0
  104. package/lib/utils/colour.js +552 -0
  105. package/lib/utils/constants.js +589 -0
  106. package/lib/utils/custom-chars.js +33 -0
  107. package/lib/utils/eve-chars.js +130 -0
  108. package/lib/utils/functions.js +42 -0
  109. package/lib/utils/lang-en.js +160 -0
  110. package/lib/utils/lang-fr.js +163 -0
  111. package/lib/utils/lang-th.js +160 -0
  112. package/lib/utils/lang.js +5 -0
  113. package/package.json +75 -0
@@ -0,0 +1,395 @@
1
+ import platformConsts from '../../utils/constants.js'
2
+ import { 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.hapUUIDGen = platform.api.hap.uuid.generate
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
+ // Set up custom variables for this device type
24
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
25
+ this.operationTimeUp = deviceConf.operationTime || platformConsts.defaultValues.operationTime
26
+ this.operationTimeDown = deviceConf.operationTimeDown || this.operationTimeUp
27
+
28
+ // Set the correct logging variables for this accessory
29
+ switch (deviceConf.overrideLogging) {
30
+ case 'standard':
31
+ this.enableLogging = true
32
+ this.enableDebugLogging = false
33
+ break
34
+ case 'debug':
35
+ this.enableLogging = true
36
+ this.enableDebugLogging = true
37
+ break
38
+ case 'disable':
39
+ this.enableLogging = false
40
+ this.enableDebugLogging = false
41
+ break
42
+ default:
43
+ this.enableLogging = !platform.config.disableDeviceLogging
44
+ this.enableDebugLogging = platform.config.debug
45
+ break
46
+ }
47
+
48
+ // Check the sensor is valid if defined by the user
49
+ if (deviceConf.sensorId) {
50
+ this.definedSensor = true
51
+ }
52
+
53
+ // If the accessory has a switch service then remove it
54
+ if (this.accessory.getService(this.hapServ.Switch)) {
55
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
56
+ }
57
+
58
+ // If the accessory has a contact sensor service then remove it
59
+ if (this.accessory.getService(this.hapServ.ContactSensor)) {
60
+ this.accessory.removeService(this.accessory.getService(this.hapServ.ContactSensor))
61
+ }
62
+
63
+ // Add the garage door service if it doesn't already exist
64
+ this.service = this.accessory.getService(this.hapServ.GarageDoorOpener)
65
+ if (!this.service) {
66
+ this.service = this.accessory.addService(this.hapServ.GarageDoorOpener)
67
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, 1)
68
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, 1)
69
+ this.service.updateCharacteristic(this.hapChar.ObstructionDetected, false)
70
+ this.service.addCharacteristic(this.eveChar.LastActivation)
71
+ this.service.addCharacteristic(this.eveChar.ResetTotal)
72
+ this.service.addCharacteristic(this.eveChar.TimesOpened)
73
+ }
74
+
75
+ // Remove unneeded characteristics
76
+ if (this.service.testCharacteristic(this.hapChar.ContactSensorState)) {
77
+ this.service.removeCharacteristic(
78
+ this.service.getCharacteristic(this.hapChar.ContactSensorState),
79
+ )
80
+ }
81
+ if (this.service.testCharacteristic(this.hapChar.OpenDuration)) {
82
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.hapChar.OpenDuration))
83
+ }
84
+ if (this.service.testCharacteristic(this.hapChar.ClosedDuration)) {
85
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.hapChar.ClosedDuration))
86
+ }
87
+
88
+ // Set up the device type and power readings if necessary
89
+ if (platformConsts.devices.switchSingle.includes(this.accessory.context.eweUIID)) {
90
+ this.setup = 'switchSingle'
91
+ } else if (platformConsts.devices.switchSinglePower.includes(this.accessory.context.eweUIID)) {
92
+ this.setup = 'switchSingle'
93
+
94
+ // Add Eve power characteristics
95
+ this.powerReadings = true
96
+ if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
97
+ this.service.addCharacteristic(this.eveChar.CurrentConsumption)
98
+ }
99
+ if (this.accessory.context.eweUIID === 32) {
100
+ if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
101
+ this.service.addCharacteristic(this.eveChar.ElectricCurrent)
102
+ }
103
+ if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
104
+ this.service.addCharacteristic(this.eveChar.Voltage)
105
+ }
106
+ }
107
+ } else if (platformConsts.devices.switchMulti.includes(this.accessory.context.eweUIID)) {
108
+ this.setup = 'switchMulti'
109
+ if (platformConsts.devices.switchMultiPower.includes(this.accessory.context.eweUIID)) {
110
+ // Add Eve power characteristics
111
+ this.powerReadings = true
112
+ if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
113
+ this.service.addCharacteristic(this.eveChar.CurrentConsumption)
114
+ }
115
+ if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
116
+ this.service.addCharacteristic(this.eveChar.ElectricCurrent)
117
+ }
118
+ if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
119
+ this.service.addCharacteristic(this.eveChar.Voltage)
120
+ }
121
+ this.isDualR3 = true
122
+ }
123
+ } else if (platformConsts.devices.switchSCM.includes(this.accessory.context.eweUIID)) {
124
+ this.setup = 'switchMulti'
125
+ } else if (platformConsts.devices.switchSCMPower.includes(this.accessory.context.eweUIID)) {
126
+ // Could do power here
127
+ this.setup = 'switchMulti'
128
+ }
129
+
130
+ // Add the set handler to the garage door reset total characteristic
131
+ this.service.getCharacteristic(this.eveChar.ResetTotal).onSet(() => {
132
+ this.service.updateCharacteristic(this.eveChar.TimesOpened, 0)
133
+ })
134
+
135
+ // Add the set handler to the target position characteristic
136
+ this.service.getCharacteristic(this.hapChar.TargetDoorState).onSet((value) => {
137
+ // We don't use await as we want the callback to be run straight away
138
+ this.internalUpdate(value)
139
+ })
140
+
141
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
142
+ if (!platform.config.disableNoResponse) {
143
+ this.service.getCharacteristic(this.hapChar.CurrentDoorState).onGet(() => {
144
+ if (!this.isOnline) {
145
+ throw new this.hapErr(-70402)
146
+ }
147
+ return this.service.getCharacteristic(this.hapChar.CurrentDoorState).value
148
+ })
149
+ this.service.getCharacteristic(this.hapChar.TargetDoorState).onGet(() => {
150
+ if (!this.isOnline) {
151
+ throw new this.hapErr(-70402)
152
+ }
153
+ return this.service.getCharacteristic(this.hapChar.TargetDoorState).value
154
+ })
155
+ }
156
+
157
+ // Pass the accessory to Fakegato to set up with Eve
158
+ this.accessory.eveService = new platform.eveService('door', this.accessory, {
159
+ log: () => {},
160
+ })
161
+ this.accessory.eveService.addEntry({
162
+ status: this.service.getCharacteristic(this.hapChar.CurrentDoorState).value === 0 ? 0 : 1,
163
+ })
164
+
165
+ // Set up an interval to get eWeLink to send power updates
166
+ if (
167
+ this.powerReadings
168
+ && (!this.isDualR3 || (this.isDualR3 && platform.config.mode !== 'lan'))
169
+ ) {
170
+ setTimeout(() => {
171
+ this.internalUIUpdate()
172
+ this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
173
+ }, 5000)
174
+
175
+ // Stop the intervals on Homebridge shutdown
176
+ platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
177
+ }
178
+
179
+ // Output the customised options to the log
180
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
181
+ const opts = JSON.stringify({
182
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
183
+ operationTimeDown: this.operationTimeDown,
184
+ operationTimeUp: this.operationTimeUp,
185
+ showAs: 'garage',
186
+ })
187
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
188
+ }
189
+
190
+ async internalUpdate(value) {
191
+ try {
192
+ const newPos = value
193
+ const params = {}
194
+ let delay = 0
195
+ const prevState = this.service.getCharacteristic(this.hapChar.CurrentDoorState).value
196
+ if (newPos === prevState % 2) {
197
+ return
198
+ }
199
+ this.inUse = true
200
+ this.cacheState = value
201
+ if (this.setup === 'switchSingle' && [2, 3].includes(prevState)) {
202
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, ((prevState * 2) % 3) + 2)
203
+ delay = 1500
204
+ }
205
+ if (this.cacheState !== newPos) {
206
+ return
207
+ }
208
+ await sleep(delay)
209
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, newPos)
210
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, newPos + 2)
211
+ switch (this.setup) {
212
+ case 'switchSingle':
213
+ params.switch = 'on'
214
+ break
215
+ case 'switchMulti':
216
+ params.switches = [
217
+ {
218
+ switch: newPos === 0 ? 'on' : 'off',
219
+ outlet: 0,
220
+ },
221
+ {
222
+ switch: newPos === 1 ? 'on' : 'off',
223
+ outlet: 1,
224
+ },
225
+ ]
226
+ break
227
+ default:
228
+ return
229
+ }
230
+ await this.platform.sendDeviceUpdate(this.accessory, params)
231
+ await sleep(2000)
232
+ this.inUse = false
233
+ if (!this.definedSensor && newPos === 0) {
234
+ this.accessory.eveService.addEntry({ status: 0 })
235
+ const initialTime = this.accessory.eveService.getInitialTime()
236
+ this.service.updateCharacteristic(
237
+ this.eveChar.LastActivation,
238
+ Math.round(new Date().valueOf() / 1000) - initialTime,
239
+ )
240
+ this.service.updateCharacteristic(
241
+ this.eveChar.TimesOpened,
242
+ this.service.getCharacteristic(this.eveChar.TimesOpened).value + 1,
243
+ )
244
+ }
245
+ const operationTime = newPos === 0 ? this.operationTimeUp : this.operationTimeDown
246
+ await sleep(Math.max((operationTime - 20) * 100, 0))
247
+ if (!this.definedSensor) {
248
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, newPos)
249
+ if (newPos === 1) {
250
+ this.accessory.eveService.addEntry({ status: 1 })
251
+ }
252
+ if (this.enableLogging) {
253
+ this.log(
254
+ '[%s] %s [%s].',
255
+ this.name,
256
+ this.lang.curState,
257
+ newPos === 0 ? this.lang.doorOpen : this.lang.doorClosed,
258
+ )
259
+ }
260
+ }
261
+ } catch (err) {
262
+ this.inUse = false
263
+ this.platform.deviceUpdateError(this.accessory, err, true)
264
+ setTimeout(() => {
265
+ this.service.updateCharacteristic(
266
+ this.hapChar.TargetDoorState,
267
+ this.service.getCharacteristic(this.hapChar.TargetDoorState).value,
268
+ )
269
+ }, 2000)
270
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, new this.hapErr(-70402))
271
+ }
272
+ }
273
+
274
+ async internalUIUpdate() {
275
+ try {
276
+ // Skip polling if device isn't online
277
+ if (!this.isOnline) {
278
+ return
279
+ }
280
+
281
+ // Send the params to request the updates
282
+ if (this.isDualR3) {
283
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: { outlet: 0, time: 120 } })
284
+ } else {
285
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: 120 })
286
+ }
287
+ } catch (err) {
288
+ // Suppress errors here
289
+ }
290
+ }
291
+
292
+ async externalUpdate(params) {
293
+ try {
294
+ if (this.powerReadings) {
295
+ let logger = false
296
+ let power
297
+ let voltage
298
+ let current
299
+ if (hasProperty(params, 'actPow_00')) {
300
+ power = Number.parseInt(params.actPow_00, 10) / 100
301
+ this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
302
+ logger = true
303
+ } else if (hasProperty(params, 'power')) {
304
+ power = Number.parseFloat(params.power)
305
+ this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
306
+ logger = true
307
+ }
308
+ if (hasProperty(params, 'voltage_00')) {
309
+ voltage = Number.parseInt(params.voltage_00, 10) / 100
310
+ this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
311
+ logger = true
312
+ } else if (hasProperty(params, 'voltage')) {
313
+ voltage = Number.parseFloat(params.voltage)
314
+ this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
315
+ logger = true
316
+ }
317
+ if (hasProperty(params, 'current_00')) {
318
+ current = Number.parseInt(params.current_00, 10) / 100
319
+ this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
320
+ logger = true
321
+ } else if (hasProperty(params, 'current')) {
322
+ current = Number.parseFloat(params.current)
323
+ this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
324
+ logger = true
325
+ }
326
+ if (params.updateSource && logger && this.enableLogging) {
327
+ this.log(
328
+ '[%s] %s%s%s.',
329
+ this.name,
330
+ power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
331
+ voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
332
+ current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
333
+ )
334
+ }
335
+ }
336
+
337
+ if (!this.inUse && !this.definedSensor) {
338
+ const prevState = this.service.getCharacteristic(this.hapChar.CurrentDoorState).value
339
+ const newPos = [0, 2].includes(prevState) ? 3 : 2
340
+ if (this.setup === 'switchMulti' && params.switches) {
341
+ if (
342
+ params.switches[0].switch === params.switches[1].switch
343
+ || params.switches[prevState % 2].switch === 'on'
344
+ ) {
345
+ return
346
+ }
347
+ } else if (this.setup === 'switchSingle' && params.switch) {
348
+ if (params.switch === 'off') {
349
+ return
350
+ }
351
+ } else {
352
+ return
353
+ }
354
+ this.inUse = true
355
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, newPos - 2)
356
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, newPos)
357
+ await sleep(2000)
358
+ this.inUse = false
359
+ if (newPos === 2) {
360
+ this.accessory.eveService.addEntry({ status: 0 })
361
+ const initialTime = this.accessory.eveService.getInitialTime()
362
+ this.service.updateCharacteristic(
363
+ this.eveChar.LastActivation,
364
+ Math.round(new Date().valueOf() / 1000) - initialTime,
365
+ )
366
+ this.service.updateCharacteristic(
367
+ this.eveChar.TimesOpened,
368
+ this.service.getCharacteristic(this.eveChar.TimesOpened).value + 1,
369
+ )
370
+ }
371
+ const operationTime = newPos === 2 ? this.operationTimeUp : this.operationTimeDown
372
+ await sleep(Math.max((operationTime - 20) * 100, 0))
373
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, newPos - 2)
374
+ if (newPos === 3) {
375
+ this.accessory.eveService.addEntry({ status: 1 })
376
+ }
377
+ if (this.enableLogging) {
378
+ this.log(
379
+ '[%s] %s [%s].',
380
+ this.name,
381
+ this.lang.curState,
382
+ newPos === 2 ? this.lang.doorOpen : this.lang.doorClosed,
383
+ )
384
+ }
385
+ }
386
+ } catch (err) {
387
+ this.inUse = false
388
+ this.platform.deviceUpdateError(this.accessory, err, false)
389
+ }
390
+ }
391
+
392
+ markStatus(isOnline) {
393
+ this.isOnline = isOnline
394
+ }
395
+ }