@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,103 +1,67 @@
1
- import { request } from 'undici';
2
- import { sleep } from '../utils.js';
3
- import { interval, Subject } from 'rxjs';
4
- import { SwitchBotPlatform } from '../platform.js';
5
- import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
6
- import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge';
7
- import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js';
8
-
9
- export class RobotVacuumCleaner {
10
- public readonly api: API;
11
- public readonly log: Logging;
12
- public readonly config!: SwitchBotPlatformConfig;
13
- protected readonly hap: HAP;
1
+ /* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
2
+ *
3
+ * robotvacuumcleaner.ts: @switchbot/homebridge-switchbot.
4
+ */
5
+ import { deviceBase } from './device.js';
6
+ import { Subject, interval, skipWhile } from 'rxjs';
7
+ import { debounceTime, take, tap } from 'rxjs/operators';
8
+
9
+ import type { SwitchBotPlatform } from '../platform.js';
10
+ import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
11
+ import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
12
+
13
+ export class RobotVacuumCleaner extends deviceBase {
14
14
  // Services
15
- batteryService: Service;
16
- robotVacuumCleanerService: Service;
17
-
18
- // Characteristic Values
19
- On!: CharacteristicValue;
20
- Brightness!: CharacteristicValue;
21
- BatteryLevel!: CharacteristicValue;
22
- StatusLowBattery!: CharacteristicValue;
23
- FirmwareRevision!: CharacteristicValue;
24
-
25
- // OpenAPI Status
26
- OpenAPI_On: deviceStatus['power'];
27
- OpenAPI_BatteryLevel: deviceStatus['battery'];
28
- OpenAPI_FirmwareRevision: deviceStatus['version'];
29
-
30
- // BLE Status
31
- BLE_On: serviceData['state'];
32
-
33
- // BLE Others
34
- BLE_IsConnected?: boolean;
35
-
36
- // Config
37
- scanDuration!: number;
38
- deviceLogging!: string;
39
- deviceRefreshRate!: number;
15
+ private LightBulb: {
16
+ Name: CharacteristicValue;
17
+ Service: Service;
18
+ On: CharacteristicValue;
19
+ Brightness: CharacteristicValue;
20
+ };
21
+
22
+ private Battery: {
23
+ Name: CharacteristicValue;
24
+ Service: Service;
25
+ BatteryLevel: CharacteristicValue;
26
+ StatusLowBattery: CharacteristicValue;
27
+ ChargingState: CharacteristicValue;
28
+ };
40
29
 
41
30
  // Updates
42
31
  robotVacuumCleanerUpdateInProgress!: boolean;
43
32
  doRobotVacuumCleanerUpdate!: Subject<void>;
44
33
 
45
- // Connection
46
- private readonly OpenAPI: boolean;
47
- private readonly BLE: boolean;
48
-
49
34
  constructor(
50
- private readonly platform: SwitchBotPlatform,
51
- private accessory: PlatformAccessory,
52
- public device: device & devicesConfig,
35
+ readonly platform: SwitchBotPlatform,
36
+ accessory: PlatformAccessory,
37
+ device: device & devicesConfig,
53
38
  ) {
54
- this.api = this.platform.api;
55
- this.log = this.platform.log;
56
- this.config = this.platform.config;
57
- this.hap = this.api.hap;
58
- // Connection
59
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
60
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
61
- // default placeholders
62
- this.deviceLogs(device);
63
- this.scan(device);
64
- this.refreshRate(device);
65
- this.deviceContext();
66
- this.deviceConfig(device);
67
-
39
+ super(platform, accessory, device);
68
40
  // this is subject we use to track when we need to POST changes to the SwitchBot API
69
41
  this.doRobotVacuumCleanerUpdate = new Subject();
70
42
  this.robotVacuumCleanerUpdateInProgress = false;
71
43
 
72
- // Retrieve initial values and updateHomekit
73
- this.refreshStatus();
74
-
75
- // set accessory information
76
- accessory
77
- .getService(this.hap.Service.AccessoryInformation)!
78
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
79
- .setCharacteristic(this.hap.Characteristic.Model, this.model(device))
80
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
81
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
82
-
83
- // get the Lightbulb service if it exists, otherwise create a new Lightbulb service
84
- // you can create multiple services for each accessory
85
- const robotVacuumCleanerService = `${accessory.displayName} ${device.deviceType}`;
86
- (this.robotVacuumCleanerService = accessory.getService(this.hap.Service.Lightbulb)
87
- || accessory.addService(this.hap.Service.Lightbulb)), robotVacuumCleanerService;
88
-
89
- this.robotVacuumCleanerService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
90
- if (!this.robotVacuumCleanerService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
91
- this.robotVacuumCleanerService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
92
- }
93
- // each service must implement at-minimum the "required characteristics" for the given service type
94
- // see https://developers.homebridge.io/#/service/Lightbulb
95
-
96
- // create handlers for required characteristics
97
- this.robotVacuumCleanerService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this));
44
+ // Initialize Lightbulb Service
45
+ accessory.context.LightBulb = accessory.context.LightBulb ?? {};
46
+ this.LightBulb = {
47
+ Name: accessory.context.LightBulb.Name ?? accessory.displayName,
48
+ Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service,
49
+ On: accessory.context.On ?? false,
50
+ Brightness: accessory.context.Brightness ?? 0,
51
+ };
52
+ accessory.context.LightBulb = this.LightBulb as object;
53
+
54
+ // Initialize LightBulb Characteristics
55
+ this.LightBulb.Service
56
+ .setCharacteristic(this.hap.Characteristic.Name, accessory.displayName)
57
+ .getCharacteristic(this.hap.Characteristic.On)
58
+ .onGet(() => {
59
+ return this.LightBulb.On;
60
+ })
61
+ .onSet(this.OnSet.bind(this));
98
62
 
99
- // handle Brightness events using the Brightness characteristic
100
- this.robotVacuumCleanerService
63
+ // Initialize LightBulb Brightness Characteristic
64
+ this.LightBulb.Service
101
65
  .getCharacteristic(this.hap.Characteristic.Brightness)
102
66
  .setProps({
103
67
  minStep: 25,
@@ -107,20 +71,43 @@ export class RobotVacuumCleaner {
107
71
  validValueRanges: [0, 100],
108
72
  })
109
73
  .onGet(() => {
110
- return this.Brightness;
74
+ return this.LightBulb.Brightness;
111
75
  })
112
76
  .onSet(this.BrightnessSet.bind(this));
113
77
 
114
- // Battery Service
115
- const batteryService = `${accessory.displayName} Battery`;
116
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
117
- || accessory.addService(this.hap.Service.Battery)), batteryService;
78
+ // Initialize Battery Service
79
+ accessory.context.Battery = accessory.context.Battery ?? {};
80
+ this.Battery = {
81
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
82
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
83
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
84
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
85
+ ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING,
86
+ };
87
+ accessory.context.Battery = this.Battery as object;
88
+
89
+ // Initialize Battery Characteristics
90
+ this.Battery.Service
91
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
92
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
93
+ .onGet(() => {
94
+ return this.Battery.BatteryLevel;
95
+ });
118
96
 
119
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
120
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
121
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
122
- }
97
+ this.Battery.Service
98
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
99
+ .onGet(() => {
100
+ return this.Battery.StatusLowBattery;
101
+ });
102
+
103
+ this.Battery.Service
104
+ .getCharacteristic(this.hap.Characteristic.ChargingState)
105
+ .onGet(() => {
106
+ return this.Battery.ChargingState;
107
+ });
123
108
 
