@switchbot/homebridge-switchbot 3.5.0-beta.9 → 3.5.1-beta.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 (177) hide show
  1. package/.vscode/settings.json +4 -1
  2. package/CHANGELOG.md +4 -1
  3. package/config.schema.json +164 -22
  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 +457 -866
  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 +851 -913
  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 +316 -742
  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 +380 -833
  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 +267 -602
  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 +469 -897
  27. package/dist/device/curtain.js.map +1 -1
  28. package/dist/device/device.d.ts +76 -0
  29. package/dist/device/device.d.ts.map +1 -0
  30. package/dist/device/device.js +846 -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 +645 -0
  35. package/dist/device/fan.js.map +1 -0
  36. package/dist/device/hub.d.ts +19 -58
  37. package/dist/device/hub.d.ts.map +1 -1
  38. package/dist/device/hub.js +304 -640
  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 +300 -710
  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 +225 -643
  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 +371 -742
  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 +309 -642
  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 +223 -638
  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 +216 -644
  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 +209 -588
  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 +141 -532
  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 +252 -609
  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 +179 -521
  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 +337 -482
  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 +108 -333
  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 +47 -299
  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 +95 -364
  102. package/dist/irdevice/fan.js.map +1 -1
  103. package/dist/irdevice/irdevice.d.ts +46 -0
  104. package/dist/irdevice/irdevice.d.ts.map +1 -0
  105. package/dist/irdevice/irdevice.js +393 -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 +121 -376
  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 +441 -584
  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 +94 -353
  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 +48 -299
  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 +50 -305
  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 +66 -18
  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 +95 -0
  136. package/dist/utils.d.ts.map +1 -1
  137. package/dist/utils.js +657 -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 +488 -937
  143. package/src/device/bot.ts +907 -1016
  144. package/src/device/ceilinglight.ts +330 -798
  145. package/src/device/colorbulb.ts +394 -904
  146. package/src/device/contact.ts +298 -637
  147. package/src/device/curtain.ts +496 -954
  148. package/src/device/device.ts +872 -0
  149. package/src/device/fan.ts +692 -0
  150. package/src/device/hub.ts +338 -671
  151. package/src/device/humidifier.ts +330 -777
  152. package/src/device/iosensor.ts +258 -671
  153. package/src/device/lightstrip.ts +394 -809
  154. package/src/device/lock.ts +345 -700
  155. package/src/device/meter.ts +256 -666
  156. package/src/device/meterplus.ts +248 -672
  157. package/src/device/motion.ts +234 -622
  158. package/src/device/plug.ts +152 -572
  159. package/src/device/robotvacuumcleaner.ts +273 -653
  160. package/src/device/waterdetector.ts +198 -549
  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 +368 -540
  165. package/src/irdevice/airpurifier.ts +134 -369
  166. package/src/irdevice/camera.ts +60 -324
  167. package/src/irdevice/fan.ts +111 -392
  168. package/src/irdevice/irdevice.ts +406 -0
  169. package/src/irdevice/light.ts +159 -425
  170. package/src/irdevice/other.ts +509 -661
  171. package/src/irdevice/tv.ts +123 -388
  172. package/src/irdevice/vacuumcleaner.ts +61 -324
  173. package/src/irdevice/waterheater.ts +63 -333
  174. package/src/platform.ts +395 -259
  175. package/src/settings.ts +120 -610
  176. package/src/utils.ts +661 -1
  177. package/src/device/waterdetector.json +0 -11
@@ -1,193 +1,179 @@
1
- import { request } from 'undici';
2
- import { sleep } from '../utils.js';
3
- import { MqttClient } from 'mqtt';
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';
4
6
  import { interval, Subject } from 'rxjs';
5
- import asyncmqtt from 'async-mqtt';
6
- import { SwitchBotPlatform } from '../platform.js';
7
+ import { deviceBase } from './device.js';
7
8
  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
