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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/.vscode/settings.json +4 -1
  2. package/CHANGELOG.md +4 -1
  3. package/config.schema.json +170 -26
  4. package/dist/device/blindtilt.d.ts +21 -96
  5. package/dist/device/blindtilt.d.ts.map +1 -1
  6. package/dist/device/blindtilt.js +439 -817
  7. package/dist/device/blindtilt.js.map +1 -1
  8. package/dist/device/bot.d.ts +27 -72
  9. package/dist/device/bot.d.ts.map +1 -1
  10. package/dist/device/bot.js +835 -865
  11. package/dist/device/bot.js.map +1 -1
  12. package/dist/device/ceilinglight.d.ts +15 -69
  13. package/dist/device/ceilinglight.d.ts.map +1 -1
  14. package/dist/device/ceilinglight.js +287 -653
  15. package/dist/device/ceilinglight.js.map +1 -1
  16. package/dist/device/colorbulb.d.ts +15 -76
  17. package/dist/device/colorbulb.d.ts.map +1 -1
  18. package/dist/device/colorbulb.js +354 -732
  19. package/dist/device/colorbulb.js.map +1 -1
  20. package/dist/device/contact.d.ts +14 -66
  21. package/dist/device/contact.d.ts.map +1 -1
  22. package/dist/device/contact.js +268 -576
  23. package/dist/device/contact.js.map +1 -1
  24. package/dist/device/curtain.d.ts +18 -89
  25. package/dist/device/curtain.d.ts.map +1 -1
  26. package/dist/device/curtain.js +431 -851
  27. package/dist/device/curtain.js.map +1 -1
  28. package/dist/device/device.d.ts +60 -0
  29. package/dist/device/device.d.ts.map +1 -0
  30. package/dist/device/device.js +739 -0
  31. package/dist/device/device.js.map +1 -0
  32. package/dist/device/fan.d.ts +63 -0
  33. package/dist/device/fan.d.ts.map +1 -0
  34. package/dist/device/fan.js +698 -0
  35. package/dist/device/fan.js.map +1 -0
  36. package/dist/device/hub.d.ts +15 -60
  37. package/dist/device/hub.d.ts.map +1 -1
  38. package/dist/device/hub.js +257 -631
  39. package/dist/device/hub.js.map +1 -1
  40. package/dist/device/humidifier.d.ts +11 -66
  41. package/dist/device/humidifier.d.ts.map +1 -1
  42. package/dist/device/humidifier.js +266 -629
  43. package/dist/device/humidifier.js.map +1 -1
  44. package/dist/device/iosensor.d.ts +12 -64
  45. package/dist/device/iosensor.d.ts.map +1 -1
  46. package/dist/device/iosensor.js +215 -615
  47. package/dist/device/iosensor.js.map +1 -1
  48. package/dist/device/lightstrip.d.ts +12 -70
  49. package/dist/device/lightstrip.d.ts.map +1 -1
  50. package/dist/device/lightstrip.js +336 -645
  51. package/dist/device/lightstrip.js.map +1 -1
  52. package/dist/device/lock.d.ts +17 -63
  53. package/dist/device/lock.d.ts.map +1 -1
  54. package/dist/device/lock.js +290 -595
  55. package/dist/device/lock.js.map +1 -1
  56. package/dist/device/meter.d.ts +15 -64
  57. package/dist/device/meter.d.ts.map +1 -1
  58. package/dist/device/meter.js +211 -608
  59. package/dist/device/meter.js.map +1 -1
  60. package/dist/device/meterplus.d.ts +15 -65
  61. package/dist/device/meterplus.d.ts.map +1 -1
  62. package/dist/device/meterplus.js +205 -615
  63. package/dist/device/meterplus.js.map +1 -1
  64. package/dist/device/motion.d.ts +12 -69
  65. package/dist/device/motion.d.ts.map +1 -1
  66. package/dist/device/motion.js +202 -563
  67. package/dist/device/motion.js.map +1 -1
  68. package/dist/device/plug.d.ts +10 -57
  69. package/dist/device/plug.d.ts.map +1 -1
  70. package/dist/device/plug.js +125 -484
  71. package/dist/device/plug.js.map +1 -1
  72. package/dist/device/robotvacuumcleaner.d.ts +13 -63
  73. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  74. package/dist/device/robotvacuumcleaner.js +233 -549
  75. package/dist/device/robotvacuumcleaner.js.map +1 -1
  76. package/dist/device/waterdetector.d.ts +11 -63
  77. package/dist/device/waterdetector.d.ts.map +1 -1
  78. package/dist/device/waterdetector.js +170 -493
  79. package/dist/device/waterdetector.js.map +1 -1
  80. package/dist/homebridge-ui/public/index.html +22 -16
  81. package/dist/homebridge-ui/server.js +1 -1
  82. package/dist/homebridge-ui/server.js.map +1 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +2 -2
  86. package/dist/index.js.map +1 -1
  87. package/dist/irdevice/airconditioner.d.ts +10 -50
  88. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  89. package/dist/irdevice/airconditioner.js +340 -468
  90. package/dist/irdevice/airconditioner.js.map +1 -1
  91. package/dist/irdevice/airpurifier.d.ts +8 -39
  92. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  93. package/dist/irdevice/airpurifier.js +105 -319
  94. package/dist/irdevice/airpurifier.js.map +1 -1
  95. package/dist/irdevice/camera.d.ts +7 -36
  96. package/dist/irdevice/camera.d.ts.map +1 -1
  97. package/dist/irdevice/camera.js +44 -285
  98. package/dist/irdevice/camera.js.map +1 -1
  99. package/dist/irdevice/fan.d.ts +7 -43
  100. package/dist/irdevice/fan.d.ts.map +1 -1
  101. package/dist/irdevice/fan.js +92 -350
  102. package/dist/irdevice/fan.js.map +1 -1
  103. package/dist/irdevice/irdevice.d.ts +40 -0
  104. package/dist/irdevice/irdevice.d.ts.map +1 -0
  105. package/dist/irdevice/irdevice.js +369 -0
  106. package/dist/irdevice/irdevice.js.map +1 -0
  107. package/dist/irdevice/light.d.ts +12 -45
  108. package/dist/irdevice/light.d.ts.map +1 -1
  109. package/dist/irdevice/light.js +118 -362
  110. package/dist/irdevice/light.js.map +1 -1
  111. package/dist/irdevice/other.d.ts +19 -48
  112. package/dist/irdevice/other.d.ts.map +1 -1
  113. package/dist/irdevice/other.js +438 -570
  114. package/dist/irdevice/other.js.map +1 -1
  115. package/dist/irdevice/tv.d.ts +8 -40
  116. package/dist/irdevice/tv.d.ts.map +1 -1
  117. package/dist/irdevice/tv.js +91 -339
  118. package/dist/irdevice/tv.js.map +1 -1
  119. package/dist/irdevice/vacuumcleaner.d.ts +7 -36
  120. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  121. package/dist/irdevice/vacuumcleaner.js +45 -285
  122. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  123. package/dist/irdevice/waterheater.d.ts +7 -36
  124. package/dist/irdevice/waterheater.d.ts.map +1 -1
  125. package/dist/irdevice/waterheater.js +47 -291
  126. package/dist/irdevice/waterheater.js.map +1 -1
  127. package/dist/platform.d.ts +22 -8
  128. package/dist/platform.d.ts.map +1 -1
  129. package/dist/platform.js +342 -191
  130. package/dist/platform.js.map +1 -1
  131. package/dist/settings.d.ts +64 -17
  132. package/dist/settings.d.ts.map +1 -1
  133. package/dist/settings.js +16 -552
  134. package/dist/settings.js.map +1 -1
  135. package/dist/utils.d.ts +77 -0
  136. package/dist/utils.d.ts.map +1 -1
  137. package/dist/utils.js +638 -1
  138. package/dist/utils.js.map +1 -1
  139. package/eslint.config.js +96 -0
  140. package/package.json +20 -16
  141. package/src/custom.d.ts +2 -1
  142. package/src/device/blindtilt.ts +472 -888
  143. package/src/device/bot.ts +893 -968
  144. package/src/device/ceilinglight.ts +304 -709
  145. package/src/device/colorbulb.ts +370 -803
  146. package/src/device/contact.ts +301 -611
  147. package/src/device/curtain.ts +460 -908
  148. package/src/device/device.ts +755 -0
  149. package/src/device/fan.ts +749 -0
  150. package/src/device/hub.ts +294 -664
  151. package/src/device/humidifier.ts +299 -696
  152. package/src/device/iosensor.ts +251 -644
  153. package/src/device/lightstrip.ts +362 -712
  154. package/src/device/lock.ts +328 -653
  155. package/src/device/meter.ts +247 -637
  156. package/src/device/meterplus.ts +240 -644
  157. package/src/device/motion.ts +230 -598
  158. package/src/device/plug.ts +139 -525
  159. package/src/device/robotvacuumcleaner.ts +258 -593
  160. package/src/device/waterdetector.ts +191 -522
  161. package/src/homebridge-ui/public/index.html +22 -16
  162. package/src/homebridge-ui/server.ts +1 -1
  163. package/src/index.ts +4 -3
  164. package/src/irdevice/airconditioner.ts +370 -527
  165. package/src/irdevice/airpurifier.ts +132 -356
  166. package/src/irdevice/camera.ts +58 -311
  167. package/src/irdevice/fan.ts +109 -379
  168. package/src/irdevice/irdevice.ts +382 -0
  169. package/src/irdevice/light.ts +157 -412
  170. package/src/irdevice/other.ts +507 -648
  171. package/src/irdevice/tv.ts +121 -375
  172. package/src/irdevice/vacuumcleaner.ts +59 -311
  173. package/src/irdevice/waterheater.ts +61 -320
  174. package/src/platform.ts +395 -259
  175. package/src/settings.ts +118 -609
  176. package/src/utils.ts +642 -1
  177. package/.github/npm-version-script.cjs +0 -81
  178. package/src/device/waterdetector.json +0 -11