109
+ // Retrieve initial values and updateHomekit
110
+ this.refreshStatus();
124
111
  // Update Homekit
125
112
  this.updateHomeKitCharacteristics();
126
113
 
@@ -132,26 +119,7 @@ export class RobotVacuumCleaner {
132
119
  });
133
120
 
134
121
  //regisiter webhook event handler
135
- if (this.device.webhook) {
136
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
137
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
138
- try {
139
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
140
- const { onlineStatus, battery } = context;
141
- const { On, BatteryLevel } = this;
142
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
143
- '(onlineStatus, battery) = ' +
144
- `Webhook:(${onlineStatus}, ${battery}), ` +
145
- `current:(${On}, ${BatteryLevel})`);
146
- this.On = onlineStatus === 'online' ? true : false;
147
- this.BatteryLevel = battery;
148
- this.updateHomeKitCharacteristics();
149
- } catch (e: any) {
150
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
151
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
152
- }
153
- };
154
- }
122
+ this.registerWebhook(accessory, device);
155
123
 
156
124
  // Watch for Plug change events
157
125
  // We put in a debounce of 100ms so we don't make duplicate calls
@@ -160,81 +128,86 @@ export class RobotVacuumCleaner {
160
128
  tap(() => {
161
129
  this.robotVacuumCleanerUpdateInProgress = true;
162
130
  }),
163
- debounceTime(this.platform.config.options!.pushRate! * 1000),
131
+ debounceTime(this.devicePushRate * 1000),
164
132
  )
