@switchbot/homebridge-switchbot 2.6.0-beta.4 → 2.6.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 (143) hide show
  1. package/.vscode/tasks.json +13 -0
  2. package/CHANGELOG.md +4 -2
  3. package/README.md +18 -9
  4. package/dist/device/blindtilt.d.ts +1 -5
  5. package/dist/device/blindtilt.d.ts.map +1 -1
  6. package/dist/device/blindtilt.js +80 -140
  7. package/dist/device/blindtilt.js.map +1 -1
  8. package/dist/device/bot.d.ts +1 -5
  9. package/dist/device/bot.d.ts.map +1 -1
  10. package/dist/device/bot.js +79 -138
  11. package/dist/device/bot.js.map +1 -1
  12. package/dist/device/ceilinglight.d.ts +2 -6
  13. package/dist/device/ceilinglight.d.ts.map +1 -1
  14. package/dist/device/ceilinglight.js +157 -304
  15. package/dist/device/ceilinglight.js.map +1 -1
  16. package/dist/device/colorbulb.d.ts +2 -6
  17. package/dist/device/colorbulb.d.ts.map +1 -1
  18. package/dist/device/colorbulb.js +164 -311
  19. package/dist/device/colorbulb.js.map +1 -1
  20. package/dist/device/contact.d.ts +1 -5
  21. package/dist/device/contact.d.ts.map +1 -1
  22. package/dist/device/contact.js +36 -72
  23. package/dist/device/contact.js.map +1 -1
  24. package/dist/device/curtain.d.ts +1 -5
  25. package/dist/device/curtain.d.ts.map +1 -1
  26. package/dist/device/curtain.js +78 -138
  27. package/dist/device/curtain.js.map +1 -1
  28. package/dist/device/hub.d.ts +2 -6
  29. package/dist/device/hub.d.ts.map +1 -1
  30. package/dist/device/hub.js +30 -63
  31. package/dist/device/hub.js.map +1 -1
  32. package/dist/device/humidifier.d.ts +1 -5
  33. package/dist/device/humidifier.d.ts.map +1 -1
  34. package/dist/device/humidifier.js +115 -228
  35. package/dist/device/humidifier.js.map +1 -1
  36. package/dist/device/iosensor.d.ts +1 -5
  37. package/dist/device/iosensor.d.ts.map +1 -1
  38. package/dist/device/iosensor.js +38 -74
  39. package/dist/device/iosensor.js.map +1 -1
  40. package/dist/device/{striplight.d.ts → lightstrip.d.ts} +2 -10
  41. package/dist/device/lightstrip.d.ts.map +1 -0
  42. package/dist/device/{striplight.js → lightstrip.js} +138 -194
  43. package/dist/device/lightstrip.js.map +1 -0
  44. package/dist/device/lock.d.ts +1 -5
  45. package/dist/device/lock.d.ts.map +1 -1
  46. package/dist/device/lock.js +66 -126
  47. package/dist/device/lock.js.map +1 -1
  48. package/dist/device/meter.d.ts +1 -5
  49. package/dist/device/meter.d.ts.map +1 -1
  50. package/dist/device/meter.js +38 -74
  51. package/dist/device/meter.js.map +1 -1
  52. package/dist/device/meterplus.d.ts +1 -5
  53. package/dist/device/meterplus.d.ts.map +1 -1
  54. package/dist/device/meterplus.js +39 -75
  55. package/dist/device/meterplus.js.map +1 -1
  56. package/dist/device/motion.d.ts +1 -5
  57. package/dist/device/motion.d.ts.map +1 -1
  58. package/dist/device/motion.js +35 -72
  59. package/dist/device/motion.js.map +1 -1
  60. package/dist/device/plug.d.ts +1 -5
  61. package/dist/device/plug.d.ts.map +1 -1
  62. package/dist/device/plug.js +65 -126
  63. package/dist/device/plug.js.map +1 -1
  64. package/dist/device/robotvacuumcleaner.d.ts +1 -5
  65. package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
  66. package/dist/device/robotvacuumcleaner.js +79 -151
  67. package/dist/device/robotvacuumcleaner.js.map +1 -1
  68. package/dist/irdevice/airconditioner.d.ts +3 -7
  69. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  70. package/dist/irdevice/airconditioner.js +34 -62
  71. package/dist/irdevice/airconditioner.js.map +1 -1
  72. package/dist/irdevice/airpurifier.d.ts +3 -7
  73. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  74. package/dist/irdevice/airpurifier.js +31 -59
  75. package/dist/irdevice/airpurifier.js.map +1 -1
  76. package/dist/irdevice/camera.d.ts +1 -5
  77. package/dist/irdevice/camera.d.ts.map +1 -1
  78. package/dist/irdevice/camera.js +30 -57
  79. package/dist/irdevice/camera.js.map +1 -1
  80. package/dist/irdevice/fan.d.ts +2 -6
  81. package/dist/irdevice/fan.d.ts.map +1 -1
  82. package/dist/irdevice/fan.js +39 -66
  83. package/dist/irdevice/fan.js.map +1 -1
  84. package/dist/irdevice/light.d.ts +1 -5
  85. package/dist/irdevice/light.d.ts.map +1 -1
  86. package/dist/irdevice/light.js +32 -59
  87. package/dist/irdevice/light.js.map +1 -1
  88. package/dist/irdevice/other.d.ts +1 -5
  89. package/dist/irdevice/other.d.ts.map +1 -1
  90. package/dist/irdevice/other.js +30 -57
  91. package/dist/irdevice/other.js.map +1 -1
  92. package/dist/irdevice/tv.d.ts +3 -7
  93. package/dist/irdevice/tv.d.ts.map +1 -1
  94. package/dist/irdevice/tv.js +46 -76
  95. package/dist/irdevice/tv.js.map +1 -1
  96. package/dist/irdevice/vacuumcleaner.d.ts +2 -6
  97. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  98. package/dist/irdevice/vacuumcleaner.js +30 -58
  99. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  100. package/dist/irdevice/waterheater.d.ts +7 -23
  101. package/dist/irdevice/waterheater.d.ts.map +1 -1
  102. package/dist/irdevice/waterheater.js +41 -74
  103. package/dist/irdevice/waterheater.js.map +1 -1
  104. package/dist/platform.d.ts +14 -5
  105. package/dist/platform.d.ts.map +1 -1
  106. package/dist/platform.js +156 -149
  107. package/dist/platform.js.map +1 -1
  108. package/dist/settings.d.ts +4 -0
  109. package/dist/settings.d.ts.map +1 -1
  110. package/dist/settings.js +5 -1
  111. package/dist/settings.js.map +1 -1
  112. package/nodemon.json +1 -1
  113. package/package.json +10 -10
  114. package/src/device/blindtilt.ts +122 -181
  115. package/src/device/bot.ts +96 -149
  116. package/src/device/ceilinglight.ts +172 -315
  117. package/src/device/colorbulb.ts +178 -324
  118. package/src/device/contact.ts +48 -78
  119. package/src/device/curtain.ts +93 -148
  120. package/src/device/hub.ts +34 -63
  121. package/src/device/humidifier.ts +135 -243
  122. package/src/device/iosensor.ts +49 -79
  123. package/src/device/{striplight.ts → lightstrip.ts} +148 -200
  124. package/src/device/lock.ts +78 -134
  125. package/src/device/meter.ts +48 -78
  126. package/src/device/meterplus.ts +50 -80
  127. package/src/device/motion.ts +46 -77
  128. package/src/device/plug.ts +76 -134
  129. package/src/device/robotvacuumcleaner.ts +91 -159
  130. package/src/irdevice/airconditioner.ts +56 -81
  131. package/src/irdevice/airpurifier.ts +41 -67
  132. package/src/irdevice/camera.ts +35 -60
  133. package/src/irdevice/fan.ts +46 -71
  134. package/src/irdevice/light.ts +38 -63
  135. package/src/irdevice/other.ts +35 -60
  136. package/src/irdevice/tv.ts +52 -80
  137. package/src/irdevice/vacuumcleaner.ts +36 -62
  138. package/src/irdevice/waterheater.ts +46 -76
  139. package/src/platform.ts +144 -158
  140. package/src/settings.ts +5 -0
  141. package/.github/npm-version-script.js +0 -81
  142. package/dist/device/striplight.d.ts.map +0 -1
  143. package/dist/device/striplight.js.map +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "Homebridge SwitchBot",