9
 
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;
10
+ import type { SwitchBotPlatform } from '../platform.js';
11
+ import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
12
+ import type { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange } from 'homebridge';
31
13
 
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;
14
+ export class Curtain extends deviceBase {
15
+ // Services
16
+ private WindowCovering: {
17
+ Name: CharacteristicValue;
18
+ Service: Service;
19
+ PositionState: CharacteristicValue;
20
+ TargetPosition: CharacteristicValue;
21
+ CurrentPosition: CharacteristicValue;
22
+ HoldPosition: CharacteristicValue;
23
+ };
24
+
25
+ private Battery: {
26
+ Name: CharacteristicValue;
27
+ Service: Service;
28
+ BatteryLevel: CharacteristicValue;
29
+ StatusLowBattery: CharacteristicValue;
30
+ ChargingState: CharacteristicValue;
31
+ };
32
+
33
+ private LightSensor?: {
34
+ Name: CharacteristicValue;
35
+ Service: Service;
36
+ CurrentAmbientLightLevel?: CharacteristicValue;
37
+ };
53
38
 
54
39
  // Target
55
40
  setNewTarget!: boolean;
56
41
  setNewTargetTimer!: NodeJS.Timeout;
57
42
 
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
43
  // Updates
73
44
  curtainUpdateInProgress!: boolean;
74
45
  doCurtainUpdate!: Subject<void>;
75
46
 
76
- // Connection
77
- private readonly OpenAPI: boolean;
78
- private readonly BLE: boolean;
79
-
80
- // EVE history service handler
81
- historyService: any = null;
82
-
83
47
  constructor(
84
- private readonly platform: SwitchBotPlatform,
85
- private accessory: PlatformAccessory,
86
- public device: device & devicesConfig,
48
+ readonly platform: SwitchBotPlatform,
49
+ accessory: PlatformAccessory,
50
+ device: device & devicesConfig,
87
51
  ) {
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
-
52
+ super(platform, accessory, device);
53
+ // default placeholder
54
+ this.history(device);
103
55
  // this is subject we use to track when we need to POST changes to the SwitchBot API
104
56
  this.doCurtainUpdate = new Subject();
105
57
  this.curtainUpdateInProgress = false;
106
58
  this.setNewTarget = false;
107
59
 
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);
60
+ // Initialize WindowCovering Service
61
+ accessory.context.WindowCovering = accessory.context.WindowCovering ?? {};
62
+ this.WindowCovering = {
63
+ Name: accessory.context.WindowCovering.Name ?? accessory.displayName,
64
+ Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service,
65
+ PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED,
66
+ TargetPosition: accessory.context.TargetPosition ?? 100,
67
+ CurrentPosition: accessory.context.CurrentPosition ?? 100,
68
+ HoldPosition: accessory.context.HoldPosition ?? false,
69
+ };
70
+ accessory.context.WindowCovering = this.WindowCovering as object;
71
+
72
+ // Initialize WindowCovering Service
73
+ this.WindowCovering.Service.
74
+ setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name)
75
+ .setCharacteristic(this.hap.Characteristic.ObstructionDetected, false)
76
+ .getCharacteristic(this.hap.Characteristic.PositionState)
77
+ .onGet(() => {
78
+ return this.WindowCovering.PositionState;
79
+ });
135
80
 
136
- this.windowCoveringService
81
+ // Initialize WindowCovering CurrentPosition
82
+ this.WindowCovering.Service
137
83
  .getCharacteristic(this.hap.Characteristic.CurrentPosition)
138
84
  .setProps({
139
- minStep: this.minStep(device),
85
+ minStep: device.curtain?.set_minStep ?? 1,
140
86
  minValue: 0,
141
87
  maxValue: 100,
142
88
  validValueRanges: [0, 100],
143
89
  })
144
90
  .onGet(() => {
145
- return this.CurrentPosition;
91
+ return this.WindowCovering.CurrentPosition;
146
92
  });
147
93
 
148
- this.windowCoveringService
94
+ // Initialize WindowCovering TargetPosition
95
+ this.WindowCovering.Service
149
96
  .getCharacteristic(this.hap.Characteristic.TargetPosition)
150
97
  .setProps({
151
- minStep: this.minStep(device),
98
+ minStep: device.curtain?.set_minStep ?? 1,
152
99
  minValue: 0,
153
100
  maxValue: 100,
154
101
  validValueRanges: [0, 100],
155
102
  })
103
+ .onGet(() => {
104
+ return this.WindowCovering.TargetPosition;
105
+ })
156
106
  .onSet(this.TargetPositionSet.bind(this));
157
107
 
158
- this.windowCoveringService
108
+ // Initialize WindowCovering TargetPosition
109
+ this.WindowCovering.Service
159
110
  .getCharacteristic(this.hap.Characteristic.HoldPosition)
111
+ .onGet(() => {
112
+ return this.WindowCovering.HoldPosition;
113
+ })
160
114
  .onSet(this.HoldPositionSet.bind(this));
