tsvesync 1.0.89 → 1.0.91
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/README.md +1 -36
- package/dist/__tests__/fans/vesyncAir131.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAir131.test.js +365 -0
- package/dist/__tests__/fans/vesyncAirBaseV2.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAirBaseV2.test.js +283 -0
- package/dist/__tests__/fans/vesyncAirBypass.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAirBypass.test.js +257 -0
- package/dist/__tests__/vesyncFan.test.d.ts +4 -0
- package/dist/__tests__/vesyncFan.test.js +263 -0
- package/dist/lib/fans/air131.js +5 -4
- package/dist/lib/fans/airBypass.js +12 -4
- package/dist/lib/helpers.d.ts +0 -13
- package/dist/lib/helpers.js +4 -158
- package/dist/lib/vesync.d.ts +1 -1
- package/dist/lib/vesync.js +5 -5
- package/dist/lib/vesyncFan.d.ts +30 -4
- package/dist/lib/vesyncFan.js +70 -5
- package/package.json +1 -1
- package/src/lib/fans/air131.ts +5 -4
- package/src/lib/fans/airBypass.ts +10 -4
- package/src/lib/helpers.ts +3 -169
- package/src/lib/vesync.ts +6 -6
- package/src/lib/vesyncFan.ts +79 -7
package/dist/lib/vesyncFan.js
CHANGED
|
@@ -375,7 +375,14 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
|
|
|
375
375
|
*/
|
|
376
376
|
get filterLife() {
|
|
377
377
|
var _a;
|
|
378
|
-
|
|
378
|
+
// Handle different filter_life formats from different devices
|
|
379
|
+
const filterLife = this.details.filter_life;
|
|
380
|
+
// For Air131 devices, filter_life is an object with percent property
|
|
381
|
+
if (typeof filterLife === 'object' && filterLife !== null && 'percent' in filterLife) {
|
|
382
|
+
return (_a = filterLife.percent) !== null && _a !== void 0 ? _a : 0;
|
|
383
|
+
}
|
|
384
|
+
// For other devices, filter_life is a direct number
|
|
385
|
+
return typeof filterLife === 'number' ? filterLife : 0;
|
|
379
386
|
}
|
|
380
387
|
/**
|
|
381
388
|
* Get screen status
|
|
@@ -392,12 +399,46 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
|
|
|
392
399
|
return (_a = this.details.child_lock) !== null && _a !== void 0 ? _a : false;
|
|
393
400
|
}
|
|
394
401
|
/**
|
|
395
|
-
* Get air quality (if supported)
|
|
402
|
+
* Get air quality level (if supported)
|
|
396
403
|
*/
|
|
397
|
-
get
|
|
404
|
+
get airQualityLevel() {
|
|
398
405
|
var _a;
|
|
399
406
|
return (_a = this.details.air_quality) !== null && _a !== void 0 ? _a : '';
|
|
400
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Get air quality (alias for airQualityLevel for backward compatibility)
|
|
410
|
+
*/
|
|
411
|
+
get airQuality() {
|
|
412
|
+
return this.airQualityLevel;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Get air quality value - PM2.5 in μg/m³ (if supported)
|
|
416
|
+
*/
|
|
417
|
+
get airQualityValue() {
|
|
418
|
+
var _a;
|
|
419
|
+
return (_a = this.details.air_quality_value) !== null && _a !== void 0 ? _a : 0;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Get PM1 value (if supported)
|
|
423
|
+
*/
|
|
424
|
+
get pm1() {
|
|
425
|
+
var _a;
|
|
426
|
+
return (_a = this.details.pm1) !== null && _a !== void 0 ? _a : 0;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Get PM10 value (if supported)
|
|
430
|
+
*/
|
|
431
|
+
get pm10() {
|
|
432
|
+
var _a;
|
|
433
|
+
return (_a = this.details.pm10) !== null && _a !== void 0 ? _a : 0;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Get air quality percentage (if supported)
|
|
437
|
+
*/
|
|
438
|
+
get aqPercent() {
|
|
439
|
+
var _a;
|
|
440
|
+
return (_a = this.details.aq_percent) !== null && _a !== void 0 ? _a : 0;
|
|
441
|
+
}
|
|
401
442
|
/**
|
|
402
443
|
* Get humidity level (if supported)
|
|
403
444
|
*/
|
|
@@ -445,7 +486,19 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
|
|
|
445
486
|
info.push(['Child Lock: ', this.childLock ? 'Enabled' : 'Disabled']);
|
|
446
487
|
}
|
|
447
488
|
if (this.hasFeature('air_quality')) {
|
|
448
|
-
info.push(['Air Quality: ', this.
|
|
489
|
+
info.push(['Air Quality Level: ', this.airQualityLevel]);
|
|
490
|
+
if (this.airQualityValue > 0) {
|
|
491
|
+
info.push(['Air Quality Value: ', this.airQualityValue, ' μg/m³']);
|
|
492
|
+
}
|
|
493
|
+
if (this.pm1 > 0) {
|
|
494
|
+
info.push(['PM1: ', this.pm1, ' μg/m³']);
|
|
495
|
+
}
|
|
496
|
+
if (this.pm10 > 0) {
|
|
497
|
+
info.push(['PM10: ', this.pm10, ' μg/m³']);
|
|
498
|
+
}
|
|
499
|
+
if (this.aqPercent > 0) {
|
|
500
|
+
info.push(['AQ Percent: ', this.aqPercent, '%']);
|
|
501
|
+
}
|
|
449
502
|
}
|
|
450
503
|
if (this.hasFeature('humidity')) {
|
|
451
504
|
info.push(['Humidity: ', this.humidity, '%']);
|
|
@@ -486,7 +539,19 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
|
|
|
486
539
|
details['Child Lock'] = this.childLock ? 'Enabled' : 'Disabled';
|
|
487
540
|
}
|
|
488
541
|
if (this.hasFeature('air_quality')) {
|
|
489
|
-
details['Air Quality'] = this.
|
|
542
|
+
details['Air Quality Level'] = this.airQualityLevel.toString();
|
|
543
|
+
if (this.airQualityValue > 0) {
|
|
544
|
+
details['Air Quality Value'] = this.airQualityValue.toString();
|
|
545
|
+
}
|
|
546
|
+
if (this.pm1 > 0) {
|
|
547
|
+
details['PM1'] = this.pm1.toString();
|
|
548
|
+
}
|
|
549
|
+
if (this.pm10 > 0) {
|
|
550
|
+
details['PM10'] = this.pm10.toString();
|
|
551
|
+
}
|
|
552
|
+
if (this.aqPercent > 0) {
|
|
553
|
+
details['AQ Percent'] = this.aqPercent.toString();
|
|
554
|
+
}
|
|
490
555
|
}
|
|
491
556
|
if (this.hasFeature('humidity')) {
|
|
492
557
|
details['Humidity'] = this.humidity.toString();
|
package/package.json
CHANGED
package/src/lib/fans/air131.ts
CHANGED
|
@@ -67,10 +67,11 @@ export class VeSyncAir131 extends VeSyncFan {
|
|
|
67
67
|
this.details = {
|
|
68
68
|
mode: data.mode || 'manual', // Default to manual if not specified
|
|
69
69
|
speed: data.level || 0,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
filter_life: data.filterLife || data.filter_life || 0,
|
|
71
|
+
screen_status: data.screenStatus || 'off',
|
|
72
|
+
child_lock: data.childLock || false,
|
|
73
|
+
air_quality: data.airQuality || 'unknown',
|
|
74
|
+
air_quality_value: data.air_quality_value || 0,
|
|
74
75
|
active_time: data.activeTime || 0
|
|
75
76
|
};
|
|
76
77
|
|
|
@@ -64,12 +64,18 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
64
64
|
this.details = {
|
|
65
65
|
mode: result.mode || '',
|
|
66
66
|
speed: result.level || 0,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
filter_life: result.filter_life || 0,
|
|
68
|
+
screen_status: result.display ? 'on' : 'off',
|
|
69
|
+
child_lock: result.child_lock || false,
|
|
70
|
+
air_quality: result.air_quality || 0,
|
|
71
|
+
air_quality_value: result.air_quality_value || 0
|
|
71
72
|
};
|
|
72
73
|
|
|
74
|
+
// Capture additional air quality properties if available
|
|
75
|
+
if ('PM1' in result) this.details.pm1 = result.PM1;
|
|
76
|
+
if ('PM10' in result) this.details.pm10 = result.PM10;
|
|
77
|
+
if ('AQPercent' in result) this.details.aq_percent = result.AQPercent;
|
|
78
|
+
|
|
73
79
|
// Don't overwrite config as it contains features array
|
|
74
80
|
if (result.configuration) {
|
|
75
81
|
this.details.configuration = result.configuration;
|
package/src/lib/helpers.ts
CHANGED
|
@@ -7,157 +7,9 @@ import crypto from 'crypto';
|
|
|
7
7
|
import { VeSync } from './vesync';
|
|
8
8
|
import { logger } from './logger';
|
|
9
9
|
|
|
10
|
-
// API configuration
|
|
10
|
+
// API configuration - Always use US endpoint
|
|
11
11
|
let _apiBaseUrl = 'https://smartapi.vesync.com';
|
|
12
12
|
|
|
13
|
-
// Regional API endpoints
|
|
14
|
-
export const REGIONAL_ENDPOINTS = {
|
|
15
|
-
US: 'https://smartapi.vesync.com',
|
|
16
|
-
EU: 'https://smartapi.vesync.eu',
|
|
17
|
-
ASIA: 'https://smartapi.vesync.com.cn'
|
|
18
|
-
} as const;
|
|
19
|
-
|
|
20
|
-
// Timezone to region mapping for API endpoint detection
|
|
21
|
-
const TIMEZONE_REGION_MAP: Record<string, keyof typeof REGIONAL_ENDPOINTS> = {
|
|
22
|
-
// European timezones - Western Europe
|
|
23
|
-
'Europe/Berlin': 'EU',
|
|
24
|
-
'Europe/London': 'EU',
|
|
25
|
-
'Europe/Paris': 'EU',
|
|
26
|
-
'Europe/Rome': 'EU',
|
|
27
|
-
'Europe/Madrid': 'EU',
|
|
28
|
-
'Europe/Amsterdam': 'EU',
|
|
29
|
-
'Europe/Vienna': 'EU',
|
|
30
|
-
'Europe/Brussels': 'EU',
|
|
31
|
-
'Europe/Copenhagen': 'EU',
|
|
32
|
-
'Europe/Dublin': 'EU',
|
|
33
|
-
'Europe/Helsinki': 'EU',
|
|
34
|
-
'Europe/Lisbon': 'EU',
|
|
35
|
-
'Europe/Luxembourg': 'EU',
|
|
36
|
-
'Europe/Oslo': 'EU',
|
|
37
|
-
'Europe/Prague': 'EU',
|
|
38
|
-
'Europe/Stockholm': 'EU',
|
|
39
|
-
'Europe/Warsaw': 'EU',
|
|
40
|
-
'Europe/Zurich': 'EU',
|
|
41
|
-
|
|
42
|
-
// European timezones - Eastern Europe
|
|
43
|
-
'Europe/Athens': 'EU',
|
|
44
|
-
'Europe/Budapest': 'EU',
|
|
45
|
-
'Europe/Bucharest': 'EU',
|
|
46
|
-
'Europe/Sofia': 'EU',
|
|
47
|
-
'Europe/Kiev': 'EU',
|
|
48
|
-
'Europe/Riga': 'EU',
|
|
49
|
-
'Europe/Vilnius': 'EU',
|
|
50
|
-
'Europe/Tallinn': 'EU',
|
|
51
|
-
'Europe/Zagreb': 'EU',
|
|
52
|
-
'Europe/Ljubljana': 'EU',
|
|
53
|
-
'Europe/Bratislava': 'EU',
|
|
54
|
-
'Europe/Belgrade': 'EU',
|
|
55
|
-
'Europe/Sarajevo': 'EU',
|
|
56
|
-
'Europe/Podgorica': 'EU',
|
|
57
|
-
'Europe/Skopje': 'EU',
|
|
58
|
-
'Europe/Tirane': 'EU',
|
|
59
|
-
|
|
60
|
-
// European timezones - Nordic/Baltic
|
|
61
|
-
'Europe/Reykjavik': 'EU',
|
|
62
|
-
'Europe/Moscow': 'EU',
|
|
63
|
-
'Europe/Kaliningrad': 'EU',
|
|
64
|
-
'Europe/Minsk': 'EU',
|
|
65
|
-
|
|
66
|
-
// European timezones - Other
|
|
67
|
-
'Europe/Malta': 'EU',
|
|
68
|
-
'Europe/Monaco': 'EU',
|
|
69
|
-
'Europe/Vatican': 'EU',
|
|
70
|
-
'Europe/San_Marino': 'EU',
|
|
71
|
-
'Europe/Andorra': 'EU',
|
|
72
|
-
'Europe/Gibraltar': 'EU',
|
|
73
|
-
'Europe/Isle_of_Man': 'EU',
|
|
74
|
-
'Europe/Jersey': 'EU',
|
|
75
|
-
'Europe/Guernsey': 'EU',
|
|
76
|
-
|
|
77
|
-
// Asian timezones - East Asia
|
|
78
|
-
'Asia/Shanghai': 'ASIA',
|
|
79
|
-
'Asia/Beijing': 'ASIA',
|
|
80
|
-
'Asia/Hong_Kong': 'ASIA',
|
|
81
|
-
'Asia/Tokyo': 'ASIA',
|
|
82
|
-
'Asia/Seoul': 'ASIA',
|
|
83
|
-
'Asia/Singapore': 'ASIA',
|
|
84
|
-
'Asia/Taipei': 'ASIA',
|
|
85
|
-
'Asia/Macau': 'ASIA',
|
|
86
|
-
|
|
87
|
-
// Asian timezones - Southeast Asia
|
|
88
|
-
'Asia/Bangkok': 'ASIA',
|
|
89
|
-
'Asia/Jakarta': 'ASIA',
|
|
90
|
-
'Asia/Manila': 'ASIA',
|
|
91
|
-
'Asia/Ho_Chi_Minh': 'ASIA',
|
|
92
|
-
'Asia/Kuala_Lumpur': 'ASIA',
|
|
93
|
-
'Asia/Phnom_Penh': 'ASIA',
|
|
94
|
-
'Asia/Vientiane': 'ASIA',
|
|
95
|
-
'Asia/Yangon': 'ASIA',
|
|
96
|
-
'Asia/Brunei': 'ASIA',
|
|
97
|
-
|
|
98
|
-
// Asian timezones - South Asia
|
|
99
|
-
'Asia/Mumbai': 'ASIA',
|
|
100
|
-
'Asia/Delhi': 'ASIA',
|
|
101
|
-
'Asia/Kolkata': 'ASIA',
|
|
102
|
-
'Asia/Dhaka': 'ASIA',
|
|
103
|
-
'Asia/Karachi': 'ASIA',
|
|
104
|
-
'Asia/Colombo': 'ASIA',
|
|
105
|
-
'Asia/Kathmandu': 'ASIA',
|
|
106
|
-
'Asia/Thimphu': 'ASIA',
|
|
107
|
-
|
|
108
|
-
// Asian timezones - Central Asia
|
|
109
|
-
'Asia/Almaty': 'ASIA',
|
|
110
|
-
'Asia/Bishkek': 'ASIA',
|
|
111
|
-
'Asia/Dushanbe': 'ASIA',
|
|
112
|
-
'Asia/Tashkent': 'ASIA',
|
|
113
|
-
'Asia/Ashgabat': 'ASIA',
|
|
114
|
-
|
|
115
|
-
// Asian timezones - West Asia / Middle East
|
|
116
|
-
'Asia/Dubai': 'ASIA',
|
|
117
|
-
'Asia/Riyadh': 'ASIA',
|
|
118
|
-
'Asia/Tehran': 'ASIA',
|
|
119
|
-
'Asia/Baghdad': 'ASIA',
|
|
120
|
-
'Asia/Kuwait': 'ASIA',
|
|
121
|
-
'Asia/Qatar': 'ASIA',
|
|
122
|
-
'Asia/Bahrain': 'ASIA',
|
|
123
|
-
'Asia/Muscat': 'ASIA',
|
|
124
|
-
'Asia/Baku': 'ASIA',
|
|
125
|
-
'Asia/Yerevan': 'ASIA',
|
|
126
|
-
'Asia/Tbilisi': 'ASIA',
|
|
127
|
-
|
|
128
|
-
// Oceania (using ASIA endpoint for now)
|
|
129
|
-
'Australia/Sydney': 'ASIA',
|
|
130
|
-
'Australia/Melbourne': 'ASIA',
|
|
131
|
-
'Australia/Brisbane': 'ASIA',
|
|
132
|
-
'Australia/Perth': 'ASIA',
|
|
133
|
-
'Australia/Adelaide': 'ASIA',
|
|
134
|
-
'Australia/Hobart': 'ASIA',
|
|
135
|
-
'Australia/Darwin': 'ASIA',
|
|
136
|
-
'Australia/Canberra': 'ASIA',
|
|
137
|
-
'Pacific/Auckland': 'ASIA',
|
|
138
|
-
'Pacific/Wellington': 'ASIA',
|
|
139
|
-
'Pacific/Fiji': 'ASIA',
|
|
140
|
-
'Pacific/Honolulu': 'US',
|
|
141
|
-
|
|
142
|
-
// North America (Canada)
|
|
143
|
-
'America/Toronto': 'US',
|
|
144
|
-
'America/Montreal': 'US',
|
|
145
|
-
'America/Vancouver': 'US',
|
|
146
|
-
'America/Calgary': 'US',
|
|
147
|
-
'America/Edmonton': 'US',
|
|
148
|
-
'America/Winnipeg': 'US',
|
|
149
|
-
'America/Halifax': 'US',
|
|
150
|
-
'America/St_Johns': 'US',
|
|
151
|
-
|
|
152
|
-
// North America (Mexico)
|
|
153
|
-
'America/Mexico_City': 'US',
|
|
154
|
-
'America/Tijuana': 'US',
|
|
155
|
-
'America/Cancun': 'US',
|
|
156
|
-
'America/Merida': 'US',
|
|
157
|
-
'America/Monterrey': 'US'
|
|
158
|
-
// US and other timezones default to US endpoint
|
|
159
|
-
};
|
|
160
|
-
|
|
161
13
|
export function getApiBaseUrl(): string {
|
|
162
14
|
return _apiBaseUrl;
|
|
163
15
|
}
|
|
@@ -166,24 +18,6 @@ export function setApiBaseUrl(url: string): void {
|
|
|
166
18
|
_apiBaseUrl = url;
|
|
167
19
|
}
|
|
168
20
|
|
|
169
|
-
/**
|
|
170
|
-
* Get regional API endpoint based on timezone
|
|
171
|
-
*/
|
|
172
|
-
export function getRegionalEndpoint(timeZone: string): string {
|
|
173
|
-
const region = TIMEZONE_REGION_MAP[timeZone] || 'US';
|
|
174
|
-
return REGIONAL_ENDPOINTS[region];
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Set API endpoint based on timezone if not explicitly set
|
|
179
|
-
*/
|
|
180
|
-
export function setRegionalEndpoint(timeZone: string): void {
|
|
181
|
-
const regionalUrl = getRegionalEndpoint(timeZone);
|
|
182
|
-
if (_apiBaseUrl === REGIONAL_ENDPOINTS.US && regionalUrl !== REGIONAL_ENDPOINTS.US) {
|
|
183
|
-
logger.debug(`Setting regional API endpoint for ${timeZone}: ${regionalUrl}`);
|
|
184
|
-
setApiBaseUrl(regionalUrl);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
21
|
|
|
188
22
|
export const API_RATE_LIMIT = 30;
|
|
189
23
|
export const API_TIMEOUT = 15000;
|
|
@@ -375,8 +209,8 @@ export class Helpers {
|
|
|
375
209
|
try {
|
|
376
210
|
// Ensure API base URL is properly set
|
|
377
211
|
if (!_apiBaseUrl || _apiBaseUrl === 'undefined') {
|
|
378
|
-
logger.error('API base URL is not properly configured. Setting
|
|
379
|
-
|
|
212
|
+
logger.error('API base URL is not properly configured. Setting to default US endpoint...');
|
|
213
|
+
setApiBaseUrl('https://smartapi.vesync.com');
|
|
380
214
|
}
|
|
381
215
|
|
|
382
216
|
const url = _apiBaseUrl + endpoint;
|
package/src/lib/vesync.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* VeSync API Device Library
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { Helpers, API_RATE_LIMIT, DEFAULT_TZ, setApiBaseUrl,
|
|
5
|
+
import { Helpers, API_RATE_LIMIT, DEFAULT_TZ, setApiBaseUrl, getApiBaseUrl } from './helpers';
|
|
6
6
|
import { VeSyncBaseDevice } from './vesyncBaseDevice';
|
|
7
7
|
import { fanModules } from './vesyncFanImpl';
|
|
8
8
|
import { outletModules } from './vesyncOutletImpl';
|
|
@@ -180,7 +180,7 @@ export class VeSync {
|
|
|
180
180
|
* Initialize VeSync Manager
|
|
181
181
|
* @param username - VeSync account username
|
|
182
182
|
* @param password - VeSync account password
|
|
183
|
-
* @param timeZone - Optional timezone (defaults to America/New_York)
|
|
183
|
+
* @param timeZone - Optional timezone for device operations (defaults to America/New_York)
|
|
184
184
|
* @param debug - Optional debug mode flag
|
|
185
185
|
* @param redact - Optional redact mode flag
|
|
186
186
|
* @param apiUrl - Optional API base URL override
|
|
@@ -241,12 +241,12 @@ export class VeSync {
|
|
|
241
241
|
logger.debug('Time zone is not a string');
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
// Set custom API URL if provided, otherwise use
|
|
244
|
+
// Set custom API URL if provided, otherwise use default US endpoint
|
|
245
245
|
if (apiUrl) {
|
|
246
246
|
setApiBaseUrl(apiUrl);
|
|
247
247
|
} else {
|
|
248
|
-
//
|
|
249
|
-
|
|
248
|
+
// Always use US endpoint
|
|
249
|
+
setApiBaseUrl('https://smartapi.vesync.com');
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
// Set custom logger if provided
|
|
@@ -535,7 +535,7 @@ export class VeSync {
|
|
|
535
535
|
switch (response.code) {
|
|
536
536
|
case -11012022:
|
|
537
537
|
logger.error('App version too low error. Current version:', body.appVersion);
|
|
538
|
-
logger.error('This typically indicates
|
|
538
|
+
logger.error('This typically indicates an API version compatibility issue.');
|
|
539
539
|
break;
|
|
540
540
|
case -11003:
|
|
541
541
|
logger.error('Authentication failed - check credentials');
|
package/src/lib/vesyncFan.ts
CHANGED
|
@@ -322,10 +322,14 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
322
322
|
protected details: {
|
|
323
323
|
mode?: string;
|
|
324
324
|
speed?: number;
|
|
325
|
-
filter_life?: number;
|
|
325
|
+
filter_life?: number | { percent: number };
|
|
326
326
|
screen_status?: 'on' | 'off';
|
|
327
327
|
child_lock?: boolean;
|
|
328
|
-
air_quality?: string;
|
|
328
|
+
air_quality?: string | number;
|
|
329
|
+
air_quality_value?: number;
|
|
330
|
+
pm1?: number;
|
|
331
|
+
pm10?: number;
|
|
332
|
+
aq_percent?: number;
|
|
329
333
|
humidity?: number;
|
|
330
334
|
mist_level?: number;
|
|
331
335
|
warm_level?: number;
|
|
@@ -417,7 +421,16 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
417
421
|
* Get filter life percentage
|
|
418
422
|
*/
|
|
419
423
|
get filterLife(): number {
|
|
420
|
-
|
|
424
|
+
// Handle different filter_life formats from different devices
|
|
425
|
+
const filterLife = this.details.filter_life;
|
|
426
|
+
|
|
427
|
+
// For Air131 devices, filter_life is an object with percent property
|
|
428
|
+
if (typeof filterLife === 'object' && filterLife !== null && 'percent' in filterLife) {
|
|
429
|
+
return filterLife.percent ?? 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// For other devices, filter_life is a direct number
|
|
433
|
+
return typeof filterLife === 'number' ? filterLife : 0;
|
|
421
434
|
}
|
|
422
435
|
|
|
423
436
|
/**
|
|
@@ -435,12 +448,47 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
435
448
|
}
|
|
436
449
|
|
|
437
450
|
/**
|
|
438
|
-
* Get air quality (if supported)
|
|
451
|
+
* Get air quality level (if supported)
|
|
439
452
|
*/
|
|
440
|
-
get
|
|
453
|
+
get airQualityLevel(): string | number {
|
|
441
454
|
return this.details.air_quality ?? '';
|
|
442
455
|
}
|
|
443
456
|
|
|
457
|
+
/**
|
|
458
|
+
* Get air quality (alias for airQualityLevel for backward compatibility)
|
|
459
|
+
*/
|
|
460
|
+
get airQuality(): string | number {
|
|
461
|
+
return this.airQualityLevel;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Get air quality value - PM2.5 in μg/m³ (if supported)
|
|
466
|
+
*/
|
|
467
|
+
get airQualityValue(): number {
|
|
468
|
+
return this.details.air_quality_value ?? 0;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Get PM1 value (if supported)
|
|
473
|
+
*/
|
|
474
|
+
get pm1(): number {
|
|
475
|
+
return this.details.pm1 ?? 0;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Get PM10 value (if supported)
|
|
480
|
+
*/
|
|
481
|
+
get pm10(): number {
|
|
482
|
+
return this.details.pm10 ?? 0;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Get air quality percentage (if supported)
|
|
487
|
+
*/
|
|
488
|
+
get aqPercent(): number {
|
|
489
|
+
return this.details.aq_percent ?? 0;
|
|
490
|
+
}
|
|
491
|
+
|
|
444
492
|
/**
|
|
445
493
|
* Get humidity level (if supported)
|
|
446
494
|
*/
|
|
@@ -491,7 +539,19 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
491
539
|
info.push(['Child Lock: ', this.childLock ? 'Enabled' : 'Disabled']);
|
|
492
540
|
}
|
|
493
541
|
if (this.hasFeature('air_quality')) {
|
|
494
|
-
info.push(['Air Quality: ', this.
|
|
542
|
+
info.push(['Air Quality Level: ', this.airQualityLevel]);
|
|
543
|
+
if (this.airQualityValue > 0) {
|
|
544
|
+
info.push(['Air Quality Value: ', this.airQualityValue, ' μg/m³']);
|
|
545
|
+
}
|
|
546
|
+
if (this.pm1 > 0) {
|
|
547
|
+
info.push(['PM1: ', this.pm1, ' μg/m³']);
|
|
548
|
+
}
|
|
549
|
+
if (this.pm10 > 0) {
|
|
550
|
+
info.push(['PM10: ', this.pm10, ' μg/m³']);
|
|
551
|
+
}
|
|
552
|
+
if (this.aqPercent > 0) {
|
|
553
|
+
info.push(['AQ Percent: ', this.aqPercent, '%']);
|
|
554
|
+
}
|
|
495
555
|
}
|
|
496
556
|
if (this.hasFeature('humidity')) {
|
|
497
557
|
info.push(['Humidity: ', this.humidity, '%']);
|
|
@@ -534,7 +594,19 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
534
594
|
details['Child Lock'] = this.childLock ? 'Enabled' : 'Disabled';
|
|
535
595
|
}
|
|
536
596
|
if (this.hasFeature('air_quality')) {
|
|
537
|
-
details['Air Quality'] = this.
|
|
597
|
+
details['Air Quality Level'] = this.airQualityLevel.toString();
|
|
598
|
+
if (this.airQualityValue > 0) {
|
|
599
|
+
details['Air Quality Value'] = this.airQualityValue.toString();
|
|
600
|
+
}
|
|
601
|
+
if (this.pm1 > 0) {
|
|
602
|
+
details['PM1'] = this.pm1.toString();
|
|
603
|
+
}
|
|
604
|
+
if (this.pm10 > 0) {
|
|
605
|
+
details['PM10'] = this.pm10.toString();
|
|
606
|
+
}
|
|
607
|
+
if (this.aqPercent > 0) {
|
|
608
|
+
details['AQ Percent'] = this.aqPercent.toString();
|
|
609
|
+
}
|
|
538
610
|
}
|
|
539
611
|
if (this.hasFeature('humidity')) {
|
|
540
612
|
details['Humidity'] = this.humidity.toString();
|