@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,289 @@
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
+ // If the accessory has an outlet service then remove it
51
+ if (this.accessory.getService(this.hapServ.Outlet)) {
52
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Outlet))
53
+ }
54
+
55
+ // Add the air purifier service if it doesn't already exist
56
+ this.service = this.accessory.getService(this.hapServ.AirPurifier)
57
+ || this.accessory.addService(this.hapServ.AirPurifier)
58
+
59
+ // Set up the device type and power readings if necessary
60
+ if (platformConsts.devices.switchSingle.includes(this.accessory.context.eweUIID)) {
61
+ this.setup = 'switchSingle'
62
+ } else if (platformConsts.devices.switchSinglePower.includes(this.accessory.context.eweUIID)) {
63
+ this.setup = 'switchSingle'
64
+
65
+ // Add Eve power characteristics
66
+ this.powerReadings = true
67
+ if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
68
+ this.service.addCharacteristic(this.eveChar.CurrentConsumption)
69
+ }
70
+ if (this.accessory.context.eweUIID === 32) {
71
+ if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
72
+ this.service.addCharacteristic(this.eveChar.ElectricCurrent)
73
+ }
74
+ if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
75
+ this.service.addCharacteristic(this.eveChar.Voltage)
76
+ }
77
+ }
78
+ } else if (platformConsts.devices.switchMulti.includes(this.accessory.context.eweUIID)) {
79
+ this.setup = 'switchMulti'
80
+ if (platformConsts.devices.switchMultiPower.includes(this.accessory.context.eweUIID)) {
81
+ // Add Eve power characteristics
82
+ this.powerReadings = true
83
+ if (!this.service.testCharacteristic(this.eveChar.CurrentConsumption)) {
84
+ this.service.addCharacteristic(this.eveChar.CurrentConsumption)
85
+ }
86
+ if (!this.service.testCharacteristic(this.eveChar.ElectricCurrent)) {
87
+ this.service.addCharacteristic(this.eveChar.ElectricCurrent)
88
+ }
89
+ if (!this.service.testCharacteristic(this.eveChar.Voltage)) {
90
+ this.service.addCharacteristic(this.eveChar.Voltage)
91
+ }
92
+ this.isDualR3 = true
93
+ }
94
+ } else if (platformConsts.devices.switchSCM.includes(this.accessory.context.eweUIID)) {
95
+ this.setup = 'switchMulti'
96
+ } else if (platformConsts.devices.switchSCMPower.includes(this.accessory.context.eweUIID)) {
97
+ // Could do power here
98
+ this.setup = 'switchMulti'
99
+ }
100
+
101
+ // Add the set handler to the purifier active characteristic
102
+ this.service
103
+ .getCharacteristic(this.hapChar.Active)
104
+ .onSet(async value => this.internalStateUpdate(value))
105
+
106
+ // Add options to the purifier target state characteristic
107
+ this.service
108
+ .getCharacteristic(this.hapChar.TargetAirPurifierState)
109
+ .setProps({
110
+ minValue: 1,
111
+ maxValue: 1,
112
+ validValues: [1],
113
+ })
114
+ .updateValue(1)
115
+
116
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
117
+ if (!platform.config.disableNoResponse) {
118
+ this.service.getCharacteristic(this.hapChar.Active).onGet(() => {
119
+ if (!this.isOnline) {
120
+ throw new this.hapErr(-70402)
121
+ }
122
+ return this.service.getCharacteristic(this.hapChar.Active).value
123
+ })
124
+ this.service.getCharacteristic(this.hapChar.CurrentAirPurifierState).onGet(() => {
125
+ if (!this.isOnline) {
126
+ throw new this.hapErr(-70402)
127
+ }
128
+ return this.service.getCharacteristic(this.hapChar.CurrentAirPurifierState).value
129
+ })
130
+ this.service.getCharacteristic(this.hapChar.TargetAirPurifierState).onGet(() => {
131
+ if (!this.isOnline) {
132
+ throw new this.hapErr(-70402)
133
+ }
134
+ return this.service.getCharacteristic(this.hapChar.TargetAirPurifierState).value
135
+ })
136
+ }
137
+
138
+ // Set up an interval to get eWeLink to send power updates
139
+ if (
140
+ this.powerReadings
141
+ && (!this.isDualR3 || (this.isDualR3 && platform.config.mode !== 'lan'))
142
+ ) {
143
+ setTimeout(() => {
144
+ this.internalUIUpdate()
145
+ this.intervalPoll = setInterval(() => this.internalUIUpdate(), 120000)
146
+ }, 5000)
147
+
148
+ // Stop the intervals on Homebridge shutdown
149
+ platform.api.on('shutdown', () => clearInterval(this.intervalPoll))
150
+ }
151
+
152
+ // Output the customised options to the log
153
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
154
+ const opts = JSON.stringify({
155
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
156
+ showAs: 'purifier',
157
+ })
158
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
159
+ }
160
+
161
+ async internalStateUpdate(value) {
162
+ try {
163
+ const params = {}
164
+ switch (this.setup) {
165
+ case 'switchSingle':
166
+ params.switch = value === 1 ? 'on' : 'off'
167
+ break
168
+ case 'switchMulti':
169
+ params.switches = [
170
+ {
171
+ switch: value === 1 ? 'on' : 'off',
172
+ outlet: 0,
173
+ },
174
+ ]
175
+ break
176
+ default:
177
+ return
178
+ }
179
+ await this.platform.sendDeviceUpdate(this.accessory, params)
180
+ this.cacheState = value === 1 ? 'on' : 'off'
181
+ this.service.updateCharacteristic(this.hapChar.CurrentAirPurifierState, value === 1 ? 2 : 0)
182
+ if (this.enableLogging) {
183
+ this.log('[%s] %s [%s].', this.name, this.lang.curPur, this.cacheState)
184
+ }
185
+ } catch (err) {
186
+ this.platform.deviceUpdateError(this.accessory, err, true)
187
+ setTimeout(() => {
188
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
189
+ }, 2000)
190
+ throw new this.hapErr(-70402)
191
+ }
192
+ }
193
+
194
+ async internalUIUpdate() {
195
+ try {
196
+ // Skip polling if device isn't online
197
+ if (!this.isOnline) {
198
+ return
199
+ }
200
+
201
+ // Send the params to request the updates
202
+ if (this.isDualR3) {
203
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: { outlet: 0, time: 120 } })
204
+ } else {
205
+ await this.platform.sendDeviceUpdate(this.accessory, { uiActive: 120 })
206
+ }
207
+ } catch (err) {
208
+ // Suppress errors here
209
+ }
210
+ }
211
+
212
+ async externalUpdate(params) {
213
+ try {
214
+ if (
215
+ (this.setup === 'switchMulti' && params.switches)
216
+ || (this.setup === 'switchSingle' && params.switch)
217
+ ) {
218
+ let newState
219
+ if (this.setup === 'switchMulti' && params.switches) {
220
+ newState = params.switches[0].switch
221
+ } else if (this.setup === 'switchSingle' && params.switch) {
222
+ newState = params.switch
223
+ }
224
+ if (newState && newState !== this.cacheState) {
225
+ this.cacheState = newState
226
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState === 'on' ? 1 : 0)
227
+ this.service.updateCharacteristic(
228
+ this.hapChar.CurrentAirPurifierState,
229
+ this.cacheState === 'on' ? 2 : 0,
230
+ )
231
+ if (params.updateSource && this.enableLogging) {
232
+ this.log('[%s] %s [%s].', this.name, this.lang.curPur, this.cacheState)
233
+ }
234
+ }
235
+ }
236
+
237
+ // Get the power readings given by certain devices
238
+ if (!this.powerReadings) {
239
+ return
240
+ }
241
+ let logger = false
242
+ let power
243
+ let voltage
244
+ let current
245
+ if (hasProperty(params, 'actPow_00')) {
246
+ power = Number.parseInt(params.actPow_00, 10) / 100
247
+ this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
248
+ logger = true
249
+ } else if (hasProperty(params, 'power')) {
250
+ power = Number.parseFloat(params.power)
251
+ this.service.updateCharacteristic(this.eveChar.CurrentConsumption, power)
252
+ logger = true
253
+ }
254
+ if (hasProperty(params, 'voltage_00')) {
255
+ voltage = Number.parseInt(params.voltage_00, 10) / 100
256
+ this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
257
+ logger = true
258
+ } else if (hasProperty(params, 'voltage')) {
259
+ voltage = Number.parseFloat(params.voltage)
260
+ this.service.updateCharacteristic(this.eveChar.Voltage, voltage)
261
+ logger = true
262
+ }
263
+ if (hasProperty(params, 'current_00')) {
264
+ current = Number.parseInt(params.current_00, 10) / 100
265
+ this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
266
+ logger = true
267
+ } else if (hasProperty(params, 'current')) {
268
+ current = Number.parseFloat(params.current)
269
+ this.service.updateCharacteristic(this.eveChar.ElectricCurrent, current)
270
+ logger = true
271
+ }
272
+ if (params.updateSource && logger && this.enableLogging) {
273
+ this.log(
274
+ '[%s] %s%s%s.',
275
+ this.name,
276
+ power !== undefined ? `${this.lang.curPower} [${power}W]` : '',
277
+ voltage !== undefined ? ` ${this.lang.curVolt} [${voltage}V]` : '',
278
+ current !== undefined ? ` ${this.lang.curCurr} [${current}A]` : '',
279
+ )
280
+ }
281
+ } catch (err) {
282
+ this.platform.deviceUpdateError(this.accessory, err, false)
283
+ }
284
+ }
285
+
286
+ markStatus(isOnline) {
287
+ this.isOnline = isOnline
288
+ }
289
+ }
@@ -0,0 +1,192 @@
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
+ // Set up custom variables for this device type
23
+ const deviceConf = platform.rfSubdevices[accessory.context.hbDeviceId] || {}
24
+ this.operationTimeUp = deviceConf.operationTime || platformConsts.defaultValues.operationTime
25
+ this.operationTimeDown = deviceConf.operationTimeDown || this.operationTimeUp;
26
+ [this.chOpen, this.chStop, this.chClose] = Object.keys(this.accessory.context.buttons)
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
+ // If the accessory has a door service (from an old simulation) then remove it
49
+ if (this.accessory.getService(this.hapServ.Door)) {
50
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Door))
51
+ }
52
+
53
+ // If the accessory has a window service (from an old simulation) then remove it
54
+ if (this.accessory.getService(this.hapServ.Window)) {
55
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Window))
56
+ }
57
+
58
+ // Remove old switch services from before the simulation was configured
59
+ this.accessory.services
60
+ .filter(el => el.constructor.name === 'Switch')
61
+ .forEach(el => this.accessory.removeService(el))
62
+
63
+ // Set up the accessory with default positions when added the first time
64
+ if (!hasProperty(this.accessory.context, 'cacheCurrentPosition')) {
65
+ this.accessory.context.cacheCurrentPosition = 0
66
+ this.accessory.context.cachePositionState = 2
67
+ this.accessory.context.cacheTargetPosition = 0
68
+ }
69
+
70
+ // Add the window covering service if it doesn't already exist
71
+ this.service = this.accessory.getService(this.hapServ.WindowCovering)
72
+ if (!this.service) {
73
+ this.service = this.accessory.addService(this.hapServ.WindowCovering)
74
+ this.service.updateCharacteristic(this.hapChar.CurrentPosition, 0)
75
+ this.service.updateCharacteristic(this.hapChar.TargetPosition, 0)
76
+ this.service.updateCharacteristic(this.hapChar.PositionState, 2)
77
+ }
78
+
79
+ // Add the set handler to the target position characteristic
80
+ this.service.getCharacteristic(this.hapChar.TargetPosition).onSet((value) => {
81
+ // We don't use await as we want the callback to be run straight away
82
+ this.internalPositionUpdate(value)
83
+ })
84
+
85
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
86
+ if (!platform.config.disableNoResponse) {
87
+ this.service.getCharacteristic(this.hapChar.CurrentPosition).onGet(() => {
88
+ if (!this.isOnline) {
89
+ throw new this.hapErr(-70402)
90
+ }
91
+ return this.service.getCharacteristic(this.hapChar.CurrentPosition).value
92
+ })
93
+ this.service.getCharacteristic(this.hapChar.TargetPosition).onGet(() => {
94
+ if (!this.isOnline) {
95
+ throw new this.hapErr(-70402)
96
+ }
97
+ return this.service.getCharacteristic(this.hapChar.TargetPosition).value
98
+ })
99
+ }
100
+
101
+ // Output the customised options to the log
102
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
103
+ const opts = JSON.stringify({
104
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
105
+ operationTime: this.operationTimeUp,
106
+ operationTimeDown: this.operationTimeDown,
107
+ })
108
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
109
+ }
110
+
111
+ async internalPositionUpdate(value) {
112
+ try {
113
+ let prevPosition = this.accessory.context.cacheCurrentPosition
114
+ if (value === prevPosition) {
115
+ return
116
+ }
117
+ const params = { cmd: 'transmit' }
118
+ const prevState = this.accessory.context.cachePositionState
119
+ const percentStepUpPerDS = this.operationTimeUp / 100
120
+ const percentStepDownPerDS = this.operationTimeDown / 100
121
+ const updateKey = generateRandomString(5)
122
+ this.updateKey = updateKey
123
+ if (prevState !== 2) {
124
+ const posPercentChange = Math.floor(Date.now() / 100) - this.accessory.context.cacheLastStartTime
125
+ const posPercentChangeUp = Math.floor(percentStepUpPerDS * posPercentChange)
126
+ const posPercentChangeDown = Math.floor(percentStepDownPerDS * posPercentChange)
127
+ if (prevState === 0) {
128
+ // Was going down
129
+ prevPosition -= posPercentChangeDown
130
+ } else {
131
+ // Was going up
132
+ prevPosition += posPercentChangeUp
133
+ }
134
+ prevPosition = Math.min(Math.max(prevPosition, 0), 100)
135
+ this.service.updateCharacteristic(this.hapChar.CurrentPosition, prevPosition)
136
+ this.accessory.context.cacheCurrentPosition = prevPosition
137
+ }
138
+ const diffPosition = value - prevPosition
139
+ const setToMoveUp = diffPosition > 0
140
+ let decisecondsToMove
141
+ if (setToMoveUp) {
142
+ decisecondsToMove = Math.round(Math.abs(diffPosition) * percentStepUpPerDS)
143
+ params.rfChl = Number.parseInt(this.chOpen, 10)
144
+ } else {
145
+ decisecondsToMove = Math.round(Math.abs(diffPosition) * percentStepDownPerDS)
146
+ params.rfChl = Number.parseInt(this.chClose, 10)
147
+ }
148
+
149
+ // temp extra debug logging
150
+ this.log(
151
+ '[%s] from %s% to %s% - moving %s for %s seconds',
152
+ this.name,
153
+ prevPosition,
154
+ value,
155
+ setToMoveUp ? 'up' : 'down',
156
+ decisecondsToMove / 10,
157
+ )
158
+
159
+ // Send the update
160
+ await this.platform.sendDeviceUpdate(this.accessory, params)
161
+ this.accessory.context.cacheTargetPosition = value
162
+ this.accessory.context.cachePositionState = setToMoveUp ? 1 : 0
163
+ this.accessory.context.cacheLastStartTime = Math.floor(Date.now() / 100)
164
+ await sleep(decisecondsToMove * 100)
165
+ if (this.updateKey !== updateKey) {
166
+ return
167
+ }
168
+ params.rfChl = Number.parseInt(this.chStop, 10)
169
+ await this.platform.sendDeviceUpdate(this.accessory, params)
170
+ this.service.updateCharacteristic(this.hapChar.PositionState, 2)
171
+ this.service.updateCharacteristic(this.hapChar.CurrentPosition, value)
172
+ this.accessory.context.cachePositionState = 2
173
+ this.accessory.context.cacheCurrentPosition = value
174
+ if (this.enableLogging) {
175
+ this.log('[%s] %s [%s%]', this.name, this.lang.curPos, value)
176
+ }
177
+ } catch (err) {
178
+ this.platform.deviceUpdateError(this.accessory, err, true)
179
+ setTimeout(() => {
180
+ this.service.updateCharacteristic(
181
+ this.hapChar.TargetPosition,
182
+ this.accessory.context.cacheTargetPosition,
183
+ )
184
+ }, 2000)
185
+ this.service.updateCharacteristic(this.hapChar.TargetPosition, new this.hapErr(-70402))
186
+ }
187
+ }
188
+
189
+ markStatus(isOnline) {
190
+ this.isOnline = isOnline
191
+ }
192
+ }
@@ -0,0 +1,180 @@
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
+ // Set up custom variables for this device type
23
+ const deviceConf = platform.rfSubdevices[accessory.context.hbDeviceId] || {}
24
+ this.operationTimeUp = deviceConf.operationTime || platformConsts.defaultValues.operationTime
25
+ this.operationTimeDown = deviceConf.operationTimeDown || this.operationTimeUp;
26
+ [this.chOpen, this.chStop, this.chClose] = Object.keys(this.accessory.context.buttons)
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
+ // If the accessory has a window covering service (from an old simulation) then remove it
49
+ if (this.accessory.getService(this.hapServ.WindowCovering)) {
50
+ this.accessory.removeService(this.accessory.getService(this.hapServ.WindowCovering))
51
+ }
52
+
53
+ // If the accessory has a window service (from an old simulation) then remove it
54
+ if (this.accessory.getService(this.hapServ.Window)) {
55
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Window))
56
+ }
57
+
58
+ // Remove old switch services from before the simulation was configured
59
+ this.accessory.services
60
+ .filter(el => el.constructor.name === 'Switch')
61
+ .forEach(el => this.accessory.removeService(el))
62
+
63
+ // Set up the accessory with default positions when added the first time
64
+ if (!hasProperty(this.accessory.context, 'cacheCurrentPosition')) {
65
+ this.accessory.context.cacheCurrentPosition = 0
66
+ this.accessory.context.cachePositionState = 2
67
+ this.accessory.context.cacheTargetPosition = 0
68
+ }
69
+
70
+ // Add the door service if it doesn't already exist
71
+ this.service = this.accessory.getService(this.hapServ.Door)
72
+ if (!this.service) {
73
+ this.service = this.accessory.addService(this.hapServ.Door)
74
+ this.service.updateCharacteristic(this.hapChar.CurrentPosition, 0)
75
+ this.service.updateCharacteristic(this.hapChar.TargetPosition, 0)
76
+ this.service.updateCharacteristic(this.hapChar.PositionState, 2)
77
+ }
78
+
79
+ // Add the set handler to the target position characteristic
80
+ this.service.getCharacteristic(this.hapChar.TargetPosition).onSet((value) => {
81
+ // We don't use await as we want the callback to be run straight away
82
+ this.internalPositionUpdate(value)
83
+ })
84
+
85
+ // Add the get handlers only if the user hasn't disabled the disableNoResponse setting
86
+ if (!platform.config.disableNoResponse) {
87
+ this.service.getCharacteristic(this.hapChar.CurrentPosition).onGet(() => {
88
+ if (!this.isOnline) {
89
+ throw new this.hapErr(-70402)
90
+ }
91
+ return this.service.getCharacteristic(this.hapChar.CurrentPosition).value
92
+ })
93
+ this.service.getCharacteristic(this.hapChar.TargetPosition).onGet(() => {
94
+ if (!this.isOnline) {
95
+ throw new this.hapErr(-70402)
96
+ }
97
+ return this.service.getCharacteristic(this.hapChar.TargetPosition).value
98
+ })
99
+ }
100
+
101
+ // Output the customised options to the log
102
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
103
+ const opts = JSON.stringify({
104
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
105
+ operationTime: this.operationTimeUp,
106
+ operationTimeDown: this.operationTimeDown,
107
+ })
108
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
109
+ }
110
+
111
+ async internalPositionUpdate(value) {
112
+ try {
113
+ let prevPosition = this.accessory.context.cacheCurrentPosition
114
+ if (value === prevPosition) {
115
+ return
116
+ }
117
+ const params = { cmd: 'transmit' }
118
+ const prevState = this.accessory.context.cachePositionState
119
+ const percentStepUpPerDS = this.operationTimeUp / 100
120
+ const percentStepDownPerDS = this.operationTimeDown / 100
121
+ const updateKey = generateRandomString(5)
122
+ this.updateKey = updateKey
123
+ if (prevState !== 2) {
124
+ const posPercentChange = Math.floor(Date.now() / 100) - this.accessory.context.cacheLastStartTime
125
+ const posPercentChangeUp = Math.floor(percentStepUpPerDS * posPercentChange)
126
+ const posPercentChangeDown = Math.floor(percentStepDownPerDS * posPercentChange)
127
+ if (prevState === 0) {
128
+ // Was going down
129
+ prevPosition -= posPercentChangeDown
130
+ } else {
131
+ // Was going up
132
+ prevPosition += posPercentChangeUp
133
+ }
134
+ prevPosition = Math.min(Math.max(prevPosition, 0), 100)
135
+ this.service.updateCharacteristic(this.hapChar.CurrentPosition, prevPosition)
136
+ this.accessory.context.cacheCurrentPosition = prevPosition
137
+ }
138
+ const diffPosition = value - prevPosition
139
+ const setToMoveUp = diffPosition > 0
140
+ let decisecondsToMove
141
+ if (setToMoveUp) {
142
+ decisecondsToMove = Math.round(Math.abs(diffPosition) * percentStepUpPerDS)
143
+ params.rfChl = Number.parseInt(this.chOpen, 10)
144
+ } else {
145
+ decisecondsToMove = Math.round(Math.abs(diffPosition) * percentStepDownPerDS)
146
+ params.rfChl = Number.parseInt(this.chClose, 10)
147
+ }
148
+ await this.platform.sendDeviceUpdate(this.accessory, params)
149
+ this.accessory.context.cacheTargetPosition = value
150
+ this.accessory.context.cachePositionState = setToMoveUp ? 1 : 0
151
+ this.accessory.context.cacheLastStartTime = Math.floor(Date.now() / 100)
152
+ await sleep(decisecondsToMove * 100)
153
+ if (this.updateKey !== updateKey) {
154
+ return
155
+ }
156
+ params.rfChl = Number.parseInt(this.chStop, 10)
157
+ await this.platform.sendDeviceUpdate(this.accessory, params)
158
+ this.service.updateCharacteristic(this.hapChar.PositionState, 2)
159
+ this.service.updateCharacteristic(this.hapChar.CurrentPosition, value)
160
+ this.accessory.context.cachePositionState = 2
161
+ this.accessory.context.cacheCurrentPosition = value
162
+ if (this.enableLogging) {
163
+ this.log('[%s] %s [%s%]', this.name, this.lang.curPos, value)
164
+ }
165
+ } catch (err) {
166
+ this.platform.deviceUpdateError(this.accessory, err, true)
167
+ setTimeout(() => {
168
+ this.service.updateCharacteristic(
169
+ this.hapChar.TargetPosition,
170
+ this.accessory.context.cacheTargetPosition,
171
+ )
172
+ }, 2000)
173
+ this.service.updateCharacteristic(this.hapChar.TargetPosition, new this.hapErr(-70402))
174
+ }
175
+ }
176
+
177
+ markStatus(isOnline) {
178
+ this.isOnline = isOnline
179
+ }
180
+ }