161
115
 
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;
116
+ // Initialize Battery Service
117
+ accessory.context.Battery = accessory.context.Battery ?? {};
118
+ this.Battery = {
119
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
120
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
121
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
122
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
123
+ ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING,
124
+ };
125
+ accessory.context.Battery = this.Battery as object;
126
+
127
+ // Initialize Battery Service
128
+ this.Battery.Service
129
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
130
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
131
+ .onGet(() => {
132
+ return this.Battery.BatteryLevel;
133
+ });
172
134
 
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
- }
135
+ this.Battery.Service
136
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
137
+ .onGet(() => {
138
+ return this.Battery.StatusLowBattery;
139
+ });
180
140
 
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;
141
+ this.Battery.Service
142
+ .getCharacteristic(this.hap.Characteristic.ChargingState)
143
+ .onGet(() => {
144
+ return this.Battery.ChargingState;
145
+ });
185
146
 
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`);
147
+ // Initialize LightSensor Service
148
+ if (device.curtain?.hide_lightsensor) {
149
+ if (this.LightSensor?.Service) {
150
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
151
+ this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service;
152
+ accessory.removeService(this.LightSensor.Service);
153
+ accessory.context.LightSensor = {};
154
+ }
155
+ } else {
156
+ accessory.context.LightSensor = accessory.context.LightSensor ?? {};
157
+ this.LightSensor = {
158
+ Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
159
+ Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service,
160
+ CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001,
161
+ };
162
+ accessory.context.LightSensor = this.LightSensor as object;
163
+
164
+ // Initialize LightSensor Characteristic
165
+ this.LightSensor.Service
166
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name)
167
+ .setCharacteristic(this.hap.Characteristic.StatusActive, true)
168
+ .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
169
+ .onGet(() => {
170
+ return this.LightSensor!.CurrentAmbientLightLevel!;
171
+ });
189
172
  }
190
173
 
174
+ // Retrieve initial values and updateHomekit
175
+ this.refreshStatus();
176
+
191
177
  // Update Homekit
192
178
  this.updateHomeKitCharacteristics();
193
179
 
@@ -199,35 +185,17 @@ export class Curtain {
199
185
  });
200
186
 
201
187
  //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
- }
188
+ this.registerWebhook(accessory, device);
222
189
 
223
190
  // update slide progress
224
- interval(this.updateRate * 1000)
191
+ interval(this.deviceUpdateRate * 1000)
225
192
  //.pipe(skipWhile(() => this.curtainUpdateInProgress))
226
193
  .subscribe(async () => {
227
- if (this.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
194
+ if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
228
195
  return;
229
196
  }
230
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`);
197
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving,`
198
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
231
199
  await this.refreshStatus();
232
200
  });
233
201
 
@@ -238,29 +206,37 @@ export class Curtain {
238
206
  tap(() => {
239
207
  this.curtainUpdateInProgress = true;
240
208
  }),
241
- debounceTime(this.platform.config.options!.pushRate! * 1000),
209
+ debounceTime(this.devicePushRate * 1000),
242
210
  )
243
211
  .subscribe(async () => {
244
212
  try {
245
213
  await this.pushChanges();
246
214
  } catch (e: any) {
247
215
  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
- );
216
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
217
+ + ` Error Message: ${JSON.stringify(e.message)}`);
252
218
  }
253
219
  this.curtainUpdateInProgress = false;
254
220
  });
255
221
 
256
222
  // Setup EVE history features
257
- this.setupHistoryService(device);
223
+ this.setupHistoryService(accessory, device);
224
+ }
225
+
226
+ private history(device: device & devicesConfig) {
227
+ if (device.history === true) {
228
+ // initialize when this accessory is newly created.
229
+ this.accessory.context.lastActivation = this.accessory.context.lastActivation ?? 0;
230
+ } else {
231
+ // removes cached values if history is turned off
232
+ delete this.accessory.context.lastActivation;
233
+ }
258
234
  }
259
235
 
260
236
  /*
261
237
  * Setup EVE history features for curtain devices.
262
238
  */
263
- async setupHistoryService(device: device & devicesConfig): Promise<void> {
239
+ async setupHistoryService(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
264
240
  if (device.history !== true) {
265
241
  return;
266
242
  }
@@ -295,8 +271,7 @@ export class Curtain {
295
271
  this.accessory.context.lastActivation = entry.time;
296
272
  sensor?.updateCharacteristic(
297
273
  this.platform.eve.Characteristics.LastActivation,
298
- Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime()),
299
- );
274
+ Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime()));
300
275
  this.historyService.addEntry(entry);
301
276
  }
302
277
  });
@@ -304,7 +279,7 @@ export class Curtain {
304
279
  }
305
280
 
306
281
  async updateHistory(): Promise<void> {
307
- const motion = Number(this.CurrentPosition) > 0 ? 1 : 0;
282
+ const motion = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0;
308
283
  this.historyService.addEntry({
309
284
  time: Math.round(new Date().valueOf() / 1000),
310
285
  motion: motion,
@@ -317,198 +292,220 @@ export class Curtain {
317
292
  );
318
293
  }
319
294
 
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> {
295
+ async BLEparseStatus(serviceData: serviceData): Promise<void> {
339
296
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
297
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(serviceData)}`);
340
298
  // CurrentPosition
341
- this.CurrentPosition = 100 - Number(this.BLE_CurrentPosition);
299
+ this.WindowCovering.CurrentPosition = 100 - Number(serviceData.position!);
342
300
  await this.setMinMax();
343
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`);
301
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.WindowCovering.CurrentPosition}`);
344
302
  if (this.setNewTarget) {
345
303
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
346
304
  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}`);
305
+ if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
306
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
307
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
308
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
309
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
310
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
311
+ } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) {
312
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
313
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
314
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
315
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
316
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
357
317
  } 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}`);