3
3
  "name": "@switchbot/homebridge-switchbot",
4
- "version": "2.6.0-beta.4",
4
+ "version": "2.6.0",
5
5
  "description": "The [Homebridge](https://homebridge.io) SwitchBot plugin allows you to access your [SwitchBot](https://www.switch-bot.com) device(s) from HomeKit.",
6
6
  "author": "SwitchBot <support@wondertechlabs.com> (https://github.com/SwitchBot)",
7
7
  "license": "ISC",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "engines": {
16
16
  "homebridge": "^1.6.0 || ^2.0.0-beta.0",
17
- "node": "^16.20.0 || ^18.15.0"
17
+ "node": "^16.20.0 || ^18.16.0"
18
18
  },
19
19
  "main": "dist/index.js",
20
20
  "scripts": {
@@ -56,26 +56,26 @@
56
56
  ],
57
57
  "dependencies": {
58
58
  "@homebridge/plugin-ui-utils": "^0.0.19",
59
- "fakegato-history": "^0.6.3",
60
- "super-stringify": "^1.0.0",
61
59
  "async-mqtt": "^2.6.3",
62
60
  "crypto-js": "^4.1.1",
61
+ "fakegato-history": "^0.6.3",
62
+ "node-switchbot": "1.8.1",
63
63
  "rxjs": "^7.8.0",
64
- "node-switchbot": "1.8.1"
64
+ "undici": "^5.21.2"
65
65
  },
