@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.
- package/.vscode/tasks.json +13 -0
- package/CHANGELOG.md +4 -2
- package/README.md +18 -9
- package/dist/device/blindtilt.d.ts +1 -5
- package/dist/device/blindtilt.d.ts.map +1 -1
- package/dist/device/blindtilt.js +80 -140
- package/dist/device/blindtilt.js.map +1 -1
- package/dist/device/bot.d.ts +1 -5
- package/dist/device/bot.d.ts.map +1 -1
- package/dist/device/bot.js +79 -138
- package/dist/device/bot.js.map +1 -1
- package/dist/device/ceilinglight.d.ts +2 -6
- package/dist/device/ceilinglight.d.ts.map +1 -1
- package/dist/device/ceilinglight.js +157 -304
- package/dist/device/ceilinglight.js.map +1 -1
- package/dist/device/colorbulb.d.ts +2 -6
- package/dist/device/colorbulb.d.ts.map +1 -1
- package/dist/device/colorbulb.js +164 -311
- package/dist/device/colorbulb.js.map +1 -1
- package/dist/device/contact.d.ts +1 -5
- package/dist/device/contact.d.ts.map +1 -1
- package/dist/device/contact.js +36 -72
- package/dist/device/contact.js.map +1 -1
- package/dist/device/curtain.d.ts +1 -5
- package/dist/device/curtain.d.ts.map +1 -1
- package/dist/device/curtain.js +78 -138
- package/dist/device/curtain.js.map +1 -1
- package/dist/device/hub.d.ts +2 -6
- package/dist/device/hub.d.ts.map +1 -1
- package/dist/device/hub.js +30 -63
- package/dist/device/hub.js.map +1 -1
- package/dist/device/humidifier.d.ts +1 -5
- package/dist/device/humidifier.d.ts.map +1 -1
- package/dist/device/humidifier.js +115 -228
- package/dist/device/humidifier.js.map +1 -1
- package/dist/device/iosensor.d.ts +1 -5
- package/dist/device/iosensor.d.ts.map +1 -1
- package/dist/device/iosensor.js +38 -74
- package/dist/device/iosensor.js.map +1 -1
- package/dist/device/{striplight.d.ts → lightstrip.d.ts} +2 -10
- package/dist/device/lightstrip.d.ts.map +1 -0
- package/dist/device/{striplight.js → lightstrip.js} +138 -194
- package/dist/device/lightstrip.js.map +1 -0
- package/dist/device/lock.d.ts +1 -5
- package/dist/device/lock.d.ts.map +1 -1
- package/dist/device/lock.js +66 -126
- package/dist/device/lock.js.map +1 -1
- package/dist/device/meter.d.ts +1 -5
- package/dist/device/meter.d.ts.map +1 -1
- package/dist/device/meter.js +38 -74
- package/dist/device/meter.js.map +1 -1
- package/dist/device/meterplus.d.ts +1 -5
- package/dist/device/meterplus.d.ts.map +1 -1
- package/dist/device/meterplus.js +39 -75
- package/dist/device/meterplus.js.map +1 -1
- package/dist/device/motion.d.ts +1 -5
- package/dist/device/motion.d.ts.map +1 -1
- package/dist/device/motion.js +35 -72
- package/dist/device/motion.js.map +1 -1
- package/dist/device/plug.d.ts +1 -5
- package/dist/device/plug.d.ts.map +1 -1
- package/dist/device/plug.js +65 -126
- package/dist/device/plug.js.map +1 -1
- package/dist/device/robotvacuumcleaner.d.ts +1 -5
- package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
- package/dist/device/robotvacuumcleaner.js +79 -151
- package/dist/device/robotvacuumcleaner.js.map +1 -1
- package/dist/irdevice/airconditioner.d.ts +3 -7
- package/dist/irdevice/airconditioner.d.ts.map +1 -1
- package/dist/irdevice/airconditioner.js +34 -62
- package/dist/irdevice/airconditioner.js.map +1 -1
- package/dist/irdevice/airpurifier.d.ts +3 -7
- package/dist/irdevice/airpurifier.d.ts.map +1 -1
- package/dist/irdevice/airpurifier.js +31 -59
- package/dist/irdevice/airpurifier.js.map +1 -1
- package/dist/irdevice/camera.d.ts +1 -5
- package/dist/irdevice/camera.d.ts.map +1 -1
- package/dist/irdevice/camera.js +30 -57
- package/dist/irdevice/camera.js.map +1 -1
- package/dist/irdevice/fan.d.ts +2 -6
- package/dist/irdevice/fan.d.ts.map +1 -1
- package/dist/irdevice/fan.js +39 -66
- package/dist/irdevice/fan.js.map +1 -1
- package/dist/irdevice/light.d.ts +1 -5
- package/dist/irdevice/light.d.ts.map +1 -1
- package/dist/irdevice/light.js +32 -59
- package/dist/irdevice/light.js.map +1 -1
- package/dist/irdevice/other.d.ts +1 -5
- package/dist/irdevice/other.d.ts.map +1 -1
- package/dist/irdevice/other.js +30 -57
- package/dist/irdevice/other.js.map +1 -1
- package/dist/irdevice/tv.d.ts +3 -7
- package/dist/irdevice/tv.d.ts.map +1 -1
- package/dist/irdevice/tv.js +46 -76
- package/dist/irdevice/tv.js.map +1 -1
- package/dist/irdevice/vacuumcleaner.d.ts +2 -6
- package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
- package/dist/irdevice/vacuumcleaner.js +30 -58
- package/dist/irdevice/vacuumcleaner.js.map +1 -1
- package/dist/irdevice/waterheater.d.ts +7 -23
- package/dist/irdevice/waterheater.d.ts.map +1 -1
- package/dist/irdevice/waterheater.js +41 -74
- package/dist/irdevice/waterheater.js.map +1 -1
- package/dist/platform.d.ts +14 -5
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +156 -149
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +4 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +5 -1
- package/dist/settings.js.map +1 -1
- package/nodemon.json +1 -1
- package/package.json +10 -10
- package/src/device/blindtilt.ts +122 -181
- package/src/device/bot.ts +96 -149
- package/src/device/ceilinglight.ts +172 -315
- package/src/device/colorbulb.ts +178 -324
- package/src/device/contact.ts +48 -78
- package/src/device/curtain.ts +93 -148
- package/src/device/hub.ts +34 -63
- package/src/device/humidifier.ts +135 -243
- package/src/device/iosensor.ts +49 -79
- package/src/device/{striplight.ts → lightstrip.ts} +148 -200
- package/src/device/lock.ts +78 -134
- package/src/device/meter.ts +48 -78
- package/src/device/meterplus.ts +50 -80
- package/src/device/motion.ts +46 -77
- package/src/device/plug.ts +76 -134
- package/src/device/robotvacuumcleaner.ts +91 -159
- package/src/irdevice/airconditioner.ts +56 -81
- package/src/irdevice/airpurifier.ts +41 -67
- package/src/irdevice/camera.ts +35 -60
- package/src/irdevice/fan.ts +46 -71
- package/src/irdevice/light.ts +38 -63
- package/src/irdevice/other.ts +35 -60
- package/src/irdevice/tv.ts +52 -80
- package/src/irdevice/vacuumcleaner.ts +36 -62
- package/src/irdevice/waterheater.ts +46 -76
- package/src/platform.ts +144 -158
- package/src/settings.ts +5 -0
- package/.github/npm-version-script.js +0 -81
- package/dist/device/striplight.d.ts.map +0 -1
- package/dist/device/striplight.js.map +0 -1
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
89
|
+
const body = JSON.stringify({
|
|
93
90
|
'command': command,
|
|
94
91
|
'parameter': 'default',
|
|
95
92
|
'commandType': commandType,
|
|
96
93
|
});
|
|
97
|
-
await this.pushChanges(
|
|
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
|
-
|
|
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 =
|
|
104
|
+
const body = JSON.stringify({
|
|
108
105
|
'command': command,
|
|
109
106
|
'parameter': 'default',
|
|
110
107
|
'commandType': commandType,
|
|
111
108
|
});
|
|
112
|
-
await this.pushChanges(
|
|
109
|
+
await this.pushChanges(body);
|
|
113
110
|
}
|
|
114
111
|
}
|
|
115
112
|
|
|
116
|
-
async pushChanges(
|
|
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
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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(
|
|
159
|
-
this.errorLog({
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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(
|
|
229
|
-
switch (
|
|
195
|
+
async statusCode(statusCode: number): Promise<void> {
|
|
196
|
+
switch (statusCode) {
|
|
230
197
|
case 151:
|
|
231
|
-
this.errorLog(
|
|
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(
|
|
201
|
+
this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
|
|
235
202
|
break;
|
|
236
203
|
case 160:
|
|
237
|
-
this.errorLog(
|
|
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(
|
|
207
|
+
this.errorLog(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
|
|
241
208
|
break;
|
|
242
209
|
case 171:
|
|
243
|
-
this.errorLog(
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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: ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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/
|
|
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
|
|
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
|
-
|
|
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,
|
|
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: ${
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
this.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
-
|
|
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: ${
|
|
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(
|
|
2128
|
-
switch (
|
|
2115
|
+
async statusCode(statusCode: number): Promise<void> {
|
|
2116
|
+
switch (statusCode) {
|
|
2129
2117
|
case 151:
|
|
2130
|
-
this.errorLog(
|
|
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(
|
|
2122
|
+
this.errorLog(`Device not found, statusCode: ${statusCode}`);
|
|
2134
2123
|
break;
|
|
2135
2124
|
case 160:
|
|
2136
|
-
this.errorLog(
|
|
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(
|
|
2128
|
+
this.errorLog(`Device is offline, statusCode: ${statusCode}`);
|
|
2140
2129
|
break;
|
|
2141
2130
|
case 171:
|
|
2142
|
-
this.errorLog(
|
|
2131
|
+
this.errorLog(`is offline, statusCode: ${statusCode}`);
|
|
2143
2132
|
break;
|
|
2144
2133
|
case 190:
|
|
2145
|
-
this.errorLog(
|
|
2134
|
+
this.errorLog(`Requests reached the daily limit, statusCode: ${statusCode}`);
|
|
2146
2135
|
break;
|
|
2147
2136
|
case 100:
|
|
2148
|
-
|
|
2149
|
-
|
|
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
|
-
|
|
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
|
*/
|