318
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,`
319
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
320
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
321
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
322
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
323
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
362
324
  }
363
325
  } 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;
326
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
327
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
328
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
367
329
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
368
330
  }
369
- this.debugLog(
370
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
371
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
372
- );
331
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
332
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
373
333
 
374
334
  if (!this.device.curtain?.hide_lightsensor) {
375
- this.set_minLux = this.minLux();
376
- this.set_maxLux = this.maxLux();
377
- this.spaceBetweenLevels = 9;
335
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
336
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
337
+ const spaceBetweenLevels = 9;
378
338
 
339
+ serviceData.lightLevel = 1;
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_maxLux - set_minLux) / spaceBetweenLevels) * (serviceData.lightLevel === 1
344
+ ? set_minLux : 2);
345
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 LightLevel: ${serviceData.lightLevel},`
346
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
347
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
348
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 LightLevel: ${serviceData.lightLevel},`
349
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
384
350
  break;
385
351
  case 2:
386
- this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
387
- this.debugLog(
388
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
389
- ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
390
- );
352
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(serviceData.lightLevel) - 1);
353
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 LightLevel: ${serviceData.lightLevel},`
354
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
355
+ this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels;
356
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 LightLevel: ${serviceData.lightLevel},`
357
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
358
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 3 LightLevel: ${serviceData.lightLevel},`
359
+ + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`);
391
360
  break;
392
361
  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}`);
362
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(serviceData.lightLevel) - 1);
363
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 LightLevel: ${serviceData.lightLevel},`
364
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
365
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2;
366
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 LightLevel: ${serviceData.lightLevel},`
367
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
368
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
395
369
  break;
396
370
  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}`);
371
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3;
372
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
373
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
399
374
  break;
400
375
  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}`);
376
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4;
377
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
378
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
403
379
  break;
404
380
  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}`);
381
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5;
382
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
383
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
407
384
  break;
408
385
  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}`);
386
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6;
387
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
388
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
411
389
  break;
412
390
  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}`);
391
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7;
392
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
393
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
415
394
  break;
416
395
  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}`);
396
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8;
397
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
398
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
419
399
  break;
420
400
  case 10:
421
401
  default:
422
- this.CurrentAmbientLightLevel = this.set_maxLux;
402
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(serviceData.lightLevel) - 1);
403
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 1 LightLevel: ${serviceData.lightLevel},`
404
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
405
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
406
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} 2 LightLevel: ${serviceData.lightLevel},`
407
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
423
408
  this.debugLog();
424
409
  }
425
- this.debugLog(
426
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
427
- ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
428
- );
410
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
411
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
429
412
  }
430
413
  // Battery
431
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
432
- if (this.BatteryLevel < 10) {
433
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
414
+ this.Battery.BatteryLevel = Number(serviceData.battery);
415
+ if (this.Battery.BatteryLevel < 10) {
416
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
434
417
  } else {
435
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
418
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
436
419
  }
437
- this.debugLog(
438
- `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`,
439
- );
420
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
421
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
440
422
  }
441
423
 
