@switchbot/homebridge-switchbot 3.7.1-beta.1 → 3.7.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.
- package/CHANGELOG.md +8 -0
- package/config.schema.json +7 -1
- package/dist/device/blindtilt.d.ts +3 -1
- package/dist/device/blindtilt.d.ts.map +1 -1
- package/dist/device/blindtilt.js +61 -28
- package/dist/device/blindtilt.js.map +1 -1
- package/dist/device/bot.d.ts +1 -0
- package/dist/device/bot.d.ts.map +1 -1
- package/dist/device/bot.js +102 -80
- package/dist/device/bot.js.map +1 -1
- package/dist/device/ceilinglight.d.ts +1 -0
- package/dist/device/ceilinglight.d.ts.map +1 -1
- package/dist/device/ceilinglight.js +53 -28
- package/dist/device/ceilinglight.js.map +1 -1
- package/dist/device/colorbulb.d.ts +1 -0
- package/dist/device/colorbulb.d.ts.map +1 -1
- package/dist/device/colorbulb.js +53 -28
- package/dist/device/colorbulb.js.map +1 -1
- package/dist/device/contact.d.ts +1 -0
- package/dist/device/contact.d.ts.map +1 -1
- package/dist/device/contact.js +53 -28
- package/dist/device/contact.js.map +1 -1
- package/dist/device/curtain.d.ts +2 -0
- package/dist/device/curtain.d.ts.map +1 -1
- package/dist/device/curtain.js +74 -43
- package/dist/device/curtain.js.map +1 -1
- package/dist/device/device.d.ts.map +1 -1
- package/dist/device/device.js +247 -394
- package/dist/device/device.js.map +1 -1
- package/dist/device/fan.d.ts +1 -0
- package/dist/device/fan.d.ts.map +1 -1
- package/dist/device/fan.js +53 -28
- package/dist/device/fan.js.map +1 -1
- package/dist/device/hub.d.ts +1 -0
- package/dist/device/hub.d.ts.map +1 -1
- package/dist/device/hub.js +53 -28
- package/dist/device/hub.js.map +1 -1
- package/dist/device/humidifier.d.ts +1 -0
- package/dist/device/humidifier.d.ts.map +1 -1
- package/dist/device/humidifier.js +53 -28
- package/dist/device/humidifier.js.map +1 -1
- package/dist/device/iosensor.d.ts +1 -0
- package/dist/device/iosensor.d.ts.map +1 -1
- package/dist/device/iosensor.js +53 -28
- package/dist/device/iosensor.js.map +1 -1
- package/dist/device/lightstrip.d.ts +1 -0
- package/dist/device/lightstrip.d.ts.map +1 -1
- package/dist/device/lightstrip.js +53 -28
- package/dist/device/lightstrip.js.map +1 -1
- package/dist/device/lock.d.ts +1 -0
- package/dist/device/lock.d.ts.map +1 -1
- package/dist/device/lock.js +54 -29
- package/dist/device/lock.js.map +1 -1
- package/dist/device/meter.d.ts +1 -0
- package/dist/device/meter.d.ts.map +1 -1
- package/dist/device/meter.js +53 -28
- package/dist/device/meter.js.map +1 -1
- package/dist/device/meterplus.d.ts +1 -0
- package/dist/device/meterplus.d.ts.map +1 -1
- package/dist/device/meterplus.js +53 -28
- package/dist/device/meterplus.js.map +1 -1
- package/dist/device/motion.d.ts +1 -0
- package/dist/device/motion.d.ts.map +1 -1
- package/dist/device/motion.js +53 -28
- package/dist/device/motion.js.map +1 -1
- package/dist/device/plug.d.ts +1 -0
- package/dist/device/plug.d.ts.map +1 -1
- package/dist/device/plug.js +54 -29
- package/dist/device/plug.js.map +1 -1
- package/dist/device/robotvacuumcleaner.d.ts +1 -0
- package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
- package/dist/device/robotvacuumcleaner.js +53 -28
- package/dist/device/robotvacuumcleaner.js.map +1 -1
- package/dist/device/waterdetector.d.ts +1 -0
- package/dist/device/waterdetector.d.ts.map +1 -1
- package/dist/device/waterdetector.js +53 -28
- package/dist/device/waterdetector.js.map +1 -1
- package/dist/irdevice/irdevice.d.ts.map +1 -1
- package/dist/irdevice/irdevice.js +30 -133
- package/dist/irdevice/irdevice.js.map +1 -1
- package/dist/platform.d.ts +19 -18
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +369 -524
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +1 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/package.json +13 -13
- package/src/device/blindtilt.ts +59 -27
- package/src/device/bot.ts +98 -77
- package/src/device/ceilinglight.ts +51 -27
- package/src/device/colorbulb.ts +50 -26
- package/src/device/contact.ts +50 -26
- package/src/device/curtain.ts +71 -40
- package/src/device/device.ts +283 -395
- package/src/device/fan.ts +50 -26
- package/src/device/hub.ts +50 -26
- package/src/device/humidifier.ts +50 -26
- package/src/device/iosensor.ts +50 -26
- package/src/device/lightstrip.ts +50 -26
- package/src/device/lock.ts +51 -27
- package/src/device/meter.ts +50 -26
- package/src/device/meterplus.ts +50 -26
- package/src/device/motion.ts +50 -26
- package/src/device/plug.ts +51 -27
- package/src/device/robotvacuumcleaner.ts +50 -26
- package/src/device/waterdetector.ts +50 -26
- package/src/irdevice/irdevice.ts +54 -134
- package/src/platform.ts +379 -529
- package/src/settings.ts +1 -0
package/src/platform.ts
CHANGED
|
@@ -51,6 +51,10 @@ import type { blindTilt, curtain, curtain3, device } from './types/devicelist.js
|
|
|
51
51
|
import type { API, DynamicPlatformPlugin, Logging, PlatformAccessory } from 'homebridge';
|
|
52
52
|
import type { SwitchBotPlatformConfig, devicesConfig, irDevicesConfig } from './settings.js';
|
|
53
53
|
import type { IncomingMessage, Server, ServerResponse } from 'http';
|
|
54
|
+
/*
|
|
55
|
+
* For Testing Locally:
|
|
56
|
+
* import { SwitchBotModel } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js';
|
|
57
|
+
*/
|
|
54
58
|
import { SwitchBotModel } from 'node-switchbot';
|
|
55
59
|
|
|
56
60
|
/**
|
|
@@ -59,10 +63,12 @@ import { SwitchBotModel } from 'node-switchbot';
|
|
|
59
63
|
* parse the user config and discover/register accessories with Homebridge.
|
|
60
64
|
*/
|
|
61
65
|
export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
62
|
-
|
|
66
|
+
// Platform properties
|
|
67
|
+
public accessories: PlatformAccessory[] = [];
|
|
63
68
|
public readonly api: API;
|
|
64
69
|
public readonly log: Logging;
|
|
65
70
|
|
|
71
|
+
// Configuration properties
|
|
66
72
|
version!: string;
|
|
67
73
|
Logging?: string;
|
|
68
74
|
debugMode!: boolean;
|
|
@@ -72,11 +78,15 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
72
78
|
platformLogging!: SwitchBotPlatformConfig['logging'];
|
|
73
79
|
config!: SwitchBotPlatformConfig;
|
|
74
80
|
|
|
81
|
+
// MQTT and Webhook properties
|
|
75
82
|
mqttClient: MqttClient | null = null;
|
|
76
83
|
webhookEventListener: Server | null = null;
|
|
77
84
|
|
|
85
|
+
// External APIs
|
|
78
86
|
public readonly eve: any;
|
|
79
87
|
public readonly fakegatoAPI: any;
|
|
88
|
+
|
|
89
|
+
// Event Handlers
|
|
80
90
|
public readonly webhookEventHandler: { [x: string]: (context: any) => void } = {};
|
|
81
91
|
public readonly bleEventHandler: { [x: string]: (context: any) => void } = {};
|
|
82
92
|
|
|
@@ -85,8 +95,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
85
95
|
config: SwitchBotPlatformConfig,
|
|
86
96
|
api: API,
|
|
87
97
|
) {
|
|
88
|
-
// initialize
|
|
89
|
-
this.accessories = [];
|
|
90
98
|
this.api = api;
|
|
91
99
|
this.log = log;
|
|
92
100
|
|
|
@@ -130,26 +138,38 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
130
138
|
// in order to ensure they weren't added to homebridge already. This event can also be used
|
|
131
139
|
// to start discovery of new accessories.
|
|
132
140
|
this.api.on('didFinishLaunching', async () => {
|
|
133
|
-
this.debugLog('Executed didFinishLaunching callback');
|
|
141
|
+
await this.debugLog('Executed didFinishLaunching callback');
|
|
134
142
|
// run the method to discover / register your devices as accessories
|
|
135
143
|
try {
|
|
136
144
|
if (this.config.credentials?.openToken && !this.config.credentials.token) {
|
|
137
145
|
await this.updateToken();
|
|
138
146
|
} else if (this.config.credentials?.token && !this.config.credentials?.secret) {
|
|
139
147
|
|
|
140
|
-
this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.');
|
|
148
|
+
await this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.');
|
|
141
149
|
} else {
|
|
142
|
-
this.discoverDevices();
|
|
150
|
+
await this.discoverDevices();
|
|
143
151
|
}
|
|
144
152
|
} catch (e: any) {
|
|
145
|
-
this.errorLog(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
|
|
153
|
+
await this.errorLog(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
|
|
146
154
|
await this.debugErrorLog(`Failed to Discover, Error: ${e}`);
|
|
147
155
|
}
|
|
148
156
|
});
|
|
149
157
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
try {
|
|
159
|
+
this.setupMqtt();
|
|
160
|
+
} catch (e: any) {
|
|
161
|
+
this.errorLog(`Setup MQTT, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
this.setupwebhook();
|
|
165
|
+
} catch (e: any) {
|
|
166
|
+
this.errorLog(`Setup Webhook, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
this.setupBlE();
|
|
170
|
+
} catch (e: any) {
|
|
171
|
+
this.errorLog(`Setup Platform BLE, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
|
|
172
|
+
}
|
|
153
173
|
}
|
|
154
174
|
|
|
155
175
|
async setupMqtt(): Promise<void> {
|
|
@@ -167,7 +187,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
167
187
|
this.mqttClient.subscribe('homebridge-switchbot/webhook/+');
|
|
168
188
|
this.mqttClient.on('message', async (topic: string, message) => {
|
|
169
189
|
try {
|
|
170
|
-
await
|
|
190
|
+
await this.debugLog(`Received Webhook via MQTT: ${topic}=${message}`);
|
|
171
191
|
const context = JSON.parse(message.toString());
|
|
172
192
|
this.webhookEventHandler[context.deviceMac]?.(context);
|
|
173
193
|
} catch (e: any) {
|
|
@@ -199,16 +219,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
199
219
|
const body = JSON.parse(data);
|
|
200
220
|
await this.debugLog(`Received Webhook: ${JSON.stringify(body)}`);
|
|
201
221
|
if (this.config.options?.mqttURL) {
|
|
202
|
-
const mac = body.context.deviceMac
|
|
203
|
-
?.toLowerCase()
|
|
204
|
-
.match(/[\s\S]{1,2}/g)
|
|
205
|
-
?.join(':');
|
|
222
|
+
const mac = body.context.deviceMac?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
|
|
206
223
|
const options = this.config.options?.mqttPubOptions || {};
|
|
207
224
|
this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options);
|
|
208
225
|
}
|
|
209
226
|
this.webhookEventHandler[body.context.deviceMac]?.(body.context);
|
|
210
227
|
} catch (e: any) {
|
|
211
|
-
this.errorLog(`Failed to handle webhook event. Error:${e}`);
|
|
228
|
+
await this.errorLog(`Failed to handle webhook event. Error:${e}`);
|
|
212
229
|
}
|
|
213
230
|
});
|
|
214
231
|
response.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
@@ -223,7 +240,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
223
240
|
}
|
|
224
241
|
}).listen(port ? port : 80);
|
|
225
242
|
} catch (e: any) {
|
|
226
|
-
this.errorLog(`Failed to create webhook listener. Error:${e.message}`);
|
|
243
|
+
await this.errorLog(`Failed to create webhook listener. Error:${e.message}`);
|
|
227
244
|
return;
|
|
228
245
|
}
|
|
229
246
|
|
|
@@ -238,15 +255,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
238
255
|
}),
|
|
239
256
|
});
|
|
240
257
|
const response: any = await body.json();
|
|
241
|
-
await this.debugLog(`setupWebhook: url:${url}`);
|
|
242
|
-
await this.debugLog(`setupWebhook: body:${JSON.stringify(response)}`);
|
|
243
|
-
await this.debugLog(`setupWebhook: statusCode:${statusCode}`);
|
|
258
|
+
await this.debugLog(`setupWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
|
|
244
259
|
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
245
|
-
this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} `
|
|
260
|
+
await this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} `
|
|
246
261
|
+ `message:${response?.message}`);
|
|
247
262
|
}
|
|
248
263
|
} catch (e: any) {
|
|
249
|
-
this.errorLog(`Failed to configure webhook. Error: ${e.message}`);
|
|
264
|
+
await this.errorLog(`Failed to configure webhook. Error: ${e.message}`);
|
|
250
265
|
}
|
|
251
266
|
|
|
252
267
|
try {
|
|
@@ -260,14 +275,12 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
260
275
|
}),
|
|
261
276
|
});
|
|
262
277
|
const response: any = await body.json();
|
|
263
|
-
await this.debugLog(`updateWebhook: url:${url}`);
|
|
264
|
-
await this.debugLog(`updateWebhook: body:${JSON.stringify(response)}`);
|
|
265
|
-
await this.debugLog(`updateWebhook: statusCode:${statusCode}`);
|
|
278
|
+
await this.debugLog(`updateWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
|
|
266
279
|
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
267
|
-
this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
280
|
+
await this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
268
281
|
}
|
|
269
282
|
} catch (e: any) {
|
|
270
|
-
this.errorLog(`Failed to update webhook. Error:${e.message}`);
|
|
283
|
+
await this.errorLog(`Failed to update webhook. Error:${e.message}`);
|
|
271
284
|
}
|
|
272
285
|
|
|
273
286
|
try {
|
|
@@ -282,12 +295,12 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
282
295
|
await this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`);
|
|
283
296
|
await this.debugLog(`queryWebhook: statusCode:${statusCode}`);
|
|
284
297
|
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
285
|
-
this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
298
|
+
await this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
286
299
|
} else {
|
|
287
|
-
this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`);
|
|
300
|
+
await this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`);
|
|
288
301
|
}
|
|
289
302
|
} catch (e: any) {
|
|
290
|
-
this.errorLog(`Failed to query webhook. Error:${e}`);
|
|
303
|
+
await this.errorLog(`Failed to query webhook. Error:${e}`);
|
|
291
304
|
}
|
|
292
305
|
|
|
293
306
|
this.api.on('shutdown', async () => {
|
|
@@ -303,12 +316,12 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
303
316
|
const response: any = await body.json();
|
|
304
317
|
await this.debugLog(`deleteWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
|
|
305
318
|
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
306
|
-
this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
319
|
+
await this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
307
320
|
} else {
|
|
308
|
-
this.infoLog('Unregistered webhook to close listening.');
|
|
321
|
+
await this.infoLog('Unregistered webhook to close listening.');
|
|
309
322
|
}
|
|
310
323
|
} catch (e: any) {
|
|
311
|
-
this.errorLog(`Failed to delete webhook. Error:${e.message}`);
|
|
324
|
+
await this.errorLog(`Failed to delete webhook. Error:${e.message}`);
|
|
312
325
|
}
|
|
313
326
|
});
|
|
314
327
|
}
|
|
@@ -340,9 +353,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
340
353
|
this.api.on('shutdown', async () => {
|
|
341
354
|
try {
|
|
342
355
|
switchbot.stopScan();
|
|
343
|
-
this.infoLog('Stopped BLE scanning to close listening.');
|
|
356
|
+
await this.infoLog('Stopped BLE scanning to close listening.');
|
|
344
357
|
} catch (e: any) {
|
|
345
|
-
this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message}`);
|
|
358
|
+
await this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message}`);
|
|
346
359
|
}
|
|
347
360
|
});
|
|
348
361
|
}
|
|
@@ -356,7 +369,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
356
369
|
* It should be used to setup event handlers for characteristics and update respective values.
|
|
357
370
|
*/
|
|
358
371
|
async configureAccessory(accessory: PlatformAccessory) {
|
|
359
|
-
|
|
372
|
+
const { displayName } = accessory;
|
|
373
|
+
await this.debugLog(`Loading accessory from cache: ${displayName}`);
|
|
360
374
|
|
|
361
375
|
// add the restored accessory to the accessories cache so we can track if it has already been registered
|
|
362
376
|
this.accessories.push(accessory);
|
|
@@ -366,7 +380,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
366
380
|
* Verify the config passed to the plugin is valid
|
|
367
381
|
*/
|
|
368
382
|
async verifyConfig() {
|
|
369
|
-
this.debugLog('Verifying Config');
|
|
383
|
+
await this.debugLog('Verifying Config');
|
|
370
384
|
this.config = this.config || {};
|
|
371
385
|
this.config.options = this.config.options || {};
|
|
372
386
|
|
|
@@ -404,10 +418,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
404
418
|
for (const irDeviceConfig of this.config.options.irdevices) {
|
|
405
419
|
if (!irDeviceConfig.hide_device) {
|
|
406
420
|
if (!irDeviceConfig.deviceId) {
|
|
407
|
-
this.errorLog('The devices config section is missing the *Device ID* in the config. Please check your config.');
|
|
421
|
+
await this.errorLog('The devices config section is missing the *Device ID* in the config. Please check your config.');
|
|
408
422
|
}
|
|
409
423
|
if (!irDeviceConfig.deviceId && !irDeviceConfig.configRemoteType) {
|
|
410
|
-
this.errorLog('The devices config section is missing the *Device Type* in the config. Please check your config.');
|
|
424
|
+
await this.errorLog('The devices config section is missing the *Device Type* in the config. Please check your config.');
|
|
411
425
|
}
|
|
412
426
|
}
|
|
413
427
|
}
|
|
@@ -421,18 +435,18 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
421
435
|
if (!this.config.options.refreshRate) {
|
|
422
436
|
// default 120 seconds (2 minutes)
|
|
423
437
|
this.config.options!.refreshRate! = 120;
|
|
424
|
-
this.debugWarnLog('Using Default Refresh Rate (2 minutes).');
|
|
438
|
+
await this.debugWarnLog('Using Default Refresh Rate (2 minutes).');
|
|
425
439
|
}
|
|
426
440
|
|
|
427
441
|
if (!this.config.options.pushRate) {
|
|
428
442
|
// default 100 milliseconds
|
|
429
443
|
this.config.options!.pushRate! = 0.1;
|
|
430
|
-
this.debugWarnLog('Using Default Push Rate.');
|
|
444
|
+
await this.debugWarnLog('Using Default Push Rate.');
|
|
431
445
|
}
|
|
432
446
|
|
|
433
447
|
if (!this.config.options.maxRetries) {
|
|
434
448
|
this.config.options.maxRetries = 5;
|
|
435
|
-
this.debugWarnLog('Using Default Max Retries.');
|
|
449
|
+
await this.debugWarnLog('Using Default Max Retries.');
|
|
436
450
|
} else {
|
|
437
451
|
this.maxRetries = this.config.options.maxRetries;
|
|
438
452
|
}
|
|
@@ -440,22 +454,22 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
440
454
|
if (!this.config.options.delayBetweenRetries) {
|
|
441
455
|
// default 3 seconds
|
|
442
456
|
this.config.options!.delayBetweenRetries! = 3000;
|
|
443
|
-
this.debugWarnLog('Using Default Delay Between Retries.');
|
|
457
|
+
await this.debugWarnLog('Using Default Delay Between Retries.');
|
|
444
458
|
} else {
|
|
445
459
|
this.delayBetweenRetries = this.config.options.delayBetweenRetries * 1000;
|
|
446
460
|
}
|
|
447
461
|
|
|
448
462
|
if (!this.config.credentials && !this.config.options) {
|
|
449
|
-
this.debugWarnLog('Missing Credentials');
|
|
463
|
+
await this.debugWarnLog('Missing Credentials');
|
|
450
464
|
} else if (this.config.credentials && !this.config.credentials.notice) {
|
|
451
465
|
if (!this.config.credentials?.token) {
|
|
452
|
-
this.debugErrorLog('Missing token');
|
|
453
|
-
this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
|
|
466
|
+
await this.debugErrorLog('Missing token');
|
|
467
|
+
await this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
|
|
454
468
|
}
|
|
455
469
|
if (this.config.credentials?.token) {
|
|
456
470
|
if (!this.config.credentials?.secret) {
|
|
457
|
-
this.debugErrorLog('Missing secret');
|
|
458
|
-
this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
|
|
471
|
+
await this.debugErrorLog('Missing secret');
|
|
472
|
+
await this.debugWarnLog('Cloud Enabled SwitchBot Devices & IR Devices will not work');
|
|
459
473
|
}
|
|
460
474
|
}
|
|
461
475
|
}
|
|
@@ -494,11 +508,11 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
494
508
|
}
|
|
495
509
|
// Move openToken to token
|
|
496
510
|
if (!this.config.credentials.secret) {
|
|
497
|
-
this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken,'
|
|
511
|
+
await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken,'
|
|
498
512
|
+ ' "openToken" cconfig has been moved to the "token" config');
|
|
499
513
|
this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.');
|
|
500
514
|
} else {
|
|
501
|
-
this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, '
|
|
515
|
+
await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, '
|
|
502
516
|
+ '"openToken" config has been moved to the "token" config, please restart Homebridge.');
|
|
503
517
|
}
|
|
504
518
|
|
|
@@ -512,9 +526,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
512
526
|
|
|
513
527
|
// save the config, ensuring we maintain pretty json
|
|
514
528
|
writeFileSync(this.api.user.configPath(), JSON.stringify(currentConfig, null, 4));
|
|
515
|
-
this.verifyConfig();
|
|
529
|
+
await this.verifyConfig();
|
|
516
530
|
} catch (e: any) {
|
|
517
|
-
this.errorLog(`Update Token: ${e}`);
|
|
531
|
+
await this.errorLog(`Update Token: ${e}`);
|
|
518
532
|
}
|
|
519
533
|
}
|
|
520
534
|
|
|
@@ -537,331 +551,219 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
537
551
|
};
|
|
538
552
|
};
|
|
539
553
|
|
|
540
|
-
/**
|
|
541
|
-
* this method discovers devices
|
|
542
|
-
*
|
|
543
|
-
const t = `${Date.now()}`;
|
|
544
|
-
const nonce = 'requestID';
|
|
545
|
-
const data = this.config.credentials?.token + t + nonce;
|
|
546
|
-
const signTerm = crypto.createHmac('sha256', this.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
|
|
547
|
-
const sign = signTerm.toString('base64');
|
|
548
|
-
*/
|
|
549
554
|
async discoverDevices() {
|
|
550
|
-
if (this.config.credentials?.token) {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
const delayBetweenRetries = this.delayBetweenRetries; // Delay between retries in milliseconds
|
|
554
|
-
await this.debugWarnLog(`Retry Count: ${retryCount}`);
|
|
555
|
-
await this.debugWarnLog(`Max Retries: ${maxRetries}`);
|
|
556
|
-
await this.debugWarnLog(`Delay Between Retries: ${delayBetweenRetries}`);
|
|
557
|
-
while (retryCount < maxRetries) {
|
|
558
|
-
try {
|
|
559
|
-
const { body, statusCode } = await request(Devices, {
|
|
560
|
-
headers: this.generateHeaders(),
|
|
561
|
-
});
|
|
562
|
-
await this.debugWarnLog(`statusCode: ${statusCode}`);
|
|
563
|
-
const devicesAPI: any = await body.json();
|
|
564
|
-
await this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`);
|
|
565
|
-
await this.debugWarnLog(`devicesAPI Body: ${JSON.stringify(devicesAPI.body)}`);
|
|
566
|
-
await this.debugWarnLog(`devicesAPI StatusCode: ${devicesAPI.statusCode}`);
|
|
567
|
-
if ((statusCode === 200 || statusCode === 100) && (devicesAPI.statusCode === 200 || devicesAPI.statusCode === 100)) {
|
|
568
|
-
await this.debugErrorLog(`statusCode: ${statusCode} & devicesAPI StatusCode: ${devicesAPI.statusCode}`);
|
|
569
|
-
// SwitchBot Devices
|
|
570
|
-
const deviceLists = devicesAPI.body.deviceList;
|
|
571
|
-
await this.debugWarnLog(`DeviceLists: ${JSON.stringify(deviceLists)}`);
|
|
572
|
-
await this.debugWarnLog(`DeviceLists Length: ${deviceLists.length}`);
|
|
573
|
-
if (!this.config.options?.devices) {
|
|
574
|
-
await this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
|
|
575
|
-
if (deviceLists.length === 0) {
|
|
576
|
-
await this.debugLog(`SwitchBot API Has No Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`);
|
|
577
|
-
} else {
|
|
578
|
-
const devices = deviceLists.map((v: any) => v);
|
|
579
|
-
for (const device of devices) {
|
|
580
|
-
if (device.deviceType) {
|
|
581
|
-
if (device.configDeviceName) {
|
|
582
|
-
device.deviceName = device.configDeviceName;
|
|
583
|
-
}
|
|
584
|
-
this.createDevice(device);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
} else if (this.config.credentials?.token && this.config.options.devices) {
|
|
589
|
-
await this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
|
|
590
|
-
if (deviceLists.length === 0) {
|
|
591
|
-
await this.debugLog(`SwitchBot API Has No Devices With Cloud Services Enabled: ${JSON.stringify(devicesAPI.body)}`);
|
|
592
|
-
} else {
|
|
593
|
-
const deviceConfigs = this.config.options?.devices;
|
|
594
|
-
|
|
595
|
-
const mergeBydeviceId = (a1: { deviceId: string }[], a2: any[]) =>
|
|
596
|
-
a1.map((itm: { deviceId: string }) => ({
|
|
597
|
-
...a2.find(
|
|
598
|
-
(item: { deviceId: string }) =>
|
|
599
|
-
item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') && item,
|
|
600
|
-
),
|
|
601
|
-
...itm,
|
|
602
|
-
}));
|
|
603
|
-
|
|
604
|
-
const devices = mergeBydeviceId(deviceLists, deviceConfigs);
|
|
605
|
-
await this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`);
|
|
606
|
-
for (const device of devices) {
|
|
607
|
-
if (!device.deviceType) {
|
|
608
|
-
device.deviceType = device.configDeviceType;
|
|
609
|
-
this.errorLog(`API has displaying no deviceType: ${device.deviceType}, So using configDeviceType: ${device.configDeviceType}`);
|
|
610
|
-
}
|
|
611
|
-
if (device.deviceType) {
|
|
612
|
-
if (device.configDeviceName) {
|
|
613
|
-
device.deviceName = device.configDeviceName;
|
|
614
|
-
}
|
|
615
|
-
this.createDevice(device);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
} else {
|
|
620
|
-
this.errorLog('SwitchBot Token Supplied, Issue with Auth.');
|
|
621
|
-
}
|
|
622
|
-
if (devicesAPI.body.deviceList.length !== 0) {
|
|
623
|
-
this.infoLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
|
|
624
|
-
} else {
|
|
625
|
-
await this.debugLog(`Total SwitchBot Devices Found: ${devicesAPI.body.deviceList.length}`);
|
|
626
|
-
}
|
|
555
|
+
if (!this.config.credentials?.token) {
|
|
556
|
+
return this.handleManualConfig();
|
|
557
|
+
}
|
|
627
558
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}));
|
|
650
|
-
|
|
651
|
-
const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig);
|
|
652
|
-
await this.debugLog(`IR Devices: ${JSON.stringify(devices)}`);
|
|
653
|
-
for (const device of devices) {
|
|
654
|
-
this.createIRDevice(device);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
if (devicesAPI.body.infraredRemoteList.length !== 0) {
|
|
658
|
-
this.infoLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
|
|
659
|
-
} else {
|
|
660
|
-
await this.debugLog(`Total IR Devices Found: ${devicesAPI.body.infraredRemoteList.length}`);
|
|
661
|
-
}
|
|
662
|
-
break;
|
|
663
|
-
} else {
|
|
664
|
-
this.statusCode(statusCode);
|
|
665
|
-
this.statusCode(devicesAPI.statusCode);
|
|
666
|
-
if (statusCode === 500) {
|
|
667
|
-
retryCount++;
|
|
668
|
-
this.infoLog(`statusCode: ${statusCode} Attempt ${retryCount} of ${maxRetries}`);
|
|
669
|
-
await sleep(delayBetweenRetries);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
} catch (e: any) {
|
|
559
|
+
let retryCount = 0;
|
|
560
|
+
const maxRetries = this.maxRetries;
|
|
561
|
+
const delayBetweenRetries = this.delayBetweenRetries;
|
|
562
|
+
|
|
563
|
+
await this.debugWarnLog(`Retry Count: ${retryCount}`);
|
|
564
|
+
await this.debugWarnLog(`Max Retries: ${maxRetries}`);
|
|
565
|
+
await this.debugWarnLog(`Delay Between Retries: ${delayBetweenRetries}`);
|
|
566
|
+
|
|
567
|
+
while (retryCount < maxRetries) {
|
|
568
|
+
try {
|
|
569
|
+
const { body, statusCode } = await request(Devices, { headers: this.generateHeaders() });
|
|
570
|
+
await this.debugWarnLog(`statusCode: ${statusCode}`);
|
|
571
|
+
const devicesAPI: any = await body.json();
|
|
572
|
+
await this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`);
|
|
573
|
+
|
|
574
|
+
if (this.isSuccessfulResponse(statusCode, devicesAPI.statusCode)) {
|
|
575
|
+
await this.handleDevices(devicesAPI.body.deviceList);
|
|
576
|
+
await this.handleIRDevices(devicesAPI.body.infraredRemoteList);
|
|
577
|
+
break;
|
|
578
|
+
} else {
|
|
579
|
+
await this.handleErrorResponse(statusCode, devicesAPI.statusCode, retryCount, maxRetries, delayBetweenRetries);
|
|
673
580
|
retryCount++;
|
|
674
|
-
this.debugErrorLog(
|
|
675
|
-
`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug');
|
|
676
|
-
await this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`);
|
|
677
581
|
}
|
|
582
|
+
} catch (e: any) {
|
|
583
|
+
retryCount++;
|
|
584
|
+
await this.debugErrorLog(`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)},`
|
|
585
|
+
+ ' Submit Bugs Here: https://tinyurl.com/SwitchBotBug');
|
|
586
|
+
await this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`);
|
|
678
587
|
}
|
|
679
|
-
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
private async handleManualConfig() {
|
|
592
|
+
if (this.config.options?.devices) {
|
|
680
593
|
await this.debugLog(`SwitchBot Device Manual Config Set: ${JSON.stringify(this.config.options?.devices)}`);
|
|
681
|
-
const
|
|
682
|
-
const devices = deviceConfigs.map((v: any) => v);
|
|
594
|
+
const devices = this.config.options.devices.map((v: any) => v);
|
|
683
595
|
for (const device of devices) {
|
|
684
596
|
device.deviceType = device.configDeviceType;
|
|
685
597
|
device.deviceName = device.configDeviceName;
|
|
686
598
|
if (device.deviceType) {
|
|
687
|
-
this.createDevice(device);
|
|
599
|
+
await this.createDevice(device);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
} else {
|
|
603
|
+
await this.errorLog('Neither SwitchBot Token or Device Config are set.');
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
private isSuccessfulResponse(statusCode: number, apiStatusCode: number): boolean {
|
|
608
|
+
return (statusCode === 200 || statusCode === 100) && (apiStatusCode === 200 || apiStatusCode === 100);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private async handleDevices(deviceLists: any[]) {
|
|
612
|
+
if (!this.config.options?.devices) {
|
|
613
|
+
await this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`);
|
|
614
|
+
if (deviceLists.length === 0) {
|
|
615
|
+
await this.debugLog('SwitchBot API Has No Devices With Cloud Services Enabled');
|
|
616
|
+
} else {
|
|
617
|
+
for (const device of deviceLists) {
|
|
618
|
+
if (device.deviceType) {
|
|
619
|
+
if (device.configDeviceName) {
|
|
620
|
+
device.deviceName = device.configDeviceName;
|
|
621
|
+
}
|
|
622
|
+
await this.createDevice(device);
|
|
623
|
+
}
|
|
688
624
|
}
|
|
689
625
|
}
|
|
690
626
|
} else {
|
|
691
|
-
this.
|
|
627
|
+
await this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
|
|
628
|
+
const devices = this.mergeByDeviceId(deviceLists, this.config.options.devices);
|
|
629
|
+
await this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`);
|
|
630
|
+
for (const device of devices) {
|
|
631
|
+
if (!device.deviceType && device.configDeviceType) {
|
|
632
|
+
device.deviceType = device.configDeviceType;
|
|
633
|
+
await this.warnLog(`API is displaying no deviceType: ${device.deviceType}, So using configDeviceType: ${device.configDeviceType}`);
|
|
634
|
+
} else if (!device.deviceType && !device.configDeviceName) {
|
|
635
|
+
await this.errorLog('No deviceType or configDeviceType for device. No device will be created.');
|
|
636
|
+
}
|
|
637
|
+
if (device.deviceType) {
|
|
638
|
+
if (device.configDeviceName) {
|
|
639
|
+
device.deviceName = device.configDeviceName;
|
|
640
|
+
}
|
|
641
|
+
await this.createDevice(device);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
private async handleIRDevices(irDeviceLists: any[]) {
|
|
648
|
+
if (!this.config.options?.irdevices) {
|
|
649
|
+
await this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`);
|
|
650
|
+
for (const device of irDeviceLists) {
|
|
651
|
+
if (device.remoteType) {
|
|
652
|
+
await this.createIRDevice(device);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
} else {
|
|
656
|
+
await this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`);
|
|
657
|
+
const devices = this.mergeByDeviceId(irDeviceLists, this.config.options.irdevices);
|
|
658
|
+
await this.debugLog(`IR Devices: ${JSON.stringify(devices)}`);
|
|
659
|
+
for (const device of devices) {
|
|
660
|
+
await this.createIRDevice(device);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
private mergeByDeviceId(a1: { deviceId: string }[], a2: any[]) {
|
|
666
|
+
const normalizeDeviceId = (deviceId: string) => deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '');
|
|
667
|
+
return a1.map(itm => {
|
|
668
|
+
const matchingItem = a2.find(item => normalizeDeviceId(item.deviceId) === normalizeDeviceId(itm.deviceId));
|
|
669
|
+
return { ...matchingItem, ...itm };
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
private async handleErrorResponse(statusCode: number, apiStatusCode: number, retryCount: number, maxRetries: number, delayBetweenRetries: number) {
|
|
674
|
+
await this.statusCode(statusCode);
|
|
675
|
+
await this.statusCode(apiStatusCode);
|
|
676
|
+
if (statusCode === 500) {
|
|
677
|
+
this.infoLog(`statusCode: ${statusCode} Attempt ${retryCount + 1} of ${maxRetries}`);
|
|
678
|
+
await sleep(delayBetweenRetries);
|
|
692
679
|
}
|
|
693
680
|
}
|
|
694
681
|
|
|
695
682
|
private async createDevice(device: device & devicesConfig) {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
case 'Contact Sensor':
|
|
737
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
738
|
-
this.createContact(device);
|
|
739
|
-
break;
|
|
740
|
-
case 'Curtain':
|
|
741
|
-
case 'Curtain3':
|
|
742
|
-
await this.debugLog(`Discovered ${device.deviceType} ${device.deviceName}: ${device.deviceId}`);
|
|
743
|
-
this.createCurtain(device);
|
|
744
|
-
break;
|
|
745
|
-
case 'Blind Tilt':
|
|
746
|
-
await this.debugLog(`Discovered ${device.deviceType} ${device.deviceName}: ${device.deviceId}`);
|
|
747
|
-
this.createBlindTilt(device);
|
|
748
|
-
break;
|
|
749
|
-
case 'Plug':
|
|
750
|
-
case 'Plug Mini (US)':
|
|
751
|
-
case 'Plug Mini (JP)':
|
|
752
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
753
|
-
this.createPlug(device);
|
|
754
|
-
break;
|
|
755
|
-
case 'Smart Lock':
|
|
756
|
-
case 'Smart Lock Pro':
|
|
757
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
758
|
-
this.createLock(device);
|
|
759
|
-
break;
|
|
760
|
-
case 'Color Bulb':
|
|
761
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
762
|
-
this.createColorBulb(device);
|
|
763
|
-
break;
|
|
764
|
-
case 'K10+':
|
|
765
|
-
case 'WoSweeper':
|
|
766
|
-
case 'WoSweeperMini':
|
|
767
|
-
case 'Robot Vacuum Cleaner S1':
|
|
768
|
-
case 'Robot Vacuum Cleaner S1 Plus':
|
|
769
|
-
case 'Robot Vacuum Cleaner S10':
|
|
770
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
771
|
-
this.createRobotVacuumCleaner(device);
|
|
772
|
-
break;
|
|
773
|
-
case 'Ceiling Light':
|
|
774
|
-
case 'Ceiling Light Pro':
|
|
775
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
776
|
-
this.createCeilingLight(device);
|
|
777
|
-
break;
|
|
778
|
-
case 'Strip Light':
|
|
779
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
780
|
-
this.createStripLight(device);
|
|
781
|
-
break;
|
|
782
|
-
case 'Battery Circulator Fan':
|
|
783
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
784
|
-
this.createFan(device);
|
|
785
|
-
break;
|
|
786
|
-
case 'Remote':
|
|
787
|
-
case 'Indoor Cam':
|
|
788
|
-
case 'remote with screen+':
|
|
789
|
-
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}, is currently not supported, device: ${JSON.stringify(device)}`);
|
|
790
|
-
break;
|
|
791
|
-
default:
|
|
792
|
-
this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`,
|
|
793
|
-
+ `Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}`);
|
|
683
|
+
const deviceTypeHandlers: { [key: string]: (device: device & devicesConfig) => Promise<void> } = {
|
|
684
|
+
'Humidifier': this.createHumidifier.bind(this),
|
|
685
|
+
'Hub 2': this.createHub2.bind(this),
|
|
686
|
+
'Bot': this.createBot.bind(this),
|
|
687
|
+
'Meter': this.createMeter.bind(this),
|
|
688
|
+
'MeterPlus': this.createMeterPlus.bind(this),
|
|
689
|
+
'Meter Plus (JP)': this.createMeterPlus.bind(this),
|
|
690
|
+
'WoIOSensor': this.createIOSensor.bind(this),
|
|
691
|
+
'Water Detector': this.createWaterDetector.bind(this),
|
|
692
|
+
'Motion Sensor': this.createMotion.bind(this),
|
|
693
|
+
'Contact Sensor': this.createContact.bind(this),
|
|
694
|
+
'Curtain': this.createCurtain.bind(this),
|
|
695
|
+
'Curtain3': this.createCurtain.bind(this),
|
|
696
|
+
'Blind Tilt': this.createBlindTilt.bind(this),
|
|
697
|
+
'Plug': this.createPlug.bind(this),
|
|
698
|
+
'Plug Mini (US)': this.createPlug.bind(this),
|
|
699
|
+
'Plug Mini (JP)': this.createPlug.bind(this),
|
|
700
|
+
'Smart Lock': this.createLock.bind(this),
|
|
701
|
+
'Smart Lock Pro': this.createLock.bind(this),
|
|
702
|
+
'Color Bulb': this.createColorBulb.bind(this),
|
|
703
|
+
'K10+': this.createRobotVacuumCleaner.bind(this),
|
|
704
|
+
'WoSweeper': this.createRobotVacuumCleaner.bind(this),
|
|
705
|
+
'WoSweeperMini': this.createRobotVacuumCleaner.bind(this),
|
|
706
|
+
'Robot Vacuum Cleaner S1': this.createRobotVacuumCleaner.bind(this),
|
|
707
|
+
'Robot Vacuum Cleaner S1 Plus': this.createRobotVacuumCleaner.bind(this),
|
|
708
|
+
'Robot Vacuum Cleaner S10': this.createRobotVacuumCleaner.bind(this),
|
|
709
|
+
'Ceiling Light': this.createCeilingLight.bind(this),
|
|
710
|
+
'Ceiling Light Pro': this.createCeilingLight.bind(this),
|
|
711
|
+
'Strip Light': this.createStripLight.bind(this),
|
|
712
|
+
'Battery Circulator Fan': this.createFan.bind(this),
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
if (deviceTypeHandlers[device.deviceType!]) {
|
|
716
|
+
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}`);
|
|
717
|
+
await deviceTypeHandlers[device.deviceType!](device);
|
|
718
|
+
} else if (['Hub Mini', 'Hub Plus', 'Remote', 'Indoor Cam', 'remote with screen'].includes(device.deviceType!)) {
|
|
719
|
+
await this.debugLog(`Discovered ${device.deviceType}: ${device.deviceId}, is currently not supported, device: ${JSON.stringify(device)}`);
|
|
720
|
+
} else {
|
|
721
|
+
await this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.deviceType}, is currently not supported.`
|
|
722
|
+
+ ` Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}`);
|
|
794
723
|
}
|
|
795
724
|
}
|
|
796
725
|
|
|
797
726
|
private async createIRDevice(device: irdevice & devicesConfig) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
case 'DIY Air Purifier':
|
|
839
|
-
await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
|
|
840
|
-
this.createAirPurifier(device);
|
|
841
|
-
break;
|
|
842
|
-
case 'Water Heater':
|
|
843
|
-
case 'DIY Water Heater':
|
|
844
|
-
await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
|
|
845
|
-
this.createWaterHeater(device);
|
|
846
|
-
break;
|
|
847
|
-
case 'Vacuum Cleaner':
|
|
848
|
-
case 'DIY Vacuum Cleaner':
|
|
849
|
-
await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
|
|
850
|
-
this.createVacuumCleaner(device);
|
|
851
|
-
break;
|
|
852
|
-
case 'Camera':
|
|
853
|
-
case 'DIY Camera':
|
|
854
|
-
await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
|
|
855
|
-
this.createCamera(device);
|
|
856
|
-
break;
|
|
857
|
-
case 'Others':
|
|
858
|
-
await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
|
|
859
|
-
this.createOthers(device);
|
|
860
|
-
break;
|
|
861
|
-
default:
|
|
862
|
-
await this.debugLog(`Unsupported Device: ${JSON.stringify(device)}`);
|
|
863
|
-
this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.remoteType}, is currently not supported.`);
|
|
864
|
-
this.warnLog('Submit Feature Requests Here: ' + 'https://tinyurl.com/SwitchBotFeatureRequest');
|
|
727
|
+
device.connectionType = device.connectionType ?? 'OpenAPI';
|
|
728
|
+
const deviceTypeHandlers: { [key: string]: (device: irdevice & devicesConfig) => Promise<void> } = {
|
|
729
|
+
'TV': this.createTV.bind(this),
|
|
730
|
+
'DIY TV': this.createTV.bind(this),
|
|
731
|
+
'Projector': this.createTV.bind(this),
|
|
732
|
+
'DIY Projector': this.createTV.bind(this),
|
|
733
|
+
'Set Top Box': this.createTV.bind(this),
|
|
734
|
+
'DIY Set Top Box': this.createTV.bind(this),
|
|
735
|
+
'IPTV': this.createTV.bind(this),
|
|
736
|
+
'DIY IPTV': this.createTV.bind(this),
|
|
737
|
+
'DVD': this.createTV.bind(this),
|
|
738
|
+
'DIY DVD': this.createTV.bind(this),
|
|
739
|
+
'Speaker': this.createTV.bind(this),
|
|
740
|
+
'DIY Speaker': this.createTV.bind(this),
|
|
741
|
+
'Fan': this.createIRFan.bind(this),
|
|
742
|
+
'DIY Fan': this.createIRFan.bind(this),
|
|
743
|
+
'Air Conditioner': this.createAirConditioner.bind(this),
|
|
744
|
+
'DIY Air Conditioner': this.createAirConditioner.bind(this),
|
|
745
|
+
'Light': this.createLight.bind(this),
|
|
746
|
+
'DIY Light': this.createLight.bind(this),
|
|
747
|
+
'Air Purifier': this.createAirPurifier.bind(this),
|
|
748
|
+
'DIY Air Purifier': this.createAirPurifier.bind(this),
|
|
749
|
+
'Water Heater': this.createWaterHeater.bind(this),
|
|
750
|
+
'DIY Water Heater': this.createWaterHeater.bind(this),
|
|
751
|
+
'Vacuum Cleaner': this.createVacuumCleaner.bind(this),
|
|
752
|
+
'DIY Vacuum Cleaner': this.createVacuumCleaner.bind(this),
|
|
753
|
+
'Camera': this.createCamera.bind(this),
|
|
754
|
+
'DIY Camera': this.createCamera.bind(this),
|
|
755
|
+
'Others': this.createOthers.bind(this),
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
if (deviceTypeHandlers[device.remoteType!]) {
|
|
759
|
+
await this.debugLog(`Discovered ${device.remoteType}: ${device.deviceId}`);
|
|
760
|
+
if (device.remoteType.startsWith('DIY') && device.external === undefined) {
|
|
761
|
+
device.external = true;
|
|
762
|
+
}
|
|
763
|
+
await deviceTypeHandlers[device.remoteType!](device);
|
|
764
|
+
} else {
|
|
765
|
+
await this.warnLog(`Device: ${device.deviceName} with Device Type: ${device.remoteType}, is currently not supported.`
|
|
766
|
+
+ ` Submit Feature Requests Here: https://tinyurl.com/SwitchBotFeatureRequest, device: ${JSON.stringify(device)}`);
|
|
865
767
|
}
|
|
866
768
|
}
|
|
867
769
|
|
|
@@ -2060,7 +1962,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2060
1962
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2061
1963
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2062
1964
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2063
|
-
accessory.context.connectionType = device.connectionType;
|
|
2064
1965
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2065
1966
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2066
1967
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2069,7 +1970,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2069
1970
|
new TV(this, accessory, device);
|
|
2070
1971
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2071
1972
|
|
|
2072
|
-
this.
|
|
1973
|
+
this.externalOrPlatform(device, accessory);
|
|
2073
1974
|
this.accessories.push(accessory);
|
|
2074
1975
|
} else {
|
|
2075
1976
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2120,7 +2021,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2120
2021
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2121
2022
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2122
2023
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2123
|
-
accessory.context.connectionType = device.connectionType;
|
|
2124
2024
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2125
2025
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2126
2026
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2130,7 +2030,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2130
2030
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2131
2031
|
|
|
2132
2032
|
// publish device externally or link the accessory to your platform
|
|
2133
|
-
this.
|
|
2033
|
+
this.externalOrPlatform(device, accessory);
|
|
2134
2034
|
this.accessories.push(accessory);
|
|
2135
2035
|
} else {
|
|
2136
2036
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2181,7 +2081,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2181
2081
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2182
2082
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2183
2083
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2184
|
-
accessory.context.connectionType = device.connectionType;
|
|
2185
2084
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2186
2085
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2187
2086
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2191,7 +2090,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2191
2090
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2192
2091
|
|
|
2193
2092
|
// publish device externally or link the accessory to your platform
|
|
2194
|
-
this.
|
|
2093
|
+
this.externalOrPlatform(device, accessory);
|
|
2195
2094
|
this.accessories.push(accessory);
|
|
2196
2095
|
} else {
|
|
2197
2096
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2242,7 +2141,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2242
2141
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2243
2142
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2244
2143
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2245
|
-
accessory.context.connectionType = device.connectionType;
|
|
2246
2144
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2247
2145
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2248
2146
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2252,7 +2150,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2252
2150
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2253
2151
|
|
|
2254
2152
|
// publish device externally or link the accessory to your platform
|
|
2255
|
-
this.
|
|
2153
|
+
this.externalOrPlatform(device, accessory);
|
|
2256
2154
|
this.accessories.push(accessory);
|
|
2257
2155
|
} else {
|
|
2258
2156
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2303,7 +2201,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2303
2201
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2304
2202
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2305
2203
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2306
|
-
accessory.context.connectionType = device.connectionType;
|
|
2307
2204
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2308
2205
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2309
2206
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2313,7 +2210,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2313
2210
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2314
2211
|
|
|
2315
2212
|
// publish device externally or link the accessory to your platform
|
|
2316
|
-
this.
|
|
2213
|
+
this.externalOrPlatform(device, accessory);
|
|
2317
2214
|
this.accessories.push(accessory);
|
|
2318
2215
|
} else {
|
|
2319
2216
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2364,7 +2261,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2364
2261
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2365
2262
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2366
2263
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2367
|
-
accessory.context.connectionType = device.connectionType;
|
|
2368
2264
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2369
2265
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2370
2266
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2374,7 +2270,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2374
2270
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2375
2271
|
|
|
2376
2272
|
// publish device externally or link the accessory to your platform
|
|
2377
|
-
this.
|
|
2273
|
+
this.externalOrPlatform(device, accessory);
|
|
2378
2274
|
this.accessories.push(accessory);
|
|
2379
2275
|
} else {
|
|
2380
2276
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2425,7 +2321,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2425
2321
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2426
2322
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2427
2323
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2428
|
-
accessory.context.connectionType = device.connectionType;
|
|
2429
2324
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2430
2325
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2431
2326
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2435,7 +2330,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2435
2330
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2436
2331
|
|
|
2437
2332
|
// publish device externally or link the accessory to your platform
|
|
2438
|
-
this.
|
|
2333
|
+
this.externalOrPlatform(device, accessory);
|
|
2439
2334
|
this.accessories.push(accessory);
|
|
2440
2335
|
} else {
|
|
2441
2336
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2486,7 +2381,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2486
2381
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2487
2382
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2488
2383
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2489
|
-
accessory.context.connectionType = device.connectionType;
|
|
2490
2384
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2491
2385
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2492
2386
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2496,7 +2390,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2496
2390
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2497
2391
|
|
|
2498
2392
|
// publish device externally or link the accessory to your platform
|
|
2499
|
-
this.
|
|
2393
|
+
this.externalOrPlatform(device, accessory);
|
|
2500
2394
|
this.accessories.push(accessory);
|
|
2501
2395
|
} else {
|
|
2502
2396
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2547,7 +2441,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2547
2441
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
2548
2442
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName);
|
|
2549
2443
|
accessory.context.connectionType = await this.connectionType(device);
|
|
2550
|
-
accessory.context.connectionType = device.connectionType;
|
|
2551
2444
|
accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0';
|
|
2552
2445
|
const newOrExternal = !device.external ? 'Adding new' : 'Loading external';
|
|
2553
2446
|
await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`);
|
|
@@ -2557,7 +2450,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2557
2450
|
await this.debugLog(`${device.remoteType} uuid: ${device.deviceId}-${device.remoteType}, (${accessory.UUID})`);
|
|
2558
2451
|
|
|
2559
2452
|
// publish device externally or link the accessory to your platform
|
|
2560
|
-
this.
|
|
2453
|
+
this.externalOrPlatform(device, accessory);
|
|
2561
2454
|
this.accessories.push(accessory);
|
|
2562
2455
|
} else {
|
|
2563
2456
|
await this.debugLog(`Device not registered: ${device.deviceName} ${device.remoteType} deviceId: ${device.deviceId}`);
|
|
@@ -2565,13 +2458,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2565
2458
|
}
|
|
2566
2459
|
|
|
2567
2460
|
async registerCurtains(device: device & devicesConfig): Promise<boolean> {
|
|
2568
|
-
/*function isCurtainDevice(device: device & devicesConfig): device is (curtain | curtain3) & devicesConfig {
|
|
2569
|
-
return device.deviceType === 'Curtain' || device.deviceType === 'Curtain3';
|
|
2570
|
-
}
|
|
2571
|
-
|
|
2572
|
-
function isBlindTiltDevice(device: device & devicesConfig): device is blindTilt & devicesConfig {
|
|
2573
|
-
return device.deviceType === 'Blind Tilt';
|
|
2574
|
-
}*/
|
|
2575
2461
|
let registerWindowCovering: boolean;
|
|
2576
2462
|
if (isCurtainDevice(device)) {
|
|
2577
2463
|
await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds},`
|
|
@@ -2717,111 +2603,72 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2717
2603
|
return registerDevice;
|
|
2718
2604
|
}
|
|
2719
2605
|
|
|
2720
|
-
public async
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
* Only one TV can exist per bridge, to bypass this limitation, you should
|
|
2724
|
-
* publish your TV as an external accessory.
|
|
2725
|
-
*/
|
|
2726
|
-
if (device.external) {
|
|
2727
|
-
await this.debugWarnLog(`${accessory.displayName} External Accessory Mode`);
|
|
2728
|
-
this.externalAccessory(accessory);
|
|
2729
|
-
} else {
|
|
2730
|
-
await this.debugLog(`${accessory.displayName} External Accessory Mode: ${device.external}`);
|
|
2731
|
-
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
2732
|
-
}
|
|
2733
|
-
}
|
|
2606
|
+
public async externalOrPlatform(device: device & (irDevicesConfig | devicesConfig), accessory: PlatformAccessory) {
|
|
2607
|
+
const { displayName } = accessory;
|
|
2608
|
+
const isExternal = device.external ?? false;
|
|
2734
2609
|
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
await this.externalAccessory(accessory);
|
|
2610
|
+
if (isExternal) {
|
|
2611
|
+
await this.debugWarnLog(`${displayName} External Accessory Mode`);
|
|
2612
|
+
this.api.publishExternalAccessories(PLUGIN_NAME, [accessory]);
|
|
2739
2613
|
} else {
|
|
2740
|
-
await this.debugLog(`${
|
|
2614
|
+
await this.debugLog(`${displayName} External Accessory Mode: ${isExternal}`);
|
|
2741
2615
|
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
2742
2616
|
}
|
|
2743
2617
|
}
|
|
2744
2618
|
|
|
2745
|
-
public async externalAccessory(accessory: PlatformAccessory) {
|
|
2746
|
-
this.api.publishExternalAccessories(PLUGIN_NAME, [accessory]);
|
|
2747
|
-
}
|
|
2748
|
-
|
|
2749
2619
|
public unregisterPlatformAccessories(existingAccessory: PlatformAccessory) {
|
|
2620
|
+
const { displayName } = existingAccessory;
|
|
2750
2621
|
// remove platform accessories when no longer present
|
|
2751
2622
|
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]);
|
|
2752
|
-
this.warnLog(`Removing existing accessory from cache: ${
|
|
2623
|
+
this.warnLog(`Removing existing accessory from cache: ${displayName}`);
|
|
2753
2624
|
}
|
|
2754
2625
|
|
|
2626
|
+
/**
|
|
2627
|
+
* Handles the status codes returned by the device and logs appropriate messages.
|
|
2628
|
+
*
|
|
2629
|
+
* @param statusCode - The status code returned by the device.
|
|
2630
|
+
* @returns A promise that resolves when the logging is complete.
|
|
2631
|
+
*/
|
|
2755
2632
|
async statusCode(statusCode: number): Promise<void> {
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
+ `statusCode: ${statusCode}`);
|
|
2789
|
-
break;
|
|
2790
|
-
case 403:
|
|
2791
|
-
await this.errorLog('Forbidden, The request has been authenticated but does not have appropriate permissions, or a requested resource is not '
|
|
2792
|
-
+ `found, statusCode: ${statusCode}`);
|
|
2793
|
-
break;
|
|
2794
|
-
case 404:
|
|
2795
|
-
await this.errorLog(`Not Found, Specifies the requested path does not exist, statusCode: ${statusCode}`);
|
|
2796
|
-
break;
|
|
2797
|
-
case 406:
|
|
2798
|
-
await this.errorLog('Not Acceptable, The client has requested a MIME type via the Accept header for a value not supported by the server, '
|
|
2799
|
-
+ `statusCode: ${statusCode}`);
|
|
2800
|
-
break;
|
|
2801
|
-
case 415:
|
|
2802
|
-
await this.errorLog('Unsupported Media Type, The client has defined a contentType header that is not supported by the server, '
|
|
2803
|
-
+ `statusCode: ${statusCode}`);
|
|
2804
|
-
break;
|
|
2805
|
-
case 422:
|
|
2806
|
-
await this.errorLog('Unprocessable Entity, The client has made a valid request, but the server cannot process it. This is often used for '
|
|
2807
|
-
+ `APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
|
|
2808
|
-
break;
|
|
2809
|
-
case 429:
|
|
2810
|
-
await this.errorLog('Too Many Requests, The client has exceeded the number of requests allowed for a given time window, '
|
|
2811
|
-
+ `statusCode: ${statusCode}`);
|
|
2812
|
-
break;
|
|
2813
|
-
case 500:
|
|
2814
|
-
await this.errorLog('Internal Server Error, An unexpected error on the SmartThings servers has occurred. These errors should be rare, '
|
|
2815
|
-
+ `statusCode: ${statusCode}`);
|
|
2816
|
-
break;
|
|
2817
|
-
default:
|
|
2818
|
-
await this.errorLog(`Unknown statusCode, statusCode: ${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`);
|
|
2633
|
+
const messages: { [key: number]: string } = {
|
|
2634
|
+
151: `Command not supported by this device type, statusCode: ${statusCode}, Submit Feature Request Here:
|
|
2635
|
+
https://tinyurl.com/SwitchBotFeatureRequest`,
|
|
2636
|
+
152: `Device not found, statusCode: ${statusCode}`,
|
|
2637
|
+
160: `Command is not supported, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`,
|
|
2638
|
+
161: `Device is offline, statusCode: ${statusCode}`,
|
|
2639
|
+
171: `is offline, statusCode: ${statusCode}`,
|
|
2640
|
+
190: `Requests reached the daily limit, statusCode: ${statusCode}`,
|
|
2641
|
+
100: `Command successfully sent, statusCode: ${statusCode}`,
|
|
2642
|
+
200: `Request successful, statusCode: ${statusCode}`,
|
|
2643
|
+
400: `Bad Request, The client has issued an invalid request. This is commonly used to specify validation errors in a request payload,
|
|
2644
|
+
statusCode: ${statusCode}`,
|
|
2645
|
+
401: `Unauthorized, Authorization for the API is required, but the request has not been authenticated, statusCode: ${statusCode}`,
|
|
2646
|
+
403: `Forbidden, The request has been authenticated but does not have appropriate permissions, or a requested resource is not found,
|
|
2647
|
+
statusCode: ${statusCode}`,
|
|
2648
|
+
404: `Not Found, Specifies the requested path does not exist, statusCode: ${statusCode}`,
|
|
2649
|
+
406: `Not Acceptable, The client has requested a MIME type via the Accept header for a value not supported by the server,
|
|
2650
|
+
statusCode: ${statusCode}`,
|
|
2651
|
+
415: `Unsupported Media Type, The client has defined a contentType header that is not supported by the server, statusCode: ${statusCode}`,
|
|
2652
|
+
422: `Unprocessable Entity, The client has made a valid request, but the server cannot process it. This is often used for APIs for which
|
|
2653
|
+
certain limits have been exceeded, statusCode: ${statusCode}`,
|
|
2654
|
+
429: `Too Many Requests, The client has exceeded the number of requests allowed for a given time window, statusCode: ${statusCode}`,
|
|
2655
|
+
500: `Internal Server Error, An unexpected error on the SmartThings servers has occurred. These errors should be rare,
|
|
2656
|
+
statusCode: ${statusCode}`,
|
|
2657
|
+
};
|
|
2658
|
+
|
|
2659
|
+
const message = messages[statusCode] ?? `Unknown statusCode, statusCode: ${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`;
|
|
2660
|
+
|
|
2661
|
+
if ([100, 200].includes(statusCode)) {
|
|
2662
|
+
await this.debugLog(message);
|
|
2663
|
+
} else {
|
|
2664
|
+
await this.errorLog(message);
|
|
2819
2665
|
}
|
|
2820
2666
|
}
|
|
2821
2667
|
|
|
2822
2668
|
async retryRequest(deviceMaxRetries: number, deviceDelayBetweenRetries: number, url: string | URL | UrlObject,
|
|
2823
2669
|
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions,
|
|
2824
|
-
'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions,
|
|
2670
|
+
'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions,
|
|
2671
|
+
'method'>>): Promise<{ body: any; statusCode: number }> {
|
|
2825
2672
|
let retryCount = 0;
|
|
2826
2673
|
const maxRetries = deviceMaxRetries;
|
|
2827
2674
|
const delayBetweenRetries = deviceDelayBetweenRetries;
|
|
@@ -2844,20 +2691,20 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2844
2691
|
}
|
|
2845
2692
|
|
|
2846
2693
|
// BLE Connection
|
|
2847
|
-
async connectBLE(accessory: PlatformAccessory, device: device & devicesConfig) {
|
|
2848
|
-
let switchbot: any;
|
|
2694
|
+
async connectBLE(accessory: PlatformAccessory, device: device & devicesConfig): Promise<any> {
|
|
2849
2695
|
try {
|
|
2850
|
-
const SwitchBot =
|
|
2851
|
-
|
|
2696
|
+
const { SwitchBot } = await import('node-switchbot');
|
|
2697
|
+
const switchbot = new SwitchBot();
|
|
2698
|
+
queueScheduler.schedule(async () => switchbot);
|
|
2852
2699
|
await this.debugLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' found: ${switchbot}`);
|
|
2700
|
+
return switchbot;
|
|
2853
2701
|
} catch (e: any) {
|
|
2854
|
-
switchbot
|
|
2855
|
-
|
|
2702
|
+
await this.errorLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' not found, Error: ${e}`);
|
|
2703
|
+
return false;
|
|
2856
2704
|
}
|
|
2857
|
-
return switchbot;
|
|
2858
2705
|
}
|
|
2859
2706
|
|
|
2860
|
-
async getVersion() {
|
|
2707
|
+
async getVersion(): Promise<string> {
|
|
2861
2708
|
const json = JSON.parse(
|
|
2862
2709
|
readFileSync(
|
|
2863
2710
|
new URL('../package.json', import.meta.url),
|
|
@@ -2866,42 +2713,35 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2866
2713
|
);
|
|
2867
2714
|
await this.debugLog(`Plugin Version: ${json.version}`);
|
|
2868
2715
|
this.version = json.version;
|
|
2716
|
+
return json.version;
|
|
2869
2717
|
}
|
|
2870
2718
|
|
|
2871
2719
|
async getPlatformConfigSettings() {
|
|
2720
|
+
const { options } = this.config;
|
|
2872
2721
|
const platformConfig: SwitchBotPlatformConfig['options'] = {};
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
platformConfig.pushRate = this.config.options.pushRate;
|
|
2885
|
-
}
|
|
2886
|
-
if (this.config.options.maxRetries) {
|
|
2887
|
-
this.maxRetries = this.config.options.maxRetries;
|
|
2888
|
-
platformConfig.maxRetries = this.config.options.maxRetries;
|
|
2889
|
-
} else {
|
|
2890
|
-
this.maxRetries = 3;
|
|
2891
|
-
this.debugWarnLog('Using Default Max Retries');
|
|
2892
|
-
platformConfig.maxRetries = this.maxRetries;
|
|
2722
|
+
|
|
2723
|
+
if (options) {
|
|
2724
|
+
platformConfig.logging = options.logging;
|
|
2725
|
+
platformConfig.refreshRate = options.refreshRate;
|
|
2726
|
+
platformConfig.updateRate = options.updateRate;
|
|
2727
|
+
platformConfig.pushRate = options.pushRate;
|
|
2728
|
+
|
|
2729
|
+
this.maxRetries = options.maxRetries || 3;
|
|
2730
|
+
platformConfig.maxRetries = this.maxRetries;
|
|
2731
|
+
if (!options.maxRetries) {
|
|
2732
|
+
await this.debugWarnLog('Using Default Max Retries');
|
|
2893
2733
|
}
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
this.
|
|
2899
|
-
this.debugWarnLog('Using Default Delay Between Retries');
|
|
2900
|
-
platformConfig.delayBetweenRetries = this.delayBetweenRetries / 1000;
|
|
2734
|
+
|
|
2735
|
+
this.delayBetweenRetries = (options.delayBetweenRetries || 3) * 1000;
|
|
2736
|
+
platformConfig.delayBetweenRetries = this.delayBetweenRetries / 1000;
|
|
2737
|
+
if (!options.delayBetweenRetries) {
|
|
2738
|
+
await this.debugWarnLog('Using Default Delay Between Retries');
|
|
2901
2739
|
}
|
|
2902
|
-
|
|
2740
|
+
|
|
2741
|
+
if (Object.keys(platformConfig).length) {
|
|
2903
2742
|
await this.debugLog(`Platform Config: ${JSON.stringify(platformConfig)}`);
|
|
2904
2743
|
}
|
|
2744
|
+
|
|
2905
2745
|
this.platformConfig = platformConfig;
|
|
2906
2746
|
}
|
|
2907
2747
|
}
|
|
@@ -2922,35 +2762,45 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
2922
2762
|
|
|
2923
2763
|
/**
|
|
2924
2764
|
* Validate and clean a string value for a Name Characteristic.
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2765
|
+
* @param displayName - The display name of the accessory.
|
|
2766
|
+
* @param name - The name of the characteristic.
|
|
2767
|
+
* @param value - The value to be validated and cleaned.
|
|
2768
|
+
* @returns The cleaned string value.
|
|
2769
|
+
*/
|
|
2930
2770
|
async validateAndCleanDisplayName(displayName: string, name: string, value: string): Promise<string> {
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2771
|
+
if (this.config.options?.allowInvalidCharacters) {
|
|
2772
|
+
return value;
|
|
2773
|
+
} else {
|
|
2774
|
+
const validPattern = new RegExp(/^[\p{L}\p{N}][\p{L}\p{N} ']*[\p{L}\p{N}]$/u);
|
|
2775
|
+
const invalidCharsPattern = /[^\p{L}\p{N} ']/gu;
|
|
2776
|
+
const invalidStartEndPattern = /^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu;
|
|
2777
|
+
|
|
2778
|
+
if (typeof value === 'string' && !validPattern.test(value)) {
|
|
2779
|
+
await this.warnLog(`WARNING: The accessory '${displayName}' has an invalid '${name}' characteristic ('${value}').`
|
|
2780
|
+
+ ' Please use only alphanumeric, space, and apostrophe characters. Ensure it starts and ends with an alphabetic or numeric character,'
|
|
2781
|
+
+ ' and avoid emojis. This may prevent the accessory from being added in the Home App or cause unresponsiveness.');
|
|
2782
|
+
|
|
2783
|
+
// Remove invalid characters
|
|
2784
|
+
if (invalidCharsPattern.test(value)) {
|
|
2785
|
+
const before = value;
|
|
2786
|
+
await this.warnLog(`Removing invalid characters from '${name}' characteristic, if you feel this is incorrect,`
|
|
2787
|
+
+ ' please enable \'allowInvalidCharacter\' in the config to allow all characters');
|
|
2788
|
+
value = value.replace(invalidCharsPattern, '');
|
|
2789
|
+
await this.warnLog(`${name} Before: '${before}' After: '${value}'`);
|
|
2790
|
+
}
|
|
2939
2791
|
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2792
|
+
// Ensure it starts and ends with an alphanumeric character
|
|
2793
|
+
if (invalidStartEndPattern.test(value)) {
|
|
2794
|
+
const before = value;
|
|
2795
|
+
await this.warnLog(`Removing invalid starting or ending characters from '${name}' characteristic, if you feel this is incorrect,`
|
|
2796
|
+
+ ' please enable \'allowInvalidCharacter\' in the config to allow all characters');
|
|
2797
|
+
value = value.replace(invalidStartEndPattern, '');
|
|
2798
|
+
await this.warnLog(`${name} Before: '${before}' After: '${value}'`);
|
|
2799
|
+
}
|
|
2944
2800
|
}
|
|
2945
2801
|
|
|
2946
|
-
|
|
2947
|
-
if (invalidStartEndPattern.test(value)) {
|
|
2948
|
-
this.warnLog(`Removing invalid starting or ending characters from '${name}' characteristic`);
|
|
2949
|
-
value = value.replace(invalidStartEndPattern, '');
|
|
2950
|
-
}
|
|
2802
|
+
return value;
|
|
2951
2803
|
}
|
|
2952
|
-
|
|
2953
|
-
return value;
|
|
2954
2804
|
}
|
|
2955
2805
|
|
|
2956
2806
|
/**
|