@homebridge-plugins/homebridge-ewelink 12.3.3 → 12.4.1

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 (72) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +1 -1
  3. package/README.md +1 -1
  4. package/eslint.config.js +36 -1
  5. package/lib/connection/http.js +2 -0
  6. package/lib/connection/lan.js +2 -0
  7. package/lib/connection/ws.js +6 -5
  8. package/lib/device/diffuser.js +2 -2
  9. package/lib/device/fan.js +1 -1
  10. package/lib/device/group.js +1 -1
  11. package/lib/device/index.js +3 -3
  12. package/lib/device/light-cct.js +1 -1
  13. package/lib/device/light-dimmer.js +1 -1
  14. package/lib/device/light-rgb-cct.js +1 -1
  15. package/lib/device/light-rgb.js +1 -1
  16. package/lib/device/outlet-multi.js +2 -2
  17. package/lib/device/outlet-single-inched.js +1 -1
  18. package/lib/device/outlet-single.js +1 -1
  19. package/lib/device/panel.js +3 -3
  20. package/lib/device/rf-bridge.js +1 -0
  21. package/lib/device/sensor-ambient.js +3 -3
  22. package/lib/device/sensor-contact.js +1 -1
  23. package/lib/device/sensor-temp-humi.js +2 -2
  24. package/lib/device/simulation/cooler.js +2 -2
  25. package/lib/device/simulation/doorbell.js +1 -1
  26. package/lib/device/simulation/heater.js +2 -2
  27. package/lib/device/simulation/index.js +1 -1
  28. package/lib/device/simulation/light-fan.js +1 -1
  29. package/lib/device/simulation/lock-eachen.js +1 -1
  30. package/lib/device/simulation/lock-one.js +1 -1
  31. package/lib/device/simulation/p-button.js +1 -1
  32. package/lib/device/simulation/purifier.js +1 -1
  33. package/lib/device/simulation/sensor-leak.js +1 -1
  34. package/lib/device/simulation/sensor-visible.js +1 -1
  35. package/lib/device/simulation/switch-valve.js +1 -1
  36. package/lib/device/simulation/th-cooler.js +3 -3
  37. package/lib/device/simulation/th-dehumidifier.js +3 -3
  38. package/lib/device/simulation/th-heater.js +3 -3
  39. package/lib/device/simulation/th-humidifier.js +3 -3
  40. package/lib/device/simulation/th-thermostat.js +1 -1
  41. package/lib/device/simulation/tv.js +1 -1
  42. package/lib/device/switch-mate.js +1 -1
  43. package/lib/device/switch-multi.js +1 -1
  44. package/lib/device/switch-single-inched.js +1 -1
  45. package/lib/device/switch-single.js +1 -1
  46. package/lib/device/thermostat.js +1 -1
  47. package/lib/device/zigbee/index.js +2 -0
  48. package/lib/device/zigbee/light-cct.js +1 -1
  49. package/lib/device/zigbee/light-dimmer.js +1 -1
  50. package/lib/device/zigbee/light-rgb-cct.js +1 -1
  51. package/lib/device/zigbee/motor.js +1 -1
  52. package/lib/device/zigbee/sensor-ambient.js +3 -3
  53. package/lib/device/zigbee/sensor-contact.js +1 -1
  54. package/lib/device/zigbee/sensor-leak.js +1 -1
  55. package/lib/device/zigbee/sensor-motion.js +1 -1
  56. package/lib/device/zigbee/sensor-occupancy.js +1 -1
  57. package/lib/device/zigbee/sensor-smoke.js +2 -2
  58. package/lib/device/zigbee/switch-stateless.js +2 -2
  59. package/lib/device/zigbee/thermostat.js +1 -1
  60. package/lib/device/zigbee/zigbee-water-valve.js +195 -0
  61. package/lib/fakegato/fakegato-history.js +5 -4
  62. package/lib/homebridge-ui/public/index.html +4 -0
  63. package/lib/homebridge-ui/server.js +1 -0
  64. package/lib/index.js +1 -0
  65. package/lib/node-dns-sd/lib/dns-sd-composer.js +1 -1
  66. package/lib/node-dns-sd/lib/dns-sd-parser.js +1 -1
  67. package/lib/node-dns-sd/lib/dns-sd.js +2 -1
  68. package/lib/platform.js +31 -21
  69. package/lib/utils/constants.js +5 -1
  70. package/lib/utils/custom-chars.js +2 -2
  71. package/lib/utils/eve-chars.js +9 -9
  72. package/package.json +9 -9