165
133
  .subscribe(async () => {
166
134
  try {
167
- if (this.On !== this.accessory.context.On) {
168
- await this.pushChanges();
169
- }
170
- if (this.On && this.Brightness !== this.accessory.context.Brightness) {
171
- await this.openAPIpushBrightnessChanges();
172
- }
135
+ await this.pushChanges();
173
136
  } catch (e: any) {
174
137
  this.apiError(e);
175
- this.errorLog(
176
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
177
- ` Error Message: ${JSON.stringify(e.message)}`,
178
- );
138
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,`
139
+ + ` Error Message: ${JSON.stringify(e.message)}`);
179
140
  }
180
141
  this.robotVacuumCleanerUpdateInProgress = false;
181
142
  });
182
143
  }
183
144
 
184
- /**
185
- * Parse the device status from the SwitchBot api
186
- */
187
- async parseStatus(): Promise<void> {
188
- if (!this.device.enableCloudService && this.OpenAPI) {
189
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
190
- /*} else if (this.BLE) {
191
- await this.BLEparseStatus();*/
192
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
193
- await this.openAPIparseStatus();
145
+ async BLEparseStatus(serviceData: serviceData): Promise<void> {
146
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
147
+
148
+ // Battery
149
+ this.Battery.BatteryLevel = Number(serviceData.battery);
150
+ if (this.Battery.BatteryLevel < 10) {
151
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
194
152
  } else {
195
- await this.offlineOff();
196
- this.debugWarnLog(
197
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
198
- );
153
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
199
154
  }
200
- }
155
+ this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`);
201
156
 
202
- async BLEparseStatus(): Promise<void> {
203
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
204
157
  // State
205
- switch (this.BLE_On) {
158
+ switch (serviceData.state) {
206
159
  case 'on':
207
- this.On = true;
160
+ this.LightBulb.On = true;
208
161
  break;
209
162
  default:
210
- this.On = false;
163
+ this.LightBulb.On = false;
211
164
  }
212
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
165
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
213
166
  }
214
167
 
215
- async openAPIparseStatus() {
168
+ async openAPIparseStatus(deviceStatus: deviceStatus) {
216
169
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
217
- switch (this.OpenAPI_On) {
170
+ switch (deviceStatus.body.power) {
218
171
  case 'on':
219
- this.On = true;
172
+ this.LightBulb.On = true;
220
173
  break;
221
174
  default:
222
- this.On = false;
175
+ this.LightBulb.On = false;
223
176
  }
224
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
177
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
225
178
 
226
- // Battery
227
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
228
- if (this.BatteryLevel < 15) {
229
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
179
+ // BatteryLevel
180
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
181
+
182
+ // StatusLowBattery
183
+ if (this.Battery.BatteryLevel < 10) {
184
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
230
185
  } else {
231
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
186
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
187
+ }
188
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
189
+ this.Battery.BatteryLevel = 100;
232
190
  }
233
- this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
191
+ // ChargingState
192
+ this.Battery.ChargingState = deviceStatus.body.workingStatus === 'Charging'
193
+ ? this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
194
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
195
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}, ChargingState: ${this.Battery.ChargingState}`);
234
196
 
235
- // FirmwareRevision
236
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
237
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
197
+ // Firmware Version
198
+ const version = deviceStatus.body.version?.toString();
199
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
200
+ if (deviceStatus.body.version) {
201
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
202
+ this.accessory
203
+ .getService(this.hap.Service.AccessoryInformation)!
204
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
205
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
206
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
207
+ .updateValue(deviceVersion);
208
+ this.accessory.context.deviceVersion = deviceVersion;
209
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
210
+ }
238
211
  }
239
212
 
240
213
  /**
@@ -243,132 +216,82 @@ export class RobotVacuumCleaner {
243
216
  async refreshStatus(): Promise<void> {
244
217
  if (!this.device.enableCloudService && this.OpenAPI) {
245
218
  this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`);
246
- /*} else if (this.BLE) {
247
- await this.BLERefreshStatus();*/
219
+ } else if (this.BLE) {
220
+ await this.BLERefreshStatus();
248
221
  } else if (this.OpenAPI && this.platform.config.credentials?.token) {
249
222
  await this.openAPIRefreshStatus();
250
223
  } else {
251
224
  await this.offlineOff();
252
- this.debugWarnLog(
253
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
254
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
255
- );
225
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
226
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
256
227
  }
257
228
  }
258
229
 
259
230
  async BLERefreshStatus(): Promise<void> {
260
231
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
261
- const switchbot = await this.platform.connectBLE();
262
- // Convert to BLE Address
263
- this.device.bleMac = this.device
264
- .deviceId!.match(/.{1,2}/g)!
265
- .join(':')
266
- .toLowerCase();
267
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
268
- this.getCustomBLEAddress(switchbot);
232
+ const switchbot = await this.switchbotBLE();
233
+
234
+ if (switchbot === undefined) {
235
+ await this.BLERefreshConnection(switchbot);
236
+ } else {
269
237
  // Start to monitor advertisement packets
270
- (async () => {
271
- await switchbot.startScan({
272
- model: '?',
273
- id: this.device.bleMac,
274
- });
275
- // Set an event handler
276
- switchbot.onadvertisement = (ad: any) => {
277
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
278
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
279
- if (this.device.bleMac === ad.address && ad.serviceData.model === '?') {
280
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
281
- this.BLE_On = ad.serviceData.state;
238
+ (async () => {
239
+ // Start to monitor advertisement packets
240
+ const serviceData: serviceData = await this.monitorAdvertisementPackets(switchbot);
241
+ // Update HomeKit
242
+ if (serviceData.model !== '' && serviceData.modelName !== '') {
243
+ await this.BLEparseStatus(serviceData);
244
+ await this.updateHomeKitCharacteristics();
282
245
  } else {
283
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
284
- }
285
- };
286
- // Wait 1 seconds
287
- await switchbot.wait(this.scanDuration * 1000);
288
- // Stop to monitor
289
- await switchbot.stopScan();
290
- // Update HomeKit
291
- await this.BLEparseStatus();
292
- await this.updateHomeKitCharacteristics();
293
- })();
294
- /*if (switchbot !== false) {
295
- switchbot
296
- .startScan({
297
- model: this.BLEmodel(),
298
- id: this.device.bleMac,
299
- })
300
- .then(async () => {
301
- // Set an event handler
302
- switchbot.onadvertisement = async (ad: ad) => {
303
- this.debugLog(
304
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
305
- ` BLE Address Found: ${ad.address}`,
306
- );
307
- this.BLE_On = ad.serviceData.state;
308
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
309
- this.debugLog(
310
- `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` +
311
- `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` +
312
- `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`,
313
- );
314
-
315
- if (ad.serviceData) {
316
- this.BLE_IsConnected = true;
317
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
318
- await this.stopScanning(switchbot);
319
- } else {
320
- this.BLE_IsConnected = false;
321
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
322
- }
323
- };
324
- // Wait
325
- return await sleep(this.scanDuration * 1000);
326
- })
327
- .then(async () => {
328
- // Stop to monitor
329
- await this.stopScanning(switchbot);
330
- })
331
- .catch(async (e: any) => {
332
- this.apiError(e);
333
- this.errorLog(
334
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
335
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
336
- );
246
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed to get serviceData, serviceData: ${serviceData}`);
337
247
  await this.BLERefreshConnection(switchbot);
338
- });
339
- } else {
340
- await this.BLERefreshConnection(switchbot);
341
- }*/
248
+ }
249
+ })();
250
+ }
342
251
  }
343
252
 
344
- async openAPIRefreshStatus() {
253
+ async openAPIRefreshStatus(): Promise<void> {
345
254
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
346
255
  try {
347
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
348
- headers: this.platform.generateHeaders(),
349
- });
350
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
256
+ const { body, statusCode } = await this.deviceRefreshStatus();
351
257
  const deviceStatus: any = await body.json();
352
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
353
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
258
+ await this.refreshStatusCodes(statusCode, deviceStatus);;
354
259
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
355
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
356
- + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
357
- this.OpenAPI_On = deviceStatus.body.power;
358
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
359
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
360
- this.openAPIparseStatus();
361
- this.updateHomeKitCharacteristics();
260
+ await this.successfulRefreshStatus(statusCode, deviceStatus);
261
+ await this.openAPIparseStatus(deviceStatus);
262
+ await this.updateHomeKitCharacteristics();
362
263
  } else {
363
- this.statusCode(statusCode);
364
- this.statusCode(deviceStatus.statusCode);
264
+ await this.statusCodes(statusCode, deviceStatus);
365
265
  }
366
266
  } catch (e: any) {
367
267
  this.apiError(e);
368
- this.errorLog(
369
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
370
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
371
- );
268
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
269
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
270
+ }
271
+ }
272
+
273
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
274
+ if (device.webhook) {
275
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
276
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
277
+ try {
278
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
279
+ const { onlineStatus, battery, workingStatus } = context;
280
+ const { On } = this.LightBulb;
281
+ const { BatteryLevel, ChargingState } = this.Battery;
282
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (onlineStatus, battery, workingStatus) = `
283
+ + `Webhook: (${onlineStatus}, ${battery}, ${workingStatus}), current: (${On}, ${BatteryLevel}, ${ChargingState})`);
284
+ this.LightBulb.On = onlineStatus === 'online' ? true : false;
285
+ this.Battery.ChargingState = workingStatus === 'Charging'
286
+ ? this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
287
+ this.Battery.BatteryLevel = battery;
288
+ this.updateHomeKitCharacteristics();
289
+ } catch (e: any) {
290
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
291
+ }
292
+ };
293
+ } else {
294
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
372
295
  }
373
296
  }
374
297
 
@@ -384,15 +307,14 @@ export class RobotVacuumCleaner {
384
307
  async pushChanges(): Promise<void> {
385
308
  if (!this.device.enableCloudService && this.OpenAPI) {
386
309
  this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} pushChanges enableCloudService: ${this.device.enableCloudService}`);
387
- /*} else if (this.BLE) {
388
- await this.BLEpushChanges();*/
310
+ } else if (this.BLE) {
311
+ await this.BLEpushChanges();
389
312
  } else if (this.OpenAPI && this.platform.config.credentials?.token) {
390
313
  await this.openAPIpushChanges();
391
314
  } else {
392
315
  await this.offlineOff();
393
- this.debugWarnLog(
394
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
395
- );
316
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
317
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
396
318
  }