442
- async openAPIparseStatus(): Promise<void> {
424
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise<void> {
443
425
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
444
426
  // CurrentPosition
445
- this.CurrentPosition = 100 - Number(this.OpenAPI_CurrentPosition);
427
+ this.WindowCovering!.CurrentPosition = 100 - Number(deviceStatus.body.slidePosition);
446
428
  await this.setMinMax();
447
- this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
429
+ this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
448
430
  if (this.setNewTarget) {
449
431
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
450
432
  }
451
433
 
452
- if (this.setNewTarget && this.OpenAPI_InMotion) {
434
+ if (this.setNewTarget && deviceStatus.body.moving) {
453
435
  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}`);
436
+ if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
437
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
438
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
439
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
440
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
441
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
442
+ } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) {
443
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
444
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
445
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
446
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
447
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
464
448
  } 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}`);
449
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,`
450
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
451
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
452
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
453
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
454
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
469
455
  }
470
456
  } 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;
457
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
458
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
459
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
474
460
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
475
461
  }
476
- this.debugLog(
477
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
478
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
479
- );
462
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
463
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
480
464
 
481
465
  // Brightness
482
466
  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;
467
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
468
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
469
+ switch (deviceStatus.body.brightness) {
489
470
  case 'bright':
471
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
472
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
473
+ break;
474
+ case 'dim':
490
475
  default:
491
- this.CurrentAmbientLightLevel = this.set_maxLux;
476
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
492
477
  }
493
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
478
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
479
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
494
480
  }
495
481
 
496
482
  // BatteryLevel
497
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
498
- if (this.BatteryLevel < 10) {
499
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
483
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
484
+ if (this.Battery.BatteryLevel < 10) {
485
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
500
486
  } else {
501
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
487
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
502
488
  }
503
- if (Number.isNaN(this.BatteryLevel)) {
504
- this.BatteryLevel = 100;
489
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
490
+ this.Battery.BatteryLevel = 100;
505
491
  }
506
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
507
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
492
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
493
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
508
494
 
509
- // FirmwareRevision
510
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
511
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
495
+ // Firmware Version
496
+ const version = deviceStatus.body.version?.toString();
497
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
498
+ if (deviceStatus.body.version) {
499
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
500
+ this.accessory
501
+ .getService(this.hap.Service.AccessoryInformation)!
502
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
503
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
504
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
505
+ .updateValue(deviceVersion);
506
+ this.accessory.context.deviceVersion = deviceVersion;
507
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
508
+ }
512
509
  }
513
510
 
514
511
  async refreshStatus(): Promise<void> {
@@ -520,147 +517,73 @@ export class Curtain {
520
517
  await this.openAPIRefreshStatus();
521
518
  } else {
522
519
  await this.offlineOff();
523
- this.debugWarnLog(
524
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
525
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
526
- );
520
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
521
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
527
522
  }
528
523
  }
529
524
 
530
525
  async BLERefreshStatus(): Promise<void> {
531
526
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
532
- const switchbot = await this.platform.connectBLE();
533
- // Convert to BLE Address
534
- this.device.bleMac = this.device
535
- .deviceId!.match(/.{1,2}/g)!
536
- .join(':')
537
- .toLowerCase();
538
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
539
- this.getCustomBLEAddress(switchbot);
527
+ const switchbot = await this.switchbotBLE();
528
+
529
+ if (switchbot === undefined) {
530
+ await this.BLERefreshConnection(switchbot);
531
+ } else {
540
532
  // 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
- (async () => {
533
+ (async () => {
550
534
  // Start to monitor advertisement packets
551
- await switchbot.startScan({
552
- model: model,
553
- id: this.device.bleMac,
554
- });
555
- // Set an event handler
556
- 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') {
560
- 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;
535
+ const serviceData: serviceData = await this.monitorAdvertisementPackets(switchbot);
536
+ // Update HomeKit
537
+ if (serviceData.model !== '' && serviceData.modelName !== '') {
538
+ await this.BLEparseStatus(serviceData);
539
+ await this.updateHomeKitCharacteristics();
566
540
  } else {
567
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
568
- }
569
- };
570
- // Wait 1 seconds
571
- await switchbot.wait(this.scanDuration * 1000);
572
- // Stop to monitor
573
- await switchbot.stopScan();
574
- // Update HomeKit
575
- await this.BLEparseStatus();
576
- await this.updateHomeKitCharacteristics();
577
- })();
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
- );
541
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed to get serviceData, serviceData: ${serviceData}`);
627
542
  await this.BLERefreshConnection(switchbot);
628
- });
629
- } else {
630
- await this.BLERefreshConnection(switchbot);
631
- }*/
543
+ }
544
+ })();
545
+ }
632
546
  }
633
547
 
634
548
  async openAPIRefreshStatus(): Promise<void> {
635
549
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
636
550
  try {
637
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
638
- headers: this.platform.generateHeaders(),
639
- });
640
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
551
+ const { body, statusCode } = await this.deviceRefreshStatus();
641
552
  const deviceStatus: any = await body.json();
642
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
643
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
553
+ await this.refreshStatusCodes(statusCode, deviceStatus);
644
554
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
645
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
646
- + `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();
653
- this.updateHomeKitCharacteristics();
555
+ await this.successfulRefreshStatus(statusCode, deviceStatus);
556
+ await this.openAPIparseStatus(deviceStatus);
557
+ await this.updateHomeKitCharacteristics();
654
558
  } else {
655
- this.statusCode(statusCode);
656
- this.statusCode(deviceStatus.statusCode);
559
+ await this.statusCodes(statusCode, deviceStatus);
657
560
  }
658
561
  } catch (e: any) {
659
562
  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
- );
563
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
564
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
565
+ }
566
+ }
567
+
568
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
569
+ if (device.webhook) {
570
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
571
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
572
+ try {
573
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
574
+ const { slidePosition, battery } = context;
575
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ` +
576
+ '(slidePosition, battery) = ' +
577
+ `Webhook:(${slidePosition}, ${battery}), `
578
+ + `current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
579
+ this.WindowCovering.CurrentPosition = slidePosition;
580
+ this.Battery.BatteryLevel = battery;
581
+ this.updateHomeKitCharacteristics();
582
+ } catch (e: any) {
583
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} `
584
+ + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
585
+ }
586
+ };
664
587
  }
665
588
  }
666
589
 
@@ -673,9 +596,8 @@ export class Curtain {
673
596
  await this.openAPIpushChanges();
674
597
  } else {
675
598
  await this.offlineOff();
676
- this.debugWarnLog(
677
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
678
- );
599
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
600
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
679
601
  }
680
602
  // Refresh the status from the API
681
603
  interval(15000)