66
66
  "devDependencies": {
67
- "@types/node": "^18.15.11",
68
67
  "@types/crypto-js": "^4.1.1",
69
- "@typescript-eslint/eslint-plugin": "^5.57.1",
70
- "@typescript-eslint/parser": "^5.57.1",
68
+ "@types/node": "^18.15.11",
69
+ "@typescript-eslint/eslint-plugin": "^5.58.0",
70
+ "@typescript-eslint/parser": "^5.58.0",
71
71
  "eslint": "^8.38.0",
72
72
  "eslint-config-prettier": "8.8.0",
73
73
  "eslint-plugin-prettier": "4.2.1",
74
74
  "homebridge": "^1.6.0",
75
75
  "nodemon": "^2.0.22",
76
- "npm-check-updates": "^16.10.7",
76
+ "npm-check-updates": "^16.10.8",
77
77
  "prettier": "2.8.7",
78
- "rimraf": "^4.4.1",
78
+ "rimraf": "^5.0.0",
79
79
  "ts-node": "^10.9.1",
80
80
  "typescript": "^5.0.4"
81
81
  }
@@ -1,16 +1,13 @@
1
- import https from 'https';
2
- import crypto from 'crypto';
3
1
  import { Context } from 'vm';
2
+ import { request } from 'undici';
3
+ import { sleep } from '../utils';
4
4
  import { MqttClient } from 'mqtt';
5
- import { IncomingMessage } from 'http';
6
5
  import { interval, Subject } from 'rxjs';
7
6
  import { connectAsync } from 'async-mqtt';
8
- import superStringify from 'super-stringify';
9
7
  import { SwitchBotPlatform } from '../platform';
10
8
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
11
9
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
12
- import { device, devicesConfig, serviceData, switchbot, deviceStatus, ad, HostDomain, DevicePath } from '../settings';
13
- import { sleep } from '../utils';
10
+ import { device, devicesConfig, serviceData, switchbot, deviceStatus, ad, Devices } from '../settings';
14
11
 
15
12
  enum BlindTiltMappingMode {
16
13
  OnlyUp = 'only_up',
@@ -240,7 +237,7 @@ export class BlindTilt {
240
237
  } catch (e: any) {
241
238
  this.apiError(e);
242
239
  this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
243
- + ` Error Message: ${superStringify(e.message)}`);
240
+ + ` Error Message: ${JSON.stringify(e.message)}`);
244
241
  }
245
242
  this.blindTiltUpdateInProgress = false;
246
243
  });
@@ -259,7 +256,7 @@ export class BlindTilt {
259
256
  } else {
260
257
  await this.offlineOff();
261
258
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
262
- + ` ${this.device.connectionType}, parseStatus will not happen.`);
259
+ + ` ${this.device.connectionType}, parseStatus will not happen.`);
263
260
  }
264
261
  }
265
262
 
@@ -299,7 +296,7 @@ export class BlindTilt {
299
296
  }