@@ -0,0 +1,749 @@
1
+ /* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
2
+ *
3
+ * plug.ts: @switchbot/homebridge-switchbot.
4
+ */
5
+ import { request } from 'undici';
6
+ import { deviceBase } from './device.js';
7
+ import { Devices } from '../settings.js';
8
+ import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs';
9
+
10
+ import type { SwitchBotPlatform } from '../platform.js';
11
+ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
12
+ import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
13
+
14
+ export class Fan extends deviceBase {
15
+ // Services
16
+ private Fan: {
17
+ Name: CharacteristicValue;
18
+ Service: Service;
19
+ Active: CharacteristicValue;
20
+ SwingMode: CharacteristicValue;
21
+ RotationSpeed: CharacteristicValue;
22
+ };
23
+
24
+ private Battery: {
25
+ Name: CharacteristicValue;
26
+ Service: Service;
27
+ BatteryLevel: CharacteristicValue;
28
+ StatusLowBattery: CharacteristicValue;
29
+ ChargingState: CharacteristicValue;
30
+ };
31
+
32
+ private LightBulb: {
33
+ Name: CharacteristicValue;
34
+ Service: Service;
35
+ On: CharacteristicValue;
36
+ Brightness: CharacteristicValue;
37
+ };
38
+
39
+ // Updates
40
+ fanUpdateInProgress!: boolean;
41
+ doFanUpdate!: Subject<void>;
42
+
43
+ constructor(
44
+ readonly platform: SwitchBotPlatform,
45
+ accessory: PlatformAccessory,
46
+ device: device & devicesConfig,
47
+ ) {
48
+ super(platform, accessory, device);
49
+ // this is subject we use to track when we need to POST changes to the SwitchBot API
50
+ this.doFanUpdate = new Subject();
51
+ this.fanUpdateInProgress = false;
52
+
53
+ // Initialize Fan Service
54
+ accessory.context.Fan = accessory.context.Fan ?? {};
55
+ this.Fan = {
56
+ Name: accessory.context.Fan.Name ?? accessory.displayName,
57
+ Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service,
58
+ Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE,
59
+ SwingMode: accessory.context.SwingMode ?? this.hap.Characteristic.SwingMode.SWING_DISABLED,
60
+ RotationSpeed: accessory.context.RotationSpeed ?? 0,
61
+ };
62
+ accessory.context.Fan = this.Fan as object;
63
+
64
+ // Initialize Fan Service
65
+ this.Fan.Service
66
+ .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name)
67
+ .getCharacteristic(this.hap.Characteristic.Active)
68
+ .onGet(() => {
69
+ return this.Fan.Active;
70
+ })
71
+ .onSet(this.ActiveSet.bind(this));
72
+
73
+ // Initialize Fan RotationSpeed Characteristic
74
+ this.Fan.Service
75
+ .getCharacteristic(this.hap.Characteristic.RotationSpeed)
76
+ .onGet(() => {
77
+ return this.Fan.RotationSpeed;
78
+ })
79
+ .onSet(this.RotationSpeedSet.bind(this));
80
+
81
+ // Initialize Fan SwingMode Characteristic
82
+ this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode)
83
+ .onGet(() => {
84
+ return this.Fan.SwingMode;
85
+ })
86
+ .onSet(this.SwingModeSet.bind(this));
87
+
88
+ // Initialize Battery Service
89
+ accessory.context.Battery = accessory.context.Battery ?? {};
90
+ this.Battery = {
91
+ Name: accessory.context.Battery.Name ?? accessory.displayName,
92
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
93
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
94
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
95
+ ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING,
96
+ };
97
+ accessory.context.Battery = this.Battery as object;
98
+
99
+ // Initialize Battery Service
100
+ this.Battery.Service
101
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
102
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
103
+ .onGet(() => {
104
+ return this.Battery.BatteryLevel;
105
+ });
106
+
107
+ // Initialize Battery ChargingState Characteristic
108
+ this.Battery.Service
109
+ .getCharacteristic(this.hap.Characteristic.ChargingState)
110
+ .onGet(() => {
111
+ return this.Battery.ChargingState;
112
+ });
113
+
114
+ // Initialize Battery StatusLowBattery Characteristic
115
+ this.Battery.Service
116
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
117
+ .onGet(() => {
118
+ return this.Battery.StatusLowBattery;
119
+ });
120
+
121
+ // Initialize LightBulb Service
122
+ accessory.context.LightBulb = accessory.context.LightBulb ?? {};
123
+ this.LightBulb = {
124
+ Name: accessory.context.LightBulb.Name ?? accessory.displayName,
125
+ Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service,
126
+ On: accessory.context.On ?? false,
127
+ Brightness: accessory.context.Brightness ?? 0,
128
+ };
129
+ accessory.context.LightBulb = this.LightBulb as object;
130
+
131
+ // Initialize LightBulb Characteristics
132
+ this.LightBulb.Service
133
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name)
134
+ .getCharacteristic(this.hap.Characteristic.On)
135
+ .onGet(() => {
136
+ return this.LightBulb.On;
137
+ })
138
+ .onSet(this.OnSet.bind(this));
139
+
140
+ // Initialize LightBulb Brightness Characteristic
141
+ this.LightBulb.Service
142
+ .getCharacteristic(this.hap.Characteristic.Brightness)
143
+ .onGet(() => {
144
+ return this.LightBulb.Brightness;
145
+ })
146
+ .onSet(this.BrightnessSet.bind(this));
147
+
148
+ // Retrieve initial values and updateHomekit
149
+ this.refreshStatus();
150
+
151
+ // Update Homekit
152
+ this.updateHomeKitCharacteristics();
153
+
154
+ // Start an update interval
155
+ interval(this.deviceRefreshRate * 1000)
156
+ .pipe(skipWhile(() => this.fanUpdateInProgress))
157
+ .subscribe(async () => {
158
+ await this.refreshStatus();
159
+ });
160
+
161
+ //regisiter webhook event handler
162
+ this.registerWebhook(accessory, device);
163
+
164
+ // Watch for Plug change events
165
+ // We put in a debounce of 100ms so we don't make duplicate calls
166
+ this.doFanUpdate
167
+ .pipe(
168
+ tap(() => {
169
+ this.fanUpdateInProgress = true;
170
+ }),
171
+ debounceTime(this.devicePushRate * 1000),
172
+ )
173
+ .subscribe(async () => {
174
+ try {
175
+ await this.pushChanges();
176
+ } catch (e: any) {
177
+ this.apiError(e);
178
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,`
179
+ + ` Error Message: ${JSON.stringify(e.message)}`);
180
+ }
181
+ this.fanUpdateInProgress = false;
182
+ });
183
+ }
184
+
185
+ async BLEparseStatus(serviceData: serviceData): Promise<void> {
186
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
187
+
188
+ // State
189
+ switch (serviceData.state) {
190
+ case 'on':
191
+ this.Fan.Active = true;
192
+ break;
193
+ default:
194
+ this.Fan.Active = false;
195
+ }
196
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan.Active}`);
197
+ }
198
+
199
+ async openAPIparseStatus(deviceStatus: deviceStatus) {
200
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
201
+
202
+ // Active
203
+ this.Fan.Active = deviceStatus.body.power === 'on' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
204
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`);
205
+
206
+ // SwingMode
207
+ this.Fan.SwingMode = deviceStatus.body.oscillation === 'on' ?
208
+ this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED;
209
+
210
+ // RotationSpeed
211
+ this.Fan.RotationSpeed = deviceStatus.body.fanSpeed;
212
+
213
+ // ChargingState
214
+ this.Battery.ChargingState = deviceStatus.body.chargingStatus === 'charging' ?
215
+ this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
216
+
217
+ // BatteryLevel
218
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
219
+ if (this.Battery.BatteryLevel < 10) {
220
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
221
+ } else {
222
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
223
+ }
224
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
225
+ this.Battery.BatteryLevel = 100;
226
+ }
227
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
228
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
229
+
230
+ // Firmware Version
231
+ const version = deviceStatus.body.version?.toString();
232
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
233
+ if (deviceStatus.body.version) {
234
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
235
+ this.accessory
236
+ .getService(this.hap.Service.AccessoryInformation)!
237
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
238
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
239
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
240
+ .updateValue(deviceVersion);
241
+ this.accessory.context.deviceVersion = deviceVersion;
242
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
243
+ }
244
+ }
245
+
246
+
247
+
248
+ /**
249
+ * Asks the SwitchBot API for the latest device information
250
+ */
251
+ async refreshStatus(): Promise<void> {
252
+ if (!this.device.enableCloudService && this.OpenAPI) {
253
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`);
254
+ } else if (this.BLE) {
255
+ await this.BLERefreshStatus();
256
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
257
+ await this.openAPIRefreshStatus();
258
+ } else {
259
+ await this.offlineOff();
260
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
261
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
262
+ }
263
+ }
264
+
265
+ async BLERefreshStatus(): Promise<void> {
266
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
267
+ const switchbot = await this.platform.connectBLE();
268
+ // Convert to BLE Address
269
+ this.device.bleMac = this.device
270
+ .deviceId!.match(/.{1,2}/g)!
271
+ .join(':')
272
+ .toLowerCase();
273
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
274
+ this.getCustomBLEAddress(switchbot);
275
+ // Start to monitor advertisement packets
276
+ (async () => {
277
+ // Start to monitor advertisement packets
278
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
279
+ // Set an event handler
280
+ switchbot.onadvertisement = (ad: any) => {
281
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
282
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
283
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
284
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
285
+ } else {
286
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
287
+ }
288
+ };
289
+ // Wait 10 seconds
290
+ await switchbot.wait(this.scanDuration * 1000);
291
+ // Stop to monitor
292
+ await switchbot.stopScan();
293
+ // Update HomeKit
294
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
295
+ await this.updateHomeKitCharacteristics();
296
+ })();
297
+ if (switchbot === undefined) {
298
+ await this.BLERefreshConnection(switchbot);
299
+ }
300
+ }
301
+
302
+ async openAPIRefreshStatus(): Promise<void> {
303
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
304
+ try {
305
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
306
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
307
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
308
+ const deviceStatus: any = await body.json();
309
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
310
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
311
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
312
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
313
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
314
+ this.openAPIparseStatus(deviceStatus);
315
+ this.updateHomeKitCharacteristics();
316
+ } else {
317
+ this.statusCode(statusCode);
318
+ this.statusCode(deviceStatus.statusCode);
319
+ }
320
+ } catch (e: any) {
321
+ this.apiError(e);
322
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
323
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
324
+ }
325
+ }
326
+
327
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
328
+ if (device.webhook) {
329
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
330
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
331
+ try {
332
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
333
+ const { version, battery, powerState, oscillation, chargingStatus, fanSpeed } = context;
334
+ const { Active, SwingMode, RotationSpeed } = this.Fan;
335
+ const { BatteryLevel, ChargingState } = this.Battery;
336
+ const { FirmwareRevision } = accessory.context;
337
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (version, battery, powerState, oscillation, chargingStatus, fanSpeed) = `
338
+ + `Webhook:(${version}, ${battery}, ${powerState}, ${oscillation}, ${chargingStatus}, ${fanSpeed}), `
339
+ + `current:(${FirmwareRevision}, ${BatteryLevel}, ${Active}, ${SwingMode}, ${ChargingState}, ${RotationSpeed})`);
340
+
341
+ // Active
342
+ this.Fan.Active = powerState === 'ON' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
343
+
344
+ // SwingMode
345
+ this.Fan.SwingMode = oscillation === 'on' ?
346
+ this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED;
347
+
348
+ // RotationSpeed
349
+ this.Fan.RotationSpeed = fanSpeed;
350
+
351
+ // ChargingState
352
+ this.Battery.ChargingState = chargingStatus === 'charging' ?
353
+ this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
354
+
355
+ // BatteryLevel
356
+ this.Battery.BatteryLevel = battery;
357
+
358
+ // Firmware Version
359
+ if (version) {
360
+ accessory.context.version = version;
361
+ accessory
362
+ .getService(this.hap.Service.AccessoryInformation)!
363
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version)
364
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
365
+ .updateValue(accessory.context.version);
366
+ }
367
+ this.updateHomeKitCharacteristics();
368
+ } catch (e: any) {
369
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
370
+ }
371
+ };
372
+ } else {
373
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Pushes the requested changes to the SwitchBot API
379
+ * commandType command parameter Description
380
+ * "command" "turnOff" "default" = set to OFF state
381
+ * "command" "turnOn" "default" = set to ON state
382
+ * "command" "setNightLightMode" "off, 1, or 2" = off, turn off nightlight, (1, bright) (2, dim)
383
+ * "command" "setWindMode" "direct, natural, sleep, or baby" = Set fan mode
384
+ * "command" "setWindSpeed" "{1-100} e.g. 10" = Set fan speed 1~100
385
+ */
386
+
387
+ async pushChanges(): Promise<void> {
388
+ if (!this.device.enableCloudService && this.OpenAPI) {
389
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} pushChanges enableCloudService: ${this.device.enableCloudService}`);
390
+ } else if (this.BLE) {
391
+ await this.BLEpushChanges();
392
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
393
+ await this.openAPIpushChanges();
394
+ } else {
395
+ await this.offlineOff();
396
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
397
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
398
+ }
399
+ // Refresh the status from the API
400
+ interval(15000)
401
+ .pipe(skipWhile(() => this.fanUpdateInProgress))
402
+ .pipe(take(1))
403
+ .subscribe(async () => {
404
+ await this.refreshStatus();
405
+ });
406
+ }
407
+
408
+ async BLEpushChanges(): Promise<void> {
409
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
410
+ if (this.Fan.Active !== this.accessory.context.Active) {
411
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`
412
+ + ` On: ${this.Fan.Active} OnCached: ${this.accessory.context.Active}`);
413
+ const switchbot = await this.platform.connectBLE();
414
+ // Convert to BLE Address
415
+ this.device.bleMac = this.device
416
+ .deviceId!.match(/.{1,2}/g)!
417
+ .join(':')
418
+ .toLowerCase();
419
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
420
+ switchbot
421
+ .discover({
422
+ model: this.device.bleModel,
423
+ id: this.device.bleMac,
424
+ })
425
+ .then(async (device_list: any) => {
426
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan.Active}`);
427
+ return await this.retryBLE({
428
+ max: await this.maxRetryBLE(),
429
+ fn: async () => {
430
+ if (this.Fan.Active) {
431
+ return await device_list[0].turnOn({ id: this.device.bleMac });
432
+ } else {
433
+ return await device_list[0].turnOff({ id: this.device.bleMac });
434
+ }
435
+ },
436
+ });
437
+ })
438
+ .then(() => {
439
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
440
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
441
+ + `Active: ${this.Fan.Active} sent over BLE, sent successfully`);
442
+ this.Fan.Active = false;
443
+ })
444
+ .catch(async (e: any) => {
445
+ this.apiError(e);
446
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
447
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
448
+ await this.BLEPushConnection();
449
+ });
450
+ } else {
451
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,`
452
+ + ` Active: ${this.Fan.Active}, ActiveCached: ${this.accessory.context.Active}`);
453
+ }
454
+ }
455
+
456
+ async openAPIpushChanges() {
457
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
458
+ if (this.Fan.Active !== this.accessory.context.Active) {
459
+ let command = '';
460
+ if (this.Fan.Active) {
461
+ command = 'turnOn';
462
+ } else {
463
+ command = 'turnOff';
464
+ }
465
+ const bodyChange = JSON.stringify({
466
+ command: `${command}`,
467
+ parameter: 'default',
468
+ commandType: 'command',
469
+ });
470
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
471
+ try {
472
+ const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
473
+ body: bodyChange,
474
+ method: 'POST',
475
+ headers: this.platform.generateHeaders(),
476
+ });
477
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
478
+ const deviceStatus: any = await body.json();
479
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
480
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
481
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
482
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
483
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
484
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
485
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
486
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
487
+ } else {
488
+ this.statusCode(statusCode);
489
+ this.statusCode(deviceStatus.statusCode);
490
+ }
491
+ } catch (e: any) {
492
+ this.apiError(e);
493
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
494
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
495
+ }
496
+ } else {
497
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.`
498
+ + `On: ${this.Fan.Active}, ActiveCached: ${this.accessory.context.Active}`);
499
+ }
500
+ // Push RotationSpeed Update
501
+ if (this.Fan.Active) {
502
+ await this.pushRotationSpeedChanges();
503
+ }
504
+ // Push SwingMode Update
505
+ if (this.Fan.Active) {
506
+ await this.pushSwingModeChanges();
507
+ }
508
+ }
509
+
510
+ async pushRotationSpeedChanges(): Promise<void> {
511
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushRotationSpeedChanges`);
512
+ if (this.Fan.SwingMode !== this.accessory.context.SwingMode) {
513
+ const bodyChange = JSON.stringify({
514
+ command: 'setWindSpeed',
515
+ parameter: `${this.Fan.RotationSpeed}`,
516
+ commandType: 'command',
517
+ });
518
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
519
+ try {
520
+ const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
521
+ body: bodyChange,
522
+ method: 'POST',
523
+ headers: this.platform.generateHeaders(),
524
+ });
525
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
526
+ const deviceStatus: any = await body.json();
527
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
528
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
529
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
530
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
531
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
532
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
533
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
534
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
535
+ } else {
536
+ this.statusCode(statusCode);
537
+ this.statusCode(deviceStatus.statusCode);
538
+ }
539
+ } catch (e: any) {
540
+ this.apiError(e);
541
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushRotationSpeedChanges with ${this.device.connectionType}`
542
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
543
+ }
544
+ } else {
545
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, pushRotationSpeedChanges: ${this.Fan.RotationSpeed}, `
546
+ + `pushRotationSpeedChangesCached: ${this.accessory.context.RotationSpeed}`);
547
+ }
548
+ }
549
+
550
+ async pushSwingModeChanges(): Promise<void> {
551
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushSwingModeChanges`);
552
+ if (this.Fan.SwingMode !== this.accessory.context.SwingMode) {
553
+ let parameter = '';
554
+ if (this.Fan.SwingMode === this.hap.Characteristic.SwingMode.SWING_ENABLED) {
555
+ parameter = 'on';
556
+ } else {
557
+ parameter = 'off';
558
+ }
559
+ const bodyChange = JSON.stringify({
560
+ command: 'setOscillation',
561
+ parameter: `${parameter}`,
562
+ commandType: 'command',
563
+ });
564
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
565
+ try {
566
+ const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
567
+ body: bodyChange,
568
+ method: 'POST',
569
+ headers: this.platform.generateHeaders(),
570
+ });
571
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
572
+ const deviceStatus: any = await body.json();
573
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
574
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
575
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
576
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
577
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
578
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
579
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
580
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
581
+ } else {
582
+ this.statusCode(statusCode);
583
+ this.statusCode(deviceStatus.statusCode);
584
+ }
585
+ } catch (e: any) {
586
+ this.apiError(e);
587
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushSwingModeChanges with ${this.device.connectionType}`
588
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
589
+ }
590
+ } else {
591
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, SwingMode: ${this.Fan.SwingMode}, `
592
+ + `SwingModeCached: ${this.accessory.context.SwingMode}`);
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Handle requests to set the value of the "On" characteristic
598
+ */
599
+ async ActiveSet(value: CharacteristicValue): Promise<void> {
600
+ if (this.Fan.Active === this.accessory.context.Active) {
601
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Active: ${value}`);
602
+ } else {
603
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`);
604
+ }
605
+
606
+ this.Fan.Active = value;
607
+ this.doFanUpdate.next();
608
+ }
609
+
610
+ /**
611
+ * Handle requests to set the value of the "On" characteristic
612
+ */
613
+ async RotationSpeedSet(value: CharacteristicValue): Promise<void> {
614
+ if (this.Fan.RotationSpeed === this.accessory.context.RotationSpeed) {
615
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set RotationSpeed: ${value}`);
616
+ } else {
617
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set RotationSpeed: ${value}`);
618
+ }
619
+
620
+ this.Fan.RotationSpeed = value;
621
+ this.doFanUpdate.next();
622
+ }
623
+
624
+ /**
625
+ * Handle requests to set the value of the "On" characteristic
626
+ */
627
+ async SwingModeSet(value: CharacteristicValue): Promise<void> {
628
+ if (this.Fan.SwingMode === this.accessory.context.SwingMode) {
629
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set SwingMode: ${value}`);
630
+ } else {
631
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set SwingMode: ${value}`);
632
+ }
633
+
634
+ this.Fan.SwingMode = value;
635
+ this.doFanUpdate.next();
636
+ }
637
+
638
+ /**
639
+ * Handle requests to set the value of the "On" characteristic
640
+ */
641
+ async OnSet(value: CharacteristicValue): Promise<void> {
642
+ if (this.LightBulb.On === this.accessory.context.On) {
643
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`);
644
+ } else {
645
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`);
646
+ }
647
+
648
+ this.LightBulb.On = value;
649
+ this.doFanUpdate.next();
650
+ }
651
+
652
+ /**
653
+ * Handle requests to set the value of the "Brightness" characteristic
654
+ */
655
+ async BrightnessSet(value: CharacteristicValue): Promise<void> {
656
+ if (this.LightBulb.Brightness === this.accessory.context.Brightness) {
657
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`);
658
+ } else if (this.LightBulb.On) {
659
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
660
+ } else {
661
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
662
+ }
663
+
664
+ this.LightBulb.Brightness = value;
665
+ this.doFanUpdate.next();
666
+ }
667
+
668
+ async updateHomeKitCharacteristics(): Promise<void> {
669
+ // Active
670
+ if (this.Fan.Active === undefined) {
671
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`);
672
+ } else {
673
+ this.accessory.context.Active = this.Fan.Active;
674
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.Fan.Active);
675
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Fan.Active}`);
676
+ }
677
+ // RotationSpeed
678
+ if (this.Fan.RotationSpeed === undefined) {
679
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} RotationSpeed: ${this.Fan.RotationSpeed}`);
680
+ } else {
681
+ this.accessory.context.RotationSpeed = this.Fan.RotationSpeed;
682
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.Fan.RotationSpeed);
683
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.Fan.RotationSpeed}`);
684
+ }
685
+ // SwingMode
686
+ if (this.Fan.SwingMode === undefined) {
687
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} SwingMode: ${this.Fan.SwingMode}`);
688
+ } else {
689
+ this.accessory.context.SwingMode = this.Fan.SwingMode;
690
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, this.Fan.SwingMode);
691
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.Fan.SwingMode}`);
692
+ }
693
+ // BateryLevel
694
+ if (this.Battery.BatteryLevel === undefined) {
695
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
696
+ } else {
697
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
698
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
699
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
700
+ }
701
+ // ChargingState
702
+ if (this.Battery.ChargingState === undefined) {
703
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`);
704
+ } else {
705
+ this.accessory.context.ChargingState = this.Battery.ChargingState;
706
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState);
707
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ChargingState: ${this.Battery.ChargingState}`);
708
+ }
709
+ // StatusLowBattery
710
+ if (this.Battery.StatusLowBattery === undefined) {
711
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
712
+ } else {
713
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
714
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
715
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
716
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
717
+ }
718
+ }
719
+
720
+ async BLEPushConnection() {
721
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
722
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
723
+ await this.openAPIpushChanges();
724
+ }
725
+ }
726
+
727
+ async BLERefreshConnection(switchbot: any): Promise<void> {
728
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
729
+ + ` ${JSON.stringify(switchbot)}`);
730
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
731
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`);
732
+ await this.openAPIRefreshStatus();
733
+ }
734
+ }
735
+
736
+ async offlineOff(): Promise<void> {
737
+ if (this.device.offline) {
738
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE);
739
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, 0);
740
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, this.hap.Characteristic.SwingMode.SWING_DISABLED);
741
+ }
742
+ }
743
+
744
+ async apiError(e: any): Promise<void> {
745
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, e);
746
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e);
747
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, e);
748
+ }
749
+ }