@switchbot/homebridge-switchbot 0.2.0-alpha.2 → 1.0.1-beta.2
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/nodejs-beta.yml +1 -1
- package/.github/workflows/nodejs.yml +1 -1
- package/.github/workflows/{prerelease.js → prerelease-beta.js} +0 -0
- package/CHANGELOG.md +1 -1
- package/README.md +9 -5
- package/config.schema.json +370 -142
- package/dist/devices/bots.d.ts +18 -34
- package/dist/devices/bots.d.ts.map +1 -1
- package/dist/devices/bots.js +166 -171
- package/dist/devices/bots.js.map +1 -1
- package/dist/devices/{bulb.d.ts → colorbulb.d.ts} +12 -19
- package/dist/devices/colorbulb.d.ts.map +1 -0
- package/dist/devices/{bulb.js → colorbulb.js} +34 -32
- package/dist/devices/colorbulb.js.map +1 -0
- package/dist/devices/contact.d.ts +18 -16
- package/dist/devices/contact.d.ts.map +1 -1
- package/dist/devices/contact.js +101 -44
- package/dist/devices/contact.js.map +1 -1
- package/dist/devices/curtains.d.ts +17 -51
- package/dist/devices/curtains.d.ts.map +1 -1
- package/dist/devices/curtains.js +170 -248
- package/dist/devices/curtains.js.map +1 -1
- package/dist/devices/humidifiers.d.ts +13 -4
- package/dist/devices/humidifiers.d.ts.map +1 -1
- package/dist/devices/humidifiers.js +81 -8
- package/dist/devices/humidifiers.js.map +1 -1
- package/dist/devices/indoorcam.d.ts +6 -13
- package/dist/devices/indoorcam.d.ts.map +1 -1
- package/dist/devices/indoorcam.js.map +1 -1
- package/dist/devices/meters.d.ts +12 -15
- package/dist/devices/meters.d.ts.map +1 -1
- package/dist/devices/meters.js +38 -44
- package/dist/devices/meters.js.map +1 -1
- package/dist/devices/motion.d.ts +17 -15
- package/dist/devices/motion.d.ts.map +1 -1
- package/dist/devices/motion.js +87 -40
- package/dist/devices/motion.js.map +1 -1
- package/dist/devices/plugs.d.ts +6 -13
- package/dist/devices/plugs.d.ts.map +1 -1
- package/dist/devices/plugs.js.map +1 -1
- package/dist/irdevices/airconditioners.d.ts +10 -10
- package/dist/irdevices/airconditioners.d.ts.map +1 -1
- package/dist/irdevices/airconditioners.js +29 -9
- package/dist/irdevices/airconditioners.js.map +1 -1
- package/dist/irdevices/airpurifiers.d.ts +11 -11
- package/dist/irdevices/airpurifiers.d.ts.map +1 -1
- package/dist/irdevices/airpurifiers.js.map +1 -1
- package/dist/irdevices/cameras.d.ts +3 -3
- package/dist/irdevices/cameras.d.ts.map +1 -1
- package/dist/irdevices/cameras.js.map +1 -1
- package/dist/irdevices/fans.d.ts +6 -6
- package/dist/irdevices/fans.d.ts.map +1 -1
- package/dist/irdevices/fans.js +10 -10
- package/dist/irdevices/fans.js.map +1 -1
- package/dist/irdevices/lights.d.ts +3 -3
- package/dist/irdevices/lights.d.ts.map +1 -1
- package/dist/irdevices/lights.js.map +1 -1
- package/dist/irdevices/others.d.ts +3 -3
- package/dist/irdevices/others.d.ts.map +1 -1
- package/dist/irdevices/others.js.map +1 -1
- package/dist/irdevices/tvs.d.ts +3 -3
- package/dist/irdevices/tvs.d.ts.map +1 -1
- package/dist/irdevices/tvs.js +7 -5
- package/dist/irdevices/tvs.js.map +1 -1
- package/dist/irdevices/vacuumcleaners.d.ts +3 -3
- package/dist/irdevices/vacuumcleaners.d.ts.map +1 -1
- package/dist/irdevices/vacuumcleaners.js.map +1 -1
- package/dist/irdevices/waterheaters.d.ts +3 -3
- package/dist/irdevices/waterheaters.d.ts.map +1 -1
- package/dist/irdevices/waterheaters.js.map +1 -1
- package/dist/platform.d.ts +3 -2
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +136 -46
- package/dist/platform.js.map +1 -1
- package/dist/settings.d.ts +51 -8
- package/dist/settings.d.ts.map +1 -1
- package/package.json +65 -65
- package/src/devices/bots.ts +188 -193
- package/src/devices/{bulb.ts → colorbulb.ts} +46 -51
- package/src/devices/contact.ts +124 -68
- package/src/devices/curtains.ts +192 -300
- package/src/devices/humidifiers.ts +97 -14
- package/src/devices/indoorcam.ts +10 -20
- package/src/devices/meters.ts +57 -69
- package/src/devices/motion.ts +108 -62
- package/src/devices/plugs.ts +8 -19
- package/src/irdevices/airconditioners.ts +43 -20
- package/src/irdevices/airpurifiers.ts +15 -11
- package/src/irdevices/cameras.ts +5 -3
- package/src/irdevices/fans.ts +22 -16
- package/src/irdevices/lights.ts +5 -3
- package/src/irdevices/others.ts +5 -3
- package/src/irdevices/tvs.ts +13 -7
- package/src/irdevices/vacuumcleaners.ts +5 -3
- package/src/irdevices/waterheaters.ts +5 -3
- package/src/platform.ts +148 -49
- package/src/settings.ts +80 -8
- package/dist/devices/bulb.d.ts.map +0 -1
- package/dist/devices/bulb.js.map +0 -1
package/src/devices/curtains.ts
CHANGED
|
@@ -1,70 +1,60 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
-
import { Service, PlatformAccessory, CharacteristicValue
|
|
2
|
+
import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
|
|
3
3
|
import { SwitchBotPlatform } from '../platform';
|
|
4
4
|
import { interval, Subject } from 'rxjs';
|
|
5
5
|
import { debounceTime, skipWhile, tap } from 'rxjs/operators';
|
|
6
|
-
import { DeviceURL, device, devicesConfig } from '../settings';
|
|
6
|
+
import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse } from '../settings';
|
|
7
|
+
import { AxiosResponse } from 'axios';
|
|
7
8
|
|
|
8
9
|
export class Curtain {
|
|
9
10
|
// Services
|
|
10
11
|
private service: Service;
|
|
11
12
|
private lightSensorService: Service;
|
|
13
|
+
private batteryService?: Service;
|
|
12
14
|
|
|
13
15
|
// Characteristic Values
|
|
14
16
|
CurrentPosition!: CharacteristicValue;
|
|
15
17
|
PositionState!: CharacteristicValue;
|
|
16
18
|
TargetPosition!: CharacteristicValue;
|
|
17
19
|
CurrentAmbientLightLevel!: CharacteristicValue;
|
|
20
|
+
BatteryLevel?: CharacteristicValue;
|
|
21
|
+
StatusLowBattery?: CharacteristicValue;
|
|
18
22
|
|
|
19
|
-
// Others
|
|
20
|
-
deviceStatus!:
|
|
21
|
-
moveTimer!: NodeJS.Timeout;
|
|
22
|
-
ScanIntervalId!: NodeJS.Timeout;
|
|
23
|
-
setNewTargetTimer!: NodeJS.Timeout;
|
|
24
|
-
AutoDisableFastScanTimeoutId!: NodeJS.Timeout;
|
|
23
|
+
// OpenAPI Others
|
|
24
|
+
deviceStatus!: deviceStatusResponse;
|
|
25
25
|
setNewTarget!: boolean;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
setNewTargetTimer!: NodeJS.Timeout;
|
|
27
|
+
|
|
28
|
+
// BLE Others
|
|
29
|
+
switchbot!: switchbot;
|
|
30
|
+
serviceData!: serviceData;
|
|
31
|
+
calibration: serviceData['calibration'];
|
|
32
|
+
battery: serviceData['battery'];
|
|
33
|
+
position: serviceData['position'];
|
|
34
|
+
lightLevel: serviceData['lightLevel'];
|
|
35
|
+
|
|
36
|
+
// Config
|
|
36
37
|
set_minStep!: number;
|
|
37
38
|
refreshRate!: number;
|
|
38
|
-
switchbot!: {
|
|
39
|
-
discover: (
|
|
40
|
-
arg0:
|
|
41
|
-
{
|
|
42
|
-
duration: any;
|
|
43
|
-
model: string;
|
|
44
|
-
quick: boolean;
|
|
45
|
-
id: MacAddress;
|
|
46
|
-
}
|
|
47
|
-
) => Promise<any>;
|
|
48
|
-
wait: (
|
|
49
|
-
arg0: number
|
|
50
|
-
) => any;
|
|
51
|
-
};
|
|
52
39
|
|
|
53
40
|
// Updates
|
|
54
41
|
curtainUpdateInProgress!: boolean;
|
|
55
|
-
doCurtainUpdate
|
|
42
|
+
doCurtainUpdate!: Subject<void>;
|
|
56
43
|
|
|
57
44
|
constructor(
|
|
58
45
|
private readonly platform: SwitchBotPlatform,
|
|
59
46
|
private accessory: PlatformAccessory,
|
|
60
47
|
public device: device & devicesConfig,
|
|
61
48
|
) {
|
|
49
|
+
// Curtain Config
|
|
50
|
+
this.platform.device(`[Curtain Config] ble: ${device.ble}, disable_group: ${device.curtain?.disable_group},`
|
|
51
|
+
+ ` refreshRate: ${device.curtain?.refreshRate}, set_max: ${device.curtain?.set_max}, set_min: ${device.curtain?.set_min},`
|
|
52
|
+
+ ` set_minStep: ${device.curtain?.set_minStep}`);
|
|
53
|
+
|
|
62
54
|
// default placeholders
|
|
63
|
-
this.setMinMax();
|
|
64
55
|
this.CurrentPosition = 0;
|
|
65
56
|
this.TargetPosition = 0;
|
|
66
57
|
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
67
|
-
this.ScanDuration = this.platform.config.options!.refreshRate!;
|
|
68
58
|
|
|
69
59
|
// BLE Connection
|
|
70
60
|
if (device.ble) {
|
|
@@ -121,7 +111,7 @@ export class Curtain {
|
|
|
121
111
|
validValueRanges: [0, 100],
|
|
122
112
|
})
|
|
123
113
|
.onGet(() => {
|
|
124
|
-
return
|
|
114
|
+
return this.CurrentPosition;
|
|
125
115
|
});
|
|
126
116
|
|
|
127
117
|
this.service
|
|
@@ -137,15 +127,6 @@ export class Curtain {
|
|
|
137
127
|
accessory.getService(this.platform.Service.LightSensor) ||
|
|
138
128
|
accessory.addService(this.platform.Service.LightSensor)), `${device.deviceName} Light Sensor`;
|
|
139
129
|
this.lightSensorService.setCharacteristic(this.platform.Characteristic.Name, `${device.deviceName} Light Sensor`);
|
|
140
|
-
// handle on / off events using the On characteristic
|
|
141
|
-
this.lightSensorService.getCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel).onGet(() => {
|
|
142
|
-
if (this.CurrentAmbientLightLevel === 'bright') {
|
|
143
|
-
return 100000;
|
|
144
|
-
} else {
|
|
145
|
-
return 0.0001;
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
|
|
149
130
|
|
|
150
131
|
// Update Homekit
|
|
151
132
|
this.updateHomeKitCharacteristics();
|
|
@@ -224,10 +205,80 @@ export class Curtain {
|
|
|
224
205
|
|
|
225
206
|
private async BLEparseStatus() {
|
|
226
207
|
this.platform.device('Curtains BLE Device parseStatus');
|
|
208
|
+
this.CurrentPosition = 100 - Number(this.position);
|
|
209
|
+
this.setMinMax();
|
|
210
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition - Device is Currently: ${this.CurrentPosition}`);
|
|
211
|
+
if (this.setNewTarget) {
|
|
212
|
+
this.platform.log.info(`Checking ${this.accessory.displayName} Status ...`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (this.setNewTarget) {
|
|
216
|
+
if (this.TargetPosition > this.CurrentPosition) {
|
|
217
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Current position: ${this.CurrentPosition} closing`);
|
|
218
|
+
this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
|
|
219
|
+
} else if (this.TargetPosition < this.CurrentPosition) {
|
|
220
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Current position: ${this.CurrentPosition} opening`);
|
|
221
|
+
this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
|
|
222
|
+
} else {
|
|
223
|
+
this.platform.debug(`Curtain ${this.CurrentPosition} - standby`);
|
|
224
|
+
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Current position: ${this.CurrentPosition} standby`);
|
|
228
|
+
if (!this.setNewTarget) {
|
|
229
|
+
/*If Curtain calibration distance is short, there will be an error between the current percentage and the target percentage.*/
|
|
230
|
+
this.TargetPosition = this.CurrentPosition;
|
|
231
|
+
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Brightness
|
|
235
|
+
switch (this.lightLevel) {
|
|
236
|
+
case 1:
|
|
237
|
+
this.CurrentAmbientLightLevel = 0.0001;
|
|
238
|
+
break;
|
|
239
|
+
case 2:
|
|
240
|
+
this.CurrentAmbientLightLevel = 100;
|
|
241
|
+
break;
|
|
242
|
+
case 3:
|
|
243
|
+
this.CurrentAmbientLightLevel = 200;
|
|
244
|
+
break;
|
|
245
|
+
case 4:
|
|
246
|
+
this.CurrentAmbientLightLevel = 4000;
|
|
247
|
+
break;
|
|
248
|
+
case 5:
|
|
249
|
+
this.CurrentAmbientLightLevel = 12000;
|
|
250
|
+
break;
|
|
251
|
+
case 6:
|
|
252
|
+
this.CurrentAmbientLightLevel = 28000;
|
|
253
|
+
break;
|
|
254
|
+
case 7:
|
|
255
|
+
this.CurrentAmbientLightLevel = 56000;
|
|
256
|
+
break;
|
|
257
|
+
case 8:
|
|
258
|
+
this.CurrentAmbientLightLevel = 75000;
|
|
259
|
+
break;
|
|
260
|
+
case 9:
|
|
261
|
+
this.CurrentAmbientLightLevel = 90000;
|
|
262
|
+
break;
|
|
263
|
+
case 10:
|
|
264
|
+
default:
|
|
265
|
+
this.CurrentAmbientLightLevel = 100000;
|
|
266
|
+
}
|
|
267
|
+
// Battery
|
|
268
|
+
this.BatteryLevel = Number(this.battery);
|
|
269
|
+
if (this.BatteryLevel < 10) {
|
|
270
|
+
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
|
|
271
|
+
} else {
|
|
272
|
+
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
273
|
+
}
|
|
274
|
+
this.platform.debug(
|
|
275
|
+
`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},`
|
|
276
|
+
+ ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`
|
|
277
|
+
+ ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
|
|
227
278
|
}
|
|
228
279
|
|
|
229
280
|
private async openAPIparseStatus() {
|
|
230
|
-
this.
|
|
281
|
+
this.platform.device('Curtains OpenAPI Device parseStatus');
|
|
231
282
|
this.CurrentPosition = 100 - this.deviceStatus.body.slidePosition!;
|
|
232
283
|
this.setMinMax();
|
|
233
284
|
this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition - Device is Currently: ${this.CurrentPosition}`);
|
|
@@ -260,80 +311,76 @@ export class Curtain {
|
|
|
260
311
|
case 'dim':
|
|
261
312
|
this.CurrentAmbientLightLevel = 0.0001;
|
|
262
313
|
break;
|
|
314
|
+
case 'bright':
|
|
263
315
|
default:
|
|
264
316
|
this.CurrentAmbientLightLevel = 100000;
|
|
265
317
|
}
|
|
266
318
|
this.platform.debug(
|
|
267
|
-
`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}
|
|
268
|
-
|
|
269
|
-
|
|
319
|
+
`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},`
|
|
320
|
+
+ ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`
|
|
321
|
+
+ ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
|
|
270
322
|
}
|
|
271
323
|
|
|
272
324
|
async refreshStatus() {
|
|
273
325
|
if (this.device.ble) {
|
|
274
326
|
this.platform.device('BLE');
|
|
275
|
-
await this.
|
|
327
|
+
await this.BLERefreshStatus();
|
|
276
328
|
} else {
|
|
277
329
|
this.platform.device('OpenAPI');
|
|
278
330
|
await this.openAPIRefreshStatus();
|
|
279
331
|
}
|
|
280
332
|
}
|
|
281
333
|
|
|
282
|
-
private
|
|
283
|
-
this.platform.debug('Curtains BLE Device RefreshStatus');
|
|
334
|
+
private connectBLE() {
|
|
284
335
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
285
336
|
const Switchbot = require('node-switchbot');
|
|
286
337
|
const switchbot = new Switchbot();
|
|
287
338
|
const colon = this.device.deviceId!.match(/.{1,2}/g);
|
|
288
339
|
const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
|
|
289
340
|
this.device.bleMac = bleMac.toLowerCase();
|
|
290
|
-
this.platform.
|
|
291
|
-
switchbot
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.
|
|
296
|
-
|
|
297
|
-
switchbot
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
this.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
this.platform.log.error(error);
|
|
327
|
-
await this.openAPIRefreshStatus();
|
|
328
|
-
});
|
|
329
|
-
}, this.platform.config.options!.refreshRate! * 60000);
|
|
341
|
+
this.platform.device(this.device.bleMac!);
|
|
342
|
+
return switchbot;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private async BLERefreshStatus() {
|
|
346
|
+
this.platform.debug('Curtains BLE Device RefreshStatus');
|
|
347
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
348
|
+
const switchbot = this.connectBLE();
|
|
349
|
+
// Start to monitor advertisement packets
|
|
350
|
+
switchbot.startScan({
|
|
351
|
+
model: 'c',
|
|
352
|
+
id: this.device.bleMac,
|
|
353
|
+
}).then(() => {
|
|
354
|
+
// Set an event hander
|
|
355
|
+
switchbot.onadvertisement = (ad: any) => {
|
|
356
|
+
this.serviceData = ad.serviceData;
|
|
357
|
+
this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`);
|
|
358
|
+
this.calibration = ad.serviceData.calibration;
|
|
359
|
+
this.battery = ad.serviceData.battery;
|
|
360
|
+
this.position = ad.serviceData.position;
|
|
361
|
+
this.lightLevel = ad.serviceData.lightLevel;
|
|
362
|
+
this.platform.device(`${this.accessory.displayName}, Calibration: ${ad.serviceData.calibration}, Position: ${ad.serviceData.position},`
|
|
363
|
+
+ ` Light Level: ${ad.serviceData.lightLevel}, Battery: ${ad.serviceData.battery}`);
|
|
364
|
+
};
|
|
365
|
+
// Wait 10 seconds
|
|
366
|
+
return switchbot.wait(10000);
|
|
367
|
+
}).then(() => {
|
|
368
|
+
// Stop to monitor
|
|
369
|
+
switchbot.stopScan();
|
|
370
|
+
this.parseStatus();
|
|
371
|
+
this.updateHomeKitCharacteristics();
|
|
372
|
+
}).catch(async (e: any) => {
|
|
373
|
+
this.platform.log.error(`BLE Connection Failed: ${e.message}`);
|
|
374
|
+
this.platform.log.warn('Using OpenAPI Connection');
|
|
375
|
+
await this.openAPIRefreshStatus();
|
|
376
|
+
});
|
|
330
377
|
}
|
|
331
378
|
|
|
332
379
|
private async openAPIRefreshStatus() {
|
|
380
|
+
this.platform.debug('Curtains OpenAPI Device RefreshStatus');
|
|
333
381
|
try {
|
|
334
382
|
this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
|
|
335
383
|
this.platform.debug(`Curtain ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
|
|
336
|
-
this.setMinMax();
|
|
337
384
|
this.parseStatus();
|
|
338
385
|
this.updateHomeKitCharacteristics();
|
|
339
386
|
} catch (e: any) {
|
|
@@ -355,6 +402,15 @@ export class Curtain {
|
|
|
355
402
|
|
|
356
403
|
private async BLEpushChanges() {
|
|
357
404
|
this.platform.device('Curtains BLE Device pushChanges');
|
|
405
|
+
const switchbot = this.connectBLE();
|
|
406
|
+
switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac }).then((device_list) => {
|
|
407
|
+
this.platform.log.info(`${this.accessory.displayName}, Target Position: ${this.TargetPosition}`);
|
|
408
|
+
return device_list[0].runToPos(100 - Number(this.TargetPosition));
|
|
409
|
+
}).then(() => {
|
|
410
|
+
this.platform.device('Done.');
|
|
411
|
+
}).catch((e: any) => {
|
|
412
|
+
this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`);
|
|
413
|
+
});
|
|
358
414
|
}
|
|
359
415
|
|
|
360
416
|
private async OpenAPIpushChanges() {
|
|
@@ -380,7 +436,6 @@ export class Curtain {
|
|
|
380
436
|
this.platform.debug(`Curtain ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`);
|
|
381
437
|
|
|
382
438
|
// Make the API request
|
|
383
|
-
this.platform.log.error(this.device.deviceId!);
|
|
384
439
|
const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId!}/commands`, payload));
|
|
385
440
|
this.platform.debug(`Curtain ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
386
441
|
this.statusCode(push);
|
|
@@ -390,13 +445,12 @@ export class Curtain {
|
|
|
390
445
|
updateHomeKitCharacteristics() {
|
|
391
446
|
this.platform.debug(
|
|
392
447
|
`Curtain ${this.accessory.displayName} updateHomeKitCharacteristics - ${JSON.stringify({
|
|
393
|
-
CurrentPosition:
|
|
448
|
+
CurrentPosition: this.CurrentPosition,
|
|
394
449
|
PositionState: this.PositionState,
|
|
395
|
-
TargetPosition:
|
|
450
|
+
TargetPosition: this.TargetPosition,
|
|
396
451
|
CurrentAmbientLightLevel: this.CurrentAmbientLightLevel,
|
|
397
452
|
})}`,
|
|
398
453
|
);
|
|
399
|
-
this.setMinMax();
|
|
400
454
|
if (this.CurrentPosition === undefined) {
|
|
401
455
|
this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
|
|
402
456
|
} else {
|
|
@@ -421,6 +475,20 @@ export class Curtain {
|
|
|
421
475
|
this.lightSensorService.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
|
|
422
476
|
this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
|
|
423
477
|
}
|
|
478
|
+
if (this.device.ble) {
|
|
479
|
+
if (this.BatteryLevel === undefined) {
|
|
480
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
|
|
481
|
+
} else {
|
|
482
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
|
|
483
|
+
this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
|
|
484
|
+
}
|
|
485
|
+
if (this.StatusLowBattery === undefined) {
|
|
486
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
|
|
487
|
+
} else {
|
|
488
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
|
|
489
|
+
this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
424
492
|
}
|
|
425
493
|
|
|
426
494
|
public apiError(e: any) {
|
|
@@ -428,9 +496,13 @@ export class Curtain {
|
|
|
428
496
|
this.service.updateCharacteristic(this.platform.Characteristic.PositionState, e);
|
|
429
497
|
this.service.updateCharacteristic(this.platform.Characteristic.TargetPosition, e);
|
|
430
498
|
this.lightSensorService.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
|
|
499
|
+
if (this.device.ble) {
|
|
500
|
+
this.batteryService!.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
|
|
501
|
+
this.batteryService!.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
|
|
502
|
+
}
|
|
431
503
|
}
|
|
432
504
|
|
|
433
|
-
private statusCode(push: {
|
|
505
|
+
private statusCode(push: AxiosResponse<{ statusCode: number; }>) {
|
|
434
506
|
switch (push.data.statusCode) {
|
|
435
507
|
case 151:
|
|
436
508
|
this.platform.log.error('Command not supported by this device type.');
|
|
@@ -462,215 +534,35 @@ export class Curtain {
|
|
|
462
534
|
* Handle requests to set the value of the "Target Position" characteristic
|
|
463
535
|
*/
|
|
464
536
|
TargetPositionSet(value: CharacteristicValue) {
|
|
465
|
-
|
|
466
|
-
this.TargetPosition = value as number;
|
|
467
|
-
this.platform.log.info('Target position of Curtain setting: ' + this.TargetPosition + '%');
|
|
468
|
-
clearTimeout(this.moveTimer);
|
|
469
|
-
if (this.TargetPosition > this.CurrentPosition) {
|
|
470
|
-
this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
|
|
471
|
-
} else if (this.TargetPosition < this.CurrentPosition) {
|
|
472
|
-
this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
|
|
473
|
-
} else {
|
|
474
|
-
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
475
|
-
}
|
|
537
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Set TargetPosition: ${value}`);
|
|
476
538
|
|
|
477
|
-
|
|
478
|
-
this.service?.getCharacteristic(this.platform.Characteristic.TargetPosition).updateValue(this.TargetPosition);
|
|
479
|
-
this.service?.getCharacteristic(this.platform.Characteristic.CurrentPosition).updateValue(this.CurrentPosition);
|
|
480
|
-
this.service?.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
|
|
481
|
-
} else {
|
|
482
|
-
this.runToPosition(this.convertFromHomeKitPosition(this.TargetPosition))
|
|
483
|
-
.then(() => {
|
|
484
|
-
this.platform.log.info('Done.');
|
|
485
|
-
this.platform.log.info('Target position of Curtain has been set to: ' + this.TargetPosition + '%');
|
|
486
|
-
this.moveTimer = setTimeout(() => {
|
|
487
|
-
// log.info("setTimeout", this.positionState.toString(), this.currentPosition.toString(), this.targetPosition.toString());
|
|
488
|
-
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
489
|
-
// this.curtainService?.getCharacteristic(hap.Characteristic.TargetPosition).updateValue(this.targetPosition);
|
|
490
|
-
this.service?.getCharacteristic(this.platform.Characteristic.CurrentPosition).updateValue(this.CurrentPosition);
|
|
491
|
-
this.service?.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
|
|
492
|
-
}, this.moveTime);
|
|
493
|
-
})
|
|
494
|
-
.catch((error: any) => {
|
|
495
|
-
this.platform.log.error(error);
|
|
496
|
-
this.moveTimer = setTimeout(() => {
|
|
497
|
-
this.TargetPosition = this.CurrentPosition;
|
|
498
|
-
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
499
|
-
this.service?.getCharacteristic(this.platform.Characteristic.TargetPosition).updateValue(this.TargetPosition);
|
|
500
|
-
// this.curtainService?.getCharacteristic(hap.Characteristic.CurrentPosition).updateValue(this.currentPosition);
|
|
501
|
-
this.service?.getCharacteristic(this.platform.Characteristic.PositionState).updateValue(this.PositionState);
|
|
502
|
-
}, 1000);
|
|
503
|
-
this.platform.log.info('Target position of Curtain failed to be set to: ' + this.TargetPosition + '%');
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
} else {
|
|
507
|
-
this.platform.debug(`Curtain ${this.accessory.displayName} - Set TargetPosition: ${value}`);
|
|
539
|
+
this.TargetPosition = value;
|
|
508
540
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
|
|
517
|
-
this.setNewTarget = true;
|
|
518
|
-
this.setMinMax();
|
|
519
|
-
} else {
|
|
520
|
-
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
521
|
-
this.setNewTarget = false;
|
|
522
|
-
this.setMinMax();
|
|
523
|
-
}
|
|
524
|
-
this.service.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
* If Curtain movement time is short, the moving flag from backend is always false.
|
|
528
|
-
* The minimum time depends on the network control latency.
|
|
529
|
-
*/
|
|
530
|
-
clearTimeout(this.setNewTargetTimer);
|
|
531
|
-
if (this.setNewTarget) {
|
|
532
|
-
this.setNewTargetTimer = setTimeout(() => {
|
|
533
|
-
this.platform.debug(`Curtain ${this.accessory.displayName} - setNewTarget ${this.setNewTarget} timeout`);
|
|
534
|
-
this.setNewTarget = false;
|
|
535
|
-
}, 10000);
|
|
536
|
-
}
|
|
537
|
-
this.doCurtainUpdate.next();
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* Convert to/from device/HomeKit's position, since:
|
|
543
|
-
*
|
|
544
|
-
* - opened is 0% in HomeKit and 100% in Curtain device.
|
|
545
|
-
* - closed is 100% in HomeKit, 0% in Curtain device.
|
|
546
|
-
*/
|
|
547
|
-
convertFromHomeKitPosition(n: number): number {
|
|
548
|
-
let covertToDevicePosition: number;
|
|
549
|
-
if (this.ReverseDir) {
|
|
550
|
-
covertToDevicePosition = n;
|
|
541
|
+
this.setMinMax();
|
|
542
|
+
if (value > this.CurrentPosition) {
|
|
543
|
+
this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
|
|
544
|
+
this.setNewTarget = true;
|
|
545
|
+
} else if (value < this.CurrentPosition) {
|
|
546
|
+
this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
|
|
547
|
+
this.setNewTarget = true;
|
|
551
548
|
} else {
|
|
552
|
-
|
|
549
|
+
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
550
|
+
this.setNewTarget = false;
|
|
553
551
|
}
|
|
554
|
-
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Ask the device to start moving to the given position (which must be a device value and not an HomeKit value).
|
|
559
|
-
* Returns a Promise that's resolved as soon as the command was sent to the device.
|
|
560
|
-
*/
|
|
561
|
-
public async runToPosition(pos: number): Promise<void> {
|
|
562
|
-
const SwitchBot = require('node-switchbot');
|
|
563
|
-
const switchbot = new SwitchBot();
|
|
564
|
-
|
|
565
|
-
return switchbot
|
|
566
|
-
.discover({ duration: this.ScanDuration, model: 'c', quick: false })
|
|
567
|
-
.then((device_list: any) => {
|
|
568
|
-
let targetDevice: any = null;
|
|
569
|
-
|
|
570
|
-
for (const device of device_list) {
|
|
571
|
-
this.platform.log.info(device.modelName, device.address);
|
|
572
|
-
if (device.address === this.device.bleMac!.toLowerCase()) {
|
|
573
|
-
targetDevice = device;
|
|
574
|
-
break;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (!targetDevice) {
|
|
579
|
-
return new Promise((resolve, reject) => {
|
|
580
|
-
reject(new Error('Curtain \'' + this.device.deviceName + '\' (' + this.device.bleMac!.toLowerCase() + '): device not found.'));
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
this.startFastScan();
|
|
585
|
-
|
|
586
|
-
this.platform.log.info(`Curtain ${this.device.deviceName} (${this.device.bleMac!.toLowerCase()}) is moving to ${pos}...`);
|
|
587
|
-
return targetDevice.runToPos(pos);
|
|
588
|
-
});
|
|
589
|
-
}
|
|
552
|
+
this.service.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
|
|
590
553
|
|
|
591
|
-
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
* device position does not change for more than `fastScanDuration`.
|
|
554
|
+
/**
|
|
555
|
+
* If Curtain movement time is short, the moving flag from backend is always false.
|
|
556
|
+
* The minimum time depends on the network control latency.
|
|
595
557
|
*/
|
|
596
|
-
|
|
597
|
-
if (this.
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
private stopFastScan() {
|
|
605
|
-
if (!this.FastScanEnabled) {
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
this.FastScanEnabled = false;
|
|
609
|
-
this.startScanLoop();
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
private get scanInterval(): number {
|
|
613
|
-
return this.FastScanEnabled ? this.FastScanInterval : this.SlowScanInterval;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
private startScanLoop() {
|
|
617
|
-
if (this.ScanIntervalId !== null) {
|
|
618
|
-
clearInterval(this.ScanIntervalId);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
this.platform.log.info(`Curtain ${this.device.deviceName}: starting scan loop with interval ${this.scanInterval}`);
|
|
622
|
-
|
|
623
|
-
this.ScanIntervalId = setInterval(() => {
|
|
624
|
-
this.scan().catch((err) => {
|
|
625
|
-
this.platform.log.error(`error while scanning for Curtain ${this.device.deviceName}: ${err}`);
|
|
626
|
-
});
|
|
627
|
-
}, this.scanInterval);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
private scan(): Promise<void> {
|
|
631
|
-
return new Promise((resolve, reject) => {
|
|
632
|
-
const SwitchBot = require('node-switchbot');
|
|
633
|
-
const switchbot = new SwitchBot();
|
|
634
|
-
switchbot.onadvertisement = (ad: any) => {
|
|
635
|
-
this.applyPosition(ad);
|
|
636
|
-
};
|
|
637
|
-
switchbot.startScan({ id: this.device.bleMac!.toLowerCase() })
|
|
638
|
-
.then(() => {
|
|
639
|
-
return switchbot.wait(this.ScanDuration);
|
|
640
|
-
})
|
|
641
|
-
.then(() => {
|
|
642
|
-
resolve();
|
|
643
|
-
switchbot.stopScan();
|
|
644
|
-
})
|
|
645
|
-
.catch((err: any) => {
|
|
646
|
-
reject(err);
|
|
647
|
-
});
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
private applyPosition(ad: any) {
|
|
652
|
-
let pos = ad.serviceData.position;
|
|
653
|
-
|
|
654
|
-
if (pos + this.OpenCloseThreshold >= 100) {
|
|
655
|
-
pos = 100;
|
|
656
|
-
} else if (pos - this.OpenCloseThreshold <= 0) {
|
|
657
|
-
pos = 0;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
if (pos === this.PreviousPosition) {
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
this.PreviousPosition = this.Position;
|
|
665
|
-
this.Position = pos;
|
|
666
|
-
|
|
667
|
-
if (this.AutoDisableFastScanTimeoutId !== null) {
|
|
668
|
-
clearTimeout(this.AutoDisableFastScanTimeoutId);
|
|
558
|
+
clearTimeout(this.setNewTargetTimer);
|
|
559
|
+
if (this.setNewTarget) {
|
|
560
|
+
this.setNewTargetTimer = setTimeout(() => {
|
|
561
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - setNewTarget ${this.setNewTarget} timeout`);
|
|
562
|
+
this.setNewTarget = false;
|
|
563
|
+
}, 10000);
|
|
669
564
|
}
|
|
670
|
-
|
|
671
|
-
this.AutoDisableFastScanTimeoutId = setTimeout(() => {
|
|
672
|
-
this.stopFastScan();
|
|
673
|
-
}, this.FastScanDuration);
|
|
565
|
+
this.doCurtainUpdate.next();
|
|
674
566
|
}
|
|
675
567
|
|
|
676
568
|
public setMinMax() {
|