@@ -688,36 +610,32 @@ export class Curtain {
688
610
 
689
611
  async BLEpushChanges(): Promise<void> {
690
612
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
691
- if (this.TargetPosition !== this.CurrentPosition) {
613
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) {
692
614
  const switchbot = await this.platform.connectBLE();
693
615
  // Convert to BLE Address
694
- this.device.bleMac = this.device
695
- .deviceId!.match(/.{1,2}/g)!
696
- .join(':')
697
- .toLowerCase();
698
- 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}`);
616
+ await this.convertBLEAddress();
617
+ const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
618
+ const adjustedMode = setPositionMode === 1 ? 0x01 : 0xff;
619
+ this.debugLog(`${this.accessory.displayName} Mode: ${Mode}`);
702
620
  if (switchbot !== false) {
703
621
  try {
704
- const device_list = await switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac });
705
- this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`);
622
+ const device_list = await switchbot.discover({ model: this.device.bleModel, quick: true, id: this.device.bleMac });
623
+ this.infoLog(`${this.accessory.displayName} Target Position: ${this.WindowCovering.TargetPosition}`);
706
624
 
707
- await this.retry({
708
- max: this.maxRetry(),
625
+ await this.retryBLE({
626
+ max: await this.maxRetryBLE(),
709
627
  fn: async () => {
710
- await device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode);
628
+ await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), adjustedMode);
711
629
  },
712
630
  });
713
631
 
714
632
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
633
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
634
+ + `TargetPosition: ${this.WindowCovering.TargetPosition} sent over BLE, sent successfully`);
715
635
  } catch (e) {
716
636
  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
- );
637
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
638
+ + ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`);
721
639
  await this.BLEPushConnection();
722
640
  throw new Error('Connection error');
723
641
  }
@@ -726,30 +644,8 @@ export class Curtain {
726
644
  await this.BLEPushConnection();
727
645
  }
728
646
  } 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;
647
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.`
648
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`);
753
649
  }
754
650
  }
755
651
 
@@ -758,24 +654,13 @@ export class Curtain {
758
654
  let parameter: string;
759
655
  let commandType: string;
760
656
  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) {
657
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition || this.device.disableCaching) {
658
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.WindowCovering.TargetPosition}`);
659
+ const adjustedTargetPosition = 100 - Number(this.WindowCovering.TargetPosition);
660
+ const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
661
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${Mode}`);
662
+ const adjustedMode = setPositionMode || 'ff';
663
+ if (this.WindowCovering.HoldPosition) {
779
664
  command = 'pause';
780
665
  parameter = 'default';
781
666
  commandType = 'command';
@@ -791,35 +676,26 @@ export class Curtain {
791
676
  });
792
677
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
793
678
  try {
794
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
795
- body: bodyChange,
796
- method: 'POST',
797
- headers: this.platform.generateHeaders(),
798
- });
799
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
679
+ const { body, statusCode } = await this.pushChangeRequest(bodyChange);
800
680
  const deviceStatus: any = await body.json();
801
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
802
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
803
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
681
+ await this.pushStatusCodes(statusCode, deviceStatus);
804
682
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
805
683
  this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
806
684
  + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
685
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
686
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
807
687
  } else {
808
688
  this.statusCode(statusCode);
809
689
  this.statusCode(deviceStatus.statusCode);
810
690
  }
811
691
  } catch (e: any) {
812
692
  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
- );
693
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
694
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
817
695
  }
818
696
  } 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
- );
697
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.`
698
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`);
823
699
  }
824
700
  }
825
701
 
@@ -827,50 +703,53 @@ export class Curtain {
827
703
  * Handle requests to set the value of the "Target Position" characteristic
828
704
  */
829
705
  async TargetPositionSet(value: CharacteristicValue): Promise<void> {
830
- if (this.TargetPosition === this.accessory.context.TargetPosition) {
706
+ if (this.WindowCovering.TargetPosition === this.accessory.context.TargetPosition) {
831
707
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetPosition: ${value}`);
832
708
  } else {
833
709
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
834
710
  }
835
711
 
836
712
  // Set HoldPosition to false when TargetPosition is changed
837
- this.HoldPosition = false;
838
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.HoldPosition);
713
+ this.WindowCovering.HoldPosition = false;
714
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition);
839
715
 
840
- this.TargetPosition = value;
716
+ this.WindowCovering.TargetPosition = value;
841
717
  if (this.device.mqttURL) {
842
- this.mqttPublish('TargetPosition', this.TargetPosition);
843
- this.mqttPublish('HoldPosition', this.HoldPosition);
718
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
719
+ this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString()); // Convert boolean to string
844
720
  }
845
721
 
846
722
  await this.setMinMax();
847
- if (value > this.CurrentPosition) {
848
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
723
+ if (value > this.WindowCovering.CurrentPosition) {
724
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
849
725
  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;
726
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
727
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
728
+ } else if (value < this.WindowCovering.CurrentPosition) {
729
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
853
730
  this.setNewTarget = true;
854
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
731
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
732
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
855
733
  } else {
856
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
734
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
857
735
  this.setNewTarget = false;
858
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
736
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
737
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
859
738
  }
860
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState);
861
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
739
+ this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState);
740
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
862
741
 
