tsvesync 1.0.43 → 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.
@@ -21,6 +21,24 @@ export class VeSyncAirBypass extends VeSyncFan {
21
21
  logger.debug(`Initialized VeSyncAirBypass device: ${this.deviceName}`);
22
22
  }
23
23
 
24
+ /**
25
+ * Build API dictionary
26
+ */
27
+ protected buildApiDict(method: string): [Record<string, any>, Record<string, any>] {
28
+ const head = Helpers.reqHeaderBypass();
29
+ const body = {
30
+ ...Helpers.reqBody(this.manager, 'bypassV2'),
31
+ cid: this.cid,
32
+ configModule: this.configModule,
33
+ payload: {
34
+ method,
35
+ source: 'APP',
36
+ data: {}
37
+ }
38
+ };
39
+ return [head, body];
40
+ }
41
+
24
42
  /**
25
43
  * Get device details
26
44
  */
@@ -1260,12 +1278,48 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
1260
1278
  export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1261
1279
  protected _lightDetection: boolean = false;
1262
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;
1263
1287
 
1264
1288
  constructor(details: Record<string, any>, manager: VeSync) {
1265
1289
  super(details, manager);
1266
1290
  logger.debug(`Initialized VeSyncAirBaseV2 device: ${this.deviceName}`);
1267
1291
  }
1268
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
+
1269
1323
  /**
1270
1324
  * Build API dictionary
1271
1325
  */
@@ -1273,7 +1327,7 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1273
1327
  const head = Helpers.reqHeaderBypass();
1274
1328
  const body = {
1275
1329
  ...Helpers.reqBody(this.manager, 'bypassV2'),
1276
- deviceId: this.cid,
1330
+ cid: this.cid,
1277
1331
  configModule: this.configModule,
1278
1332
  payload: {
1279
1333
  method,
@@ -1284,25 +1338,100 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1284
1338
  return [head, body];
1285
1339
  }
1286
1340
 
1287
- get lightDetection(): boolean {
1288
- return this._lightDetection;
1289
- }
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');
1290
1347
 
1291
- set lightDetection(toggle: boolean) {
1292
- this._lightDetection = toggle;
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;
1293
1374
  }
1294
1375
 
1295
- get lightDetectionState(): boolean {
1296
- return this._lightDetectionState;
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
+ }
1297
1419
  }
1298
1420
 
1299
- async setLightDetection(toggle: boolean): Promise<boolean> {
1300
- const [head, body] = this.buildApiDict('setLightDetection');
1301
- if (!head || !body) {
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');
1302
1427
  return false;
1303
1428
  }
1304
1429
 
1305
- body.payload.data = { enabled: toggle };
1430
+ const [head, body] = this.buildApiDict('setSwitch');
1431
+ body.payload.data = {
1432
+ powerSwitch: toggle ? 1 : 0,
1433
+ switchIdx: 0
1434
+ };
1306
1435
 
1307
1436
  const [response, status] = await this.callApi(
1308
1437
  '/cloud/v2/deviceManaged/bypassV2',
@@ -1311,55 +1440,255 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1311
1440
  head
1312
1441
  );
1313
1442
 
1314
- const success = this.checkResponse([response, status], 'setLightDetection');
1443
+ const success = this.checkResponse([response, status], 'toggleSwitch');
1315
1444
  if (success) {
1316
- this._lightDetection = toggle;
1445
+ this.deviceStatus = toggle ? 'on' : 'off';
1317
1446
  }
1318
1447
  return success;
1319
1448
  }
1320
1449
 
1321
- async setLightDetectionOn(): Promise<boolean> {
1322
- return this.setLightDetection(true);
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);
1323
1456
  }
1324
1457
 
1325
- async setLightDetectionOff(): Promise<boolean> {
1326
- return this.setLightDetection(false);
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);
1327
1464
  }
1328
1465
 
1329
- async turboMode(): Promise<boolean> {
1330
- return this.mode_toggle('turbo');
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;
1331
1507
  }
1332
1508
 
1333
- async petMode(): Promise<boolean> {
1334
- return this.mode_toggle('pet');
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;
1335
1546
  }
1336
1547
 
1337
1548
  /**
1338
- * Build configuration dictionary
1549
+ * Set display status
1339
1550
  */
1340
- protected buildConfigDict(configDict: Record<string, any>): void {
1341
- if (configDict) {
1342
- this.config = configDict;
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);
1343
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;
1344
1575
  }
1345
1576
 
1346
1577
  /**
1347
- * Set mode
1578
+ * Set child lock
1348
1579
  */
1349
- protected async mode_toggle(mode: string): Promise<boolean> {
1350
- const validModes = this.modes.map(m => m.toLowerCase());
1351
- if (!validModes.includes(mode.toLowerCase())) {
1352
- logger.debug(`Invalid mode used - ${mode}`);
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);
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;
1604
+ }
1605
+
1606
+ /**
1607
+ * Set timer
1608
+ */
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);
1614
+ }
1615
+
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');
1353
1622
  return false;
1354
1623
  }
1355
1624
 
