@switchbot/homebridge-switchbot 3.5.0-beta.9 → 3.5.0

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 (178) hide show
  1. package/.vscode/settings.json +4 -1
  2. package/CHANGELOG.md +4 -1
  3. package/config.schema.json +166 -24
  4. package/dist/device/blindtilt.d.ts +21 -96
  5. package/dist/device/blindtilt.d.ts.map +1 -1
  6. package/dist/device/blindtilt.js +439 -817
  7. package/dist/device/blindtilt.js.map +1 -1
  8. package/dist/device/bot.d.ts +27 -72
  9. package/dist/device/bot.d.ts.map +1 -1
  10. package/dist/device/bot.js +835 -865
  11. package/dist/device/bot.js.map +1 -1
  12. package/dist/device/ceilinglight.d.ts +15 -69
  13. package/dist/device/ceilinglight.d.ts.map +1 -1
  14. package/dist/device/ceilinglight.js +287 -653
  15. package/dist/device/ceilinglight.js.map +1 -1
  16. package/dist/device/colorbulb.d.ts +15 -76
  17. package/dist/device/colorbulb.d.ts.map +1 -1
  18. package/dist/device/colorbulb.js +354 -732
  19. package/dist/device/colorbulb.js.map +1 -1
  20. package/dist/device/contact.d.ts +14 -66
  21. package/dist/device/contact.d.ts.map +1 -1
  22. package/dist/device/contact.js +268 -576
  23. package/dist/device/contact.js.map +1 -1
  24. package/dist/device/curtain.d.ts +18 -89
  25. package/dist/device/curtain.d.ts.map +1 -1
  26. package/dist/device/curtain.js +431 -851
  27. package/dist/device/curtain.js.map +1 -1
  28. package/dist/device/device.d.ts +60 -0
  29. package/dist/device/device.d.ts.map +1 -0
  30. package/dist/device/device.js +739 -0
  31. package/dist/device/device.js.map +1 -0
  32. package/dist/device/fan.d.ts +63 -0
  33. package/dist/device/fan.d.ts.map +1 -0
  34. package/dist/device/fan.js +698 -0
  35. package/dist/device/fan.js.map +1 -0
  36. package/dist/device/hub.d.ts +15 -60
  37. package/dist/device/hub.d.ts.map +1 -1
  38. package/dist/device/hub.js +257 -631
  39. package/dist/device/hub.js.map +1 -1
  40. package/dist/device/humidifier.d.ts +11 -66
  41. package/dist/device/humidifier.d.ts.map +1 -1
  42. package/dist/device/humidifier.js +266 -629
  43. package/dist/device/humidifier.js.map +1 -1
  44. package/dist/device/iosensor.d.ts +12 -64
  45. package/dist/device/iosensor.d.ts.map +1 -1
  46. package/dist/device/iosensor.js +215 -615
  47. package/dist/device/iosensor.js.map +1 -1
  48. package/dist/device/lightstrip.d.ts +12 -70
  49. package/dist/device/lightstrip.d.ts.map +1 -1
  50. package/dist/device/lightstrip.js +336 -645
  51. package/dist/device/lightstrip.js.map +1 -1
  52. package/dist/device/lock.d.ts +17 -63
  53. package/dist/device/lock.d.ts.map +1 -1
  54. package/dist/device/lock.js +290 -595
  55. package/dist/device/lock.js.map +1 -1
  56. package/dist/device/meter.d.ts +15 -64
  57. package/dist/device/meter.d.ts.map +1 -1
  58. package/dist/device/meter.js +211 -608
  59. package/dist/device/meter.js.map +1 -1
  60. package/dist/device/meterplus.d.ts +15 -65
  61. package/dist/device/meterplus.d.ts.map +1 -1
  62. package/dist/device/meterplus.js +205 -615
  63. package/dist/device/meterplus.js.map +1 -1
  64. package/dist/device/motion.d.ts +12 -69
  65. package/dist/device/motion.d.ts.map +1 -1
  66. package/dist/device/motion.js +202 -563
  67. package/dist/device/motion.js.map +1 -1
  68. package/dist/device/plug.d.ts +10 -57
  69. package/dist/device/plug.d.ts.map +1 -1
  70. package/dist/device/plug.js +125 -484
  71. package/dist/device/plug.js.map +1 -1
  72. package/dist/device/robotvacuumcleaner.d.ts +13 -63
  73. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  74. package/dist/device/robotvacuumcleaner.js +233 -549
  75. package/dist/device/robotvacuumcleaner.js.map +1 -1
  76. package/dist/device/waterdetector.d.ts +11 -63
  77. package/dist/device/waterdetector.d.ts.map +1 -1
  78. package/dist/device/waterdetector.js +170 -493
  79. package/dist/device/waterdetector.js.map +1 -1
  80. package/dist/homebridge-ui/public/index.html +22 -16
  81. package/dist/homebridge-ui/server.js +1 -1
  82. package/dist/homebridge-ui/server.js.map +1 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +2 -2
  86. package/dist/index.js.map +1 -1
  87. package/dist/irdevice/airconditioner.d.ts +10 -50
  88. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  89. package/dist/irdevice/airconditioner.js +340 -468
  90. package/dist/irdevice/airconditioner.js.map +1 -1
  91. package/dist/irdevice/airpurifier.d.ts +8 -39
  92. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  93. package/dist/irdevice/airpurifier.js +105 -319
  94. package/dist/irdevice/airpurifier.js.map +1 -1
  95. package/dist/irdevice/camera.d.ts +7 -36
  96. package/dist/irdevice/camera.d.ts.map +1 -1
  97. package/dist/irdevice/camera.js +44 -285
  98. package/dist/irdevice/camera.js.map +1 -1
  99. package/dist/irdevice/fan.d.ts +7 -43
  100. package/dist/irdevice/fan.d.ts.map +1 -1
  101. package/dist/irdevice/fan.js +92 -350
  102. package/dist/irdevice/fan.js.map +1 -1
  103. package/dist/irdevice/irdevice.d.ts +40 -0
  104. package/dist/irdevice/irdevice.d.ts.map +1 -0
  105. package/dist/irdevice/irdevice.js +369 -0
  106. package/dist/irdevice/irdevice.js.map +1 -0
  107. package/dist/irdevice/light.d.ts +12 -45
  108. package/dist/irdevice/light.d.ts.map +1 -1
  109. package/dist/irdevice/light.js +118 -362
  110. package/dist/irdevice/light.js.map +1 -1
  111. package/dist/irdevice/other.d.ts +19 -48
  112. package/dist/irdevice/other.d.ts.map +1 -1
  113. package/dist/irdevice/other.js +438 -570
  114. package/dist/irdevice/other.js.map +1 -1
  115. package/dist/irdevice/tv.d.ts +8 -40
  116. package/dist/irdevice/tv.d.ts.map +1 -1
  117. package/dist/irdevice/tv.js +91 -339
  118. package/dist/irdevice/tv.js.map +1 -1
  119. package/dist/irdevice/vacuumcleaner.d.ts +7 -36
  120. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  121. package/dist/irdevice/vacuumcleaner.js +45 -285
  122. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  123. package/dist/irdevice/waterheater.d.ts +7 -36
  124. package/dist/irdevice/waterheater.d.ts.map +1 -1
  125. package/dist/irdevice/waterheater.js +47 -291
  126. package/dist/irdevice/waterheater.js.map +1 -1
  127. package/dist/platform.d.ts +22 -8
  128. package/dist/platform.d.ts.map +1 -1
  129. package/dist/platform.js +342 -191
  130. package/dist/platform.js.map +1 -1
  131. package/dist/settings.d.ts +64 -17
  132. package/dist/settings.d.ts.map +1 -1
  133. package/dist/settings.js +16 -552
  134. package/dist/settings.js.map +1 -1
  135. package/dist/utils.d.ts +77 -0
  136. package/dist/utils.d.ts.map +1 -1
  137. package/dist/utils.js +638 -1
  138. package/dist/utils.js.map +1 -1
  139. package/eslint.config.js +96 -0
  140. package/package.json +20 -16
  141. package/src/custom.d.ts +2 -1
  142. package/src/device/blindtilt.ts +472 -888
  143. package/src/device/bot.ts +893 -968
  144. package/src/device/ceilinglight.ts +304 -709
  145. package/src/device/colorbulb.ts +370 -803
  146. package/src/device/contact.ts +301 -611
  147. package/src/device/curtain.ts +460 -908
  148. package/src/device/device.ts +755 -0
  149. package/src/device/fan.ts +749 -0
  150. package/src/device/hub.ts +294 -664
  151. package/src/device/humidifier.ts +299 -696
  152. package/src/device/iosensor.ts +251 -644
  153. package/src/device/lightstrip.ts +362 -712
  154. package/src/device/lock.ts +328 -653
  155. package/src/device/meter.ts +247 -637
  156. package/src/device/meterplus.ts +240 -644
  157. package/src/device/motion.ts +230 -598
  158. package/src/device/plug.ts +139 -525
  159. package/src/device/robotvacuumcleaner.ts +258 -593
  160. package/src/device/waterdetector.ts +191 -522
  161. package/src/homebridge-ui/public/index.html +22 -16
  162. package/src/homebridge-ui/server.ts +1 -1
  163. package/src/index.ts +4 -3
  164. package/src/irdevice/airconditioner.ts +370 -527
  165. package/src/irdevice/airpurifier.ts +132 -356
  166. package/src/irdevice/camera.ts +58 -311
  167. package/src/irdevice/fan.ts +109 -379
  168. package/src/irdevice/irdevice.ts +382 -0
  169. package/src/irdevice/light.ts +157 -412
  170. package/src/irdevice/other.ts +507 -648
  171. package/src/irdevice/tv.ts +121 -375
  172. package/src/irdevice/vacuumcleaner.ts +59 -311
  173. package/src/irdevice/waterheater.ts +61 -320
  174. package/src/platform.ts +395 -259
  175. package/src/settings.ts +118 -609
  176. package/src/utils.ts +642 -1
  177. package/.github/npm-version-script.cjs +0 -81
  178. package/src/device/waterdetector.json +0 -11
