@switchbot/homebridge-switchbot 0.2.0-alpha.1 → 1.0.1-beta.1
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 +350 -126
- package/dist/devices/bots.d.ts +17 -33
- package/dist/devices/bots.d.ts.map +1 -1
- package/dist/devices/bots.js +103 -152
- 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} +32 -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 +168 -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 +78 -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 +35 -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 +50 -8
- package/dist/settings.d.ts.map +1 -1
- package/package.json +65 -65
- package/src/devices/bots.ts +126 -176
- package/src/devices/{bulb.ts → colorbulb.ts} +43 -51
- package/src/devices/contact.ts +124 -68
- package/src/devices/curtains.ts +189 -300
- package/src/devices/humidifiers.ts +93 -14
- package/src/devices/indoorcam.ts +10 -20
- package/src/devices/meters.ts +53 -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 +79 -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,58 +1,45 @@
|
|
|
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,
|
|
@@ -60,11 +47,9 @@ export class Curtain {
|
|
|
60
47
|
public device: device & devicesConfig,
|
|
61
48
|
) {
|
|
62
49
|
// default placeholders
|
|
63
|
-
this.setMinMax();
|
|
64
50
|
this.CurrentPosition = 0;
|
|
65
51
|
this.TargetPosition = 0;
|
|
66
52
|
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
67
|
-
this.ScanDuration = this.platform.config.options!.refreshRate!;
|
|
68
53
|
|
|
69
54
|
// BLE Connection
|
|
70
55
|
if (device.ble) {
|
|
@@ -98,6 +83,8 @@ export class Curtain {
|
|
|
98
83
|
accessory.getService(this.platform.Service.WindowCovering) ||
|
|
99
84
|
accessory.addService(this.platform.Service.WindowCovering)), `${device.deviceName} ${device.deviceType}`;
|
|
100
85
|
|
|
86
|
+
this.platform.device(`[Curtain Config] disable_group: ${device.curtain?.disable_group}, refreshRate: ${device.curtain?.refreshRate},`
|
|
87
|
+
+ ` set_max: ${device.curtain?.set_max}, set_min: ${device.curtain?.set_min}, set_minStep: ${device.curtain?.set_minStep}`);
|
|
101
88
|
// To avoid "Cannot add a Service with the same UUID another Service without also defining a unique 'subtype' property." error,
|
|
102
89
|
// when creating multiple services of the same type, you need to use the following syntax to specify a name and subtype id:
|
|
103
90
|
// accessory.getService('NAME') ?? accessory.addService(this.platform.Service.Lightbulb, 'NAME', 'USER_DEFINED_SUBTYPE');
|
|
@@ -121,7 +108,7 @@ export class Curtain {
|
|
|
121
108
|
validValueRanges: [0, 100],
|
|
122
109
|
})
|
|
123
110
|
.onGet(() => {
|
|
124
|
-
return
|
|
111
|
+
return this.CurrentPosition;
|
|
125
112
|
});
|
|
126
113
|
|
|
127
114
|
this.service
|
|
@@ -137,15 +124,6 @@ export class Curtain {
|
|
|
137
124
|
accessory.getService(this.platform.Service.LightSensor) ||
|
|
138
125
|
accessory.addService(this.platform.Service.LightSensor)), `${device.deviceName} Light Sensor`;
|
|
139
126
|
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
127
|
|
|
150
128
|
// Update Homekit
|
|
151
129
|
this.updateHomeKitCharacteristics();
|
|
@@ -224,10 +202,80 @@ export class Curtain {
|
|
|
224
202
|
|
|
225
203
|
private async BLEparseStatus() {
|
|
226
204
|
this.platform.device('Curtains BLE Device parseStatus');
|
|
205
|
+
this.CurrentPosition = 100 - Number(this.position);
|
|
206
|
+
this.setMinMax();
|
|
207
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition - Device is Currently: ${this.CurrentPosition}`);
|
|
208
|
+
if (this.setNewTarget) {
|
|
209
|
+
this.platform.log.info(`Checking ${this.accessory.displayName} Status ...`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (this.setNewTarget) {
|
|
213
|
+
if (this.TargetPosition > this.CurrentPosition) {
|
|
214
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Current position: ${this.CurrentPosition} closing`);
|
|
215
|
+
this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
|
|
216
|
+
} else if (this.TargetPosition < this.CurrentPosition) {
|
|
217
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Current position: ${this.CurrentPosition} opening`);
|
|
218
|
+
this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
|
|
219
|
+
} else {
|
|
220
|
+
this.platform.debug(`Curtain ${this.CurrentPosition} - standby`);
|
|
221
|
+
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Current position: ${this.CurrentPosition} standby`);
|
|
225
|
+
if (!this.setNewTarget) {
|
|
226
|
+
/*If Curtain calibration distance is short, there will be an error between the current percentage and the target percentage.*/
|
|
227
|
+
this.TargetPosition = this.CurrentPosition;
|
|
228
|
+
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Brightness
|
|
232
|
+
switch (this.lightLevel) {
|
|
233
|
+
case 1:
|
|
234
|
+
this.CurrentAmbientLightLevel = 0.0001;
|
|
235
|
+
break;
|
|
236
|
+
case 2:
|
|
237
|
+
this.CurrentAmbientLightLevel = 100;
|
|
238
|
+
break;
|
|
239
|
+
case 3:
|
|
240
|
+
this.CurrentAmbientLightLevel = 200;
|
|
241
|
+
break;
|
|
242
|
+
case 4:
|
|
243
|
+
this.CurrentAmbientLightLevel = 4000;
|
|
244
|
+
break;
|
|
245
|
+
case 5:
|
|
246
|
+
this.CurrentAmbientLightLevel = 12000;
|
|
247
|
+
break;
|
|
248
|
+
case 6:
|
|
249
|
+
this.CurrentAmbientLightLevel = 28000;
|
|
250
|
+
break;
|
|
251
|
+
case 7:
|
|
252
|
+
this.CurrentAmbientLightLevel = 56000;
|
|
253
|
+
break;
|
|
254
|
+
case 8:
|
|
255
|
+
this.CurrentAmbientLightLevel = 75000;
|
|
256
|
+
break;
|
|
257
|
+
case 9:
|
|
258
|
+
this.CurrentAmbientLightLevel = 90000;
|
|
259
|
+
break;
|
|
260
|
+
case 10:
|
|
261
|
+
default:
|
|
262
|
+
this.CurrentAmbientLightLevel = 100000;
|
|
263
|
+
}
|
|
264
|
+
// Battery
|
|
265
|
+
this.BatteryLevel = Number(this.battery);
|
|
266
|
+
if (this.BatteryLevel < 10) {
|
|
267
|
+
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
|
|
268
|
+
} else {
|
|
269
|
+
this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
|
|
270
|
+
}
|
|
271
|
+
this.platform.debug(
|
|
272
|
+
`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},`
|
|
273
|
+
+ ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`
|
|
274
|
+
+ ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
|
|
227
275
|
}
|
|
228
276
|
|
|
229
277
|
private async openAPIparseStatus() {
|
|
230
|
-
this.
|
|
278
|
+
this.platform.device('Curtains OpenAPI Device parseStatus');
|
|
231
279
|
this.CurrentPosition = 100 - this.deviceStatus.body.slidePosition!;
|
|
232
280
|
this.setMinMax();
|
|
233
281
|
this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition - Device is Currently: ${this.CurrentPosition}`);
|
|
@@ -260,80 +308,76 @@ export class Curtain {
|
|
|
260
308
|
case 'dim':
|
|
261
309
|
this.CurrentAmbientLightLevel = 0.0001;
|
|
262
310
|
break;
|
|
311
|
+
case 'bright':
|
|
263
312
|
default:
|
|
264
313
|
this.CurrentAmbientLightLevel = 100000;
|
|
265
314
|
}
|
|
266
315
|
this.platform.debug(
|
|
267
|
-
`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}
|
|
268
|
-
|
|
269
|
-
|
|
316
|
+
`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},`
|
|
317
|
+
+ ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`
|
|
318
|
+
+ ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
|
|
270
319
|
}
|
|
271
320
|
|
|
272
321
|
async refreshStatus() {
|
|
273
322
|
if (this.device.ble) {
|
|
274
323
|
this.platform.device('BLE');
|
|
275
|
-
await this.
|
|
324
|
+
await this.BLERefreshStatus();
|
|
276
325
|
} else {
|
|
277
326
|
this.platform.device('OpenAPI');
|
|
278
327
|
await this.openAPIRefreshStatus();
|
|
279
328
|
}
|
|
280
329
|
}
|
|
281
330
|
|
|
282
|
-
private
|
|
283
|
-
this.platform.debug('Curtains BLE Device RefreshStatus');
|
|
331
|
+
private connectBLE() {
|
|
284
332
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
285
333
|
const Switchbot = require('node-switchbot');
|
|
286
334
|
const switchbot = new Switchbot();
|
|
287
335
|
const colon = this.device.deviceId!.match(/.{1,2}/g);
|
|
288
336
|
const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A;
|
|
289
337
|
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);
|
|
338
|
+
this.platform.device(this.device.bleMac!);
|
|
339
|
+
return switchbot;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private async BLERefreshStatus() {
|
|
343
|
+
this.platform.debug('Curtains BLE Device RefreshStatus');
|
|
344
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
345
|
+
const switchbot = this.connectBLE();
|
|
346
|
+
// Start to monitor advertisement packets
|
|
347
|
+
switchbot.startScan({
|
|
348
|
+
model: 'c',
|
|
349
|
+
id: this.device.bleMac,
|
|
350
|
+
}).then(() => {
|
|
351
|
+
// Set an event hander
|
|
352
|
+
switchbot.onadvertisement = (ad: any) => {
|
|
353
|
+
this.serviceData = ad.serviceData;
|
|
354
|
+
this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`);
|
|
355
|
+
this.calibration = ad.serviceData.calibration;
|
|
356
|
+
this.battery = ad.serviceData.battery;
|
|
357
|
+
this.position = ad.serviceData.position;
|
|
358
|
+
this.lightLevel = ad.serviceData.lightLevel;
|
|
359
|
+
this.platform.device(`${this.accessory.displayName}, Calibration: ${ad.serviceData.calibration}, Position: ${ad.serviceData.position},`
|
|
360
|
+
+ ` Light Level: ${ad.serviceData.lightLevel}, Battery: ${ad.serviceData.battery}`);
|
|
361
|
+
};
|
|
362
|
+
// Wait 10 seconds
|
|
363
|
+
return switchbot.wait(10000);
|
|
364
|
+
}).then(() => {
|
|
365
|
+
// Stop to monitor
|
|
366
|
+
switchbot.stopScan();
|
|
367
|
+
this.parseStatus();
|
|
368
|
+
this.updateHomeKitCharacteristics();
|
|
369
|
+
}).catch(async (e: any) => {
|
|
370
|
+
this.platform.log.error(`BLE Connection Failed: ${e.message}`);
|
|
371
|
+
this.platform.log.warn('Using OpenAPI Connection');
|
|
372
|
+
await this.openAPIRefreshStatus();
|
|
373
|
+
});
|
|
330
374
|
}
|
|
331
375
|
|
|
332
376
|
private async openAPIRefreshStatus() {
|
|
377
|
+
this.platform.debug('Curtains OpenAPI Device RefreshStatus');
|
|
333
378
|
try {
|
|
334
379
|
this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data;
|
|
335
380
|
this.platform.debug(`Curtain ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`);
|
|
336
|
-
this.setMinMax();
|
|
337
381
|
this.parseStatus();
|
|
338
382
|
this.updateHomeKitCharacteristics();
|
|
339
383
|
} catch (e: any) {
|
|
@@ -355,6 +399,15 @@ export class Curtain {
|
|
|
355
399
|
|
|
356
400
|
private async BLEpushChanges() {
|
|
357
401
|
this.platform.device('Curtains BLE Device pushChanges');
|
|
402
|
+
const switchbot = this.connectBLE();
|
|
403
|
+
switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac }).then((device_list) => {
|
|
404
|
+
this.platform.log.info(`${this.accessory.displayName}, Target Position: ${this.TargetPosition}`);
|
|
405
|
+
return device_list[0].runToPos(100 - Number(this.TargetPosition));
|
|
406
|
+
}).then(() => {
|
|
407
|
+
this.platform.device('Done.');
|
|
408
|
+
}).catch((e: any) => {
|
|
409
|
+
this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`);
|
|
410
|
+
});
|
|
358
411
|
}
|
|
359
412
|
|
|
360
413
|
private async OpenAPIpushChanges() {
|
|
@@ -380,7 +433,6 @@ export class Curtain {
|
|
|
380
433
|
this.platform.debug(`Curtain ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`);
|
|
381
434
|
|
|
382
435
|
// Make the API request
|
|
383
|
-
this.platform.log.error(this.device.deviceId!);
|
|
384
436
|
const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId!}/commands`, payload));
|
|
385
437
|
this.platform.debug(`Curtain ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`);
|
|
386
438
|
this.statusCode(push);
|
|
@@ -390,13 +442,12 @@ export class Curtain {
|
|
|
390
442
|
updateHomeKitCharacteristics() {
|
|
391
443
|
this.platform.debug(
|
|
392
444
|
`Curtain ${this.accessory.displayName} updateHomeKitCharacteristics - ${JSON.stringify({
|
|
393
|
-
CurrentPosition:
|
|
445
|
+
CurrentPosition: this.CurrentPosition,
|
|
394
446
|
PositionState: this.PositionState,
|
|
395
|
-
TargetPosition:
|
|
447
|
+
TargetPosition: this.TargetPosition,
|
|
396
448
|
CurrentAmbientLightLevel: this.CurrentAmbientLightLevel,
|
|
397
449
|
})}`,
|
|
398
450
|
);
|
|
399
|
-
this.setMinMax();
|
|
400
451
|
if (this.CurrentPosition === undefined) {
|
|
401
452
|
this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
|
|
402
453
|
} else {
|
|
@@ -421,6 +472,20 @@ export class Curtain {
|
|
|
421
472
|
this.lightSensorService.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
|
|
422
473
|
this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
|
|
423
474
|
}
|
|
475
|
+
if (this.device.ble) {
|
|
476
|
+
if (this.BatteryLevel === undefined) {
|
|
477
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
|
|
478
|
+
} else {
|
|
479
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel);
|
|
480
|
+
this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
|
|
481
|
+
}
|
|
482
|
+
if (this.StatusLowBattery === undefined) {
|
|
483
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
|
|
484
|
+
} else {
|
|
485
|
+
this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery);
|
|
486
|
+
this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
424
489
|
}
|
|
425
490
|
|
|
426
491
|
public apiError(e: any) {
|
|
@@ -428,9 +493,13 @@ export class Curtain {
|
|
|
428
493
|
this.service.updateCharacteristic(this.platform.Characteristic.PositionState, e);
|
|
429
494
|
this.service.updateCharacteristic(this.platform.Characteristic.TargetPosition, e);
|
|
430
495
|
this.lightSensorService.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, e);
|
|
496
|
+
if (this.device.ble) {
|
|
497
|
+
this.batteryService!.updateCharacteristic(this.platform.Characteristic.BatteryLevel, e);
|
|
498
|
+
this.batteryService!.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, e);
|
|
499
|
+
}
|
|
431
500
|
}
|
|
432
501
|
|
|
433
|
-
private statusCode(push: {
|
|
502
|
+
private statusCode(push: AxiosResponse<{ statusCode: number; }>) {
|
|
434
503
|
switch (push.data.statusCode) {
|
|
435
504
|
case 151:
|
|
436
505
|
this.platform.log.error('Command not supported by this device type.');
|
|
@@ -462,215 +531,35 @@ export class Curtain {
|
|
|
462
531
|
* Handle requests to set the value of the "Target Position" characteristic
|
|
463
532
|
*/
|
|
464
533
|
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
|
-
}
|
|
534
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - Set TargetPosition: ${value}`);
|
|
476
535
|
|
|
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}`);
|
|
536
|
+
this.TargetPosition = value;
|
|
508
537
|
|
|
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;
|
|
538
|
+
this.setMinMax();
|
|
539
|
+
if (value > this.CurrentPosition) {
|
|
540
|
+
this.PositionState = this.platform.Characteristic.PositionState.INCREASING;
|
|
541
|
+
this.setNewTarget = true;
|
|
542
|
+
} else if (value < this.CurrentPosition) {
|
|
543
|
+
this.PositionState = this.platform.Characteristic.PositionState.DECREASING;
|
|
544
|
+
this.setNewTarget = true;
|
|
551
545
|
} else {
|
|
552
|
-
|
|
546
|
+
this.PositionState = this.platform.Characteristic.PositionState.STOPPED;
|
|
547
|
+
this.setNewTarget = false;
|
|
553
548
|
}
|
|
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
|
-
}
|
|
549
|
+
this.service.setCharacteristic(this.platform.Characteristic.PositionState, this.PositionState);
|
|
590
550
|
|
|
591
|
-
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
* device position does not change for more than `fastScanDuration`.
|
|
551
|
+
/**
|
|
552
|
+
* If Curtain movement time is short, the moving flag from backend is always false.
|
|
553
|
+
* The minimum time depends on the network control latency.
|
|
595
554
|
*/
|
|
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);
|
|
555
|
+
clearTimeout(this.setNewTargetTimer);
|
|
556
|
+
if (this.setNewTarget) {
|
|
557
|
+
this.setNewTargetTimer = setTimeout(() => {
|
|
558
|
+
this.platform.debug(`Curtain ${this.accessory.displayName} - setNewTarget ${this.setNewTarget} timeout`);
|
|
559
|
+
this.setNewTarget = false;
|
|
560
|
+
}, 10000);
|
|
669
561
|
}
|
|
670
|
-
|
|
671
|
-
this.AutoDisableFastScanTimeoutId = setTimeout(() => {
|
|
672
|
-
this.stopFastScan();
|
|
673
|
-
}, this.FastScanDuration);
|
|
562
|
+
this.doCurtainUpdate.next();
|
|
674
563
|
}
|
|
675
564
|
|
|
676
565
|
public setMinMax() {
|