@switchbot/homebridge-switchbot 0.2.0-alpha.1 → 1.0.1-beta.1

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 (99) hide show
  1. package/.github/workflows/nodejs-beta.yml +1 -1
  2. package/.github/workflows/nodejs.yml +1 -1
  3. package/.github/workflows/{prerelease.js → prerelease-beta.js} +0 -0
  4. package/CHANGELOG.md +1 -1
  5. package/README.md +9 -5
  6. package/config.schema.json +350 -126
  7. package/dist/devices/bots.d.ts +17 -33
  8. package/dist/devices/bots.d.ts.map +1 -1
  9. package/dist/devices/bots.js +103 -152
  10. package/dist/devices/bots.js.map +1 -1
  11. package/dist/devices/{bulb.d.ts → colorbulb.d.ts} +12 -19
  12. package/dist/devices/colorbulb.d.ts.map +1 -0
  13. package/dist/devices/{bulb.js → colorbulb.js} +32 -32
  14. package/dist/devices/colorbulb.js.map +1 -0
  15. package/dist/devices/contact.d.ts +18 -16
  16. package/dist/devices/contact.d.ts.map +1 -1
  17. package/dist/devices/contact.js +101 -44
  18. package/dist/devices/contact.js.map +1 -1
  19. package/dist/devices/curtains.d.ts +17 -51
  20. package/dist/devices/curtains.d.ts.map +1 -1
  21. package/dist/devices/curtains.js +168 -248
  22. package/dist/devices/curtains.js.map +1 -1
  23. package/dist/devices/humidifiers.d.ts +13 -4
  24. package/dist/devices/humidifiers.d.ts.map +1 -1
  25. package/dist/devices/humidifiers.js +78 -8
  26. package/dist/devices/humidifiers.js.map +1 -1
  27. package/dist/devices/indoorcam.d.ts +6 -13
  28. package/dist/devices/indoorcam.d.ts.map +1 -1
  29. package/dist/devices/indoorcam.js.map +1 -1
  30. package/dist/devices/meters.d.ts +12 -15
  31. package/dist/devices/meters.d.ts.map +1 -1
  32. package/dist/devices/meters.js +35 -44
  33. package/dist/devices/meters.js.map +1 -1
  34. package/dist/devices/motion.d.ts +17 -15
  35. package/dist/devices/motion.d.ts.map +1 -1
  36. package/dist/devices/motion.js +87 -40
  37. package/dist/devices/motion.js.map +1 -1
  38. package/dist/devices/plugs.d.ts +6 -13
  39. package/dist/devices/plugs.d.ts.map +1 -1
  40. package/dist/devices/plugs.js.map +1 -1
  41. package/dist/irdevices/airconditioners.d.ts +10 -10
  42. package/dist/irdevices/airconditioners.d.ts.map +1 -1
  43. package/dist/irdevices/airconditioners.js +29 -9
  44. package/dist/irdevices/airconditioners.js.map +1 -1
  45. package/dist/irdevices/airpurifiers.d.ts +11 -11
  46. package/dist/irdevices/airpurifiers.d.ts.map +1 -1
  47. package/dist/irdevices/airpurifiers.js.map +1 -1
  48. package/dist/irdevices/cameras.d.ts +3 -3
  49. package/dist/irdevices/cameras.d.ts.map +1 -1
  50. package/dist/irdevices/cameras.js.map +1 -1
  51. package/dist/irdevices/fans.d.ts +6 -6
  52. package/dist/irdevices/fans.d.ts.map +1 -1
  53. package/dist/irdevices/fans.js +10 -10
  54. package/dist/irdevices/fans.js.map +1 -1
  55. package/dist/irdevices/lights.d.ts +3 -3
  56. package/dist/irdevices/lights.d.ts.map +1 -1
  57. package/dist/irdevices/lights.js.map +1 -1
  58. package/dist/irdevices/others.d.ts +3 -3
  59. package/dist/irdevices/others.d.ts.map +1 -1
  60. package/dist/irdevices/others.js.map +1 -1
  61. package/dist/irdevices/tvs.d.ts +3 -3
  62. package/dist/irdevices/tvs.d.ts.map +1 -1
  63. package/dist/irdevices/tvs.js +7 -5
  64. package/dist/irdevices/tvs.js.map +1 -1
  65. package/dist/irdevices/vacuumcleaners.d.ts +3 -3
  66. package/dist/irdevices/vacuumcleaners.d.ts.map +1 -1
  67. package/dist/irdevices/vacuumcleaners.js.map +1 -1
  68. package/dist/irdevices/waterheaters.d.ts +3 -3
  69. package/dist/irdevices/waterheaters.d.ts.map +1 -1
  70. package/dist/irdevices/waterheaters.js.map +1 -1
  71. package/dist/platform.d.ts +3 -2
  72. package/dist/platform.d.ts.map +1 -1
  73. package/dist/platform.js +136 -46
  74. package/dist/platform.js.map +1 -1
  75. package/dist/settings.d.ts +50 -8
  76. package/dist/settings.d.ts.map +1 -1
  77. package/package.json +65 -65
  78. package/src/devices/bots.ts +126 -176
  79. package/src/devices/{bulb.ts → colorbulb.ts} +43 -51
  80. package/src/devices/contact.ts +124 -68
  81. package/src/devices/curtains.ts +189 -300
  82. package/src/devices/humidifiers.ts +93 -14
  83. package/src/devices/indoorcam.ts +10 -20
  84. package/src/devices/meters.ts +53 -69
  85. package/src/devices/motion.ts +108 -62
  86. package/src/devices/plugs.ts +8 -19
  87. package/src/irdevices/airconditioners.ts +43 -20
  88. package/src/irdevices/airpurifiers.ts +15 -11
  89. package/src/irdevices/cameras.ts +5 -3
  90. package/src/irdevices/fans.ts +22 -16
  91. package/src/irdevices/lights.ts +5 -3
  92. package/src/irdevices/others.ts +5 -3
  93. package/src/irdevices/tvs.ts +13 -7
  94. package/src/irdevices/vacuumcleaners.ts +5 -3
  95. package/src/irdevices/waterheaters.ts +5 -3
  96. package/src/platform.ts +148 -49
  97. package/src/settings.ts +79 -8
  98. package/dist/devices/bulb.d.ts.map +0 -1
  99. package/dist/devices/bulb.js.map +0 -1
