@switchbot/homebridge-switchbot 2.0.0-beta.19 → 2.0.0-beta.20
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 +6 -1
- package/README.md +1 -2
- package/config.schema.json +51 -32
- package/dist/device/bot.d.ts +32 -29
- package/dist/device/bot.d.ts.map +1 -1
- package/dist/device/bot.js +549 -575
- package/dist/device/bot.js.map +1 -1
- package/dist/device/colorbulb.d.ts +33 -15
- package/dist/device/colorbulb.d.ts.map +1 -1
- package/dist/device/colorbulb.js +426 -341
- package/dist/device/colorbulb.js.map +1 -1
- package/dist/device/contact.d.ts +16 -9
- package/dist/device/contact.d.ts.map +1 -1
- package/dist/device/contact.js +246 -208
- package/dist/device/contact.js.map +1 -1
- package/dist/device/curtain.d.ts +21 -18
- package/dist/device/curtain.d.ts.map +1 -1
- package/dist/device/curtain.js +449 -449
- package/dist/device/curtain.js.map +1 -1
- package/dist/device/humidifier.d.ts +22 -20
- package/dist/device/humidifier.d.ts.map +1 -1
- package/dist/device/humidifier.js +346 -324
- package/dist/device/humidifier.js.map +1 -1
- package/dist/device/lock.d.ts +22 -10
- package/dist/device/lock.d.ts.map +1 -1
- package/dist/device/lock.js +257 -178
- package/dist/device/lock.js.map +1 -1
- package/dist/device/meter.d.ts +21 -14
- package/dist/device/meter.d.ts.map +1 -1
- package/dist/device/meter.js +287 -239
- package/dist/device/meter.js.map +1 -1
- package/dist/device/meterplus.d.ts +23 -10
- package/dist/device/meterplus.d.ts.map +1 -1
- package/dist/device/meterplus.js +308 -190
- package/dist/device/meterplus.js.map +1 -1
- package/dist/device/motion.d.ts +17 -10
- package/dist/device/motion.d.ts.map +1 -1
- package/dist/device/motion.js +225 -193
- package/dist/device/motion.js.map +1 -1
- package/dist/device/plug.d.ts +24 -17
- package/dist/device/plug.d.ts.map +1 -1
- package/dist/device/plug.js +270 -267
- package/dist/device/plug.js.map +1 -1
- package/dist/device/striplight.d.ts +36 -14
- package/dist/device/striplight.d.ts.map +1 -1
- package/dist/device/striplight.js +447 -299
- package/dist/device/striplight.js.map +1 -1
- package/dist/irdevice/airconditioner.d.ts +7 -14
- package/dist/irdevice/airconditioner.d.ts.map +1 -1
- package/dist/irdevice/airconditioner.js +177 -169
- package/dist/irdevice/airconditioner.js.map +1 -1
- package/dist/irdevice/airpurifier.d.ts +3 -5
- package/dist/irdevice/airpurifier.d.ts.map +1 -1
- package/dist/irdevice/airpurifier.js +72 -57
- package/dist/irdevice/airpurifier.js.map +1 -1
- package/dist/irdevice/camera.d.ts +3 -4
- package/dist/irdevice/camera.d.ts.map +1 -1
- package/dist/irdevice/camera.js +47 -33
- package/dist/irdevice/camera.js.map +1 -1
- package/dist/irdevice/fan.d.ts +3 -4
- package/dist/irdevice/fan.d.ts.map +1 -1
- package/dist/irdevice/fan.js +66 -48
- package/dist/irdevice/fan.js.map +1 -1
- package/dist/irdevice/light.d.ts +3 -4
- package/dist/irdevice/light.d.ts.map +1 -1
- package/dist/irdevice/light.js +48 -47
- package/dist/irdevice/light.js.map +1 -1
- package/dist/irdevice/other.d.ts +3 -4
- package/dist/irdevice/other.d.ts.map +1 -1
- package/dist/irdevice/other.js +47 -33
- package/dist/irdevice/other.js.map +1 -1
- package/dist/irdevice/tv.d.ts +10 -11
- package/dist/irdevice/tv.d.ts.map +1 -1
- package/dist/irdevice/tv.js +64 -48
- package/dist/irdevice/tv.js.map +1 -1
- package/dist/irdevice/vacuumcleaner.d.ts +7 -12
- package/dist/irdevice/vacuumcleaner.d.ts.map +1 -1
- package/dist/irdevice/vacuumcleaner.js +52 -41
- package/dist/irdevice/vacuumcleaner.js.map +1 -1
- package/dist/irdevice/waterheater.d.ts +6 -11
- package/dist/irdevice/waterheater.d.ts.map +1 -1
- package/dist/irdevice/waterheater.js +52 -42
- package/dist/irdevice/waterheater.js.map +1 -1
- package/dist/platform.d.ts +0 -4
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +44 -66
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +3 -3
- package/dist/settings.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/device/bot.ts +551 -589
- package/src/device/colorbulb.ts +441 -375
- package/src/device/contact.ts +249 -213
- package/src/device/curtain.ts +452 -461
- package/src/device/humidifier.ts +358 -365
- package/src/device/lock.ts +263 -179
- package/src/device/meter.ts +302 -253
- package/src/device/meterplus.ts +319 -194
- package/src/device/motion.ts +230 -199
- package/src/device/plug.ts +273 -274
- package/src/device/striplight.ts +460 -316
- package/src/irdevice/airconditioner.ts +181 -183
- package/src/irdevice/airpurifier.ts +70 -60
- package/src/irdevice/camera.ts +46 -36
- package/src/irdevice/fan.ts +64 -49
- package/src/irdevice/light.ts +47 -51
- package/src/irdevice/other.ts +46 -36
- package/src/irdevice/tv.ts +64 -51
- package/src/irdevice/vacuumcleaner.ts +52 -45
- package/src/irdevice/waterheater.ts +50 -45
- package/src/platform.ts +44 -68
- package/src/settings.ts +3 -3
package/src/device/meterplus.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import https from 'https';
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import { Context } from 'vm';
|
|
4
|
+
import { hostname } from 'os';
|
|
5
|
+
import { MqttClient } from 'mqtt';
|
|
6
|
+
import { connectAsync } from 'async-mqtt';
|
|
4
7
|
import { interval, Subject } from 'rxjs';
|
|
5
8
|
import { skipWhile } from 'rxjs/operators';
|
|
6
9
|
import superStringify from 'super-stringify';
|
|
7
10
|
import { SwitchBotPlatform } from '../platform';
|
|
8
11
|
import { Service, PlatformAccessory, Units, CharacteristicValue } from 'homebridge';
|
|
9
12
|
import { device, devicesConfig, serviceData, ad, switchbot, temperature, deviceStatus, HostDomain, DevicePath } from '../settings';
|
|
13
|
+
import { IncomingMessage } from 'http';
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* Platform Accessory
|
|
@@ -34,15 +38,20 @@ export class MeterPlus {
|
|
|
34
38
|
// BLE Others
|
|
35
39
|
connected?: boolean;
|
|
36
40
|
switchbot!: switchbot;
|
|
37
|
-
SwitchToOpenAPI?: boolean;
|
|
38
41
|
serviceData!: serviceData;
|
|
39
42
|
address!: ad['address'];
|
|
43
|
+
SwitchToOpenAPI?: boolean;
|
|
40
44
|
temperature!: temperature['c'];
|
|
41
45
|
battery!: serviceData['battery'];
|
|
42
46
|
humidity!: serviceData['humidity'];
|
|
43
47
|
|
|
48
|
+
//MQTT stuff
|
|
49
|
+
mqttClient: MqttClient | null = null;
|
|
50
|
+
|
|
51
|
+
// EVE history service handler
|
|
52
|
+
historyService: any;
|
|
53
|
+
|
|
44
54
|
// Config
|
|
45
|
-
OpenAPI?: boolean;
|
|
46
55
|
scanDuration!: number;
|
|
47
56
|
deviceLogging!: string;
|
|
48
57
|
deviceRefreshRate!: number;
|
|
@@ -51,23 +60,19 @@ export class MeterPlus {
|
|
|
51
60
|
meterUpdateInProgress!: boolean;
|
|
52
61
|
doMeterUpdate: Subject<void>;
|
|
53
62
|
|
|
63
|
+
// Connection
|
|
64
|
+
private readonly BLE = (this.device.connectionType === 'BLE' || this.device.connectionType === 'Both');
|
|
65
|
+
private readonly OpenAPI = (this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'Both');
|
|
66
|
+
|
|
54
67
|
constructor(private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, public device: device & devicesConfig) {
|
|
55
68
|
// default placeholders
|
|
56
69
|
this.logs(device);
|
|
57
|
-
this.openAPI();
|
|
58
70
|
this.scan(device);
|
|
59
71
|
this.refreshRate(device);
|
|
60
72
|
this.config(device);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
|
|
65
|
-
}
|
|
66
|
-
if (this.CurrentTemperature === undefined) {
|
|
67
|
-
this.CurrentTemperature = 0;
|
|
68
|
-
} else {
|
|
69
|
-
this.CurrentTemperature = this.accessory.context.CurrentTemperature;
|
|
70
|
-
}
|
|
73
|
+
this.context();
|
|
74
|
+
this.setupHistoryService(device);
|
|
75
|
+
this.setupMqtt(device);
|
|
71
76
|
|
|
72
77
|
// this is subject we use to track when we need to POST changes to the SwitchBot API
|
|
73
78
|
this.doMeterUpdate = new Subject();
|
|
@@ -141,11 +146,11 @@ export class MeterPlus {
|
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
// Battery Service
|
|
144
|
-
if (!
|
|
149
|
+
if (!this.BLE) {
|
|
145
150
|
this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Battery Service`);
|
|
146
151
|
this.batteryService = this.accessory.getService(this.platform.Service.Battery);
|
|
147
152
|
accessory.removeService(this.batteryService!);
|
|
148
|
-
} else if (
|
|
153
|
+
} else if (this.BLE && !this.batteryService) {
|
|
149
154
|
this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Battery Service`);
|
|
150
155
|
(this.batteryService = this.accessory.getService(this.platform.Service.Battery) || this.accessory.addService(this.platform.Service.Battery)),
|
|
151
156
|
`${accessory.displayName} Battery`;
|
|
@@ -172,15 +177,19 @@ export class MeterPlus {
|
|
|
172
177
|
* Parse the device status from the SwitchBot api
|
|
173
178
|
*/
|
|
174
179
|
async parseStatus(): Promise<void> {
|
|
175
|
-
if (this.
|
|
180
|
+
if (this.BLE && !this.SwitchToOpenAPI) {
|
|
181
|
+
await this.BLEparseStatus();
|
|
182
|
+
} else if (this.OpenAPI && this.platform.config.credentials?.token) {
|
|
183
|
+
this.SwitchToOpenAPI = false;
|
|
176
184
|
await this.openAPIparseStatus();
|
|
177
185
|
} else {
|
|
178
|
-
|
|
186
|
+
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
|
|
187
|
+
+ ` ${this.device.connectionType}, parseStatus will not happen.`);
|
|
179
188
|
}
|
|
180
189
|
}
|
|
181
190
|
|
|
182
191
|
async BLEparseStatus(): Promise<void> {
|
|
183
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}
|
|
192
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
|
|
184
193
|
|
|
185
194
|
// Battery
|
|
186
195
|
this.BatteryLevel = Number(this.battery);
|
|
@@ -206,22 +215,17 @@ export class MeterPlus {
|
|
|
206
215
|
}
|
|
207
216
|
|
|
208
217
|
async openAPIparseStatus(): Promise<void> {
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
|
|
219
|
+
// Current Relative Humidity
|
|
220
|
+
if (!this.device.meter?.hide_humidity) {
|
|
221
|
+
this.CurrentRelativeHumidity = this.Humidity!;
|
|
222
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
|
|
211
223
|
}
|
|
212
|
-
if (this.platform.config.credentials?.token) {
|
|
213
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI parseStatus`);
|
|
214
|
-
// Current Relative Humidity
|
|
215
|
-
if (!this.device.meter?.hide_humidity) {
|
|
216
|
-
this.CurrentRelativeHumidity = this.Humidity!;
|
|
217
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
|
|
218
|
-
}
|
|
219
224
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
+
// Current Temperature
|
|
226
|
+
if (!this.device.meter?.hide_temperature) {
|
|
227
|
+
this.CurrentTemperature = this.Temperature!;
|
|
228
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
|
|
225
229
|
}
|
|
226
230
|
}
|
|
227
231
|
|
|
@@ -229,15 +233,18 @@ export class MeterPlus {
|
|
|
229
233
|
* Asks the SwitchBot API for the latest device information
|
|
230
234
|
*/
|
|
231
235
|
async refreshStatus(): Promise<void> {
|
|
232
|
-
if (this.
|
|
233
|
-
this.
|
|
236
|
+
if (this.BLE) {
|
|
237
|
+
await this.BLERefreshStatus();
|
|
238
|
+
} else if (this.OpenAPI && this.platform.config.credentials?.token) {
|
|
239
|
+
await this.openAPIRefreshStatus();
|
|
234
240
|
} else {
|
|
235
|
-
this.
|
|
241
|
+
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
|
|
242
|
+
+ ` ${this.device.connectionType}, refreshStatus will not happen.`);
|
|
236
243
|
}
|
|
237
244
|
}
|
|
238
245
|
|
|
239
|
-
async
|
|
240
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}
|
|
246
|
+
async BLERefreshStatus(): Promise<void> {
|
|
247
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
|
|
241
248
|
const switchbot = await this.platform.connectBLE();
|
|
242
249
|
// Convert to BLE Address
|
|
243
250
|
this.device.bleMac = this.device
|
|
@@ -263,9 +270,12 @@ export class MeterPlus {
|
|
|
263
270
|
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address Found: ${this.address}`);
|
|
264
271
|
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac}`);
|
|
265
272
|
}
|
|
273
|
+
if (ad.serviceData.humidity! > 0) {
|
|
274
|
+
// reject unreliable data
|
|
275
|
+
this.humidity = ad.serviceData.humidity;
|
|
276
|
+
}
|
|
266
277
|
this.serviceData = ad.serviceData;
|
|
267
278
|
this.temperature = ad.serviceData.temperature!.c;
|
|
268
|
-
this.humidity = ad.serviceData.humidity;
|
|
269
279
|
this.battery = ad.serviceData.battery;
|
|
270
280
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${superStringify(ad.serviceData)}`);
|
|
271
281
|
this.debugLog(
|
|
@@ -296,111 +306,66 @@ export class MeterPlus {
|
|
|
296
306
|
}
|
|
297
307
|
})
|
|
298
308
|
.catch(async (e: any) => {
|
|
299
|
-
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed refreshStatus with BLE Connection`);
|
|
300
|
-
if (this.deviceLogging.includes('debug')) {
|
|
301
|
-
this.errorLog(
|
|
302
|
-
`${this.device.deviceType}: ${this.accessory.displayName} failed refreshStatus with BLE Connection,`
|
|
303
|
-
+ ` Error Message: ${superStringify(e.message)}`,
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
if (this.platform.config.credentials?.token) {
|
|
307
|
-
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
|
|
308
|
-
this.SwitchToOpenAPI = true;
|
|
309
|
-
this.openAPIRefreshStatus();
|
|
310
|
-
}
|
|
311
309
|
this.apiError(e);
|
|
310
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}`
|
|
311
|
+
+ ` Connection, Error Message: ${superStringify(e.message)}`);
|
|
312
|
+
await this.BLEconnection(switchbot);
|
|
312
313
|
});
|
|
313
314
|
} else {
|
|
314
315
|
await this.BLEconnection(switchbot);
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
318
|
|
|
318
|
-
async getCustomBLEAddress(switchbot: any) {
|
|
319
|
-
if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
|
|
320
|
-
(async () => {
|
|
321
|
-
// Start to monitor advertisement packets
|
|
322
|
-
await switchbot.startScan({
|
|
323
|
-
model: 'i',
|
|
324
|
-
});
|
|
325
|
-
// Set an event handler
|
|
326
|
-
switchbot.onadvertisement = (ad: any) => {
|
|
327
|
-
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
|
|
328
|
-
};
|
|
329
|
-
await switchbot.wait(10000);
|
|
330
|
-
// Stop to monitor
|
|
331
|
-
switchbot.stopScan();
|
|
332
|
-
})();
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async BLEconnection(switchbot: any): Promise<void> {
|
|
337
|
-
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
|
|
338
|
-
if (this.platform.config.credentials?.token) {
|
|
339
|
-
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
|
|
340
|
-
this.SwitchToOpenAPI = true;
|
|
341
|
-
await this.openAPIRefreshStatus();
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
319
|
async openAPIRefreshStatus(): Promise<void> {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
|
|
374
|
-
});
|
|
375
|
-
res.on('end', () => {
|
|
376
|
-
try {
|
|
377
|
-
this.deviceStatus = JSON.parse(rawData);
|
|
378
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus: ${superStringify(this.deviceStatus)}`);
|
|
379
|
-
this.Humidity = this.deviceStatus.body.humidity!;
|
|
380
|
-
this.Temperature = this.deviceStatus.body.temperature!;
|
|
381
|
-
this.parseStatus();
|
|
382
|
-
this.updateHomeKitCharacteristics();
|
|
383
|
-
} catch (e: any) {
|
|
384
|
-
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
|
|
385
|
-
}
|
|
386
|
-
});
|
|
320
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
|
|
321
|
+
try {
|
|
322
|
+
const t = Date.now();
|
|
323
|
+
const nonce = 'requestID';
|
|
324
|
+
const data = this.platform.config.credentials?.token + t + nonce;
|
|
325
|
+
const signTerm = crypto.createHmac('sha256', this.platform.config.credentials?.secret).update(Buffer.from(data, 'utf-8')).digest();
|
|
326
|
+
const sign = signTerm.toString('base64');
|
|
327
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} sign: ${sign}`);
|
|
328
|
+
const options = {
|
|
329
|
+
hostname: HostDomain,
|
|
330
|
+
port: 443,
|
|
331
|
+
path: `${DevicePath}/${this.device.deviceId}/status`,
|
|
332
|
+
method: 'GET',
|
|
333
|
+
headers: {
|
|
334
|
+
Authorization: this.platform.config.credentials?.token,
|
|
335
|
+
sign: sign,
|
|
336
|
+
nonce: nonce,
|
|
337
|
+
t: t,
|
|
338
|
+
'Content-Type': 'application/json',
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
const req = https.request(options, (res) => {
|
|
342
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus statusCode: ${res.statusCode}`);
|
|
343
|
+
let rawData = '';
|
|
344
|
+
res.on('data', (d) => {
|
|
345
|
+
rawData += d;
|
|
346
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} d: ${d}`);
|
|
387
347
|
});
|
|
388
|
-
|
|
389
|
-
|
|
348
|
+
res.on('end', () => {
|
|
349
|
+
try {
|
|
350
|
+
this.deviceStatus = JSON.parse(rawData);
|
|
351
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus: ${superStringify(this.deviceStatus)}`);
|
|
352
|
+
this.Humidity = this.deviceStatus.body.humidity!;
|
|
353
|
+
this.Temperature = this.deviceStatus.body.temperature!;
|
|
354
|
+
this.parseStatus();
|
|
355
|
+
this.updateHomeKitCharacteristics();
|
|
356
|
+
} catch (e: any) {
|
|
357
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
|
|
358
|
+
}
|
|
390
359
|
});
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
this.apiError(e);
|
|
401
|
-
}
|
|
402
|
-
} else {
|
|
403
|
-
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI is disabled, commands will not be sent.`);
|
|
360
|
+
});
|
|
361
|
+
req.on('error', (e: any) => {
|
|
362
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} error message: ${e.message}`);
|
|
363
|
+
});
|
|
364
|
+
req.end();
|
|
365
|
+
} catch (e: any) {
|
|
366
|
+
this.apiError(e);
|
|
367
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
|
|
368
|
+
+ ` Connection, Error Message: ${superStringify(e.message)}`);
|
|
404
369
|
}
|
|
405
370
|
}
|
|
406
371
|
|
|
@@ -408,51 +373,130 @@ export class MeterPlus {
|
|
|
408
373
|
* Updates the status for each of the HomeKit Characteristics
|
|
409
374
|
*/
|
|
410
375
|
async updateHomeKitCharacteristics(): Promise<void> {
|
|
376
|
+
const mqttmessage: string[] = [];
|
|
377
|
+
const entry = { time: Math.round(new Date().valueOf() / 1000) };
|
|
411
378
|
if (!this.device.meter?.hide_humidity) {
|
|
412
379
|
if (this.CurrentRelativeHumidity === undefined) {
|
|
413
380
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
|
|
414
381
|
} else {
|
|
382
|
+
this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity;
|
|
415
383
|
this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
|
|
416
384
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
|
|
417
385
|
+ ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
|
|
386
|
+
mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`);
|
|
387
|
+
entry['humidity'] = this.CurrentRelativeHumidity;
|
|
418
388
|
}
|
|
419
389
|
}
|
|
420
390
|
if (!this.device.meter?.hide_temperature) {
|
|
421
391
|
if (this.CurrentTemperature === undefined) {
|
|
422
392
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
|
|
423
393
|
} else {
|
|
394
|
+
mqttmessage.push(`"temperature": ${this.CurrentTemperature}`);
|
|
395
|
+
entry['temp'] = this.CurrentTemperature;
|
|
396
|
+
this.accessory.context.CurrentTemperature = this.CurrentTemperature;
|
|
424
397
|
this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature);
|
|
425
398
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
|
|
426
399
|
}
|
|
427
400
|
}
|
|
428
|
-
if (this.
|
|
401
|
+
if (this.BLE) {
|
|
429
402
|
if (this.BatteryLevel === undefined) {
|
|
430
403
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
|
|
431
404
|
} else {
|
|
405
|
+
mqttmessage.push(`"battery": ${this.BatteryLevel}`);
|
|
406
|
+
this.accessory.context.BatteryLevel = this.BatteryLevel;
|
|
432
407
|
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
|
|
433
408
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
|
|
434
409
|
}
|
|
435
410
|
if (this.StatusLowBattery === undefined) {
|
|
436
411
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
|
|
437
412
|
} else {
|
|
413
|
+
mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`);
|
|
414
|
+
this.accessory.context.StatusLowBattery = this.StatusLowBattery;
|
|
438
415
|
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
|
|
439
416
|
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
|
|
440
417
|
}
|
|
441
418
|
}
|
|
419
|
+
this.mqttPublish(`{${mqttmessage.join(',')}}`);
|
|
420
|
+
if (this.CurrentRelativeHumidity > 0) {
|
|
421
|
+
// reject unreliable data
|
|
422
|
+
this.historyService?.addEntry(entry);
|
|
423
|
+
}
|
|
442
424
|
}
|
|
443
425
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
426
|
+
/*
|
|
427
|
+
* Publish MQTT message for topics of
|
|
428
|
+
* 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
|
|
429
|
+
*/
|
|
430
|
+
mqttPublish(message: any) {
|
|
431
|
+
const mac = this.device.deviceId
|
|
432
|
+
?.toLowerCase()
|
|
433
|
+
.match(/[\s\S]{1,2}/g)
|
|
434
|
+
?.join(':');
|
|
435
|
+
const options = this.device.mqttPubOptions || {};
|
|
436
|
+
this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options);
|
|
437
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${superStringify(options)}`);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/*
|
|
441
|
+
* Setup MQTT hadler if URL is specifed.
|
|
442
|
+
*/
|
|
443
|
+
async setupMqtt(device: device & devicesConfig): Promise<void> {
|
|
444
|
+
if (device.mqttURL) {
|
|
445
|
+
try {
|
|
446
|
+
this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
|
|
447
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
|
|
448
|
+
this.mqttClient.on('error', (e: Error) => {
|
|
449
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
|
|
450
|
+
});
|
|
451
|
+
} catch (e) {
|
|
452
|
+
this.mqttClient = null;
|
|
453
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
|
|
454
|
+
}
|
|
447
455
|
}
|
|
448
|
-
|
|
449
|
-
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/*
|
|
459
|
+
* Setup EVE history graph feature if enabled.
|
|
460
|
+
*/
|
|
461
|
+
async setupHistoryService(device: device & devicesConfig): Promise<void> {
|
|
462
|
+
const mac = this.device
|
|
463
|
+
.deviceId!.match(/.{1,2}/g)!
|
|
464
|
+
.join(':')
|
|
465
|
+
.toLowerCase();
|
|
466
|
+
this.historyService = device.history
|
|
467
|
+
? new this.platform.fakegatoAPI('room', this.accessory, {
|
|
468
|
+
log: this.platform.log,
|
|
469
|
+
storage: 'fs',
|
|
470
|
+
filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
|
|
471
|
+
})
|
|
472
|
+
: null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async getCustomBLEAddress(switchbot: any) {
|
|
476
|
+
if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
|
|
477
|
+
(async () => {
|
|
478
|
+
// Start to monitor advertisement packets
|
|
479
|
+
await switchbot.startScan({
|
|
480
|
+
model: 'i',
|
|
481
|
+
});
|
|
482
|
+
// Set an event handler
|
|
483
|
+
switchbot.onadvertisement = (ad: any) => {
|
|
484
|
+
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${superStringify(ad, null, ' ')}`);
|
|
485
|
+
};
|
|
486
|
+
await switchbot.wait(10000);
|
|
487
|
+
// Stop to monitor
|
|
488
|
+
switchbot.stopScan();
|
|
489
|
+
})();
|
|
450
490
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async BLEconnection(switchbot: any): Promise<void> {
|
|
494
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`);
|
|
495
|
+
if (this.platform.config.credentials?.token && this.device.connectionType !== 'Both') {
|
|
496
|
+
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection`);
|
|
497
|
+
this.SwitchToOpenAPI = true;
|
|
498
|
+
await this.openAPIRefreshStatus();
|
|
454
499
|
}
|
|
455
|
-
//throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
|
|
456
500
|
}
|
|
457
501
|
|
|
458
502
|
model(device: device & devicesConfig): CharacteristicValue {
|
|
@@ -465,28 +509,126 @@ export class MeterPlus {
|
|
|
465
509
|
return model;
|
|
466
510
|
}
|
|
467
511
|
|
|
468
|
-
async
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
512
|
+
async scan(device: device & devicesConfig): Promise<void> {
|
|
513
|
+
if (device.scanDuration) {
|
|
514
|
+
this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
|
|
515
|
+
if (this.BLE) {
|
|
516
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
this.scanDuration = this.accessory.context.scanDuration = 1;
|
|
520
|
+
if (this.BLE) {
|
|
521
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
|
|
522
|
+
}
|
|
472
523
|
}
|
|
473
|
-
|
|
474
|
-
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async statusCode({ res }: { res: IncomingMessage }): Promise<void> {
|
|
527
|
+
switch (res.statusCode) {
|
|
528
|
+
case 151:
|
|
529
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this device type.`);
|
|
530
|
+
break;
|
|
531
|
+
case 152:
|
|
532
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found.`);
|
|
533
|
+
break;
|
|
534
|
+
case 160:
|
|
535
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported.`);
|
|
536
|
+
break;
|
|
537
|
+
case 161:
|
|
538
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline.`);
|
|
539
|
+
this.offlineOff();
|
|
540
|
+
break;
|
|
541
|
+
case 171:
|
|
542
|
+
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} is offline. Hub: ${this.device.hubDeviceId}`);
|
|
543
|
+
this.offlineOff();
|
|
544
|
+
break;
|
|
545
|
+
case 190:
|
|
546
|
+
this.errorLog(
|
|
547
|
+
`${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
|
|
548
|
+
` Or command: ${superStringify(res)} format is invalid`,
|
|
549
|
+
);
|
|
550
|
+
break;
|
|
551
|
+
case 100:
|
|
552
|
+
if (this.platform.debugMode) {
|
|
553
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent.`);
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
556
|
+
default:
|
|
557
|
+
if (this.platform.debugMode) {
|
|
558
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode.`);
|
|
559
|
+
}
|
|
475
560
|
}
|
|
476
|
-
|
|
477
|
-
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
async offlineOff(): Promise<void> {
|
|
564
|
+
if (this.device.offline) {
|
|
565
|
+
if (!this.device.meter?.hide_humidity) {
|
|
566
|
+
this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity,
|
|
567
|
+
this.accessory.context.CurrentRelativeHumidity);
|
|
568
|
+
}
|
|
569
|
+
if (!this.device.meter?.hide_temperature) {
|
|
570
|
+
this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.accessory.context.CurrentTemperature);
|
|
571
|
+
}
|
|
572
|
+
if (this.BLE) {
|
|
573
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.accessory.context.BatteryLevel);
|
|
574
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.accessory.context.StatusLowBattery);
|
|
575
|
+
}
|
|
576
|
+
await this.updateHomeKitCharacteristics();
|
|
478
577
|
}
|
|
479
|
-
|
|
480
|
-
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
async apiError(e: any): Promise<void> {
|
|
581
|
+
if (!this.device.meter?.hide_humidity) {
|
|
582
|
+
this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, e);
|
|
481
583
|
}
|
|
482
|
-
if (device.
|
|
483
|
-
|
|
584
|
+
if (!this.device.meter?.hide_temperature) {
|
|
585
|
+
this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, e);
|
|
484
586
|
}
|
|
485
|
-
if (
|
|
486
|
-
|
|
587
|
+
if (this.BLE) {
|
|
588
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
|
|
589
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
|
|
487
590
|
}
|
|
488
|
-
|
|
489
|
-
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
|
|
594
|
+
let FirmwareRevision: string;
|
|
595
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
|
|
596
|
+
+ ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
|
|
597
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
|
|
598
|
+
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
|
|
599
|
+
if (accessory.context.FirmwareRevision) {
|
|
600
|
+
FirmwareRevision = accessory.context.FirmwareRevision;
|
|
601
|
+
} else if (device.firmware) {
|
|
602
|
+
FirmwareRevision = device.firmware;
|
|
603
|
+
} else {
|
|
604
|
+
FirmwareRevision = this.platform.version;
|
|
605
|
+
}
|
|
606
|
+
return FirmwareRevision;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private context() {
|
|
610
|
+
if (this.CurrentRelativeHumidity === undefined) {
|
|
611
|
+
this.CurrentRelativeHumidity = 0;
|
|
612
|
+
} else {
|
|
613
|
+
this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
|
|
614
|
+
}
|
|
615
|
+
if (this.CurrentTemperature === undefined) {
|
|
616
|
+
this.CurrentTemperature = 0;
|
|
617
|
+
} else {
|
|
618
|
+
this.CurrentTemperature = this.accessory.context.CurrentTemperature;
|
|
619
|
+
}
|
|
620
|
+
if (this.BLE) {
|
|
621
|
+
if (this.BatteryLevel === undefined) {
|
|
622
|
+
this.BatteryLevel = 100;
|
|
623
|
+
} else {
|
|
624
|
+
this.BatteryLevel = this.accessory.context.BatteryLevel;
|
|
625
|
+
}
|
|
626
|
+
if (this.StatusLowBattery === undefined) {
|
|
627
|
+
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
628
|
+
this.accessory.context.StatusLowBattery = this.StatusLowBattery;
|
|
629
|
+
} else {
|
|
630
|
+
this.StatusLowBattery = this.accessory.context.StatusLowBattery;
|
|
631
|
+
}
|
|
490
632
|
}
|
|
491
633
|
}
|
|
492
634
|
|
|
@@ -500,26 +642,25 @@ export class MeterPlus {
|
|
|
500
642
|
}
|
|
501
643
|
}
|
|
502
644
|
|
|
503
|
-
async
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
this.OpenAPI = this.device.openAPI;
|
|
645
|
+
async config(device: device & devicesConfig): Promise<void> {
|
|
646
|
+
let config = {};
|
|
647
|
+
if (device.meter) {
|
|
648
|
+
config = device.meter;
|
|
508
649
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
650
|
+
if (device.connectionType !== undefined) {
|
|
651
|
+
config['connectionType'] = device.connectionType;
|
|
652
|
+
}
|
|
653
|
+
if (device.logging !== undefined) {
|
|
654
|
+
config['logging'] = device.logging;
|
|
655
|
+
}
|
|
656
|
+
if (device.refreshRate !== undefined) {
|
|
657
|
+
config['refreshRate'] = device.refreshRate;
|
|
658
|
+
}
|
|
659
|
+
if (device.scanDuration !== undefined) {
|
|
660
|
+
config['scanDuration'] = device.scanDuration;
|
|
661
|
+
}
|
|
662
|
+
if (Object.entries(config).length !== 0) {
|
|
663
|
+
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${superStringify(config)}`);
|
|
523
664
|
}
|
|
524
665
|
}
|
|
525
666
|
|
|
@@ -539,22 +680,6 @@ export class MeterPlus {
|
|
|
539
680
|
}
|
|
540
681
|
}
|
|
541
682
|
|
|
542
|
-
FirmwareRevision(accessory: PlatformAccessory<Context>, device: device & devicesConfig): CharacteristicValue {
|
|
543
|
-
let FirmwareRevision: string;
|
|
544
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
|
|
545
|
-
+ ` accessory.context.FirmwareRevision: ${accessory.context.FirmwareRevision}`);
|
|
546
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} device.firmware: ${device.firmware}`);
|
|
547
|
-
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} this.platform.version: ${this.platform.version}`);
|
|
548
|
-
if (accessory.context.FirmwareRevision) {
|
|
549
|
-
FirmwareRevision = accessory.context.FirmwareRevision;
|
|
550
|
-
} else if (device.firmware) {
|
|
551
|
-
FirmwareRevision = device.firmware;
|
|
552
|
-
} else {
|
|
553
|
-
FirmwareRevision = this.platform.version;
|
|
554
|
-
}
|
|
555
|
-
return FirmwareRevision;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
683
|
/**
|
|
559
684
|
* Logging for Device
|
|
560
685
|
*/
|