@@ -1,193 +1,181 @@
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';
1
6
  import { request } from 'undici';
2
- import { sleep } from '../utils.js';
3
- import { MqttClient } from 'mqtt';
4
7
  import { interval, Subject } from 'rxjs';
5
- import asyncmqtt from 'async-mqtt';
6
- import { SwitchBotPlatform } from '../platform.js';
8
+ import { deviceBase } from './device.js';
9
+ import { Devices } from '../settings.js';
7
10
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
8
- import { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange, API, Logging, HAP } from 'homebridge';
9
- import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js';
10
- import { hostname } from 'os';
11
-
12
- export class Curtain {
13
- public readonly api: API;
14
- public readonly log: Logging;
15
- public readonly config!: SwitchBotPlatformConfig;
16
- protected readonly hap: HAP;
17
- // Services
18
- batteryService: Service;
19
- lightSensorService?: Service;
20
- windowCoveringService!: Service;
21
11
 
22
- // Characteristic Values
23
- BatteryLevel!: CharacteristicValue;
24
- HoldPosition!: CharacteristicValue;
25
- PositionState!: CharacteristicValue;
26
- TargetPosition!: CharacteristicValue;
27
- CurrentPosition!: CharacteristicValue;
28
- FirmwareRevision!: CharacteristicValue;
29
- StatusLowBattery!: CharacteristicValue;
30
- CurrentAmbientLightLevel?: CharacteristicValue;
12
+ import type { SwitchBotPlatform } from '../platform.js';
13
+ import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
14
+ import type { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange } from 'homebridge';
31
15
 
32
- // OpenAPI Status
33
- OpenAPI_InMotion: deviceStatus['moving'];
34
- OpenAPI_BatteryLevel: deviceStatus['battery'];
35
- OpenAPI_FirmwareRevision: deviceStatus['version'];
36
- OpenAPI_CurrentPosition: deviceStatus['slidePosition'];
37
- OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness'];
38
-
39
- // Others
40
- Mode!: string;
41
- setPositionMode?: string;
42
-
43
- // BLE Status
44
- BLE_InMotion: serviceData['inMotion'];
45
- BLE_BatteryLevel: serviceData['battery'];
46
- BLE_Calibration: serviceData['calibration'];
47
- BLE_CurrentPosition: serviceData['position'];
48
- BLE_CurrentAmbientLightLevel: serviceData['lightLevel'];
49
-
50
- // BLE Others
51
- BLE_IsConnected?: boolean;
52
- spaceBetweenLevels!: number;
16
+ export class Curtain extends deviceBase {
17
+ // Services
18
+ private WindowCovering: {
19
+ Name: CharacteristicValue;
20
+ Service: Service;
21
+ PositionState: CharacteristicValue;
22
+ TargetPosition: CharacteristicValue;
23
+ CurrentPosition: CharacteristicValue;
24
+ HoldPosition: CharacteristicValue;
25
+ };
26
+
27
+ private Battery: {
28
+ Name: CharacteristicValue;
29
+ Service: Service;
30
+ BatteryLevel: CharacteristicValue;
31
+ StatusLowBattery: CharacteristicValue;
32
+ ChargingState: CharacteristicValue;
33
+ };
34
+
35
+ private LightSensor?: {
36
+ Name: CharacteristicValue;
37
+ Service: Service;
38
+ CurrentAmbientLightLevel?: CharacteristicValue;
39
+ };
53
40
 
54
41
  // Target
55
42
  setNewTarget!: boolean;
56
43
  setNewTargetTimer!: NodeJS.Timeout;
57
44
 
58
- //MQTT stuff
59
- mqttClient: MqttClient | null = null;
60
-
61
- // Config
62
- updateRate!: number;
63
- set_minLux!: number;
64
- set_maxLux!: number;
65
- set_minStep!: number;
66
- setOpenMode!: string;
67
- setCloseMode!: string;
68
- scanDuration!: number;
69
- deviceLogging!: string;
70
- deviceRefreshRate!: number;
71
-
72
45
  // Updates
73
46
  curtainUpdateInProgress!: boolean;
74
47
  doCurtainUpdate!: Subject<void>;
75
48
 
76
- // Connection
77
- private readonly OpenAPI: boolean;
78
- private readonly BLE: boolean;
79
-
80
- // EVE history service handler
81
- historyService: any = null;
82
-
83
49
  constructor(
84
- private readonly platform: SwitchBotPlatform,
85
- private accessory: PlatformAccessory,
86
- public device: device & devicesConfig,
50
+ readonly platform: SwitchBotPlatform,
51
+ accessory: PlatformAccessory,
52
+ device: device & devicesConfig,
87
53
  ) {
88
- this.api = this.platform.api;
89
- this.log = this.platform.log;
90
- this.config = this.platform.config;
91
- this.hap = this.api.hap;
92
- // Connection
93
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
94
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
95
- // default placeholders
96
- this.deviceLogs(device);
97
- this.refreshRate(device);
98
- this.scan(device);
99
- this.setupMqtt(device);
100
- this.deviceContext();
101
- this.deviceConfig(device);
102
-
54
+ super(platform, accessory, device);
55
+ // default placeholder
56
+ this.history(device);
103
57
  // this is subject we use to track when we need to POST changes to the SwitchBot API
104
58
  this.doCurtainUpdate = new Subject();
105
59
  this.curtainUpdateInProgress = false;
106
60
  this.setNewTarget = false;
107
61
 
108
- // Retrieve initial values and updateHomekit
109
- this.refreshStatus();
110
-
111
- // set accessory information
112
- accessory
113
- .getService(this.hap.Service.AccessoryInformation)!
114
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
115
- .setCharacteristic(this.hap.Characteristic.Model, 'W0701600')
116
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
117
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
118
-
119
- // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
120
- // you can create multiple services for each accessory
121
- const windowCoveringService = `${accessory.displayName} ${device.deviceType}`;
122
- (this.windowCoveringService = accessory.getService(this.hap.Service.WindowCovering)
123
- || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService;
124
-
125
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
126
- if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
127
- this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
128
- }
129
-
130
- // each service must implement at-minimum the "required characteristics" for the given service type
131
- // see https://developers.homebridge.io/#/service/WindowCovering
132
-
133
- // create handlers for required characteristics
134
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState);
62
+ // Initialize WindowCovering Service
63
+ accessory.context.WindowCovering = accessory.context.WindowCovering ?? {};
64
+ this.WindowCovering = {
65
+ Name: accessory.context.WindowCovering.Name ?? accessory.displayName,
66
+ Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service,
67
+ PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED,
68
+ TargetPosition: accessory.context.TargetPosition ?? 100,
69
+ CurrentPosition: accessory.context.CurrentPosition ?? 100,
70
+ HoldPosition: accessory.context.HoldPosition ?? false,
71
+ };
72
+ accessory.context.WindowCovering = this.WindowCovering as object;
73
+
74
+ // Initialize WindowCovering Service
75
+ this.WindowCovering.Service.
76
+ setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name)
77
+ .setCharacteristic(this.hap.Characteristic.ObstructionDetected, false)
78
+ .getCharacteristic(this.hap.Characteristic.PositionState)
79
+ .onGet(() => {
80
+ return this.WindowCovering.PositionState;
81
+ });
135
82
 
136
- this.windowCoveringService
83
+ // Initialize WindowCovering CurrentPosition
84
+ this.WindowCovering.Service
137
85
  .getCharacteristic(this.hap.Characteristic.CurrentPosition)
138
86
  .setProps({
139
- minStep: this.minStep(device),
87
+ minStep: device.curtain?.set_minStep ?? 1,
140
88
  minValue: 0,
141
89
  maxValue: 100,
142
90
  validValueRanges: [0, 100],
143
91
  })
144
92
  .onGet(() => {
145
- return this.CurrentPosition;
93
+ return this.WindowCovering.CurrentPosition;
146
94
  });
147
95
 
148
- this.windowCoveringService
96
+ // Initialize WindowCovering TargetPosition
97
+ this.WindowCovering.Service
149
98
  .getCharacteristic(this.hap.Characteristic.TargetPosition)
150
99
  .setProps({
151
- minStep: this.minStep(device),
100
+ minStep: device.curtain?.set_minStep ?? 1,
152
101
  minValue: 0,
153
102
  maxValue: 100,
154
103
  validValueRanges: [0, 100],
155
104
  })
105
+ .onGet(() => {
106
+ return this.WindowCovering.TargetPosition;
107
+ })
156
108
  .onSet(this.TargetPositionSet.bind(this));
157
109
 
158
- this.windowCoveringService
110
+ // Initialize WindowCovering TargetPosition
111
+ this.WindowCovering.Service
159
112
  .getCharacteristic(this.hap.Characteristic.HoldPosition)
113
+ .onGet(() => {
114
+ return this.WindowCovering.HoldPosition;
115
+ })
160
116
  .onSet(this.HoldPositionSet.bind(this));
161
117
 
162
- // Light Sensor Service
163
- if (device.curtain?.hide_lightsensor) {
164
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
165
- this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor);
166
- accessory.removeService(this.lightSensorService!);
167
- } else if (!this.lightSensorService) {
168
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`);
169
- const lightSensorService = `${accessory.displayName} Light Sensor`;
170
- (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor)
171
- || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService;
118
+ // Initialize Battery Service
119
+ accessory.context.Battery = accessory.context.Battery ?? {};
120
+ this.Battery = {
121
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
122
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
123
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
124
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
125
+ ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING,
126
+ };
127
+ accessory.context.Battery = this.Battery as object;
128
+
129
+ // Initialize Battery Service
130
+ this.Battery.Service
131
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
132
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
133
+ .onGet(() => {
134
+ return this.Battery.BatteryLevel;
135
+ });
172
136
 
173
- this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`);
174
- if (!this.lightSensorService?.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
175
- this.lightSensorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`);
176
- }
177
- } else {
178
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`);
179
- }
137
+ this.Battery.Service
138
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
139
+ .onGet(() => {
140
+ return this.Battery.StatusLowBattery;
141
+ });
180
142
 