@@ -1,8 +1,9 @@
1
- import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
1
+ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
- import { DeviceURL, device, devicesConfig } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse } from '../settings';
6
+ import { AxiosResponse } from 'axios';
6
7
 
7
8
  /**
8
9
  * Platform Accessory
@@ -10,31 +11,29 @@ import { DeviceURL, device, devicesConfig } from '../settings';
10
11
  * Each accessory may expose multiple services of different service types.
11
12
  */
12
13
  export class Bot {
14
+ // Services
13
15
  private service: Service;
16
+ private batteryService?: Service;
14
17
 
18
+ // Characteristic Values
15
19
  SwitchOn!: CharacteristicValue;
16
- OutletInUse!: CharacteristicValue;
17
- deviceStatus!: { statusCode: number; body: { deviceId: string; deviceType: string; hubDeviceId: string; power: string; }; message: string; };
18
- RunTimer!: NodeJS.Timeout;
19
- ScanDuration: number;
20
- TargetState;
21
- switchbot!: {
22
- discover: (
23
- arg0:
24
- {
25
- duration: any;
26
- model: string;
27
- quick: boolean;
28
- id: MacAddress;
29
- }
30
- ) => Promise<any>;
31
- wait: (
32
- arg0: number
33
- ) => any;
34
- };
20
+ BatteryLevel!: CharacteristicValue;
21
+ StatusLowBattery!: CharacteristicValue;
22
+
23
+ // OpenAPI Others
24
+ deviceStatus!: deviceStatusResponse;
35
25
 
26
+ // BLE Others
27
+ switchbot!: switchbot;
28
+ TargetState!: boolean;
29
+ serviceData!: serviceData;
30
+ Mode!: serviceData['mode'];
31
+ state!: serviceData['state'];
32
+ battery!: serviceData['battery'];
33
+
34
+ // Updates
36
35
  botUpdateInProgress!: boolean;
37
- doBotUpdate;
36
+ doBotUpdate!: Subject<void>;
38
37
 
39
38
  constructor(
40
39
  private readonly platform: SwitchBotPlatform,
@@ -43,27 +42,13 @@ export class Bot {
43
42
  ) {
44
43
  // default placeholders
45
44
  this.SwitchOn = false;
46
- this.ScanDuration = this.platform.config.options!.refreshRate!;
47
- if (device.bot?.mode !== 'switch') {
48
- this.OutletInUse = true;
49
- }
50
- if (device.ble) {
51
- this.SwitchOn = false;
52
- // eslint-disable-next-line @typescript-eslint/no-var-requires
53
- const SwitchBot = require('node-switchbot');
54
- this.switchbot = new SwitchBot();
55
- const colon = device.deviceId!.match(/.{1,2}/g);
56
- const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
57
- this.device.bleMac = bleMac.toLowerCase();
58
- this.platform.device(this.device.bleMac);
59
- }
60
45
 
61
46
  // this is subject we use to track when we need to POST changes to the SwitchBot API
62
47
  this.doBotUpdate = new Subject();
63
48
  this.botUpdateInProgress = false;
64
49
 
65
50
  // Retrieve initial values and updateHomekit
66
- this.parseStatus();
51
+ this.refreshStatus();
67
52
 
68
53
  // set accessory information
69
54
  accessory
@@ -84,6 +69,14 @@ export class Bot {
84
69
  accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Outlet`;
85
70
  }
86
71
 
72
+ if (device.ble) {
73
+ (this.batteryService =
74
+ accessory.getService(this.platform.Service.Battery) ||
75
+ accessory.addService(this.platform.Service.Battery)), `${accessory.displayName} Battery`;
76
+
77
+ this.batteryService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Battery`);
78
+ }
79
+
87
80
  // To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
88
81
  // when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
89
82
  // accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Outlet, 'NAME', 'USER_DEFINED_SUBTYPE');
@@ -142,18 +135,24 @@ export class Bot {
142
135
  }
143
136
 
144
137
  private async BLEparseStatus() {
145
- this.platform.debug('Bots BLE Device RefreshStatus');
138
+ this.platform.debug('Bots BLE Device parseStatus');
139
+ // BLEmode (true if Switch Mode) | (false if Press Mode)
140
+ if (this.Mode) {
141
+ this.platform.device(`Switch Mode, Mode: ${JSON.stringify(this.Mode)}`);
142
+ }
143
+ this.SwitchOn = Boolean(this.state);
144
+ this.BatteryLevel = Number(this.battery);
145
+ if (this.BatteryLevel < 10) {
146
+ this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
147
+ } else {
148
+ this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
149
+ }
150
+ this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}, BatteryLevel: ${this.BatteryLevel}`);
146
151
  }
147
152
 
148
153
  private async openAPIparseStatus() {
149
- if (this.device.bot?.mode !== 'switch') {
150
- this.OutletInUse = true;
151
- this.SwitchOn = false;
152
- this.platform.debug(`Bot ${this.accessory.displayName} OutletInUse: ${this.OutletInUse} On: ${this.SwitchOn}`);
153
- } else {
154
- this.SwitchOn = false;
155
- this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}`);
156
- }
154
+ this.SwitchOn = false;
155
+ this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}`);
157
156
  }
158
157
 
159
158
  /**
@@ -162,15 +161,14 @@ export class Bot {
162
161
  async refreshStatus() {
163
162
  if (this.device.ble) {
164
163
  this.platform.device('BLE');
165
- await this.BLErefreshStatus();
164
+ await this.BLERefreshStatus();
166
165
  } else {
167
166
  this.platform.device('OpenAPI');
168
167
  await this.openAPIRefreshStatus();
169
168
  }
170
169
  }
171
170
 
172
- private async BLErefreshStatus() {
173
- this.platform.device('Bot BLE Device refreshStatus');
171
+ private connectBLE() {
174
172
  // eslint-disable-next-line @typescript-eslint/no-var-requires
175
173
  const Switchbot = require('node-switchbot');
176
174
  const switchbot = new Switchbot();
@@ -178,45 +176,40 @@ export class Bot {
178
176
  const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
179
177
  this.device.bleMac = bleMac.toLowerCase();
180
178
  this.platform.device(this.device.bleMac!);
181
- switchbot.onadvertisement = (ad: any) => {
182
- this.platform.debug(JSON.stringify(ad, null, ' '));
183
- this.platform.device(`ad: ${JSON.stringify(ad)}`);
184
- };
185
- switchbot
186
- .startScan({
187
- id: this.device.bleMac,
188
- })
189
- .then(() => {
190
- return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
191
- })
192
- .then(() => {
193
- switchbot.stopScan();
194
- })
195
- .catch(async (error: any) => {
196
- this.platform.log.error(error);
197
- this.openAPIRefreshStatus();
198
- });
199
- setInterval(() => {
200
- this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
201
- switchbot
202
- .startScan({
203
- mode: 'T',
204
- id: bleMac,
205
- })
206
- .then(() => {
207
- return switchbot.wait(this.platform.config.options!.refreshRate! * 1000);
208
- })
209
- .then(() => {
210
- switchbot.stopScan();
211
- this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')');
212
- })
213
- .catch(async (error: any) => {
214
- this.platform.log.error(error);
215
- this.openAPIRefreshStatus();
216
- });
179
+ return switchbot;
180
+ }
181
+
182
+ private async BLERefreshStatus() {
183
+ this.platform.device('Bot BLE Device refreshStatus');
184
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
185
+ const switchbot = this.connectBLE();
186
+ // Start to monitor advertisement packets
187
+ switchbot.startScan({
188
+ model: 'H',
189
+ id: this.device.bleMac,
190
+ }).then(() => {
191
+ // Set an event hander
192
+ switchbot.onadvertisement = (ad: ad) => {
193
+ this.serviceData = ad.serviceData;
194
+ this.Mode = ad.serviceData.mode;
195
+ this.state = ad.serviceData.state;
196
+ this.battery = ad.serviceData.battery;
197
+ this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`);
198
+ this.platform.device(`${this.accessory.displayName}, Model: ${ad.serviceData.model}, Model Name: ${ad.serviceData.modelName},`
199
+ + ` Mode: ${ad.serviceData.mode}, State: ${ad.serviceData.state}, Battery: ${ad.serviceData.battery}`);
200
+ };
201
+ // Wait 10 seconds
202
+ return switchbot.wait(10000);
203
+ }).then(() => {
204
+ // Stop to monitor
205
+ switchbot.stopScan();
217
206
  this.parseStatus();
218
207
  this.updateHomeKitCharacteristics();
219
- }, this.platform.config.options!.refreshRate! * 60000);
208
+ }).catch(async (e: any) => {
209
+ this.platform.log.error(`BLE Connection Failed: ${e.message}`);
210
+ this.platform.log.warn('Using OpenAPI Connection');
211
+ await this.openAPIRefreshStatus();
212
+ });
220
213
  }