397
319
  // Refresh the status from the API
398
320
  interval(15000)
@@ -405,26 +327,23 @@ export class RobotVacuumCleaner {
405
327
 
406
328
  async BLEpushChanges(): Promise<void> {
407
329
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
408
- if (this.On !== this.accessory.context.On) {
409
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`);
330
+ if (this.LightBulb.On !== this.accessory.context.On) {
331
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`
332
+ + ` On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`);
410
333
  const switchbot = await this.platform.connectBLE();
411
334
  // Convert to BLE Address
412
- this.device.bleMac = this.device
413
- .deviceId!.match(/.{1,2}/g)!
414
- .join(':')
415
- .toLowerCase();
416
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
335
+ await this.convertBLEAddress();
417
336
  switchbot
418
337
  .discover({
419
338
  model: '?',
420
339
  id: this.device.bleMac,
421
340
  })
422
341
  .then(async (device_list: any) => {
423
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
424
- return await this.retry({
425
- max: this.maxRetry(),
342
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
343
+ return await this.retryBLE({
344
+ max: await this.maxRetryBLE(),
426
345
  fn: async () => {
427
- if (this.On) {
346
+ if (this.LightBulb.On) {
428
347
  return await device_list[0].turnOn({ id: this.device.bleMac });
429
348
  } else {
430
349
  return await device_list[0].turnOff({ id: this.device.bleMac });
@@ -434,105 +353,77 @@ export class RobotVacuumCleaner {
434
353
  })
435
354
  .then(() => {
436
355
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
437
- this.On = false;
356
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
357
+ + `On: ${this.LightBulb.On} sent over BLE, sent successfully`);
358
+ this.LightBulb.On = false;
438
359
  })
439
360
  .catch(async (e: any) => {
440
361
  this.apiError(e);
441
- this.errorLog(
442
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
443
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
444
- );
362
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
363
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
445
364
  await this.BLEPushConnection();
446
365
  });
447
366
  } else {
448
- this.debugLog(
449
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`,
450
- );
367
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,`
368
+ + ` On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`);
451
369
  }
452
370
  }
453
371
 
454
372
  async openAPIpushChanges() {
455
373
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
456
- if (this.On !== this.accessory.context.On) {
457
- let command = '';
458
- if (this.On) {
459
- command = 'turnOn';
460
- } else {
461
- command = 'turnOff';
462
- }
463
- const bodyChange = JSON.stringify({
464
- command: `${command}`,
465
- parameter: 'default',
466
- commandType: 'command',
467
- });
374
+ if (this.LightBulb.On !== this.accessory.context.On) {
375
+ const bodyChange = await this.commands();
468
376
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
469
377
  try {
470
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
471
- body: bodyChange,
472
- method: 'POST',
473
- headers: this.platform.generateHeaders(),
474
- });
475
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
378
+ const { body, statusCode } = await this.pushChangeRequest(bodyChange);
476
379
  const deviceStatus: any = await body.json();
477
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
478
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
479
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
380
+ await this.pushStatusCodes(statusCode, deviceStatus);
480
381
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
481
382
  this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
482
383
  + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
384
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
385
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
483
386
  } else {
484
387
  this.statusCode(statusCode);
485
388
  this.statusCode(deviceStatus.statusCode);
486
389
  }
487
390
  } catch (e: any) {
488
391
  this.apiError(e);
489
- this.errorLog(
490
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
491
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
492
- );
392
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
393
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
493
394
  }
494
395
  } else {
495
- this.debugLog(
496
- `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` +
497
- `On: ${this.On}, ` +
498
- `OnCached: ${this.accessory.context.On}`,
499
- );
396
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges, On: ${this.LightBulb.On}, `
397
+ + `OnCached: ${this.accessory.context.On}`);
500
398
  }
501
399
  }
502
400
 
503
401
  async openAPIpushBrightnessChanges() {
504
402
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushBrightnessChanges`);
505
- const body = await this.brightnessCommands();
506
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${body},`);
403
+ const bodyChange = await this.brightnessCommands();
404
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
507
405
  try {
508
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
509
- method: 'POST',
510
- headers: this.platform.generateHeaders(),
511
- });
512
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
406
+ const { body, statusCode } = await this.pushChangeRequest(bodyChange);
513
407
  const deviceStatus: any = await body.json();
514
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
515
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
408
+ await this.refreshStatusCodes(statusCode, deviceStatus);;
516
409
  if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
517
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
518
- + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
410
+ await this.successfulPushChange(statusCode, deviceStatus, bodyChange);
411
+ await this.updateHomeKitCharacteristics();
519
412
  } else {
520
- this.statusCode(statusCode);
521
- this.statusCode(deviceStatus.statusCode);
413
+ await this.statusCode(statusCode);
414
+ await this.statusCode(deviceStatus.statusCode);
522
415
  }
523
416
  } catch (e: any) {
524
417
  this.apiError(e);
525
- this.errorLog(
526
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
527
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
528
- );
418
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
419
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
529
420
  }
530
421
  }
531
422
 
532
- private commands() {
423
+ async commands() {
533
424
  let command: string;
534
425
  let parameter: string;
535
- if (this.On) {
426
+ if (this.LightBulb.On) {
536
427
  command = 'start';
537
428
  parameter = 'default';
538
429
  } else {
@@ -547,44 +438,44 @@ export class RobotVacuumCleaner {
547
438
  return body;
548
439
  }
549
440
 
550
- private brightnessCommands() {
441
+ async brightnessCommands(): Promise<string> {
551
442
  let command: string;
552
443
  let parameter: string;
553
- if (this.Brightness === 25) {
444
+ if (this.LightBulb.Brightness === 25) {
554
445
  command = 'PowLevel';
555
446
  parameter = '0';
556
- } else if (this.Brightness === 50) {
447
+ } else if (this.LightBulb.Brightness === 50) {
557
448
  command = 'PowLevel';
558
449
  parameter = '1';
559
- } else if (this.Brightness === 75) {
450
+ } else if (this.LightBulb.Brightness === 75) {
560
451
  command = 'PowLevel';
561
452
  parameter = '2';
562
- } else if (this.Brightness === 100) {
453
+ } else if (this.LightBulb.Brightness === 100) {
563
454
  command = 'PowLevel';
564
455
  parameter = '3';
565
456
  } else {
566
457
  command = 'dock';
567
458
  parameter = 'default';
568
459
  }
569
- const body = JSON.stringify({
460
+ const bodyChange = JSON.stringify({
570
461
  command: `${command}`,
571
462
  parameter: `${parameter}`,
572
463
  commandType: 'command',
573
464
  });
574
- return body;
465
+ return bodyChange;
575
466
  }
576
467
 
577
468
  /**
578
469
  * Handle requests to set the value of the "On" characteristic
579
470
  */
580
471
  async OnSet(value: CharacteristicValue): Promise<void> {
581
- if (this.On === this.accessory.context.On) {
472
+ if (this.LightBulb.On === this.accessory.context.On) {
582
473
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`);
583
474
  } else {
584
475
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`);
585
476
  }
586
477
 
587
- this.On = value;
478
+ this.LightBulb.On = value;
588
479
  this.doRobotVacuumCleanerUpdate.next();
589
480
  }
590
481
 
@@ -592,78 +483,60 @@ export class RobotVacuumCleaner {
592
483
  * Handle requests to set the value of the "Brightness" characteristic
593
484
  */
594
485
  async BrightnessSet(value: CharacteristicValue): Promise<void> {
595
- if (this.Brightness === this.accessory.context.Brightness) {
486
+ if (this.LightBulb.Brightness === this.accessory.context.Brightness) {
596
487
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`);
597
- } else if (this.On) {
488
+ } else if (this.LightBulb.On) {
598
489
  this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
599
490
  } else {
600
491
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
601
492
  }
602
493
 
603
- this.Brightness = value;
494
+ this.LightBulb.Brightness = value;
604
495
  this.doRobotVacuumCleanerUpdate.next();
605
496
  }
606
497
 
607
498
  async updateHomeKitCharacteristics(): Promise<void> {
608
499
  // On
609
- if (this.On === undefined) {
610
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
500
+ if (this.LightBulb.On === undefined) {
501
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
611
502
  } else {
612
- this.accessory.context.On = this.On;
613
- this.robotVacuumCleanerService.updateCharacteristic(this.hap.Characteristic.On, this.On);
614
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
503
+ this.accessory.context.On = this.LightBulb.On;
504
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On);
505
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`);
615
506
  }
616
507
  // Brightness
617
- if (this.Brightness === undefined) {
618
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`);
508
+ if (this.LightBulb.Brightness === undefined) {
509
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
619
510
  } else {
620
- this.accessory.context.Brightness = this.Brightness;
621
- this.robotVacuumCleanerService.updateCharacteristic(this.hap.Characteristic.Brightness, this.Brightness);
622
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`);
511
+ this.accessory.context.Brightness = this.LightBulb.Brightness;
512
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, this.LightBulb.Brightness);
513
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.LightBulb.Brightness}`);
623
514
  }
624
515
  // BatteryLevel
625
- if (this.BatteryLevel === undefined) {
626
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
516
+ if (this.Battery.BatteryLevel === undefined) {
517
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
627
518
  } else {
628
- this.accessory.context.BatteryLevel = this.BatteryLevel;
629
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
630
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
519
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
520
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
521
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
631
522
  }
632
523
  // StatusLowBattery
633
- if (this.StatusLowBattery === undefined) {
634
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
524
+ if (this.Battery.StatusLowBattery === undefined) {
525
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
635
526
  } else {
636
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
637
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
638
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
639
- }
640
- }
641
-
642
- async stopScanning(switchbot: any) {
643
- switchbot.stopScan();
644
- if (this.BLE_IsConnected) {
645
- await this.BLEparseStatus();
646
- await this.updateHomeKitCharacteristics();
527
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
528
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
529
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
530
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
531
+ }
532
+ // ChargingState
533
+ if (this.Battery.ChargingState === undefined) {
534
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`);
647
535
  } else {
648
- await this.BLERefreshConnection(switchbot);
649
- }
650
- }
651
-
652
- async getCustomBLEAddress(switchbot: any) {
653
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
654
- (async () => {
655
- // Start to monitor advertisement packets
656
- await switchbot.startScan({
657
- model: '?',
658
- });
659
- // Set an event handler
660
- switchbot.onadvertisement = (ad: any) => {
661
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
662
- };
663
- await sleep(10000);
664
- // Stop to monitor
665
- switchbot.stopScan();
666
- })();
536
+ this.accessory.context.ChargingState = this.Battery.ChargingState;
537
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState);
538
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
539
+ + ` ChargingState: ${this.Battery.ChargingState}`);
667
540
  }
668
541
  }
669
542
 
@@ -683,266 +556,13 @@ export class RobotVacuumCleaner {
683
556
  }
684
557
  }
685
558
 
686
- async retry({ max, fn }: { max: number; fn: { (): any; (): Promise<any> } }): Promise<null> {
687
- return fn().catch(async (e: any) => {
688
- if (max === 0) {
689
- throw e;
690
- }
691
- this.infoLog(e);
692
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
693
- await sleep(1000);
694
- return this.retry({ max: max - 1, fn });
695
- });
696
- }
697
-
698
- maxRetry(): number {
699
- if (this.device.maxRetry) {
700
- return this.device.maxRetry;
701
- } else {
702
- return 5;
703
- }
704
- }
705
-
706
- model(device: device & devicesConfig): string {
707
- let model: string;
708
- if (device.deviceType === 'Robot Vacuum Cleaner S1 Plus') {
709
- model = 'W3011010';
710
- } else if (device.deviceType === 'Robot Vacuum Cleaner S1') {
711
- model = 'W3011000';
712
- } else {
713
- model = 'N/A';
714
- }
715
- return model;
716
- }
717
-
718
- async scan(device: device & devicesConfig): Promise<void> {
719
- if (device.scanDuration) {
720
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
721
- if (this.BLE) {
722
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
723
- }
724
- } else {
725
- this.scanDuration = this.accessory.context.scanDuration = 1;
726
- if (this.BLE) {
727
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
728
- }
729
- }
730
- }
731
-
732
- async statusCode(statusCode: number): Promise<void> {
733
- switch (statusCode) {
734
- case 151:
735
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
736
- break;
737
- case 152:
738
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
739
- break;
740
- case 160:
741
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
742
- break;
743
- case 161:
744
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
745
- this.offlineOff();
746
- break;
747
- case 171:
748
- this.errorLog(
749
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
750
- `Hub: ${this.device.hubDeviceId}`,
751
- );
752
- this.offlineOff();
753
- break;
754
- case 190:
755
- this.errorLog(
756
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
757
- ` Or command format is invalid, statusCode: ${statusCode}`,
758
- );
759
- break;
760
- case 100:
761
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
762
- break;
763
- case 200:
764
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
765
- break;
766
- case 400:
767
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
768
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
769
- break;
770
- case 401:
771
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
772
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
773
- break;
774
- case 403:
775
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
776
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
777
- break;
778
- case 404:
779
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
780
- + `statusCode: ${statusCode}`);
781
- break;
782
- case 406:
783
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
784
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
785
- break;
786
- case 415:
787
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
788
- + `header that is not supported by the server, statusCode: ${statusCode}`);
789
- break;
790
- case 422:
791
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
792
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
793
- break;
794
- case 429:
795
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
796
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
797
- break;
798
- case 500:
799
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
800
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
801
- break;
802
- default:
803
- this.infoLog(
804
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
805
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
806
- );
807
- }
808
- }
809
-
810
559
  async offlineOff(): Promise<void> {
811
560
  if (this.device.offline) {
812
- await this.deviceContext();
813
- await this.updateHomeKitCharacteristics();
561
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false);
814
562
  }
815
563
  }
816
564
 
817
565
  async apiError(e: any): Promise<void> {
818
- this.robotVacuumCleanerService.updateCharacteristic(this.hap.Characteristic.On, e);
819
- }
820
-
821
- async deviceContext() {
822
- if (this.On === undefined) {
823
- this.On = false;
824
- } else {
825
- this.On = this.accessory.context.On;
826
- }
827
- if (this.Brightness === undefined) {
828
- this.Brightness = 0;
829
- } else {
830
- this.Brightness = this.accessory.context.Brightness;
831
- }
832
- if (this.FirmwareRevision === undefined) {
833
- this.FirmwareRevision = this.platform.version;
834
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
835
- }
836
- }
837
-
838
- async refreshRate(device: device & devicesConfig): Promise<void> {
839
- if (device.refreshRate) {
840
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
841
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
842
- } else if (this.platform.config.options!.refreshRate) {
843
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
844
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
845
- }
846
- }
847
-
848
- async deviceConfig(device: device & devicesConfig): Promise<void> {
849
- let config = {};
850
- if (device.plug) {
851
- config = device.plug;
852
- }
853
- if (device.connectionType !== undefined) {
854
- config['connectionType'] = device.connectionType;
855
- }
856
- if (device.external !== undefined) {
857
- config['external'] = device.external;
858
- }
859
- if (device.logging !== undefined) {
860
- config['logging'] = device.logging;
861
- }
862
- if (device.refreshRate !== undefined) {
863
- config['refreshRate'] = device.refreshRate;
864
- }
865
- if (device.scanDuration !== undefined) {
866
- config['scanDuration'] = device.scanDuration;
867
- }
868
- if (device.offline !== undefined) {
869
- config['offline'] = device.offline;
870
- }
871
- if (device.maxRetry !== undefined) {
872
- config['maxRetry'] = device.maxRetry;
873
- }
874
- if (device.webhook !== undefined) {
875
- config['webhook'] = device.webhook;
876
- }
877
- if (Object.entries(config).length !== 0) {
878
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
879
- }
880
- }
881
-
882
- async deviceLogs(device: device & devicesConfig): Promise<void> {
883
- if (this.platform.debugMode) {
884
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
885
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
886
- } else if (device.logging) {
887
- this.deviceLogging = this.accessory.context.logging = device.logging;
888
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
889
- } else if (this.platform.config.options?.logging) {
890
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
891
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
892
- } else {
893
- this.deviceLogging = this.accessory.context.logging = 'standard';
894
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
895
- }
896
- }
897
-
898
- /**
899
- * Logging for Device
900
- */
901
- infoLog(...log: any[]): void {
902
- if (this.enablingDeviceLogging()) {
903
- this.platform.log.info(String(...log));
904
- }
905
- }
906
-
907
- warnLog(...log: any[]): void {
908
- if (this.enablingDeviceLogging()) {
909
- this.platform.log.warn(String(...log));
910
- }
911
- }
912
-
913
- debugWarnLog(...log: any[]): void {
914
- if (this.enablingDeviceLogging()) {
915
- if (this.deviceLogging?.includes('debug')) {
916
- this.platform.log.warn('[DEBUG]', String(...log));
917
- }
918
- }
919
- }
920
-
921
- errorLog(...log: any[]): void {
922
- if (this.enablingDeviceLogging()) {
923
- this.platform.log.error(String(...log));
924
- }
925
- }
926
-
927
- debugErrorLog(...log: any[]): void {
928
- if (this.enablingDeviceLogging()) {
929
- if (this.deviceLogging?.includes('debug')) {
930
- this.platform.log.error('[DEBUG]', String(...log));
931
- }
932
- }
933
- }
934
-
935
- debugLog(...log: any[]): void {
936
- if (this.enablingDeviceLogging()) {
937
- if (this.deviceLogging === 'debug') {
938
- this.platform.log.info('[DEBUG]', String(...log));
939
- } else {
940
- this.platform.log.debug(String(...log));
941
- }
942
- }
943
- }
944
-
945
- enablingDeviceLogging(): boolean {
946
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
566
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, e);
947
567
  }
948
568
  }