@switchbot/homebridge-switchbot 1.15.1-beta.0 → 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 (117) 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/config.schema.json +11 -4
  7. package/dist/device/bot.d.ts +8 -7
  8. package/dist/device/bot.d.ts.map +1 -1
  9. package/dist/device/bot.js +104 -28
  10. package/dist/device/bot.js.map +1 -1
  11. package/dist/device/colorbulb.d.ts +8 -7
  12. package/dist/device/colorbulb.d.ts.map +1 -1
  13. package/dist/device/colorbulb.js +223 -58
  14. package/dist/device/colorbulb.js.map +1 -1
  15. package/dist/device/contact.d.ts +3 -3
  16. package/dist/device/contact.d.ts.map +1 -1
  17. package/dist/device/contact.js +54 -11
  18. package/dist/device/contact.js.map +1 -1
  19. package/dist/device/curtain.d.ts +9 -8
  20. package/dist/device/curtain.d.ts.map +1 -1
  21. package/dist/device/curtain.js +102 -28
  22. package/dist/device/curtain.js.map +1 -1
  23. package/dist/device/humidifier.d.ts +8 -7
  24. package/dist/device/humidifier.d.ts.map +1 -1
  25. package/dist/device/humidifier.js +187 -66
  26. package/dist/device/humidifier.js.map +1 -1
  27. package/dist/device/lock.d.ts +8 -7
  28. package/dist/device/lock.d.ts.map +1 -1
  29. package/dist/device/lock.js +98 -22
  30. package/dist/device/lock.js.map +1 -1
  31. package/dist/device/meter.d.ts +4 -4
  32. package/dist/device/meter.d.ts.map +1 -1
  33. package/dist/device/meter.js +55 -12
  34. package/dist/device/meter.js.map +1 -1
  35. package/dist/device/meterplus.d.ts +3 -3
  36. package/dist/device/meterplus.d.ts.map +1 -1
  37. package/dist/device/meterplus.js +53 -10
  38. package/dist/device/meterplus.js.map +1 -1
  39. package/dist/device/motion.d.ts +3 -3
  40. package/dist/device/motion.d.ts.map +1 -1
  41. package/dist/device/motion.js +53 -13
  42. package/dist/device/motion.js.map +1 -1
  43. package/dist/device/plug.d.ts +8 -7
  44. package/dist/device/plug.d.ts.map +1 -1
  45. package/dist/device/plug.js +100 -24
  46. package/dist/device/plug.js.map +1 -1
  47. package/dist/device/striplight.d.ts +8 -7
  48. package/dist/device/striplight.d.ts.map +1 -1
  49. package/dist/device/striplight.js +186 -47
  50. package/dist/device/striplight.js.map +1 -1
  51. package/dist/irdevice/airconditioner.d.ts +7 -6
  52. package/dist/irdevice/airconditioner.d.ts.map +1 -1
  53. package/dist/irdevice/airconditioner.js +40 -6
  54. package/dist/irdevice/airconditioner.js.map +1 -1
  55. package/dist/irdevice/airpurifier.d.ts +7 -6
  56. package/dist/irdevice/airpurifier.d.ts.map +1 -1
  57. package/dist/irdevice/airpurifier.js +46 -11
  58. package/dist/irdevice/airpurifier.js.map +1 -1
  59. package/dist/irdevice/camera.d.ts +7 -6
  60. package/dist/irdevice/camera.d.ts.map +1 -1
  61. package/dist/irdevice/camera.js +40 -6
  62. package/dist/irdevice/camera.js.map +1 -1
  63. package/dist/irdevice/fan.d.ts +8 -7
  64. package/dist/irdevice/fan.d.ts.map +1 -1
  65. package/dist/irdevice/fan.js +40 -6
  66. package/dist/irdevice/fan.js.map +1 -1
  67. package/dist/irdevice/light.d.ts +7 -6
  68. package/dist/irdevice/light.d.ts.map +1 -1
  69. package/dist/irdevice/light.js +40 -6
  70. package/dist/irdevice/light.js.map +1 -1
  71. package/dist/irdevice/other.d.ts +7 -6
  72. package/dist/irdevice/other.d.ts.map +1 -1
  73. package/dist/irdevice/other.js +40 -6
  74. package/dist/irdevice/other.js.map +1 -1
  75. package/dist/irdevice/tv.d.ts +8 -7
  76. package/dist/irdevice/tv.d.ts.map +1 -1
  77. package/dist/irdevice/tv.js +40 -6
  78. package/dist/irdevice/tv.js.map +1 -1
  79. package/dist/irdevice/vacuumcleaner.d.ts +7 -6
  80. package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
  81. package/dist/irdevice/vacuumcleaner.js +40 -6
  82. package/dist/irdevice/vacuumcleaner.js.map +1 -1
  83. package/dist/irdevice/waterheater.d.ts +7 -6
  84. package/dist/irdevice/waterheater.d.ts.map +1 -1
  85. package/dist/irdevice/waterheater.js +40 -6
  86. package/dist/irdevice/waterheater.js.map +1 -1
  87. package/dist/platform.d.ts +10 -6
  88. package/dist/platform.d.ts.map +1 -1
  89. package/dist/platform.js +211 -93
  90. package/dist/platform.js.map +1 -1
  91. package/dist/settings.d.ts +4 -21
  92. package/dist/settings.d.ts.map +1 -1
  93. package/dist/settings.js +3 -3
  94. package/dist/settings.js.map +1 -1
  95. package/package.json +12 -12
  96. package/src/device/bot.ts +104 -37
  97. package/src/device/colorbulb.ts +227 -95
  98. package/src/device/contact.ts +53 -14
  99. package/src/device/curtain.ts +105 -38
  100. package/src/device/humidifier.ts +189 -84
  101. package/src/device/lock.ts +97 -30
  102. package/src/device/meter.ts +55 -16
  103. package/src/device/meterplus.ts +52 -13
  104. package/src/device/motion.ts +52 -16
  105. package/src/device/plug.ts +101 -33
  106. package/src/device/striplight.ts +189 -68
  107. package/src/irdevice/airconditioner.ts +44 -9
  108. package/src/irdevice/airpurifier.ts +52 -17
  109. package/src/irdevice/camera.ts +46 -11
  110. package/src/irdevice/fan.ts +50 -15
  111. package/src/irdevice/light.ts +48 -13
  112. package/src/irdevice/other.ts +46 -11
  113. package/src/irdevice/tv.ts +56 -21
  114. package/src/irdevice/vacuumcleaner.ts +46 -11
  115. package/src/irdevice/waterheater.ts +46 -11
  116. package/src/platform.ts +232 -107
  117. package/src/settings.ts +4 -25