1356
- const [head, body] = this.buildApiDict('setPurifierMode');
1357
- if (!head || !body) {
1625
+ const [head, body] = this.buildApiDict('addTimerV2');
1626
+ body.payload.subDeviceNo = 0;
1627
+ body.payload.data = {
1628
+ startAct: [{
1629
+ type: method,
1630
+ num: 0,
1631
+ act: action === 'on' ? 1 : 0
1632
+ }],
1633
+ total: timerDuration,
1634
+ subDeviceNo: 0
1635
+ };
1636
+
1637
+ const [response, status] = await this.callApi(
1638
+ '/cloud/v2/deviceManaged/bypassV2',
1639
+ 'post',
1640
+ body,
1641
+ head
1642
+ );
1643
+
1644
+ const success = this.checkResponse([response, status], 'setTimer');
1645
+ if (success) {
1646
+ this.timer = { duration: timerDuration, action };
1647
+ }
1648
+ return success;
1649
+ }
1650
+
1651
+ /**
1652
+ * Clear timer
1653
+ */
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;
1677
+ }
1678
+
1679
+ /**
1680
+ * Set auto preference
1681
+ */
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`);
1358
1685
  return false;
1359
1686
  }
1360
1687
 
1688
+ const [head, body] = this.buildApiDict('setAutoPreference');
1361
1689
  body.payload.data = {
1362
- mode: mode
1690
+ autoPreference: preference,
1691
+ roomSize: roomSize
1363
1692
  };
1364
1693
 
1365
1694
  const [response, status] = await this.callApi(
@@ -1369,14 +1698,94 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1369
1698
  head
1370
1699
  );
1371
1700
 
1372
- const success = this.checkResponse([response, status], 'mode_toggle');
1701
+ const success = this.checkResponse([response, status], 'setAutoPreference');
1373
1702
  if (success) {
1374
- this.details.mode = mode;
1375
- } else {
1376
- logger.debug('Error setting mode');
1703
+ this.details.auto_preference = preference;
1704
+ }
1705
+ return success;
1706
+ }
1707
+
1708
+ /**
1709
+ * Set light detection
1710
+ */
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;
1377
1727
  }
1378
1728
  return success;
1379
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);
1743
+ }
1744
+
1745
+ /**
1746
+ * Get auto preference type
1747
+ */
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);
1788
+ }
1380
1789
  }
1381
1790
 
1382
1791
  /**
@@ -1391,20 +1800,36 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1391
1800
  logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
1392
1801
  }
1393
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
+
1394
1819
  /**
1395
1820
  * Build API dictionary
1396
1821
  */
1397
1822
  protected buildApiDict(method: string): [Record<string, any>, Record<string, any>] {
1398
1823
  const head = Helpers.reqHeaderBypass();
1399
- const body = Helpers.reqBody(this.manager, 'bypassV2');
1400
- body.cid = this.cid;
1401
- body.deviceId = this.cid;
1402
- body.configModule = this.configModule;
1403
- body.configModel = this.configModule;
1404
- body.payload = {
1405
- method: method,
1406
- source: 'APP',
1407
- data: {}
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
+ }
1408
1833
  };
1409
1834
  return [head, body];
1410
1835
  }
@@ -1645,13 +2070,6 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1645
2070
  return Boolean(this.details.muteState);
1646
2071
  }
1647
2072
 
1648
- /**
1649
- * Get timer status (in seconds)
1650
- */
1651
- get timer(): number {
1652
- return this.details.timerRemain || 0;
1653
- }
1654
-
1655
2073
  /**
1656
2074
  * Get temperature (in tenths of a degree)
1657
2075
  */
@@ -1780,22 +2198,25 @@ export const fanModules: Record<string, new (details: Record<string, any>, manag
1780
2198
  'LAP-C601S-WUS': VeSyncAirBypass,
1781
2199
  'LAP-C601S-WUSR': VeSyncAirBypass,
1782
2200
  'LAP-C601S-WEU': VeSyncAirBypass,
1783
- 'LAP-V102S-AASR': VeSyncAirBypass,
1784
- 'LAP-V102S-WUS': VeSyncAirBypass,
1785
- 'LAP-V102S-WEU': VeSyncAirBypass,
1786
- 'LAP-V102S-AUSR': VeSyncAirBypass,
1787
- 'LAP-V102S-WJP': VeSyncAirBypass,
1788
- 'LAP-V201S-AASR': VeSyncAirBypass,
1789
- 'LAP-V201S-WJP': VeSyncAirBypass,
1790
- 'LAP-V201S-WEU': VeSyncAirBypass,
1791
- 'LAP-V201S-WUS': VeSyncAirBypass,
1792
- 'LAP-V201-AUSR': VeSyncAirBypass,
1793
- 'LAP-V201S-AUSR': VeSyncAirBypass,
1794
- 'LAP-V201S-AEUR': VeSyncAirBypass,
1795
- 'LAP-EL551S-AUS': VeSyncAirBypass,
1796
- 'LAP-EL551S-AEUR': VeSyncAirBypass,
1797
- 'LAP-EL551S-WEU': VeSyncAirBypass,
1798
- 'LAP-EL551S-WUS': VeSyncAirBypass,
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,
1799
2220
 
1800
2221
  // LTF Series
1801
2222
  'LTF-F422S-KEU': VeSyncTowerFan,