@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
@@ -1,9 +1,6 @@
1
- import https from 'https';
2
- import crypto from 'crypto';
3
- import { IncomingMessage } from 'http';
4
- import superStringify from 'super-stringify';
1
+ import { request } from 'undici';
5
2
  import { SwitchBotPlatform } from '../platform';
6
- import { irDevicesConfig, irdevice, HostDomain, DevicePath } from '../settings';
3
+ import { irDevicesConfig, irdevice, Devices } from '../settings';
7
4
  import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
8
5
 
9
6
  /**
@@ -85,87 +82,57 @@ export class WaterHeater {
85
82
  */
86
83
  async pushWaterHeaterOnChanges(): Promise<void> {
87
84
  this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushWaterHeaterOnChanges Active: ${this.Active},`
88
- + ` disablePushOn: ${this.disablePushOn}`);
85
+ + ` disablePushOn: ${this.disablePushOn}`);
89
86
  if (this.Active === this.platform.Characteristic.Active.ACTIVE && !this.disablePushOn) {
90
87
  const commandType: string = await this.commandType();
91
88
  const command: string = await this.commandOn();
92
- const body = superStringify({
89
+ const body = JSON.stringify({
93
90
  'command': command,
94
91
  'parameter': 'default',
95
92
  'commandType': commandType,
96
93
  });
97
- await this.pushChanges({ body });
94
+ await this.pushChanges(body);
98
95
  }
99
96
  }
100
97
 
101
98
  async pushWaterHeaterOffChanges(): Promise<void> {
102
99
  this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushWaterHeaterOffChanges Active: ${this.Active},`
103
- + ` disablePushOff: ${this.disablePushOff}`);
100
+ + ` disablePushOff: ${this.disablePushOff}`);
104
101
  if (this.Active === this.platform.Characteristic.Active.INACTIVE && !this.disablePushOff) {
105
102
  const commandType: string = await this.commandType();
106
103
  const command: string = await this.commandOff();
107
- const body = superStringify({
104
+ const body = JSON.stringify({
108
105
  'command': command,
109
106
  'parameter': 'default',
110
107
  'commandType': commandType,
111
108
  });
112
- await this.pushChanges({ body });
109
+ await this.pushChanges(body);
113
110
  }
114
111
  }
115
112
 
116
- async pushChanges({ body }: { body: any; }): Promise<void> {
113
+ async pushChanges(body: any): Promise<void> {
114
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushChanges`);
117
115
  if (this.device.connectionType === 'OpenAPI') {
116
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${body},`);
118
117
  try {
119
- // Make Push On request to the API
120
- const t = Date.now();
121
- const nonce = 'requestID';
122
- const data = this.platform.config.credentials?.token + t + nonce;
123
- const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret)
124
- .update(Buffer.from(data, 'utf-8'))
125
- .digest();
126
- const sign = signTerm.toString('base64');
127
- this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} sign: ${sign}`);
128
- this.infoLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Sending request to SwitchBot API. body: ${body},`] });
129
- const options = {
130
- hostname: HostDomain,
131
- port: 443,
132
- path: `${DevicePath}/${this.device.deviceId}/commands`,
118
+ const { body, statusCode, headers } = await request(`${Devices}/${this.device.deviceId}/commands`, {
133
119
  method: 'POST',
134
- headers: {
135
- 'Authorization': this.platform.config.credentials?.token,
136
- 'sign': sign,
137
- 'nonce': nonce,
138
- 't': t,
139
- 'Content-Type': 'application/json',
140
- 'Content-Length': body.length,
141
- },
142
- };
143
- const req = https.request(options, res => {
144
- this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges statusCode: ${res.statusCode}`);
145
- this.statusCode({ res });
146
- res.on('data', d => {
147
- this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} d: ${d}`);
148
- });
120
+ headers: this.platform.generateHeaders(),
149
121
  });
150
- req.on('error', (e: any) => {
151
- this.errorLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} error message: ${e.message}`] });
152
- });
153
- req.write(body);
154
- req.end();
155
- this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} pushchanges: ${superStringify(req)}`);
122
+ const deviceStatus = await body.json();
123
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Devices: ${JSON.stringify(deviceStatus.body)}`);
124
+ this.statusCode(statusCode);
125
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Headers: ${JSON.stringify(headers)}`);
156
126
  this.updateHomeKitCharacteristics();