@@ -1,6 +1,8 @@
1
- import { AxiosResponse } from 'axios';
1
+ import https from 'https';
2
+ import crypto from 'crypto';
3
+ import { IncomingMessage } from 'http';
2
4
  import { SwitchBotPlatform } from '../platform';
3
- import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings';
5
+ import { irDevicesConfig, irdevice, HostDomain, DevicePath } from '../settings';
4
6
  import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
5
7
 
6
8
  /**
@@ -95,7 +97,7 @@ export class WaterHeater {
95
97
  commandType: 'command',
96
98
  parameter: 'default',
97
99
  command: 'turnOn',
98
- } as payload;
100
+ };
99
101
  await this.pushChanges(payload);
100
102
  }
101
103
  }
@@ -106,12 +108,12 @@ export class WaterHeater {
106
108
  commandType: 'command',
107
109
  parameter: 'default',
108
110
  command: 'turnOff',
109
- } as payload;
111
+ };
110
112
  await this.pushChanges(payload);
111
113
  }
112
114
  }
113
115
 
114
- async pushChanges(payload: payload): Promise<void> {
116
+ async pushChanges(payload): Promise<void> {
115
117
  try {
116
118
  this.infoLog(
117
119
  `Water Heater: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` +
@@ -119,9 +121,42 @@ export class WaterHeater {
119
121
  );
120
122
 
121
123
  // Make the API request
122
- const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload);
123
- this.debugLog(`Water Heater: ${this.accessory.displayName} pushChanges: ${push.data}`);
124
- this.statusCode(push);
124
+ const t = Date.now();
125
+ const nonce = 'requestID';
126
+ const data = this.platform.config.credentials?.token + t + nonce;
127
+ const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
128
+ const sign = signTerm.toString('base64');
129
+ this.debugLog(`Water Heater: ${this.accessory.displayName} sign: ${sign}`);
130
+ const options = {
131
+ hostname: HostDomain,
132
+ port: 443,
133
+ path: `${DevicePath}/${this.device.deviceId}/commands`,
134
+ method: 'POST',
135
+ headers: {
136
+ Authorization: this.platform.config.credentials?.token,
137
+ sign: sign,
138
+ nonce: nonce,
139
+ t: t,
140
+ 'Content-Type': 'application/json',
141
+ },
142
+ };
143
+
144
+ const req = https.request(options, (res) => {
145
+ this.debugLog(`Water Heater: ${this.accessory.displayName} statusCode: ${res.statusCode}`);
146
+ this.statusCode({ res });
147
+ res.on('data', (d) => {
148
+ this.debugLog(`Water Heater: ${this.accessory.displayName} d: ${d}`);
149
+ });
150
+ });
151
+
152
+ req.on('error', (error) => {
153
+ this.errorLog(`Water Heater: ${this.accessory.displayName} error: ${error}`);
154
+ });
155
+
156
+ req.write(payload);
157
+ req.end();
158
+
159
+ this.debugLog(`Water Heater: ${this.accessory.displayName} pushchanges: ${JSON.stringify(req)}`);
125
160
  this.updateHomeKitCharacteristics();
126
161
  } catch (e: any) {
127
162
  this.errorLog(`Water Heater: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`);
@@ -134,8 +169,8 @@ export class WaterHeater {
134
169
  }
135
170
  }
136
171
 
137
- async statusCode(push: AxiosResponse<{ statusCode: number }>): Promise<void>{
138
- switch (push.data.statusCode) {
172
+ async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
173
+ switch (res.statusCode) {
139
174
  case 151:
140
175
  this.errorLog(`Water Heater: ${this.accessory.displayName} Command not supported by this device type.`);
141
176
  break;
@@ -154,7 +189,7 @@ export class WaterHeater {
154
189
  case 190:
155
190
  this.errorLog(
156
191
  `Water Heater: ${this.accessory.displayName} Device internal error due to device states not synchronized` +
157
- ` with server, Or command: ${JSON.stringify(push.data)} format is invalid`,
192
+ ` with server, Or command: ${JSON.stringify(res)} format is invalid`,
158
193
  );
159
194
  break;
160
195
  case 100:
package/src/platform.ts CHANGED
@@ -18,10 +18,13 @@ import { AirPurifier } from './irdevice/airpurifier';
18
18
  import { WaterHeater } from './irdevice/waterheater';
19
19
  import { VacuumCleaner } from './irdevice/vacuumcleaner';
20
20
  import { AirConditioner } from './irdevice/airconditioner';
21
- import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
22
- import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, Service, Characteristic } from 'homebridge';
23
- import { PLATFORM_NAME, PLUGIN_NAME, DeviceURL, irdevice, device, SwitchBotPlatformConfig, deviceResponses, devicesConfig } from './settings';
21
+ import https from 'https';
22
+ import crypto from 'crypto';
23
+ import { Buffer } from 'buffer';
24
24
  import fakegato from 'fakegato-history';
25
+ import { readFileSync, writeFileSync } from 'fs';
26
+ import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, Service, Characteristic } from 'homebridge';
27
+ import { PLATFORM_NAME, PLUGIN_NAME, irdevice, device, SwitchBotPlatformConfig, devicesConfig, DevicePath, HostDomain } from './settings';
25
28
 
26
29
  /**
27
30
  * HomebridgePlatform
@@ -35,12 +38,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
35
38
  // this is used to track restored cached accessories
36
39
  public readonly accessories: PlatformAccessory[] = [];
37
40
 
38
- public axios: AxiosInstance = axios.create({
39
- responseType: 'json',
40
- });
41
-
42
41
  version = require('../package.json').version || '1.12.8'; // eslint-disable-line @typescript-eslint/no-var-requires
43
- deviceStatus!: deviceResponses;
42
+ deviceStatus!: any;
44
43
  registeringDevice!: boolean;
45
44
  debugMode!: boolean;
46
45
  platformLogging?: string;
@@ -49,6 +48,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
49
48
 
50
49
  constructor(public readonly log: Logger, public readonly config: SwitchBotPlatformConfig, public readonly api: API) {
51
50
  this.logs();
51
+
52
52
  this.debugLog('Finished initializing platform:', this.config.name);
53
53
  // only load if configured
54
54
  if (!this.config) {
@@ -72,13 +72,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
72
72
  return;
73
73
  }
74
74
 
75
- // setup axios interceptor to add headers / api key to each request
76
- this.axios.interceptors.request.use((request: AxiosRequestConfig) => {
77
- request.headers!.Authorization = this.config.credentials?.openToken;
78
- request.headers!['Content-Type'] = 'application/json; charset=utf8';
79
- return request;
80
- });
81
-
82
75
  // import fakegato-history module
83
76
  this.fakegatoAPI = fakegato(api);
84
77
 
@@ -90,7 +83,11 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
90
83
  this.debugLog('Executed didFinishLaunching callback');
91
84
  // run the method to discover / register your devices as accessories
92
85
  try {
93
- this.discoverDevices();
86
+ if (this.config.credentials?.openToken && !this.config.credentials.token) {
87
+ await this.updateToken();
88
+ } else {
89
+ this.discoverDevices();
90
+ }
94
91
  } catch (e: any) {
95
92
  this.errorLog('Failed to Discover Devices.', JSON.stringify(e.message));
96
93
  if (this.platformLogging?.includes('debug')) {
@@ -134,7 +131,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
134
131
  /**
135
132
  * Verify the config passed to the plugin is valid
136
133
  */
