@switchbot/homebridge-switchbot 1.15.1-beta.1 → 1.15.1-beta.10

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 (116) hide show
  1. package/.github/ISSUE_TEMPLATE/bug-report.yml +1 -0
  2. package/.github/ISSUE_TEMPLATE/feature-request.yml +1 -0
  3. package/.github/ISSUE_TEMPLATE/support-request.yml +1 -0
  4. package/.prettierrc +1 -1
  5. package/CHANGELOG.md +9 -0
  6. package/dist/device/bot.d.ts +8 -7
  7. package/dist/device/bot.d.ts.map +1 -1
  8. package/dist/device/bot.js +97 -21
  9. package/dist/device/bot.js.map +1 -1
  10. package/dist/device/colorbulb.d.ts +8 -7
  11. package/dist/device/colorbulb.d.ts.map +1 -1
  12. package/dist/device/colorbulb.js +221 -56
  13. package/dist/device/colorbulb.js.map +1 -1
  14. package/dist/device/contact.d.ts +3 -3
  15. package/dist/device/contact.d.ts.map +1 -1
  16. package/dist/device/contact.js +50 -7
  17. package/dist/device/contact.js.map +1 -1
  18. package/dist/device/curtain.d.ts +9 -8
  19. package/dist/device/curtain.d.ts.map +1 -1
  20. package/dist/device/curtain.js +95 -21
  21. package/dist/device/curtain.js.map +1 -1
  22. package/dist/device/humidifier.d.ts +8 -7
  23. package/dist/device/humidifier.d.ts.map +1 -1
  24. package/dist/device/humidifier.js +180 -59
  25. package/dist/device/humidifier.js.map +1 -1
  26. package/dist/device/lock.d.ts +8 -7
  27. package/dist/device/lock.d.ts.map +1 -1
  28. package/dist/device/lock.js +98 -22
  29. package/dist/device/lock.js.map +1 -1
  30. package/dist/device/meter.d.ts +4 -4
  31. package/dist/device/meter.d.ts.map +1 -1
  32. package/dist/device/meter.js +51 -8
  33. package/dist/device/meter.js.map +1 -1
  34. package/dist/device/meterplus.d.ts +3 -3
  35. package/dist/device/meterplus.d.ts.map +1 -1
  36. package/dist/device/meterplus.js +49 -6
  37. package/dist/device/meterplus.js.map +1 -1
  38. package/dist/device/motion.d.ts +3 -3
  39. package/dist/device/motion.d.ts.map +1 -1
  40. package/dist/device/motion.js +49 -9
  41. package/dist/device/motion.js.map +1 -1
  42. package/dist/device/plug.d.ts +8 -7
  43. package/dist/device/plug.d.ts.map +1 -1
  44. package/dist/device/plug.js +96 -20
  45. package/dist/device/plug.js.map +1 -1
  46. package/dist/device/striplight.d.ts +8 -7
  47. package/dist/device/striplight.d.ts.map +1 -1
  48. package/dist/device/striplight.js +186 -47
  49. package/dist/device/striplight.js.map +1 -1
  50. package/dist/irdevice/airconditioner.d.ts +7 -6
  51. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  52. package/dist/irdevice/airconditioner.js +40 -6
  53. package/dist/irdevice/airconditioner.js.map +1 -1
  54. package/dist/irdevice/airpurifier.d.ts +7 -6
  55. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  56. package/dist/irdevice/airpurifier.js +46 -11
  57. package/dist/irdevice/airpurifier.js.map +1 -1
  58. package/dist/irdevice/camera.d.ts +7 -6
  59. package/dist/irdevice/camera.d.ts.map +1 -1
  60. package/dist/irdevice/camera.js +40 -6
  61. package/dist/irdevice/camera.js.map +1 -1
  62. package/dist/irdevice/fan.d.ts +8 -7
  63. package/dist/irdevice/fan.d.ts.map +1 -1
  64. package/dist/irdevice/fan.js +40 -6
  65. package/dist/irdevice/fan.js.map +1 -1
  66. package/dist/irdevice/light.d.ts +7 -6
  67. package/dist/irdevice/light.d.ts.map +1 -1
  68. package/dist/irdevice/light.js +40 -6
  69. package/dist/irdevice/light.js.map +1 -1
  70. package/dist/irdevice/other.d.ts +7 -6
  71. package/dist/irdevice/other.d.ts.map +1 -1
  72. package/dist/irdevice/other.js +40 -6
  73. package/dist/irdevice/other.js.map +1 -1
  74. package/dist/irdevice/tv.d.ts +8 -7
  75. package/dist/irdevice/tv.d.ts.map +1 -1
  76. package/dist/irdevice/tv.js +40 -6
  77. package/dist/irdevice/tv.js.map +1 -1
  78. package/dist/irdevice/vacuumcleaner.d.ts +7 -6
  79. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  80. package/dist/irdevice/vacuumcleaner.js +40 -6
  81. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  82. package/dist/irdevice/waterheater.d.ts +7 -6
  83. package/dist/irdevice/waterheater.d.ts.map +1 -1
  84. package/dist/irdevice/waterheater.js +40 -6
  85. package/dist/irdevice/waterheater.js.map +1 -1
  86. package/dist/platform.d.ts +3 -5
  87. package/dist/platform.d.ts.map +1 -1
  88. package/dist/platform.js +143 -87
  89. package/dist/platform.js.map +1 -1
  90. package/dist/settings.d.ts +2 -21
  91. package/dist/settings.d.ts.map +1 -1
  92. package/dist/settings.js +3 -3
  93. package/dist/settings.js.map +1 -1
  94. package/package.json +10 -12
  95. package/src/device/bot.ts +97 -30
  96. package/src/device/colorbulb.ts +225 -93
  97. package/src/device/contact.ts +49 -10
  98. package/src/device/curtain.ts +98 -31
  99. package/src/device/humidifier.ts +182 -77
  100. package/src/device/lock.ts +97 -30
  101. package/src/device/meter.ts +51 -12
  102. package/src/device/meterplus.ts +48 -9
  103. package/src/device/motion.ts +48 -12
  104. package/src/device/plug.ts +97 -29
  105. package/src/device/striplight.ts +189 -68
  106. package/src/irdevice/airconditioner.ts +44 -9
  107. package/src/irdevice/airpurifier.ts +52 -17
  108. package/src/irdevice/camera.ts +46 -11
  109. package/src/irdevice/fan.ts +50 -15
  110. package/src/irdevice/light.ts +48 -13
  111. package/src/irdevice/other.ts +46 -11
  112. package/src/irdevice/tv.ts +56 -21
  113. package/src/irdevice/vacuumcleaner.ts +46 -11
  114. package/src/irdevice/waterheater.ts +46 -11
  115. package/src/platform.ts +157 -100
  116. package/src/settings.ts +2 -25