157
127
  } catch (e: any) {
158
- this.apiError({ e });
159
- this.errorLog({
160
- log: [`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
161
- + ` Error Message: ${superStringify(e.message)}`],
162
- });
128
+ this.apiError(e);
129
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType}`
130
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`,
131
+ );
163
132
  }
164
133
  } else {
165
- this.warnLog({
166
- log: [`${this.device.remoteType}: ${this.accessory.displayName}`
167
- + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`],
168
- });
134
+ this.warnLog(`${this.device.remoteType}: ${this.accessory.displayName}`
135
+ + ` Connection Type: ${this.device.connectionType}, commands will not be sent to OpenAPI`);
169
136
  }
170
137
  }
171
138
 
@@ -225,36 +192,39 @@ export class WaterHeater {
225
192
  return command;
226
193
  }
227
194
 
228
- async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
229
- switch (res.statusCode) {
195
+ async statusCode(statusCode: number): Promise<void> {
196
+ switch (statusCode) {
230
197
  case 151:
231
- this.errorLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this device type.`] });
198
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
232
199
  break;
233
200
  case 152:
234
- this.errorLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Device not found.`] });
201
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
235
202
  break;
236
203
  case 160:
237
- this.errorLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported.`] });
204
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
238
205
  break;
239
206
  case 161:
240
- this.errorLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Device is offline.`] });
207
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
241
208
  break;
242
209
  case 171:
243
- this.errorLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline. Hub: ${this.device.hubDeviceId}`] });
210
+ this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. `
211
+ + `Hub: ${this.device.hubDeviceId}`);
244
212
  break;
245
213
  case 190:
246
214
  this.errorLog(
247
- {
248
- log: [`${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized` +
249
- ` with server, Or command: ${superStringify(res)} format is invalid`],
250
- },
215
+ `${this.device.remoteType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
216
+ ` Or command format is invalid, statusCode: ${statusCode}`,
251
217
  );
252
218
  break;
253
219
  case 100:
254
- this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent.`);
220
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
221
+ break;
222
+ case 200:
223
+ this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
255
224
  break;
256
225
  default:
257
- this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode.`);
226
+ this.infoLog(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode: `
227
+ + `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`);
258
228
  }
259
229
  }
260
230
 