181
- // Battery Service
182
- const batteryService = `${accessory.displayName} Battery`;
183
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
184
- || accessory.addService(this.hap.Service.Battery)), batteryService;
143
+ this.Battery.Service
144
+ .getCharacteristic(this.hap.Characteristic.ChargingState)
145
+ .onGet(() => {
146
+ return this.Battery.ChargingState;
147
+ });
185
148
 
186
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
187
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
188
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
149
+ // Initialize LightSensor Service
150
+ if (device.curtain?.hide_lightsensor) {
151
+ if (this.LightSensor?.Service) {
152
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
153
+ this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service;
154
+ accessory.removeService(this.LightSensor.Service);
155
+ accessory.context.LightSensor = {};
156
+ }
157
+ } else {
158
+ accessory.context.LightSensor = accessory.context.LightSensor ?? {};
159
+ this.LightSensor = {
160
+ Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
161
+ Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service,
162
+ CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001,
163
+ };
164
+ accessory.context.LightSensor = this.LightSensor as object;
165
+
166
+ // Initialize LightSensor Characteristic
167
+ this.LightSensor.Service
168
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name)
169
+ .setCharacteristic(this.hap.Characteristic.StatusActive, true)
170
+ .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
171
+ .onGet(() => {
172
+ return this.LightSensor!.CurrentAmbientLightLevel!;
173
+ });
189
174
  }
190
175
 
176
+ // Retrieve initial values and updateHomekit
177
+ this.refreshStatus();
178
+
191
179
  // Update Homekit
192
180
  this.updateHomeKitCharacteristics();
193
181
 
@@ -199,35 +187,17 @@ export class Curtain {
199
187
  });
200
188
 
201
189
  //regisiter webhook event handler
202
- if (this.device.webhook) {
203
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
204
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
205
- try {
206
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
207
- const { slidePosition, battery } = context;
208
- const { CurrentPosition, BatteryLevel } = this;
209
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
210
- '(slidePosition, battery) = ' +
211
- `Webhook:(${slidePosition}, ${battery}), ` +
212
- `current:(${CurrentPosition}, ${BatteryLevel})`);
213
- this.CurrentPosition = slidePosition;
214
- this.BatteryLevel = battery;
215
- this.updateHomeKitCharacteristics();
216
- } catch (e: any) {
217
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
218
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
219
- }
220
- };
221
- }
190
+ this.registerWebhook(accessory, device);
222
191
 
223
192
  // update slide progress
224
- interval(this.updateRate * 1000)
193
+ interval(this.deviceUpdateRate * 1000)
225
194
  //.pipe(skipWhile(() => this.curtainUpdateInProgress))
226
195
  .subscribe(async () => {
227
- if (this.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
196
+ if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
228
197
  return;
229
198
  }
230
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`);
199
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving,`
200
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
231
201
  await this.refreshStatus();
232
202
  });
233
203
 
@@ -238,29 +208,37 @@ export class Curtain {
238
208
  tap(() => {
239
209
  this.curtainUpdateInProgress = true;
240
210
  }),
241
- debounceTime(this.platform.config.options!.pushRate! * 1000),
211
+ debounceTime(this.devicePushRate * 1000),
242
212
  )
243
213
  .subscribe(async () => {
244
214
  try {
245
215
  await this.pushChanges();
246
216
  } catch (e: any) {
247
217
  this.apiError(e);
248
- this.errorLog(
249
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
250
- ` Error Message: ${JSON.stringify(e.message)}`,
251
- );
218
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
219
+ + ` Error Message: ${JSON.stringify(e.message)}`);
252
220
  }
253
221
  this.curtainUpdateInProgress = false;
254
222
  });
255
223
 
256
224
  // Setup EVE history features
257
- this.setupHistoryService(device);
225
+ this.setupHistoryService(accessory, device);
226
+ }
227
+
228
+ private history(device: device & devicesConfig) {
229
+ if (device.history === true) {
230
+ // initialize when this accessory is newly created.
231
+ this.accessory.context.lastActivation = this.accessory.context.lastActivation ?? 0;
232
+ } else {
233
+ // removes cached values if history is turned off
234
+ delete this.accessory.context.lastActivation;
235
+ }
258
236
  }
259
237
 
260
238
  /*
261
239
  * Setup EVE history features for curtain devices.
262
240
  */
263
- async setupHistoryService(device: device & devicesConfig): Promise<void> {
241
+ async setupHistoryService(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
264
242
  if (device.history !== true) {
265
243
  return;
266
244
  }
@@ -295,8 +273,7 @@ export class Curtain {
295
273
  this.accessory.context.lastActivation = entry.time;
296
274
  sensor?.updateCharacteristic(
297
275
  this.platform.eve.Characteristics.LastActivation,
298
- Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime()),
299
- );
276
+ Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime()));
300
277
  this.historyService.addEntry(entry);