300
297
  this.debugLog(
301
298
  `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
302
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
299
+ ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
303
300
  );
304
301
 
305
302
  if (!this.device.blindTilt?.hide_lightsensor) {
@@ -317,7 +314,7 @@ export class BlindTilt {
317
314
  this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
318
315
  this.debugLog(
319
316
  `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` +
320
- ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
317
+ ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
321
318
  );
322
319
  break;
323
320
  case 3:
@@ -366,7 +363,7 @@ export class BlindTilt {
366
363
  this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
367
364
  }
368
365
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
369
- +` StatusLowBattery: ${this.StatusLowBattery}`);
366
+ + ` StatusLowBattery: ${this.StatusLowBattery}`);
370
367
  }
371
368
 
372
369
  async openAPIparseStatus(): Promise<void> {
@@ -380,7 +377,7 @@ export class BlindTilt {
380
377
  await this.setMinMax();
381
378
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
382
379
 
383
- if(homekitTiltAngle) {
380
+ if (homekitTiltAngle) {
384
381
  this.CurrentHorizontalTiltAngle = homekitTiltAngle!;
385
382
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`);
386
383
  }
@@ -402,17 +399,17 @@ export class BlindTilt {
402
399
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
403
400
  this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
404
401
  } else {
405
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby because reached position,`+
406
- ` CurrentPosition: ${this.CurrentPosition}`);
402
+ this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby because reached position,` +
403
+ ` CurrentPosition: ${this.CurrentPosition}`);
407
404
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
408
405
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
409
406
  this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
410
407
  }
411
408
  } else {
412
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,`+
413
- ` CurrentPosition: ${this.CurrentPosition}`);
409
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,` +
410
+ ` CurrentPosition: ${this.CurrentPosition}`);
414
411
  this.TargetPosition = this.CurrentPosition;
415
- if(homekitTiltAngle) {
412
+ if (homekitTiltAngle) {
416
413
  this.TargetHorizontalTiltAngle = this.CurrentHorizontalTiltAngle;
417
414
  }
418
415
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
@@ -420,7 +417,7 @@ export class BlindTilt {
420
417
  }
421
418
  this.debugLog(
422
419
  `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
423
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
420
+ ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
424
421
  );
425
422
 
426
423
  if (!this.device.blindTilt?.hide_lightsensor) {
@@ -449,7 +446,7 @@ export class BlindTilt {
449
446
  } else {
450
447
  await this.offlineOff();
451
448
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
452
- + ` ${this.device.connectionType}, refreshStatus will not happen.`);
449
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
453
450
  }
454
451
  }
455
452
 
@@ -475,18 +472,18 @@ export class BlindTilt {
475
472
  switchbot.onadvertisement = async (ad: any) => {
476
473
  this.address = ad.address;
477
474
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},`
478
- + ` BLE Address Found: ${this.address}`);
475
+ + ` BLE Address Found: ${this.address}`);
479
476
  this.serviceData = ad.serviceData;
480
477
  this.calibration = ad.serviceData.calibration;
481
478
  this.battery = ad.serviceData.battery;
482
479
  this.inMotion = ad.serviceData.inMotion;
483
480
  this.position = ad.serviceData.position;
484
481
  this.lightLevel = ad.serviceData.lightLevel;
485
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${superStringify(ad.serviceData)}`);
482
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
486
483
  this.debugLog(
487
484
  `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
488
- `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}, ` +
489
- `inMotion: ${ad.serviceData.inMotion}`);
485
+ `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}, ` +
486
+ `inMotion: ${ad.serviceData.inMotion}`);
490
487
 