863
742
  /**
864
743
  * If Curtain movement time is short, the moving flag from backend is always false.
865
744
  * The minimum time depends on the network control latency.
866
745
  */
867
746
  clearTimeout(this.setNewTargetTimer);
868
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
747
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.deviceUpdateRate}`);
869
748
  if (this.setNewTarget) {
870
749
  this.setNewTargetTimer = setTimeout(() => {
871
750
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
872
751
  this.setNewTarget = false;
873
- }, this.updateRate * 1000);
752
+ }, this.deviceUpdateRate * 1000);
874
753
  }
875
754
  this.doCurtainUpdate.next();
876
755
  }
@@ -880,153 +759,107 @@ export class Curtain {
880
759
  */
881
760
  async HoldPositionSet(value: CharacteristicValue): Promise<void> {
882
761
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${value}`);
883
- this.HoldPosition = value;
762
+ this.WindowCovering.HoldPosition = value;
884
763
  this.doCurtainUpdate.next();
885
764
  }
886
765
 
887
766
  async updateHomeKitCharacteristics(): Promise<void> {
888
767
  await this.setMinMax();
889
- if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) {
890
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
768
+ if (this.WindowCovering.CurrentPosition === undefined || Number.isNaN(this.WindowCovering.CurrentPosition)) {
769
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
891
770
  } else {
771
+ this.accessory.context.CurrentPosition = this.WindowCovering.CurrentPosition;
772
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.WindowCovering.CurrentPosition));
773
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
774
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
892
775
  if (this.device.mqttURL) {
893
- this.mqttPublish('CurrentPosition', this.CurrentPosition);
776
+ this.mqttPublish('CurrentPosition', this.WindowCovering.CurrentPosition.toString()); // Convert to string
894
777
  }
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
778
  }
899
- if (this.PositionState === undefined) {
900
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
779
+ if (this.WindowCovering.PositionState === undefined) {
780
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.WindowCovering.PositionState}`);
901
781
  } else {
902
782
  if (this.device.mqttURL) {
903
- this.mqttPublish('PositionState', this.PositionState);
783
+ this.mqttPublish('PositionState', this.WindowCovering.PositionState.toString());
904
784
  }
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}`);
785
+ this.accessory.context.PositionState = this.WindowCovering.PositionState;
786
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.WindowCovering.PositionState));
787
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
788
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
908
789
  }
909
- if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
910
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
790
+ if (this.WindowCovering.TargetPosition === undefined || Number.isNaN(this.WindowCovering.TargetPosition)) {
791
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.WindowCovering.TargetPosition}`);
911
792
  } else {
912
793
  if (this.device.mqttURL) {
913
- this.mqttPublish('TargetPosition', this.TargetPosition);
794
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
914
795
  }
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}`);
796
+ this.accessory.context.TargetPosition = this.WindowCovering.TargetPosition;
797
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.WindowCovering.TargetPosition));
798
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
799
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}`);
918
800
  }
919
- if (this.HoldPosition === undefined) {
920
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.HoldPosition}`);
801
+ if (this.WindowCovering.HoldPosition === undefined) {
802
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.WindowCovering.HoldPosition}`);
921
803
  } else {
922
804
  if (this.device.mqttURL) {
923
- this.mqttPublish('HoldPosition', this.HoldPosition);
805
+ this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString());
924
806
  }
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}`);
807
+ this.accessory.context.HoldPosition = this.WindowCovering.HoldPosition;
808
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition);
809
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
810
+ + ` HoldPosition: ${this.WindowCovering.HoldPosition}`);
928
811
  }
929
812
  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}`);
813
+ if (this.LightSensor!.CurrentAmbientLightLevel === undefined || Number.isNaN(this.LightSensor!.CurrentAmbientLightLevel)) {
814
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
815
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
932
816
  } else {
933
817
  if (this.device.mqttURL) {
934
- this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
818
+ this.mqttPublish('CurrentAmbientLightLevel', this.LightSensor!.CurrentAmbientLightLevel.toString());
935
819
  }
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
- );
820
+ this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel;
821
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel);
822
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
823
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
942
824
  if (this.device.history) {
943
825
  this.historyService?.addEntry({
944
826
  time: Math.round(new Date().valueOf() / 1000),
945
- lux: this.CurrentAmbientLightLevel,
827
+ lux: this.LightSensor!.CurrentAmbientLightLevel,
946
828
  });
947
829
  }
948
830
  }
949
831
  }
950
- if (this.BatteryLevel === undefined) {
951
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
832
+ if (this.Battery.BatteryLevel === undefined) {
833
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
952
834
  } else {
953
835
  if (this.device.mqttURL) {
954
- this.mqttPublish('BatteryLevel', this.BatteryLevel);
836
+ this.mqttPublish('BatteryLevel', this.Battery.BatteryLevel.toString());
955
837
  }
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}`);
838
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
839
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
840
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
959
841
  }