301
278
  }
302
279
  });
@@ -304,7 +281,7 @@ export class Curtain {
304
281
  }
305
282
 
306
283
  async updateHistory(): Promise<void> {
307
- const motion = Number(this.CurrentPosition) > 0 ? 1 : 0;
284
+ const motion = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0;
308
285
  this.historyService.addEntry({
309
286
  time: Math.round(new Date().valueOf() / 1000),
310
287
  motion: motion,
@@ -317,198 +294,200 @@ export class Curtain {
317
294
  );
318
295
  }
319
296
 
320
- /**
321
- * Parse the device status from the SwitchBot api
322
- */
323
- async parseStatus(): Promise<void> {
324
- if (!this.device.enableCloudService && this.OpenAPI) {
325
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
326
- } else if (this.BLE) {
327
- await this.BLEparseStatus();
328
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
329
- await this.openAPIparseStatus();
330
- } else {
331
- await this.offlineOff();
332
- this.debugWarnLog(
333
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
334
- );
335
- }
336
- }
337
-
338
- async BLEparseStatus(): Promise<void> {
297
+ async BLEparseStatus(serviceData: serviceData): Promise<void> {
339
298
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
340
299
  // CurrentPosition
341
- this.CurrentPosition = 100 - Number(this.BLE_CurrentPosition);
300
+ this.WindowCovering.CurrentPosition = 100 - Number(serviceData.position);
342
301
  await this.setMinMax();
343
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`);
302
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.WindowCovering.CurrentPosition}`);
344
303
  if (this.setNewTarget) {
345
304
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
346
305
  await this.setMinMax();
347
- if (Number(this.TargetPosition) > this.CurrentPosition) {
348
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`);
349
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
350
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
351
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
352
- } else if (Number(this.TargetPosition) < this.CurrentPosition) {
353
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`);
354
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
355
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
356
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
306
+ if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
307
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
308
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
309
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
310
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
311
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
312
+ } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) {
313
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
314
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
315
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
316
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
317
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
357
318
  } else {
358
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
359
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
360
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
361
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
319
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,`
320
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
321
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
322
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
323
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
324
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
362
325
  }
363
326
  } else {
364
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
365
- this.TargetPosition = this.CurrentPosition;
366
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
327
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
328
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
329
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
367
330
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
368
331
  }
369
- this.debugLog(
370
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
371
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
372
- );
332
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
333
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
373
334
 
374
335
  if (!this.device.curtain?.hide_lightsensor) {
375
- this.set_minLux = this.minLux();
376
- this.set_maxLux = this.maxLux();
377
- this.spaceBetweenLevels = 9;
336
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
337
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
338
+ const spaceBetweenLevels = 9;
378
339
 
379
340
  // Brightness
380
- switch (this.BLE_CurrentAmbientLightLevel) {
341
+ switch (serviceData.lightLevel) {
381
342
  case 1:
382
- this.CurrentAmbientLightLevel = this.set_minLux;
383
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
343
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
344
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
384
345
  break;
385
346
  case 2:
386
- this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
347
+ this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels;
387
348
  this.debugLog(
388
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
389
- ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
390
- );
349
+ `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
350
+ + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`);
391
351
  break;
392
352
  case 3:
393
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2;
394
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
353
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2;
354
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
355
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
395
356
  break;
396
357
  case 4:
397
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3;
398
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
358
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3;
359
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
360
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
399
361
  break;
400
362
  case 5:
401
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4;
402
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
363
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4;
364
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
365
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
403
366
  break;
404
367
  case 6:
405
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5;
406
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
368
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5;
369
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
370
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
407
371
  break;
408
372
  case 7:
409
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6;
410
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
373
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6;
374
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
375
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
411
376
  break;
412
377
  case 8:
413
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7;
414
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
378
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7;
379
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
380
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
415
381
  break;
416
382
  case 9:
417
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8;
418
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
383
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8;
384
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
385
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
419
386
  break;
420
387
  case 10:
421
388
  default:
422
- this.CurrentAmbientLightLevel = this.set_maxLux;
389
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
423
390
  this.debugLog();
424
391
  }
425
- this.debugLog(
426
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
427
- ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
428
- );
392
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
393
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
429
394
  }
430
395
  // Battery
431
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
432
- if (this.BatteryLevel < 10) {
433
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
396
+ this.Battery.BatteryLevel = Number(serviceData.battery);
397
+ if (this.Battery.BatteryLevel < 10) {
398
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
434
399
  } else {
435
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
400
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
436
401
  }
437
- this.debugLog(
438
- `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`,
439
- );
402
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
403
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
440
404
  }
441
405
 
442
- async openAPIparseStatus(): Promise<void> {
406
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise<void> {
443
407
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
444
408
  // CurrentPosition
445
- this.CurrentPosition = 100 - Number(this.OpenAPI_CurrentPosition);
409
+ this.WindowCovering!.CurrentPosition = 100 - Number(deviceStatus.body.slidePosition);
446
410
  await this.setMinMax();
447
- this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
411
+ this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
448
412
  if (this.setNewTarget) {
449
413
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
450
414
  }
451
415
 
452
- if (this.setNewTarget && this.OpenAPI_InMotion) {
416
+ if (this.setNewTarget && deviceStatus.body.moving) {
453
417
  await this.setMinMax();
454
- if (Number(this.TargetPosition) > this.CurrentPosition) {
455
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `);
456
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
457
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
458
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
459
- } else if (Number(this.TargetPosition) < this.CurrentPosition) {
460
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `);
461
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
462
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
463
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
418
+ if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
419
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
420
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
421
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
422
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
423
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
424
+ } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) {
425
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
426
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
427
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
428
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
429
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
464
430
  } else {
465
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
466
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
467
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
468
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
431
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,`
432
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
433
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
434
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
435
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
436
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
469
437
  }
470
438
  } else {
471
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
472
- this.TargetPosition = this.CurrentPosition;
473
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
439
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
440
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
441
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
474
442
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
475
443
  }
476
- this.debugLog(
477
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
478
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
479
- );
444
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
445
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
480
446
 
481
447
  // Brightness