221
214
 
222
215
  private async openAPIRefreshStatus() {
@@ -260,53 +253,38 @@ export class Bot {
260
253
 
261
254
  private async BLEpushChanges() {
262
255
  this.platform.device('Bot BLE Device pushChanges');
263
- // Target state has been changed.
264
- this.platform.log.info('Target state of Bot setting: ' + (this.TargetState ? 'ON' : 'OFF'));
265
- this.switchbot
266
- .discover({ duration: this.ScanDuration, model: 'H', quick: true, id: (this.device.bleMac!) })
267
- .then((device_list: any) => {
268
- this.platform.log.info('Scan done.');
269
- let targetDevice: any = null;
270
- for (const device of device_list) {
271
- // log.info(device.modelName, device.address);
272
- if (device.address === this.device.bleMac) {
273
- targetDevice = device;
274
- break;
275
- }
276
- }
277
- if (!targetDevice) {
278
- this.platform.log.info('No device was found during scan.');
279
- return new Promise((resolve, reject) => {
280
- reject(new Error('No device was found during scan.'));
281
- });
282
- } else {
283
- this.platform.log.info(targetDevice.modelName + ' (' + targetDevice.address + ') was found.');
284
- // Set event handers
285
- targetDevice.onconnect = () => {
286
- // log.info('Connected.');
287
- };
288
- targetDevice.ondisconnect = () => {
289
- // log.info('Disconnected.');
290
- };
291
- this.platform.log.info('Bot is running...');
292
- return this.setTargetDeviceState(targetDevice, this.TargetState);
293
- }
294
- })
295
- .then(() => {
296
- this.platform.log.info('Done.');
297
- this.SwitchOn = this.TargetState;
298
- this.RunTimer = setTimeout(() => {
299
- this.service?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.SwitchOn);
300
- }, 500);
301
- this.platform.log.info('Bot state has been set to: ' + (this.SwitchOn ? 'ON' : 'OFF'));
302
- })
303
- .catch((error: any) => {
304
- this.platform.log.error(error);
305
- this.RunTimer = setTimeout(() => {
306
- this.service?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.SwitchOn);
307
- }, 500);
308
- this.platform.log.info('Bot state failed to be set to: ' + (this.TargetState ? 'ON' : 'OFF'));
256
+ const switchbot = this.connectBLE();
257
+ if (this.device.bot?.mode === 'press') {
258
+ this.platform.device(`Press Mode: ${this.device.bot?.mode}`);
259
+ switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list) => {
260
+ this.platform.log.info(`${this.accessory.displayName}, On: ${this.SwitchOn}`);
261
+ return device_list[0].press({ id: this.device.bleMac });
262
+ }).then(() => {
263
+ this.platform.device('Done.');
264
+ }).catch((e: any) => {
265
+ this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`);
266
+ });
267
+ } else if (this.device.bot?.mode === 'switch') {
268
+ this.platform.device(`Press Mode: ${this.device.bot?.mode}`);
269
+ switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list) => {
270
+ this.platform.log.info(`${this.accessory.displayName}, On: ${this.SwitchOn}`);
271
+ return this.turnOnOff(device_list);
272
+ }).then(() => {
273
+ this.platform.device('Done.');
274
+ }).catch((e: any) => {
275
+ this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`);
309
276
  });
277
+ } else {
278
+ this.platform.log.error('Mode Not Set.');
279
+ }
280
+ }
281
+
282
+ private turnOnOff(device_list: any) {
283
+ if (this.SwitchOn) {
284
+ return device_list[0].turnOn({ id: this.device.bleMac });
285
+ } else {
286
+ return device_list[0].turnOff({ id: this.device.bleMac });
287
+ }
310
288
  }
