@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,411 @@
1
+ import platformConsts from '../utils/constants.js'
2
+ import { hasProperty, sleep } from '../utils/functions.js'
3
+
4
+ export default class {
5
+ constructor(platform, accessory, devicesInHB) {
6
+ // Set up variables from the platform
7
+ this.cusChar = platform.cusChar
8
+ this.devicesInHB = devicesInHB
9
+ this.eveChar = platform.eveChar
10
+ this.hapChar = platform.api.hap.Characteristic
11
+ this.hapErr = platform.api.hap.HapStatusError
12
+ this.hapServ = platform.api.hap.Service
13
+ this.hapUUIDGen = platform.api.hap.uuid.generate
14
+ this.lang = platform.lang
15
+ this.log = platform.log
16
+ this.platform = platform
17
+
18
+ // Set up variables from the accessory
19
+ this.name = accessory.displayName
20
+ this.accessory = accessory
21
+
22
+ // Initially set the online flag as true (to be then updated as false if necessary)
23
+ this.isOnline = true
24
+
25
+ /*
26
+ UIID 2/3/4/7/8/9/29/30/31/41/82/83/84/113/114/161/162/2256/3256/4256: multi switch, no power readings
27
+ UIID 126: multi switch, with wattage, voltage and amp readings (DUALR3)
28
+ */
29
+
30
+ // Set up custom variables for this device type
31
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
32
+ this.hideChannels = deviceConf.hideChannels
33
+ this.inchChannels = deviceConf.inchChannels || ''
34
+ this.inUsePowerThreshold = deviceConf.inUsePowerThreshold || platformConsts.defaultValues.inUsePowerThreshold
35
+ accessory.context.isInched = accessory.context.switchNumber !== '0' && this.inchChannels.includes(accessory.context.switchNumber)
36
+
37
+ // Set the correct logging variables for this accessory
38
+ switch (deviceConf.overrideLogging) {
39
+ case 'standard':
40
+ this.enableLogging = true
41
+ this.enableDebugLogging = false
42
+ break
43
+ case 'debug':
44
+ this.enableLogging = true
45
+ this.enableDebugLogging = true
46
+ break
47
+ case 'disable':
48
+ this.enableLogging = false
49
+ this.enableDebugLogging = false
50
+ break
51
+ default:
52
+ this.enableLogging = !platform.config.disableDeviceLogging
53
+ this.enableDebugLogging = platform.config.debug
54
+ break
55
+ }
56
+
57
+ // If the accessory has an outlet service then remove it
58
+ if (this.accessory.getService(this.hapServ.Outlet)) {
59
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Outlet))
60
+ }
61
+
62
+ // Add the switch service if it doesn't already exist
63
+ this.service = this.accessory.getService(this.hapServ.Switch)
64
+ || this.accessory.addService(this.hapServ.Switch)
65
+
66
+ if (platformConsts.devices.switchMultiPower.includes(this.accessory.context.eweUIID)) {
67
+ // Add Eve power characteristics
68
+ this.powerReadings = true
69
+ this.isDual = true
70
+ if (accessory.context.switchNumber !== '0') {
71
+ if (!this.service.testCharacteristic(this.hapChar.OutletInUse)) {
72
+ this.service.addCharacteristic(this.hapChar.OutletInUse)
73
+ }
74
+ if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
75
+ this.service.addCharacteristic(this.eveChar.CurrentConsumption)
76
+ }
77
+ if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
78
+ this.service.addCharacteristic(this.eveChar.ElectricCurrent)
79
+ }
80
+ if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
81
+ this.service.addCharacteristic(this.eveChar.Voltage)
82
+ }
83
+ }
84
+ } else {
85
+ // Remove unused Eve characteristics
86
+ if (this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
87
+ this.service.removeCharacteristic(
88
+ this.service.getCharacteristic(this.eveChar.CurrentConsumption),
89
+ )
90
+ }
91
+ if (this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
92
+ this.service.removeCharacteristic(
93
+ this.service.getCharacteristic(this.eveChar.ElectricCurrent),
94
+ )
95
+ }
96
+ if (this.service.testCharacteristic(this.eveChar.Voltage)) {
97
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.eveChar.Voltage))
98
+ }
99
+ }
100
+
101
+ // Add the set handler to the switch on/off characteristic
102
+ this.service
103
+ .getCharacteristic(this.hapChar.On)
104
+ .onSet(async value => this.internalStateUpdate(value))
105
+
106
+ // Add a custom characteristic for inverting the switch if inching setting is enabled
107
+ if (this.accessory.context.isInched) {
108
+ if (!this.service.testCharacteristic(this.cusChar.InvertSwitch)) {
109
+ this.service.addCharacteristic(this.cusChar.InvertSwitch)
110
+ }
111
+ this.service.updateCharacteristic(
112
+ this.cusChar.InvertSwitch,
113
+ this.service.getCharacteristic(this.hapChar.On).value,
114
+ )
115
+
116
+ // Add the set handler to invert the switch
117
+ this.service.getCharacteristic(this.cusChar.InvertSwitch).onSet((value) => {
118
+ this.service.updateCharacteristic(this.hapChar.On, value)
119
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, value ? 'on' : 'off')
120
+ })
121
+ }
122
+
123
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
124
+ if (!platform.config.disableNoResponse) {
125
+ this.service.getCharacteristic(this.hapChar.On).onGet(() => {
126
+ if (!this.isOnline) {
127
+ throw new this.hapErr(-70402)
128
+ }
129
+ return this.service.getCharacteristic(this.hapChar.On).value
130
+ })
131
+ }
132
+
133
+ // Pass the accessory to Fakegato to set up with Eve
134
+ this.accessory.eveService = new platform.eveService('switch', this.accessory, {
135
+ log: () => {},
136
+ })
137
+
138
+ // Set up extra features for outlets that provide power readings
139
+ if (
140
+ this.powerReadings
141
+ && accessory.context.switchNumber === '0'
142
+ && platform.config.mode !== 'lan'
143
+ ) {
144
+ // Set up an interval to get eWeLink to send power updates
145
+ setTimeout(() => {
146
+ this.internalUIUpdate()
147
+ this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
148
+ }, 5000)
149
+
150
+ // Stop the intervals on Homebridge shutdown
151
+ platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
152
+ }
153
+
154
+ // Output the customised options to the log
155
+ if (accessory.context.switchNumber === '0') {
156
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
157
+ const opts = JSON.stringify({
158
+ hideChannels: this.hideChannels,
159
+ inchChannels: this.inchChannels,
160
+ inUsePowerThreshold: this.inUsePowerThreshold,
161
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
162
+ showAs: 'default',
163
+ })
164
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
165
+ }
166
+ }
167
+
168
+ async internalStateUpdate(value) {
169
+ try {
170
+ let primaryState = false
171
+ if (this.accessory.context.isInched) {
172
+ value = !this.service.getCharacteristic(this.hapChar.On).value
173
+ }
174
+ const params = {
175
+ switches: [],
176
+ }
177
+ const { switchNumber } = this.accessory.context
178
+ switch (switchNumber) {
179
+ case '0':
180
+ params.switches.push({ switch: value ? 'on' : 'off', outlet: 0 })
181
+ params.switches.push({ switch: value ? 'on' : 'off', outlet: 1 })
182
+ if (!this.isDual) {
183
+ params.switches.push({ switch: value ? 'on' : 'off', outlet: 2 })
184
+ params.switches.push({ switch: value ? 'on' : 'off', outlet: 3 })
185
+ }
186
+ break
187
+ case '1':
188
+ case '2':
189
+ case '3':
190
+ case '4':
191
+ params.switches.push({
192
+ switch: value || this.accessory.context.isInched ? 'on' : 'off',
193
+ outlet: switchNumber - 1,
194
+ })
195
+ break
196
+ default:
197
+ return
198
+ }
199
+ if (this.accessory.context.isInched) {
200
+ this.accessory.context.ignoreUpdates = true
201
+ setTimeout(() => {
202
+ this.accessory.context.ignoreUpdates = false
203
+ }, 1500)
204
+ }
205
+ await this.platform.sendDeviceUpdate(this.accessory, params)
206
+ if (this.accessory.context.isInched) {
207
+ this.service.updateCharacteristic(this.cusChar.InvertSwitch, value)
208
+ }
209
+ switch (switchNumber) {
210
+ case '0':
211
+ for (let i = 0; i <= this.accessory.context.channelCount; i += 1) {
212
+ const idToCheck = `${this.accessory.context.eweDeviceId}SW${i}`
213
+ const uuid = this.hapUUIDGen(idToCheck)
214
+ if (this.devicesInHB.has(uuid)) {
215
+ const subAccessory = this.devicesInHB.get(uuid)
216
+ subAccessory
217
+ .getService(this.hapServ.Switch)
218
+ .updateCharacteristic(this.hapChar.On, value)
219
+ subAccessory.eveService.addEntry({ status: value ? 1 : 0 })
220
+ if (i > 0 && this.enableLogging) {
221
+ this.log(
222
+ '[%s] %s [%s].',
223
+ subAccessory.displayName,
224
+ this.lang.curState,
225
+ value ? 'on' : 'off',
226
+ )
227
+ }
228
+ }
229
+ }
230
+ break
231
+ case '1':
232
+ case '2':
233
+ case '3':
234
+ case '4':
235
+ for (let i = 1; i <= this.accessory.context.channelCount; i += 1) {
236
+ const idToCheck = `${this.accessory.context.eweDeviceId}SW${i}`
237
+ const uuid = this.hapUUIDGen(idToCheck)
238
+ if (this.devicesInHB.has(uuid)) {
239
+ const subAccessory = this.devicesInHB.get(uuid)
240
+ if (i === Number.parseInt(switchNumber, 10)) {
241
+ if (value) {
242
+ primaryState = true
243
+ }
244
+ subAccessory.eveService.addEntry({ status: value ? 1 : 0 })
245
+ if (i > 0 && this.enableLogging) {
246
+ this.log(
247
+ '[%s] %s [%s].',
248
+ subAccessory.displayName,
249
+ this.lang.curState,
250
+ value ? 'on' : 'off',
251
+ )
252
+ }
253
+ } else if (
254
+ subAccessory.getService(this.hapServ.Switch).getCharacteristic(this.hapChar.On).value
255
+ ) {
256
+ primaryState = true
257
+ }
258
+ }
259
+ }
260
+ if (!this.platform.hideMasters.includes(this.accessory.context.eweDeviceId)) {
261
+ const idToCheck = `${this.accessory.context.eweDeviceId}SW0`
262
+ const uuid = this.hapUUIDGen(idToCheck)
263
+ const priAccessory = this.devicesInHB.get(uuid)
264
+ priAccessory
265
+ .getService(this.hapServ.Switch)
266
+ .updateCharacteristic(this.hapChar.On, primaryState)
267
+ priAccessory.eveService.addEntry({ status: primaryState ? 1 : 0 })
268
+ }
269
+ break
270
+ default:
271
+ }
272
+ } catch (err) {
273
+ this.platform.deviceUpdateError(this.accessory, err, true)
274
+ setTimeout(() => {
275
+ this.service.updateCharacteristic(this.hapChar.On, !value)
276
+ }, 2000)
277
+ throw new this.hapErr(-70402)
278
+ }
279
+ }
280
+
281
+ async internalUIUpdate() {
282
+ try {
283
+ // Skip polling if device isn't online
284
+ if (!this.isOnline) {
285
+ return
286
+ }
287
+
288
+ // Send the params to request the updates
289
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: { outlet: 0, time: 120 } })
290
+ await sleep(2000)
291
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: { outlet: 1, time: 120 } })
292
+ } catch (err) {
293
+ // Suppress errors here
294
+ }
295
+ }
296
+
297
+ async externalUpdate(params) {
298
+ try {
299
+ const idToCheck = `${this.accessory.context.eweDeviceId}SW`
300
+ let primaryState = false
301
+ for (let i = 1; i <= this.accessory.context.channelCount; i += 1) {
302
+ const uuid = this.hapUUIDGen(idToCheck + i)
303
+ if (this.devicesInHB.has(uuid)) {
304
+ const subAccessory = this.devicesInHB.get(uuid)
305
+ const service = subAccessory.getService(this.hapServ.Switch)
306
+ if (params.switches) {
307
+ const currentState = service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
308
+ if (subAccessory.context.isInched) {
309
+ if (params.switches[i - 1].switch === 'on' && !subAccessory.context.ignoreUpdates) {
310
+ subAccessory.context.ignoreUpdates = true
311
+ setTimeout(() => {
312
+ subAccessory.context.ignoreUpdates = false
313
+ }, 1500)
314
+ const newState = currentState === 'on' ? 'off' : 'on'
315
+ if (newState === 'on') {
316
+ primaryState = true
317
+ }
318
+ service.updateCharacteristic(this.hapChar.On, newState === 'on')
319
+ service.updateCharacteristic(this.cusChar.InvertSwitch, newState === 'on')
320
+ subAccessory.eveService.addEntry({
321
+ status: newState === 'on' ? 1 : 0,
322
+ })
323
+ if (params.updateSource && this.enableLogging) {
324
+ this.log('[%s] %s [%s].', subAccessory.displayName, this.lang.curState, newState)
325
+ }
326
+ }
327
+ } else {
328
+ if (params.switches[i - 1].switch === 'on') {
329
+ primaryState = true
330
+ }
331
+ if (!params.updateSource || params.switches[i - 1].switch !== currentState) {
332
+ service.updateCharacteristic(
333
+ this.hapChar.On,
334
+ params.switches[i - 1].switch === 'on',
335
+ )
336
+ subAccessory.eveService.addEntry({
337
+ status: params.switches[i - 1].switch === 'on' ? 1 : 0,
338
+ })
339
+ if (params.updateSource && this.enableLogging) {
340
+ this.log(
341
+ '[%s] %s [%s].',
342
+ subAccessory.displayName,
343
+ this.lang.curState,
344
+ params.switches[i - 1].switch,
345
+ )
346
+ }
347
+ }
348
+ }
349
+ }
350
+ if (this.powerReadings) {
351
+ let logger = false
352
+ let power
353
+ let voltage
354
+ let current
355
+ if (hasProperty(params, `actPow_0${i - 1}`)) {
356
+ power = Number.parseInt(params[`actPow_0${i - 1}`], 10) / 100
357
+ const currentState = service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off'
358
+ service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
359
+ service.updateCharacteristic(
360
+ this.hapChar.OutletInUse,
361
+ currentState === 'on' && power > this.inUsePowerThreshold,
362
+ )
363
+ logger = true
364
+ }
365
+ if (hasProperty(params, `voltage_0${i - 1}`)) {
366
+ voltage = Number.parseInt(params[`voltage_0${i - 1}`], 10) / 100
367
+ service.updateCharacteristic(this.eveChar.Voltage, voltage)
368
+ logger = true
369
+ }
370
+ if (hasProperty(params, `current_0${i - 1}`)) {
371
+ current = Number.parseInt(params[`current_0${i - 1}`], 10) / 100
372
+ service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
373
+ logger = true
374
+ }
375
+ if (params.updateSource && logger && this.enableLogging) {
376
+ this.log(
377
+ '[%s] %s%s%s.',
378
+ subAccessory.displayName,
379
+ power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
380
+ voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
381
+ current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
382
+ )
383
+ }
384
+ }
385
+ }
386
+ }
387
+ if (
388
+ !this.platform.hideMasters.includes(this.accessory.context.eweDeviceId)
389
+ && params.switches
390
+ ) {
391
+ this.service.updateCharacteristic(this.hapChar.On, primaryState)
392
+ this.accessory.eveService.addEntry({ status: primaryState ? 1 : 0 })
393
+ }
394
+ } catch (err) {
395
+ this.platform.deviceUpdateError(this.accessory, err, false)
396
+ }
397
+ }
398
+
399
+ markStatus(isOnline) {
400
+ this.isOnline = isOnline
401
+ }
402
+
403
+ currentState() {
404
+ const toReturn = {}
405
+ toReturn.services = ['switch']
406
+ toReturn.switch = {
407
+ state: this.service.getCharacteristic(this.hapChar.On).value ? 'on' : 'off',
408
+ }
409
+ return toReturn
410
+ }
411
+ }