491
488
  if (this.serviceData) {
492
489
  this.connected = true;
@@ -507,7 +504,7 @@ export class BlindTilt {
507
504
  .catch(async (e: any) => {
508
505
  this.apiError(e);
509
506
  this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}`
510
- + ` Connection, Error Message: ${superStringify(e.message)}`);
507
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
511
508
  await this.BLERefreshConnection(switchbot);
512
509
  });
513
510
  } else {
@@ -518,55 +515,27 @@ export class BlindTilt {
518
515
  async openAPIRefreshStatus(): Promise<void> {
519
516
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
520
517
  try {
521
- const t = Date.now();
522
- const nonce = 'requestID';
523
- const data = this.platform.config.credentials?.token + t + nonce;
524
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
525
- const sign = signTerm.toString('base64');
526
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
527
- const options = {
528
- hostname: HostDomain,
529
- port: 443,
530
- path: `${DevicePath}/${this.device.deviceId}/status`,
531
- method: 'GET',
532
- headers: {
533
- Authorization: this.platform.config.credentials?.token,
534
- sign: sign,
535
- nonce: nonce,
536
- t: t,
537
- 'Content-Type': 'application/json',
538
- },
539
- };
540
- const req = https.request(options, (res) => {
541
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
542
- let rawData = '';
543
- res.on('data', (d) => {
544
- rawData += d;
545
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
546
- });
547
- res.on('end', () => {
548
- try {
549
- this.deviceStatus = JSON.parse(rawData);
550
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus: ${superStringify(this.deviceStatus)}`);
551
- this.slidePosition = this.deviceStatus.body.slidePosition;
552
- this.direction = this.deviceStatus.body.direction;
553
- this.moving = this.deviceStatus.body.moving;
554
- this.brightness = this.deviceStatus.body.brightness;
555
- this.openAPIparseStatus();
556
- this.updateHomeKitCharacteristics();
557
- } catch (e: any) {
558
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
559
- }
560
- });
561
- });
562
- req.on('error', (e: any) => {
563
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
518
+ const { body, statusCode, headers } = await request(`${Devices}/${this.device.deviceId}/status`, {
519
+ headers: this.platform.generateHeaders(),
564
520
  });
565
- req.end();
521
+ const deviceStatus = await body.json();
522
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Devices: ${JSON.stringify(deviceStatus.body)}`);
523
+ this.statusCode(statusCode);
524
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Headers: ${JSON.stringify(headers)}`);
525
+ this.debugLog(
526
+ `${this.device.deviceType}: ${this.accessory.displayName
527
+ } refreshStatus: ${JSON.stringify(deviceStatus)}`,
528
+ );
529
+ this.slidePosition = deviceStatus.body.slidePosition;
530
+ this.direction = deviceStatus.body.direction;
531
+ this.moving = deviceStatus.body.moving;
532
+ this.brightness = deviceStatus.body.brightness;
533
+ this.openAPIparseStatus();
534
+ this.updateHomeKitCharacteristics();
566
535
  } catch (e: any) {
567
536
  this.apiError(e);
568
537
  this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
569
- + ` Connection, Error Message: ${superStringify(e.message)}`);
538
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
570
539
  }
571
540
  }
572
541
 
@@ -580,7 +549,7 @@ export class BlindTilt {
580
549
  } else {
581
550
  await this.offlineOff();
582
551
  this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
583
- + ` ${this.device.connectionType}, pushChanges will not happen.`);
552
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
584
553
  }
585
554
  // Refresh the status from the API
586
555
  interval(15000)
@@ -625,7 +594,7 @@ export class BlindTilt {
625
594
  .catch(async (e: any) => {
626
595
  this.apiError(e);
627
596
  this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
628
- + ` Connection, Error Message: ${superStringify(e.message)}`);
597
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
629
598
  await this.BLEPushConnection();
630
599
  });