960
- if (this.StatusLowBattery === undefined) {
961
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
842
+ if (this.Battery.StatusLowBattery === undefined) {
843
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
962
844
  } else {
963
845
  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}`);
846
+ this.mqttPublish('StatusLowBattery', this.Battery.StatusLowBattery.toString());
1001
847
  }
848
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
849
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
850
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
851
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
1002
852
  }
1003
- }
1004
-
1005
- async stopScanning(switchbot: any) {
1006
- switchbot.stopScan();
1007
- if (this.BLE_IsConnected) {
1008
- await this.BLEparseStatus();
1009
- await this.updateHomeKitCharacteristics();
853
+ if (this.Battery.ChargingState === undefined) {
854
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`);
1010
855
  } 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
- })();
856
+ if (this.device.mqttURL) {
857
+ this.mqttPublish('ChargingState', this.Battery.ChargingState.toString());
858
+ }
859
+ this.accessory.context.ChargingState = this.Battery.ChargingState;
860
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState);
861
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
862
+ + ` ChargingState: ${this.Battery.ChargingState}`);
1030
863
  }
1031
864
  }
1032
865
 
@@ -1038,372 +871,81 @@ export class Curtain {
1038
871
  }
1039
872
 
1040
873
  async BLERefreshConnection(switchbot: any): Promise<void> {
1041
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
874
+ if (switchbot !== undefined) {
875
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
1042
876
  + ` ${JSON.stringify(switchbot)}`);
877
+ }
1043
878
  if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
1044
879
  this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`);
1045
880
  await this.openAPIRefreshStatus();
1046
881
  }
1047
882
  }
1048
883
 
1049
- async SilentPerformance() {
1050
- if (Number(this.TargetPosition) > 50) {
884
+ async setPerformance() {
885
+ let setPositionMode: number;
886
+ let Mode: string;
887
+ if (Number(this.WindowCovering.TargetPosition) > 50) {
1051
888
  if (this.device.curtain?.setOpenMode === '1') {
1052
- this.setPositionMode = '1';
1053
- this.Mode = 'Silent Mode';
889
+ setPositionMode = 1;
890
+ Mode = 'Silent Mode';
891
+ } else if (this.device.curtain?.setOpenMode === '0') {
892
+ setPositionMode = 0;
893
+ Mode = 'Performance Mode';
1054
894
  } else {
1055
- this.setPositionMode = '0';
1056
- this.Mode = 'Performance Mode';
895
+ setPositionMode = 0;
896
+ Mode = 'Default Mode';
1057
897
  }
1058
898
  } else {
1059
899
  if (this.device.curtain?.setCloseMode === '1') {
1060
- this.setPositionMode = '1';
1061
- this.Mode = 'Silent Mode';
900
+ setPositionMode = 1;
901
+ Mode = 'Silent Mode';
902
+ } else if (this.device.curtain?.setOpenMode === '0') {
903
+ setPositionMode = 0;
904
+ Mode = 'Performance Mode';
1062
905
  } else {
1063
- this.setPositionMode = '0';
1064
- this.Mode = 'Performance Mode';
906
+ setPositionMode = 0;
907
+ Mode = 'Default Mode';
1065
908
  }
1066
909
  }
910
+ return { setPositionMode, Mode };
1067
911
  }
1068
912
 
1069
913
  async setMinMax(): Promise<void> {
1070
914
  if (this.device.curtain?.set_min) {
1071
- if (Number(this.CurrentPosition) <= this.device.curtain?.set_min) {
1072
- this.CurrentPosition = 0;
915
+ if (Number(this.WindowCovering.CurrentPosition) <= this.device.curtain?.set_min) {
916
+ this.WindowCovering.CurrentPosition = 0;
1073
917
  }
1074
918
  }
1075
919
  if (this.device.curtain?.set_max) {
1076
- if (Number(this.CurrentPosition) >= this.device.curtain?.set_max) {
1077
- this.CurrentPosition = 100;
920
+ if (Number(this.WindowCovering.CurrentPosition) >= this.device.curtain?.set_max) {
921
+ this.WindowCovering.CurrentPosition = 100;
1078
922
  }
1079
923
  }
1080
924
  if (this.device.history) {
1081
925
  const motion = this.accessory.getService(this.hap.Service.MotionSensor);
1082
- const state = Number(this.CurrentPosition) > 0 ? 1 : 0;
926
+ const state = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0;
1083
927
  motion?.updateCharacteristic(this.hap.Characteristic.MotionDetected, state);
1084
928
  }
1085
929
  }
1086
930
 
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
931
  async offlineOff(): Promise<void> {
1221
932
  if (this.device.offline) {
1222
- await this.deviceContext();
1223
- await this.updateHomeKitCharacteristics();
933
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100);
934
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100);
935
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
1224
936
  }
1225
937
  }
1226
938
 
1227
939
  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);
940
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
941
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e);
942
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
943
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
944
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
945
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, e);
1231
946
  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;
947
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
948
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e);
1244
949
  }
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}`);
1298
- }
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
950
  }
1409
951
  }