@@ -1,10 +1,12 @@
1
- import { AxiosResponse } from 'axios';
1
+ import https from 'https';
2
+ import crypto from 'crypto';
3
+ import { Context } from 'vm';
4
+ import { IncomingMessage } from 'http';
2
5
  import { interval, Subject } from 'rxjs';
3
6
  import { SwitchBotPlatform } from '../platform';
4
7
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
5
8
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
6
- import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload, deviceStatus } from '../settings';
7
- import { Context } from 'vm';
9
+ import { device, devicesConfig, deviceStatus, HostDomain, DevicePath } from '../settings';
8
10
 
9
11
  export class Lock {
10
12
  // Services
@@ -19,7 +21,7 @@ export class Lock {
19
21
  // OpenAPI Others
20
22
  doorState!: deviceStatus['doorState'];
21
23
  lockState!: deviceStatus['lockState'];
22
- deviceStatus!: deviceStatusResponse;
24
+ deviceStatus!: any; //deviceStatusResponse;
23
25
 
24
26
  // Config
25
27
  scanDuration!: number;
@@ -151,12 +153,49 @@ export class Lock {
151
153
 
152
154
  async refreshStatus(): Promise<void> {
153
155
  try {
154
- this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
155
- this.debugLog(`Lock: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
156
- this.lockState = this.deviceStatus.body.lockState;
157
- this.doorState = this.deviceStatus.body.doorState;
158
- this.parseStatus();
159
- this.updateHomeKitCharacteristics();
156
+ const t = Date.now();
157
+ const nonce = 'requestID';
158
+ const data = this.platform.config.credentials?.token + t + nonce;
159
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
160
+ const sign = signTerm.toString('base64');
161
+ this.debugLog(`Lock: ${this.accessory.displayName} sign: ${sign}`);
162
+ const options = {
163
+ hostname: HostDomain,
164
+ port: 443,
165
+ path: `${DevicePath}/${this.device.deviceId}/status`,
166
+ method: 'GET',
167
+ headers: {
168
+ Authorization: this.platform.config.credentials?.token,
169
+ sign: sign,
170
+ nonce: nonce,
171
+ t: t,
172
+ 'Content-Type': 'application/json',
173
+ },
174
+ };
175
+ const req = https.request(options, (res) => {
176
+ this.debugLog(`Lock: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
177
+ let rawData = '';
178
+ res.on('data', (d) => {
179
+ rawData += d;
180
+ this.debugLog(`Lock: ${this.accessory.displayName} d: ${d}`);
181
+ });
182
+ res.on('end', () => {
183
+ try {
184
+ this.deviceStatus = JSON.parse(rawData);
185
+ this.debugLog(`Lock: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
186
+ this.lockState = this.deviceStatus.body.lockState;
187
+ this.doorState = this.deviceStatus.body.doorState;
188
+ this.parseStatus();
189
+ this.updateHomeKitCharacteristics();
190
+ } catch (e: any) {
191
+ this.errorLog(`Lock: ${this.accessory.displayName} error message: ${e.message}`);
192
+ }
193
+ });
194
+ });
195
+ req.on('error', (e: any) => {
196
+ this.errorLog(`Lock: ${this.accessory.displayName} error message: ${e.message}`);
197
+ });
198
+ req.end();
160
199
  } catch (e: any) {
161
200
  this.errorLog(`Lock: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
162
201
  if (this.deviceLogging.includes('debug')) {
@@ -175,26 +214,54 @@ export class Lock {
175
214
  * Lock - "command" "unlock" "default" = set to ???? state - LockCurrentState
176
215
  */
177
216
  async pushChanges(): Promise<void> {
178
- const payload = {
179
- commandType: 'command',
180
- parameter: 'default',
181
- } as payload;
182
-
217
+ // Make Push On request to the API
218
+ const t = Date.now();
219
+ const nonce = 'requestID';
220
+ const data = this.platform.config.credentials?.token + t + nonce;
221
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
222
+ .update(Buffer.from(data, 'utf-8'))
223
+ .digest();
224
+ const sign = signTerm.toString('base64');
225
+ this.debugLog(`Lock: ${this.accessory.displayName} sign: ${sign}`);
226
+ let command = '';
183
227
  if (this.LockTargetState) {
184
- payload.command = 'lock';
228
+ command = 'lock';
185
229
  } else {
186
- payload.command = 'unlock';
230
+ command = 'unlock';
187
231
  }
188
-
189
- this.infoLog(
190
- `Lock: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` +
191
- ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`,
192
- );
193
-
194
- // Make the API request
195
- const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
196
- this.debugLog(`Lock: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`);
197
- this.statusCode(push);
232
+ const body = JSON.stringify({
233
+ 'command': `${command}`,
234
+ 'parameter': 'default',
235
+ 'commandType': 'command',
236
+ });
237
+ this.infoLog(`Lock: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
238
+ const options = {
239
+ hostname: HostDomain,
240
+ port: 443,
241
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
242
+ method: 'POST',
243
+ headers: {
244
+ 'Authorization': this.platform.config.credentials?.token,
245
+ 'sign': sign,
246
+ 'nonce': nonce,
247
+ 't': t,
248
+ 'Content-Type': 'application/json',
249
+ 'Content-Length': body.length,
250
+ },
251
+ };
252
+ const req = https.request(options, res => {
253
+ this.debugLog(`Lock: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
254
+ this.statusCode({ res });
255
+ res.on('data', d => {
256
+ this.debugLog(`Lock: ${this.accessory.displayName} d: ${d}`);
257
+ });
258
+ });
259
+ req.on('error', (e: any) => {
260
+ this.errorLog(`Lock: ${this.accessory.displayName} error message: ${e.message}`);
261
+ });
262
+ req.write(body);
263
+ req.end();
264
+ this.debugLog(`Lock: ${this.accessory.displayName} pushchanges: ${JSON.stringify(req)}`);
198
265
  this.accessory.context.On = this.LockTargetState;
199
266
  interval(5000)
200
267
  .pipe(take(1))
@@ -235,8 +302,8 @@ export class Lock {
235
302
  //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
236
303
  }
237
304
 
238
- async statusCode(push: AxiosResponse<{ statusCode: number }>): Promise<void> {
239
- switch (push.data.statusCode) {
305
+ async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
306
+ switch (res.statusCode) {
240
307
  case 151:
241
308
  this.errorLog(`Lock: ${this.accessory.displayName} Command not supported by this device type.`);
242
309
  break;
@@ -255,7 +322,7 @@ export class Lock {
255
322
  case 190:
256
323
  this.errorLog(
257
324
  `Lock: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
258
- ` Or command: ${JSON.stringify(push.data)} format is invalid`,
325
+ ` Or command: ${JSON.stringify(res)} format is invalid`,
259
326
  );
260
327
  break;
261
328
  case 100:
@@ -1,12 +1,14 @@
1
+ import https from 'https';
2
+ import crypto from 'crypto';
3
+ import { Context } from 'vm';
4
+ import { hostname } from 'os';
5
+ import { MqttClient } from 'mqtt';
6
+ import { connectAsync } from 'async-mqtt';
1
7
  import { interval, Subject } from 'rxjs';
2
8
  import { skipWhile } from 'rxjs/operators';
3
9
  import { SwitchBotPlatform } from '../platform';
4
10
  import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
5
- import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature, deviceStatus } from '../settings';
6
- import { Context } from 'vm';
7
- import { MqttClient } from 'mqtt';
8
- import { connectAsync } from 'async-mqtt';
9
- import { hostname } from 'os';
11
+ import { device, devicesConfig, serviceData, ad, switchbot, temperature, deviceStatus, HostDomain, DevicePath } from '../settings';
10
12
 
11
13
  /**
12
14
  * Platform Accessory
@@ -33,7 +35,7 @@ export class Meter {
33
35
  // OpenAPI Others
34
36
  Temperature: deviceStatus['temperature'];
35
37
  Humidity: deviceStatus['humidity'];
36
- deviceStatus!: deviceStatusResponse;
38
+ deviceStatus!: any; //deviceStatusResponse;
37
39
 
38
40
  // BLE Others
39
41
  connected?: boolean;
@@ -407,12 +409,49 @@ export class Meter {
407
409
  if (this.platform.config.credentials?.token) {
408
410
  this.debugLog(`Meter: ${this.accessory.displayName} OpenAPI RefreshStatus`);
409
411
  try {
410
- this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
411
- this.debugLog(`Meter: ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`);
412
- this.Humidity = this.deviceStatus.body.humidity!;
413
- this.Temperature = this.deviceStatus.body.temperature!;
414
- this.parseStatus();
415
- this.updateHomeKitCharacteristics();
412
+ const t = Date.now();
413
+ const nonce = 'requestID';
414
+ const data = this.platform.config.credentials?.token + t + nonce;
415
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
416
+ const sign = signTerm.toString('base64');
417
+ this.debugLog(`Meter: ${this.accessory.displayName} sign: ${sign}`);
418
+ const options = {
419
+ hostname: HostDomain,
420
+ port: 443,
421
+ path: `${DevicePath}/${this.device.deviceId}/status`,
422
+ method: 'GET',
423
+ headers: {
424
+ Authorization: this.platform.config.credentials?.token,
425
+ sign: sign,
426
+ nonce: nonce,
427
+ t: t,
428
+ 'Content-Type': 'application/json',
429
+ },
430
+ };
431
+ const req = https.request(options, (res) => {
432
+ this.debugLog(`Meter: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
433
+ let rawData = '';
434
+ res.on('data', (d) => {
435
+ rawData += d;
436
+ this.debugLog(`Meter: ${this.accessory.displayName} d: ${d}`);
437
+ });
438
+ res.on('end', () => {
439
+ try {
440
+ this.deviceStatus = JSON.parse(rawData);
441
+ this.debugLog(`Meter: ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`);
442
+ this.Humidity = this.deviceStatus.body.humidity!;
443
+ this.Temperature = this.deviceStatus.body.temperature!;
444
+ this.parseStatus();
445
+ this.updateHomeKitCharacteristics();
446
+ } catch (e: any) {
447
+ this.errorLog(`Meter: ${this.accessory.displayName} error message: ${e.message}`);
448
+ }
449
+ });
450
+ });
451
+ req.on('error', (e: any) => {
452
+ this.errorLog(`Meter: ${this.accessory.displayName} error message: ${e.message}`);
453
+ });
454
+ req.end();
416
455
  } catch (e: any) {
417
456
  this.errorLog(`Meter: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
418
457
  if (this.deviceLogging.includes('debug')) {
@@ -1,9 +1,11 @@
1
+ import https from 'https';
2
+ import crypto from 'crypto';
3
+ import { Context } from 'vm';
1
4
  import { interval, Subject } from 'rxjs';
2
5
  import { skipWhile } from 'rxjs/operators';
3
6
  import { SwitchBotPlatform } from '../platform';
4
7
  import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
5
- import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature, deviceStatus } from '../settings';
6
- import { Context } from 'vm';
8
+ import { device, devicesConfig, serviceData, ad, switchbot, temperature, deviceStatus, HostDomain, DevicePath } from '../settings';
7
9
 
8
10
  /**
9
11
  * Platform Accessory
@@ -30,7 +32,7 @@ export class MeterPlus {
30
32
  // OpenAPI Others
31
33
  Temperature: deviceStatus['temperature'];
32
34
  Humidity: deviceStatus['humidity'];
33
- deviceStatus!: deviceStatusResponse;
35
+ deviceStatus!: any; //deviceStatusResponse;
34
36
 
35
37
  // BLE Others
36
38
  connected?: boolean;
@@ -344,12 +346,49 @@ export class MeterPlus {
344
346
  if (this.platform.config.credentials?.token) {
345
347
  this.debugLog(`Meter Plus: ${this.accessory.displayName} OpenAPI RefreshStatus`);
346
348
  try {
347
- this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
348
- this.debugLog(`Meter Plus: ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`);
349
- this.Humidity = this.deviceStatus.body.humidity!;
350
- this.Temperature = this.deviceStatus.body.temperature!;
351
- this.parseStatus();
352
- this.updateHomeKitCharacteristics();
349
+ const t = Date.now();
350
+ const nonce = 'requestID';
351
+ const data = this.platform.config.credentials?.token + t + nonce;
352
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
353
+ const sign = signTerm.toString('base64');
354
+ this.debugLog(`Meter Plus: ${this.accessory.displayName} sign: ${sign}`);
355
+ const options = {
356
+ hostname: HostDomain,
357
+ port: 443,
358
+ path: `${DevicePath}/${this.device.deviceId}/status`,
359
+ method: 'GET',
360
+ headers: {
361
+ Authorization: this.platform.config.credentials?.token,
362
+ sign: sign,
363
+ nonce: nonce,
364
+ t: t,
365
+ 'Content-Type': 'application/json',
366
+ },
367
+ };
368
+ const req = https.request(options, (res) => {
369
+ this.debugLog(`Meter Plus: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
370
+ let rawData = '';
371
+ res.on('data', (d) => {
372
+ rawData += d;
373
+ this.debugLog(`Meter Plus: ${this.accessory.displayName} d: ${d}`);
374
+ });
375
+ res.on('end', () => {
376
+ try {
377
+ this.deviceStatus = JSON.parse(rawData);
378
+ this.debugLog(`Meter Plus: ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`);
379
+ this.Humidity = this.deviceStatus.body.humidity!;
380
+ this.Temperature = this.deviceStatus.body.temperature!;
381
+ this.parseStatus();
382
+ this.updateHomeKitCharacteristics();
383
+ } catch (e: any) {
384
+ this.errorLog(`Meter Plus: ${this.accessory.displayName} error message: ${e.message}`);
385
+ }
386
+ });
387
+ });
388
+ req.on('error', (e: any) => {
389
+ this.errorLog(`Meter Plus: ${this.accessory.displayName} error message: ${e.message}`);
390
+ });
391
+ req.end();
353
392
  } catch (e: any) {
354
393
  this.errorLog(`Meter Plus: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
355
394
  if (this.deviceLogging.includes('debug')) {
@@ -1,9 +1,11 @@
1
+ import https from 'https';
2
+ import crypto from 'crypto';
3
+ import { Context } from 'vm';
1
4
  import { interval, Subject } from 'rxjs';
2
5
  import { skipWhile } from 'rxjs/operators';
3
6
  import { SwitchBotPlatform } from '../platform';
4
7
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
5
- import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, deviceStatus, ad } from '../settings';
6
- import { Context } from 'vm';
8
+ import { device, devicesConfig, serviceData, switchbot, deviceStatus, ad, HostDomain, DevicePath } from '../settings';
7
9
 
8
10
  /**
9
11
  * Platform Accessory
@@ -23,7 +25,7 @@ export class Motion {
23
25
  StatusLowBattery?: CharacteristicValue;
24
26
 
25
27
  // OpenAPI Others
26
- deviceStatus!: deviceStatusResponse;
28
+ deviceStatus!: any; //deviceStatusResponse;
27
29
  moveDetected: deviceStatus['moveDetected'];
28
30
  brightness: deviceStatus['brightness'];
29
31
 
@@ -326,15 +328,49 @@ export class Motion {
326
328
  if (this.platform.config.credentials?.token) {
327
329
  this.debugLog(`Motion Sensor: ${this.accessory.displayName} OpenAPI RefreshStatus`);
328
330
  try {
329
- this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
330
- this.debugLog(`Motion Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
331
- if (this.deviceStatus.body.moveDetected === undefined) {
332
- this.errorLog(`Motion Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
333
- }
334
- this.moveDetected = this.deviceStatus.body.moveDetected;
335
- this.brightness = this.deviceStatus.body.brightness;
336
- this.parseStatus();
337
- this.updateHomeKitCharacteristics();
331
+ const t = Date.now();
332
+ const nonce = 'requestID';
333
+ const data = this.platform.config.credentials?.token + t + nonce;
334
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
335
+ const sign = signTerm.toString('base64');
336
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} sign: ${sign}`);
337
+ const options = {
338
+ hostname: HostDomain,
339
+ port: 443,
340
+ path: `${DevicePath}/${this.device.deviceId}/status`,
341
+ method: 'GET',
342
+ headers: {
343
+ Authorization: this.platform.config.credentials?.token,
344
+ sign: sign,
345
+ nonce: nonce,
346
+ t: t,
347
+ 'Content-Type': 'application/json',
348
+ },
349
+ };
350
+ const req = https.request(options, (res) => {
351
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
352
+ let rawData = '';
353
+ res.on('data', (d) => {
354
+ rawData += d;
355
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} d: ${d}`);
356
+ });
357
+ res.on('end', () => {
358
+ try {
359
+ this.deviceStatus = JSON.parse(rawData);
360
+ this.debugLog(`Motion Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
361
+ this.moveDetected = this.deviceStatus.body.moveDetected;
362
+ this.brightness = this.deviceStatus.body.brightness;
363
+ this.parseStatus();
364
+ this.updateHomeKitCharacteristics();
365
+ } catch (e: any) {
366
+ this.errorLog(`Motion Sensor: ${this.accessory.displayName} error message: ${e.message}`);
367
+ }
368
+ });
369
+ });
370
+ req.on('error', (e: any) => {
371
+ this.errorLog(`Motion Sensor: ${this.accessory.displayName} error message: ${e.message}`);
372
+ });
373
+ req.end();
338
374
  } catch (e: any) {
339
375
  this.errorLog(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
340
376
  if (this.deviceLogging.includes('debug')) {
@@ -1,10 +1,12 @@
1
- import { AxiosResponse } from 'axios';
1
+ import https from 'https';
2
+ import crypto from 'crypto';
3
+ import { Context } from 'vm';
4
+ import { IncomingMessage } from 'http';
2
5
  import { interval, Subject } from 'rxjs';
3
6
  import { SwitchBotPlatform } from '../platform';
4
7
  import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
5
8
  import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
6
- import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload, deviceStatus, ad, serviceData, switchbot } from '../settings';
7
- import { Context } from 'vm';
9
+ import { device, devicesConfig, deviceStatus, ad, serviceData, switchbot, HostDomain, DevicePath } from '../settings';
8
10
 
9
11
  export class Plug {
10
12
  // Services
@@ -16,7 +18,7 @@ export class Plug {
16
18
 
17
19
  // OpenAPI Others
18
20
  power: deviceStatus['power'];
19
- deviceStatus!: deviceStatusResponse;
21
+ deviceStatus!: any; //deviceStatusResponse;
20
22
 
21
23
  // BLE Others
22
24
  connected?: boolean;
@@ -281,11 +283,48 @@ export class Plug {
281
283
 
282
284
  async openAPIRefreshStatus() {
283
285
  try {
284
- this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
285
- this.debugLog(`Plug: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
286
- this.power = this.deviceStatus.body.power;
287
- this.parseStatus();
288
- this.updateHomeKitCharacteristics();
286
+ const t = Date.now();
287
+ const nonce = 'requestID';
288
+ const data = this.platform.config.credentials?.token + t + nonce;
289
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
290
+ const sign = signTerm.toString('base64');
291
+ this.debugLog(`Plug: ${this.accessory.displayName} sign: ${sign}`);
292
+ const options = {
293
+ hostname: HostDomain,
294
+ port: 443,
295
+ path: `${DevicePath}/${this.device.deviceId}/status`,
296
+ method: 'GET',
297
+ headers: {
298
+ Authorization: this.platform.config.credentials?.token,
299
+ sign: sign,
300
+ nonce: nonce,
301
+ t: t,
302
+ 'Content-Type': 'application/json',
303
+ },
304
+ };
305
+ const req = https.request(options, (res) => {
306
+ this.debugLog(`Plug: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
307
+ let rawData = '';
308
+ res.on('data', (d) => {
309
+ rawData += d;
310
+ this.debugLog(`Plug: ${this.accessory.displayName} d: ${d}`);
311
+ });
312
+ res.on('end', () => {
313
+ try {
314
+ this.deviceStatus = JSON.parse(rawData);
315
+ this.debugLog(`Plug: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
316
+ this.power = this.deviceStatus.body.power;
317
+ this.parseStatus();
318
+ this.updateHomeKitCharacteristics();
319
+ } catch (e: any) {
320
+ this.errorLog(`Plug: ${this.accessory.displayName} error message: ${e.message}`);
321
+ }
322
+ });
323
+ });
324
+ req.on('error', (e: any) => {
325
+ this.errorLog(`Plug: ${this.accessory.displayName} error message: ${e.message}`);
326
+ });
327
+ req.end();
289
328
  } catch (e: any) {
290
329
  this.errorLog(`Plug: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`);
291
330
  if (this.deviceLogging.includes('debug')) {
@@ -376,27 +415,56 @@ export class Plug {
376
415
  async openAPIpushChanges() {
377
416
  if (this.platform.config.credentials?.token) {
378
417
  if (this.On !== this.OnCached) {
418
+ // Make Push On request to the API
379
419
  this.debugLog(`Plug: ${this.accessory.displayName} OpenAPI pushChanges`);
380
- const payload = {
381
- commandType: 'command',
382
- parameter: 'default',
383
- } as payload;
384
-
420
+ const t = Date.now();
421
+ const nonce = 'requestID';
422
+ const data = this.platform.config.credentials?.token + t + nonce;
423
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
424
+ .update(Buffer.from(data, 'utf-8'))
425
+ .digest();
426
+ const sign = signTerm.toString('base64');
427
+ this.debugLog(`Plug: ${this.accessory.displayName} sign: ${sign}`);
428
+
429
+ let command = '';
385
430
  if (this.On) {
386
- payload.command = 'turnOn';
431
+ command = 'turnOn';
387
432
  } else {
388
- payload.command = 'turnOff';
433
+ command = 'turnOff';
389
434
  }
390
-
391
- this.infoLog(
392
- `Plug: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` +
393
- ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`,
394
- );
395
-
396
- // Make the API request
397
- const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
398
- this.debugLog(`Plug: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`);
399
- this.statusCode(push);
435
+ const body = JSON.stringify({
436
+ 'command': `${command}`,
437
+ 'parameter': 'default',
438
+ 'commandType': 'command',
439
+ });
440
+ this.infoLog(`Plug: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`);
441
+ const options = {
442
+ hostname: HostDomain,
443
+ port: 443,
444
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
445
+ method: 'POST',
446
+ headers: {
447
+ 'Authorization': this.platform.config.credentials?.token,
448
+ 'sign': sign,
449
+ 'nonce': nonce,
450
+ 't': t,
451
+ 'Content-Type': 'application/json',
452
+ 'Content-Length': body.length,
453
+ },
454
+ };
455
+ const req = https.request(options, res => {
456
+ this.debugLog(`Plug: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
457
+ this.statusCode({ res });
458
+ res.on('data', d => {
459
+ this.debugLog(`Plug: ${this.accessory.displayName} d: ${d}`);
460
+ });
461
+ });
462
+ req.on('error', (e: any) => {
463
+ this.errorLog(`Plug: ${this.accessory.displayName} error message: ${e.message}`);
464
+ });
465
+ req.write(body);
466
+ req.end();
467
+ this.debugLog(`Plug: ${this.accessory.displayName} openAPIpushChanges: ${JSON.stringify(req)}`);
400
468
  this.OnCached = this.On;
401
469
  this.accessory.context.On = this.OnCached;
402
470
  }
@@ -422,8 +490,8 @@ export class Plug {
422
490
  //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
423
491
  }
424
492
 
425
- async statusCode(push: AxiosResponse<{ statusCode: number }>): Promise<void> {
426
- switch (push.data.statusCode) {
493
+ async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
494
+ switch (res.statusCode) {
427
495
  case 151:
428
496
  this.errorLog(`Plug: ${this.accessory.displayName} Command not supported by this device type.`);
429
497
  break;
@@ -444,7 +512,7 @@ export class Plug {
444
512
  case 190:
445
513
  this.errorLog(
446
514
  `Plug: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
447
- ` Or command: ${JSON.stringify(push.data)} format is invalid`,
515
+ ` Or command: ${JSON.stringify(res)} format is invalid`,
448
516
  );
449
517
  break;
450
518
  case 100: