@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,190 @@
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
+ // Set up custom variables for this device type
23
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
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
+ // If the accessory has a switch service then remove it
46
+ if (this.accessory.getService(this.hapServ.Switch)) {
47
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
48
+ }
49
+
50
+ // Add the doorbell service if it doesn't already exist
51
+ this.service = this.accessory.getService(this.hapServ.Doorbell)
52
+ || this.accessory.addService(this.hapServ.Doorbell)
53
+
54
+ // Set up the device type and power readings if necessary
55
+ if (platformConsts.devices.switchSingle.includes(this.accessory.context.eweUIID)) {
56
+ this.setup = 'switchSingle'
57
+ } else if (platformConsts.devices.switchSinglePower.includes(this.accessory.context.eweUIID)) {
58
+ this.setup = 'switchSingle'
59
+ this.powerReadings = true
60
+ } else if (platformConsts.devices.switchMulti.includes(this.accessory.context.eweUIID)) {
61
+ this.setup = 'switchMulti'
62
+ if (platformConsts.devices.switchMultiPower.includes(this.accessory.context.eweUIID)) {
63
+ // Add Eve power characteristics
64
+ this.powerReadings = true
65
+ this.isDualR3 = true
66
+ }
67
+ } else if (platformConsts.devices.switchSCM.includes(this.accessory.context.eweUIID)) {
68
+ this.setup = 'switchMulti'
69
+ } else if (platformConsts.devices.switchSCMPower.includes(this.accessory.context.eweUIID)) {
70
+ // Could do power here
71
+ this.setup = 'switchMulti'
72
+ }
73
+
74
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
75
+ if (!platform.config.disableNoResponse) {
76
+ this.service.getCharacteristic(this.hapChar.ProgrammableSwitchEvent).onGet(() => {
77
+ if (!this.isOnline) {
78
+ throw new this.hapErr(-70402)
79
+ }
80
+ return this.service.getCharacteristic(this.hapChar.ProgrammableSwitchEvent).value
81
+ })
82
+ }
83
+
84
+ // Set up an interval to get eWeLink to send power updates
85
+ if (
86
+ this.powerReadings
87
+ && (!this.isDualR3 || (this.isDualR3 && platform.config.mode !== 'lan'))
88
+ ) {
89
+ setTimeout(() => {
90
+ this.internalUIUpdate()
91
+ this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
92
+ }, 5000)
93
+
94
+ // Stop the intervals on Homebridge shutdown
95
+ platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
96
+ }
97
+
98
+ // Output the customised options to the log
99
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
100
+ const opts = JSON.stringify({
101
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
102
+ showAs: 'doorbell',
103
+ })
104
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
105
+ }
106
+
107
+ async internalUIUpdate() {
108
+ try {
109
+ // Skip polling if device isn't online
110
+ if (!this.isOnline) {
111
+ return
112
+ }
113
+
114
+ // Send the params to request the updates
115
+ if (this.isDualR3) {
116
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: { outlet: 0, time: 120 } })
117
+ } else {
118
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: 120 })
119
+ }
120
+ } catch (err) {
121
+ // Suppress errors here
122
+ }
123
+ }
124
+
125
+ async externalUpdate(params) {
126
+ try {
127
+ if (!this.inUse) {
128
+ if (
129
+ (this.setup === 'switchMulti' && params.switches && params.switches[0].switch === 'on')
130
+ || (this.setup === 'switchSingle' && params.switch && params.switch === 'on')
131
+ ) {
132
+ this.inUse = true
133
+ setTimeout(() => {
134
+ this.inUse = false
135
+ }, 2000)
136
+ this.service.updateCharacteristic(this.hapChar.ProgrammableSwitchEvent, 0)
137
+ if (params.updateSource && this.enableLogging) {
138
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.lang.buttonSingle)
139
+ }
140
+ }
141
+ }
142
+
143
+ // Get the power readings given by certain devices
144
+ if (!this.powerReadings) {
145
+ return
146
+ }
147
+ let logger = false
148
+ let power
149
+ let voltage
150
+ let current
151
+ if (hasProperty(params, 'actPow_00')) {
152
+ power = Number.parseInt(params.actPow_00, 10) / 100
153
+ logger = true
154
+ } else if (hasProperty(params, 'power')) {
155
+ power = Number.parseFloat(params.power)
156
+ logger = true
157
+ }
158
+ if (hasProperty(params, 'voltage_00')) {
159
+ voltage = Number.parseInt(params.voltage_00, 10) / 100
160
+ logger = true
161
+ } else if (hasProperty(params, 'voltage')) {
162
+ voltage = Number.parseFloat(params.voltage)
163
+ logger = true
164
+ }
165
+ if (hasProperty(params, 'current_00')) {
166
+ current = Number.parseInt(params.current_00, 10) / 100
167
+ logger = true
168
+ } else if (hasProperty(params, 'current')) {
169
+ current = Number.parseFloat(params.current)
170
+ logger = true
171
+ }
172
+ if (params.updateSource && logger && this.enableLogging) {
173
+ this.log(
174
+ '[%s] %s%s%s.',
175
+ this.name,
176
+ power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
177
+ voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
178
+ current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
179
+ )
180
+ }
181
+ } catch (err) {
182
+ this.inUse = false
183
+ this.platform.deviceUpdateError(this.accessory, err, false)
184
+ }
185
+ }
186
+
187
+ markStatus(isOnline) {
188
+ this.isOnline = isOnline
189
+ }
190
+ }
@@ -0,0 +1,235 @@
1
+ import platformConsts from '../../utils/constants.js'
2
+ import { generateRandomString, 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
+ // Set up custom variables for this device type
23
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
24
+ this.operationTime = deviceConf.operationTime || platformConsts.defaultValues.operationTime
25
+
26
+ // Set the correct logging variables for this accessory
27
+ switch (deviceConf.overrideLogging) {
28
+ case 'standard':
29
+ this.enableLogging = true
30
+ this.enableDebugLogging = false
31
+ break
32
+ case 'debug':
33
+ this.enableLogging = true
34
+ this.enableDebugLogging = true
35
+ break
36
+ case 'disable':
37
+ this.enableLogging = false
38
+ this.enableDebugLogging = false
39
+ break
40
+ default:
41
+ this.enableLogging = !platform.config.disableDeviceLogging
42
+ this.enableDebugLogging = platform.config.debug
43
+ break
44
+ }
45
+
46
+ // If the accessory has a switch service then remove it
47
+ if (this.accessory.getService(this.hapServ.Switch)) {
48
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Switch))
49
+ }
50
+
51
+ // If the accessory has a contact sensor service then remove it
52
+ if (this.accessory.getService(this.hapServ.ContactSensor)) {
53
+ this.accessory.removeService(this.accessory.getService(this.hapServ.ContactSensor))
54
+ }
55
+
56
+ // If the accessory has a lock service then remove it
57
+ if (this.accessory.getService(this.hapServ.LockMechanism)) {
58
+ this.accessory.removeService(this.accessory.getService(this.hapServ.LockMechanism))
59
+ }
60
+
61
+ // Add the garage door service if it doesn't already exist
62
+ this.service = this.accessory.getService(this.hapServ.GarageDoorOpener)
63
+ if (!this.service) {
64
+ this.service = this.accessory.addService(this.hapServ.GarageDoorOpener)
65
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, 1)
66
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, 1)
67
+ this.service.updateCharacteristic(this.hapChar.ObstructionDetected, false)
68
+ this.service.addCharacteristic(this.eveChar.LastActivation)
69
+ this.service.addCharacteristic(this.eveChar.ResetTotal)
70
+ this.service.addCharacteristic(this.eveChar.TimesOpened)
71
+ }
72
+
73
+ // Remove unneeded characteristics
74
+ if (this.service.testCharacteristic(this.hapChar.ContactSensorState)) {
75
+ this.service.removeCharacteristic(
76
+ this.service.getCharacteristic(this.hapChar.ContactSensorState),
77
+ )
78
+ }
79
+ if (this.service.testCharacteristic(this.hapChar.OpenDuration)) {
80
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.hapChar.OpenDuration))
81
+ }
82
+ if (this.service.testCharacteristic(this.hapChar.ClosedDuration)) {
83
+ this.service.removeCharacteristic(this.service.getCharacteristic(this.hapChar.ClosedDuration))
84
+ }
85
+
86
+ // Add the set handler to the garage door reset total characteristic
87
+ this.service.getCharacteristic(this.eveChar.ResetTotal).onSet(() => {
88
+ this.service.updateCharacteristic(this.eveChar.TimesOpened, 0)
89
+ })
90
+
91
+ // Add the set handler to the target position characteristic
92
+ this.service.getCharacteristic(this.hapChar.TargetDoorState).onSet((value) => {
93
+ // We don't use await as we want the callback to be run straight away
94
+ this.internalStateUpdate(value)
95
+ })
96
+
97
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
98
+ if (!platform.config.disableNoResponse) {
99
+ this.service.getCharacteristic(this.hapChar.CurrentDoorState).onGet(() => {
100
+ if (!this.isOnline) {
101
+ throw new this.hapErr(-70402)
102
+ }
103
+ return this.service.getCharacteristic(this.hapChar.CurrentDoorState).value
104
+ })
105
+ this.service.getCharacteristic(this.hapChar.TargetDoorState).onGet(() => {
106
+ if (!this.isOnline) {
107
+ throw new this.hapErr(-70402)
108
+ }
109
+ return this.service.getCharacteristic(this.hapChar.TargetDoorState).value
110
+ })
111
+ }
112
+
113
+ // Update the obstruction detected to false on Homebridge restart
114
+ this.service.updateCharacteristic(this.hapChar.ObstructionDetected, false)
115
+
116
+ // Pass the accessory to Fakegato to set up with Eve
117
+ this.accessory.eveService = new platform.eveService('door', this.accessory, {
118
+ log: () => {},
119
+ })
120
+ this.accessory.eveService.addEntry({
121
+ status: this.service.getCharacteristic(this.hapChar.CurrentDoorState).value === 0 ? 0 : 1,
122
+ })
123
+
124
+ // Output the customised options to the log
125
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
126
+ const opts = JSON.stringify({
127
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
128
+ operationTime: this.operationTime,
129
+ showAs: 'garage_eachen',
130
+ })
131
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
132
+ }
133
+
134
+ async internalStateUpdate(value) {
135
+ try {
136
+ const params = { switch: value === 0 ? 'on' : 'off' }
137
+ const curState = this.service.getCharacteristic(this.hapChar.CurrentDoorState).value
138
+ if (value === curState % 2) {
139
+ return
140
+ }
141
+ this.inUse = true
142
+ await this.platform.sendDeviceUpdate(this.accessory, params)
143
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, value)
144
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, value + 2)
145
+ await sleep(3000)
146
+ this.inUse = false
147
+ if (value === 0) {
148
+ this.accessory.eveService.addEntry({ status: 0 })
149
+ this.service.updateCharacteristic(
150
+ this.eveChar.LastActivation,
151
+ Math.round(new Date().valueOf() / 1000) - this.accessory.eveService.getInitialTime(),
152
+ )
153
+ this.service.updateCharacteristic(
154
+ this.eveChar.TimesOpened,
155
+ this.service.getCharacteristic(this.eveChar.TimesOpened).value + 1,
156
+ )
157
+ await sleep(Math.max((this.operationTime - 30) * 100, 0))
158
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, 0)
159
+ if (this.enableLogging) {
160
+ this.log('[%s] %s [%s].', this.name, this.lang.curState, this.lang.doorOpen)
161
+ }
162
+ }
163
+ } catch (err) {
164
+ this.inUse = false
165
+ this.platform.deviceUpdateError(this.accessory, err, true)
166
+ setTimeout(() => {
167
+ this.service.updateCharacteristic(
168
+ this.hapChar.TargetDoorState,
169
+ this.accessory.context.cacheTargetDoorState,
170
+ )
171
+ }, 2000)
172
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, new this.hapErr(-70402))
173
+ }
174
+ }
175
+
176
+ async externalUpdate(params) {
177
+ try {
178
+ if (!params.switch || this.inUse) {
179
+ return
180
+ }
181
+
182
+ // In a period of three seconds we want to only use the 'last' update
183
+ const updateKey = generateRandomString(5)
184
+ this.updateKey = updateKey
185
+ await sleep(3000)
186
+ if (this.updateKey !== updateKey) {
187
+ return
188
+ }
189
+
190
+ // Update the characteristics
191
+ const newValue = params.switch === 'on' ? 0 : 1
192
+ if (this.service.getCharacteristic(this.hapChar.TargetDoorState).value !== newValue) {
193
+ this.service.updateCharacteristic(this.hapChar.TargetDoorState, newValue)
194
+ }
195
+ if (this.service.getCharacteristic(this.hapChar.CurrentDoorState).value !== newValue) {
196
+ this.service.updateCharacteristic(this.hapChar.CurrentDoorState, newValue)
197
+ }
198
+ this.accessory.eveService.addEntry({ status: newValue })
199
+ if (newValue === 0) {
200
+ this.service.updateCharacteristic(
201
+ this.eveChar.LastActivation,
202
+ Math.round(new Date().valueOf() / 1000) - this.accessory.eveService.getInitialTime(),
203
+ )
204
+ this.service.updateCharacteristic(
205
+ this.eveChar.TimesOpened,
206
+ this.service.getCharacteristic(this.eveChar.TimesOpened).value + 1,
207
+ )
208
+ }
209
+ if (params.updateSource && this.enableLogging) {
210
+ this.log(
211
+ '[%s] %s [%s].',
212
+ this.name,
213
+ this.lang.curState,
214
+ params.switch === 'on' ? this.lang.doorOpen : this.lang.doorClosed,
215
+ )
216
+ }
217
+ } catch (err) {
218
+ this.platform.deviceUpdateError(this.accessory, err, false)
219
+ }
220
+ }
221
+
222
+ currentState() {
223
+ return {
224
+ services: ['garage'],
225
+ garage: {
226
+ target: this.service.getCharacteristic(this.hapChar.TargetDoorState).value,
227
+ state: this.service.getCharacteristic(this.hapChar.CurrentDoorState).value,
228
+ },
229
+ }
230
+ }
231
+
232
+ markStatus(isOnline) {
233
+ this.isOnline = isOnline
234
+ }
235
+ }
@@ -0,0 +1,176 @@
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.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.operationTimeUp = deviceConf.operationTime || platformConsts.defaultValues.operationTime
24
+ this.operationTimeDown = deviceConf.operationTimeDown || this.operationTimeUp
25
+
26
+ // Set the correct logging variables for this accessory
27
+ switch (deviceConf.overrideLogging) {
28
+ case 'standard':
29
+ this.enableLogging = true
30
+ this.enableDebugLogging = false
31
+ break
32
+ case 'debug':
33
+ this.enableLogging = true
34
+ this.enableDebugLogging = true
35
+ break
36
+ case 'disable':
37
+ this.enableLogging = false
38
+ this.enableDebugLogging = false
39
+ break
40
+ default:
41
+ this.enableLogging = !platform.config.disableDeviceLogging
42
+ this.enableDebugLogging = platform.config.debug
43
+ break
44
+ }
45
+
46
+ // Set up the accessory with default positions when added the first time
47
+ if (!hasProperty(this.accessory.context, 'cacheStates')) {
48
+ this.accessory.context.cacheStates = [
49
+ { cacheCurrentDoorState: 1, cacheTargetDoorState: 1 },
50
+ { cacheCurrentDoorState: 1, cacheTargetDoorState: 1 },
51
+ { cacheCurrentDoorState: 1, cacheTargetDoorState: 1 },
52
+ { cacheCurrentDoorState: 1, cacheTargetDoorState: 1 },
53
+ ]
54
+ }
55
+
56
+ // We want four garage door services for this accessory
57
+ ['A', 'B', 'C', 'D'].forEach((v) => {
58
+ // Add the garage door service if it doesn't already exist
59
+ let gdService = this.accessory.getService(`Garage ${v}`)
60
+ if (!gdService) {
61
+ gdService = this.accessory.addService(this.hapServ.GarageDoorOpener, `Garage ${v}`, `garage${v}`)
62
+ gdService.updateCharacteristic(this.hapChar.CurrentDoorState, 1)
63
+ gdService.updateCharacteristic(this.hapChar.TargetDoorState, 1)
64
+ gdService.updateCharacteristic(this.hapChar.ObstructionDetected, false)
65
+ }
66
+
67
+ // Add the set handler to the target position characteristic
68
+ gdService.getCharacteristic(this.hapChar.TargetDoorState).onSet((value) => {
69
+ // We don't use await as we want the callback to be run straight away
70
+ this.internalStateUpdate(v, value)
71
+ })
72
+
73
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
74
+ if (!platform.config.disableNoResponse) {
75
+ gdService.getCharacteristic(this.hapChar.CurrentDoorState).onGet(() => {
76
+ if (!this.isOnline) {
77
+ throw new this.hapErr(-70402)
78
+ }
79
+ return gdService.getCharacteristic(this.hapChar.CurrentDoorState).value
80
+ })
81
+ gdService.getCharacteristic(this.hapChar.TargetDoorState).onGet(() => {
82
+ if (!this.isOnline) {
83
+ throw new this.hapErr(-70402)
84
+ }
85
+ return gdService.getCharacteristic(this.hapChar.TargetDoorState).value
86
+ })
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
+ operationTimeDown: this.operationTimeDown,
95
+ operationTimeUp: this.operationTimeUp,
96
+ showAs: 'garage_four',
97
+ })
98
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
99
+ }
100
+
101
+ async internalStateUpdate(garage, value) {
102
+ try {
103
+ let garageChannel
104
+ switch (garage) {
105
+ case 'A':
106
+ garageChannel = 0
107
+ break
108
+ case 'B':
109
+ garageChannel = 1
110
+ break
111
+ case 'C':
112
+ garageChannel = 2
113
+ break
114
+ case 'D':
115
+ garageChannel = 3
116
+ break
117
+ default:
118
+ return
119
+ }
120
+ const prevState = this.accessory.context.cacheStates[garageChannel].cacheCurrentDoorState
121
+ if (value === prevState % 2) {
122
+ return
123
+ }
124
+ const gdService = this.accessory.getService(`Garage ${garage}`)
125
+ this.inUse = true
126
+ const updateKey = generateRandomString(5)
127
+ this.updateKey = updateKey
128
+ gdService.updateCharacteristic(this.hapChar.TargetDoorState, value)
129
+ gdService.updateCharacteristic(this.hapChar.CurrentDoorState, value + 2)
130
+ this.accessory.context.cacheStates[garageChannel].cacheTargetDoorState = value
131
+ this.accessory.context.cacheStates[garageChannel].cacheCurrentDoorState = value + 2
132
+ const params = {
133
+ switches: [
134
+ {
135
+ switch: 'on',
136
+ outlet: garageChannel,
137
+ },
138
+ ],
139
+ }
140
+ await this.platform.sendDeviceUpdate(this.accessory, params)
141
+ await sleep(2000)
142
+ this.inUse = false
143
+ const operationTime = value === 0 ? this.operationTimeUp : this.operationTimeDown
144
+ await sleep(Math.max((operationTime - 20) * 100, 0))
145
+ if (this.updateKey !== updateKey) {
146
+ return
147
+ }
148
+ gdService.updateCharacteristic(this.hapChar.CurrentDoorState, value)
149
+ this.accessory.context.cacheStates[garageChannel].cacheCurrentDoorState = value
150
+ if (this.enableLogging) {
151
+ this.log(
152
+ '[%s] %s [garage %s %s].',
153
+ this.name,
154
+ this.lang.curState,
155
+ garageChannel,
156
+ value === 0 ? this.lang.doorOpen : this.lang.doorClosed,
157
+ )
158
+ }
159
+ } catch (err) {
160
+ this.inUse = false
161
+ this.platform.deviceUpdateError(this.accessory, err, true)
162
+ const gdService = this.accessory.getService(`Garage ${garage}`)
163
+ setTimeout(() => {
164
+ gdService.updateCharacteristic(
165
+ this.hapChar.TargetDoorState,
166
+ this.accessory.context.cacheTargetDoorState,
167
+ )
168
+ }, 2000)
169
+ gdService.updateCharacteristic(this.hapChar.TargetDoorState, new this.hapErr(-70402))
170
+ }
171
+ }
172
+
173
+ markStatus(isOnline) {
174
+ this.isOnline = isOnline
175
+ }
176
+ }
@@ -0,0 +1,36 @@
1
+ export default class {
2
+ constructor(platform, accessory, devicesInHB) {
3
+ // Set up variables from the platform
4
+ this.hapChar = platform.api.hap.Characteristic
5
+ this.hapServ = platform.api.hap.Service
6
+ this.platform = platform
7
+
8
+ // Set up variables from the accessory
9
+ this.accessory = accessory
10
+
11
+ // Set up custom variables for this device type
12
+ const garageId = platform.obstructSwitches[accessory.context.eweDeviceId]
13
+
14
+ // Check the garage door exists in Homebridge
15
+ const uuid = platform.api.hap.uuid.generate(`${garageId}SWX`)
16
+ if (!devicesInHB.has(uuid)) {
17
+ // Can't find garage so throw an error
18
+ throw new Error(platform.lang.noGarageForOD)
19
+ } else {
20
+ // Get the garage door accessory
21
+ this.gService = devicesInHB.get(uuid).getService(this.hapServ.GarageDoorOpener)
22
+ }
23
+ }
24
+
25
+ async externalUpdate(params) {
26
+ try {
27
+ if (!params.switch || params.switch === this.cacheState || !this.gService) {
28
+ return
29
+ }
30
+ this.gService.updateCharacteristic(this.hapChar.ObstructionDetected, params.switch === 'on')
31
+ this.cacheState = params.switch
32
+ } catch (err) {
33
+ this.platform.deviceUpdateError(this.accessory, err, false)
34
+ }
35
+ }
36
+ }