631
600
  } else {
@@ -634,7 +603,7 @@ export class BlindTilt {
634
603
  }
635
604
  } else {
636
605
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` +
637
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
606
+ ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
638
607
  );
639
608
  }
640
609
  }
@@ -660,86 +629,52 @@ export class BlindTilt {
660
629
  }
661
630
 
662
631
  async openAPIpushChanges(): Promise<void> {
663
- try {
664
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI pushChanges`);
665
-
666
- const hasDifferentAndRelevantHorizontalTiltAngle = (
667
- this.mappingMode === BlindTiltMappingMode.UseTiltForDirection
668
- && this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle);
669
- if ((this.TargetPosition !== this.CurrentPosition) || hasDifferentAndRelevantHorizontalTiltAngle || this.device.disableCaching) {
670
- // Make Push On request to the API
671
- const t = Date.now();
672
- const nonce = 'requestID';
673
- const data = this.platform.config.credentials?.token + t + nonce;
674
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
675
- .update(Buffer.from(data, 'utf-8'))
676
- .digest();
677
- const sign = signTerm.toString('base64');
678
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
679
-
680
-
681
- const [direction, position] = this.mapHomekitValuesToDeviceValues(Number(this.TargetPosition), Number(this.TargetHorizontalTiltAngle));
682
- this.debugLog(`Pushing ${this.TargetPosition} (device = ${direction};${position})`);
683
-
684
- this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`);
685
-
686
- let body = '';
687
- if(position === 100) {
688
- body = superStringify({
689
- 'command': 'fullyOpen',
690
- 'commandType': 'command',
691
- });
692
- } else if(position === 0) {
693
- body = superStringify({
694
- 'command': direction === 'up' ? 'closeUp' : 'closeDown',
695
- 'commandType': 'command',
696
- });
697
- } else {
698
- body = superStringify({
699
- 'command': 'setPosition',
700
- 'parameter': `${direction};${position}`,
701
- 'commandType': 'command',
702
- });
703
- }
704
-
705
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${body},`);
706
- const options = {
707
- hostname: HostDomain,
708
- port: 443,
709
- path: `${DevicePath}/${this.device.deviceId}/commands`,
710
- method: 'POST',
711
- headers: {
712
- 'Authorization': this.platform.config.credentials?.token,
713
- 'sign': sign,
714
- 'nonce': nonce,
715
- 't': t,
716
- 'Content-Type': 'application/json',
717
- 'Content-Length': body.length,
718
- },
719
- };
720
- const req = https.request(options, res => {
721
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges statusCode: ${res.statusCode}`);
722
- this.statusCode({ res });
723
- res.on('data', d => {
724
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
725
- });
632
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI pushChanges`);
633
+ const hasDifferentAndRelevantHorizontalTiltAngle = (
634
+ this.mappingMode === BlindTiltMappingMode.UseTiltForDirection
635
+ && this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle);
636
+ if ((this.TargetPosition !== this.CurrentPosition) || hasDifferentAndRelevantHorizontalTiltAngle || this.device.disableCaching) {
637
+ const [direction, position] = this.mapHomekitValuesToDeviceValues(Number(this.TargetPosition), Number(this.TargetHorizontalTiltAngle));
638
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.TargetPosition} (device = ${direction};${position})`);
639
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${this.Mode}`);
640
+ let body = '';
641
+ if (position === 100) {
642
+ body = JSON.stringify({
643
+ 'command': 'fullyOpen',
644
+ 'commandType': 'command',
726
645
  });
727
- req.on('error', (e: any) => {
728
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
646
+ } else if (position === 0) {
647
+ body = JSON.stringify({
648
+ 'command': direction === 'up' ? 'closeUp' : 'closeDown',
649
+ 'commandType': 'command',
729
650
  });
730
- req.write(body);
731
- req.end();
732
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges: ${superStringify(req)}`);
733
651
  } else {
734
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.`
735
- +` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`
736
- +` CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}, TargetPosition ${this.TargetHorizontalTiltAngle}`,
652
+ body = JSON.stringify({
653
+ 'command': 'setPosition',
654
+ 'parameter': `${direction};${position}`,
655
+ 'commandType': 'command',
656
+ });
657
+ }
658
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${body},`);
659
+ try {
660
+ const { body, statusCode, headers } = await request(`${Devices}/${this.device.deviceId}/commands`, {
661
+ method: 'POST',
662
+ headers: this.platform.generateHeaders(),
663
+ });
664
+ const deviceStatus = await body.json();
665
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Devices: ${JSON.stringify(deviceStatus.body)}`);
666
+ this.statusCode(statusCode);
667
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Headers: ${JSON.stringify(headers)}`);
668
+ } catch (e: any) {
669
+ this.apiError(e);
670
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
671
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`,
737
672
  );
738
673
  }
739
- } catch (e: any) {
740
- this.apiError(e);
741
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
742
- + ` Connection, Error Message: ${superStringify(e.message)}`,
674
+ } else {
675
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.`
676
+ + ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`
677
+ + ` CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}, TargetPosition ${this.TargetHorizontalTiltAngle}`,
743
678
  );
744
679
  }
745
680
  }
@@ -756,13 +691,14 @@ export class BlindTilt {
756
691
 
757
692
  //value = value < 0 ? -90 : 90;
758
693
  this.TargetHorizontalTiltAngle = value;
759
- if(this.device.mqttURL) {
694
+ if (this.device.mqttURL) {
760
695
  this.mqttPublish('TargetHorizontalTiltAngle', this.TargetHorizontalTiltAngle);
761
696
  }
762
697
 
763
698
  this.startUpdatingBlindTiltIfNeeded();
764
699
  }
765
700
 
701
+
766
702
  /**
767
703
  * Handle requests to set the value of the "Target Position" characteristic
768
704
  */
@@ -786,18 +722,18 @@ export class BlindTilt {
786
722
  if (this.TargetPosition > this.CurrentPosition || (this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle)) {
787
723
  this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
788
724
  this.setNewTarget = true;
789
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},`+
790
- ` CurrentPosition: ${this.CurrentPosition}`);
725
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` +
726
+ ` CurrentPosition: ${this.CurrentPosition}`);
791
727
  } else if (this.TargetPosition < this.CurrentPosition) {
792
728
  this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
793
729
  this.setNewTarget = true;
794
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},`+
795
- ` CurrentPosition: ${this.CurrentPosition}`);
730
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` +
731
+ ` CurrentPosition: ${this.CurrentPosition}`);
796
732
  } else {
797
733
  this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
798
734
  this.setNewTarget = false;
799
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},`+
800
- ` CurrentPosition: ${this.CurrentPosition}`);
735
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` +
736
+ ` CurrentPosition: ${this.CurrentPosition}`);
801
737
  }
