@switchbot/homebridge-switchbot 3.7.1-beta.2 → 3.7.1-beta.21

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 (199) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +14 -0
  3. package/assets/security-privacy-bluetooth.png +0 -0
  4. package/config.schema.json +74 -2
  5. package/dist/device/blindtilt.d.ts +21 -9
  6. package/dist/device/blindtilt.d.ts.map +1 -1
  7. package/dist/device/blindtilt.js +167 -96
  8. package/dist/device/blindtilt.js.map +1 -1
  9. package/dist/device/bot-old.d.ts +132 -0
  10. package/dist/device/bot-old.d.ts.map +1 -0
  11. package/dist/device/bot-old.js +1482 -0
  12. package/dist/device/bot-old.js.map +1 -0
  13. package/dist/device/bot.d.ts +10 -9
  14. package/dist/device/bot.d.ts.map +1 -1
  15. package/dist/device/bot.js +186 -187
  16. package/dist/device/bot.js.map +1 -1
  17. package/dist/device/ceilinglight.d.ts +14 -13
  18. package/dist/device/ceilinglight.d.ts.map +1 -1
  19. package/dist/device/ceilinglight.js +87 -97
  20. package/dist/device/ceilinglight.js.map +1 -1
  21. package/dist/device/colorbulb.d.ts +13 -12
  22. package/dist/device/colorbulb.d.ts.map +1 -1
  23. package/dist/device/colorbulb.js +92 -105
  24. package/dist/device/colorbulb.js.map +1 -1
  25. package/dist/device/contact.d.ts +6 -5
  26. package/dist/device/contact.d.ts.map +1 -1
  27. package/dist/device/contact.js +71 -68
  28. package/dist/device/contact.js.map +1 -1
  29. package/dist/device/curtain.d.ts +17 -5
  30. package/dist/device/curtain.d.ts.map +1 -1
  31. package/dist/device/curtain.js +184 -118
  32. package/dist/device/curtain.js.map +1 -1
  33. package/dist/device/device.d.ts +18 -18
  34. package/dist/device/device.d.ts.map +1 -1
  35. package/dist/device/device.js +33 -36
  36. package/dist/device/device.js.map +1 -1
  37. package/dist/device/fan.d.ts +9 -8
  38. package/dist/device/fan.d.ts.map +1 -1
  39. package/dist/device/fan.js +94 -101
  40. package/dist/device/fan.js.map +1 -1
  41. package/dist/device/hub.d.ts +6 -5
  42. package/dist/device/hub.d.ts.map +1 -1
  43. package/dist/device/hub.js +69 -65
  44. package/dist/device/hub.js.map +1 -1
  45. package/dist/device/humidifier.d.ts +6 -5
  46. package/dist/device/humidifier.d.ts.map +1 -1
  47. package/dist/device/humidifier.js +89 -104
  48. package/dist/device/humidifier.js.map +1 -1
  49. package/dist/device/iosensor.d.ts +6 -5
  50. package/dist/device/iosensor.d.ts.map +1 -1
  51. package/dist/device/iosensor.js +72 -66
  52. package/dist/device/iosensor.js.map +1 -1
  53. package/dist/device/lightstrip.d.ts +7 -6
  54. package/dist/device/lightstrip.d.ts.map +1 -1
  55. package/dist/device/lightstrip.js +85 -99
  56. package/dist/device/lightstrip.js.map +1 -1
  57. package/dist/device/lock.d.ts +10 -9
  58. package/dist/device/lock.d.ts.map +1 -1
  59. package/dist/device/lock.js +89 -79
  60. package/dist/device/lock.js.map +1 -1
  61. package/dist/device/meter.d.ts +6 -5
  62. package/dist/device/meter.d.ts.map +1 -1
  63. package/dist/device/meter.js +72 -65
  64. package/dist/device/meter.js.map +1 -1
  65. package/dist/device/meterplus.d.ts +6 -5
  66. package/dist/device/meterplus.d.ts.map +1 -1
  67. package/dist/device/meterplus.js +72 -64
  68. package/dist/device/meterplus.js.map +1 -1
  69. package/dist/device/motion.d.ts +6 -5
  70. package/dist/device/motion.d.ts.map +1 -1
  71. package/dist/device/motion.js +65 -58
  72. package/dist/device/motion.js.map +1 -1
  73. package/dist/device/plug.d.ts +13 -12
  74. package/dist/device/plug.d.ts.map +1 -1
  75. package/dist/device/plug.js +68 -52
  76. package/dist/device/plug.js.map +1 -1
  77. package/dist/device/robotvacuumcleaner.d.ts +12 -11
  78. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  79. package/dist/device/robotvacuumcleaner.js +93 -82
  80. package/dist/device/robotvacuumcleaner.js.map +1 -1
  81. package/dist/device/waterdetector.d.ts +6 -5
  82. package/dist/device/waterdetector.d.ts.map +1 -1
  83. package/dist/device/waterdetector.js +73 -63
  84. package/dist/device/waterdetector.js.map +1 -1
  85. package/dist/homebridge-ui/server.js +2 -2
  86. package/dist/homebridge-ui/server.js.map +1 -1
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +0 -4
  89. package/dist/index.js.map +1 -1
  90. package/dist/irdevice/airconditioner.d.ts +8 -8
  91. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  92. package/dist/irdevice/airconditioner.js +61 -105
  93. package/dist/irdevice/airconditioner.js.map +1 -1
  94. package/dist/irdevice/airpurifier.d.ts +10 -10
  95. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  96. package/dist/irdevice/airpurifier.js +22 -42
  97. package/dist/irdevice/airpurifier.js.map +1 -1
  98. package/dist/irdevice/camera.d.ts +9 -9
  99. package/dist/irdevice/camera.d.ts.map +1 -1
  100. package/dist/irdevice/camera.js +15 -25
  101. package/dist/irdevice/camera.js.map +1 -1
  102. package/dist/irdevice/fan.d.ts +8 -8
  103. package/dist/irdevice/fan.d.ts.map +1 -1
  104. package/dist/irdevice/fan.js +21 -40
  105. package/dist/irdevice/fan.js.map +1 -1
  106. package/dist/irdevice/irdevice.d.ts +11 -11
  107. package/dist/irdevice/irdevice.d.ts.map +1 -1
  108. package/dist/irdevice/irdevice.js +14 -22
  109. package/dist/irdevice/irdevice.js.map +1 -1
  110. package/dist/irdevice/light.d.ts +9 -9
  111. package/dist/irdevice/light.d.ts.map +1 -1
  112. package/dist/irdevice/light.js +24 -47
  113. package/dist/irdevice/light.js.map +1 -1
  114. package/dist/irdevice/other.d.ts +9 -9
  115. package/dist/irdevice/other.d.ts.map +1 -1
  116. package/dist/irdevice/other.js +40 -83
  117. package/dist/irdevice/other.js.map +1 -1
  118. package/dist/irdevice/tv.d.ts +9 -9
  119. package/dist/irdevice/tv.d.ts.map +1 -1
  120. package/dist/irdevice/tv.js +29 -54
  121. package/dist/irdevice/tv.js.map +1 -1
  122. package/dist/irdevice/vacuumcleaner.d.ts +5 -5
  123. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  124. package/dist/irdevice/vacuumcleaner.js +11 -21
  125. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  126. package/dist/irdevice/waterheater.d.ts +5 -5
  127. package/dist/irdevice/waterheater.d.ts.map +1 -1
  128. package/dist/irdevice/waterheater.js +11 -22
  129. package/dist/irdevice/waterheater.js.map +1 -1
  130. package/dist/platform.d.ts +24 -23
  131. package/dist/platform.d.ts.map +1 -1
  132. package/dist/platform.js +509 -723
  133. package/dist/platform.js.map +1 -1
  134. package/dist/settings.d.ts +5 -2
  135. package/dist/settings.d.ts.map +1 -1
  136. package/dist/settings.js.map +1 -1
  137. package/dist/types/bledevicestatus.d.ts +9 -9
  138. package/dist/types/bledevicestatus.d.ts.map +1 -1
  139. package/dist/types/devicelist.d.ts +6 -6
  140. package/dist/types/devicelist.d.ts.map +1 -1
  141. package/dist/types/deviceresponse.d.ts +4 -4
  142. package/dist/types/deviceresponse.d.ts.map +1 -1
  143. package/dist/types/devicestatus.d.ts +2 -2
  144. package/dist/types/devicestatus.d.ts.map +1 -1
  145. package/dist/types/devicewebhookstatus.d.ts +5 -5
  146. package/dist/types/devicewebhookstatus.d.ts.map +1 -1
  147. package/dist/types/irdevicelist.d.ts +4 -4
  148. package/dist/types/irdevicelist.d.ts.map +1 -1
  149. package/dist/types/pushbody.d.ts +2 -2
  150. package/dist/types/pushbody.d.ts.map +1 -1
  151. package/dist/utils.d.ts +4 -4
  152. package/dist/utils.d.ts.map +1 -1
  153. package/dist/utils.js +9 -13
  154. package/dist/utils.js.map +1 -1
  155. package/eslint.config.js +48 -93
  156. package/package.json +64 -57
  157. package/src/device/blindtilt.ts +537 -456
  158. package/src/device/bot-old.ts +1540 -0
  159. package/src/device/bot.ts +732 -725
  160. package/src/device/ceilinglight.ts +359 -368
  161. package/src/device/colorbulb.ts +441 -453
  162. package/src/device/contact.ts +229 -226
  163. package/src/device/curtain.ts +526 -444
  164. package/src/device/device.ts +206 -208
  165. package/src/device/fan.ts +336 -345
  166. package/src/device/hub.ts +213 -214
  167. package/src/device/humidifier.ts +326 -347
  168. package/src/device/iosensor.ts +211 -209
  169. package/src/device/lightstrip.ts +391 -404
  170. package/src/device/lock.ts +292 -282
  171. package/src/device/meter.ts +212 -210
  172. package/src/device/meterplus.ts +212 -209
  173. package/src/device/motion.ts +198 -190
  174. package/src/device/plug.ts +203 -182
  175. package/src/device/robotvacuumcleaner.ts +280 -269
  176. package/src/device/waterdetector.ts +198 -189
  177. package/src/homebridge-ui/server.ts +16 -15
  178. package/src/index.ts +5 -6
  179. package/src/irdevice/airconditioner.ts +277 -326
  180. package/src/irdevice/airpurifier.ts +124 -143
  181. package/src/irdevice/camera.ts +58 -64
  182. package/src/irdevice/fan.ts +106 -123
  183. package/src/irdevice/irdevice.ts +116 -119
  184. package/src/irdevice/light.ts +119 -143
  185. package/src/irdevice/other.ts +417 -461
  186. package/src/irdevice/tv.ts +155 -177
  187. package/src/irdevice/vacuumcleaner.ts +54 -60
  188. package/src/irdevice/waterheater.ts +56 -63
  189. package/src/platform.ts +1491 -1681
  190. package/src/settings.ts +172 -169
  191. package/src/types/bledevicestatus.ts +252 -256
  192. package/src/types/devicelist.ts +79 -79
  193. package/src/types/deviceresponse.ts +12 -13
  194. package/src/types/devicestatus.ts +124 -124
  195. package/src/types/devicewebhookstatus.ts +126 -131
  196. package/src/types/irdevicelist.ts +10 -10
  197. package/src/types/pushbody.ts +5 -5
  198. package/src/utils.ts +64 -65
  199. package/tsconfig.json +10 -9