137
- verifyConfig() {
134
+ async verifyConfig() {
138
135
  this.config.options = this.config.options || {};
139
136
 
140
137
  const platformConfig = {};
@@ -181,8 +178,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
181
178
  }
182
179
  }
183
180
 
184
- if (this.config.options!.refreshRate! < 30) {
185
- throw new Error('Refresh Rate must be above 30 (30 seconds).');
181
+ if (this.config.options!.refreshRate! < 5) {
182
+ throw new Error('Refresh Rate must be above 5 (5 seconds).');
186
183
  }
187
184
 
188
185
  if (!this.config.options.refreshRate) {
@@ -205,12 +202,71 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
205
202
  this.debugLog('Missing Credentials');
206
203
  }
207
204
  }
208
- if (!this.config.credentials?.openToken) {
205
+ if (!this.config.credentials?.token) {
209
206
  if (this.platformLogging?.includes('debug')) {
210
- this.errorLog('Missing openToken');
207
+ this.errorLog('Missing token');
211
208
  this.warnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
212
209
  }
213
210
  }
211
+ if (this.config.credentials?.token) {
212
+ if (!this.config.credentials?.secret) {
213
+ if (this.platformLogging?.includes('debug')) {
214
+ this.errorLog('Missing secret');
215
+ this.warnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
216
+ }
217
+ throw Error('Missing secret');
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * The openToken was old config.
225
+ * This method saves the openToken as the token in the config.json file
226
+ * @param this.config.credentials.openToken
227
+ */
228
+ async updateToken() {
229
+ try {
230
+ // check the new token was provided
231
+ if (!this.config.credentials?.openToken) {
232
+ throw new Error('New token not provided');
233
+ }
234
+
235
+ // load in the current config
236
+ const currentConfig = JSON.parse(readFileSync(this.api.user.configPath(), 'utf8'));
237
+
238
+ // check the platforms section is an array before we do array things on it
239
+ if (!Array.isArray(currentConfig.platforms)) {
240
+ throw new Error('Cannot find platforms array in config');
241
+ }
242
+
243
+ // find this plugins current config
244
+ const pluginConfig = currentConfig.platforms.find((x: { platform: string }) => x.platform === PLATFORM_NAME);
245
+
246
+ if (!pluginConfig) {
247
+ throw new Error(`Cannot find config for ${PLATFORM_NAME} in platforms array`);
248
+ }
249
+
250
+ // check the .credentials is an object before doing object things with it
251
+ if (typeof pluginConfig.credentials !== 'object') {
252
+ throw new Error('pluginConfig.credentials is not an object');
253
+ }
254
+ // Move openToken to token
255
+ this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken.');
256
+ this.warnLog('`openToken` config has been moved to the `token` config, Please restart Homebridge.');
257
+
258
+ // set the refresh token
259
+ pluginConfig.credentials.token = this.config.credentials?.openToken;
260
+ pluginConfig.credentials.openToken = 'Delete';
261
+
262
+ if (this.platformLogging?.includes('debug')) {
263
+ this.warnLog(`Token: ${pluginConfig.credentials.token}`);
264
+ }
265
+ // save the config, ensuring we maintain pretty json
266
+ writeFileSync(this.api.user.configPath(), JSON.stringify(currentConfig, null, 4));
267
+ this.verifyConfig();
268
+ } catch (e: any) {
269
+ this.errorLog(e);
214
270
  }
215
271
  }
216
272
 
@@ -219,92 +275,130 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
219
275
  */
220
276
  async discoverDevices() {
221
277
  try {
222
- if (this.config.credentials?.openToken) {
223
- const devicesAPI: any = (await this.axios.get(DeviceURL)).data;
224
- this.deviceListInfo(devicesAPI);
225
- this.debugLog(JSON.stringify(devicesAPI));
226
-
227
- // SwitchBot Devices
228
- if (devicesAPI.body.deviceList.length !== 0) {
229
- this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
230
- } else {
231
- this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
232
- }
233
- const deviceLists = devicesAPI.body.deviceList;
234
- if (!this.config.options?.devices) {
235
- this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
236
- const devices = deviceLists.map((v: any) => v);
237
- for (const device of devices) {
238
- if (device.deviceType) {
239
- if (device.configDeviceName) {
240
- device.deviceName = device.configDeviceName;
278
+ if (this.config.credentials?.token) {
279
+ const t = Date.now();
280
+ const nonce = 'requestID';
281
+ const data = this.config.credentials?.token + t + nonce;
282
+ const signTerm = crypto.createHmac('sha256', this.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
283
+ const sign = signTerm.toString('base64');
284
+ this.debugLog(sign);
285
+ const options = {
286
+ hostname: HostDomain,
287
+ port: 443,
288
+ path: DevicePath,
289
+ method: 'GET',
290
+ headers: {
291
+ 'Authorization': this.config.credentials?.token,
292
+ 'sign': sign,
293
+ 'nonce': nonce,
294
+ 't': t,
295
+ 'Content-Type': 'application/json',
296
+ },
297
+ };
298
+ const req = https.request(options, (res) => {
299
+ this.debugLog(`statusCode: ${res.statusCode}`);
300
+ this.debugLog(`headers: ${JSON.stringify(res.headers)}`);
301
+ let rawData = '';
302
+ res.on('data', (d) => {
303
+ rawData += d;
304
+ this.debugLog(`d: ${d}`);
305
+ });
306
+ res.on('end', () => {
307
+ try {
308
+ const devicesAPI = JSON.parse(rawData);
309
+ this.debugLog(`devicesAPI: ${JSON.stringify(devicesAPI.body)}`);
310
+
311
+ // SwitchBot Devices
312
+ if (devicesAPI.body.deviceList.length !== 0) {
313
+ this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
314
+ } else {
315
+ this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
241
316
  }
242
- this.createDevice(device);
243
- }
244
- }
245
- } else if (this.config.credentials?.openToken && this.config.options.devices) {
246
- this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
247
- const deviceConfigs = this.config.options?.devices;
248
-
249
- const mergeBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
250
- a1.map((itm: { deviceId: string }) => ({
251
- ...a2.find(
252
- (item: { deviceId: string }) =>
253
- item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
254
- ),
255
- ...itm,
256
- }));
257
-
258
- const devices = mergeBydeviceId(deviceLists, deviceConfigs);
259
- this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`);
260
- for (const device of devices) {
261
- if (device.deviceType) {
262
- if (device.configDeviceName) {
263
- device.deviceName = device.configDeviceName;
317
+ const deviceLists = devicesAPI.body.deviceList;
318
+ if (!this.config.options?.devices) {
319
+ this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
320
+ const devices = deviceLists.map((v: any) => v);
321
+ for (const device of devices) {
322
+ if (device.deviceType) {
323
+ if (device.configDeviceName) {
324
+ device.deviceName = device.configDeviceName;
325
+ }
326
+ this.createDevice(device);
327
+ }
328
+ }
329
+ } else if (this.config.credentials?.token && this.config.options.devices) {
330
+ this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
331
+ const deviceConfigs = this.config.options?.devices;
332
+
333
+ const mergeBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
334
+ a1.map((itm: { deviceId: string }) => ({
335
+ ...a2.find(
336
+ (item: { deviceId: string }) =>
337
+ item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
338
+ ),
339
+ ...itm,
340
+ }));
341
+
342
+ const devices = mergeBydeviceId(deviceLists, deviceConfigs);
343
+ this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`);
344
+ for (const device of devices) {
345
+ if (device.deviceType) {
346
+ if (device.configDeviceName) {
347
+ device.deviceName = device.configDeviceName;
348
+ }
349
+ this.createDevice(device);
350
+ }
351
+ }
352
+ } else {
353
+ this.errorLog('SwitchBot Token Supplied, Issue with Auth.');
264
354
  }
265
- this.createDevice(device);
266
- }
267
- }
268
- } else {
269
- this.errorLog('Neither SwitchBot OpenToken or Device Config are not set.');
270
- }
271
- // IR Devices
272
- if (devicesAPI.body.infraredRemoteList.length !== 0) {
273
- this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
274
- } else {
275
- this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
276
- }
277
- const irDeviceLists = devicesAPI.body.infraredRemoteList;
278
- if (!this.config.options?.irdevices) {
279
- this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`);
280
- const devices = irDeviceLists.map((v: any) => v);
281
- for (const device of devices) {
282
- if (device.remoteType) {
283
- this.createIRDevice(device);
284
- }
285
- }
286
- } else {
287
- this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`);
288
- const irDeviceConfig = this.config.options?.irdevices;
289
-
290
- const mergeIRBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
291
- a1.map((itm: { deviceId: string }) => ({
292
- ...a2.find(
293
- (item: { deviceId: string }) =>
294
- item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
295
- ),
296
- ...itm,
297
- }));
298
-
299
- const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig);
300
- this.debugLog(`IR Devices: ${JSON.stringify(devices)}`);
301
- for (const device of devices) {
302
- if (device.remoteType) {
303
- this.createIRDevice(device);
355
+
356
+ // IR Devices
357
+ if (devicesAPI.body.infraredRemoteList.length !== 0) {
358
+ this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
359
+ } else {
360
+ this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
361
+ }
362
+ const irDeviceLists = devicesAPI.body.infraredRemoteList;
363
+ if (!this.config.options?.irdevices) {
364
+ this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`);
365
+ const devices = irDeviceLists.map((v: any) => v);
366
+ for (const device of devices) {
367
+ if (device.remoteType) {
368
+ this.createIRDevice(device);
369
+ }
370
+ }
371
+ } else {
372
+ this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`);
373
+ const irDeviceConfig = this.config.options?.irdevices;
374
+
375
+ const mergeIRBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
376
+ a1.map((itm: { deviceId: string }) => ({
377
+ ...a2.find(
378
+ (item: { deviceId: string }) =>
379
+ item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
380
+ ),
381
+ ...itm,
382
+ }));
383
+
384
+ const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig);
385
+ this.debugLog(`IR Devices: ${JSON.stringify(devices)}`);
386
+ for (const device of devices) {
387
+ if (device.remoteType) {
388
+ this.createIRDevice(device);
389
+ }
390
+ }
391
+ }
392
+ } catch (e:any) {
393
+ this.errorLog(e.message);
304
394
  }
305
- }
306
- }
307
- } else if (!this.config.credentials?.openToken && this.config.options?.devices) {
395
+ });
396
+ });
397
+ req.on('error', (error) => {
398
+ this.errorLog(error);
399
+ });
400
+ req.end();
401
+ } else if (!this.config.credentials?.token && this.config.options?.devices) {
308
402
  this.debugLog(`SwitchBot Device Manual Config Set: ${JSON.stringify(this.config.options?.devices)}`);
309
403
  const deviceConfigs = this.config.options?.devices;
310
404
  const devices = deviceConfigs.map((v: any) => v);
@@ -316,7 +410,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
316
410
  }
317
411
  }
