@switchbot/homebridge-switchbot 3.5.1-beta.12 → 3.5.1-beta.13
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/config.schema.json +19 -3
- package/dist/device/blindtilt.d.ts +6 -4
- package/dist/device/blindtilt.d.ts.map +1 -1
- package/dist/device/blindtilt.js +57 -45
- package/dist/device/blindtilt.js.map +1 -1
- package/dist/device/bot.d.ts +6 -3
- package/dist/device/bot.d.ts.map +1 -1
- package/dist/device/bot.js +38 -24
- package/dist/device/bot.js.map +1 -1
- package/dist/device/ceilinglight.d.ts +6 -3
- package/dist/device/ceilinglight.d.ts.map +1 -1
- package/dist/device/ceilinglight.js +40 -21
- package/dist/device/ceilinglight.js.map +1 -1
- package/dist/device/colorbulb.d.ts +6 -3
- package/dist/device/colorbulb.d.ts.map +1 -1
- package/dist/device/colorbulb.js +42 -31
- package/dist/device/colorbulb.js.map +1 -1
- package/dist/device/contact.d.ts +6 -3
- package/dist/device/contact.d.ts.map +1 -1
- package/dist/device/contact.js +40 -28
- package/dist/device/contact.js.map +1 -1
- package/dist/device/curtain.d.ts +6 -4
- package/dist/device/curtain.d.ts.map +1 -1
- package/dist/device/curtain.js +35 -22
- package/dist/device/curtain.js.map +1 -1
- package/dist/device/device.d.ts +13 -2
- package/dist/device/device.d.ts.map +1 -1
- package/dist/device/device.js +223 -108
- package/dist/device/device.js.map +1 -1
- package/dist/device/fan.d.ts +6 -3
- package/dist/device/fan.d.ts.map +1 -1
- package/dist/device/fan.js +48 -29
- package/dist/device/fan.js.map +1 -1
- package/dist/device/hub.d.ts +6 -3
- package/dist/device/hub.d.ts.map +1 -1
- package/dist/device/hub.js +43 -26
- package/dist/device/hub.js.map +1 -1
- package/dist/device/humidifier.d.ts +6 -3
- package/dist/device/humidifier.d.ts.map +1 -1
- package/dist/device/humidifier.js +43 -26
- package/dist/device/humidifier.js.map +1 -1
- package/dist/device/iosensor.d.ts +6 -3
- package/dist/device/iosensor.d.ts.map +1 -1
- package/dist/device/iosensor.js +39 -24
- package/dist/device/iosensor.js.map +1 -1
- package/dist/device/lightstrip.d.ts +6 -3
- package/dist/device/lightstrip.d.ts.map +1 -1
- package/dist/device/lightstrip.js +43 -27
- package/dist/device/lightstrip.js.map +1 -1
- package/dist/device/lock.d.ts +6 -3
- package/dist/device/lock.d.ts.map +1 -1
- package/dist/device/lock.js +33 -22
- package/dist/device/lock.js.map +1 -1
- package/dist/device/meter.d.ts +6 -3
- package/dist/device/meter.d.ts.map +1 -1
- package/dist/device/meter.js +38 -24
- package/dist/device/meter.js.map +1 -1
- package/dist/device/meterplus.d.ts +6 -3
- package/dist/device/meterplus.d.ts.map +1 -1
- package/dist/device/meterplus.js +37 -24
- package/dist/device/meterplus.js.map +1 -1
- package/dist/device/motion.d.ts +6 -3
- package/dist/device/motion.d.ts.map +1 -1
- package/dist/device/motion.js +33 -22
- package/dist/device/motion.js.map +1 -1
- package/dist/device/plug.d.ts +6 -3
- package/dist/device/plug.d.ts.map +1 -1
- package/dist/device/plug.js +27 -16
- package/dist/device/plug.js.map +1 -1
- package/dist/device/robotvacuumcleaner.d.ts +6 -3
- package/dist/device/robotvacuumcleaner.d.ts.map +1 -1
- package/dist/device/robotvacuumcleaner.js +34 -21
- package/dist/device/robotvacuumcleaner.js.map +1 -1
- package/dist/device/waterdetector.d.ts +6 -3
- package/dist/device/waterdetector.d.ts.map +1 -1
- package/dist/device/waterdetector.js +38 -23
- package/dist/device/waterdetector.js.map +1 -1
- package/dist/irdevice/airconditioner.d.ts.map +1 -1
- package/dist/irdevice/airconditioner.js +19 -72
- package/dist/irdevice/airconditioner.js.map +1 -1
- package/dist/irdevice/airpurifier.js +4 -4
- package/dist/irdevice/airpurifier.js.map +1 -1
- package/dist/irdevice/camera.d.ts.map +1 -1
- package/dist/irdevice/camera.js +3 -10
- package/dist/irdevice/camera.js.map +1 -1
- package/dist/irdevice/fan.d.ts.map +1 -1
- package/dist/irdevice/fan.js +7 -25
- package/dist/irdevice/fan.js.map +1 -1
- package/dist/irdevice/irdevice.d.ts +1 -1
- package/dist/irdevice/irdevice.js +1 -1
- package/dist/irdevice/irdevice.js.map +1 -1
- package/dist/irdevice/light.d.ts.map +1 -1
- package/dist/irdevice/light.js +9 -27
- package/dist/irdevice/light.js.map +1 -1
- package/dist/irdevice/other.js +1 -1
- package/dist/irdevice/other.js.map +1 -1
- package/dist/irdevice/tv.d.ts.map +1 -1
- package/dist/irdevice/tv.js +3 -18
- package/dist/irdevice/tv.js.map +1 -1
- package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
- package/dist/irdevice/vacuumcleaner.js +2 -9
- package/dist/irdevice/vacuumcleaner.js.map +1 -1
- package/dist/irdevice/waterheater.d.ts.map +1 -1
- package/dist/irdevice/waterheater.js +2 -9
- package/dist/irdevice/waterheater.js.map +1 -1
- package/dist/platform.d.ts +0 -11
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +11 -184
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +9 -8
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/dist/types/bledevicestatus.d.ts +2 -1
- package/dist/types/bledevicestatus.d.ts.map +1 -1
- package/dist/types/devicestatus.d.ts +1 -1
- package/dist/types/devicestatus.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/device/blindtilt.ts +60 -47
- package/src/device/bot.ts +47 -25
- package/src/device/ceilinglight.ts +52 -21
- package/src/device/colorbulb.ts +45 -31
- package/src/device/contact.ts +43 -28
- package/src/device/curtain.ts +45 -23
- package/src/device/device.ts +226 -96
- package/src/device/fan.ts +61 -30
- package/src/device/hub.ts +55 -29
- package/src/device/humidifier.ts +55 -31
- package/src/device/iosensor.ts +45 -27
- package/src/device/lightstrip.ts +48 -28
- package/src/device/lock.ts +49 -23
- package/src/device/meter.ts +54 -26
- package/src/device/meterplus.ts +50 -26
- package/src/device/motion.ts +45 -24
- package/src/device/plug.ts +34 -17
- package/src/device/robotvacuumcleaner.ts +49 -23
- package/src/device/waterdetector.ts +56 -25
- package/src/irdevice/airconditioner.ts +19 -65
- package/src/irdevice/airpurifier.ts +4 -4
- package/src/irdevice/camera.ts +4 -9
- package/src/irdevice/fan.ts +10 -22
- package/src/irdevice/irdevice.ts +1 -1
- package/src/irdevice/light.ts +10 -24
- package/src/irdevice/other.ts +1 -1
- package/src/irdevice/tv.ts +5 -16
- package/src/irdevice/vacuumcleaner.ts +3 -8
- package/src/irdevice/waterheater.ts +3 -8
- package/src/platform.ts +11 -184
- package/src/settings.ts +10 -8
- package/src/types/bledevicestatus.ts +4 -2
- package/src/types/devicestatus.ts +1 -1
package/src/device/curtain.ts
CHANGED
|
@@ -40,6 +40,15 @@ export class Curtain extends deviceBase {
|
|
|
40
40
|
CurrentAmbientLightLevel?: CharacteristicValue;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
// OpenAPI
|
|
44
|
+
deviceStatus!: curtainStatus;
|
|
45
|
+
|
|
46
|
+
//Webhook
|
|
47
|
+
webhookContext!: curtainWebhookContext | curtain3WebhookContext;
|
|
48
|
+
|
|
49
|
+
// BLE
|
|
50
|
+
serviceData!: curtainServiceData | curtain3ServiceData;
|
|
51
|
+
|
|
43
52
|
// Target
|
|
44
53
|
setNewTarget!: boolean;
|
|
45
54
|
setNewTargetTimer!: NodeJS.Timeout;
|
|
@@ -175,12 +184,11 @@ export class Curtain extends deviceBase {
|
|
|
175
184
|
}
|
|
176
185
|
|
|
177
186
|
// Retrieve initial values and updateHomekit
|
|
187
|
+
this.debugLog('Retrieve initial values and update Homekit');
|
|
178
188
|
this.refreshStatus();
|
|
179
189
|
|
|
180
|
-
// Update Homekit
|
|
181
|
-
this.updateHomeKitCharacteristics();
|
|
182
|
-
|
|
183
190
|
//regisiter webhook event handler
|
|
191
|
+
this.debugLog('Registering Webhook Event Handler');
|
|
184
192
|
this.registerWebhook();
|
|
185
193
|
|
|
186
194
|
// History
|
|
@@ -293,22 +301,24 @@ export class Curtain extends deviceBase {
|
|
|
293
301
|
}, 10 * 60 * 1000);
|
|
294
302
|
}
|
|
295
303
|
|
|
296
|
-
async BLEparseStatus(
|
|
304
|
+
async BLEparseStatus(): Promise<void> {
|
|
297
305
|
await this.debugLog('BLEparseStatus');
|
|
306
|
+
await this.debugLog(`(position, battery) = BLE:(${this.serviceData.position}, ${this.serviceData.battery}),`
|
|
307
|
+
+ ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
|
|
298
308
|
// CurrentPosition
|
|
299
|
-
this.WindowCovering.CurrentPosition = 100 -
|
|
309
|
+
this.WindowCovering.CurrentPosition = 100 -this.serviceData.position;
|
|
300
310
|
await this.getCurrentPostion();
|
|
301
311
|
// CurrentAmbientLightLevel
|
|
302
312
|
if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) {
|
|
303
313
|
const set_minLux = this.device.curtain?.set_minLux ?? 1;
|
|
304
314
|
const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
|
|
305
|
-
const lightLevel =
|
|
315
|
+
const lightLevel = this.serviceData.lightLevel;
|
|
306
316
|
this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 19);
|
|
307
|
-
this.debugLog(`LightLevel: ${serviceData.lightLevel},`
|
|
317
|
+
this.debugLog(`LightLevel: ${this.serviceData.lightLevel},`
|
|
308
318
|
+ ` CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`);
|
|
309
319
|
}
|
|
310
320
|
// BatteryLevel
|
|
311
|
-
this.Battery.BatteryLevel =
|
|
321
|
+
this.Battery.BatteryLevel = this.serviceData.battery;
|
|
312
322
|
await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
|
|
313
323
|
// StatusLowBattery
|
|
314
324
|
this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
|
|
@@ -316,16 +326,18 @@ export class Curtain extends deviceBase {
|
|
|
316
326
|
await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
|
|
317
327
|
}
|
|
318
328
|
|
|
319
|
-
async openAPIparseStatus(
|
|
329
|
+
async openAPIparseStatus(): Promise<void> {
|
|
320
330
|
await this.debugLog('openAPIparseStatus');
|
|
331
|
+
await this.debugLog(`(slidePosition, battery) = OpenAPI:(${this.deviceStatus.slidePosition}, ${this.deviceStatus.battery}),`
|
|
332
|
+
+ ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
|
|
321
333
|
// CurrentPosition
|
|
322
|
-
this.WindowCovering.CurrentPosition = 100 - deviceStatus.slidePosition;
|
|
334
|
+
this.WindowCovering.CurrentPosition = 100 - this.deviceStatus.slidePosition;
|
|
323
335
|
await this.setMinMax();
|
|
324
336
|
await this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
|
|
325
337
|
if (this.setNewTarget) {
|
|
326
338
|
await this.infoLog('Checking Status ...');
|
|
327
339
|
}
|
|
328
|
-
if (this.setNewTarget && deviceStatus.moving) {
|
|
340
|
+
if (this.setNewTarget && this.deviceStatus.moving) {
|
|
329
341
|
await this.setMinMax();
|
|
330
342
|
if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
|
|
331
343
|
await this.debugLog(`Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
|
|
@@ -351,25 +363,29 @@ export class Curtain extends deviceBase {
|
|
|
351
363
|
}
|
|
352
364
|
await this.debugLog(`CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition},`
|
|
353
365
|
+ ` PositionState: ${this.WindowCovering.PositionState},`);
|
|
366
|
+
|
|
354
367
|
// Brightness
|
|
355
368
|
if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) {
|
|
356
369
|
const set_minLux = this.device.curtain?.set_minLux ?? 1;
|
|
357
370
|
const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
|
|
358
|
-
const lightLevel = deviceStatus.lightLevel === 'bright' ? set_maxLux : set_minLux;
|
|
371
|
+
const lightLevel = this.deviceStatus.lightLevel === 'bright' ? set_maxLux : set_minLux;
|
|
359
372
|
this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2);
|
|
360
373
|
await this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
|
|
361
374
|
+ ` CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`);
|
|
362
375
|
}
|
|
376
|
+
|
|
363
377
|
// BatteryLevel
|
|
364
|
-
this.Battery.BatteryLevel =
|
|
378
|
+
this.Battery.BatteryLevel = this.deviceStatus.battery;
|
|
365
379
|
await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
|
|
380
|
+
|
|
366
381
|
// StatusLowBattery
|
|
367
382
|
this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
|
|
368
383
|
? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
369
384
|
await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`);
|
|
385
|
+
|
|
370
386
|
// Firmware Version
|
|
371
|
-
if (deviceStatus.version) {
|
|
372
|
-
const version = deviceStatus.version.toString();
|
|
387
|
+
if (this.deviceStatus.version) {
|
|
388
|
+
const version = this.deviceStatus.version.toString();
|
|
373
389
|
await this.debugLog(`Firmware Version: ${version.replace(/^V|-.*$/g, '')}`);
|
|
374
390
|
const deviceVersion = version.replace(/^V|-.*$/g, '') ?? '0.0.0';
|
|
375
391
|
this.accessory
|
|
@@ -383,16 +399,19 @@ export class Curtain extends deviceBase {
|
|
|
383
399
|
}
|
|
384
400
|
}
|
|
385
401
|
|
|
386
|
-
async parseStatusWebhook(
|
|
402
|
+
async parseStatusWebhook(): Promise<void> {
|
|
387
403
|
await this.debugLog('parseStatusWebhook');
|
|
388
|
-
await this.debugLog(`(slidePosition, battery) = Webhook:(${
|
|
404
|
+
await this.debugLog(`(slidePosition, battery) = Webhook:(${this.webhookContext.slidePosition}, ${this.webhookContext.battery}),`
|
|
389
405
|
+ ` current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
|
|
406
|
+
|
|
390
407
|
// CurrentPosition
|
|
391
|
-
this.WindowCovering.CurrentPosition = 100 -
|
|
408
|
+
this.WindowCovering.CurrentPosition = 100 - this.webhookContext.slidePosition;
|
|
392
409
|
await this.getCurrentPostion();
|
|
410
|
+
|
|
393
411
|
// BatteryLevel
|
|
394
|
-
this.Battery.BatteryLevel =
|
|
412
|
+
this.Battery.BatteryLevel = this.webhookContext.battery;
|
|
395
413
|
await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`);
|
|
414
|
+
|
|
396
415
|
// StatusLowBattery
|
|
397
416
|
this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10
|
|
398
417
|
? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
@@ -429,7 +448,8 @@ export class Curtain extends deviceBase {
|
|
|
429
448
|
// Update HomeKit
|
|
430
449
|
if ((serviceData.model === SwitchBotBLEModel.Curtain || SwitchBotBLEModel.Curtain3)
|
|
431
450
|
&& (serviceData.modelName === SwitchBotBLEModelName.Curtain || SwitchBotBLEModelName.Curtain3)) {
|
|
432
|
-
|
|
451
|
+
this.serviceData = serviceData;
|
|
452
|
+
await this.BLEparseStatus();
|
|
433
453
|
await this.updateHomeKitCharacteristics();
|
|
434
454
|
} else {
|
|
435
455
|
await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`);
|
|
@@ -447,7 +467,8 @@ export class Curtain extends deviceBase {
|
|
|
447
467
|
await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);;
|
|
448
468
|
if (await this.successfulStatusCodes(statusCode, deviceStatus)) {
|
|
449
469
|
await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
|
|
450
|
-
|
|
470
|
+
this.deviceStatus = deviceStatus.body;
|
|
471
|
+
await this.openAPIparseStatus();
|
|
451
472
|
await this.updateHomeKitCharacteristics();
|
|
452
473
|
} else {
|
|
453
474
|
await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`);
|
|
@@ -462,10 +483,11 @@ export class Curtain extends deviceBase {
|
|
|
462
483
|
async registerWebhook() {
|
|
463
484
|
if (this.device.webhook) {
|
|
464
485
|
await this.debugLog('is listening webhook.');
|
|
465
|
-
this.
|
|
486
|
+
this.webhookEventHandler[this.device.deviceId] = async (context: curtainWebhookContext | curtain3WebhookContext) => {
|
|
466
487
|
try {
|
|
467
488
|
await this.debugLog(`received Webhook: ${JSON.stringify(context)}`);
|
|
468
|
-
|
|
489
|
+
this.webhookContext = context;
|
|
490
|
+
await this.parseStatusWebhook();
|
|
469
491
|
await this.updateHomeKitCharacteristics();
|
|
470
492
|
} catch (e: any) {
|
|
471
493
|
await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
|
package/src/device/device.ts
CHANGED
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
import { hostname } from 'os';
|
|
7
7
|
import { request } from 'undici';
|
|
8
8
|
import asyncmqtt from 'async-mqtt';
|
|
9
|
-
import {
|
|
9
|
+
import { createServer } from 'http';
|
|
10
10
|
import { BlindTiltMappingMode, sleep } from '../utils.js';
|
|
11
|
+
import { Devices, deleteWebhook, queryWebhook, setupWebhook, updateWebhook } from '../settings.js';
|
|
12
|
+
import { SwitchBotModel, SwitchBotBLEModel, SwitchBotBLEModelName, SwitchBotBLEModelFriendlyName } from 'node-switchbot';
|
|
11
13
|
|
|
12
14
|
import type { MqttClient } from 'mqtt';
|
|
13
15
|
import type { device } from '../types/devicelist.js';
|
|
14
16
|
import type { ad } from '../types/bledevicestatus.js';
|
|
15
17
|
import type { SwitchBotPlatform } from '../platform.js';
|
|
18
|
+
import type { Server, IncomingMessage, ServerResponse } from 'http';
|
|
16
19
|
import type { SwitchBotPlatformConfig, devicesConfig } from '../settings.js';
|
|
17
20
|
import type { API, CharacteristicValue, HAP, Logging, PlatformAccessory, Service } from 'homebridge';
|
|
18
|
-
import { SwitchBotModel, SwitchBotBLEModel, SwitchBotBLEModelName, SwitchBotBLEModelFriendlyName } from 'node-switchbot';
|
|
19
21
|
|
|
20
22
|
export abstract class deviceBase {
|
|
21
23
|
public readonly api: API;
|
|
@@ -39,6 +41,15 @@ export abstract class deviceBase {
|
|
|
39
41
|
protected deviceModel!: SwitchBotModel;
|
|
40
42
|
protected deviceBLEModel!: SwitchBotBLEModel;
|
|
41
43
|
|
|
44
|
+
// Webhook
|
|
45
|
+
protected readonly webhookEventHandler: { [x: string]: (context: any) => void } = {};
|
|
46
|
+
protected webhookEventListener: Server | null = null;
|
|
47
|
+
|
|
48
|
+
// MQTT
|
|
49
|
+
protected deviceMqttURL!: string;
|
|
50
|
+
protected deviceMqttOptions!: any;
|
|
51
|
+
protected deviceMqttPubOptions!: any;
|
|
52
|
+
|
|
42
53
|
// BLE
|
|
43
54
|
protected scanDuration!: number;
|
|
44
55
|
|
|
@@ -48,6 +59,8 @@ export abstract class deviceBase {
|
|
|
48
59
|
//MQTT stuff
|
|
49
60
|
protected mqttClient: MqttClient | null = null;
|
|
50
61
|
|
|
62
|
+
|
|
63
|
+
|
|
51
64
|
constructor(
|
|
52
65
|
protected readonly platform: SwitchBotPlatform,
|
|
53
66
|
protected accessory: PlatformAccessory,
|
|
@@ -68,7 +81,9 @@ export abstract class deviceBase {
|
|
|
68
81
|
this.getDeviceConfigSettings(accessory, device);
|
|
69
82
|
this.getDeviceContext(accessory, device);
|
|
70
83
|
this.getDeviceScanDuration(accessory, device);
|
|
84
|
+
this.getMqttSettings(device);
|
|
71
85
|
this.setupMqtt(device);
|
|
86
|
+
this.setupwebhook();
|
|
72
87
|
|
|
73
88
|
// Set accessory information
|
|
74
89
|
accessory
|
|
@@ -76,82 +91,41 @@ export abstract class deviceBase {
|
|
|
76
91
|
.setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
|
|
77
92
|
.setCharacteristic(this.hap.Characteristic.AppMatchingIdentifier, 'id1087374760')
|
|
78
93
|
.setCharacteristic(this.hap.Characteristic.Name, device.deviceName ?? accessory.displayName)
|
|
79
|
-
|
|
94
|
+
//.setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName ?? accessory.displayName)
|
|
80
95
|
.setCharacteristic(this.hap.Characteristic.Model, device.model ?? accessory.context.model)
|
|
81
96
|
.setCharacteristic(this.hap.Characteristic.ProductData, device.deviceId ?? accessory.context.deviceId)
|
|
82
97
|
.setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId);
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
async getDeviceLogSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
this.deviceLogging = accessory.context.logging = device.logging;
|
|
91
|
-
this.debugWarnLog(`Using Device Config Logging: ${this.deviceLogging}`);
|
|
92
|
-
} else if (this.config.logging) {
|
|
93
|
-
this.deviceLogging = accessory.context.logging = this.config.logging;
|
|
94
|
-
this.debugWarnLog(`Using Platform Config Logging: ${this.deviceLogging}`);
|
|
95
|
-
} else {
|
|
96
|
-
this.deviceLogging = accessory.context.logging = 'standard';
|
|
97
|
-
this.debugWarnLog(`Logging Not Set, Using: ${this.deviceLogging}`);
|
|
98
|
-
}
|
|
101
|
+
this.deviceLogging = this.platform.debugMode ? 'debugMode' : device.logging ?? this.config.logging ?? 'standard';
|
|
102
|
+
const logging = this.platform.debugMode ? 'Debug Mode' : device.logging ? 'Device Config' : this.config.logging ? 'Platform Config' : 'Default';
|
|
103
|
+
accessory.context.deviceLogging = this.deviceLogging;
|
|
104
|
+
await this.debugLog(`Using ${logging} Logging: ${this.deviceLogging}`);
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
async getDeviceRateSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
|
|
102
108
|
// refreshRate
|
|
103
|
-
|
|
104
|
-
this.deviceRefreshRate = device.refreshRate;
|
|
105
|
-
this.debugLog(`Using Device Config refreshRate: ${this.deviceRefreshRate}`);
|
|
106
|
-
} else if (this.config.options?.refreshRate) {
|
|
107
|
-
this.deviceRefreshRate = this.config.options.refreshRate;
|
|
108
|
-
this.debugLog(`Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
|
|
109
|
-
} else {
|
|
110
|
-
this.deviceRefreshRate = 5;
|
|
111
|
-
this.debugLog(`Using Default refreshRate: ${this.deviceRefreshRate}`);
|
|
112
|
-
}
|
|
109
|
+
this.deviceRefreshRate = device.refreshRate ?? this.config.options?.refreshRate ?? 5;
|
|
113
110
|
accessory.context.deviceRefreshRate = this.deviceRefreshRate;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this.deviceUpdateRate = device.updateRate;
|
|
117
|
-
this.debugLog(`Using Device Config updateRate: ${this.deviceUpdateRate}`);
|
|
118
|
-
} else if (this.config.options?.updateRate) {
|
|
119
|
-
this.deviceUpdateRate = this.config.options.updateRate;
|
|
120
|
-
this.debugLog(`Using Platform Config updateRate: ${this.deviceUpdateRate}`);
|
|
121
|
-
} else {
|
|
122
|
-
this.deviceUpdateRate = 5;
|
|
123
|
-
this.debugLog(`Using Default updateRate: ${this.deviceUpdateRate}`);
|
|
124
|
-
}
|
|
111
|
+
const refreshRate = device.refreshRate ? 'Device Config' : this.config.options?.refreshRate ? 'Platform Config' : 'Default';
|
|
112
|
+
this.deviceUpdateRate = device.updateRate ?? this.config.options?.updateRate ?? 5;
|
|
125
113
|
accessory.context.deviceUpdateRate = this.deviceUpdateRate;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
this.devicePushRate = device.pushRate;
|
|
129
|
-
this.debugLog(`Using Device Config pushRate: ${this.deviceUpdateRate}`);
|
|
130
|
-
} else if (this.config.options?.pushRate) {
|
|
131
|
-
this.devicePushRate = this.config.options.pushRate;
|
|
132
|
-
this.debugLog(`Using Platform Config pushRate: ${this.deviceUpdateRate}`);
|
|
133
|
-
} else {
|
|
134
|
-
this.devicePushRate = 1;
|
|
135
|
-
this.debugLog(`Using Default pushRate: ${this.deviceUpdateRate}`);
|
|
136
|
-
}
|
|
114
|
+
const updateRate = device.updateRate ? 'Device Config' : this.config.options?.updateRate ? 'Platform Config' : 'Default';
|
|
115
|
+
this.devicePushRate = device.pushRate ?? this.config.options?.pushRate ?? 1;
|
|
137
116
|
accessory.context.devicePushRate = this.devicePushRate;
|
|
117
|
+
const pushRate = device.pushRate ? 'Device Config' : this.config.options?.pushRate ? 'Platform Config' : 'Default';
|
|
118
|
+
await this.debugLog(`Using ${refreshRate} refreshRate: ${this.deviceRefreshRate}, ${updateRate} updateRate: ${this.deviceUpdateRate},`
|
|
119
|
+
+ ` ${pushRate} pushRate: ${this.devicePushRate}`);
|
|
138
120
|
}
|
|
139
121
|
|
|
140
122
|
async getDeviceRetry(device: device & devicesConfig): Promise<void> {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
if (device.delayBetweenRetries) {
|
|
149
|
-
this.deviceDelayBetweenRetries = device.delayBetweenRetries * 1000;
|
|
150
|
-
this.debugLog(`Using Device Delay Between Retries: ${this.deviceDelayBetweenRetries}`);
|
|
151
|
-
} else {
|
|
152
|
-
this.deviceDelayBetweenRetries = 3000; // Delay between retries in milliseconds
|
|
153
|
-
this.debugLog(`Delay Between Retries Not Set, Using: ${this.deviceDelayBetweenRetries}`);
|
|
154
|
-
}
|
|
123
|
+
this.deviceMaxRetries = device.maxRetries ?? 5;
|
|
124
|
+
const maxRetries = device.maxRetries ? 'Device' : 'Default';
|
|
125
|
+
this.deviceDelayBetweenRetries = device.delayBetweenRetries ? (device.delayBetweenRetries * 1000) : 3000;
|
|
126
|
+
const delayBetweenRetries = device.delayBetweenRetries ? 'Device' : 'Default';
|
|
127
|
+
await this.debugLog(`Using ${maxRetries} Max Retries: ${this.deviceMaxRetries},`
|
|
128
|
+
+ ` ${delayBetweenRetries} Delay Between Retries: ${this.deviceDelayBetweenRetries}`);
|
|
155
129
|
}
|
|
156
130
|
|
|
157
131
|
async retryBLE({ max, fn }: { max: number; fn: { (): any; (): Promise<any> } }): Promise<null> {
|
|
@@ -167,14 +141,12 @@ export abstract class deviceBase {
|
|
|
167
141
|
}
|
|
168
142
|
|
|
169
143
|
async maxRetryBLE(): Promise<number> {
|
|
170
|
-
|
|
171
|
-
return this.device.maxRetry;
|
|
172
|
-
} else {
|
|
173
|
-
return 5;
|
|
174
|
-
}
|
|
144
|
+
return this.device.maxRetry ? this.device.maxRetry : 5;
|
|
175
145
|
}
|
|
176
146
|
|
|
177
147
|
async getDeviceScanDuration(accessory: PlatformAccessory, device: device & devicesConfig): Promise<void> {
|
|
148
|
+
this.scanDuration = device.scanDuration ? (this.deviceUpdateRate > device.scanDuration) ? this.deviceUpdateRate : device.scanDuration
|
|
149
|
+
? (this.deviceUpdateRate > 1) ? this.deviceUpdateRate : 1 : this.deviceUpdateRate : 1;
|
|
178
150
|
if (device.scanDuration) {
|
|
179
151
|
if (this.deviceUpdateRate > device.scanDuration) {
|
|
180
152
|
this.scanDuration = this.deviceUpdateRate;
|
|
@@ -319,21 +291,10 @@ export abstract class deviceBase {
|
|
|
319
291
|
const numberOfLevels = spaceBetweenLevels + 1;
|
|
320
292
|
this.debugLog(`LightLevel: ${lightLevel}, set_minLux: ${set_minLux}, set_maxLux: ${set_maxLux}, spaceBetweenLevels: ${spaceBetweenLevels},`
|
|
321
293
|
+ ` numberOfLevels: ${numberOfLevels}`);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
this.debugLog(`LightLevel: ${lightLevel}, set_minLux: ${set_minLux}`);
|
|
327
|
-
break;
|
|
328
|
-
case numberOfLevels:
|
|
329
|
-
CurrentAmbientLightLevel = set_maxLux;
|
|
330
|
-
this.debugLog(`LightLevel: ${lightLevel}, set_maxLux: ${set_maxLux}`);
|
|
331
|
-
break;
|
|
332
|
-
default:
|
|
333
|
-
CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(lightLevel) - 1);
|
|
334
|
-
this.debugLog(`LightLevel: ${lightLevel}`);
|
|
335
|
-
}
|
|
336
|
-
this.debugLog(`CurrentAmbientLightLevel: ${CurrentAmbientLightLevel}`);
|
|
294
|
+
const CurrentAmbientLightLevel = lightLevel === 1 ? set_minLux : lightLevel = numberOfLevels
|
|
295
|
+
? set_maxLux : ((set_maxLux - set_minLux) / spaceBetweenLevels) * (Number(lightLevel) - 1);
|
|
296
|
+
await this.debugLog(`CurrentAmbientLightLevel: ${CurrentAmbientLightLevel}, LightLevel: ${lightLevel}, set_minLux: ${set_minLux},`
|
|
297
|
+
+ ` set_maxLux: ${set_maxLux}`);
|
|
337
298
|
return CurrentAmbientLightLevel;
|
|
338
299
|
}
|
|
339
300
|
|
|
@@ -342,24 +303,31 @@ export abstract class deviceBase {
|
|
|
342
303
|
* 'homebridge-switchbot/${this.device.deviceType}/xx:xx:xx:xx:xx:xx'
|
|
343
304
|
*/
|
|
344
305
|
async mqttPublish(message: string, topic?: string) {
|
|
345
|
-
const mac = this.device.deviceId
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const options = this.device.mqttPubOptions || {};
|
|
350
|
-
let mqttTopic: string;
|
|
351
|
-
let mqttMessageTopic: string;
|
|
352
|
-
if (topic) {
|
|
353
|
-
mqttTopic = `/${topic}`;
|
|
354
|
-
mqttMessageTopic = `${topic}/`;
|
|
355
|
-
} else {
|
|
356
|
-
mqttTopic = '';
|
|
357
|
-
mqttMessageTopic = '';
|
|
358
|
-
}
|
|
306
|
+
const mac = this.device.deviceId?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
|
|
307
|
+
const options = this.device.mqttPubOptions ?? {};
|
|
308
|
+
const mqttTopic = topic ? `/${topic}` : '';
|
|
309
|
+
const mqttMessageTopic = topic ? `${topic}/` : '';
|
|
359
310
|
this.mqttClient?.publish(`homebridge-switchbot/${this.device.deviceType}/${mac}${mqttTopic}`, `${message}`, options);
|
|
360
311
|
this.debugLog(`MQTT message: ${mqttMessageTopic}${message} options:${JSON.stringify(options)}`);
|
|
361
312
|
}
|
|
362
313
|
|
|
314
|
+
/*
|
|
315
|
+
* MQTT Settings
|
|
316
|
+
*/
|
|
317
|
+
async getMqttSettings(device: device & devicesConfig): Promise<void> {
|
|
318
|
+
// mqttURL
|
|
319
|
+
this.deviceMqttURL = device.mqttURL ?? this.config.options?.mqttURL ?? '';
|
|
320
|
+
const mqttURL = device.mqttURL ? 'Device Config' : this.config.options?.mqttURL ? 'Platform Config' : 'Default';
|
|
321
|
+
// mqttOptions
|
|
322
|
+
this.deviceMqttOptions = device.mqttOptions ?? this.config.options?.mqttOptions ?? {};
|
|
323
|
+
const mqttOptions = device.mqttOptions ? 'Device Config' : this.config.options?.mqttOptions ? 'Platform Config' : 'Default';
|
|
324
|
+
// mqttPubOptions
|
|
325
|
+
this.deviceMqttPubOptions = device.mqttPubOptions ?? this.config.options?.mqttPubOptions ?? {};
|
|
326
|
+
const mqttPubOptions = device.mqttPubOptions ? 'Device Config' : this.config.options?.mqttPubOptions ? 'Platform Config' : 'Default';
|
|
327
|
+
await this.debugLog(`Using ${mqttURL} MQTT URL: ${this.deviceMqttURL}, ${mqttOptions} mqttOptions: ${JSON.stringify(this.deviceMqttOptions)},`
|
|
328
|
+
+ ` ${mqttPubOptions} mqttPubOptions: ${JSON.stringify(this.deviceMqttPubOptions)}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
363
331
|
/*
|
|
364
332
|
* Setup MQTT hadler if URL is specified.
|
|
365
333
|
*/
|
|
@@ -379,6 +347,168 @@ export abstract class deviceBase {
|
|
|
379
347
|
}
|
|
380
348
|
}
|
|
381
349
|
|
|
350
|
+
async setupDeviceMqtt(): Promise<void> {
|
|
351
|
+
if (this.config.options?.mqttURL) {
|
|
352
|
+
try {
|
|
353
|
+
const { connectAsync } = asyncmqtt;
|
|
354
|
+
this.mqttClient = await connectAsync(this.config.options?.mqttURL, this.config.options.mqttOptions || {});
|
|
355
|
+
await this.debugLog('MQTT connection has been established successfully.');
|
|
356
|
+
this.mqttClient.on('error', async (e: Error) => {
|
|
357
|
+
await this.errorLog(`Failed to publish MQTT messages. ${e}`);
|
|
358
|
+
});
|
|
359
|
+
if (!this.config.options?.webhookURL) {
|
|
360
|
+
// receive webhook events via MQTT
|
|
361
|
+
await this.infoLog(`Webhook is configured to be received through ${this.config.options.mqttURL}/homebridge-switchbot/webhook.`);
|
|
362
|
+
this.mqttClient.subscribe('homebridge-switchbot/webhook/+');
|
|
363
|
+
this.mqttClient.on('message', async (topic: string, message) => {
|
|
364
|
+
try {
|
|
365
|
+
await this.debugLog(`Received Webhook via MQTT: ${topic}=${message}`);
|
|
366
|
+
const context = JSON.parse(message.toString());
|
|
367
|
+
this.webhookEventHandler[context.deviceMac]?.(context);
|
|
368
|
+
} catch (e: any) {
|
|
369
|
+
await this.errorLog(`Failed to handle webhook event. Error:${e}`);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
} catch (e) {
|
|
374
|
+
this.mqttClient = null;
|
|
375
|
+
await this.errorLog(`Failed to establish MQTT connection. ${e}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async setupwebhook() {
|
|
381
|
+
//webhook configuration
|
|
382
|
+
if (this.config.options?.webhookURL) {
|
|
383
|
+
const url = this.config.options?.webhookURL;
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
const xurl = new URL(url);
|
|
387
|
+
const port = Number(xurl.port);
|
|
388
|
+
const path = xurl.pathname;
|
|
389
|
+
this.webhookEventListener = createServer((request: IncomingMessage, response: ServerResponse) => {
|
|
390
|
+
try {
|
|
391
|
+
if (request.url === path && request.method === 'POST') {
|
|
392
|
+
request.on('data', async (data) => {
|
|
393
|
+
try {
|
|
394
|
+
const body = JSON.parse(data);
|
|
395
|
+
await this.debugLog(`Received Webhook: ${JSON.stringify(body)}`);
|
|
396
|
+
if (this.config.options?.mqttURL) {
|
|
397
|
+
const mac = body.context.deviceMac
|
|
398
|
+
?.toLowerCase()
|
|
399
|
+
.match(/[\s\S]{1,2}/g)
|
|
400
|
+
?.join(':');
|
|
401
|
+
const options = this.config.options?.mqttPubOptions || {};
|
|
402
|
+
this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options);
|
|
403
|
+
}
|
|
404
|
+
this.webhookEventHandler[body.context.deviceMac]?.(body.context);
|
|
405
|
+
} catch (e: any) {
|
|
406
|
+
this.errorLog(`Failed to handle webhook event. Error:${e}`);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
response.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
410
|
+
response.end('OK');
|
|
411
|
+
}
|
|
412
|
+
// else {
|
|
413
|
+
// response.writeHead(403, {'Content-Type': 'text/plain'});
|
|
414
|
+
// response.end(`NG`);
|
|
415
|
+
// }
|
|
416
|
+
} catch (e: any) {
|
|
417
|
+
this.errorLog(`Failed to handle webhook event. Error:${e}`);
|
|
418
|
+
}
|
|
419
|
+
}).listen(port ? port : 80);
|
|
420
|
+
} catch (e: any) {
|
|
421
|
+
this.errorLog(`Failed to create webhook listener. Error:${e.message}`);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
const { body, statusCode } = await request(setupWebhook, {
|
|
427
|
+
method: 'POST',
|
|
428
|
+
headers: this.platform.generateHeaders(),
|
|
429
|
+
body: JSON.stringify({
|
|
430
|
+
'action': 'setupWebhook',
|
|
431
|
+
'url': url,
|
|
432
|
+
'deviceList': 'ALL',
|
|
433
|
+
}),
|
|
434
|
+
});
|
|
435
|
+
const response: any = await body.json();
|
|
436
|
+
await this.debugLog(`setupWebhook: url:${url}`);
|
|
437
|
+
await this.debugLog(`setupWebhook: body:${JSON.stringify(response)}`);
|
|
438
|
+
await this.debugLog(`setupWebhook: statusCode:${statusCode}`);
|
|
439
|
+
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
440
|
+
this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} `
|
|
441
|
+
+ `message:${response?.message}`);
|
|
442
|
+
}
|
|
443
|
+
} catch (e: any) {
|
|
444
|
+
this.errorLog(`Failed to configure webhook. Error: ${e.message}`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
try {
|
|
448
|
+
const { body, statusCode } = await request(updateWebhook, {
|
|
449
|
+
method: 'POST', headers: this.platform.generateHeaders(), body: JSON.stringify({
|
|
450
|
+
'action': 'updateWebhook',
|
|
451
|
+
'config': {
|
|
452
|
+
'url': url,
|
|
453
|
+
'enable': true,
|
|
454
|
+
},
|
|
455
|
+
}),
|
|
456
|
+
});
|
|
457
|
+
const response: any = await body.json();
|
|
458
|
+
await this.debugLog(`updateWebhook: url:${url}`);
|
|
459
|
+
await this.debugLog(`updateWebhook: body:${JSON.stringify(response)}`);
|
|
460
|
+
await this.debugLog(`updateWebhook: statusCode:${statusCode}`);
|
|
461
|
+
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
462
|
+
this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
463
|
+
}
|
|
464
|
+
} catch (e: any) {
|
|
465
|
+
this.errorLog(`Failed to update webhook. Error:${e.message}`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
const { body, statusCode } = await request(queryWebhook, {
|
|
470
|
+
method: 'POST',
|
|
471
|
+
headers: this.platform.generateHeaders(),
|
|
472
|
+
body: JSON.stringify({
|
|
473
|
+
'action': 'queryUrl',
|
|
474
|
+
}),
|
|
475
|
+
});
|
|
476
|
+
const response: any = await body.json();
|
|
477
|
+
await this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`);
|
|
478
|
+
await this.debugLog(`queryWebhook: statusCode:${statusCode}`);
|
|
479
|
+
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
480
|
+
this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
481
|
+
} else {
|
|
482
|
+
this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`);
|
|
483
|
+
}
|
|
484
|
+
} catch (e: any) {
|
|
485
|
+
this.errorLog(`Failed to query webhook. Error:${e}`);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
this.api.on('shutdown', async () => {
|
|
489
|
+
try {
|
|
490
|
+
const { body, statusCode } = await request(deleteWebhook, {
|
|
491
|
+
method: 'POST',
|
|
492
|
+
headers: this.platform.generateHeaders(),
|
|
493
|
+
body: JSON.stringify({
|
|
494
|
+
'action': 'deleteWebhook',
|
|
495
|
+
'url': url,
|
|
496
|
+
}),
|
|
497
|
+
});
|
|
498
|
+
const response: any = await body.json();
|
|
499
|
+
await this.debugLog(`deleteWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`);
|
|
500
|
+
if (statusCode !== 200 || response?.statusCode !== 100) {
|
|
501
|
+
this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`);
|
|
502
|
+
} else {
|
|
503
|
+
this.infoLog('Unregistered webhook to close listening.');
|
|
504
|
+
}
|
|
505
|
+
} catch (e: any) {
|
|
506
|
+
this.errorLog(`Failed to delete webhook. Error:${e.message}`);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
382
512
|
/*
|
|
383
513
|
* Setup EVE history graph feature if enabled.
|
|
384
514
|
*/
|