@switchbot/homebridge-switchbot 1.12.9-beta.26 → 1.12.9-beta.29
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/.github/workflows/dependabot.yml +5 -3
- package/CHANGELOG.md +2 -0
- package/dist/device/curtain.d.ts +4 -0
- package/dist/device/curtain.d.ts.map +1 -1
- package/dist/device/curtain.js +39 -0
- package/dist/device/curtain.js.map +1 -1
- package/dist/device/meter.d.ts +6 -0
- package/dist/device/meter.d.ts.map +1 -1
- package/dist/device/meter.js +64 -5
- package/dist/device/meter.js.map +1 -1
- package/dist/platform.d.ts +1 -0
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +3 -0
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +5 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/package.json +15 -13
- package/src/device/curtain.ts +42 -0
- package/src/device/meter.ts +72 -5
- package/src/platform.ts +6 -0
- package/src/settings.ts +5 -0
package/src/device/curtain.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
|
|
|
5
5
|
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
|
|
6
6
|
import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, payload, deviceStatus, ad } from '../settings';
|
|
7
7
|
import { Context } from 'vm';
|
|
8
|
+
import { MqttClient } from 'mqtt';
|
|
9
|
+
import { connectAsync } from 'async-mqtt';
|
|
8
10
|
|
|
9
11
|
export class Curtain {
|
|
10
12
|
// Services
|
|
@@ -59,12 +61,16 @@ export class Curtain {
|
|
|
59
61
|
curtainUpdateInProgress!: boolean;
|
|
60
62
|
doCurtainUpdate!: Subject<void>;
|
|
61
63
|
|
|
64
|
+
//MQTT stuff
|
|
65
|
+
mqttClient: MqttClient | null = null;
|
|
66
|
+
|
|
62
67
|
constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
|
|
63
68
|
// default placeholders
|
|
64
69
|
this.logs(device);
|
|
65
70
|
this.refreshRate(device);
|
|
66
71
|
this.scan(device);
|
|
67
72
|
this.config(device);
|
|
73
|
+
this.setupMqtt(device);
|
|
68
74
|
this.CurrentPosition = 0;
|
|
69
75
|
this.TargetPosition = 0;
|
|
70
76
|
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
@@ -201,6 +207,35 @@ export class Curtain {
|
|
|
201
207
|
});
|
|
202
208
|
}
|
|
203
209
|
|
|
210
|
+
/*
|
|
211
|
+
* Publish MQTT message for topics of
|
|
212
|
+
* 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx'
|
|
213
|
+
*/
|
|
214
|
+
mqttPublish(topic: string, message: any) {
|
|
215
|
+
const mac = this.device.deviceId?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
|
|
216
|
+
const options = this.device.mqttPubOptions || {};
|
|
217
|
+
this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options);
|
|
218
|
+
this.debugLog(`Meter: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/*
|
|
222
|
+
* Setup MQTT hadler if URL is specifed.
|
|
223
|
+
*/
|
|
224
|
+
async setupMqtt(device: device & devicesConfig): Promise<void> {
|
|
225
|
+
if (device.mqttURL) {
|
|
226
|
+
try {
|
|
227
|
+
this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
|
|
228
|
+
this.debugLog(`Meter: ${this.accessory.displayName} MQTT connection has been established successfully.`)
|
|
229
|
+
this.mqttClient.on('error', (e: Error) => {
|
|
230
|
+
this.errorLog(`Meter: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`)
|
|
231
|
+
});
|
|
232
|
+
} catch (e) {
|
|
233
|
+
this.mqttClient = null;
|
|
234
|
+
this.errorLog(`Meter: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
204
239
|
/**
|
|
205
240
|
* Parse the device status from the SwitchBot api
|
|
206
241
|
*/
|
|
@@ -656,18 +691,21 @@ export class Curtain {
|
|
|
656
691
|
} else {
|
|
657
692
|
this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.CurrentPosition, Number(this.CurrentPosition));
|
|
658
693
|
this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
|
|
694
|
+
this.mqttPublish('CurrentPosition', this.CurrentPosition);
|
|
659
695
|
}
|
|
660
696
|
if (this.PositionState === undefined) {
|
|
661
697
|
this.debugLog(`Curtain: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
|
|
662
698
|
} else {
|
|
663
699
|
this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.PositionState, Number(this.PositionState));
|
|
664
700
|
this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
|
|
701
|
+
this.mqttPublish('PositionState', this.PositionState);
|
|
665
702
|
}
|
|
666
703
|
if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
|
|
667
704
|
this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
|
|
668
705
|
} else {
|
|
669
706
|
this.windowCoveringService.updateCharacteristic(this.platform.Characteristic.TargetPosition, Number(this.TargetPosition));
|
|
670
707
|
this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
|
|
708
|
+
this.mqttPublish('TargetPosition', this.TargetPosition);
|
|
671
709
|
}
|
|
672
710
|
if (!this.device.curtain?.hide_lightsensor) {
|
|
673
711
|
if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) {
|
|
@@ -675,6 +713,7 @@ export class Curtain {
|
|
|
675
713
|
} else {
|
|
676
714
|
this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
|
|
677
715
|
this.debugLog(`Curtain: ${this.accessory.displayName}` + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
|
|
716
|
+
this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
|
|
678
717
|
}
|
|
679
718
|
}
|
|
680
719
|
if (this.device.ble) {
|
|
@@ -683,12 +722,14 @@ export class Curtain {
|
|
|
683
722
|
} else {
|
|
684
723
|
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
|
|
685
724
|
this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
|
|
725
|
+
this.mqttPublish('BatteryLevel', this.BatteryLevel);
|
|
686
726
|
}
|
|
687
727
|
if (this.StatusLowBattery === undefined) {
|
|
688
728
|
this.debugLog(`Curtain: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
|
|
689
729
|
} else {
|
|
690
730
|
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
|
|
691
731
|
this.debugLog(`Curtain: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
|
|
732
|
+
this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
|
|
692
733
|
}
|
|
693
734
|
}
|
|
694
735
|
}
|
|
@@ -745,6 +786,7 @@ export class Curtain {
|
|
|
745
786
|
this.debugLog(`Curtain: ${this.accessory.displayName} TargetPosition: ${value}`);
|
|
746
787
|
|
|
747
788
|
this.TargetPosition = value;
|
|
789
|
+
this.mqttPublish('TargetPosition', this.TargetPosition);
|
|
748
790
|
|
|
749
791
|
await this.setMinMax();
|
|
750
792
|
if (value > this.CurrentPosition) {
|
package/src/device/meter.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { SwitchBotPlatform } from '../platform';
|
|
|
4
4
|
import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
|
|
5
5
|
import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, temperature, deviceStatus } from '../settings';
|
|
6
6
|
import { Context } from 'vm';
|
|
7
|
+
import { MqttClient } from 'mqtt';
|
|
8
|
+
import { connectAsync } from 'async-mqtt';
|
|
9
|
+
import { hostname } from "os";
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Platform Accessory
|
|
@@ -51,12 +54,20 @@ export class Meter {
|
|
|
51
54
|
meterUpdateInProgress!: boolean;
|
|
52
55
|
doMeterUpdate: Subject<void>;
|
|
53
56
|
|
|
57
|
+
//MQTT stuff
|
|
58
|
+
mqttClient: MqttClient | null = null;
|
|
59
|
+
|
|
60
|
+
// EVE history service handler
|
|
61
|
+
historyService: any;
|
|
62
|
+
|
|
54
63
|
constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
|
|
55
64
|
// default placeholders
|
|
56
65
|
this.logs(device);
|
|
57
66
|
this.scan(device);
|
|
67
|
+
this.setupHistoryService(device);
|
|
58
68
|
this.refreshRate(device);
|
|
59
69
|
this.config(device);
|
|
70
|
+
this.setupMqtt(device);
|
|
60
71
|
if (this.CurrentRelativeHumidity === undefined) {
|
|
61
72
|
this.CurrentRelativeHumidity = 0;
|
|
62
73
|
} else {
|
|
@@ -166,6 +177,48 @@ export class Meter {
|
|
|
166
177
|
});
|
|
167
178
|
}
|
|
168
179
|
|
|
180
|
+
/*
|
|
181
|
+
* Publish MQTT message for topics of
|
|
182
|
+
* 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
|
|
183
|
+
*/
|
|
184
|
+
mqttPublish(message: any) {
|
|
185
|
+
const mac = this.device.deviceId?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':');
|
|
186
|
+
const options = this.device.mqttPubOptions || {};
|
|
187
|
+
this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options);
|
|
188
|
+
this.debugLog(`Meter: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/*
|
|
192
|
+
* Setup MQTT hadler if URL is specifed.
|
|
193
|
+
*/
|
|
194
|
+
async setupMqtt(device: device & devicesConfig): Promise<void> {
|
|
195
|
+
if (device.mqttURL) {
|
|
196
|
+
try {
|
|
197
|
+
this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
|
|
198
|
+
this.debugLog(`Meter: ${this.accessory.displayName} MQTT connection has been established successfully.`)
|
|
199
|
+
this.mqttClient.on('error', (e: Error) => {
|
|
200
|
+
this.errorLog(`Meter: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`)
|
|
201
|
+
});
|
|
202
|
+
} catch (e) {
|
|
203
|
+
this.mqttClient = null;
|
|
204
|
+
this.errorLog(`Meter: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/*
|
|
210
|
+
* Setup EVE history graph feature if enabled.
|
|
211
|
+
*/
|
|
212
|
+
async setupHistoryService(device: device & devicesConfig): Promise<void> {
|
|
213
|
+
const mac = this.device.deviceId!.match(/.{1,2}/g)!.join(':').toLowerCase();
|
|
214
|
+
this.historyService = device.history ?
|
|
215
|
+
new this.platform.fakegatoAPI('room', this.accessory,
|
|
216
|
+
{log: this.platform.log, storage: 'fs',
|
|
217
|
+
filename: `${hostname().split(".")[0]}_${mac}_persist.json`
|
|
218
|
+
}) :
|
|
219
|
+
null;
|
|
220
|
+
}
|
|
221
|
+
|
|
169
222
|
/**
|
|
170
223
|
* Parse the device status from the SwitchBot api
|
|
171
224
|
*/
|
|
@@ -187,7 +240,7 @@ export class Meter {
|
|
|
187
240
|
} else {
|
|
188
241
|
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
189
242
|
}
|
|
190
|
-
|
|
243
|
+
this.debugLog(`Meter: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
|
|
191
244
|
|
|
192
245
|
// Humidity
|
|
193
246
|
if (!this.device.meter?.hide_humidity) {
|
|
@@ -261,10 +314,12 @@ export class Meter {
|
|
|
261
314
|
this.infoLog(`Meter: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
|
|
262
315
|
this.infoLog(`Meter: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
|
|
263
316
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
317
|
+
if (ad.serviceData.humidity! > 0) { // reject unreliable data
|
|
318
|
+
this.serviceData = ad.serviceData;
|
|
319
|
+
this.temperature = ad.serviceData.temperature!.c;
|
|
320
|
+
this.humidity = ad.serviceData.humidity;
|
|
321
|
+
this.battery = ad.serviceData.battery;
|
|
322
|
+
}
|
|
268
323
|
this.debugLog(`Meter: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
|
|
269
324
|
this.debugLog(
|
|
270
325
|
`Meter: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` +
|
|
@@ -365,12 +420,16 @@ export class Meter {
|
|
|
365
420
|
* Updates the status for each of the HomeKit Characteristics
|
|
366
421
|
*/
|
|
367
422
|
async updateHomeKitCharacteristics(): Promise<void> {
|
|
423
|
+
let mqttmessage: string[] = [];
|
|
424
|
+
let entry = {time: Math.round(new Date().valueOf()/1000)};
|
|
368
425
|
if (!this.device.meter?.hide_humidity) {
|
|
369
426
|
if (this.CurrentRelativeHumidity === undefined) {
|
|
370
427
|
this.debugLog(`Meter: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
|
|
371
428
|
} else {
|
|
372
429
|
this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
|
|
373
430
|
this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
|
|
431
|
+
mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`);
|
|
432
|
+
entry["humidity"] = this.CurrentRelativeHumidity;
|
|
374
433
|
}
|
|
375
434
|
}
|
|
376
435
|
if (!this.device.meter?.hide_temperature) {
|
|
@@ -379,6 +438,8 @@ export class Meter {
|
|
|
379
438
|
} else {
|
|
380
439
|
this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature);
|
|
381
440
|
this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
|
|
441
|
+
mqttmessage.push(`"temperature": ${this.CurrentTemperature}`);
|
|
442
|
+
entry["temp"] = this.CurrentTemperature;
|
|
382
443
|
}
|
|
383
444
|
}
|
|
384
445
|
if (this.device.ble) {
|
|
@@ -387,14 +448,20 @@ export class Meter {
|
|
|
387
448
|
} else {
|
|
388
449
|
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
|
|
389
450
|
this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
|
|
451
|
+
mqttmessage.push(`"battery": ${this.BatteryLevel}`);
|
|
390
452
|
}
|
|
391
453
|
if (this.StatusLowBattery === undefined) {
|
|
392
454
|
this.debugLog(`Meter: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
|
|
393
455
|
} else {
|
|
394
456
|
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
|
|
395
457
|
this.debugLog(`Meter: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
|
|
458
|
+
mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`);
|
|
396
459
|
}
|
|
397
460
|
}
|
|
461
|
+
this.mqttPublish(`{${mqttmessage.join(',')}}`)
|
|
462
|
+
if (this.CurrentRelativeHumidity > 0) { // reject unreliable data
|
|
463
|
+
this.historyService?.addEntry(entry);
|
|
464
|
+
}
|
|
398
465
|
}
|
|
399
466
|
|
|
400
467
|
async apiError(e: any): Promise<void> {
|
package/src/platform.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { AirConditioner } from './irdevice/airconditioner';
|
|
|
21
21
|
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
22
22
|
import { API, DynamicPlatformPlugin, Logger, PlatformAccessory, Service, Characteristic } from 'homebridge';
|
|
23
23
|
import { PLATFORM_NAME, PLUGIN_NAME, DeviceURL, irdevice, device, SwitchBotPlatformConfig, deviceResponses, devicesConfig } from './settings';
|
|
24
|
+
import fakegato from 'fakegato-history';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* HomebridgePlatform
|
|
@@ -44,6 +45,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
44
45
|
debugMode!: boolean;
|
|
45
46
|
platformLogging?: string;
|
|
46
47
|
|
|
48
|
+
public readonly fakegatoAPI: any;
|
|
49
|
+
|
|
47
50
|
constructor(public readonly log: Logger, public readonly config: SwitchBotPlatformConfig, public readonly api: API) {
|
|
48
51
|
this.logs();
|
|
49
52
|
this.debugLog('Finished initializing platform:', this.config.name);
|
|
@@ -76,6 +79,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin {
|
|
|
76
79
|
return request;
|
|
77
80
|
});
|
|
78
81
|
|
|
82
|
+
// import fakegato-history module
|
|
83
|
+
this.fakegatoAPI = fakegato(api);
|
|
84
|
+
|
|
79
85
|
// When this event is fired it means Homebridge has restored all cached accessories from disk.
|
|
80
86
|
// Dynamic Platform plugins should only register new accessories after this event was fired,
|
|
81
87
|
// in order to ensure they weren't added to homebridge already. This event can also be used
|
package/src/settings.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MacAddress, PlatformConfig } from 'homebridge';
|
|
2
|
+
import { IClientOptions } from 'async-mqtt';
|
|
2
3
|
/**
|
|
3
4
|
* This is the name of the platform that users will use to register the plugin in the Homebridge config.json
|
|
4
5
|
*/
|
|
@@ -58,6 +59,10 @@ export interface devicesConfig extends device {
|
|
|
58
59
|
scanDuration?: number;
|
|
59
60
|
hide_device?: boolean;
|
|
60
61
|
offline?: boolean;
|
|
62
|
+
mqttURL?: string;
|
|
63
|
+
mqttOptions?: IClientOptions;
|
|
64
|
+
mqttPubOptions?: IClientOptions;
|
|
65
|
+
history?: boolean;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
export type meter = {
|