802
738
  this.windowCoveringService.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
803
739
  this.windowCoveringService.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
@@ -820,7 +756,7 @@ export class BlindTilt {
820
756
  async updateHomeKitCharacteristics(): Promise<void> {
821
757
  await this.setMinMax();
822
758
 
823
- if(this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) {
759
+ if (this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) {
824
760
  if (this.CurrentHorizontalTiltAngle === undefined || Number.isNaN(this.CurrentHorizontalTiltAngle)) {
825
761
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`);
826
762
  } else {
@@ -875,7 +811,7 @@ export class BlindTilt {
875
811
  this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
876
812
  this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
877
813
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
878
- + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
814
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
879
815
  }
880
816
  }
881
817
  if (this.BLE) {
@@ -913,7 +849,7 @@ export class BlindTilt {
913
849
  ?.join(':');
914
850
  const options = this.device.mqttPubOptions || {};
915
851
  this.mqttClient?.publish(`homebridge-switchbot/blindtilt/${mac}/${topic}`, `${message}`, options);
916
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${superStringify(options)}`);
852
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`);
917
853
  }
918
854
 
919
855
  /*
@@ -953,7 +889,7 @@ export class BlindTilt {
953
889
  });
954
890
  // Set an event handler
955
891
  switchbot.onadvertisement = (ad: any) => {
956
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
892
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
957
893
  };
958
894
  await sleep(10000);
959
895
  // Stop to monitor
@@ -1009,7 +945,7 @@ export class BlindTilt {
1009
945
  }
1010
946
  }
1011
947
 
1012
- if(this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) {
948
+ if (this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) {
1013
949
  this.CurrentHorizontalTiltAngle = Number(this.CurrentHorizontalTiltAngle) < 0 ? -90 : 90;
1014
950
  }
1015
951
  }
@@ -1047,7 +983,7 @@ export class BlindTilt {
1047
983
  this.scanDuration = this.updateRate;
1048
984
  if (this.BLE) {
1049
985
  this.warnLog(`${this.device.deviceType}: `
1050
- + `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`);
986
+ + `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`);
1051
987
  }
1052
988
  } else {
1053
989
  this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
@@ -1067,36 +1003,41 @@ export class BlindTilt {
1067
1003
  }
1068
1004
  }
1069
1005
 
1070
- async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
1071
- switch (res.statusCode) {
1006
+ async statusCode(statusCode: number): Promise<void> {
1007
+ switch (statusCode) {
1072
1008
  case 151:
1073
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this device type.`);
1009
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
1074
1010
  break;
1075
1011
  case 152:
1076
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
1012
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
1077
1013
  break;
1078
1014
  case 160:
1079
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
1015
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
1080
1016
  break;
1081
1017
  case 161:
1082
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline.`);
1018
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
1083
1019
  this.offlineOff();
1084
1020
  break;
1085
1021
  case 171:
1086
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`);
1022
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. `
1023
+ + `Hub: ${this.device.hubDeviceId}`);
1087
1024
  this.offlineOff();
1088
1025
  break;
1089
1026
  case 190:
1090
1027
  this.errorLog(
1091
1028
  `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
1092
- ` Or command: ${superStringify(res)} format is invalid`,
1029
+ ` Or command format is invalid, statusCode: ${statusCode}`,
1093
1030
  );
1094
1031
  break;
1095
1032
  case 100:
1096
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
1033
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
1034
+ break;
1035
+ case 200:
1036
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
1097
1037
  break;
1098
1038
  default:
1099
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
1039
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: `
1040
+ + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`);
1100
1041
  }
1101
1042
  }
1102
1043
 
