@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,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 door service (from an old simulation) then remove it
54
+ if (this.accessory.getService(this.hapServ.Door)) {
55
+ this.accessory.removeService(this.accessory.getService(this.hapServ.Door))
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 service if it doesn't already exist
71
+ this.service = this.accessory.getService(this.hapServ.Window)
72
+ if (!(this.service)) {
73
+ this.service = this.accessory.addService(this.hapServ.Window)
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
+ }
@@ -0,0 +1,105 @@
1
+ import platformConsts from '../../utils/constants.js'
2
+ import { hasProperty, sleep } from '../../utils/functions.js'
3
+
4
+ export default class {
5
+ constructor(platform, accessory, subAccessory) {
6
+ // Set up variables from the platform
7
+ this.eveChar = platform.eveChar
8
+ this.hapChar = platform.api.hap.Characteristic
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.accessory = accessory
16
+ this.subAccessory = subAccessory
17
+
18
+ // Set up custom variables for this device type
19
+ const group = platform.deviceConf[subAccessory.context.eweDeviceId] || {}
20
+ this.isGarage = group.showAs === 'garage'
21
+ this.operationTime = group.operationTime || platformConsts.defaultValues.operationTime
22
+
23
+ // Set the correct logging variables for this accessory
24
+ switch (group.overrideLogging) {
25
+ case 'standard':
26
+ case 'debug':
27
+ this.enableLogging = true
28
+ break
29
+ case 'disable':
30
+ this.enableLogging = false
31
+ break
32
+ default:
33
+ this.enableLogging = !platform.config.disableDeviceLogging
34
+ break
35
+ }
36
+ }
37
+
38
+ async externalUpdate(params) {
39
+ try {
40
+ if (!hasProperty(params, 'lock') && !params.switch) {
41
+ return
42
+ }
43
+ let newState
44
+ if (params.switch) {
45
+ newState = params.switch === 'on' ? 1 : 0
46
+ } else {
47
+ newState = params.lock
48
+ }
49
+ if (newState === this.cacheState) {
50
+ return
51
+ }
52
+ this.cacheState = newState
53
+ const subService = this.subAccessory.getService(
54
+ this.isGarage ? this.hapServ.GarageDoorOpener : this.hapServ.LockMechanism,
55
+ )
56
+ const name = this.subAccessory.displayName
57
+ switch (newState) {
58
+ case 0:
59
+ if (this.isGarage) {
60
+ subService.updateCharacteristic(this.hapChar.TargetDoorState, 1)
61
+ subService.updateCharacteristic(this.hapChar.CurrentDoorState, 1)
62
+ this.subAccessory.eveService.addEntry({ status: 1 })
63
+ if (params.updateSource && this.enableLogging) {
64
+ this.log('[%s] %s [%s].', name, this.lang.curState, this.lang.doorClosed)
65
+ }
66
+ } else {
67
+ subService.updateCharacteristic(this.hapChar.LockTargetState, 1)
68
+ subService.updateCharacteristic(this.hapChar.LockCurrentState, 1)
69
+ if (params.updateSource && this.enableLogging) {
70
+ this.log('[%s] %s [%s].', name, this.lang.curState, this.lang.lockLocked)
71
+ }
72
+ }
73
+ break
74
+ case 1: {
75
+ if (this.isGarage) {
76
+ await sleep(Math.max(this.operationTime * 100, 2000))
77
+ subService.updateCharacteristic(this.hapChar.TargetDoorState, 0)
78
+ subService.updateCharacteristic(this.hapChar.CurrentDoorState, 0)
79
+ this.subAccessory.eveService.addEntry({ status: 0 })
80
+ const initialTime = this.subAccessory.eveService.getInitialTime()
81
+ subService.updateCharacteristic(
82
+ this.eveChar.LastActivation,
83
+ Math.round(new Date().valueOf() / 1000) - initialTime,
84
+ )
85
+ const newTO = subService.getCharacteristic(this.eveChar.TimesOpened).value + 1
86
+ subService.updateCharacteristic(this.eveChar.TimesOpened, newTO)
87
+ if (params.updateSource && this.enableLogging) {
88
+ this.log('[%s] %s [%s].', name, this.lang.curState, this.lang.doorOpen)
89
+ }
90
+ } else {
91
+ subService.updateCharacteristic(this.hapChar.LockTargetState, 0)
92
+ subService.updateCharacteristic(this.hapChar.LockCurrentState, 0)
93
+ if (params.updateSource && this.enableLogging) {
94
+ this.log('[%s] %s [%s].', name, this.lang.curState, this.lang.lockUnlocked)
95
+ }
96
+ }
97
+ break
98
+ }
99
+ default:
100
+ }
101
+ } catch (err) {
102
+ this.platform.deviceUpdateError(this.accessory, err, false)
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,123 @@
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.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
+ // Set up custom variables for this device type
19
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
20
+ this.lowBattThreshold = deviceConf.lowBattThreshold
21
+ ? Math.min(deviceConf.lowBattThreshold, 100)
22
+ : platformConsts.defaultValues.lowBattThreshold
23
+
24
+ // Set the correct logging variables for this accessory
25
+ switch (deviceConf.overrideLogging) {
26
+ case 'standard':
27
+ this.enableLogging = true
28
+ this.enableDebugLogging = false
29
+ break
30
+ case 'debug':
31
+ this.enableLogging = true
32
+ this.enableDebugLogging = true
33
+ break
34
+ case 'disable':
35
+ this.enableLogging = false
36
+ this.enableDebugLogging = false
37
+ break
38
+ default:
39
+ this.enableLogging = !platform.config.disableDeviceLogging
40
+ this.enableDebugLogging = platform.config.debug
41
+ break
42
+ }
43
+
44
+ // If the accessory has a contact sensor service then remove it
45
+ if (this.accessory.getService(this.hapServ.ContactSensor)) {
46
+ this.accessory.removeService(this.accessory.getService(this.hapServ.ContactSensor))
47
+ }
48
+
49
+ // Add the leak sensor service if it doesn't already exist
50
+ this.service = this.accessory.getService(this.hapServ.LeakSensor)
51
+ if (!this.service) {
52
+ this.service = this.accessory.addService(this.hapServ.LeakSensor)
53
+ this.service.addCharacteristic(this.eveChar.LastActivation)
54
+ }
55
+
56
+ // Add the battery service if it doesn't already exist
57
+ this.batteryService = this.accessory.getService(this.hapServ.Battery)
58
+ || this.accessory.addService(this.hapServ.Battery)
59
+
60
+ // Pass the accessory to Fakegato to set up with Eve
61
+ this.accessory.eveService = new platform.eveService('motion', this.accessory, {
62
+ log: () => {},
63
+ })
64
+
65
+ // Output the customised options to the log
66
+ const normalLogging = this.enableLogging ? 'standard' : 'disable'
67
+ const opts = JSON.stringify({
68
+ logging: this.enableDebugLogging ? 'debug' : normalLogging,
69
+ lowBattThreshold: this.lowBattThreshold,
70
+ showAs: 'sensor_leak',
71
+ })
72
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
73
+ }
74
+
75
+ async externalUpdate(params) {
76
+ try {
77
+ if (hasProperty(params, 'battery') && this.cacheBatt !== params.battery) {
78
+ this.cacheBatt = params.battery
79
+ if (this.accessory.context.eweUIID === 154) {
80
+ // No scaling needed for this model as provided as a %
81
+ this.cacheBattScaled = this.cacheBatt
82
+ } else {
83
+ // Scaling needed for UIID 102 as provided as a voltage rather than a %
84
+ this.cacheBattScaled = Math.min(Math.max(this.cacheBatt, 2), 3)
85
+ this.cacheBattScaled = Math.round((this.cacheBattScaled - 2) * 100)
86
+ }
87
+
88
+ this.batteryService.updateCharacteristic(this.hapChar.BatteryLevel, this.cacheBattScaled)
89
+ this.batteryService.updateCharacteristic(
90
+ this.hapChar.StatusLowBattery,
91
+ this.cacheBattScaled < this.lowBattThreshold ? 1 : 0,
92
+ )
93
+ if (params.updateSource && this.enableLogging) {
94
+ this.log('[%s] %s [%s%].', this.name, this.lang.curBatt, this.cacheBattScaled)
95
+ }
96
+ }
97
+ if (!params.switch || params.switch === this.cacheState) {
98
+ return
99
+ }
100
+ this.cacheState = params.switch
101
+ const newState = params.switch === 'on' ? 0 : 1
102
+ this.service.updateCharacteristic(this.hapChar.LeakDetected, newState)
103
+ this.accessory.eveService.addEntry({ status: newState })
104
+ if (newState === 1) {
105
+ const initialTime = this.accessory.eveService.getInitialTime()
106
+ this.service.updateCharacteristic(
107
+ this.eveChar.LastActivation,
108
+ Math.round(new Date().valueOf() / 1000) - initialTime,
109
+ )
110
+ }
111
+ if (params.updateSource && this.enableLogging) {
112
+ this.log(
113
+ '[%s] %s [%s].',
114
+ this.name,
115
+ this.lang.curState,
116
+ newState === 1 ? this.lang.leakYes : this.lang.leakNo,
117
+ )
118
+ }
119
+ } catch (err) {
120
+ this.platform.deviceUpdateError(this.accessory, err, false)
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,231 @@
1
+ import platformConsts from '../../utils/constants.js'
2
+ import { hasProperty, sleep } from '../../utils/functions.js'
3
+
4
+ export default class {
5
+ constructor(platform, accessory, subAccessory) {
6
+ // Set up variables from the platform
7
+ this.eveChar = platform.eveChar
8
+ this.hapChar = platform.api.hap.Characteristic
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.nameSub = subAccessory.displayName
17
+ this.accessory = accessory
18
+ this.subAccessory = subAccessory
19
+
20
+ // Set up custom variables for this device type
21
+ this.isDW2 = platformConsts.devices.sensorContact.includes(accessory.context.eweUIID)
22
+ this.hasBattery = platformConsts.devices.garageSensors.includes(accessory.context.eweUIID)
23
+ const deviceConf = platform.deviceConf[accessory.context.eweDeviceId] || {}
24
+ this.scaleBattery = deviceConf.scaleBattery
25
+ this.lowBattThreshold = deviceConf.lowBattThreshold
26
+ ? Math.min(deviceConf.lowBattThreshold, 100)
27
+ : platformConsts.defaultValues.lowBattThreshold
28
+ const group = platform.deviceConf[subAccessory.context.eweDeviceId] || {}
29
+ this.isGarage = group.showAs === 'garage'
30
+ this.operationTime = group.operationTime || platformConsts.defaultValues.operationTime
31
+
32
+ // Set the correct logging variables for this accessory
33
+ switch (deviceConf.overrideLogging) {
34
+ case 'standard':
35
+ this.enableLogging = true
36
+ this.enableDebugLogging = false
37
+ break
38
+ case 'debug':
39
+ this.enableLogging = true
40
+ this.enableDebugLogging = true
41
+ break
42
+ case 'disable':
43
+ this.enableLogging = false
44
+ this.enableDebugLogging = false
45
+ break
46
+ default:
47
+ this.enableLogging = !platform.config.disableDeviceLogging
48
+ this.enableDebugLogging = platform.config.debug
49
+ break
50
+ }
51
+
52
+ // Set the correct logging variables for this sub accessory
53
+ switch (group.overrideLogging) {
54
+ case 'standard':
55
+ case 'debug':
56
+ this.enableLoggingSub = true
57
+ break
58
+ case 'disable':
59
+ this.enableLoggingSub = false
60
+ break
61
+ default:
62
+ this.enableLoggingSub = !platform.config.disableDeviceLogging
63
+ break
64
+ }
65
+
66
+ // If the accessory has a leak sensor service then remove it
67
+ if (this.accessory.getService(this.hapServ.LeakSensor)) {
68
+ this.accessory.removeService(this.accessory.getService(this.hapServ.LeakSensor))
69
+ }
70
+
71
+ // Add the contact sensor service if it doesn't already exist
72
+ this.service = this.accessory.getService(this.hapServ.ContactSensor)
73
+ if (!this.service) {
74
+ this.service = this.accessory.addService(this.hapServ.ContactSensor)
75
+ this.service.addCharacteristic(this.eveChar.LastActivation)
76
+ this.service.addCharacteristic(this.eveChar.ResetTotal)
77
+ this.service.addCharacteristic(this.eveChar.OpenDuration)
78
+ this.service.addCharacteristic(this.eveChar.ClosedDuration)
79
+ this.service.addCharacteristic(this.eveChar.TimesOpened)
80
+ }
81
+
82
+ // Add the set handler to the contact sensor reset total characteristic
83
+ this.service.getCharacteristic(this.eveChar.ResetTotal).onSet(() => {
84
+ this.service.updateCharacteristic(this.eveChar.TimesOpened, 0)
85
+ })
86
+
87
+ if (this.hasBattery) {
88
+ // Add the battery service if it doesn't already exist
89
+ this.batteryService = this.accessory.getService(this.hapServ.Battery)
90
+ || this.accessory.addService(this.hapServ.Battery)
91
+ }
92
+
93
+ // Pass the accessory to Fakegato to set up with Eve
94
+ this.accessory.eveService = new platform.eveService('door', this.accessory, {
95
+ log: () => {},
96
+ })
97
+ this.accessory.eveService.addEntry({
98
+ status: this.service.getCharacteristic(this.hapChar.ContactSensorState).value,
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
+ lowBattThreshold: this.lowBattThreshold,
106
+ scaleBattery: this.scaleBattery,
107
+ })
108
+ this.log('[%s] %s %s.', this.name, this.lang.devInitOpts, opts)
109
+ }
110
+
111
+ async externalUpdate(params) {
112
+ try {
113
+ if (
114
+ this.batteryService
115
+ && hasProperty(params, 'battery')
116
+ && params.battery !== this.cacheBatt
117
+ ) {
118
+ this.cacheBatt = params.battery
119
+ if (this.isDW2) {
120
+ if (this.accessory.context.eweUIID === 154) {
121
+ // No scaling needed for this model as provided as a %
122
+ this.cacheBattScaled = this.cacheBatt
123
+ } else {
124
+ // Scaling needed for UIID 102 as provided as a voltage rather than a %
125
+ this.cacheBattScaled = Math.min(Math.max(this.cacheBatt, 2), 3)
126
+ this.cacheBattScaled = Math.round((this.cacheBattScaled - 2) * 100)
127
+ }
128
+ } else {
129
+ this.cacheBattScaled = this.scaleBattery ? this.cacheBatt * 10 : this.cacheBatt
130
+ }
131
+ this.cacheBattScaled = Math.max(Math.min(this.cacheBattScaled, 100), 0)
132
+ this.batteryService.updateCharacteristic(this.hapChar.BatteryLevel, this.cacheBattScaled)
133
+ this.batteryService.updateCharacteristic(
134
+ this.hapChar.StatusLowBattery,
135
+ this.cacheBattScaled < this.lowBattThreshold ? 1 : 0,
136
+ )
137
+ if (params.updateSource && this.enableLogging) {
138
+ this.log('[%s] %s [%s%].', this.name, this.lang.curBatt, this.cacheBattScaled)
139
+ }
140
+ }
141
+ if (!hasProperty(params, 'lock') && !params.switch) {
142
+ return
143
+ }
144
+ let newState
145
+ if (params.switch) {
146
+ newState = params.switch === 'on' ? 1 : 0
147
+ } else {
148
+ newState = params.lock
149
+ }
150
+ if (newState === this.cacheState) {
151
+ return
152
+ }
153
+ this.cacheState = newState
154
+ if (newState === 1) {
155
+ this.service.updateCharacteristic(this.hapChar.ContactSensorState, 1)
156
+ this.accessory.eveService.addEntry({ status: 1 })
157
+ const initialTime = this.accessory.eveService.getInitialTime()
158
+ this.service.updateCharacteristic(
159
+ this.eveChar.LastActivation,
160
+ Math.round(new Date().valueOf() / 1000) - initialTime,
161
+ )
162
+ const newTO = this.service.getCharacteristic(this.eveChar.TimesOpened).value + 1
163
+ this.service.updateCharacteristic(this.eveChar.TimesOpened, newTO)
164
+ } else {
165
+ this.service.updateCharacteristic(this.hapChar.ContactSensorState, 0)
166
+ this.accessory.eveService.addEntry({ status: 0 })
167
+ }
168
+ if (params.updateSource && this.enableLogging) {
169
+ this.log(
170
+ '[%s] %s [%s].',
171
+ this.name,
172
+ this.lang.curState,
173
+ newState === 0 ? this.lang.contactYes : this.lang.contactNo,
174
+ )
175
+ }
176
+ const subService = this.subAccessory.getService(
177
+ this.isGarage ? this.hapServ.GarageDoorOpener : this.hapServ.LockMechanism,
178
+ )
179
+ switch (newState) {
180
+ case 0:
181
+ if (this.isGarage) {
182
+ subService.updateCharacteristic(this.hapChar.TargetDoorState, 1)
183
+ subService.updateCharacteristic(this.hapChar.CurrentDoorState, 1)
184
+ this.subAccessory.eveService.addEntry({ status: 1 })
185
+ if (params.updateSource && this.enableLoggingSub) {
186
+ this.log('[%s] %s [%s].', this.nameSub, this.lang.curState, this.lang.doorClosed)
187
+ }
188
+ } else {
189
+ subService.updateCharacteristic(this.hapChar.LockTargetState, 1)
190
+ subService.updateCharacteristic(this.hapChar.LockCurrentState, 1)
191
+ this.subAccessory.context.contactDetected = true
192
+ if (params.updateSource && this.enableLoggingSub) {
193
+ this.log('[%s] %s [%s].', this.nameSub, this.lang.curState, this.lang.lockLocked)
194
+ }
195
+ }
196
+ break
197
+ case 1: {
198
+ if (this.isGarage) {
199
+ await sleep(Math.max(this.operationTime * 100, 2000))
200
+ subService.updateCharacteristic(this.hapChar.TargetDoorState, 0)
201
+ subService.updateCharacteristic(this.hapChar.CurrentDoorState, 0)
202
+ this.subAccessory.eveService.addEntry({ status: 0 })
203
+ const initialTime = this.subAccessory.eveService.getInitialTime()
204
+ subService.updateCharacteristic(
205
+ this.eveChar.LastActivation,
206
+ Math.round(new Date().valueOf() / 1000) - initialTime,
207
+ )
208
+ subService.updateCharacteristic(
209
+ this.eveChar.TimesOpened,
210
+ subService.getCharacteristic(this.eveChar.TimesOpened).value + 1,
211
+ )
212
+ if (params.updateSource && this.enableLoggingSub) {
213
+ this.log('[%s] %s [%s].', this.nameSub, this.lang.curState, this.lang.doorOpen)
214
+ }
215
+ } else {
216
+ subService.updateCharacteristic(this.hapChar.LockTargetState, 0)
217
+ subService.updateCharacteristic(this.hapChar.LockCurrentState, 0)
218
+ this.subAccessory.context.contactDetected = false
219
+ if (params.updateSource && this.enableLoggingSub) {
220
+ this.log('[%s] %s [%s].', this.nameSub, this.lang.curState, this.lang.lockUnlocked)
221
+ }
222
+ }
223
+ break
224
+ }
225
+ default:
226
+ }
227
+ } catch (err) {
228
+ this.platform.deviceUpdateError(this.accessory, err, false)
229
+ }
230
+ }
231
+ }