@@ -1,20 +1,22 @@
1
- /* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
2
- *
3
- * curtain.ts: @switchbot/homebridge-switchbot.
4
- */
5
- import { hostname } from 'os';
6
- import { deviceBase } from './device.js';
1
+ import { hostname } from 'node:os';
2
+ /*
3
+ * For Testing Locally:
4
+ * import { SwitchBotBLEModel, SwitchBotBLEModelName } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js';
5
+ */
7
6
  import { SwitchBotBLEModel, SwitchBotBLEModelName } from 'node-switchbot';
8
- import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs';
7
+ import { debounceTime, interval, skipWhile, Subject, take, tap } from 'rxjs';
8
+ import { deviceBase } from './device.js';
9
9
  export class Curtain extends deviceBase {
10
10
  platform;
11
11
  // Services
12
12
  WindowCovering;
13
13
  Battery;
14
14
  LightSensor;
15
+ OpenModeSwitch;
16
+ CloseModeSwitch;
15
17
  // OpenAPI
16
18
  deviceStatus;
17
- //Webhook
19
+ // Webhook
18
20
  webhookContext;
19
21
  // BLE
20
22
  serviceData;
@@ -22,6 +24,7 @@ export class Curtain extends deviceBase {
22
24
  setNewTarget;
23
25
  setNewTargetTimer;
24
26
  // Updates
27
+ curtainMoving;
25
28
  curtainUpdateInProgress;
26
29
  doCurtainUpdate;
27
30
  constructor(platform, accessory, device) {
@@ -31,6 +34,7 @@ export class Curtain extends deviceBase {
31
34
  accessory.category = 14 /* this.hap.Categories.WINDOW_COVERING */;
32
35
  // this is subject we use to track when we need to POST changes to the SwitchBot API
33
36
  this.doCurtainUpdate = new Subject();
37
+ this.curtainMoving = false;
34
38
  this.curtainUpdateInProgress = false;
35
39
  this.setNewTarget = false;
36
40
  // Initialize WindowCovering Service
@@ -45,45 +49,31 @@ export class Curtain extends deviceBase {
45
49
  };
46
50
  accessory.context.WindowCovering = this.WindowCovering;
47
51
  // Initialize WindowCovering Service
48
- this.WindowCovering.Service.
49
- setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name)
50
- .setCharacteristic(this.hap.Characteristic.ObstructionDetected, false)
51
- .getCharacteristic(this.hap.Characteristic.PositionState)
52
- .onGet(() => {
52
+ this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name).setCharacteristic(this.hap.Characteristic.ObstructionDetected, false).getCharacteristic(this.hap.Characteristic.PositionState).onGet(() => {
53
53
  return this.WindowCovering.PositionState;
54
54
  });
55
55
  // Initialize WindowCovering CurrentPosition
56
- this.WindowCovering.Service
57
- .getCharacteristic(this.hap.Characteristic.CurrentPosition)
58
- .setProps({
56
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.CurrentPosition).setProps({
59
57
  minStep: device.curtain?.set_minStep ?? 1,
60
58
  minValue: 0,
61
59
  maxValue: 100,
62
60
  validValueRanges: [0, 100],
63
- })
64
- .onGet(() => {
61
+ }).onGet(() => {
65
62
  return this.WindowCovering.CurrentPosition;
66
63
  });
67
64
  // Initialize WindowCovering TargetPosition
68
- this.WindowCovering.Service
69
- .getCharacteristic(this.hap.Characteristic.TargetPosition)
70
- .setProps({
65
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({
71
66
  minStep: device.curtain?.set_minStep ?? 1,
72
67
  minValue: 0,
73
68
  maxValue: 100,
74
69
  validValueRanges: [0, 100],
75
- })
76
- .onGet(() => {
70
+ }).onGet(() => {
77
71
  return this.WindowCovering.TargetPosition;
78
- })
79
- .onSet(this.TargetPositionSet.bind(this));
72
+ }).onSet(this.TargetPositionSet.bind(this));
80
73
  // Initialize WindowCovering TargetPosition
81
- this.WindowCovering.Service
82
- .getCharacteristic(this.hap.Characteristic.HoldPosition)
83
- .onGet(() => {
74
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.HoldPosition).onGet(() => {
84
75
  return this.WindowCovering.HoldPosition;
85
- })
86
- .onSet(this.HoldPositionSet.bind(this));
76
+ }).onSet(this.HoldPositionSet.bind(this));
87
77
  // Initialize Battery Service
88
78
  accessory.context.Battery = accessory.context.Battery ?? {};
89
79
  this.Battery = {
@@ -95,24 +85,17 @@ export class Curtain extends deviceBase {
95
85
  };
96
86
  accessory.context.Battery = this.Battery;
97
87
  // Initialize Battery Service
98
- this.Battery.Service
99
- .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
100
- .getCharacteristic(this.hap.Characteristic.BatteryLevel)
101
- .onGet(() => {
88
+ this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name).getCharacteristic(this.hap.Characteristic.BatteryLevel).onGet(() => {
102
89
  return this.Battery.BatteryLevel;
103
90
  });
104
- this.Battery.Service
105
- .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
106
- .onGet(() => {
91
+ this.Battery.Service.getCharacteristic(this.hap.Characteristic.StatusLowBattery).onGet(() => {
107
92
  return this.Battery.StatusLowBattery;
108
93
  });
109
- this.Battery.Service
110
- .getCharacteristic(this.hap.Characteristic.ChargingState)
111
- .onGet(() => {
94
+ this.Battery.Service.getCharacteristic(this.hap.Characteristic.ChargingState).onGet(() => {
112
95
  return this.Battery.ChargingState;
113
96
  });
114
97
  // Initialize LightSensor Service
115
- if (device.curtain?.hide_lightsensor) {
98
+ if (device.curtain?.hide_lightsensor || (device.deviceType !== 'curtain' && device.deviceType !== 'curtain3')) {
116
99
  if (this.LightSensor?.Service) {
117
100
  this.debugLog('Removing Light Sensor Service');
118
101
  this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor);
@@ -120,7 +103,7 @@ export class Curtain extends deviceBase {
120
103
  accessory.context.LightSensor = {};
121
104
  }
122
105
  }
123
- else {
106
+ else if (device.deviceType === 'curtain' || device.deviceType === 'curtain3') {
124
107
  accessory.context.LightSensor = accessory.context.LightSensor ?? {};
125
108
  this.LightSensor = {
126
109
  Name: `${accessory.displayName} Light Sensor`,
@@ -129,20 +112,80 @@ export class Curtain extends deviceBase {
129
112
  };
130
113
  accessory.context.LightSensor = this.LightSensor;
131
114
  // Initialize LightSensor Characteristic
132
- this.LightSensor.Service
133
- .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name)
134
- .setCharacteristic(this.hap.Characteristic.StatusActive, true)
135
- .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
136
- .onGet(() => {
115
+ this.LightSensor.Service.setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name).setCharacteristic(this.hap.Characteristic.StatusActive, true).getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel).onGet(() => {
137
116
  return this.LightSensor.CurrentAmbientLightLevel;
138
117
  });
139
118
  }
119
+ // Initialize Open Mode Switch Service
120
+ if (!device.curtain?.silentModeSwitch) {
121
+ if (this.OpenModeSwitch?.Service) {
122
+ this.debugLog('Removing Open Mode Switch Service');
123
+ this.OpenModeSwitch.Service = this.accessory.getService(this.hap.Service.Switch);
124
+ accessory.removeService(this.OpenModeSwitch.Service);
125
+ accessory.context.OpenModeSwitch = {};
126
+ }
127
+ }
128
+ else {
129
+ accessory.context.OpenModeSwitch = accessory.context.OpenModeSwitch ?? {};
130
+ this.OpenModeSwitch = {
131
+ Name: `${accessory.displayName} Silent Open Mode`,
132
+ Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch),
133
+ On: accessory.context.OpenModeSwitch.On ?? false,
134
+ };
135
+ accessory.context.OpenModeSwitch = this.OpenModeSwitch;
136
+ // Initialize Open Mode Switch Service
137
+ this.OpenModeSwitch.Service.setCharacteristic(this.hap.Characteristic.Name, this.OpenModeSwitch.Name).getCharacteristic(this.hap.Characteristic.On).onGet(() => {
138
+ return this.OpenModeSwitch?.On ?? false;
139
+ });
140
+ this.OpenModeSwitch.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.OpenModeSwitchSet.bind(this));
141
+ }
142
+ // Initialize Close Mode Switch Service
143
+ if (!device.curtain?.silentModeSwitch) {
144
+ if (this.CloseModeSwitch?.Service) {
145
+ this.debugLog('Removing Close Mode Switch Service');
146
+ this.CloseModeSwitch.Service = this.accessory.getService(this.hap.Service.Switch);
147
+ accessory.removeService(this.CloseModeSwitch.Service);
148
+ accessory.context.CloseModeSwitch = {};
149
+ }
150
+ }
151
+ else {
152
+ accessory.context.CloseModeSwitch = accessory.context.CloseModeSwitch ?? {};
153
+ this.CloseModeSwitch = {
154
+ Name: `${accessory.displayName} Silent Close Mode`,
155
+ Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch),
156
+ On: accessory.context.CloseModeSwitch.On ?? false,
157
+ };
158
+ accessory.context.CloseModeSwitch = this.CloseModeSwitch;
159
+ // Initialize Close Mode Switch Service
160
+ this.CloseModeSwitch.Service.setCharacteristic(this.hap.Characteristic.Name, this.CloseModeSwitch.Name).getCharacteristic(this.hap.Characteristic.On).onGet(() => {
161
+ return this.CloseModeSwitch?.On ?? false;
162
+ });
163
+ this.CloseModeSwitch.Service.getCharacteristic(this.hap.Characteristic.On).onSet(this.CloseModeSwitchSet.bind(this));
164
+ }
140
165
  // Retrieve initial values and updateHomekit
141
- this.debugLog('Retrieve initial values and update Homekit');
142
- this.refreshStatus();
143
- //regisiter webhook event handler
144
- this.debugLog('Registering Webhook Event Handler');
145
- this.registerWebhook();
166
+ try {
167
+ this.debugLog('Retrieve initial values and update Homekit');
168
+ this.refreshStatus();
169
+ }
170
+ catch (e) {
171
+ this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`);
172
+ }
173
+ // regisiter webhook event handler if enabled
174
+ try {
175
+ this.debugLog('Registering Webhook Event Handler');
176
+ this.registerWebhook();
177
+ }
178
+ catch (e) {
179
+ this.errorLog(`failed to registerWebhook, Error: ${e}`);
180
+ }
181
+ // regisiter platform BLE event handler if enabled
182
+ try {
183
+ this.debugLog('Registering Platform BLE Event Handler');
184
+ this.registerPlatformBLE();
185
+ }
186
+ catch (e) {
187
+ this.errorLog(`failed to registerPlatformBLE, Error: ${e}`);
188
+ }
146
189
  // History
147
190
  this.history();
148
191
  // Start an update interval
@@ -153,7 +196,7 @@ export class Curtain extends deviceBase {
153
196
  });
154
197
  // update slide progress
155
198
  interval(this.deviceUpdateRate * 1000)
156
- //.pipe(skipWhile(() => this.curtainUpdateInProgress))
199
+ .pipe(skipWhile(() => !this.curtainMoving))
157
200
  .subscribe(async () => {
158
201
  if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
159
202
  return;
@@ -206,8 +249,8 @@ export class Curtain extends deviceBase {
206
249
  storage: 'fs',
207
250
  filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
208
251
  });
209
- const motion = this.accessory.getService(this.hap.Service.MotionSensor) ||
210
- this.accessory.addService(this.hap.Service.MotionSensor, 'Motion');
252
+ const motion = this.accessory.getService(this.hap.Service.MotionSensor)
253
+ || this.accessory.addService(this.hap.Service.MotionSensor, 'Motion');
211
254
  motion.addOptionalCharacteristic(this.platform.eve.Characteristics.LastActivation);
212
255
  motion.getCharacteristic(this.platform.eve.Characteristics.LastActivation).onGet(() => {
213
256
  const lastActivation = this.accessory.context.lastActivation
@@ -234,7 +277,7 @@ export class Curtain extends deviceBase {
234
277
  const motion = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0;
235
278
  this.historyService.addEntry({
236
279
  time: Math.round(new Date().valueOf() / 1000),
237
- motion: motion,
280
+ motion,
238
281
  });
239
282
  setTimeout(async () => {
240
283
  await this.updateHistory();
@@ -242,8 +285,7 @@ export class Curtain extends deviceBase {
242
285
  }
243
286
  async BLEparseStatus() {
244
287
  await this.debugLog('BLEparseStatus');
245
- await this.debugLog(`(position, battery) = BLE:(${this.serviceData.position}, ${this.serviceData.battery}),`
246
- + ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
288
+ await this.debugLog(`(position, battery) = BLE:(${this.serviceData.position}, ${this.serviceData.battery}), current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
247
289
  // CurrentPosition
248
290
  this.WindowCovering.CurrentPosition = 100 - this.serviceData.position;
249
291
  await this.getCurrentPostion();
@@ -253,21 +295,20 @@ export class Curtain extends deviceBase {
253
295
  const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
254
296
  const lightLevel = this.serviceData.lightLevel;
255
297
  this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 19);
256
- this.debugLog(`LightLevel: ${this.serviceData.lightLevel},`
257
- + ` CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`);
298
+ this.debugLog(`LightLevel: ${this.serviceData.lightLevel}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`);
258
299
  }
259
300
  // BatteryLevel
260
301
  this.Battery.BatteryLevel = this.serviceData.battery;
261
302
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
262
303
  // StatusLowBattery
263
304
  this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
264
- ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
305
+ ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
306
+ : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
265
307
  await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
266
308
  }
267
309
  async openAPIparseStatus() {
268
310
  await this.debugLog('openAPIparseStatus');
269
- await this.debugLog(`(slidePosition, battery) = OpenAPI:(${this.deviceStatus.slidePosition}, ${this.deviceStatus.battery}),`
270
- + ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
311
+ await this.debugLog(`(slidePosition, battery, version) = OpenAPI:(${this.deviceStatus.slidePosition}, ${this.deviceStatus.battery}, ${this.deviceStatus.version}), current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel}, ${this.accessory.context.version})`);
271
312
  // CurrentPosition
272
313
  this.WindowCovering.CurrentPosition = 100 - this.deviceStatus.slidePosition;
273
314
  await this.setMinMax();
@@ -276,6 +317,7 @@ export class Curtain extends deviceBase {
276
317
  await this.infoLog('Checking Status ...');
277
318
  }
278
319
  if (this.setNewTarget && this.deviceStatus.moving) {
320
+ this.curtainMoving = true;
279
321
  await this.setMinMax();
280
322
  if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
281
323
  await this.debugLog(`Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
@@ -297,13 +339,13 @@ export class Curtain extends deviceBase {
297
339
  }
298
340
  }
299
341
  else {
342
+ this.curtainMoving = false;
300
343
  await this.debugLog(`Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
301
344
  this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
302
345
  this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
303
346
  await this.debugLog(`Stopped, PositionState: ${this.WindowCovering.PositionState}`);
304
347
  }
305
- await this.debugLog(`CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition},`
306
- + ` PositionState: ${this.WindowCovering.PositionState},`);
348
+ await this.debugLog(`CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
307
349
  // Brightness
308
350
  if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) {
309
351
  const set_minLux = this.device.curtain?.set_minLux ?? 1;
@@ -317,7 +359,8 @@ export class Curtain extends deviceBase {
317
359
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
318
360
  // StatusLowBattery
319
361
  this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
320
- ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
362
+ ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
363
+ : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
321
364
  await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
322
365
  // Firmware Version
323
366
  if (this.deviceStatus.version) {
@@ -336,8 +379,7 @@ export class Curtain extends deviceBase {
336
379
  }
337
380
  async parseStatusWebhook() {
338
381
  await this.debugLog('parseStatusWebhook');
339
- await this.debugLog(`(slidePosition, battery) = Webhook:(${this.webhookContext.slidePosition}, ${this.webhookContext.battery}),`
340
- + ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
382
+ await this.debugLog(`(slidePosition, battery) = Webhook:(${this.webhookContext.slidePosition}, ${this.webhookContext.battery}), current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
341
383
  // CurrentPosition
342
384
  this.WindowCovering.CurrentPosition = 100 - this.webhookContext.slidePosition;
343
385
  await this.getCurrentPostion();
@@ -346,7 +388,8 @@ export class Curtain extends deviceBase {
346
388
  await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
347
389
  // StatusLowBattery
348
390
  this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
349
- ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
391
+ ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW
392
+ : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
350
393
  await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
351
394
  }
352
395
  /**
@@ -356,7 +399,7 @@ export class Curtain extends deviceBase {
356
399
  if (!this.device.enableCloudService && this.OpenAPI) {
357
400
  await this.errorLog(`refreshStatus enableCloudService: ${this.device.enableCloudService}`);
358
401
  }
359
- else if (this.BLE || this.config.options?.BLE) {
402
+ else if (this.BLE) {
360
403
  await this.BLERefreshStatus();
361
404
  }
362
405
  else if (this.OpenAPI && this.platform.config.credentials?.token) {
@@ -369,46 +412,27 @@ export class Curtain extends deviceBase {
369
412
  }
370
413
  async BLERefreshStatus() {
371
414
  await this.debugLog('BLERefreshStatus');
372
- if (this.config.options?.BLE) {
373
- await this.debugLog('is listening to Platform BLE.');
374
- this.device.bleMac = this.device.deviceId.match(/.{1,2}/g).join(':').toLowerCase();
375
- await this.debugLog(`bleMac: ${this.device.bleMac}`);
376
- this.platform.bleEventHandler[this.device.bleMac] = async (context) => {
377
- try {
378
- await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
379
- this.serviceData = context;
415
+ const switchbot = await this.switchbotBLE();
416
+ if (switchbot === undefined) {
417
+ await this.BLERefreshConnection(switchbot);
418
+ }
419
+ else {
420
+ // Start to monitor advertisement packets
421
+ (async () => {
422
+ // Start to monitor advertisement packets
423
+ const serviceData = await this.monitorAdvertisementPackets(switchbot);
424
+ // Update HomeKit
425
+ if ((serviceData.model === SwitchBotBLEModel.Curtain || SwitchBotBLEModel.Curtain3)
426
+ && (serviceData.modelName === SwitchBotBLEModelName.Curtain || SwitchBotBLEModelName.Curtain3)) {
427
+ this.serviceData = serviceData;
380
428
  await this.BLEparseStatus();
381
429
  await this.updateHomeKitCharacteristics();
382
430
  }
383
- catch (e) {
384
- await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
431
+ else {
432
+ await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
433
+ await this.BLERefreshConnection(switchbot);
385
434
  }
386
- };
387
- }
388
- else {
389
- await this.debugLog('is using Device BLE Scanning.');
390
- const switchbot = await this.switchbotBLE();
391
- if (switchbot === undefined) {
392
- await this.BLERefreshConnection(switchbot);
393
- }
394
- else {
395
- // Start to monitor advertisement packets
396
- (async () => {
397
- // Start to monitor advertisement packets
398
- const serviceData = await this.monitorAdvertisementPackets(switchbot);
399
- // Update HomeKit
400
- if ((serviceData.model === SwitchBotBLEModel.Curtain || SwitchBotBLEModel.Curtain3)
401
- && (serviceData.modelName === SwitchBotBLEModelName.Curtain || SwitchBotBLEModelName.Curtain3)) {
402
- this.serviceData = serviceData;
403
- await this.BLEparseStatus();
404
- await this.updateHomeKitCharacteristics();
405
- }
406
- else {
407
- await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
408
- await this.BLERefreshConnection(switchbot);
409
- }
410
- })();
411
- }
435
+ })();
412
436
  }
413
437
  }
414
438
  async openAPIRefreshStatus() {
@@ -417,7 +441,6 @@ export class Curtain extends deviceBase {
417
441
  const { body, statusCode } = await this.deviceRefreshStatus();
418
442
  const deviceStatus = await body.json();
419
443
  await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
420
- ;
421
444
  if (await this.successfulStatusCodes(statusCode, deviceStatus)) {
422
445
  await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
423
446
  this.deviceStatus = deviceStatus.body;
@@ -453,6 +476,28 @@ export class Curtain extends deviceBase {
453
476
  await this.debugLog('is not listening webhook.');
454
477
  }
455
478
  }
479
+ async registerPlatformBLE() {
480
+ await this.debugLog('registerPlatformBLE');
481
+ if (this.config.options?.BLE) {
482
+ await this.debugLog('is listening to Platform BLE.');
483
+ this.device.bleMac = this.device.deviceId.match(/.{1,2}/g).join(':').toLowerCase();
484
+ await this.debugLog(`bleMac: ${this.device.bleMac}`);
485
+ this.platform.bleEventHandler[this.device.bleMac] = async (context) => {
486
+ try {
487
+ await this.debugLog(`received BLE: ${JSON.stringify(context)}`);
488
+ this.serviceData = context;
489
+ await this.BLEparseStatus();
490
+ await this.updateHomeKitCharacteristics();
491
+ }
492
+ catch (e) {
493
+ await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`);
494
+ }
495
+ };
496
+ }
497
+ else {
498
+ await this.debugLog('is not listening to Platform BLE');
499
+ }
500
+ }
456
501
  /**
457
502
  * Pushes the requested changes to the SwitchBot API
458
503
  */
@@ -484,7 +529,7 @@ export class Curtain extends deviceBase {
484
529
  const switchbot = await this.platform.connectBLE(this.accessory, this.device);
485
530
  await this.convertBLEAddress();
486
531
  const { setPositionMode, Mode } = await this.setPerformance();
487
- const adjustedMode = setPositionMode === 1 ? 0x01 : 0xff;
532
+ const adjustedMode = setPositionMode === 1 ? 0x01 : 0xFF;
488
533
  await this.debugLog(`Mode: ${Mode}, setPositionMode: ${setPositionMode}`);
489
534
  if (switchbot !== false) {
490
535
  switchbot
@@ -513,8 +558,7 @@ export class Curtain extends deviceBase {
513
558
  }
514
559
  }
515
560
  else {
516
- await this.debugLog(`No changes (BLEpushChanges), TargetPosition: ${this.WindowCovering.TargetPosition},`
517
- + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
561
+ await this.debugLog(`No changes (BLEpushChanges), TargetPosition: ${this.WindowCovering.TargetPosition}, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
518
562
  }
519
563
  }
520
564
  async openAPIpushChanges() {
@@ -560,8 +604,7 @@ export class Curtain extends deviceBase {
560
604
  }
561
605
  }
562
606
  else {
563
- await this.debugLog(`No changes (openAPIpushChanges), CurrentPosition: ${this.WindowCovering.CurrentPosition},`
564
- + ` TargetPosition: ${this.WindowCovering.TargetPosition}`);
607
+ await this.debugLog(`No changes (openAPIpushChanges), CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition}`);
565
608
  }
566
609
  }
567
610
  /**
@@ -623,6 +666,28 @@ export class Curtain extends deviceBase {
623
666
  this.WindowCovering.HoldPosition = value;
624
667
  this.doCurtainUpdate.next();
625
668
  }
669
+ /**
670
+ * Handle requests to set the value of the "Target Position" characteristic
671
+ */
672
+ async OpenModeSwitchSet(value) {
673
+ if (this.OpenModeSwitch && this.device.curtain?.silentModeSwitch) {
674
+ this.debugLog(`Silent Open Mode: ${value}`);
675
+ this.OpenModeSwitch.On = value;
676
+ this.accessory.context.OpenModeSwitch.On = value;
677
+ this.doCurtainUpdate.next();
678
+ }
679
+ }
680
+ /**
681
+ * Handle requests to set the value of the "Target Position" characteristic
682
+ */
683
+ async CloseModeSwitchSet(value) {
684
+ if (this.CloseModeSwitch && this.device.curtain?.silentModeSwitch) {
685
+ this.debugLog(`Silent Close Mode: ${value}`);
686
+ this.CloseModeSwitch.On = value;
687
+ this.accessory.context.CloseModeSwitch.On = value;
688
+ this.doCurtainUpdate.next();
689
+ }
690
+ }
626
691
  async updateHomeKitCharacteristics() {
627
692
  await this.setMinMax();
628
693
  // CurrentPosition
@@ -662,11 +727,11 @@ export class Curtain extends deviceBase {
662
727
  let setPositionMode;
663
728
  let Mode;
664
729
  if (Number(this.WindowCovering.TargetPosition) > 50) {
665
- if (this.device.curtain?.setOpenMode === '1') {
730
+ if (this.device.curtain?.setOpenMode === '1' || this.OpenModeSwitch?.On) {
666
731
  setPositionMode = 1;
667
732
  Mode = 'Silent Mode';
668
733
  }
669
- else if (this.device.curtain?.setOpenMode === '0') {
734
+ else if (this.device.curtain?.setOpenMode === '0' || !this.OpenModeSwitch?.On) {
670
735
  setPositionMode = 0;
671
736
  Mode = 'Performance Mode';
672
737
  }
@@ -676,11 +741,11 @@ export class Curtain extends deviceBase {
676
741
  }
677
742
  }
678
743
  else {
679
- if (this.device.curtain?.setCloseMode === '1') {
744
+ if (this.device.curtain?.setCloseMode === '1' || this.CloseModeSwitch?.On) {
680
745
  setPositionMode = 1;
681
746
  Mode = 'Silent Mode';
682
747
  }
683
- else if (this.device.curtain?.setOpenMode === '0') {
748
+ else if (this.device.curtain?.setCloseMode === '0' || !this.CloseModeSwitch?.On) {
684
749
  setPositionMode = 0;
685
750
  Mode = 'Performance Mode';
686
751
  }
@@ -695,6 +760,7 @@ export class Curtain extends deviceBase {
695
760
  await this.setMinMax();
696
761
  await this.debugLog(`CurrentPosition ${this.WindowCovering.CurrentPosition}`);
697
762
  if (this.setNewTarget) {
763
+ this.curtainMoving = true;
698
764
  this.infoLog('Checking Status ...');
699
765
  await this.setMinMax();
700
766
  if (this.WindowCovering.TargetPosition > this.WindowCovering.CurrentPosition) {
@@ -717,13 +783,13 @@ export class Curtain extends deviceBase {
717
783
  }
718
784
  }
719
785
  else {
786
+ this.curtainMoving = false;
720
787
  await this.debugLog(`Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
721
788
  this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
722
789
  this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
723
790
  await this.debugLog(`Stopped, PositionState: ${this.WindowCovering.PositionState}`);
724
791
  }
725
- await this.debugLog(`CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition},`
726
- + ` PositionState: ${this.WindowCovering.PositionState},`);
792
+ await this.debugLog(`CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
727
793
  }
728
794
  async setMinMax() {
729
795
  if (this.device.curtain?.set_min) {