318
412
  } else {
319
- this.errorLog('Neither SwitchBot OpenToken or Device Config are not set.');
413
+ this.errorLog('Neither SwitchBot Token or Device Config are not set.');
320
414
  }
321
415
  } catch (e: any) {
322
416
  this.deviceError(e);
@@ -1676,7 +1770,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1676
1770
  this.warnLog(`Removing existing accessory from cache: ${existingAccessory.displayName}`);
1677
1771
  }
1678
1772
 
1679
- public deviceListInfo(devices: deviceResponses) {
1773
+ public deviceListInfo(devices) {
1680
1774
  if (this.platformLogging?.includes('debug')) {
1681
1775
  this.warnLog(`deviceListInfoStatus: ${JSON.stringify(devices)}`);
1682
1776
  }
@@ -1685,7 +1779,38 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
1685
1779
  public async deviceInfo(device: (irdevice & devicesConfig) | (device & devicesConfig)) {
1686
1780
  if (this.platformLogging?.includes('debug')) {
1687
1781
  this.warnLog(JSON.stringify(device));
1688
- this.deviceStatus = (await this.axios.get(`${DeviceURL}/${device.deviceId}/status`)).data;
1782
+
1783
+ const t = Date.now();
1784
+ const nonce = 'requestID';
1785
+ const data = this.config.credentials?.token + t + nonce;
1786
+ const signTerm = crypto.createHmac('sha256', this.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
1787
+ const sign = signTerm.toString('base64');
1788
+ this.debugLog(sign);
1789
+ const options = {
1790
+ hostname: HostDomain,
1791
+ port: 443,
1792
+ path: `${DevicePath}/${device.deviceId}/status`,
1793
+ method: 'GET',
1794
+ headers: {
1795
+ Authorization: this.config.credentials?.token,
1796
+ sign: sign,
1797
+ nonce: nonce,
1798
+ t: t,
1799
+ 'Content-Type': 'application/json',
1800
+ },
1801
+ };
1802
+ const req = https.request(options, (res) => {
1803
+ this.debugLog(`statusCode: ${res.statusCode}`);
1804
+ res.on('data', (d) => {
1805
+ process.stdout.write(d);
1806
+ });
1807
+ });
1808
+ req.on('error', (error) => {
1809
+ this.errorLog(error);
1810
+ });
1811
+ req.end();
1812
+
1813
+ this.deviceStatus = req;
1689
1814
  if (this.deviceStatus.message === 'success') {
1690
1815
  this.warnLog(`${device.deviceName} deviceInfoStatus: ${JSON.stringify(this.deviceStatus)}`);
1691
1816
  } else {
package/src/settings.ts CHANGED
@@ -13,12 +13,12 @@ export const PLUGIN_NAME = '@switchbot/homebridge-switchbot';
13
13
  /**
14
14
  * This is the main url used to access SwitchBot API
15
15
  */
16
- export const AuthURL = 'https://api.switch-bot.com';
16
+ export const HostDomain = 'api.switch-bot.com';
17
17
 
18
18
  /**
19
19
  * This is the main url used to access SwitchBot API
20
20
  */
21
- export const DeviceURL = 'https://api.switch-bot.com/v1.0/devices';
21
+ export const DevicePath = '/v1.1/devices';
22
22
 
23
23
  //Config
24
24
  export interface SwitchBotPlatformConfig extends PlatformConfig {
@@ -27,6 +27,8 @@ export interface SwitchBotPlatformConfig extends PlatformConfig {
27
27
  }
28
28
 
29
29
  export type credentials = {
30
+ token?: any;
31
+ secret?: any;
30
32
  openToken?: any;
31
33
  };
32
34
 
@@ -175,23 +177,6 @@ export type other = {
175
177
  commandOff?: string;
176
178
  };
177
179
 
178
- export type payload = {
179
- commandType: string;
180
- parameter: string;
181
- command: string;
182
- };
183
-
184
- export interface AxiosRequestConfig {
185
- params?: Record<string, unknown>;
186
- headers?: any;
187
- }
188
-
189
- export type deviceResponses = {
190
- statusCode: number | string;
191
- message: string;
192
- body: deviceList | infraredRemoteList;
193
- };
194
-
195
180
  //a list of physical devices.
196
181
  export type deviceList = {
197
182
  device: Array<device>;
@@ -234,12 +219,6 @@ export type irdevice = {
234
219
  hubDeviceId: string; //remote device's parent Hub ID
235
220
  };
236
221
 
237
- export type deviceStatusResponse = {
238
- statusCode: number;
239
- message: string;
240
- body: deviceStatus;
241
- };
242
-
243
222
  export type deviceStatus = {
244
223
  //device ID.
245
224
  deviceId: string;