@@ -265,7 +235,7 @@ export class WaterHeater {
265
235
  FirmwareRevision({ accessory, device }: { accessory: PlatformAccessory; device: irdevice & irDevicesConfig; }): string {
266
236
  let FirmwareRevision: string;
267
237
  this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName}`
268
- + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
238
+ + ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
269
239
  this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
270
240
  this.debugLog(`${this.device.remoteType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
271
241
  if (accessory.context.FirmwareRevision) {
@@ -316,7 +286,7 @@ export class WaterHeater {
316
286
  config['disablePushOff'] = device.disablePushOff;
317
287
  }
318
288
  if (Object.entries(config).length !== 0) {
319
- this.infoLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Config: ${superStringify(config)}`] });
289
+ this.infoLog({ log: [`${this.device.remoteType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`] });
320
290
  }
321
291
  }
322
292
 
@@ -339,19 +309,19 @@ export class WaterHeater {
339
309
  /**
340
310
  * Logging for Device
341
311
  */
342
- infoLog({ log = [] }: { log?: any[]; } = {}): void {
312
+ infoLog(...log: any[]): void {
343
313
  if (this.enablingDeviceLogging()) {
344
314
  this.platform.log.info(String(...log));
345
315
  }
346
316
  }
347
317
 
348
- warnLog({ log = [] }: { log?: any[]; } = {}): void {
318
+ warnLog(...log: any[]): void {
349
319
  if (this.enablingDeviceLogging()) {
350
320
  this.platform.log.warn(String(...log));
351
321
  }
352
322
  }
353
323
 
354
- debugWarnLog({ log = [] }: { log?: any[]; } = {}): void {
324
+ debugWarnLog(...log: any[]): void {
355
325
  if (this.enablingDeviceLogging()) {
356
326
  if (this.deviceLogging?.includes('debug')) {
357
327
  this.platform.log.warn('[DEBUG]', String(...log));
@@ -359,13 +329,13 @@ export class WaterHeater {
359
329
  }
360
330
  }
361
331
 
362
- errorLog({ log = [] }: { log?: any[]; } = {}): void {
332
+ errorLog(...log: any[]): void {
363
333
  if (this.enablingDeviceLogging()) {
364
334
  this.platform.log.error(String(...log));
365
335
  }
366
336
  }
367
337
 
368
- debugErrorLog({ log = [] }: { log?: any[]; } = {}): void {
338
+ debugErrorLog(...log: any[]): void {
369
339
  if (this.enablingDeviceLogging()) {
370
340
  if (this.deviceLogging?.includes('debug')) {
371
341
  this.platform.log.error('[DEBUG]', String(...log));
package/src/platform.ts CHANGED
@@ -10,7 +10,7 @@ import { IOSensor } from './device/iosensor';
10
10
  import { MeterPlus } from './device/meterplus';
11
11
  import { ColorBulb } from './device/colorbulb';
12
12
  import { CeilingLight } from './device/ceilinglight';
13
- import { StripLight } from './device/striplight';
13
+ import { StripLight } from './device/lightstrip';
14
14
  import { Humidifier } from './device/humidifier';
15
15
  import { RobotVacuumCleaner } from './device/robotvacuumcleaner';
16
16
  import { TV } from './irdevice/tv';
@@ -23,17 +23,16 @@ import { AirPurifier } from './irdevice/airpurifier';
23
23
  import { WaterHeater } from './irdevice/waterheater';
24
24
  import { VacuumCleaner } from './irdevice/vacuumcleaner';
25
25
  import { AirConditioner } from './irdevice/airconditioner';
26
- import https from 'https';
27
- import crypto from 'crypto';
26
+ import {request} from 'undici';
27
+ import crypto, { randomUUID } from 'crypto';
28
28
  import { Buffer } from 'buffer';
29
29
  import { queueScheduler } from 'rxjs';
30
30
  import fakegato from 'fakegato-history';
31
- import superStringify from 'super-stringify';
31
+
32
32
  import { readFileSync, writeFileSync } from 'fs';
33
33
  import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, Service, Characteristic } from 'homebridge';
34
34
  import {
35
- PLATFORM_NAME, PLUGIN_NAME, irdevice, device, SwitchBotPlatformConfig, devicesConfig, DevicePath, HostDomain, irDevicesConfig } from './settings';
36
- import { IncomingMessage } from 'http';
35
+ PLATFORM_NAME, PLUGIN_NAME, irdevice, device, SwitchBotPlatformConfig, devicesConfig, irDevicesConfig, Devices } from './settings';
37
36
 
38
37
  /**
39
38
  * HomebridgePlatform
@@ -131,7 +130,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
131
130
  platformConfig['pushRate'] = this.config.options.pushRate;
132
131
  }
133
132
  if (Object.entries(platformConfig).length !== 0) {
134
- this.infoLog(`Platform Config: ${superStringify(platformConfig)}`);
133
+ this.infoLog(`Platform Config: ${JSON.stringify(platformConfig)}`);
135
134
  }
136
135
 
137
136
  if (this.config.options) {
@@ -254,150 +253,139 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
254
253
  }
255
254
  }
256
255
 
256
+ generateHeaders = () => {
257
+ const t = `${Date.now()}`;
258
+ const nonce = randomUUID();
259
+ const data = this.config.credentials?.token + t + nonce;
260
+ const signTerm = crypto.createHmac('sha256', this.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
261
+ const sign = signTerm.toString('base64');
262
+
263
+ return {
264
+ 'Authorization': this.config.credentials?.token,
265
+ 'sign': sign,
266
+ 'nonce': nonce,
267
+ 't': t,
268
+ 'Content-Type': 'application/json',
269
+ };
270
+ };
271
+
257
272
  /**
258
273
  * this method discovers devices
274
+ *
275
+ const t = `${Date.now()}`;
276
+ const nonce = 'requestID';
277
+ const data = this.config.credentials?.token + t + nonce;
278
+ const signTerm = crypto.createHmac('sha256', this.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
279
+ const sign = signTerm.toString('base64');
259
280
  */
260
281
  async discoverDevices() {
261
- try {
262
- if (this.config.credentials?.token) {
263
- const t = Date.now();
264
- const nonce = 'requestID';
265
- const data = this.config.credentials?.token + t + nonce;
266
- const signTerm = crypto.createHmac('sha256', this.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
267
- const sign = signTerm.toString('base64');
268
- this.debugLog(`sing: ${sign}`);
269
- const options = {
270
- hostname: HostDomain,
271
- port: 443,
272
- path: DevicePath,
273
- method: 'GET',
274
- headers: {
275
- 'Authorization': this.config.credentials?.token,
276
- 'sign': sign,
277
- 'nonce': nonce,
278
- 't': t,
279
- 'Content-Type': 'application/json',
280
- },
281
- };
282
- const req = https.request(options, async (res) => {
283
- this.debugLog(`statusCode: ${res.statusCode}`);
284
- await this.statusCode({ res });
285
- this.debugLog(`headers: ${superStringify(res.headers)}`);
286
- let rawData = '';
287
- res.on('data', (d) => {
288
- rawData += d;
289
- this.debugLog(`d: ${d}`);
290
- });
291
- res.on('end', () => {
292
- try {
293
- const devicesAPI = JSON.parse(rawData);
294
- this.debugLog(`devicesAPI: ${superStringify(devicesAPI.body)}`);
295
- // SwitchBot Devices
296
- const deviceLists = devicesAPI.body.deviceList;
297
- if (!this.config.options?.devices) {
298
- this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
299
- const devices = deviceLists.map((v: any) => v);
300
- for (const device of devices) {
301
- if (device.deviceType) {
302
- if (device.configDeviceName) {
303
- device.deviceName = device.configDeviceName;
304
- }
305
- this.createDevice(device);
306
- }
307
- }
308
- } else if (this.config.credentials?.token && this.config.options.devices) {
309
- this.debugLog(`SwitchBot Device Config Set: ${superStringify(this.config.options?.devices)}`);
310
- const deviceConfigs = this.config.options?.devices;
311
-
312
- const mergeBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
313
- a1.map((itm: { deviceId: string }) => ({
314
- ...a2.find(
315
- (item: { deviceId: string }) =>
316
- item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
317
- ),
318
- ...itm,
319
- }));
320
-
321
- const devices = mergeBydeviceId(deviceLists, deviceConfigs);
322
- this.debugLog(`SwitchBot Devices: ${superStringify(devices)}`);
323
- for (const device of devices) {
324
- if (device.deviceType) {
325
- if (device.configDeviceName) {
326
- device.deviceName = device.configDeviceName;
327
- }
328
- this.createDevice(device);
329
- }
330
- }
331
- } else {
332
- this.errorLog('SwitchBot Token Supplied, Issue with Auth.');
282
+ if (this.config.credentials?.token) {
283
+ try {
284
+ const { body, statusCode, headers } = await request(Devices, {
285
+ headers: this.generateHeaders(),
286
+ });
287
+ const devicesAPI = await body.json();
288
+ this.debugLog(`Devices: ${JSON.stringify(devicesAPI.body)}`);
289
+ this.statusCode(statusCode);
290
+ this.debugLog(`Headers: ${JSON.stringify(headers)}`);
291
+ // SwitchBot Devices
292
+ const deviceLists = devicesAPI.body.deviceList;
293
+ if (!this.config.options?.devices) {
294
+ this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
295
+ const devices = deviceLists.map((v: any) => v);
296
+ for (const device of devices) {
297
+ if (device.deviceType) {
298
+ if (device.configDeviceName) {
299
+ device.deviceName = device.configDeviceName;
333
300
  }
334
- if (devicesAPI.body.deviceList.length !== 0) {
335
- this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
336
- } else {
337
- this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
301
+ this.createDevice(device);
302
+ }
303
+ }
304
+ } else if (this.config.credentials?.token && this.config.options.devices) {
305
+ this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
306
+ const deviceConfigs = this.config.options?.devices;
307
+
308
+ const mergeBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
309
+ a1.map((itm: { deviceId: string }) => ({
310
+ ...a2.find(
311
+ (item: { deviceId: string }) =>
312
+ item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
313
+ ),
314
+ ...itm,
315
+ }));
316
+
317
+ const devices = mergeBydeviceId(deviceLists, deviceConfigs);
318
+ this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`);
319
+ for (const device of devices) {
320
+ if (device.deviceType) {
321
+ if (device.configDeviceName) {
322
+ device.deviceName = device.configDeviceName;
338
323
  }
324
+ this.createDevice(device);
325
+ }
326
+ }
327
+ } else {
328
+ this.errorLog('SwitchBot Token Supplied, Issue with Auth.');
329
+ }
330
+ if (devicesAPI.body.deviceList.length !== 0) {
331
+ this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
332
+ } else {
333
+ this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
334
+ }
339
335
 
340
- // IR Devices
341
- const irDeviceLists = devicesAPI.body.infraredRemoteList;
342
- if (!this.config.options?.irdevices) {
343
- this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`);
344
- const devices = irDeviceLists.map((v: any) => v);
345
- for (const device of devices) {
346
- if (device.remoteType) {
347
- this.createIRDevice(device);
348
- }
349
- }
350
- } else {
351
- this.debugLog(`IR Device Config Set: ${superStringify(this.config.options?.irdevices)}`);
352
- const irDeviceConfig = this.config.options?.irdevices;
353
-
354
- const mergeIRBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
355
- a1.map((itm: { deviceId: string }) => ({
356
- ...a2.find(
357
- (item: { deviceId: string }) =>
358
- item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
359
- ),
360
- ...itm,
361
- }));
362
-
363
- const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig);
364
- this.debugLog(`IR Devices: ${superStringify(devices)}`);
365
- for (const device of devices) {
366
- this.createIRDevice(device);
367
- }
368
- }
369
- if (devicesAPI.body.infraredRemoteList.length !== 0) {
370
- this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
371
- } else {
372
- this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
373
- }
374
- } catch (e:any) {
375
- this.errorLog(`API Request: ${e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
336
+ // IR Devices
337
+ const irDeviceLists = devicesAPI.body.infraredRemoteList;
338
+ if (!this.config.options?.irdevices) {
339
+ this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`);
340
+ const devices = irDeviceLists.map((v: any) => v);
341
+ for (const device of devices) {
342
+ if (device.remoteType) {
343
+ this.createIRDevice(device);
376
344
  }
377
- });
378
- });
379
- req.on('error', (e: any) => {
380
- this.errorLog(`req: ${e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
381
- });
382
- req.end();
383
- } else if (!this.config.credentials?.token && this.config.options?.devices) {
384
- this.debugLog(`SwitchBot Device Manual Config Set: ${superStringify(this.config.options?.devices)}`);
385
- const deviceConfigs = this.config.options?.devices;
386
- const devices = deviceConfigs.map((v: any) => v);
387
- for (const device of devices) {
388
- device.deviceType = device.configDeviceType;
389
- device.deviceName = device.configDeviceName;
390
- if (device.deviceType) {
391
- this.createDevice(device);
345
+ }
346
+ } else {
347
+ this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`);
348
+ const irDeviceConfig = this.config.options?.irdevices;
349
+
350
+ const mergeIRBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
351
+ a1.map((itm: { deviceId: string }) => ({
352
+ ...a2.find(
353
+ (item: { deviceId: string }) =>
354
+ item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
355
+ ),
356
+ ...itm,
357
+ }));
358
+
359
+ const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig);
360
+ this.debugLog(`IR Devices: ${JSON.stringify(devices)}`);
361
+ for (const device of devices) {
362
+ this.createIRDevice(device);
392
363
  }
393
364
  }
394
- } else {
395
- this.errorLog('Neither SwitchBot Token or Device Config are not set.');
396
- }
397
- } catch (e: any) {
398
- this.debugErrorLog(`Failed to Discover Devices, Error Message: ${superStringify(e.message)}, Submit Bugs Here: `
365
+ if (devicesAPI.body.infraredRemoteList.length !== 0) {
366
+ this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
367
+ } else {
368
+ this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
369
+ }
370
+
371
+ } catch (e: any) {
372
+ this.debugErrorLog(`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: `
399
373
  + 'https://tinyurl.com/SwitchBotBug');
400
- this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`);
374
+ this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`);
375
+ }
376
+ } else if (!this.config.credentials?.token && this.config.options?.devices) {
377
+ this.debugLog(`SwitchBot Device Manual Config Set: ${JSON.stringify(this.config.options?.devices)}`);
378
+ const deviceConfigs = this.config.options?.devices;
379
+ const devices = deviceConfigs.map((v: any) => v);
380
+ for (const device of devices) {
381
+ device.deviceType = device.configDeviceType;
382
+ device.deviceName = device.configDeviceName;
383
+ if (device.deviceType) {
384
+ this.createDevice(device);
385
+ }
386
+ }
387
+ } else {
388
+ this.errorLog('Neither SwitchBot Token or Device Config are not set.');
401
389
  }
402
390
  }
403
391
 
@@ -556,7 +544,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
556
544
  this.createOthers(device);
557
545
  break;
558
546
  default:
559
- this.debugLog(`Unsupported Device: ${superStringify(device)}`);
547
+ this.debugLog(`Unsupported Device: ${JSON.stringify(device)}`);
560
548
  this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.remoteType}, is currently not supported.`);
561
549
  this.warnLog('Submit Feature Requests Here: ' + 'https://tinyurl.com/SwitchBotFeatureRequest');
562
550
  }
@@ -732,7 +720,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
732
720
  }
733
721
  }
734
722
 
735
- private async createMeterPlus(device: device & devicesConfig) {
723
+ private async createMeterPlus(device: device & devicesConfig) {
736
724
  const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`);
737
725
 
738
726
  // see if an accessory with the same uuid has already been registered and restored from
@@ -2124,40 +2112,38 @@ private async createMeterPlus(device: device & devicesConfig) {
2124
2112
  this.warnLog(`Removing existing accessory from cache: ${existingAccessory.displayName}`);
2125
2113
  }
2126
2114
 
2127
- async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
2128
- switch (res.statusCode) {
2115
+ async statusCode(statusCode: number): Promise<void> {
2116
+ switch (statusCode) {
2129
2117
  case 151:
2130
- this.errorLog('Command not supported by this device type, Submit Feature Request Here: ' + 'https://tinyurl.com/SwitchBotFeatureRequest');
2118
+ this.errorLog(`Command not supported by this device type, statusCode: ${statusCode}, Submit Feature Request Here: `
2119
+ + 'https://tinyurl.com/SwitchBotFeatureRequest');
2131
2120
  break;
2132
2121
  case 152:
2133
- this.errorLog('Device not found.');
2122
+ this.errorLog(`Device not found, statusCode: ${statusCode}`);
2134
2123
  break;
2135
2124
  case 160:
2136
- this.errorLog('Command is not supported, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug');
2125
+ this.errorLog(`Command is not supported, statusCode: ${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`);
2137
2126
  break;
2138
2127
  case 161:
2139
- this.errorLog('Device is offline.');
2128
+ this.errorLog(`Device is offline, statusCode: ${statusCode}`);
2140
2129
  break;
2141
2130
  case 171:
2142
- this.errorLog('is offline');
2131
+ this.errorLog(`is offline, statusCode: ${statusCode}`);
2143
2132
  break;
2144
2133
  case 190:
2145
- this.errorLog('Requests reached the daily limit');
2134
+ this.errorLog(`Requests reached the daily limit, statusCode: ${statusCode}`);
2146
2135
  break;
2147
2136
  case 100:
2148
- if (this.debugMode) {
2149
- this.debugLog('Command successfully sent.');
2150
- }
2137
+ this.debugLog(`Command successfully sent, statusCode: ${statusCode}`);
2138
+ break;
2139
+ case 200:
2140
+ this.debugLog(`Request successful, statusCode: ${statusCode}`);
2151
2141
  break;
2152
2142
  default:
2153
- if (this.debugMode) {
2154
- this.debugLog('Unknown statusCode, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug');
2155
- }
2143
+ this.infoLog(`Unknown statusCode, statusCode: ${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`);
2156
2144
  }
2157
2145
  }
2158
2146
 
2159
-
2160
-
2161
2147
  // BLE Connection
2162
2148
  connectBLE() {
2163
2149
  let Switchbot: new () => any;
package/src/settings.ts CHANGED
@@ -11,6 +11,11 @@ export const PLATFORM_NAME = 'SwitchBot';
11
11
  */
12
12
  export const PLUGIN_NAME = '@switchbot/homebridge-switchbot';
13
13
 
14
+ /**
15
+ * This is the main url used to access SwitchBot API
16
+ */
17
+ export const Devices = 'https://api.switch-bot.com/v1.1/devices';
18
+
14
19
  /**
15
20
  * This is the main url used to access SwitchBot API
16
21
  */