482
448
  if (!this.device.curtain?.hide_lightsensor) {
483
- this.set_minLux = this.minLux();
484
- this.set_maxLux = this.maxLux();
485
- switch (this.OpenAPI_CurrentAmbientLightLevel) {
486
- case 'dim':
487
- this.CurrentAmbientLightLevel = this.set_minLux;
488
- break;
449
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
450
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
451
+ switch (deviceStatus.body.brightness) {
489
452
  case 'bright':
453
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
454
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
455
+ break;
456
+ case 'dim':
490
457
  default:
491
- this.CurrentAmbientLightLevel = this.set_maxLux;
458
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
492
459
  }
493
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
460
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
461
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
494
462
  }
495
463
 
496
464
  // BatteryLevel
497
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
498
- if (this.BatteryLevel < 10) {
499
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
465
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
466
+ if (this.Battery.BatteryLevel < 10) {
467
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
500
468
  } else {
501
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
469
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
502
470
  }
503
- if (Number.isNaN(this.BatteryLevel)) {
504
- this.BatteryLevel = 100;
471
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
472
+ this.Battery.BatteryLevel = 100;
505
473
  }
506
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
507
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
474
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
475
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
508
476
 
509
- // FirmwareRevision
510
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
511
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
477
+ // Firmware Version
478
+ const version = deviceStatus.body.version?.toString();
479
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
480
+ if (deviceStatus.body.version) {
481
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
482
+ this.accessory
483
+ .getService(this.hap.Service.AccessoryInformation)!
484
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
485
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
486
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
487
+ .updateValue(deviceVersion);
488
+ this.accessory.context.deviceVersion = deviceVersion;
489
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
490
+ }
512
491
  }
513
492
 
514
493
  async refreshStatus(): Promise<void> {
@@ -520,10 +499,8 @@ export class Curtain {
520
499
  await this.openAPIRefreshStatus();
521
500
  } else {
522
501
  await this.offlineOff();
523
- this.debugWarnLog(
524
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
525
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
526
- );
502
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
503
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
527
504
  }
528
505
  }
529
506
 
@@ -538,118 +515,45 @@ export class Curtain {
538
515
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
539
516
  this.getCustomBLEAddress(switchbot);
540
517
  // Start to monitor advertisement packets
541
- let model: string;
542
- switch (this.device.configDeviceType) {
543
- case 'Curtain3':
544
- model = '{';
545
- break;
546
- default:
547
- model = 'c';
548
- }
549
518
  (async () => {
550
519
  // Start to monitor advertisement packets
551
- await switchbot.startScan({
552
- model: model,
553
- id: this.device.bleMac,
554
- });
520
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
555
521
  // Set an event handler
556
522
  switchbot.onadvertisement = (ad: any) => {
557
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
558
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
559
- if (this.device.bleMac === ad.address && ad.serviceData.model === 'c') {
523
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
524
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
525
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
560
526
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
561
- this.BLE_Calibration = ad.serviceData.calibration;
562
- this.BLE_BatteryLevel = ad.serviceData.battery;
563
- this.BLE_InMotion = ad.serviceData.inMotion;
564
- this.BLE_CurrentPosition = ad.serviceData.position;
565
- this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel;
566
527
  } else {
567
528
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
568
529
  }
569
530
  };
570
- // Wait 1 seconds
531
+ // Wait 10 seconds
571
532
  await switchbot.wait(this.scanDuration * 1000);
572
533
  // Stop to monitor
573
534
  await switchbot.stopScan();
574
535
  // Update HomeKit
575
- await this.BLEparseStatus();
536
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
576
537
  await this.updateHomeKitCharacteristics();
577
538
  })();
578
-
579
-
580
- /*if (switchbot !== false) {
581
- switchbot
582
- .startScan({
583
- model: model,
584
- id: this.device.bleMac,
585
- })
586
- .then(async () => {
587
- // Set an event handler
588
- switchbot.onadvertisement = async (ad: ad) => {
589
- this.debugLog(
590
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
591
- ` BLE Address Found: ${ad.address}`,
592
- );
593
- this.BLE_Calibration = ad.serviceData.calibration;
594
- this.BLE_BatteryLevel = ad.serviceData.battery;
595
- this.BLE_InMotion = ad.serviceData.inMotion;
596
- this.BLE_CurrentPosition = ad.serviceData.position;
597
- this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel;
598
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
599
- this.debugLog(
600
- `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
601
- `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}, ` +
602
- `inMotion: ${ad.serviceData.inMotion}`,
603
- );
604
-
605
- if (ad.serviceData) {
606
- this.BLE_IsConnected = true;
607
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
608
- await this.stopScanning(switchbot);
609
- } else {
610
- this.BLE_IsConnected = false;
611
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
612
- }
613
- };
614
- // Wait
615
- return await sleep(this.scanDuration * 1000);
616
- })
617
- .then(async () => {
618
- // Stop to monitor
619
- await this.stopScanning(switchbot);
620
- })
621
- .catch(async (e: any) => {
622
- this.apiError(e);
623
- this.errorLog(
624
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
625
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
626
- );
627
- await this.BLERefreshConnection(switchbot);
628
- });
629
- } else {
539
+ if (switchbot === undefined) {
630
540
  await this.BLERefreshConnection(switchbot);
631
- }*/
541
+ }
632
542
  }
633
543
 
634
544
  async openAPIRefreshStatus(): Promise<void> {
635
545
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
636
546
  try {
637
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
638
- headers: this.platform.generateHeaders(),
639
- });
547
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
548
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
640
549
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
641
550
  const deviceStatus: any = await body.json();
642
551
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
643
552
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
644
553
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
645
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
554
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
646
555
  + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
647
- this.OpenAPI_CurrentPosition = deviceStatus.body.slidePosition;
648
- this.OpenAPI_InMotion = deviceStatus.body.moving;
649
- this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness;
650
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
651
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
652
- this.openAPIparseStatus();
556
+ this.openAPIparseStatus(deviceStatus);
653
557
  this.updateHomeKitCharacteristics();
654
558
  } else {
655
559
  this.statusCode(statusCode);
@@ -657,10 +561,30 @@ export class Curtain {
657
561
  }
658
562
  } catch (e: any) {
659
563
  this.apiError(e);
660
- this.errorLog(
661
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
662
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
663
- );
564
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
565
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
566
+ }
567
+ }
568
+
569
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
570
+ if (device.webhook) {
571
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
572
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
573
+ try {
574
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
575
+ const { slidePosition, battery } = context;
576
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ` +
577
+ '(slidePosition, battery) = ' +
578
+ `Webhook:(${slidePosition}, ${battery}), `
579
+ + `current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
580
+ this.WindowCovering.CurrentPosition = slidePosition;
581
+ this.Battery.BatteryLevel = battery;
582
+ this.updateHomeKitCharacteristics();
583
+ } catch (e: any) {
584
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} `
585
+ + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
586
+ }
587
+ };
664
588
  }
665
589
  }
666
590
 
@@ -673,9 +597,8 @@ export class Curtain {
673
597
  await this.openAPIpushChanges();
674
598
  } else {
675
599
  await this.offlineOff();
676
- this.debugWarnLog(
677
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
678
- );
600
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
601
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
679
602
  }
680
603
  // Refresh the status from the API
681
604
  interval(15000)
@@ -688,7 +611,7 @@ export class Curtain {
688
611
 
689
612
  async BLEpushChanges(): Promise<void> {
690
613
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
691
- if (this.TargetPosition !== this.CurrentPosition) {
614
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) {
692
615
  const switchbot = await this.platform.connectBLE();
693
616
  // Convert to BLE Address
694
617
  this.device.bleMac = this.device
@@ -696,28 +619,28 @@ export class Curtain {
696
619
  .join(':')
697
620
  .toLowerCase();
698
621
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
699
- this.SilentPerformance();
700
- const adjustedMode = this.setPositionMode === '1' ? 0x01 : 0xff;
701
- this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`);
622
+ const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
623
+ const adjustedMode = setPositionMode === 1 ? 0x01 : 0xff;
624
+ this.debugLog(`${this.accessory.displayName} Mode: ${Mode}`);
702
625
  if (switchbot !== false) {
703
626
  try {
704
627
  const device_list = await switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac });
705
- this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`);
628
+ this.infoLog(`${this.accessory.displayName} Target Position: ${this.WindowCovering.TargetPosition}`);
706
629
 
707
- await this.retry({
708
- max: this.maxRetry(),
630
+ await this.retryBLE({
631
+ max: await this.maxRetryBLE(),
709
632
  fn: async () => {
710
- await device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode);
633
+ await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), adjustedMode);
711
634
  },
712
635
  });
713
636
 
714
637
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
638
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
639
+ + `TargetPosition: ${this.WindowCovering.TargetPosition} sent over BLE, sent successfully`);
715
640
  } catch (e) {
716
641
  this.apiError(e);
717
- this.errorLog(
718
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
719
- ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`,
720
- );
642
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
643
+ + ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`);
721
644
  await this.BLEPushConnection();
722
645
  throw new Error('Connection error');
723
646
  }
@@ -726,30 +649,8 @@ export class Curtain {
726
649
  await this.BLEPushConnection();
727
650
  }
728
651
  } else {
729
- this.debugLog(
730
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` +
731
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
732
- );
733
- }
734
- }
735
-
736
- async retry({ max, fn }: { max: number; fn: { (): any; (): Promise<any> } }): Promise<null> {
737
- return fn().catch(async (e: any) => {
738
- if (max === 0) {
739
- throw e;
740
- }
741
- this.infoLog(e);
742
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
743
- await sleep(1000);
744
- return this.retry({ max: max - 1, fn });
745
- });
746
- }
747
-
748
- maxRetry(): number {
749
- if (this.device.maxRetry) {
750
- return this.device.maxRetry;
751
- } else {
752
- return 5;
652
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.`
653
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`);
753
654
  }
754
655
  }
755
656
 
@@ -758,24 +659,13 @@ export class Curtain {
758
659
  let parameter: string;
759
660
  let commandType: string;
760
661
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
761
- if (this.TargetPosition !== this.CurrentPosition || this.device.disableCaching) {
762
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.TargetPosition}`);
763
- const adjustedTargetPosition = 100 - Number(this.TargetPosition);
764
- if (Number(this.TargetPosition) > 50) {
765
- this.setPositionMode = this.device.curtain?.setOpenMode;
766
- } else {
767
- this.setPositionMode = this.device.curtain?.setCloseMode;
768
- }
769
- if (this.setPositionMode === '1') {
770
- this.Mode = 'Silent Mode';
771
- } else if (this.setPositionMode === '0') {
772
- this.Mode = 'Performance Mode';
773
- } else {
774
- this.Mode = 'Default Mode';
775
- }
776
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${this.Mode}`);
777
- const adjustedMode = this.setPositionMode || 'ff';
778
- if (this.HoldPosition) {
662
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition || this.device.disableCaching) {
663
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.WindowCovering.TargetPosition}`);
664
+ const adjustedTargetPosition = 100 - Number(this.WindowCovering.TargetPosition);
665
+ const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
666
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${Mode}`);
667
+ const adjustedMode = setPositionMode || 'ff';
668
+ if (this.WindowCovering.HoldPosition) {
779
669
  command = 'pause';
780
670
  parameter = 'default';
781
671
  commandType = 'command';
@@ -804,22 +694,20 @@ export class Curtain {
804
694
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
805
695
  this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
806
696
  + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
697
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
698
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
807
699
  } else {
808
700
  this.statusCode(statusCode);
809
701
  this.statusCode(deviceStatus.statusCode);
810
702
  }
811
703
  } catch (e: any) {
812
704
  this.apiError(e);
813
- this.errorLog(
814
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
815
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
816
- );
705
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
706
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
817
707
  }
818
708
  } else {
819
- this.debugLog(
820
- `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` +
821
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
822
- );
709
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.`
710
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`);
823
711
  }
824
712
  }
825
713
 
@@ -827,50 +715,53 @@ export class Curtain {
827
715
  * Handle requests to set the value of the "Target Position" characteristic
828
716
  */
829
717
  async TargetPositionSet(value: CharacteristicValue): Promise<void> {
830
- if (this.TargetPosition === this.accessory.context.TargetPosition) {
718
+ if (this.WindowCovering.TargetPosition === this.accessory.context.TargetPosition) {
831
719
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetPosition: ${value}`);
832
720
  } else {
833
721
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
834
722
  }
835
723
 
836
724
  // Set HoldPosition to false when TargetPosition is changed
837
- this.HoldPosition = false;
838
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.HoldPosition);
725
+ this.WindowCovering.HoldPosition = false;
726
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition);
839
727
 
840
- this.TargetPosition = value;
728
+ this.WindowCovering.TargetPosition = value;
841
729
  if (this.device.mqttURL) {
842
- this.mqttPublish('TargetPosition', this.TargetPosition);
843
- this.mqttPublish('HoldPosition', this.HoldPosition);
730
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
731
+ this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString()); // Convert boolean to string
844
732
  }
845
733
 
846
734
  await this.setMinMax();
847
- if (value > this.CurrentPosition) {
848
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
735
+ if (value > this.WindowCovering.CurrentPosition) {
736
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
849
737
  this.setNewTarget = true;
850
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
851
- } else if (value < this.CurrentPosition) {
852
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
738
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
739
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
740
+ } else if (value < this.WindowCovering.CurrentPosition) {
741
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
853
742
  this.setNewTarget = true;
854
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
743
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
744
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
855
745
  } else {
856
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
746
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
857
747
  this.setNewTarget = false;
858
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
748
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
749
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
859
750
  }
860
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState);
861
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
751
+ this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState);
752
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
862
753
 
863
754
  /**
864
755
  * If Curtain movement time is short, the moving flag from backend is always false.
865
756
  * The minimum time depends on the network control latency.
866
757
  */
867
758
  clearTimeout(this.setNewTargetTimer);
868
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
759
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.deviceUpdateRate}`);
869
760
  if (this.setNewTarget) {
870
761
  this.setNewTargetTimer = setTimeout(() => {
871
762
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
872
763
  this.setNewTarget = false;
873
- }, this.updateRate * 1000);
764
+ }, this.deviceUpdateRate * 1000);
874
765
  }
875
766
  this.doCurtainUpdate.next();
876
767
  }
@@ -880,153 +771,107 @@ export class Curtain {
880
771
  */
881
772
  async HoldPositionSet(value: CharacteristicValue): Promise<void> {
882
773
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${value}`);
883
- this.HoldPosition = value;
774
+ this.WindowCovering.HoldPosition = value;
884
775
  this.doCurtainUpdate.next();
885
776
  }
886
777
 
887
778
  async updateHomeKitCharacteristics(): Promise<void> {
888
779
  await this.setMinMax();
889
- if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) {
890
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
780
+ if (this.WindowCovering.CurrentPosition === undefined || Number.isNaN(this.WindowCovering.CurrentPosition)) {
781
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
891
782
  } else {
783
+ this.accessory.context.CurrentPosition = this.WindowCovering.CurrentPosition;
784
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.WindowCovering.CurrentPosition));
785
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
786
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
892
787
  if (this.device.mqttURL) {
893
- this.mqttPublish('CurrentPosition', this.CurrentPosition);
788
+ this.mqttPublish('CurrentPosition', this.WindowCovering.CurrentPosition.toString()); // Convert to string
894
789
  }
895
- this.accessory.context.CurrentPosition = this.CurrentPosition;
896
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.CurrentPosition));
897
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
898
790
  }
899
- if (this.PositionState === undefined) {
900
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
791
+ if (this.WindowCovering.PositionState === undefined) {
792
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.WindowCovering.PositionState}`);
901
793
  } else {
902
794
  if (this.device.mqttURL) {
903
- this.mqttPublish('PositionState', this.PositionState);
795
+ this.mqttPublish('PositionState', this.WindowCovering.PositionState.toString());
904
796
  }
905
- this.accessory.context.PositionState = this.PositionState;
906
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.PositionState));
907
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
797
+ this.accessory.context.PositionState = this.WindowCovering.PositionState;
798
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.WindowCovering.PositionState));
799
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
800
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
908
801
  }
909
- if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
910
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
802
+ if (this.WindowCovering.TargetPosition === undefined || Number.isNaN(this.WindowCovering.TargetPosition)) {
803
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.WindowCovering.TargetPosition}`);
911
804
  } else {
912
805
  if (this.device.mqttURL) {
913
- this.mqttPublish('TargetPosition', this.TargetPosition);
806
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
914
807
  }
915
- this.accessory.context.TargetPosition = this.TargetPosition;
916
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.TargetPosition));
917
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
808
+ this.accessory.context.TargetPosition = this.WindowCovering.TargetPosition;
809
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.WindowCovering.TargetPosition));
810
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
811
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}`);
918
812
  }
919
- if (this.HoldPosition === undefined) {
920
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.HoldPosition}`);
813
+ if (this.WindowCovering.HoldPosition === undefined) {
814
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.WindowCovering.HoldPosition}`);
921
815
  } else {
922
816
  if (this.device.mqttURL) {
923
- this.mqttPublish('HoldPosition', this.HoldPosition);
817
+ this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString());
924
818
  }
925
- this.accessory.context.HoldPosition = this.HoldPosition;
926
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.HoldPosition);
927
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic HoldPosition: ${this.HoldPosition}`);
819
+ this.accessory.context.HoldPosition = this.WindowCovering.HoldPosition;
820
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition);
821
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
822
+ + ` HoldPosition: ${this.WindowCovering.HoldPosition}`);
928
823
  }
929
824
  if (!this.device.curtain?.hide_lightsensor) {
930
- if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) {
931
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
825
+ if (this.LightSensor!.CurrentAmbientLightLevel === undefined || Number.isNaN(this.LightSensor!.CurrentAmbientLightLevel)) {
826
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
827
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
932
828
  } else {
933
829
  if (this.device.mqttURL) {
934
- this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
830
+ this.mqttPublish('CurrentAmbientLightLevel', this.LightSensor!.CurrentAmbientLightLevel.toString());
935
831
  }
936
- this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
937
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
938
- this.debugLog(
939
- `${this.device.deviceType}: ${this.accessory.displayName}` +
940
- ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
941
- );
832
+ this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel;
833
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel);
834
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
835
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
942
836
  if (this.device.history) {
943
837
  this.historyService?.addEntry({
944
838
  time: Math.round(new Date().valueOf() / 1000),
945
- lux: this.CurrentAmbientLightLevel,
839
+ lux: this.LightSensor!.CurrentAmbientLightLevel,
946
840
  });
947
841
  }
948
842
  }
949
843
  }
950
- if (this.BatteryLevel === undefined) {
951
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
844
+ if (this.Battery.BatteryLevel === undefined) {
845
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
952
846
  } else {
953
847
  if (this.device.mqttURL) {
954
- this.mqttPublish('BatteryLevel', this.BatteryLevel);
848
+ this.mqttPublish('BatteryLevel', this.Battery.BatteryLevel.toString());
955
849
  }
956
- this.accessory.context.BatteryLevel = this.BatteryLevel;
957
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
958
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
850
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
851
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
852
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
959
853
  }
960
- if (this.StatusLowBattery === undefined) {
961
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
854
+ if (this.Battery.StatusLowBattery === undefined) {
855
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
962
856
  } else {
963
857
  if (this.device.mqttURL) {
964
- this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
965
- }
966
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
967
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
968
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
969
- }
970
- }
971
-
972
- /*
973
- * Publish MQTT message for topics of
974
- * 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx'
975
- */
976
- mqttPublish(topic: string, message: any) {
977
- const mac = this.device.deviceId
978
- ?.toLowerCase()
979
- .match(/[\s\S]{1,2}/g)
980
- ?.join(':');
981
- const options = this.device.mqttPubOptions || {};
982
- this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options);
983
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`);
984
- }
985
-
986
- /*
987
- * Setup MQTT hadler if URL is specified.
988
- */
989
- async setupMqtt(device: device & devicesConfig): Promise<void> {
990
- if (device.mqttURL) {
991
- try {
992
- const { connectAsync } = asyncmqtt;
993
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
994
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
995
- this.mqttClient.on('error', (e: Error) => {
996
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
997
- });
998
- } catch (e) {
999
- this.mqttClient = null;
1000
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
858
+ this.mqttPublish('StatusLowBattery', this.Battery.StatusLowBattery.toString());
1001
859
  }
860
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
861
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
862
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
863
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
1002
864
  }
1003
- }
1004
-
1005
- async stopScanning(switchbot: any) {
1006
- switchbot.stopScan();
1007
- if (this.BLE_IsConnected) {
1008
- await this.BLEparseStatus();
1009
- await this.updateHomeKitCharacteristics();
865
+ if (this.Battery.ChargingState === undefined) {
866
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`);
1010
867
  } else {
1011
- await this.BLERefreshConnection(switchbot);
1012
- }
1013
- }
1014
-
1015
- async getCustomBLEAddress(switchbot: any) {
1016
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
1017
- (async () => {
1018
- // Start to monitor advertisement packets
1019
- await switchbot.startScan({
1020
- model: 'c',
1021
- });
1022
- // Set an event handler
1023
- switchbot.onadvertisement = (ad: any) => {
1024
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
1025
- };
1026
- await sleep(10000);
1027
- // Stop to monitor
1028
- switchbot.stopScan();
1029
- })();
868
+ if (this.device.mqttURL) {
869
+ this.mqttPublish('ChargingState', this.Battery.ChargingState.toString());
870
+ }
871
+ this.accessory.context.ChargingState = this.Battery.ChargingState;
872
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState);
873
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
874
+ + ` ChargingState: ${this.Battery.ChargingState}`);
1030
875
  }
1031
876
  }
1032
877
 
@@ -1046,364 +891,71 @@ export class Curtain {
1046
891
  }
1047
892
  }
1048
893
 
1049
- async SilentPerformance() {
1050
- if (Number(this.TargetPosition) > 50) {
894
+ async setPerformance() {
895
+ let setPositionMode: number;
896
+ let Mode: string;
897
+ if (Number(this.WindowCovering.TargetPosition) > 50) {
1051
898
  if (this.device.curtain?.setOpenMode === '1') {
1052
- this.setPositionMode = '1';
1053
- this.Mode = 'Silent Mode';
899
+ setPositionMode = 1;
900
+ Mode = 'Silent Mode';
901
+ } else if (this.device.curtain?.setOpenMode === '0') {
902
+ setPositionMode = 0;
903
+ Mode = 'Performance Mode';
1054
904
  } else {
1055
- this.setPositionMode = '0';
1056
- this.Mode = 'Performance Mode';
905
+ setPositionMode = 0;
906
+ Mode = 'Default Mode';
1057
907
  }
1058
908
  } else {
1059
909
  if (this.device.curtain?.setCloseMode === '1') {
1060
- this.setPositionMode = '1';
1061
- this.Mode = 'Silent Mode';
910
+ setPositionMode = 1;
911
+ Mode = 'Silent Mode';
912
+ } else if (this.device.curtain?.setOpenMode === '0') {
913
+ setPositionMode = 0;
914
+ Mode = 'Performance Mode';
1062
915
  } else {
1063
- this.setPositionMode = '0';
1064
- this.Mode = 'Performance Mode';
916
+ setPositionMode = 0;
917
+ Mode = 'Default Mode';
1065
918
  }
1066
919
  }
920
+ return { setPositionMode, Mode };
1067
921
  }
1068
922
 
1069
923
  async setMinMax(): Promise<void> {
1070
924
  if (this.device.curtain?.set_min) {
1071
- if (Number(this.CurrentPosition) <= this.device.curtain?.set_min) {
1072
- this.CurrentPosition = 0;
925
+ if (Number(this.WindowCovering.CurrentPosition) <= this.device.curtain?.set_min) {
926
+ this.WindowCovering.CurrentPosition = 0;
1073
927
  }
1074
928
  }
1075
929
  if (this.device.curtain?.set_max) {
1076
- if (Number(this.CurrentPosition) >= this.device.curtain?.set_max) {
1077
- this.CurrentPosition = 100;
930
+ if (Number(this.WindowCovering.CurrentPosition) >= this.device.curtain?.set_max) {
931
+ this.WindowCovering.CurrentPosition = 100;
1078
932
  }
1079
933
  }
1080
934
  if (this.device.history) {
1081
935
  const motion = this.accessory.getService(this.hap.Service.MotionSensor);
1082
- const state = Number(this.CurrentPosition) > 0 ? 1 : 0;
936
+ const state = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0;
1083
937
  motion?.updateCharacteristic(this.hap.Characteristic.MotionDetected, state);
1084
938
  }
1085
939
  }
1086
940
 
1087
- minStep(device: device & devicesConfig): number {
1088
- if (device.curtain?.set_minStep) {
1089
- this.set_minStep = device.curtain?.set_minStep;
1090
- } else {
1091
- this.set_minStep = 1;
1092
- }
1093
- return this.set_minStep;
1094
- }
1095
-
1096
- minLux(): number {
1097
- if (this.device.curtain?.set_minLux) {
1098
- this.set_minLux = this.device.curtain?.set_minLux;
1099
- } else {
1100
- this.set_minLux = 1;
1101
- }
1102
- return this.set_minLux;
1103
- }
1104
-
1105
- maxLux(): number {
1106
- if (this.device.curtain?.set_maxLux) {
1107
- this.set_maxLux = this.device.curtain?.set_maxLux;
1108
- } else {
1109
- this.set_maxLux = 6001;
1110
- }
1111
- return this.set_maxLux;
1112
- }
1113
-
1114
- async scan(device: device & devicesConfig): Promise<void> {
1115
- if (device.scanDuration) {
1116
- if (this.updateRate > device.scanDuration) {
1117
- this.scanDuration = this.updateRate;
1118
- if (this.BLE) {
1119
- this.warnLog(
1120
- `${this.device.deviceType}: ` +
1121
- `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`,
1122
- );
1123
- }
1124
- } else {
1125
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
1126
- }
1127
- if (this.BLE) {
1128
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
1129
- }
1130
- } else {
1131
- if (this.updateRate > 1) {
1132
- this.scanDuration = this.updateRate;
1133
- } else {
1134
- this.scanDuration = this.accessory.context.scanDuration = 1;
1135
- }
1136
- if (this.BLE) {
1137
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
1138
- }
1139
- }
1140
- }
1141
-
1142
- async statusCode(statusCode: number): Promise<void> {
1143
- switch (statusCode) {
1144
- case 151:
1145
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
1146
- break;
1147
- case 152:
1148
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
1149
- break;
1150
- case 160:
1151
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
1152
- break;
1153
- case 161:
1154
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
1155
- this.offlineOff();
1156
- break;
1157
- case 171:
1158
- this.errorLog(
1159
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
1160
- `Hub: ${this.device.hubDeviceId}`,
1161
- );
1162
- this.offlineOff();
1163
- break;
1164
- case 190:
1165
- this.errorLog(
1166
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
1167
- ` Or command format is invalid, statusCode: ${statusCode}`,
1168
- );
1169
- break;
1170
- case 100:
1171
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
1172
- break;
1173
- case 200:
1174
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
1175
- break;
1176
- case 400:
1177
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
1178
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
1179
- break;
1180
- case 401:
1181
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
1182
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
1183
- break;
1184
- case 403:
1185
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
1186
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
1187
- break;
1188
- case 404:
1189
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
1190
- + `statusCode: ${statusCode}`);
1191
- break;
1192
- case 406:
1193
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
1194
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
1195
- break;
1196
- case 415:
1197
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
1198
- + `header that is not supported by the server, statusCode: ${statusCode}`);
1199
- break;
1200
- case 422:
1201
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
1202
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
1203
- break;
1204
- case 429:
1205
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
1206
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
1207
- break;
1208
- case 500:
1209
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
1210
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
1211
- break;
1212
- default:
1213
- this.infoLog(
1214
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
1215
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
1216
- );
1217
- }
1218
- }
1219
-
1220
941
  async offlineOff(): Promise<void> {
1221
942
  if (this.device.offline) {
1222
- await this.deviceContext();
1223
- await this.updateHomeKitCharacteristics();
943
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100);
944
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100);
945
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
1224
946
  }
1225
947
  }
1226
948
 
1227
949
  async apiError(e: any): Promise<void> {
1228
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
1229
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, e);
1230
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
950
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
951
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e);
952
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
953
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
954
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
955
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, e);
1231
956
  if (!this.device.curtain?.hide_lightsensor) {
1232
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
1233
- }
1234
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
1235
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
1236
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
1237
- }
1238
-
1239
- async deviceContext() {
1240
- if (this.accessory.context.CurrentPosition === undefined) {
1241
- this.CurrentPosition = 0;
1242
- } else {
1243
- this.CurrentPosition = this.accessory.context.CurrentPosition;
1244
- }
1245
-
1246
- if (this.accessory.context.TargetPosition === undefined) {
1247
- this.TargetPosition = 0;
1248
- } else {
1249
- this.TargetPosition = this.accessory.context.TargetPosition;
1250
- }
1251
-
1252
- if (this.accessory.context.PositionState === undefined) {
1253
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
1254
- } else {
1255
- this.PositionState = this.accessory.context.PositionState;
1256
- }
1257
-
1258
- if (!this.device.curtain?.hide_lightsensor) {
1259
- if (this.accessory.context.CurrentAmbientLightLevel !== undefined) {
1260
- this.CurrentAmbientLightLevel = this.accessory.context.CurrentAmbientLightLevel;
1261
- }
1262
- }
1263
-
1264
- if (this.BLE) {
1265
- if (this.accessory.context.BatteryLevel === undefined) {
1266
- this.BatteryLevel = 100;
1267
- } else {
1268
- this.BatteryLevel = this.accessory.context.BatteryLevel;
1269
- }
1270
- if (this.accessory.context.StatusLowBattery === undefined) {
1271
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
1272
- } else {
1273
- this.StatusLowBattery = this.accessory.context.StatusLowBattery;
1274
- }
1275
- }
1276
-
1277
- if (this.device.history === true) {
1278
- // initialize when this accessory is newly created.
1279
- this.accessory.context.lastActivation = this.accessory.context.lastActivation ?? 0;
1280
- } else {
1281
- // removes cached values if history is turned off
1282
- delete this.accessory.context.lastActivation;
1283
- }
1284
- if (this.FirmwareRevision === undefined) {
1285
- this.FirmwareRevision = this.platform.version;
1286
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
1287
- }
1288
- }
1289
-
1290
- async refreshRate(device: device & devicesConfig): Promise<void> {
1291
- // refreshRate
1292
- if (device.refreshRate) {
1293
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
1294
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
1295
- } else if (this.platform.config.options!.refreshRate) {
1296
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
1297
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
957
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
958
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e);
1298
959
  }
1299
- // updateRate
1300
- if (device?.curtain?.updateRate) {
1301
- this.updateRate = device?.curtain?.updateRate;
1302
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Curtain updateRate: ${this.updateRate}`);
1303
- } else {
1304
- this.updateRate = 7;
1305
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Curtain updateRate: ${this.updateRate}`);
1306
- }
1307
- }
1308
-
1309
- async deviceConfig(device: device & devicesConfig): Promise<void> {
1310
- let config = {};
1311
- if (device.curtain) {
1312
- config = device.curtain;
1313
- }
1314
- if (device.connectionType !== undefined) {
1315
- config['connectionType'] = device.connectionType;
1316
- }
1317
- if (device.external !== undefined) {
1318
- config['external'] = device.external;
1319
- }
1320
- if (device.mqttURL !== undefined) {
1321
- config['mqttURL'] = device.mqttURL;
1322
- }
1323
- if (device.logging !== undefined) {
1324
- config['logging'] = device.logging;
1325
- }
1326
- if (device.refreshRate !== undefined) {
1327
- config['refreshRate'] = device.refreshRate;
1328
- }
1329
- if (device.scanDuration !== undefined) {
1330
- config['scanDuration'] = device.scanDuration;
1331
- }
1332
- if (device.maxRetry !== undefined) {
1333
- config['maxRetry'] = device.maxRetry;
1334
- }
1335
- if (device.webhook !== undefined) {
1336
- config['webhook'] = device.webhook;
1337
- }
1338
- if (Object.entries(config).length !== 0) {
1339
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
1340
- }
1341
- }
1342
-
1343
- async deviceLogs(device: device & devicesConfig): Promise<void> {
1344
- if (this.platform.debugMode) {
1345
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
1346
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
1347
- } else if (device.logging) {
1348
- this.deviceLogging = this.accessory.context.logging = device.logging;
1349
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
1350
- } else if (this.platform.config.options?.logging) {
1351
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
1352
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
1353
- } else {
1354
- this.deviceLogging = this.accessory.context.logging = 'standard';
1355
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
1356
- }
1357
- }
1358
-
1359
- /**
1360
- * Logging for Device
1361
- */
1362
- infoLog(...log: any[]): void {
1363
- if (this.enablingDeviceLogging()) {
1364
- this.platform.log.info(String(...log));
1365
- }
1366
- }
1367
-
1368
- warnLog(...log: any[]): void {
1369
- if (this.enablingDeviceLogging()) {
1370
- this.platform.log.warn(String(...log));
1371
- }
1372
- }
1373
-
1374
- debugWarnLog(...log: any[]): void {
1375
- if (this.enablingDeviceLogging()) {
1376
- if (this.deviceLogging?.includes('debug')) {
1377
- this.platform.log.warn('[DEBUG]', String(...log));
1378
- }
1379
- }
1380
- }
1381
-
1382
- errorLog(...log: any[]): void {
1383
- if (this.enablingDeviceLogging()) {
1384
- this.platform.log.error(String(...log));
1385
- }
1386
- }
1387
-
1388
- debugErrorLog(...log: any[]): void {
1389
- if (this.enablingDeviceLogging()) {
1390
- if (this.deviceLogging?.includes('debug')) {
1391
- this.platform.log.error('[DEBUG]', String(...log));
1392
- }
1393
- }
1394
- }
1395
-
1396
- debugLog(...log: any[]): void {
1397
- if (this.enablingDeviceLogging()) {
1398
- if (this.deviceLogging === 'debug') {
1399
- this.platform.log.info('[DEBUG]', String(...log));
1400
- } else {
1401
- this.platform.log.debug(String(...log));
1402
- }
1403
- }
1404
- }
1405
-
1406
- enablingDeviceLogging(): boolean {
1407
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
1408
960
  }
1409
961
  }