311
289
 
312
290
  private async openAPIpushChanges() {
@@ -359,23 +337,27 @@ export class Bot {
359
337
  this.service.updateCharacteristic(this.platform.Characteristic.On, this.SwitchOn);
360
338
  this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic On: ${this.SwitchOn}`);
361
339
  }
362
- if (this.OutletInUse === undefined || this.device.bot?.mode === 'switch') {
363
- this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.OutletInUse}, Switch: ${this.device.bot?.mode}`);
340
+ if (this.BatteryLevel === undefined) {
341
+ this.platform.debug(`Bot ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
364
342
  } else {
365
- this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse);
366
- this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic On: ${this.OutletInUse},`
367
- + ` Switch: ${this.device.bot?.mode}`);
343
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
344
+ this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
345
+ }
346
+ if (this.StatusLowBattery === undefined) {
347
+ this.platform.debug(`Bot ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
348
+ } else {
349
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
350
+ this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
368
351
  }
369
352
  }
370
353
 
371
354
  public apiError(e: any) {
372
355
  this.service.updateCharacteristic(this.platform.Characteristic.On, e);
373
- if (this.device.bot?.mode !== 'switch') {
374
- this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, e);
375
- }
356
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
357
+ this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
376
358
  }
377
359
 
378
- private statusCode(push: { data: { statusCode: any; }; }) {
360
+ private statusCode(push: AxiosResponse<{ statusCode: number;}>) {
379
361
  switch (push.data.statusCode) {
380
362
  case 151:
381
363
  this.platform.log.error('Command not supported by this device type.');
@@ -407,40 +389,8 @@ export class Bot {
407
389
  * Handle requests to set the "On" characteristic
408
390
  */
409
391
  private handleOnSet(value: CharacteristicValue) {
410
- if (this.device.ble) {
411
- this.TargetState = value as boolean;
412
- this.platform.device(`Bot BLE Device - ${this.TargetState}`);
413
- if (this.TargetState === this.SwitchOn) {
414
- this.platform.log.info('Target state of Bot has not changed: ' + (this.SwitchOn ? 'ON' : 'OFF'));
415
- this.service?.getCharacteristic(this.platform.Characteristic.On).updateValue(this.SwitchOn);
416
- }
417
- } else {
418
- this.platform.debug(`Bot ${this.accessory.displayName} - Set On: ${value}`);
419
- this.SwitchOn = value;
420
- this.doBotUpdate.next();
421
- }
392
+ this.platform.debug(`Bot ${this.accessory.displayName} - Set On: ${value}`);
393
+ this.SwitchOn === this.TargetState === value;
394
+ this.doBotUpdate.next();
422
395
  }
423
-
424
- async setTargetDeviceState(targetDevice: any, targetState: boolean): Promise<null> {
425
- return await this.retry(5, () => {
426
- if (targetState) {
427
- return targetDevice.turnOn();
428
- } else {
429
- return targetDevice.turnOff();
430
- }
431
- });
432
- }
433
-
434
- async retry(max: number, fn: { (): any; (): Promise<any>; }): Promise<null> {
435
- return fn().catch(async (err: any) => {
436
- if (max === 0) {
437
- throw err;
438
- }
439
- this.platform.log.info(err);
440
- this.platform.log.info('Retrying');
441
- await this.switchbot.wait(1000);
442
- return this.retry(max - 1, fn);
443
- });
444
- }
445
-
446
396
  }
@@ -1,15 +1,16 @@
1
- import { Service, PlatformAccessory, CharacteristicValue, MacAddress } from 'homebridge';
1
+ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
2
2
  import { SwitchBotPlatform } from '../platform';
3
3
  import { interval, Subject } from 'rxjs';
4
4
  import { debounceTime, skipWhile, tap } from 'rxjs/operators';
5
- import { DeviceURL, device, devicesConfig } from '../settings';
5
+ import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse } from '../settings';
6
+ import { AxiosResponse } from 'axios';
6
7
 
7
8
  /**
8
9
  * Platform Accessory
9
10
  * An instance of this class is created for each accessory your platform registers
10
11
  * Each accessory may expose multiple services of different service types.
11
12
  */
12
- export class Bulb {
13
+ export class ColorBulb {
13
14
  // Services
14
15
  service!: Service;
15
16
 
@@ -18,27 +19,18 @@ export class Bulb {
18
19
  Brightness!: CharacteristicValue;
19
20
  ColorTemperature!: CharacteristicValue;
20
21
 
21
- // Others
22
- deviceStatus: any;
23
- switchbot!: {
24
- discover: (
25
- arg0:
26
- {
27
- duration: any;
28
- model: string;
29
- quick: boolean;
30
- id: MacAddress;
31
- }
32
- ) => Promise<any>;
33
- wait: (
34
- arg0: number
35
- ) => any;
36
- };
22
+ // OpenAPI Others
23
+ deviceStatus!: deviceStatusResponse;
24
+
25
+ // BLE Others
26
+ switchbot!: switchbot;
27
+
28
+ // Config
29
+ set_minStep?: number;
37
30
 
38
31
  // Updates
39
- bulbUpdateInProgress!: boolean;
40
- doBulbUpdate;
41
- set_minStep: any;
32
+ colorBulbUpdateInProgress!: boolean;
33
+ doColorBulbUpdate!: Subject<void>;
42
34
 
43
35
  constructor(
44
36
  private readonly platform: SwitchBotPlatform,
@@ -59,8 +51,8 @@ export class Bulb {
59
51
  }
60
52
 
61
53
  // this is subject we use to track when we need to POST changes to the SwitchBot API
62
- this.doBulbUpdate = new Subject();
63
- this.bulbUpdateInProgress = false;
54
+ this.doColorBulbUpdate = new Subject();
55
+ this.colorBulbUpdateInProgress = false;
64
56
 
65
57
 
66
58
  // Retrieve initial values and updateHomekit
@@ -70,7 +62,7 @@ export class Bulb {
70
62
  accessory
71
63
  .getService(this.platform.Service.AccessoryInformation)!
72
64
  .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SwitchBot')
73
- .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-BULB-W1401400')
65
+ .setCharacteristic(this.platform.Characteristic.Model, 'SWITCHBOT-COLORBULB-W1401400')
74
66
  .setCharacteristic(this.platform.Characteristic.SerialNumber, device.deviceId!);
75
67
 
76
68
  // get the Television service if it exists, otherwise create a new Television service
@@ -122,17 +114,17 @@ export class Bulb {
122
114
 
123
115
  // Start an update interval
124
116
  interval(this.platform.config.options!.refreshRate! * 1000)
125
- .pipe(skipWhile(() => this.bulbUpdateInProgress))
117
+ .pipe(skipWhile(() => this.colorBulbUpdateInProgress))
126
118
  .subscribe(() => {
127
119
  this.refreshStatus();
128
120
  });
129
121
 
130
122
  // Watch for Bulb change events
131
123
  // We put in a debounce of 100ms so we don't make duplicate calls
132
- this.doBulbUpdate
124
+ this.doColorBulbUpdate
133
125
  .pipe(
134
126
  tap(() => {
135
- this.bulbUpdateInProgress = true;
127
+ this.colorBulbUpdateInProgress = true;
136
128
  }),
137
129
  debounceTime(this.platform.config.options!.pushRate! * 1000),
138
130
  )
@@ -141,16 +133,16 @@ export class Bulb {
141
133
  await this.pushChanges();
142
134
  } catch (e: any) {
143
135
  this.platform.log.error(JSON.stringify(e.message));
144
- this.platform.debug(`Bulb ${accessory.displayName} - ${JSON.stringify(e)}`);
136
+ this.platform.debug(`Color Bulb ${accessory.displayName} - ${JSON.stringify(e)}`);
145
137
  this.apiError(e);
146
138
  }
147
- this.bulbUpdateInProgress = false;
139
+ this.colorBulbUpdateInProgress = false;
148
140
  });
149
141
  }
150
142
 
151
143
  private minStep(): number | undefined {
152
- if (this.device.bulb?.set_minStep) {
153
- this.set_minStep = this.device.bulb?.set_minStep;
144
+ if (this.device.colorbulb?.set_minStep) {
145
+ this.set_minStep = this.device.colorbulb?.set_minStep;
154
146
  } else {
155
147
  this.set_minStep = 1;
156
148
  }
@@ -165,21 +157,21 @@ export class Bulb {
165
157
  default:
166
158
  this.On = false;
167
159
  }
168
- this.platform.debug(`Bulb ${this.accessory.displayName} On: ${this.On}`);
160
+ this.platform.debug(`Color Bulb ${this.accessory.displayName} On: ${this.On}`);
169
161
  this.deviceStatus.body.brightness = Number(this.Brightness);
170
162
  this.deviceStatus.body.colorTemperature = Number(this.ColorTemperature);
171
163
  }
172
164
 
173
165
  async refreshStatus() {
174
166
  try {
175
- this.platform.debug('Bulb - Reading', `${DeviceURL}/${this.device.deviceId}/status`);
167
+ this.platform.debug('Color Bulb - Reading', `${DeviceURL}/${this.device.deviceId}/status`);
176
168
  this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
177
- this.platform.device(`Bulb ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
169
+ this.platform.device(`Color Bulb ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
178
170
  this.parseStatus();
179
171
  this.updateHomeKitCharacteristics();
180
172
  } catch (e: any) {
181
- this.platform.log.error(`Bulb ${this.accessory.displayName} failed to refresh status, Error Message ${JSON.stringify(e.message)}`);
182
- this.platform.debug(`Bulb ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`);
173
+ this.platform.log.error(`Color Bulb ${this.accessory.displayName} failed to refresh status, Error Message ${JSON.stringify(e.message)}`);
174
+ this.platform.debug(`Color Bulb ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`);
183
175
  this.apiError(e);
184
176
  }
185
177
  }
@@ -187,8 +179,8 @@ export class Bulb {
187
179
  /**
188
180
  * Pushes the requested changes to the SwitchBot API
189
181
  * deviceType commandType Command command parameter Description
190
- * Bulb - "command" "turnOff" "default" = set to OFF state
191
- * Bulb - "command" "turnOn" "default" = set to ON state
182
+ * Color Bulb - "command" "turnOff" "default" = set to OFF state
183
+ * Color Bulb - "command" "turnOn" "default" = set to ON state
192
184
  */
193
185
  async pushChanges() {
194
186
  const payload = {
@@ -212,32 +204,32 @@ export class Bulb {
212
204
  'commandType:',
213
205
  payload.commandType,
214
206
  );
215
- this.platform.debug(`Bulb ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`);
207
+ this.platform.debug(`Color Bulb ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`);
216
208
 
217
209
  // Make the API request
218
210
  const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload));
219
- this.platform.debug(`Bulb ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
211
+ this.platform.debug(`Color Bulb ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
220
212
  this.statusCode(push);
221
213
  }
222
214
 
223
215
  updateHomeKitCharacteristics() {
224
216
  if (this.On === undefined) {
225
- this.platform.debug(`Bulb ${this.accessory.displayName} On: ${this.On}`);
217
+ this.platform.debug(`Color Bulb ${this.accessory.displayName} On: ${this.On}`);
226
218
  } else {
227
219
  this.service.updateCharacteristic(this.platform.Characteristic.On, this.On);
228
- this.platform.device(`Bulb ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
220
+ this.platform.device(`Color Bulb ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
229
221
  }
230
222
  if (this.Brightness === undefined) {
231
- this.platform.debug(`Bulb ${this.accessory.displayName} Brightness: ${this.Brightness}`);
223
+ this.platform.debug(`Color Bulb ${this.accessory.displayName} Brightness: ${this.Brightness}`);
232
224
  } else {
233
225
  this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.Brightness);
234
- this.platform.device(`Bulb ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`);
226
+ this.platform.device(`Color Bulb ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`);
235
227
  }
236
228
  if (this.ColorTemperature === undefined) {
237
- this.platform.debug(`Bulb ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
229
+ this.platform.debug(`Color Bulb ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
238
230
  } else {
239
231
  this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.ColorTemperature);
240
- this.platform.debug(`Bulb ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`);
232
+ this.platform.debug(`Color Bulb ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`);
241
233
  }
242
234
  }
243
235
 
@@ -248,7 +240,7 @@ export class Bulb {
248
240
  }
249
241
 
250
242
 
251
- private statusCode(push: { data: { statusCode: any; }; }) {
243
+ private statusCode(push: AxiosResponse<{ statusCode: number;}>) {
252
244
  switch (push.data.statusCode) {
253
245
  case 151:
254
246
  this.platform.log.error('Command not supported by this device type.');
@@ -285,7 +277,7 @@ export class Bulb {
285
277
  this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`);
286
278
 
287
279
  this.On = value;
288
- this.doBulbUpdate.next();
280
+ this.doColorBulbUpdate.next();
289
281
  }
290
282
 
291
283
  /**
@@ -295,7 +287,7 @@ export class Bulb {
295
287
  this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`);
296
288
 
297
289
  this.Brightness = value;
298
- this.doBulbUpdate.next();
290
+ this.doColorBulbUpdate.next();
299
291
  }
300
292
 
301
293
  /**
@@ -305,6 +297,6 @@ export class Bulb {
305
297
  this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`);
306
298
 
307
299
  this.ColorTemperature = value;
308
- this.doBulbUpdate.next();
300
+ this.doColorBulbUpdate.next();
309
301
  }
310
302
  }