@@ -1124,7 +1065,7 @@ export class BlindTilt {
1124
1065
  FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
1125
1066
  let FirmwareRevision: string;
1126
1067
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
1127
- + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
1068
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
1128
1069
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
1129
1070
  this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
1130
1071
  if (accessory.context.FirmwareRevision) {
@@ -1172,10 +1113,10 @@ export class BlindTilt {
1172
1113
  // homekit position 0 => closed
1173
1114
  // homekit position 100 => open
1174
1115
  deviceDirection;
1175
- switch(this.mappingMode) {
1116
+ switch (this.mappingMode) {
1176
1117
  case BlindTiltMappingMode.OnlyUp:
1177
1118
  // we only close upwards, so we see anything that is tilted downwards(<50) as open
1178
- if(devicePosition < 50) {
1119
+ if (devicePosition < 50) {
1179
1120
  return [100, undefined]; // fully open in homekit
1180
1121
  } else {
1181
1122
  // we range from 50->100, with 100 being closed, so map to homekit by scaling to 0..100 and then reversing
@@ -1184,7 +1125,7 @@ export class BlindTilt {
1184
1125
 
1185
1126
  case BlindTiltMappingMode.OnlyDown:
1186
1127
  // we only close downwards, so we see anything that is tilted upwards(>50) as upwards
1187
- if(devicePosition > 50) {
1128
+ if (devicePosition > 50) {
1188
1129
  return [100, undefined]; // fully open in homekit
1189
1130
  } else {
1190
1131
  // we range from 0..50 so scale to homekit and then reverse
@@ -1202,7 +1143,7 @@ export class BlindTilt {
1202
1143
 
1203
1144
  case BlindTiltMappingMode.UseTiltForDirection:
1204
1145
  // we use tilt for direction, so being closed downwards is 0 in homekit with -90 tilt, while being closed upwards is 0 with 90 tilt.
1205
- if(devicePosition <= 50) {
1146
+ if (devicePosition <= 50) {
1206
1147
  // downwards tilted, so we range from 0..50, with 0 being closed and 50 being open, so scale.
1207
1148
  return [devicePosition * 2, -90];
1208
1149
  } else {
@@ -1228,7 +1169,7 @@ export class BlindTilt {
1228
1169
  // device position [up, 100] = open
1229
1170
  // device position [down, 100] = open
1230
1171
 
1231
- switch(this.mappingMode) {
1172
+ switch (this.mappingMode) {
1232
1173
  case BlindTiltMappingMode.OnlyUp:
1233
1174
  // invert
1234
1175
  return ['up', homekitPosition];
@@ -1240,7 +1181,7 @@ export class BlindTilt {
1240
1181
  // homekit 0 = downwards closed,
1241
1182
  // homekit 50 = open,
1242
1183
  // homekit 100 = upwards closed
1243
- if(homekitPosition <= 50) {
1184
+ if (homekitPosition <= 50) {
1244
1185
  // homekit 0..50 -> device 100..0 so scale and invert
1245
1186
  return ['down', 100 - homekitPosition * 2];
1246
1187
  } else {
@@ -1252,7 +1193,7 @@ export class BlindTilt {
1252
1193
  // homekit 0 = upwards closed,
1253
1194
  // homekit 50 = open,
1254
1195
  // homekit 100 = upwards closed
1255
- if(homekitPosition <= 50) {
1196
+ if (homekitPosition <= 50) {
1256
1197
  // homekit 0..50 -> device 0..100 so scale and invert
1257
1198
  return ['up', homekitPosition * 2];
1258
1199
  } else {
@@ -1265,7 +1206,7 @@ export class BlindTilt {
1265
1206
  // tilt -90, homekit 100 = open
1266
1207
  // tilt 90, homekit 0 = closed upwards
1267
1208
  // tilt 90, homekit 100 = open
1268
- if(homekitTiltAngle! <= 0) {
1209
+ if (homekitTiltAngle! <= 0) {
1269
1210
  // downwards
1270
1211
  // homekit 0..100 -> device 0..100, so invert
1271
1212
  return ['down', homekitPosition];
@@ -1323,14 +1264,14 @@ export class BlindTilt {
1323
1264
  config['maxRetry'] = device.maxRetry;
1324
1265
  }
1325
1266
 
1326
- if(device.blindTilt?.mode === undefined) {
1267
+ if (device.blindTilt?.mode === undefined) {
1327
1268
  config['mode'] = BlindTiltMappingMode.OnlyUp;
1328
1269
  } else {
1329
1270
  config['mode'] = device.blindTilt?.mode;
1330
1271
  }
1331
1272
 
1332
1273
  if (Object.entries(config).length !== 0) {
1333
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
1274
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
1334
1275
  }
1335
1276
  }
1336
1277