tsvesync 1.0.42 → 1.0.44
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/dist/lib/vesyncFan.d.ts +13 -2
- package/dist/lib/vesyncFan.js +48 -33
- package/dist/lib/vesyncFanImpl.d.ts +92 -39
- package/dist/lib/vesyncFanImpl.js +397 -174
- package/package.json +1 -1
- package/src/lib/vesyncFan.ts +49 -34
- package/src/lib/vesyncFanImpl.ts +476 -186
package/src/lib/vesyncFanImpl.ts
CHANGED
|
@@ -28,7 +28,7 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
28
28
|
const head = Helpers.reqHeaderBypass();
|
|
29
29
|
const body = {
|
|
30
30
|
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
31
|
-
|
|
31
|
+
cid: this.cid,
|
|
32
32
|
configModule: this.configModule,
|
|
33
33
|
payload: {
|
|
34
34
|
method,
|
|
@@ -533,53 +533,13 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
533
533
|
}
|
|
534
534
|
|
|
535
535
|
/**
|
|
536
|
-
* Set mode
|
|
536
|
+
* Set auto mode
|
|
537
537
|
*/
|
|
538
|
-
async
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
const [head, body] = this.buildApiDict('setPurifierMode');
|
|
545
|
-
if (!head || !body) {
|
|
546
|
-
return false;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
if (mode === 'manual') {
|
|
550
|
-
body.payload = {
|
|
551
|
-
data: {
|
|
552
|
-
id: 0,
|
|
553
|
-
level: 1,
|
|
554
|
-
type: 'wind'
|
|
555
|
-
},
|
|
556
|
-
method: 'setLevel',
|
|
557
|
-
type: 'APP'
|
|
558
|
-
};
|
|
559
|
-
} else {
|
|
560
|
-
body.payload.data = {
|
|
561
|
-
mode: mode.toLowerCase()
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const [response, status] = await this.callApi(
|
|
566
|
-
'/cloud/v2/deviceManaged/bypassV2',
|
|
567
|
-
'post',
|
|
568
|
-
body,
|
|
569
|
-
head
|
|
570
|
-
);
|
|
571
|
-
|
|
572
|
-
const success = this.checkResponse([response, status], 'mode_toggle');
|
|
573
|
-
if (success) {
|
|
574
|
-
if (mode === 'manual') {
|
|
575
|
-
this.details.speed = 1;
|
|
576
|
-
this.details.mode = 'manual';
|
|
577
|
-
} else {
|
|
578
|
-
this.details.mode = mode;
|
|
579
|
-
this.details.speed = 0;
|
|
580
|
-
}
|
|
581
|
-
} else {
|
|
582
|
-
logger.debug('Error setting purifier mode');
|
|
538
|
+
async autoMode(): Promise<boolean> {
|
|
539
|
+
logger.debug(`Setting auto mode for device: ${this.deviceName}`);
|
|
540
|
+
const success = await this.setMode('auto');
|
|
541
|
+
if (!success) {
|
|
542
|
+
logger.error(`Failed to set auto mode for device: ${this.deviceName}`);
|
|
583
543
|
}
|
|
584
544
|
return success;
|
|
585
545
|
}
|
|
@@ -587,34 +547,25 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
587
547
|
/**
|
|
588
548
|
* Set manual mode
|
|
589
549
|
*/
|
|
590
|
-
async
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
550
|
+
async manualMode(): Promise<boolean> {
|
|
551
|
+
logger.debug(`Setting manual mode for device: ${this.deviceName}`);
|
|
552
|
+
const success = await this.setMode('manual');
|
|
553
|
+
if (!success) {
|
|
554
|
+
logger.error(`Failed to set manual mode for device: ${this.deviceName}`);
|
|
594
555
|
}
|
|
595
|
-
return
|
|
556
|
+
return success;
|
|
596
557
|
}
|
|
597
558
|
|
|
598
559
|
/**
|
|
599
560
|
* Set sleep mode
|
|
600
561
|
*/
|
|
601
|
-
async
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
return this.mode_toggle('sleep');
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Set auto mode
|
|
611
|
-
*/
|
|
612
|
-
async auto_mode(): Promise<boolean> {
|
|
613
|
-
if (!this.modes.includes('auto')) {
|
|
614
|
-
logger.debug(`${this.deviceName} does not have auto mode`);
|
|
615
|
-
return false;
|
|
562
|
+
async sleepMode(): Promise<boolean> {
|
|
563
|
+
logger.debug(`Setting sleep mode for device: ${this.deviceName}`);
|
|
564
|
+
const success = await this.setMode('sleep');
|
|
565
|
+
if (!success) {
|
|
566
|
+
logger.error(`Failed to set sleep mode for device: ${this.deviceName}`);
|
|
616
567
|
}
|
|
617
|
-
return
|
|
568
|
+
return success;
|
|
618
569
|
}
|
|
619
570
|
|
|
620
571
|
/**
|
|
@@ -640,26 +591,6 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
640
591
|
}
|
|
641
592
|
return success;
|
|
642
593
|
}
|
|
643
|
-
|
|
644
|
-
/**
|
|
645
|
-
* Display device info
|
|
646
|
-
*/
|
|
647
|
-
override display(): void {
|
|
648
|
-
super.display();
|
|
649
|
-
const info = [
|
|
650
|
-
['Mode: ', this.details.mode || ''],
|
|
651
|
-
['Speed: ', this.speed],
|
|
652
|
-
['Filter Life: ', this.filterLife || 0, '%'],
|
|
653
|
-
['Air Quality: ', this.details.air_quality || ''],
|
|
654
|
-
['Display: ', this.details.display || false],
|
|
655
|
-
['Child Lock: ', this.details.child_lock || false],
|
|
656
|
-
['Night Light: ', this.details.night_light || 'off']
|
|
657
|
-
];
|
|
658
|
-
|
|
659
|
-
for (const [key, value, unit = ''] of info) {
|
|
660
|
-
logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
594
|
}
|
|
664
595
|
|
|
665
596
|
/**
|
|
@@ -1120,18 +1051,6 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
1120
1051
|
return success;
|
|
1121
1052
|
}
|
|
1122
1053
|
|
|
1123
|
-
/**
|
|
1124
|
-
* Set sleep mode
|
|
1125
|
-
*/
|
|
1126
|
-
async sleepMode(): Promise<boolean> {
|
|
1127
|
-
logger.debug(`Setting sleep mode for device: ${this.deviceName}`);
|
|
1128
|
-
const success = await this.setMode('sleep');
|
|
1129
|
-
if (!success) {
|
|
1130
|
-
logger.error(`Failed to set sleep mode for device: ${this.deviceName}`);
|
|
1131
|
-
}
|
|
1132
|
-
return success;
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
1054
|
/**
|
|
1136
1055
|
* Turn off display
|
|
1137
1056
|
*/
|
|
@@ -1359,12 +1278,48 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1359
1278
|
export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
1360
1279
|
protected _lightDetection: boolean = false;
|
|
1361
1280
|
protected _lightDetectionState: boolean = false;
|
|
1281
|
+
protected setSpeedLevel: number | null = null;
|
|
1282
|
+
protected autoPreferences: string[] = ['default', 'efficient', 'quiet'];
|
|
1283
|
+
protected enabled: boolean = false;
|
|
1284
|
+
protected _mode: string = '';
|
|
1285
|
+
protected _speed: number = 0;
|
|
1286
|
+
protected _timer: { duration: number; action: string } | null = null;
|
|
1362
1287
|
|
|
1363
1288
|
constructor(details: Record<string, any>, manager: VeSync) {
|
|
1364
1289
|
super(details, manager);
|
|
1365
1290
|
logger.debug(`Initialized VeSyncAirBaseV2 device: ${this.deviceName}`);
|
|
1366
1291
|
}
|
|
1367
1292
|
|
|
1293
|
+
protected buildConfigDict(configDict: Record<string, any>): void {
|
|
1294
|
+
if (configDict) {
|
|
1295
|
+
this.config = configDict;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
get mode(): string {
|
|
1300
|
+
return this._mode;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
set mode(value: string) {
|
|
1304
|
+
this._mode = value;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
get speed(): number {
|
|
1308
|
+
return this._speed;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
set speed(value: number) {
|
|
1312
|
+
this._speed = value;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
get timer(): { duration: number; action: string } | null {
|
|
1316
|
+
return this._timer;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
set timer(value: { duration: number; action: string } | null) {
|
|
1320
|
+
this._timer = value;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1368
1323
|
/**
|
|
1369
1324
|
* Build API dictionary
|
|
1370
1325
|
*/
|
|
@@ -1372,7 +1327,7 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1372
1327
|
const head = Helpers.reqHeaderBypass();
|
|
1373
1328
|
const body = {
|
|
1374
1329
|
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1375
|
-
|
|
1330
|
+
cid: this.cid,
|
|
1376
1331
|
configModule: this.configModule,
|
|
1377
1332
|
payload: {
|
|
1378
1333
|
method,
|
|
@@ -1383,25 +1338,100 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1383
1338
|
return [head, body];
|
|
1384
1339
|
}
|
|
1385
1340
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1341
|
+
/**
|
|
1342
|
+
* Get device details
|
|
1343
|
+
*/
|
|
1344
|
+
async getDetails(): Promise<Boolean> {
|
|
1345
|
+
logger.debug(`Getting details for device: ${this.deviceName}`);
|
|
1346
|
+
const [head, body] = this.buildApiDict('getPurifierStatus');
|
|
1389
1347
|
|
|
1390
|
-
|
|
1391
|
-
|
|
1348
|
+
const [response, status] = await this.callApi(
|
|
1349
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1350
|
+
'post',
|
|
1351
|
+
body,
|
|
1352
|
+
head
|
|
1353
|
+
);
|
|
1354
|
+
|
|
1355
|
+
if (!this.checkResponse([response, status], 'getDetails') || !response?.result) {
|
|
1356
|
+
logger.debug('Error getting purifier details');
|
|
1357
|
+
this.connectionStatus = 'offline';
|
|
1358
|
+
return false;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const innerResponse = response.result;
|
|
1362
|
+
if (innerResponse.code !== 0 || !innerResponse.result) {
|
|
1363
|
+
logger.debug('Error in inner response from purifier');
|
|
1364
|
+
this.connectionStatus = 'offline';
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const deviceData = innerResponse.result;
|
|
1369
|
+
this.buildPurifierDict(deviceData);
|
|
1370
|
+
if (deviceData.configuration) {
|
|
1371
|
+
this.buildConfigDict(deviceData.configuration);
|
|
1372
|
+
}
|
|
1373
|
+
return true;
|
|
1392
1374
|
}
|
|
1393
1375
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1376
|
+
/**
|
|
1377
|
+
* Build purifier dictionary
|
|
1378
|
+
*/
|
|
1379
|
+
protected buildPurifierDict(devDict: Record<string, any>): void {
|
|
1380
|
+
this.connectionStatus = 'online';
|
|
1381
|
+
const powerSwitch = Boolean(devDict.powerSwitch ?? 0);
|
|
1382
|
+
this.enabled = powerSwitch;
|
|
1383
|
+
this.deviceStatus = powerSwitch ? 'on' : 'off';
|
|
1384
|
+
this.mode = devDict.workMode ?? 'manual';
|
|
1385
|
+
|
|
1386
|
+
this.speed = devDict.fanSpeedLevel ?? 0;
|
|
1387
|
+
this.setSpeedLevel = devDict.manualSpeedLevel ?? 1;
|
|
1388
|
+
|
|
1389
|
+
this.details = {
|
|
1390
|
+
...this.details,
|
|
1391
|
+
filter_life: devDict.filterLifePercent ?? 0,
|
|
1392
|
+
child_lock: Boolean(devDict.childLockSwitch ?? 0),
|
|
1393
|
+
display: Boolean(devDict.screenState ?? 0),
|
|
1394
|
+
light_detection_switch: Boolean(devDict.lightDetectionSwitch ?? 0),
|
|
1395
|
+
environment_light_state: Boolean(devDict.environmentLightState ?? 0),
|
|
1396
|
+
screen_switch: Boolean(devDict.screenSwitch ?? 0)
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
if (this.hasFeature('air_quality')) {
|
|
1400
|
+
this.details.air_quality_value = devDict.PM25 ?? 0;
|
|
1401
|
+
this.details.air_quality = devDict.AQLevel ?? 0;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
if ('PM1' in devDict) this.details.pm1 = devDict.PM1;
|
|
1405
|
+
if ('PM10' in devDict) this.details.pm10 = devDict.PM10;
|
|
1406
|
+
if ('AQPercent' in devDict) this.details.aq_percent = devDict.AQPercent;
|
|
1407
|
+
if ('fanRotateAngle' in devDict) this.details.fan_rotate_angle = devDict.fanRotateAngle;
|
|
1408
|
+
if ('filterOpenState' in devDict) this.details.filter_open_state = Boolean(devDict.filterOpenState);
|
|
1409
|
+
|
|
1410
|
+
if ((devDict.timerRemain ?? 0) > 0) {
|
|
1411
|
+
this.timer = { duration: devDict.timerRemain, action: 'off' };
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
if (typeof devDict.autoPreference === 'object' && devDict.autoPreference) {
|
|
1415
|
+
this.details.auto_preference_type = devDict.autoPreference?.autoPreferenceType ?? 'default';
|
|
1416
|
+
} else {
|
|
1417
|
+
this.details.auto_preference_type = null;
|
|
1418
|
+
}
|
|
1396
1419
|
}
|
|
1397
1420
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1421
|
+
/**
|
|
1422
|
+
* Toggle device power
|
|
1423
|
+
*/
|
|
1424
|
+
async toggleSwitch(toggle: boolean): Promise<boolean> {
|
|
1425
|
+
if (typeof toggle !== 'boolean') {
|
|
1426
|
+
logger.debug('Invalid toggle value for purifier switch');
|
|
1401
1427
|
return false;
|
|
1402
1428
|
}
|
|
1403
1429
|
|
|
1404
|
-
body
|
|
1430
|
+
const [head, body] = this.buildApiDict('setSwitch');
|
|
1431
|
+
body.payload.data = {
|
|
1432
|
+
powerSwitch: toggle ? 1 : 0,
|
|
1433
|
+
switchIdx: 0
|
|
1434
|
+
};
|
|
1405
1435
|
|
|
1406
1436
|
const [response, status] = await this.callApi(
|
|
1407
1437
|
'/cloud/v2/deviceManaged/bypassV2',
|
|
@@ -1410,55 +1440,198 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1410
1440
|
head
|
|
1411
1441
|
);
|
|
1412
1442
|
|
|
1413
|
-
const success = this.checkResponse([response, status], '
|
|
1443
|
+
const success = this.checkResponse([response, status], 'toggleSwitch');
|
|
1414
1444
|
if (success) {
|
|
1415
|
-
this.
|
|
1445
|
+
this.deviceStatus = toggle ? 'on' : 'off';
|
|
1416
1446
|
}
|
|
1417
1447
|
return success;
|
|
1418
1448
|
}
|
|
1419
1449
|
|
|
1420
|
-
|
|
1421
|
-
|
|
1450
|
+
/**
|
|
1451
|
+
* Turn device on - compatibility method that proxies to toggleSwitch
|
|
1452
|
+
*/
|
|
1453
|
+
async turnOn(): Promise<boolean> {
|
|
1454
|
+
logger.info(`Turning on device: ${this.deviceName}`);
|
|
1455
|
+
return this.toggleSwitch(true);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* Turn device off - compatibility method that proxies to toggleSwitch
|
|
1460
|
+
*/
|
|
1461
|
+
async turnOff(): Promise<boolean> {
|
|
1462
|
+
logger.info(`Turning off device: ${this.deviceName}`);
|
|
1463
|
+
return this.toggleSwitch(false);
|
|
1422
1464
|
}
|
|
1423
1465
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1466
|
+
/**
|
|
1467
|
+
* Change fan speed
|
|
1468
|
+
*/
|
|
1469
|
+
async changeFanSpeed(speed: number): Promise<boolean> {
|
|
1470
|
+
const speeds: number[] = this.config?.levels ?? [];
|
|
1471
|
+
const currentSpeed = this.setSpeedLevel ?? 0;
|
|
1472
|
+
|
|
1473
|
+
if (speed !== undefined) {
|
|
1474
|
+
if (!speeds.includes(speed)) {
|
|
1475
|
+
logger.debug(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')}`);
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
} else {
|
|
1479
|
+
if (currentSpeed === speeds[speeds.length - 1] || currentSpeed === 0) {
|
|
1480
|
+
speed = speeds[0];
|
|
1481
|
+
} else {
|
|
1482
|
+
const currentIndex = speeds.indexOf(currentSpeed);
|
|
1483
|
+
speed = speeds[currentIndex + 1];
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
const [head, body] = this.buildApiDict('setLevel');
|
|
1488
|
+
body.payload.data = {
|
|
1489
|
+
levelIdx: 0,
|
|
1490
|
+
manualSpeedLevel: speed,
|
|
1491
|
+
levelType: 'wind'
|
|
1492
|
+
};
|
|
1493
|
+
|
|
1494
|
+
const [response, status] = await this.callApi(
|
|
1495
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1496
|
+
'post',
|
|
1497
|
+
body,
|
|
1498
|
+
head
|
|
1499
|
+
);
|
|
1500
|
+
|
|
1501
|
+
const success = this.checkResponse([response, status], 'changeFanSpeed');
|
|
1502
|
+
if (success) {
|
|
1503
|
+
this.setSpeedLevel = speed;
|
|
1504
|
+
this.mode = 'manual';
|
|
1505
|
+
}
|
|
1506
|
+
return success;
|
|
1426
1507
|
}
|
|
1427
1508
|
|
|
1428
|
-
|
|
1429
|
-
|
|
1509
|
+
/**
|
|
1510
|
+
* Set mode
|
|
1511
|
+
*/
|
|
1512
|
+
async setMode(mode: string): Promise<boolean> {
|
|
1513
|
+
if (!this.modes.includes(mode as any)) {
|
|
1514
|
+
logger.debug(`Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`);
|
|
1515
|
+
return false;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
if (mode === 'manual') {
|
|
1519
|
+
if (!this.speed || this.speed === 0) {
|
|
1520
|
+
return this.changeFanSpeed(1);
|
|
1521
|
+
}
|
|
1522
|
+
return this.changeFanSpeed(this.speed);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (mode === 'off') {
|
|
1526
|
+
return this.turnOff();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
const [head, body] = this.buildApiDict('setPurifierMode');
|
|
1530
|
+
body.payload.data = {
|
|
1531
|
+
workMode: mode
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1534
|
+
const [response, status] = await this.callApi(
|
|
1535
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1536
|
+
'post',
|
|
1537
|
+
body,
|
|
1538
|
+
head
|
|
1539
|
+
);
|
|
1540
|
+
|
|
1541
|
+
const success = this.checkResponse([response, status], 'setMode');
|
|
1542
|
+
if (success) {
|
|
1543
|
+
this.mode = mode;
|
|
1544
|
+
}
|
|
1545
|
+
return success;
|
|
1430
1546
|
}
|
|
1431
1547
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1548
|
+
/**
|
|
1549
|
+
* Set display status
|
|
1550
|
+
*/
|
|
1551
|
+
async setDisplay(enabled: boolean): Promise<boolean> {
|
|
1552
|
+
if (!this.hasFeature('display')) {
|
|
1553
|
+
const error = 'Display control not supported';
|
|
1554
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
1555
|
+
throw new Error(error);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
const [head, body] = this.buildApiDict('setDisplay');
|
|
1559
|
+
body.payload.data = {
|
|
1560
|
+
screenSwitch: enabled ? 1 : 0
|
|
1561
|
+
};
|
|
1562
|
+
|
|
1563
|
+
const [response, status] = await this.callApi(
|
|
1564
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1565
|
+
'post',
|
|
1566
|
+
body,
|
|
1567
|
+
head
|
|
1568
|
+
);
|
|
1569
|
+
|
|
1570
|
+
const success = this.checkResponse([response, status], 'setDisplay');
|
|
1571
|
+
if (success) {
|
|
1572
|
+
this.details.screen_switch = enabled;
|
|
1573
|
+
}
|
|
1574
|
+
return success;
|
|
1434
1575
|
}
|
|
1435
1576
|
|
|
1436
1577
|
/**
|
|
1437
|
-
*
|
|
1578
|
+
* Set child lock
|
|
1438
1579
|
*/
|
|
1439
|
-
|
|
1440
|
-
if (
|
|
1441
|
-
|
|
1580
|
+
async setChildLock(enabled: boolean): Promise<boolean> {
|
|
1581
|
+
if (!this.hasFeature('child_lock')) {
|
|
1582
|
+
const error = 'Child lock not supported';
|
|
1583
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
1584
|
+
throw new Error(error);
|
|
1442
1585
|
}
|
|
1586
|
+
|
|
1587
|
+
const [head, body] = this.buildApiDict('setChildLock');
|
|
1588
|
+
body.payload.data = {
|
|
1589
|
+
childLockSwitch: enabled ? 1 : 0
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
const [response, status] = await this.callApi(
|
|
1593
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1594
|
+
'post',
|
|
1595
|
+
body,
|
|
1596
|
+
head
|
|
1597
|
+
);
|
|
1598
|
+
|
|
1599
|
+
const success = this.checkResponse([response, status], 'setChildLock');
|
|
1600
|
+
if (success) {
|
|
1601
|
+
this.details.child_lock = enabled;
|
|
1602
|
+
}
|
|
1603
|
+
return success;
|
|
1443
1604
|
}
|
|
1444
1605
|
|
|
1445
1606
|
/**
|
|
1446
|
-
* Set
|
|
1607
|
+
* Set timer
|
|
1447
1608
|
*/
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
logger.
|
|
1452
|
-
|
|
1609
|
+
async setTimer(timerDuration: number, action: 'on' | 'off' = 'off', method: string = 'powerSwitch'): Promise<boolean> {
|
|
1610
|
+
if (!this.hasFeature('timer')) {
|
|
1611
|
+
const error = 'Timer not supported';
|
|
1612
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
1613
|
+
throw new Error(error);
|
|
1453
1614
|
}
|
|
1454
1615
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1616
|
+
if (!['on', 'off'].includes(action)) {
|
|
1617
|
+
logger.debug('Invalid action for timer');
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
if (method !== 'powerSwitch') {
|
|
1621
|
+
logger.debug('Invalid method for timer');
|
|
1457
1622
|
return false;
|
|
1458
1623
|
}
|
|
1459
1624
|
|
|
1625
|
+
const [head, body] = this.buildApiDict('addTimerV2');
|
|
1626
|
+
body.payload.subDeviceNo = 0;
|
|
1460
1627
|
body.payload.data = {
|
|
1461
|
-
|
|
1628
|
+
startAct: [{
|
|
1629
|
+
type: method,
|
|
1630
|
+
num: 0,
|
|
1631
|
+
act: action === 'on' ? 1 : 0
|
|
1632
|
+
}],
|
|
1633
|
+
total: timerDuration,
|
|
1634
|
+
subDeviceNo: 0
|
|
1462
1635
|
};
|
|
1463
1636
|
|
|
1464
1637
|
const [response, status] = await this.callApi(
|
|
@@ -1468,45 +1641,150 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1468
1641
|
head
|
|
1469
1642
|
);
|
|
1470
1643
|
|
|
1471
|
-
const success = this.checkResponse([response, status], '
|
|
1644
|
+
const success = this.checkResponse([response, status], 'setTimer');
|
|
1472
1645
|
if (success) {
|
|
1473
|
-
this.
|
|
1474
|
-
} else {
|
|
1475
|
-
logger.debug('Error setting mode');
|
|
1646
|
+
this.timer = { duration: timerDuration, action };
|
|
1476
1647
|
}
|
|
1477
1648
|
return success;
|
|
1478
1649
|
}
|
|
1479
1650
|
|
|
1480
1651
|
/**
|
|
1481
|
-
*
|
|
1652
|
+
* Clear timer
|
|
1482
1653
|
*/
|
|
1483
|
-
async
|
|
1484
|
-
|
|
1654
|
+
async clearTimer(): Promise<boolean> {
|
|
1655
|
+
if (!this.hasFeature('timer')) {
|
|
1656
|
+
const error = 'Timer not supported';
|
|
1657
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
1658
|
+
throw new Error(error);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
const [head, body] = this.buildApiDict('delTimerV2');
|
|
1662
|
+
body.payload.subDeviceNo = 0;
|
|
1663
|
+
body.payload.data = { id: 1, subDeviceNo: 0 };
|
|
1664
|
+
|
|
1665
|
+
const [response, status] = await this.callApi(
|
|
1666
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1667
|
+
'post',
|
|
1668
|
+
body,
|
|
1669
|
+
head
|
|
1670
|
+
);
|
|
1671
|
+
|
|
1672
|
+
const success = this.checkResponse([response, status], 'clearTimer');
|
|
1673
|
+
if (success) {
|
|
1674
|
+
this.timer = null;
|
|
1675
|
+
}
|
|
1676
|
+
return success;
|
|
1485
1677
|
}
|
|
1486
1678
|
|
|
1487
1679
|
/**
|
|
1488
|
-
* Set
|
|
1680
|
+
* Set auto preference
|
|
1489
1681
|
*/
|
|
1490
|
-
async
|
|
1491
|
-
|
|
1682
|
+
async setAutoPreference(preference: string = 'default', roomSize: number = 600): Promise<boolean> {
|
|
1683
|
+
if (!this.autoPreferences.includes(preference)) {
|
|
1684
|
+
logger.debug(`Invalid preference: ${preference} - valid preferences are default, efficient, quiet`);
|
|
1685
|
+
return false;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
const [head, body] = this.buildApiDict('setAutoPreference');
|
|
1689
|
+
body.payload.data = {
|
|
1690
|
+
autoPreference: preference,
|
|
1691
|
+
roomSize: roomSize
|
|
1692
|
+
};
|
|
1693
|
+
|
|
1694
|
+
const [response, status] = await this.callApi(
|
|
1695
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1696
|
+
'post',
|
|
1697
|
+
body,
|
|
1698
|
+
head
|
|
1699
|
+
);
|
|
1700
|
+
|
|
1701
|
+
const success = this.checkResponse([response, status], 'setAutoPreference');
|
|
1702
|
+
if (success) {
|
|
1703
|
+
this.details.auto_preference = preference;
|
|
1704
|
+
}
|
|
1705
|
+
return success;
|
|
1492
1706
|
}
|
|
1493
1707
|
|
|
1494
1708
|
/**
|
|
1495
|
-
* Set
|
|
1709
|
+
* Set light detection
|
|
1496
1710
|
*/
|
|
1497
|
-
async
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1711
|
+
async setLightDetection(enabled: boolean): Promise<boolean> {
|
|
1712
|
+
const [head, body] = this.buildApiDict('setLightDetection');
|
|
1713
|
+
body.payload.data = {
|
|
1714
|
+
lightDetectionSwitch: enabled ? 1 : 0
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1717
|
+
const [response, status] = await this.callApi(
|
|
1718
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1719
|
+
'post',
|
|
1720
|
+
body,
|
|
1721
|
+
head
|
|
1722
|
+
);
|
|
1723
|
+
|
|
1724
|
+
const success = this.checkResponse([response, status], 'setLightDetection');
|
|
1725
|
+
if (success) {
|
|
1726
|
+
this.details.light_detection_switch = enabled;
|
|
1501
1727
|
}
|
|
1502
|
-
return
|
|
1728
|
+
return success;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
/**
|
|
1732
|
+
* Get light detection status
|
|
1733
|
+
*/
|
|
1734
|
+
get lightDetection(): boolean {
|
|
1735
|
+
return Boolean(this.details.light_detection_switch);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
/**
|
|
1739
|
+
* Get light detection state
|
|
1740
|
+
*/
|
|
1741
|
+
get lightDetectionState(): boolean {
|
|
1742
|
+
return Boolean(this.details.environment_light_state);
|
|
1503
1743
|
}
|
|
1504
1744
|
|
|
1505
1745
|
/**
|
|
1506
|
-
*
|
|
1746
|
+
* Get auto preference type
|
|
1507
1747
|
*/
|
|
1508
|
-
|
|
1509
|
-
return this.
|
|
1748
|
+
get autoPreferenceType(): string | null {
|
|
1749
|
+
return this.details.auto_preference_type;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/**
|
|
1753
|
+
* Display JSON details
|
|
1754
|
+
*/
|
|
1755
|
+
override displayJSON(): string {
|
|
1756
|
+
const baseInfo = JSON.parse(super.displayJSON());
|
|
1757
|
+
const details: Record<string, string> = {
|
|
1758
|
+
...baseInfo,
|
|
1759
|
+
'Mode': this.mode,
|
|
1760
|
+
'Filter Life': this.details.filter_life.toString(),
|
|
1761
|
+
'Fan Level': this.speed.toString(),
|
|
1762
|
+
'Display On': this.details.display.toString(),
|
|
1763
|
+
'Child Lock': this.details.child_lock.toString(),
|
|
1764
|
+
'Display Set On': this.details.screen_switch.toString(),
|
|
1765
|
+
'Light Detection Enabled': this.details.light_detection_switch.toString(),
|
|
1766
|
+
'Environment Light State': this.details.environment_light_state.toString()
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
if (this.hasFeature('air_quality')) {
|
|
1770
|
+
details['Air Quality Level'] = (this.details.air_quality ?? '').toString();
|
|
1771
|
+
details['Air Quality Value'] = (this.details.air_quality_value ?? '').toString();
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
const everestKeys: Record<string, string> = {
|
|
1775
|
+
'pm1': 'PM1',
|
|
1776
|
+
'pm10': 'PM10',
|
|
1777
|
+
'fan_rotate_angle': 'Fan Rotate Angle',
|
|
1778
|
+
'filter_open_state': 'Filter Open State'
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
for (const [key, value] of Object.entries(everestKeys)) {
|
|
1782
|
+
if (key in this.details) {
|
|
1783
|
+
details[value] = this.details[key].toString();
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
return JSON.stringify(details, null, 4);
|
|
1510
1788
|
}
|
|
1511
1789
|
}
|
|
1512
1790
|
|
|
@@ -1522,20 +1800,36 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
|
1522
1800
|
logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
|
|
1523
1801
|
}
|
|
1524
1802
|
|
|
1803
|
+
/**
|
|
1804
|
+
* Get timer status (in seconds)
|
|
1805
|
+
*/
|
|
1806
|
+
override get timer(): { duration: number; action: string } | null {
|
|
1807
|
+
const timerRemain = this.details.timerRemain || 0;
|
|
1808
|
+
return timerRemain > 0 ? { duration: timerRemain, action: 'off' } : null;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
override set timer(value: { duration: number; action: string } | null) {
|
|
1812
|
+
if (value) {
|
|
1813
|
+
this.details.timerRemain = value.duration;
|
|
1814
|
+
} else {
|
|
1815
|
+
this.details.timerRemain = 0;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1525
1819
|
/**
|
|
1526
1820
|
* Build API dictionary
|
|
1527
1821
|
*/
|
|
1528
1822
|
protected buildApiDict(method: string): [Record<string, any>, Record<string, any>] {
|
|
1529
1823
|
const head = Helpers.reqHeaderBypass();
|
|
1530
|
-
const body =
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1824
|
+
const body = {
|
|
1825
|
+
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1826
|
+
cid: this.cid,
|
|
1827
|
+
configModule: this.configModule,
|
|
1828
|
+
payload: {
|
|
1829
|
+
method,
|
|
1830
|
+
source: 'APP',
|
|
1831
|
+
data: {}
|
|
1832
|
+
}
|
|
1539
1833
|
};
|
|
1540
1834
|
return [head, body];
|
|
1541
1835
|
}
|
|
@@ -1776,13 +2070,6 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
|
1776
2070
|
return Boolean(this.details.muteState);
|
|
1777
2071
|
}
|
|
1778
2072
|
|
|
1779
|
-
/**
|
|
1780
|
-
* Get timer status (in seconds)
|
|
1781
|
-
*/
|
|
1782
|
-
get timer(): number {
|
|
1783
|
-
return this.details.timerRemain || 0;
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
2073
|
/**
|
|
1787
2074
|
* Get temperature (in tenths of a degree)
|
|
1788
2075
|
*/
|
|
@@ -1911,22 +2198,25 @@ export const fanModules: Record<string, new (details: Record<string, any>, manag
|
|
|
1911
2198
|
'LAP-C601S-WUS': VeSyncAirBypass,
|
|
1912
2199
|
'LAP-C601S-WUSR': VeSyncAirBypass,
|
|
1913
2200
|
'LAP-C601S-WEU': VeSyncAirBypass,
|
|
1914
|
-
|
|
1915
|
-
'LAP-V102S-
|
|
1916
|
-
'LAP-V102S-
|
|
1917
|
-
'LAP-V102S-
|
|
1918
|
-
'LAP-V102S-
|
|
1919
|
-
'LAP-
|
|
1920
|
-
|
|
1921
|
-
'LAP-V201S-
|
|
1922
|
-
'LAP-V201S-
|
|
1923
|
-
'LAP-
|
|
1924
|
-
'LAP-V201S-
|
|
1925
|
-
'LAP-
|
|
1926
|
-
'LAP-
|
|
1927
|
-
'LAP-
|
|
1928
|
-
|
|
1929
|
-
'LAP-EL551S-
|
|
2201
|
+
// Vital100S Series - Using VeSyncAirBaseV2
|
|
2202
|
+
'LAP-V102S-AASR': VeSyncAirBaseV2,
|
|
2203
|
+
'LAP-V102S-WUS': VeSyncAirBaseV2,
|
|
2204
|
+
'LAP-V102S-WEU': VeSyncAirBaseV2,
|
|
2205
|
+
'LAP-V102S-AUSR': VeSyncAirBaseV2,
|
|
2206
|
+
'LAP-V102S-WJP': VeSyncAirBaseV2,
|
|
2207
|
+
// Vital200S Series - Using VeSyncAirBaseV2
|
|
2208
|
+
'LAP-V201S-AASR': VeSyncAirBaseV2,
|
|
2209
|
+
'LAP-V201S-WJP': VeSyncAirBaseV2,
|
|
2210
|
+
'LAP-V201S-WEU': VeSyncAirBaseV2,
|
|
2211
|
+
'LAP-V201S-WUS': VeSyncAirBaseV2,
|
|
2212
|
+
'LAP-V201-AUSR': VeSyncAirBaseV2,
|
|
2213
|
+
'LAP-V201S-AUSR': VeSyncAirBaseV2,
|
|
2214
|
+
'LAP-V201S-AEUR': VeSyncAirBaseV2,
|
|
2215
|
+
// EverestAir Series - Using VeSyncAirBaseV2
|
|
2216
|
+
'LAP-EL551S-AUS': VeSyncAirBaseV2,
|
|
2217
|
+
'LAP-EL551S-AEUR': VeSyncAirBaseV2,
|
|
2218
|
+
'LAP-EL551S-WEU': VeSyncAirBaseV2,
|
|
2219
|
+
'LAP-EL551S-WUS': VeSyncAirBaseV2,
|
|
1930
2220
|
|
|
1931
2221
|
// LTF Series
|
|
1932
2222
|
'LTF-F422S-KEU': VeSyncTowerFan,
|