tsvesync 1.3.13 → 1.4.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/dist/lib/fans/airBaseV2.d.ts +8 -0
- package/dist/lib/fans/airBaseV2.js +71 -8
- package/dist/lib/fans/airBypass.d.ts +1 -1
- package/dist/lib/fans/airBypass.js +37 -10
- package/dist/lib/vesyncFan.d.ts +30 -10
- package/dist/lib/vesyncFan.js +209 -64
- package/package.json +2 -1
- package/src/lib/fans/airBaseV2.ts +74 -8
- package/src/lib/fans/airBypass.ts +41 -10
- package/src/lib/vesyncFan.ts +231 -74
|
@@ -57,6 +57,14 @@ export declare class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
57
57
|
* Override setMode to send workMode payloads for bypassV2 purifiers.
|
|
58
58
|
*/
|
|
59
59
|
setMode(mode: string): Promise<boolean>;
|
|
60
|
+
/**
|
|
61
|
+
* Convenience helper to enter turbo mode when supported.
|
|
62
|
+
*/
|
|
63
|
+
turboMode(): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* Convenience helper to enter pet mode when supported.
|
|
66
|
+
*/
|
|
67
|
+
petMode(): Promise<boolean>;
|
|
60
68
|
/**
|
|
61
69
|
* Override changeFanSpeed to use manualSpeedLevel for bypassV2 purifiers.
|
|
62
70
|
*/
|
|
@@ -13,11 +13,11 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
|
|
|
13
13
|
this._lightDetection = false;
|
|
14
14
|
this._lightDetectionState = false;
|
|
15
15
|
this.setSpeedLevel = null;
|
|
16
|
-
this.autoPreferences = ['default', 'efficient', 'quiet'];
|
|
17
16
|
this.enabled = false;
|
|
18
17
|
this._mode = '';
|
|
19
18
|
this._speed = 0;
|
|
20
19
|
this._timer = null;
|
|
20
|
+
this.autoPreferences = [...this.getConfigAutoPreferences(['default', 'efficient', 'quiet'])];
|
|
21
21
|
logger_1.logger.debug(`Initialized VeSyncAirBaseV2 device: ${this.deviceName}`);
|
|
22
22
|
}
|
|
23
23
|
buildConfigDict(configDict) {
|
|
@@ -265,28 +265,63 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
|
|
|
265
265
|
* Override setMode to send workMode payloads for bypassV2 purifiers.
|
|
266
266
|
*/
|
|
267
267
|
async setMode(mode) {
|
|
268
|
-
|
|
268
|
+
var _a, _b;
|
|
269
|
+
const targetMode = mode.toLowerCase();
|
|
270
|
+
const supportedModes = this.modes.map(m => m.toLowerCase());
|
|
271
|
+
if (!supportedModes.includes(targetMode)) {
|
|
269
272
|
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
270
273
|
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
271
274
|
throw new Error(error);
|
|
272
275
|
}
|
|
273
276
|
// Special handling for Core200S stays the same for all models
|
|
274
|
-
if (this.deviceType.includes('Core200S') &&
|
|
277
|
+
if (this.deviceType.includes('Core200S') && targetMode === 'auto') {
|
|
275
278
|
logger_1.logger.warn(`Auto mode not supported for ${this.deviceType}, using manual mode instead`);
|
|
276
279
|
return this.setMode('manual');
|
|
277
280
|
}
|
|
281
|
+
// Manual mode should leverage changeFanSpeed to mirror pyvesync behaviour
|
|
282
|
+
if (targetMode === 'manual') {
|
|
283
|
+
const defaultLevel = this.config.levels && this.config.levels.length > 0
|
|
284
|
+
? this.config.levels[0]
|
|
285
|
+
: 1;
|
|
286
|
+
const desiredLevel = (_b = (_a = this.details.manualSpeedLevel) !== null && _a !== void 0 ? _a : this.speed) !== null && _b !== void 0 ? _b : defaultLevel;
|
|
287
|
+
const success = await this.changeFanSpeed(desiredLevel);
|
|
288
|
+
if (success) {
|
|
289
|
+
this.details.mode = 'manual';
|
|
290
|
+
this.mode = 'manual';
|
|
291
|
+
}
|
|
292
|
+
return success;
|
|
293
|
+
}
|
|
278
294
|
// BypassV2 purifiers need the workMode payload; other models use the base implementation
|
|
279
295
|
if (this.requiresPowerSwitchPayload()) {
|
|
280
296
|
logger_1.logger.debug(`Using bypassV2 mode payload for ${this.deviceName}`);
|
|
281
297
|
const [head, body] = this.buildApiDict('setPurifierMode');
|
|
282
298
|
body.payload.data = {
|
|
283
|
-
workMode:
|
|
299
|
+
workMode: targetMode
|
|
284
300
|
};
|
|
285
301
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
286
302
|
const success = this.checkV2Response(response, status, 'setMode');
|
|
287
303
|
if (success) {
|
|
288
|
-
this.details.mode =
|
|
289
|
-
this.
|
|
304
|
+
this.details.mode = targetMode;
|
|
305
|
+
this.mode = targetMode;
|
|
306
|
+
// Update cached speed snapshot for special modes
|
|
307
|
+
if (targetMode === 'sleep') {
|
|
308
|
+
this.speed = 0;
|
|
309
|
+
this.details.speed = 0;
|
|
310
|
+
}
|
|
311
|
+
else if (targetMode === 'turbo') {
|
|
312
|
+
const maxSpeed = this.config.levels && this.config.levels.length > 0
|
|
313
|
+
? Math.max(...this.config.levels)
|
|
314
|
+
: this.speed;
|
|
315
|
+
if (typeof maxSpeed === 'number' && maxSpeed > 0) {
|
|
316
|
+
this.speed = maxSpeed;
|
|
317
|
+
this.details.speed = maxSpeed;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else if (targetMode === 'auto') {
|
|
321
|
+
this.speed = 0;
|
|
322
|
+
this.details.speed = 0;
|
|
323
|
+
this.details.isAutoSentinel = true;
|
|
324
|
+
}
|
|
290
325
|
// Force a details update to sync state
|
|
291
326
|
setTimeout(() => {
|
|
292
327
|
this.getDetails().catch(e => {
|
|
@@ -296,7 +331,7 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
|
|
|
296
331
|
return true;
|
|
297
332
|
}
|
|
298
333
|
else {
|
|
299
|
-
logger_1.logger.error(`Failed to set mode to ${
|
|
334
|
+
logger_1.logger.error(`Failed to set mode to ${targetMode} for device: ${this.deviceName}`);
|
|
300
335
|
return false;
|
|
301
336
|
}
|
|
302
337
|
}
|
|
@@ -305,6 +340,26 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
|
|
|
305
340
|
return super.setMode(mode);
|
|
306
341
|
}
|
|
307
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* Convenience helper to enter turbo mode when supported.
|
|
345
|
+
*/
|
|
346
|
+
async turboMode() {
|
|
347
|
+
if (!this.modes.map(m => m.toLowerCase()).includes('turbo')) {
|
|
348
|
+
logger_1.logger.debug(`Turbo mode not supported for device: ${this.deviceName}`);
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
return this.setMode('turbo');
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Convenience helper to enter pet mode when supported.
|
|
355
|
+
*/
|
|
356
|
+
async petMode() {
|
|
357
|
+
if (!this.modes.map(m => m.toLowerCase()).includes('pet')) {
|
|
358
|
+
logger_1.logger.debug(`Pet mode not supported for device: ${this.deviceName}`);
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
return this.setMode('pet');
|
|
362
|
+
}
|
|
308
363
|
/**
|
|
309
364
|
* Override changeFanSpeed to use manualSpeedLevel for bypassV2 purifiers.
|
|
310
365
|
*/
|
|
@@ -331,6 +386,10 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
|
|
|
331
386
|
if (success) {
|
|
332
387
|
this.speed = speed;
|
|
333
388
|
this.details.manualSpeedLevel = speed;
|
|
389
|
+
this.details.speed = speed;
|
|
390
|
+
this.details.mode = 'manual';
|
|
391
|
+
this.mode = 'manual';
|
|
392
|
+
this.details.isAutoSentinel = false;
|
|
334
393
|
// Force a details update to sync state
|
|
335
394
|
setTimeout(() => {
|
|
336
395
|
this.getDetails().catch(e => {
|
|
@@ -357,6 +416,10 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
|
|
|
357
416
|
if (success) {
|
|
358
417
|
this.speed = speed;
|
|
359
418
|
this.details.manualSpeedLevel = speed;
|
|
419
|
+
this.details.speed = speed;
|
|
420
|
+
this.details.mode = 'manual';
|
|
421
|
+
this.mode = 'manual';
|
|
422
|
+
this.details.isAutoSentinel = false;
|
|
360
423
|
return true;
|
|
361
424
|
}
|
|
362
425
|
else {
|
|
@@ -370,7 +433,7 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
|
|
|
370
433
|
*/
|
|
371
434
|
async setAutoPreference(preference = 'default', roomSize = 600) {
|
|
372
435
|
if (!this.autoPreferences.includes(preference)) {
|
|
373
|
-
logger_1.logger.debug(`Invalid preference: ${preference} - valid preferences are
|
|
436
|
+
logger_1.logger.debug(`Invalid preference: ${preference} - valid preferences are ${this.autoPreferences.join(', ')}`);
|
|
374
437
|
return false;
|
|
375
438
|
}
|
|
376
439
|
const [head, body] = this.buildApiDict('setAutoPreference');
|
|
@@ -4,7 +4,7 @@ import { VeSync } from '../vesync';
|
|
|
4
4
|
* VeSync Air Purifier with Bypass
|
|
5
5
|
*/
|
|
6
6
|
export declare class VeSyncAirBypass extends VeSyncFan {
|
|
7
|
-
protected
|
|
7
|
+
protected get modes(): ReadonlyArray<string>;
|
|
8
8
|
protected readonly displayModes: readonly ["on", "off"];
|
|
9
9
|
protected readonly childLockModes: readonly ["on", "off"];
|
|
10
10
|
constructor(details: Record<string, any>, manager: VeSync);
|
|
@@ -8,9 +8,11 @@ const logger_1 = require("../logger");
|
|
|
8
8
|
* VeSync Air Purifier with Bypass
|
|
9
9
|
*/
|
|
10
10
|
class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
11
|
+
get modes() {
|
|
12
|
+
return this.getConfigModes(['auto', 'manual', 'sleep']);
|
|
13
|
+
}
|
|
11
14
|
constructor(details, manager) {
|
|
12
15
|
super(details, manager);
|
|
13
|
-
this.modes = ['auto', 'manual', 'sleep'];
|
|
14
16
|
this.displayModes = ['on', 'off'];
|
|
15
17
|
this.childLockModes = ['on', 'off'];
|
|
16
18
|
logger_1.logger.debug(`Initialized VeSyncAirBypass device: ${this.deviceName}`);
|
|
@@ -69,16 +71,30 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
69
71
|
logger_1.logger.debug(`${this.deviceName}: API response structure:`, JSON.stringify(result, null, 2));
|
|
70
72
|
}
|
|
71
73
|
}
|
|
74
|
+
// Parse air quality with fallback support for multiple API response formats
|
|
75
|
+
// Some devices (like Core 400S) may use PascalCase (AQLevel, PM25)
|
|
76
|
+
// while others use snake_case (air_quality, air_quality_value)
|
|
77
|
+
const airQualityLevel = result.air_quality || result.AQLevel || 0;
|
|
78
|
+
const airQualityValue = result.air_quality_value || result.PM25 || 0;
|
|
79
|
+
// Debug logging to diagnose air quality field availability
|
|
80
|
+
logger_1.logger.debug(`${this.deviceName}: Air quality API response fields:`, {
|
|
81
|
+
air_quality: result.air_quality,
|
|
82
|
+
AQLevel: result.AQLevel,
|
|
83
|
+
air_quality_value: result.air_quality_value,
|
|
84
|
+
PM25: result.PM25,
|
|
85
|
+
parsed_level: airQualityLevel,
|
|
86
|
+
parsed_value: airQualityValue
|
|
87
|
+
});
|
|
72
88
|
this.details = {
|
|
73
89
|
mode: result.mode || '',
|
|
74
90
|
speed: result.level || 0,
|
|
75
91
|
filter_life: filterLife,
|
|
76
92
|
screen_status: result.display ? 'on' : 'off',
|
|
77
93
|
child_lock: result.child_lock || false,
|
|
78
|
-
air_quality:
|
|
79
|
-
air_quality_value:
|
|
94
|
+
air_quality: airQualityLevel,
|
|
95
|
+
air_quality_value: airQualityValue
|
|
80
96
|
};
|
|
81
|
-
const normalizedAirQuality = helpers_1.Helpers.normalizeAirQuality(
|
|
97
|
+
const normalizedAirQuality = helpers_1.Helpers.normalizeAirQuality(airQualityLevel);
|
|
82
98
|
if (normalizedAirQuality.level >= 1) {
|
|
83
99
|
this.details.air_quality_level = normalizedAirQuality.level;
|
|
84
100
|
}
|
|
@@ -90,6 +106,14 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
90
106
|
this.details.pm10 = result.PM10;
|
|
91
107
|
if ('AQPercent' in result)
|
|
92
108
|
this.details.aq_percent = result.AQPercent;
|
|
109
|
+
// Debug log additional air quality fields for diagnostics
|
|
110
|
+
if (result.PM1 !== undefined || result.PM10 !== undefined || result.AQPercent !== undefined) {
|
|
111
|
+
logger_1.logger.debug(`${this.deviceName}: Additional air quality data:`, {
|
|
112
|
+
PM1: result.PM1,
|
|
113
|
+
PM10: result.PM10,
|
|
114
|
+
AQPercent: result.AQPercent
|
|
115
|
+
});
|
|
116
|
+
}
|
|
93
117
|
// Don't overwrite config as it contains features array
|
|
94
118
|
if (result.configuration) {
|
|
95
119
|
this.details.configuration = result.configuration;
|
|
@@ -183,14 +207,17 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
183
207
|
* Set device mode
|
|
184
208
|
*/
|
|
185
209
|
async setMode(mode) {
|
|
186
|
-
|
|
210
|
+
const requested = mode.toLowerCase();
|
|
211
|
+
const supported = this.modes.map(m => m.toLowerCase());
|
|
212
|
+
const allowsCore200AutoFallback = this.deviceType.includes('Core200S') && requested === 'auto';
|
|
213
|
+
if (!supported.includes(requested) && !allowsCore200AutoFallback) {
|
|
187
214
|
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
188
215
|
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
189
216
|
throw new Error(error);
|
|
190
217
|
}
|
|
191
|
-
logger_1.logger.debug(`Setting mode to ${
|
|
218
|
+
logger_1.logger.debug(`Setting mode to ${requested} for device: ${this.deviceName}`);
|
|
192
219
|
// Special handling for Core200S auto mode
|
|
193
|
-
if (
|
|
220
|
+
if (allowsCore200AutoFallback) {
|
|
194
221
|
// Core200S doesn't support auto mode, so we'll set it to manual mode
|
|
195
222
|
logger_1.logger.warn(`Auto mode not supported for ${this.deviceType}, using manual mode instead`);
|
|
196
223
|
return this.setMode('manual');
|
|
@@ -202,7 +229,7 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
202
229
|
configModule: this.configModule,
|
|
203
230
|
payload: {
|
|
204
231
|
data: {
|
|
205
|
-
mode
|
|
232
|
+
mode: requested
|
|
206
233
|
},
|
|
207
234
|
method: 'setPurifierMode',
|
|
208
235
|
source: 'APP'
|
|
@@ -210,11 +237,11 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
210
237
|
}, helpers_1.Helpers.reqHeaderBypass());
|
|
211
238
|
const success = this.checkResponse([response, status], 'setMode');
|
|
212
239
|
if (success) {
|
|
213
|
-
this.details.mode =
|
|
240
|
+
this.details.mode = requested;
|
|
214
241
|
return true;
|
|
215
242
|
}
|
|
216
243
|
else {
|
|
217
|
-
logger_1.logger.error(`Failed to set mode to ${
|
|
244
|
+
logger_1.logger.error(`Failed to set mode to ${requested} for device: ${this.deviceName}`);
|
|
218
245
|
return false;
|
|
219
246
|
}
|
|
220
247
|
}
|
package/dist/lib/vesyncFan.d.ts
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { VeSyncBaseDevice } from './vesyncBaseDevice';
|
|
5
5
|
import { VeSync } from './vesync';
|
|
6
|
+
interface FanConfigEntry {
|
|
7
|
+
module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S' | 'VeSyncAir131';
|
|
8
|
+
features: string[];
|
|
9
|
+
levels?: number[];
|
|
10
|
+
modes?: ReadonlyArray<string>;
|
|
11
|
+
autoPreferences?: ReadonlyArray<string>;
|
|
12
|
+
}
|
|
6
13
|
interface FanConfig {
|
|
7
|
-
[key: string]:
|
|
8
|
-
module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S' | 'VeSyncAir131';
|
|
9
|
-
features: string[];
|
|
10
|
-
levels?: number[];
|
|
11
|
-
};
|
|
14
|
+
[key: string]: FanConfigEntry;
|
|
12
15
|
}
|
|
13
16
|
export declare const fanConfig: FanConfig;
|
|
14
17
|
/**
|
|
@@ -42,16 +45,33 @@ export declare abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
42
45
|
duration: number;
|
|
43
46
|
action: string;
|
|
44
47
|
} | null;
|
|
45
|
-
protected config:
|
|
46
|
-
module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S' | 'VeSyncAir131';
|
|
47
|
-
features: string[];
|
|
48
|
-
levels?: number[];
|
|
49
|
-
};
|
|
48
|
+
protected config: FanConfigEntry;
|
|
50
49
|
constructor(details: Record<string, any>, manager: VeSync);
|
|
50
|
+
/**
|
|
51
|
+
* Find config entry by substring matching for variant device types
|
|
52
|
+
* Maps LAP variants to their base model configs (e.g., LAP-C401S-WUSR → Core400S)
|
|
53
|
+
*/
|
|
54
|
+
private findConfigBySubstring;
|
|
51
55
|
/**
|
|
52
56
|
* Check if feature is supported
|
|
53
57
|
*/
|
|
54
58
|
hasFeature(feature: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Get configured modes for the device, falling back to provided defaults.
|
|
61
|
+
*/
|
|
62
|
+
protected getConfigModes(defaultModes?: ReadonlyArray<string>): ReadonlyArray<string>;
|
|
63
|
+
/**
|
|
64
|
+
* Get configured auto preferences for the device, falling back to provided defaults.
|
|
65
|
+
*/
|
|
66
|
+
protected getConfigAutoPreferences(defaultPreferences?: ReadonlyArray<string>): ReadonlyArray<string>;
|
|
67
|
+
/**
|
|
68
|
+
* Public accessor for supported modes for consumers that need to inspect capability metadata.
|
|
69
|
+
*/
|
|
70
|
+
getSupportedModes(defaultModes?: ReadonlyArray<string>): ReadonlyArray<string>;
|
|
71
|
+
/**
|
|
72
|
+
* Public accessor for supported auto preference profiles.
|
|
73
|
+
*/
|
|
74
|
+
getSupportedAutoPreferences(defaultPreferences?: ReadonlyArray<string>): ReadonlyArray<string>;
|
|
55
75
|
/**
|
|
56
76
|
* Check if feature is supported in current mode
|
|
57
77
|
*/
|