@@ -43,7 +43,7 @@ export default class {
43
43
 
44
44
  // Add the stateless switch service if it doesn't already exist
45
45
  this.service = this.accessory.getService(this.hapServ.StatelessProgrammableSwitch)
46
- || this.accessory.addService(this.hapServ.StatelessProgrammableSwitch)
46
+ || this.accessory.addService(this.hapServ.StatelessProgrammableSwitch)
47
47
 
48
48
  // Output the customised options to the log
49
49
  const normalLogging = this.enableLogging ? 'standard' : 'disable'
@@ -61,7 +61,7 @@ export default class {
61
61
 
62
62
  // Add the switch service if it doesn't already exist
63
63
  this.service = this.accessory.getService(this.hapServ.Switch)
64
- || this.accessory.addService(this.hapServ.Switch)
64
+ || this.accessory.addService(this.hapServ.Switch)
65
65
 
66
66
  if (platformConsts.devices.switchMultiPower.includes(this.accessory.context.eweUIID)) {
67
67
  // Add Eve power characteristics
@@ -59,7 +59,7 @@ export default class {
59
59
 
60
60
  // Add the switch service if it doesn't already exist
61
61
  this.service = this.accessory.getService(this.hapServ.Switch)
62
- || this.accessory.addService(this.hapServ.Switch)
62
+ || this.accessory.addService(this.hapServ.Switch)
63
63
 
64
64
  switch (this.accessory.context.eweUIID) {
65
65
  case 5:
@@ -58,7 +58,7 @@ export default class {
58
58
 
59
59
  // Add the switch service if it doesn't already exist
60
60
  this.service = this.accessory.getService(this.hapServ.Switch)
61
- || this.accessory.addService(this.hapServ.Switch)
61
+ || this.accessory.addService(this.hapServ.Switch)
62
62
 
63
63
  // Some models provide a power reading in different decimals
64
64
  this.divisor = 1
@@ -67,7 +67,7 @@ export default class {
67
67
 
68
68
  // Add the thermostat service if it doesn't already exist
69
69
  this.service = this.accessory.getService(this.hapServ.Thermostat)
70
- || this.accessory.addService(this.hapServ.Thermostat)
70
+ || this.accessory.addService(this.hapServ.Thermostat)
71
71
 
72
72
  // Set custom properties of the current temperature characteristic
73
73
  this.service.getCharacteristic(this.hapChar.CurrentTemperature).setProps({
@@ -10,6 +10,7 @@ import deviceSensorOccupancy from './sensor-occupancy.js'
10
10
  import deviceSensorSmoke from './sensor-smoke.js'
11
11
  import deviceSwitchStateless from './switch-stateless.js'
12
12
  import deviceThermostat from './thermostat.js'
13
+ import deviceZigbeeWaterValve from './zigbee-water-valve.js'
13
14
 
14
15
  export default {
15
16
  deviceLightCCT,
@@ -24,4 +25,5 @@ export default {
24
25
  deviceSensorSmoke,
25
26
  deviceSwitchStateless,
26
27
  deviceThermostat,
28
+ deviceZigbeeWaterValve,
27
29
  }
@@ -48,7 +48,7 @@ export default class {
48
48
 
49
49
  // Add the lightbulb service if it doesn't already exist
50
50
  this.service = this.accessory.getService(this.hapServ.Lightbulb)
51
- || this.accessory.addService(this.hapServ.Lightbulb)
51
+ || this.accessory.addService(this.hapServ.Lightbulb)
52
52
 
53
53
  // If adaptive lighting has just been disabled then remove and re-add service to hide AL icon
54
54
  if (this.alShift === -1 && this.accessory.context.adaptiveLighting) {
@@ -43,7 +43,7 @@ export default class {
43
43
 
44
44
  // Add the lightbulb service if it doesn't already exist
45
45
  this.service = this.accessory.getService(this.hapServ.Lightbulb)
46
- || this.accessory.addService(this.hapServ.Lightbulb)
46
+ || this.accessory.addService(this.hapServ.Lightbulb)
47
47
 
48
48
  // Add the get/set handler to the lightbulb on/off characteristic
49
49
  this.service
@@ -48,7 +48,7 @@ export default class {
48
48
 
49
49
  // Add the lightbulb service if it doesn't already exist
50
50
  this.service = this.accessory.getService(this.hapServ.Lightbulb)
51
- || this.accessory.addService(this.hapServ.Lightbulb)
51
+ || this.accessory.addService(this.hapServ.Lightbulb)
52
52
 
53
53
  // If adaptive lighting has just been disabled then remove and re-add service to hide AL icon
54
54
  if (this.alShift === -1 && this.accessory.context.adaptiveLighting) {
@@ -48,7 +48,7 @@ export default class {
48
48
 
49
49
  // Add the battery service if it doesn't already exist
50
50
  this.battService = this.accessory.getService(this.hapServ.Battery)
51
- || this.accessory.addService(this.hapServ.Battery)
51
+ || this.accessory.addService(this.hapServ.Battery)
52
52
  this.cacheBatt = this.battService.getCharacteristic(this.hapChar.BatteryLevel).value
53
53
 
54
54
  // Add the window covering service if it doesn't already exist
@@ -48,7 +48,7 @@ export default class {
48
48
 
49
49
  // Add the temperature sensor service if it doesn't already exist
50
50
  this.tempService = this.accessory.getService(this.hapServ.TemperatureSensor)
51
- || this.accessory.addService(this.hapServ.TemperatureSensor)
51
+ || this.accessory.addService(this.hapServ.TemperatureSensor)
52
52
 
53
53
  // Add options to the current temperature characteristic
54
54
  this.tempService.getCharacteristic(this.hapChar.CurrentTemperature).setProps({
@@ -59,11 +59,11 @@ export default class {
59
59
 
60
60
  // Add the humidity sensor service if it doesn't already exist
61
61
  this.humiService = this.accessory.getService(this.hapServ.HumiditySensor)
62
- || this.accessory.addService(this.hapServ.HumiditySensor)
62
+ || this.accessory.addService(this.hapServ.HumiditySensor)
63
63
 
64
64
  // Add the battery service if it doesn't already exist
65
65
  this.battService = this.accessory.getService(this.hapServ.Battery)
66
- || this.accessory.addService(this.hapServ.Battery)
66
+ || this.accessory.addService(this.hapServ.Battery)
67
67
 
68
68
  // Pass the accessory to Fakegato to set up with Eve
69
69
  this.accessory.eveService = new platform.eveService('weather', this.accessory, {
@@ -60,7 +60,7 @@ export default class {
60
60
 
61
61
  // Add the battery service if it doesn't already exist
62
62
  this.battService = this.accessory.getService(this.hapServ.Battery)
63
- || this.accessory.addService(this.hapServ.Battery)
63
+ || this.accessory.addService(this.hapServ.Battery)
64
64
 
65
65
  // Pass the accessory to Fakegato to set up with Eve
66
66
  this.accessory.eveService = new platform.eveService('door', this.accessory, {
@@ -51,7 +51,7 @@ export default class {
51
51
 
52
52
  // Add the battery service if it doesn't already exist
53
53
  this.battService = this.accessory.getService(this.hapServ.Battery)
54
- || this.accessory.addService(this.hapServ.Battery)
54
+ || this.accessory.addService(this.hapServ.Battery)
55
55
 
56
56
  // Pass the accessory to Fakegato to set up with Eve
57
57
  this.accessory.eveService = new platform.eveService('motion', this.accessory, {
@@ -51,7 +51,7 @@ export default class {
51
51
 
52
52
  // Add the battery service if it doesn't already exist
53
53
  this.battService = this.accessory.getService(this.hapServ.Battery)
54
- || this.accessory.addService(this.hapServ.Battery)
54
+ || this.accessory.addService(this.hapServ.Battery)
55
55
 
56
56
  // Pass the accessory to Fakegato to set up with Eve
57
57
  this.accessory.eveService = new platform.eveService('motion', this.accessory, {
@@ -51,7 +51,7 @@ export default class {
51
51
 
52
52
  // Add the battery service if it doesn't already exist
53
53
  this.battService = this.accessory.getService(this.hapServ.Battery)
54
- || this.accessory.addService(this.hapServ.Battery)
54
+ || this.accessory.addService(this.hapServ.Battery)
55
55
 
56
56
  // Pass the accessory to Fakegato to set up with Eve
57
57
  this.accessory.eveService = new platform.eveService('motion', this.accessory, {
@@ -43,11 +43,11 @@ export default class {
43
43
 
44
44
  // Add the smoke sensor service if it doesn't already exist
45
45
  this.service = this.accessory.getService(this.hapServ.SmokeSensor)
46
- || this.accessory.addService(this.hapServ.SmokeSensor)
46
+ || this.accessory.addService(this.hapServ.SmokeSensor)
47
47
 
48
48
  // Add the battery service if it doesn't already exist
49
49
  this.battService = this.accessory.getService(this.hapServ.Battery)
50
- || this.accessory.addService(this.hapServ.Battery)
50
+ || this.accessory.addService(this.hapServ.Battery)
51
51
 
52
52
  // Output the customised options to the log
53
53
  const normalLogging = this.enableLogging ? 'standard' : 'disable'
@@ -44,7 +44,7 @@ export default class {
44
44
 
45
45
  // Add the stateless switch service if it doesn't already exist
46
46
  this.service = this.accessory.getService(this.hapServ.StatelessProgrammableSwitch)
47
- || this.accessory.addService(this.hapServ.StatelessProgrammableSwitch)
47
+ || this.accessory.addService(this.hapServ.StatelessProgrammableSwitch)
48
48
 
49
49
  // Hide the double and long press options if the user wants
50
50
  if (this.hideLongDouble) {
@@ -55,7 +55,7 @@ export default class {
55
55
 
56
56
  // Add the battery service if it doesn't already exist
57
57
  this.battService = this.accessory.getService(this.hapServ.Battery)
58
- || this.accessory.addService(this.hapServ.Battery)
58
+ || this.accessory.addService(this.hapServ.Battery)
59
59
 
60
60
  // Output the customised options to the log
61
61
  const normalLogging = this.enableLogging ? 'standard' : 'disable'
@@ -75,7 +75,7 @@ export default class {
75
75
 
76
76
  // Add the thermostat service if it doesn't already exist
77
77
  this.service = this.accessory.getService(this.hapServ.Thermostat)
78
- || this.accessory.addService(this.hapServ.Thermostat)
78
+ || this.accessory.addService(this.hapServ.Thermostat)
79
79
 
80
80
  // Set custom properties of the current temperature characteristic
81
81
  this.service.getCharacteristic(this.hapChar.CurrentTemperature).setProps({
@@ -0,0 +1,195 @@
1
+ import { hasProperty } from '../../utils/functions.js'
2
+
3
+ export default class {
4
+ constructor(platform, accessory) {
5
+ this.hapChar = platform.api.hap.Characteristic
6
+ this.hapServ = platform.api.hap.Service
7
+ this.hapErr = platform.api.hap.HapStatusError
8
+ this.log = platform.log
9
+ this.platform = platform
10
+ this.lang = platform.lang
11
+ this.name = accessory.displayName
12
+ this.accessory = accessory
13
+
14
+ // Set up custom variables for this device type
15
+ this.enableLogging = !platform.config.disableDeviceLogging
16
+
17
+ // Initialize cache state
18
+ this.cacheState = false
19
+
20
+ // Initialize timer references
21
+ this.timer = null
22
+ this.updateInterval = null
23
+
24
+ // Set up the Valve Service for HomeKit
25
+ this.service = this.accessory.getService(this.hapServ.Valve)
26
+ if (!this.service) {
27
+ this.service = this.accessory.addService(this.hapServ.Valve)
28
+ this.service.updateCharacteristic(this.hapChar.Active, 0)
29
+ this.service.updateCharacteristic(this.hapChar.InUse, 0)
30
+ this.service.updateCharacteristic(this.hapChar.ValveType, 1) // 1 = Irrigation
31
+ this.service.updateCharacteristic(this.hapChar.SetDuration, 30) // Default timer duration
32
+ this.service.addCharacteristic(this.hapChar.RemainingDuration)
33
+ }
34
+
35
+ // Restore timer state if it exists
36
+ if (this.accessory.context.timerEndTime) {
37
+ const now = Date.now()
38
+ if (now < this.accessory.context.timerEndTime) {
39
+ // Timer should still be running
40
+ const remainingTime = Math.round((this.accessory.context.timerEndTime - now) / 1000)
41
+ this.service.updateCharacteristic(this.hapChar.RemainingDuration, remainingTime)
42
+ this.startTimer(remainingTime)
43
+ } else {
44
+ // Timer has expired, clean up
45
+ this.service.updateCharacteristic(this.hapChar.Active, 0)
46
+ this.service.updateCharacteristic(this.hapChar.InUse, 0)
47
+ this.service.updateCharacteristic(this.hapChar.RemainingDuration, 0)
48
+ delete this.accessory.context.timerEndTime
49
+ }
50
+ }
51
+
52
+ // Set up event handlers for Active characteristic
53
+ this.service
54
+ .getCharacteristic(this.hapChar.Active)
55
+ .onSet(async (value) => {
56
+ try {
57
+ const newState = value === 1
58
+ if (this.enableLogging) {
59
+ this.log('[%s] Setting valve to [%s]', this.name, newState ? 'On' : 'Off')
60
+ }
61
+
62
+ // Send command to the device
63
+ await this.platform.sendDeviceUpdate(this.accessory, {
64
+ switch: newState,
65
+ })
66
+
67
+ // Update InUse characteristic to match Active
68
+ this.service.updateCharacteristic(this.hapChar.InUse, value)
69
+
70
+ if (value === 1) {
71
+ // If turning on, start the timer
72
+ const duration = this.service.getCharacteristic(this.hapChar.SetDuration).value
73
+ this.startTimer(duration)
74
+ } else {
75
+ // If turning off, clear the timer and reset remaining duration
76
+ this.clearTimer()
77
+ }
78
+
79
+ // Update cache after successful command
80
+ this.cacheState = newState
81
+ } catch (err) {
82
+ // Revert the characteristic state on error
83
+ this.service.updateCharacteristic(this.hapChar.Active, this.cacheState)
84
+ this.service.updateCharacteristic(this.hapChar.InUse, this.cacheState)
85
+ this.platform.deviceUpdateError(this.accessory, err, false)
86
+ throw new this.hapErr(-70402)
87
+ }
88
+ })
89
+
90
+ // Set up handler for SetDuration characteristic
91
+ this.service.getCharacteristic(this.hapChar.SetDuration)
92
+ .onSet((value) => {
93
+ if (this.service.getCharacteristic(this.hapChar.InUse).value === 1) {
94
+ // Update timer with new duration if valve is currently active
95
+ this.startTimer(value)
96
+ }
97
+ })
98
+
99
+ this.log('[%s] initialized as a Zigbee Smart Water Valve.', this.name)
100
+ }
101
+
102
+ startTimer(duration) {
103
+ // Clear any existing timer
104
+ this.clearTimer()
105
+
106
+ // Set timer end time and store it in context
107
+ this.accessory.context.timerEndTime = Date.now() + (duration * 1000)
108
+
109
+ // Update remaining duration
110
+ this.service.updateCharacteristic(this.hapChar.RemainingDuration, duration)
111
+
112
+ // Set new timer
113
+ this.timer = setTimeout(async () => {
114
+ try {
115
+ // Send explicit command to turn off the valve
116
+ await this.platform.sendDeviceUpdate(this.accessory, {
117
+ switch: false,
118
+ })
119
+
120
+ // Update HomeKit characteristics
121
+ this.service.updateCharacteristic(this.hapChar.Active, 0)
122
+ this.service.updateCharacteristic(this.hapChar.InUse, 0)
123
+ this.service.updateCharacteristic(this.hapChar.RemainingDuration, 0)
124
+ delete this.accessory.context.timerEndTime
125
+
126
+ // Update cache state
127
+ this.cacheState = false
128
+
129
+ if (this.enableLogging) {
130
+ this.log('[%s] Timer finished - turning valve [OFF]', this.name)
131
+ }
132
+ } catch (err) {
133
+ this.platform.deviceUpdateError(this.accessory, err, false)
134
+ }
135
+ }, duration * 1000)
136
+
137
+ // Start a periodic update of remaining duration
138
+ if (this.updateInterval) {
139
+ clearInterval(this.updateInterval)
140
+ this.updateInterval = null
141
+ }
142
+ this.updateInterval = setInterval(() => {
143
+ const remaining = Math.round((this.accessory.context.timerEndTime - Date.now()) / 1000)
144
+ if (remaining > 0) {
145
+ this.service.updateCharacteristic(this.hapChar.RemainingDuration, remaining)
146
+ } else {
147
+ clearInterval(this.updateInterval)
148
+ this.updateInterval = null
149
+ }
150
+ }, 1000)
151
+ }
152
+
153
+ clearTimer() {
154
+ if (this.timer) {
155
+ clearTimeout(this.timer)
156
+ this.timer = null
157
+ }
158
+ if (this.updateInterval) {
159
+ clearInterval(this.updateInterval)
160
+ this.updateInterval = null
161
+ }
162
+ delete this.accessory.context.timerEndTime
163
+ this.service.updateCharacteristic(this.hapChar.RemainingDuration, 0)
164
+ }
165
+
166
+ async externalUpdate(params) {
167
+ try {
168
+ // Handle On/Off state
169
+ if (hasProperty(params, 'switch')) {
170
+ const newState = !!params.switch
171
+ if (newState !== this.cacheState) {
172
+ this.cacheState = newState
173
+ const value = newState ? 1 : 0
174
+ this.service.updateCharacteristic(this.hapChar.Active, value)
175
+ this.service.updateCharacteristic(this.hapChar.InUse, value)
176
+
177
+ if (!newState) {
178
+ // If turned off externally, clear timer
179
+ this.clearTimer()
180
+ }
181
+
182
+ if (params.updateSource && this.enableLogging) {
183
+ this.log('[%s] Valve state updated to [%s]', this.name, newState ? 'On' : 'Off')
184
+ }
185
+ }
186
+ }
187
+ } catch (err) {
188
+ this.platform.deviceUpdateError(this.accessory, err, false)
189
+ }
190
+ }
191
+
192
+ markStatus(isOnline) {
193
+ this.isOnline = isOnline
194
+ }
195
+ }
@@ -1,5 +1,6 @@
1
1
  import { Buffer } from 'node:buffer'
2
2
  import { format as Format } from 'node:util'
3
+
3
4
  import FakeGatoStorage from './fakegato-storage.js'
4
5
  import FakeGatoTimer from './fakegato-timer.js'
5
6
  import { toLongFormUUID, toShortFormUUID } from './uuid.js'
@@ -50,7 +51,7 @@ export default function (homebridge) {
50
51
  super('S2R1', S2R1Characteristic.UUID)
51
52
  this.setProps({
52
53
  format: homebridge.hap.Formats.DATA,
53
- perms: [homebridge.hap.Perms.READ, homebridge.hap.Perms.NOTIFY, homebridge.hap.Perms.HIDDEN],
54
+ perms: [homebridge.hap.Perms.PAIRED_READ, homebridge.hap.Perms.NOTIFY, homebridge.hap.Perms.HIDDEN],
54
55
  })
55
56
  }
56
57
  }
@@ -62,7 +63,7 @@ export default function (homebridge) {
62
63
  super('S2R2', S2R2Characteristic.UUID)
63
64
  this.setProps({
64
65
  format: homebridge.hap.Formats.DATA,
65
- perms: [homebridge.hap.Perms.READ, homebridge.hap.Perms.NOTIFY, homebridge.hap.Perms.HIDDEN],
66
+ perms: [homebridge.hap.Perms.PAIRED_READ, homebridge.hap.Perms.NOTIFY, homebridge.hap.Perms.HIDDEN],
66
67
  })
67
68
  }
68
69
  }
@@ -74,7 +75,7 @@ export default function (homebridge) {
74
75
  super('S2W1', S2W1Characteristic.UUID)
75
76
  this.setProps({
76
77
  format: homebridge.hap.Formats.DATA,
77
- perms: [homebridge.hap.Perms.WRITE, homebridge.hap.Perms.HIDDEN],
78
+ perms: [homebridge.hap.Perms.PAIRED_WRITE, homebridge.hap.Perms.HIDDEN],
78
79
  })
79
80
  }
80
81
  }
@@ -86,7 +87,7 @@ export default function (homebridge) {
86
87
  super('S2W2', S2W2Characteristic.UUID)
87
88
  this.setProps({
88
89
  format: homebridge.hap.Formats.DATA,
89
- perms: [homebridge.hap.Perms.WRITE, homebridge.hap.Perms.HIDDEN],
90
+ perms: [homebridge.hap.Perms.PAIRED_WRITE, homebridge.hap.Perms.HIDDEN],
90
91
  })
91
92
  }
92
93
  }
@@ -226,6 +226,7 @@
226
226
  const introContinue = document.getElementById('introContinue')
227
227
  introContinue.addEventListener('click', () => {
228
228
  homebridge.showSpinner()
229
+ homebridge.disableSaveButton?.()
229
230
  document.getElementById('pageIntro').style.display = 'none'
230
231
  document.getElementById('menuWrapper').style.display = 'inline-flex'
231
232
  showSettings()
@@ -235,6 +236,7 @@
235
236
  }
236
237
  showDevices = async () => {
237
238
  homebridge.showSpinner()
239
+ homebridge.disableSaveButton?.()
238
240
  homebridge.hideSchemaForm()
239
241
  document.getElementById('menuHome').classList.remove('btn-elegant')
240
242
  document.getElementById('menuHome').classList.add('btn-primary')
@@ -306,6 +308,7 @@
306
308
  }
307
309
  showSupport = () => {
308
310
  homebridge.showSpinner()
311
+ homebridge.disableSaveButton?.()
309
312
  homebridge.hideSchemaForm()
310
313
  document.getElementById('menuHome').classList.add('btn-elegant')
311
314
  document.getElementById('menuHome').classList.remove('btn-primary')
@@ -319,6 +322,7 @@
319
322
  }
320
323
  showSettings = () => {
321
324
  homebridge.showSpinner()
325
+ homebridge.enableSaveButton?.()
322
326
  document.getElementById('menuHome').classList.remove('btn-elegant')
323
327
  document.getElementById('menuHome').classList.add('btn-primary')
324
328
  document.getElementById('menuDevices').classList.remove('btn-elegant')
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs'
2
+
2
3
  import { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils'
3
4
 
4
5
  class PluginUiServer extends HomebridgePluginUiServer {
package/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from 'node:module'
2
+
2
3
  import ewelinkPlatform from './platform.js'
3
4
 
4
5
  const require = createRequire(import.meta.url)
@@ -15,7 +15,7 @@ import { createRequire } from 'node:module'
15
15
 
16
16
  const require = createRequire(import.meta.url)
17
17
 
18
- const DnsSdComposer = function () {
18
+ function DnsSdComposer() {
19
19
  this._CLASSES = require('./dns-sd-classes.json')
20
20
  this._TYPES = require('./dns-sd-types.json')
21
21
  }
@@ -23,7 +23,7 @@ const require = createRequire(import.meta.url)
23
23
  /* ------------------------------------------------------------------
24
24
  * Constructor: DnsSdParser()
25
25
  * ---------------------------------------------------------------- */
26
- const DnsSdParser = function () {
26
+ function DnsSdParser() {
27
27
  this._CLASSES = require('./dns-sd-classes.json')
28
28
  this._CLASS_MAP = {}
29
29
  Object.keys(this._CLASSES).forEach((k) => {
@@ -8,13 +8,14 @@
8
8
 
9
9
  import mDgram from 'node:dgram'
10
10
  import mOs from 'node:os'
11
+
11
12
  import mDnsSdComposer from './dns-sd-composer.js'
12
13
  import mDnsSdParser from './dns-sd-parser.js'
13
14
 
14
15
  /* ------------------------------------------------------------------
15
16
  * Constructor: DnsSd()
16
17
  * ---------------------------------------------------------------- */
17
- const DnsSd = function () {
18
+ function DnsSd() {
18
19
  // Public
19
20
  this.ondata = () => { }
20
21
 
package/lib/platform.js CHANGED
@@ -2,8 +2,10 @@ import { createServer } from 'node:http'
2
2
  import { createRequire } from 'node:module'
3
3
  import { join } from 'node:path'
4
4
  import process from 'node:process'
5
+
5
6
  import storage from 'node-persist'
6
7
  import PQueue from 'p-queue'
8
+
7
9
  import apiClient from './connection/api.js'
8
10
  import httpClient from './connection/http.js'
9
11
  import lanClient from './connection/lan.js'
@@ -1479,7 +1481,7 @@ export default class {
1479
1481
 
1480
1482
  // Get the sub accessory if it's new or hasn't been removed above
1481
1483
  subAccessory = devicesInHB.get(uuidsub)
1482
- || this.addAccessory(device, fullDeviceId, false, subExtraContext, 'rf_sub')
1484
+ || this.addAccessory(device, fullDeviceId, false, subExtraContext, 'rf_sub')
1483
1485
 
1484
1486
  // Add context information to the sub accessory
1485
1487
  subAccessory.context.firmware = device.params.fwVersion || plugin.version
@@ -1644,6 +1646,16 @@ export default class {
1644
1646
  this.applyAccessoryLogging(accessory)
1645
1647
  accessory.control = new deviceTypes.zb.deviceThermostat(this, accessory)
1646
1648
  /** */
1649
+ } else if (platformConsts.devices.zbWaterValve.includes(uiid)) {
1650
+ /**
1651
+ *******************
1652
+ ZB WATER VALVE
1653
+ ********************
1654
+ */
1655
+ accessory = devicesInHB.get(uuid) || this.addAccessory(device, `${device.deviceid}SWX`)
1656
+ this.applyAccessoryLogging(accessory)
1657
+ accessory.control = new deviceTypes.zb.deviceZigbeeWaterValve(this, accessory)
1658
+ /** */
1647
1659
  } else if (platformConsts.devices.group.includes(uiid)) {
1648
1660
  /**
1649
1661
  ***********
@@ -1846,16 +1858,15 @@ export default class {
1846
1858
 
1847
1859
  // Add helpful context values to the accessory
1848
1860
  accessory.context = {
1849
- ...{
1850
- hbDeviceId,
1851
- eweDeviceId: device.deviceid,
1852
- eweUIID: device.extra.uiid,
1853
- eweModel: device.productModel,
1854
- eweApiKey: device.apikey,
1855
- switchNumber,
1856
- channelCount,
1857
- hidden,
1858
- },
1861
+
1862
+ hbDeviceId,
1863
+ eweDeviceId: device.deviceid,
1864
+ eweUIID: device.extra.uiid,
1865
+ eweModel: device.productModel,
1866
+ eweApiKey: device.apikey,
1867
+ switchNumber,
1868
+ channelCount,
1869
+ hidden,
1859
1870
  ...extraContext,
1860
1871
  }
1861
1872
 
@@ -1903,16 +1914,15 @@ export default class {
1903
1914
 
1904
1915
  // Add helpful context values to the accessory
1905
1916
  accessory.context = {
1906
- ...{
1907
- hbDeviceId,
1908
- eweDeviceId: device.deviceid,
1909
- eweUIID: device.extra.uiid,
1910
- eweModel: device.productModel,
1911
- eweApiKey: device.apikey,
1912
- switchNumber: hbDeviceId.split('SW')[1].toString(),
1913
- channelCount: platformConsts.supportedDevices[device.extra.uiid],
1914
- hidden: false,
1915
- },
1917
+
1918
+ hbDeviceId,
1919
+ eweDeviceId: device.deviceid,
1920
+ eweUIID: device.extra.uiid,
1921
+ eweModel: device.productModel,
1922
+ eweApiKey: device.apikey,
1923
+ switchNumber: hbDeviceId.split('SW')[1].toString(),
1924
+ channelCount: platformConsts.supportedDevices[device.extra.uiid],
1925
+ hidden: false,
1916
1926
  }
1917
1927
 
1918
1928
  // Register the accessory
@@ -284,7 +284,7 @@ export default {
284
284
  211,
285
285
  212,
286
286
  ],
287
- switchSingle: [1, 6, 14, 24, 27, 1009, 1256, 7004, 7005],
287
+ switchSingle: [1, 6, 14, 24, 27, 226, 1009, 1256, 7004, 7005, 7010],
288
288
  switchSCM: [77, 78, 81, 107, 112, 138, 160, 191, 209],
289
289
  switchSCMPower: [182, 190],
290
290
  switchSinglePower: [5, 32],
@@ -347,6 +347,7 @@ export default {
347
347
  zbSensorWater: [4026],
348
348
  zbSensorSmoke: [5026],
349
349
  zbThermostat: [7017],
350
+ zbWaterValve: [7027],
350
351
  group: [5000],
351
352
  template: [130], // SPM sub unit
352
353
  cannotSupport: [0, 65, 118, 119, 120, 121],
@@ -505,6 +506,7 @@ export default {
505
506
  210: 2, // Sonoff Ultimate Switch T5-2C-86
506
507
  211: 3, // Sonoff Ultimate Switch T5-3C-86
507
508
  212: 4, // Sonoff Ultimate Switch T5-4C-86
509
+ 226: 1, // https://github.com/homebridge-plugins/homebridge-ewelink/issues/636
508
510
  243: 1, // Zigbee Bridge Ultra
509
511
  1000: 1, // "zigbee_ON_OFF_SWITCH_1000" (button device)
510
512
  1009: 1, // "" (Some sort of single switch device)
@@ -529,9 +531,11 @@ export default {
529
531
  7004: 1, // Zigbee Single-Channel Switch ­_Support OTA
530
532
  7005: 1, // Some switch, not entirely sure
531
533
  7006: 1, // Zigbee Curtain_Support OTA
534
+ 7010: 1, // Zigbee Micro https://github.com/homebridge-plugins/homebridge-ewelink/issues/630
532
535
  7014: 1, // some sensor https://github.com/homebridge-plugins/homebridge-ewelink/issues/494
533
536
  7016: 1, // https://sonoff.tech/product/gateway-and-sensors/snzb-06p/
534
537
  7017: 1, // https://github.com/homebridge-plugins/homebridge-ewelink/issues/518
538
+ 7027: 1, // Zigbee Smart Water Valve
535
539
  },
536
540
 
537
541
  cannotSupportDevices: {