tsvesync 1.0.48 → 1.0.50

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.
@@ -7,6 +7,7 @@ interface FanConfig {
7
7
  [key: string]: {
8
8
  module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2';
9
9
  features: string[];
10
+ levels?: number[];
10
11
  };
11
12
  }
12
13
  export declare const fanConfig: FanConfig;
@@ -14,14 +15,30 @@ export declare const fanConfig: FanConfig;
14
15
  * Base class for VeSync Fans
15
16
  */
16
17
  export declare abstract class VeSyncFan extends VeSyncBaseDevice {
17
- protected details: Record<string, any>;
18
+ protected details: {
19
+ mode?: string;
20
+ speed?: number;
21
+ filter_life?: number;
22
+ screen_status?: string;
23
+ child_lock?: boolean;
24
+ air_quality?: string;
25
+ humidity?: number;
26
+ mist_level?: number;
27
+ warm_level?: number;
28
+ warm_enabled?: boolean;
29
+ [key: string]: any;
30
+ };
18
31
  protected mode_dict: Record<string, any>;
19
32
  protected speed_dict: Record<string, any>;
20
- protected features: string[];
21
33
  protected _timer: number | {
22
34
  duration: number;
23
35
  action: string;
24
36
  } | null;
37
+ protected config: {
38
+ module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2';
39
+ features: string[];
40
+ levels?: number[];
41
+ };
25
42
  constructor(details: Record<string, any>, manager: VeSync);
26
43
  /**
27
44
  * Check if feature is supported
@@ -46,7 +63,7 @@ export declare abstract class VeSyncFan extends VeSyncBaseDevice {
46
63
  /**
47
64
  * Get child lock status
48
65
  */
49
- get childLock(): string;
66
+ get childLock(): boolean;
50
67
  /**
51
68
  * Get air quality (if supported)
52
69
  */
@@ -11,145 +11,180 @@ exports.fanConfig = {
11
11
  // Core Series
12
12
  'Core200S': {
13
13
  module: 'VeSyncAirBypass',
14
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
14
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
15
+ levels: [1, 2, 3]
15
16
  },
16
17
  'Core300S': {
17
18
  module: 'VeSyncAirBypass',
18
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
19
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
20
+ levels: [1, 2, 3, 4]
19
21
  },
20
22
  'Core400S': {
21
23
  module: 'VeSyncAirBypass',
22
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
24
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
25
+ levels: [1, 2, 3, 4]
23
26
  },
24
27
  'Core600S': {
25
28
  module: 'VeSyncAirBypass',
26
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
29
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
30
+ levels: [1, 2, 3, 4]
27
31
  },
28
32
  // LAP Series
29
33
  'LAP-C201S-AUSR': {
30
34
  module: 'VeSyncAirBypass',
31
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
35
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
36
+ levels: [1, 2, 3]
32
37
  },
33
38
  'LAP-C202S-WUSR': {
34
39
  module: 'VeSyncAirBypass',
35
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
40
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
41
+ levels: [1, 2, 3]
36
42
  },
37
43
  'LAP-C301S-WJP': {
38
44
  module: 'VeSyncAirBypass',
39
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
45
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
46
+ levels: [1, 2, 3, 4]
40
47
  },
41
48
  'LAP-C302S-WUSB': {
42
49
  module: 'VeSyncAirBypass',
43
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
50
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
51
+ levels: [1, 2, 3, 4]
44
52
  },
45
53
  'LAP-C301S-WAAA': {
46
54
  module: 'VeSyncAirBypass',
47
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
55
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
56
+ levels: [1, 2, 3, 4]
48
57
  },
49
58
  'LAP-C401S-WJP': {
50
59
  module: 'VeSyncAirBypass',
51
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
60
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
61
+ levels: [1, 2, 3, 4]
52
62
  },
53
63
  'LAP-C401S-WUSR': {
54
64
  module: 'VeSyncAirBypass',
55
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
65
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
66
+ levels: [1, 2, 3, 4]
56
67
  },
57
68
  'LAP-C401S-WAAA': {
58
69
  module: 'VeSyncAirBypass',
59
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
70
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
71
+ levels: [1, 2, 3, 4]
60
72
  },
61
73
  'LAP-C601S-WUS': {
62
74
  module: 'VeSyncAirBypass',
63
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
75
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
76
+ levels: [1, 2, 3, 4]
64
77
  },
65
78
  'LAP-C601S-WUSR': {
66
79
  module: 'VeSyncAirBypass',
67
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
80
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
81
+ levels: [1, 2, 3, 4]
68
82
  },
69
83
  'LAP-C601S-WEU': {
70
84
  module: 'VeSyncAirBypass',
71
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
85
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
86
+ levels: [1, 2, 3, 4]
72
87
  },
73
88
  'LAP-V102S-AASR': {
74
89
  module: 'VeSyncAirBaseV2',
75
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
90
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
91
+ levels: [1, 2, 3, 4]
76
92
  },
77
93
  'LAP-V102S-WUS': {
78
94
  module: 'VeSyncAirBaseV2',
79
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
95
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
96
+ levels: [1, 2, 3, 4]
80
97
  },
81
98
  'LAP-V102S-WEU': {
82
99
  module: 'VeSyncAirBaseV2',
83
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
100
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
101
+ levels: [1, 2, 3, 4]
84
102
  },
85
103
  'LAP-V102S-AUSR': {
86
104
  module: 'VeSyncAirBaseV2',
87
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
105
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
106
+ levels: [1, 2, 3, 4]
88
107
  },
89
108
  'LAP-V102S-WJP': {
90
109
  module: 'VeSyncAirBaseV2',
91
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
110
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
111
+ levels: [1, 2, 3, 4]
92
112
  },
93
113
  'LAP-V201S-AASR': {
94
114
  module: 'VeSyncAirBaseV2',
95
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
115
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
116
+ levels: [1, 2, 3, 4]
96
117
  },
97
118
  'LAP-V201S-WJP': {
98
119
  module: 'VeSyncAirBaseV2',
99
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
120
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
121
+ levels: [1, 2, 3, 4]
100
122
  },
101
123
  'LAP-V201S-WEU': {
102
124
  module: 'VeSyncAirBaseV2',
103
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
125
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
126
+ levels: [1, 2, 3, 4]
104
127
  },
105
128
  'LAP-V201S-WUS': {
106
129
  module: 'VeSyncAirBaseV2',
107
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
130
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
131
+ levels: [1, 2, 3, 4]
108
132
  },
109
133
  'LAP-V201-AUSR': {
110
134
  module: 'VeSyncAirBaseV2',
111
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
135
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
136
+ levels: [1, 2, 3, 4]
112
137
  },
113
138
  'LAP-V201S-AUSR': {
114
139
  module: 'VeSyncAirBaseV2',
115
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
140
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
141
+ levels: [1, 2, 3, 4]
116
142
  },
117
143
  'LAP-V201S-AEUR': {
118
144
  module: 'VeSyncAirBaseV2',
119
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
145
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
146
+ levels: [1, 2, 3, 4]
120
147
  },
121
148
  'LAP-EL551S-AUS': {
122
149
  module: 'VeSyncAirBaseV2',
123
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
150
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
151
+ levels: [1, 2, 3]
124
152
  },
125
153
  'LAP-EL551S-AEUR': {
126
154
  module: 'VeSyncAirBaseV2',
127
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
155
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
156
+ levels: [1, 2, 3]
128
157
  },
129
158
  'LAP-EL551S-WEU': {
130
159
  module: 'VeSyncAirBaseV2',
131
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
160
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
161
+ levels: [1, 2, 3]
132
162
  },
133
163
  'LAP-EL551S-WUS': {
134
164
  module: 'VeSyncAirBaseV2',
135
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
165
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
166
+ levels: [1, 2, 3]
136
167
  },
137
168
  // LTF Series
138
169
  'LTF-F422S-KEU': {
139
170
  module: 'VeSyncTowerFan',
140
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
171
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
172
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
141
173
  },
142
174
  'LTF-F422S-WUSR': {
143
175
  module: 'VeSyncTowerFan',
144
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
176
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
177
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
145
178
  },
146
179
  'LTF-F422_WJP': {
147
180
  module: 'VeSyncTowerFan',
148
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
181
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
182
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
149
183
  },
150
184
  'LTF-F422S-WUS': {
151
185
  module: 'VeSyncTowerFan',
152
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
186
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
187
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
153
188
  },
154
189
  // Classic Series
155
190
  'Classic300S': {
@@ -250,11 +285,13 @@ exports.fanConfig = {
250
285
  // LV Series
251
286
  'LV-PUR131S': {
252
287
  module: 'VeSyncAirBypass',
253
- features: ['air_quality', 'display', 'child_lock', 'night_light', 'timer']
288
+ features: ['air_quality', 'display', 'child_lock', 'night_light', 'timer'],
289
+ levels: [1, 2, 3]
254
290
  },
255
291
  'LV-RH131S': {
256
292
  module: 'VeSyncAirBypass',
257
- features: ['display', 'child_lock', 'night_light', 'timer']
293
+ features: ['display', 'child_lock', 'night_light', 'timer'],
294
+ levels: [1, 2, 3]
258
295
  }
259
296
  };
260
297
  /**
@@ -262,21 +299,23 @@ exports.fanConfig = {
262
299
  */
263
300
  class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
264
301
  constructor(details, manager) {
265
- var _a;
266
302
  super(details, manager);
267
303
  this.details = {};
268
304
  this.mode_dict = {};
269
305
  this.speed_dict = {};
270
- this.features = [];
271
306
  this._timer = null;
272
307
  this.details = details;
273
- this.features = ((_a = exports.fanConfig[this.deviceType]) === null || _a === void 0 ? void 0 : _a.features) || [];
308
+ this.config = exports.fanConfig[this.deviceType] || {
309
+ module: 'VeSyncAirBypass',
310
+ features: [],
311
+ levels: []
312
+ };
274
313
  }
275
314
  /**
276
315
  * Check if feature is supported
277
316
  */
278
317
  hasFeature(feature) {
279
- return this.features.includes(feature);
318
+ return this.config.features.includes(feature);
280
319
  }
281
320
  /**
282
321
  * Get current mode
@@ -311,7 +350,7 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
311
350
  */
312
351
  get childLock() {
313
352
  var _a;
314
- return (_a = this.details.child_lock) !== null && _a !== void 0 ? _a : '';
353
+ return (_a = this.details.child_lock) !== null && _a !== void 0 ? _a : false;
315
354
  }
316
355
  /**
317
356
  * Get air quality (if supported)
@@ -364,7 +403,7 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
364
403
  info.push(['Screen Status: ', this.screenStatus]);
365
404
  }
366
405
  if (this.hasFeature('child_lock')) {
367
- info.push(['Child Lock: ', this.childLock]);
406
+ info.push(['Child Lock: ', this.childLock ? 'Enabled' : 'Disabled']);
368
407
  }
369
408
  if (this.hasFeature('air_quality')) {
370
409
  info.push(['Air Quality: ', this.airQuality]);
@@ -405,7 +444,7 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
405
444
  details['Screen Status'] = this.screenStatus;
406
445
  }
407
446
  if (this.hasFeature('child_lock')) {
408
- details['Child Lock'] = this.childLock;
447
+ details['Child Lock'] = this.childLock ? 'Enabled' : 'Disabled';
409
448
  }
410
449
  if (this.hasFeature('air_quality')) {
411
450
  details['Air Quality'] = this.airQuality;
@@ -8,7 +8,6 @@ import { VeSync } from './vesync';
8
8
  */
9
9
  export declare class VeSyncAirBypass extends VeSyncFan {
10
10
  protected readonly modes: readonly ["auto", "manual", "sleep"];
11
- protected readonly speeds: number[];
12
11
  protected readonly displayModes: readonly ["on", "off"];
13
12
  protected readonly childLockModes: readonly ["on", "off"];
14
13
  constructor(details: Record<string, any>, manager: VeSync);
@@ -314,13 +313,16 @@ export declare class VeSyncAirBaseV2 extends VeSyncAirBypass {
314
313
  * Display JSON details
315
314
  */
316
315
  displayJSON(): string;
316
+ /**
317
+ * Set oscillation state
318
+ */
319
+ setOscillation(toggle: boolean): Promise<boolean>;
317
320
  }
318
321
  /**
319
322
  * VeSync Tower Fan Implementation
320
323
  */
321
324
  export declare class VeSyncTowerFan extends VeSyncAirBaseV2 {
322
325
  protected readonly towerModes: readonly ["normal", "advancedSleep", "off"];
323
- protected readonly speeds: number[];
324
326
  constructor(details: Record<string, any>, manager: VeSync);
325
327
  /**
326
328
  * Get timer status (in seconds)
@@ -421,10 +423,6 @@ export declare class VeSyncTowerFan extends VeSyncAirBaseV2 {
421
423
  * Get error code
422
424
  */
423
425
  get errorCode(): number;
424
- /**
425
- * Set oscillation state
426
- */
427
- setOscillation(toggle: boolean): Promise<boolean>;
428
426
  /**
429
427
  * Turn oscillation on
430
428
  */
@@ -14,7 +14,6 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
14
14
  constructor(details, manager) {
15
15
  super(details, manager);
16
16
  this.modes = ['auto', 'manual', 'sleep'];
17
- this.speeds = [1, 2, 3, 4, 5];
18
17
  this.displayModes = ['on', 'off'];
19
18
  this.childLockModes = ['on', 'off'];
20
19
  logger_1.logger.debug(`Initialized VeSyncAirBypass device: ${this.deviceName}`);
@@ -210,10 +209,11 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
210
209
  * Change fan speed
211
210
  */
212
211
  async changeFanSpeed(speed) {
213
- if (!this.speeds.includes(speed)) {
214
- const error = `Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`;
215
- logger_1.logger.error(`${error} for device: ${this.deviceName}`);
216
- throw new Error(error);
212
+ var _a;
213
+ const speeds = (_a = this.config.levels) !== null && _a !== void 0 ? _a : [];
214
+ if (!speeds.includes(speed)) {
215
+ logger_1.logger.error(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')} for device: ${this.deviceName}`);
216
+ return false;
217
217
  }
218
218
  logger_1.logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
219
219
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
@@ -896,7 +896,7 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
896
896
  // Add warm humidifier specific fields
897
897
  this.details = {
898
898
  ...this.details,
899
- warm_enabled: this.details.warm_level > 0,
899
+ warm_enabled: Boolean(this.details.warm_level && this.details.warm_level > 0),
900
900
  drying_mode_enabled: this.details.autoDryingSwitch === 1,
901
901
  drying_mode_state: this.details.dryingMode || '',
902
902
  drying_mode_level: this.details.dryingLevel || 0,
@@ -1062,7 +1062,10 @@ class VeSyncAirBaseV2 extends VeSyncAirBypass {
1062
1062
  }
1063
1063
  buildConfigDict(configDict) {
1064
1064
  if (configDict) {
1065
- this.config = configDict;
1065
+ this.config = {
1066
+ ...this.config, // Preserve existing config
1067
+ ...configDict // Merge in new config
1068
+ };
1066
1069
  }
1067
1070
  }
1068
1071
  get mode() {
@@ -1208,31 +1211,28 @@ class VeSyncAirBaseV2 extends VeSyncAirBypass {
1208
1211
  * Change fan speed
1209
1212
  */
1210
1213
  async changeFanSpeed(speed) {
1211
- var _a, _b, _c;
1212
- const speeds = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.levels) !== null && _b !== void 0 ? _b : [];
1213
- const currentSpeed = (_c = this.setSpeedLevel) !== null && _c !== void 0 ? _c : 0;
1214
- if (speed !== undefined) {
1215
- if (!speeds.includes(speed)) {
1216
- logger_1.logger.debug(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')}`);
1217
- return false;
1218
- }
1214
+ var _a;
1215
+ const speeds = (_a = this.config.levels) !== null && _a !== void 0 ? _a : [];
1216
+ if (!speeds.includes(speed)) {
1217
+ logger_1.logger.error(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')} for device: ${this.deviceName}`);
1218
+ return false;
1219
1219
  }
1220
- else {
1221
- if (currentSpeed === speeds[speeds.length - 1] || currentSpeed === 0) {
1222
- speed = speeds[0];
1223
- }
1224
- else {
1225
- const currentIndex = speeds.indexOf(currentSpeed);
1226
- speed = speeds[currentIndex + 1];
1220
+ logger_1.logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
1221
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
1222
+ ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
1223
+ cid: this.cid,
1224
+ configModule: this.configModule,
1225
+ payload: {
1226
+ data: {
1227
+ id: 0,
1228
+ level: speed,
1229
+ mode: 'manual',
1230
+ type: 'wind'
1231
+ },
1232
+ method: 'setLevel',
1233
+ source: 'APP'
1227
1234
  }
1228
- }
1229
- const [head, body] = this.buildApiDict('setLevel');
1230
- body.payload.data = {
1231
- levelIdx: 0,
1232
- manualSpeedLevel: speed,
1233
- levelType: 'wind'
1234
- };
1235
- const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
1235
+ }, helpers_1.Helpers.reqHeaderBypass());
1236
1236
  const success = this.checkResponse([response, status], 'changeFanSpeed');
1237
1237
  if (success) {
1238
1238
  this.setSpeedLevel = speed;
@@ -1419,22 +1419,23 @@ class VeSyncAirBaseV2 extends VeSyncAirBypass {
1419
1419
  * Display JSON details
1420
1420
  */
1421
1421
  displayJSON() {
1422
- var _a, _b;
1422
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1423
1423
  const baseInfo = JSON.parse(super.displayJSON());
1424
1424
  const details = {
1425
1425
  ...baseInfo,
1426
1426
  'Mode': this.mode,
1427
- 'Filter Life': this.details.filter_life.toString(),
1427
+ 'Filter Life': ((_a = this.details.filter_life) !== null && _a !== void 0 ? _a : 0).toString(),
1428
1428
  'Fan Level': this.speed.toString(),
1429
- 'Display On': this.details.display.toString(),
1430
- 'Child Lock': this.details.child_lock.toString(),
1431
- 'Display Set On': this.details.screen_switch.toString(),
1432
- 'Light Detection Enabled': this.details.light_detection_switch.toString(),
1433
- 'Environment Light State': this.details.environment_light_state.toString()
1429
+ 'Display On': (_c = (_b = this.details.display) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : 'false',
1430
+ 'Child Lock': ((_d = this.details.child_lock) !== null && _d !== void 0 ? _d : false).toString(),
1431
+ 'Night Light': ((_e = this.details.night_light) !== null && _e !== void 0 ? _e : '').toString(),
1432
+ 'Display Set On': ((_f = this.details.screen_switch) !== null && _f !== void 0 ? _f : false).toString(),
1433
+ 'Light Detection Enabled': ((_g = this.details.light_detection_switch) !== null && _g !== void 0 ? _g : false).toString(),
1434
+ 'Environment Light State': ((_h = this.details.environment_light_state) !== null && _h !== void 0 ? _h : false).toString()
1434
1435
  };
1435
1436
  if (this.hasFeature('air_quality')) {
1436
- details['Air Quality Level'] = ((_a = this.details.air_quality) !== null && _a !== void 0 ? _a : '').toString();
1437
- details['Air Quality Value'] = ((_b = this.details.air_quality_value) !== null && _b !== void 0 ? _b : '').toString();
1437
+ details['Air Quality Level'] = ((_j = this.details.air_quality) !== null && _j !== void 0 ? _j : '').toString();
1438
+ details['Air Quality Value'] = ((_k = this.details.air_quality_value) !== null && _k !== void 0 ? _k : '').toString();
1438
1439
  }
1439
1440
  const everestKeys = {
1440
1441
  'pm1': 'PM1',
@@ -1444,11 +1445,29 @@ class VeSyncAirBaseV2 extends VeSyncAirBypass {
1444
1445
  };
1445
1446
  for (const [key, value] of Object.entries(everestKeys)) {
1446
1447
  if (key in this.details) {
1447
- details[value] = this.details[key].toString();
1448
+ details[value] = (_m = (_l = this.details[key]) === null || _l === void 0 ? void 0 : _l.toString()) !== null && _m !== void 0 ? _m : '';
1448
1449
  }
1449
1450
  }
1450
1451
  return JSON.stringify(details, null, 4);
1451
1452
  }
1453
+ /**
1454
+ * Set oscillation state
1455
+ */
1456
+ async setOscillation(toggle) {
1457
+ logger_1.logger.debug(`Setting oscillation to ${toggle ? 'on' : 'off'} for device: ${this.deviceName}`);
1458
+ const [head, body] = this.buildApiDict('setSwitch');
1459
+ body.payload.data = {
1460
+ oscillationSwitch: toggle ? 1 : 0,
1461
+ switchIdx: 0
1462
+ };
1463
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
1464
+ const success = this.checkResponse([response, status], 'setOscillation');
1465
+ if (success) {
1466
+ this.details.oscillationState = toggle;
1467
+ this.details.oscillationSwitch = toggle ? 1 : 0;
1468
+ }
1469
+ return success;
1470
+ }
1452
1471
  }
1453
1472
  exports.VeSyncAirBaseV2 = VeSyncAirBaseV2;
1454
1473
  /**
@@ -1458,7 +1477,6 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1458
1477
  constructor(details, manager) {
1459
1478
  super(details, manager);
1460
1479
  this.towerModes = ['normal', 'advancedSleep', 'off'];
1461
- this.speeds = Array.from({ length: 12 }, (_, i) => i + 1);
1462
1480
  logger_1.logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
1463
1481
  }
1464
1482
  /**
@@ -1522,8 +1540,6 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1522
1540
  screenSwitch: deviceData.screenSwitch,
1523
1541
  oscillationState: deviceData.oscillationState,
1524
1542
  oscillationSwitch: deviceData.oscillationSwitch,
1525
- muteState: deviceData.muteState,
1526
- muteSwitch: deviceData.muteSwitch,
1527
1543
  timerRemain: deviceData.timerRemain,
1528
1544
  temperature: deviceData.temperature || 0,
1529
1545
  humidity: deviceData.humidity || 0,
@@ -1577,12 +1593,11 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1577
1593
  * Set tower fan mode
1578
1594
  */
1579
1595
  async mode_toggle(mode) {
1580
- const validModes = this.towerModes.map(m => m.toLowerCase());
1581
- if (!validModes.includes(mode.toLowerCase())) {
1582
- logger_1.logger.debug(`Invalid tower fan mode used - ${mode}`);
1596
+ if (!this.towerModes.includes(mode)) {
1597
+ logger_1.logger.debug(`Invalid mode: ${mode}. Must be one of: ${this.towerModes.join(', ')}`);
1583
1598
  return false;
1584
1599
  }
1585
- if (mode.toLowerCase() === 'off') {
1600
+ if (mode === 'off') {
1586
1601
  return this.turnOff();
1587
1602
  }
1588
1603
  const [head, body] = this.buildApiDict('setTowerFanMode');
@@ -1596,7 +1611,7 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1596
1611
  this.details.workMode = mode;
1597
1612
  }
1598
1613
  else {
1599
- logger_1.logger.debug('Error setting tower fan mode');
1614
+ logger_1.logger.error(`Failed to set ${mode} mode for device: ${this.deviceName}`);
1600
1615
  }
1601
1616
  return success;
1602
1617
  }
@@ -1604,8 +1619,10 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1604
1619
  * Change fan speed
1605
1620
  */
1606
1621
  async changeFanSpeed(speed) {
1607
- if (!this.speeds.includes(speed)) {
1608
- logger_1.logger.debug(`Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`);
1622
+ var _a;
1623
+ const speeds = (_a = this.config.levels) !== null && _a !== void 0 ? _a : [];
1624
+ if (!speeds.includes(speed)) {
1625
+ logger_1.logger.debug(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')}`);
1609
1626
  return false;
1610
1627
  }
1611
1628
  const [head, body] = this.buildApiDict('setLevel');
@@ -1718,18 +1735,6 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1718
1735
  get errorCode() {
1719
1736
  return this.details.errorCode || 0;
1720
1737
  }
1721
- /**
1722
- * Set oscillation state
1723
- */
1724
- async setOscillation(toggle) {
1725
- logger_1.logger.debug(`Setting oscillation to ${toggle ? 'on' : 'off'} for device: ${this.deviceName}`);
1726
- const [head, body] = this.buildApiDict('setSwitch');
1727
- body.payload.data = {
1728
- oscillationSwitch: toggle ? 1 : 0
1729
- };
1730
- const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
1731
- return this.checkResponse([response, status], 'setOscillation');
1732
- }
1733
1738
  /**
1734
1739
  * Turn oscillation on
1735
1740
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "description": "A TypeScript library for interacting with VeSync smart home devices",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,6 +10,7 @@ interface FanConfig {
10
10
  [key: string]: {
11
11
  module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2';
12
12
  features: string[];
13
+ levels?: number[];
13
14
  };
14
15
  }
15
16
 
@@ -18,147 +19,182 @@ export const fanConfig: FanConfig = {
18
19
  // Core Series
19
20
  'Core200S': {
20
21
  module: 'VeSyncAirBypass',
21
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
22
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
23
+ levels: [1, 2, 3]
22
24
  },
23
25
  'Core300S': {
24
26
  module: 'VeSyncAirBypass',
25
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
27
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
28
+ levels: [1, 2, 3, 4]
26
29
  },
27
30
  'Core400S': {
28
31
  module: 'VeSyncAirBypass',
29
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
32
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
33
+ levels: [1, 2, 3, 4]
30
34
  },
31
35
  'Core600S': {
32
36
  module: 'VeSyncAirBypass',
33
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
37
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
38
+ levels: [1, 2, 3, 4]
34
39
  },
35
40
 
36
41
  // LAP Series
37
42
  'LAP-C201S-AUSR': {
38
43
  module: 'VeSyncAirBypass',
39
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
44
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
45
+ levels: [1, 2, 3]
40
46
  },
41
47
  'LAP-C202S-WUSR': {
42
48
  module: 'VeSyncAirBypass',
43
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
49
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
50
+ levels: [1, 2, 3]
44
51
  },
45
52
  'LAP-C301S-WJP': {
46
53
  module: 'VeSyncAirBypass',
47
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
54
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
55
+ levels: [1, 2, 3, 4]
48
56
  },
49
57
  'LAP-C302S-WUSB': {
50
58
  module: 'VeSyncAirBypass',
51
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
59
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
60
+ levels: [1, 2, 3, 4]
52
61
  },
53
62
  'LAP-C301S-WAAA': {
54
63
  module: 'VeSyncAirBypass',
55
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
64
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
65
+ levels: [1, 2, 3, 4]
56
66
  },
57
67
  'LAP-C401S-WJP': {
58
68
  module: 'VeSyncAirBypass',
59
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
69
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
70
+ levels: [1, 2, 3, 4]
60
71
  },
61
72
  'LAP-C401S-WUSR': {
62
73
  module: 'VeSyncAirBypass',
63
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
74
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
75
+ levels: [1, 2, 3, 4]
64
76
  },
65
77
  'LAP-C401S-WAAA': {
66
78
  module: 'VeSyncAirBypass',
67
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
79
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
80
+ levels: [1, 2, 3, 4]
68
81
  },
69
82
  'LAP-C601S-WUS': {
70
83
  module: 'VeSyncAirBypass',
71
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
84
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
85
+ levels: [1, 2, 3, 4]
72
86
  },
73
87
  'LAP-C601S-WUSR': {
74
88
  module: 'VeSyncAirBypass',
75
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
89
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
90
+ levels: [1, 2, 3, 4]
76
91
  },
77
92
  'LAP-C601S-WEU': {
78
93
  module: 'VeSyncAirBypass',
79
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
94
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
95
+ levels: [1, 2, 3, 4]
80
96
  },
81
97
  'LAP-V102S-AASR': {
82
98
  module: 'VeSyncAirBaseV2',
83
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
99
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
100
+ levels: [1, 2, 3, 4]
84
101
  },
85
102
  'LAP-V102S-WUS': {
86
103
  module: 'VeSyncAirBaseV2',
87
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
104
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
105
+ levels: [1, 2, 3, 4]
88
106
  },
89
107
  'LAP-V102S-WEU': {
90
108
  module: 'VeSyncAirBaseV2',
91
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
109
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
110
+ levels: [1, 2, 3, 4]
92
111
  },
93
112
  'LAP-V102S-AUSR': {
94
113
  module: 'VeSyncAirBaseV2',
95
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
114
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
115
+ levels: [1, 2, 3, 4]
96
116
  },
97
117
  'LAP-V102S-WJP': {
98
118
  module: 'VeSyncAirBaseV2',
99
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer']
119
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
120
+ levels: [1, 2, 3, 4]
100
121
  },
101
122
  'LAP-V201S-AASR': {
102
123
  module: 'VeSyncAirBaseV2',
103
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
124
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
125
+ levels: [1, 2, 3, 4]
104
126
  },
105
127
  'LAP-V201S-WJP': {
106
128
  module: 'VeSyncAirBaseV2',
107
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
129
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
130
+ levels: [1, 2, 3, 4]
108
131
  },
109
132
  'LAP-V201S-WEU': {
110
133
  module: 'VeSyncAirBaseV2',
111
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
134
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
135
+ levels: [1, 2, 3, 4]
112
136
  },
113
137
  'LAP-V201S-WUS': {
114
138
  module: 'VeSyncAirBaseV2',
115
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
139
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
140
+ levels: [1, 2, 3, 4]
116
141
  },
117
142
  'LAP-V201-AUSR': {
118
143
  module: 'VeSyncAirBaseV2',
119
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
144
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
145
+ levels: [1, 2, 3, 4]
120
146
  },
121
147
  'LAP-V201S-AUSR': {
122
148
  module: 'VeSyncAirBaseV2',
123
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
149
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
150
+ levels: [1, 2, 3, 4]
124
151
  },
125
152
  'LAP-V201S-AEUR': {
126
153
  module: 'VeSyncAirBaseV2',
127
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection']
154
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection'],
155
+ levels: [1, 2, 3, 4]
128
156
  },
129
157
  'LAP-EL551S-AUS': {
130
158
  module: 'VeSyncAirBaseV2',
131
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
159
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
160
+ levels: [1, 2, 3]
132
161
  },
133
162
  'LAP-EL551S-AEUR': {
134
163
  module: 'VeSyncAirBaseV2',
135
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
164
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
165
+ levels: [1, 2, 3]
136
166
  },
137
167
  'LAP-EL551S-WEU': {
138
168
  module: 'VeSyncAirBaseV2',
139
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
169
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
170
+ levels: [1, 2, 3]
140
171
  },
141
172
  'LAP-EL551S-WUS': {
142
173
  module: 'VeSyncAirBaseV2',
143
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate']
174
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate'],
175
+ levels: [1, 2, 3]
144
176
  },
145
177
 
146
178
  // LTF Series
147
179
  'LTF-F422S-KEU': {
148
180
  module: 'VeSyncTowerFan',
149
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
181
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
182
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
150
183
  },
151
184
  'LTF-F422S-WUSR': {
152
185
  module: 'VeSyncTowerFan',
153
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
186
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
187
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
154
188
  },
155
189
  'LTF-F422_WJP': {
156
190
  module: 'VeSyncTowerFan',
157
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
191
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
192
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
158
193
  },
159
194
  'LTF-F422S-WUS': {
160
195
  module: 'VeSyncTowerFan',
161
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
196
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
197
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
162
198
  },
163
199
 
164
200
  // Classic Series
@@ -264,11 +300,13 @@ export const fanConfig: FanConfig = {
264
300
  // LV Series
265
301
  'LV-PUR131S': {
266
302
  module: 'VeSyncAirBypass',
267
- features: ['air_quality', 'display', 'child_lock', 'night_light', 'timer']
303
+ features: ['air_quality', 'display', 'child_lock', 'night_light', 'timer'],
304
+ levels: [1, 2, 3]
268
305
  },
269
306
  'LV-RH131S': {
270
307
  module: 'VeSyncAirBypass',
271
- features: ['display', 'child_lock', 'night_light', 'timer']
308
+ features: ['display', 'child_lock', 'night_light', 'timer'],
309
+ levels: [1, 2, 3]
272
310
  }
273
311
  };
274
312
 
@@ -276,23 +314,43 @@ export const fanConfig: FanConfig = {
276
314
  * Base class for VeSync Fans
277
315
  */
278
316
  export abstract class VeSyncFan extends VeSyncBaseDevice {
279
- protected details: Record<string, any> = {};
317
+ protected details: {
318
+ mode?: string;
319
+ speed?: number;
320
+ filter_life?: number;
321
+ screen_status?: string;
322
+ child_lock?: boolean;
323
+ air_quality?: string;
324
+ humidity?: number;
325
+ mist_level?: number;
326
+ warm_level?: number;
327
+ warm_enabled?: boolean;
328
+ [key: string]: any;
329
+ } = {};
280
330
  protected mode_dict: Record<string, any> = {};
281
331
  protected speed_dict: Record<string, any> = {};
282
- protected features: string[] = [];
283
332
  protected _timer: number | { duration: number; action: string } | null = null;
333
+ protected config: {
334
+ module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2';
335
+ features: string[];
336
+ levels?: number[];
337
+ };
284
338
 
285
339
  constructor(details: Record<string, any>, manager: VeSync) {
286
340
  super(details, manager);
287
341
  this.details = details;
288
- this.features = fanConfig[this.deviceType]?.features || [];
342
+ this.config = fanConfig[this.deviceType] || {
343
+ module: 'VeSyncAirBypass',
344
+ features: [],
345
+ levels: []
346
+ };
289
347
  }
290
348
 
291
349
  /**
292
350
  * Check if feature is supported
293
351
  */
294
352
  hasFeature(feature: string): boolean {
295
- return this.features.includes(feature);
353
+ return this.config.features.includes(feature);
296
354
  }
297
355
 
298
356
  /**
@@ -326,8 +384,8 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
326
384
  /**
327
385
  * Get child lock status
328
386
  */
329
- get childLock(): string {
330
- return this.details.child_lock ?? '';
387
+ get childLock(): boolean {
388
+ return this.details.child_lock ?? false;
331
389
  }
332
390
 
333
391
  /**
@@ -384,7 +442,7 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
384
442
  info.push(['Screen Status: ', this.screenStatus]);
385
443
  }
386
444
  if (this.hasFeature('child_lock')) {
387
- info.push(['Child Lock: ', this.childLock]);
445
+ info.push(['Child Lock: ', this.childLock ? 'Enabled' : 'Disabled']);
388
446
  }
389
447
  if (this.hasFeature('air_quality')) {
390
448
  info.push(['Air Quality: ', this.airQuality]);
@@ -427,7 +485,7 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
427
485
  details['Screen Status'] = this.screenStatus;
428
486
  }
429
487
  if (this.hasFeature('child_lock')) {
430
- details['Child Lock'] = this.childLock;
488
+ details['Child Lock'] = this.childLock ? 'Enabled' : 'Disabled';
431
489
  }
432
490
  if (this.hasFeature('air_quality')) {
433
491
  details['Air Quality'] = this.airQuality;
@@ -12,7 +12,6 @@ import { logger } from './logger';
12
12
  */
13
13
  export class VeSyncAirBypass extends VeSyncFan {
14
14
  protected readonly modes = ['auto', 'manual', 'sleep'] as const;
15
- protected readonly speeds = [1, 2, 3, 4, 5];
16
15
  protected readonly displayModes = ['on', 'off'] as const;
17
16
  protected readonly childLockModes = ['on', 'off'] as const;
18
17
 
@@ -258,10 +257,10 @@ export class VeSyncAirBypass extends VeSyncFan {
258
257
  * Change fan speed
259
258
  */
260
259
  async changeFanSpeed(speed: number): Promise<boolean> {
261
- if (!this.speeds.includes(speed)) {
262
- const error = `Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`;
263
- logger.error(`${error} for device: ${this.deviceName}`);
264
- throw new Error(error);
260
+ const speeds = this.config.levels ?? [];
261
+ if (!speeds.includes(speed)) {
262
+ logger.error(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')} for device: ${this.deviceName}`);
263
+ return false;
265
264
  }
266
265
 
267
266
  logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
@@ -1096,7 +1095,7 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
1096
1095
  // Add warm humidifier specific fields
1097
1096
  this.details = {
1098
1097
  ...this.details,
1099
- warm_enabled: this.details.warm_level > 0,
1098
+ warm_enabled: Boolean(this.details.warm_level && this.details.warm_level > 0),
1100
1099
  drying_mode_enabled: this.details.autoDryingSwitch === 1,
1101
1100
  drying_mode_state: this.details.dryingMode || '',
1102
1101
  drying_mode_level: this.details.dryingLevel || 0,
@@ -1292,7 +1291,10 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1292
1291
 
1293
1292
  protected buildConfigDict(configDict: Record<string, any>): void {
1294
1293
  if (configDict) {
1295
- this.config = configDict;
1294
+ this.config = {
1295
+ ...this.config, // Preserve existing config
1296
+ ...configDict // Merge in new config
1297
+ };
1296
1298
  }
1297
1299
  }
1298
1300
 
@@ -1467,35 +1469,32 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1467
1469
  * Change fan speed
1468
1470
  */
1469
1471
  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
- }
1472
+ const speeds = this.config.levels ?? [];
1473
+ if (!speeds.includes(speed)) {
1474
+ logger.error(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')} for device: ${this.deviceName}`);
1475
+ return false;
1485
1476
  }
1486
1477
 
1487
- const [head, body] = this.buildApiDict('setLevel');
1488
- body.payload.data = {
1489
- levelIdx: 0,
1490
- manualSpeedLevel: speed,
1491
- levelType: 'wind'
1492
- };
1493
-
1478
+ logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
1494
1479
  const [response, status] = await this.callApi(
1495
1480
  '/cloud/v2/deviceManaged/bypassV2',
1496
1481
  'post',
1497
- body,
1498
- head
1482
+ {
1483
+ ...Helpers.reqBody(this.manager, 'bypassV2'),
1484
+ cid: this.cid,
1485
+ configModule: this.configModule,
1486
+ payload: {
1487
+ data: {
1488
+ id: 0,
1489
+ level: speed,
1490
+ mode: 'manual',
1491
+ type: 'wind'
1492
+ },
1493
+ method: 'setLevel',
1494
+ source: 'APP'
1495
+ }
1496
+ },
1497
+ Helpers.reqHeaderBypass()
1499
1498
  );
1500
1499
 
1501
1500
  const success = this.checkResponse([response, status], 'changeFanSpeed');
@@ -1757,13 +1756,14 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1757
1756
  const details: Record<string, string> = {
1758
1757
  ...baseInfo,
1759
1758
  'Mode': this.mode,
1760
- 'Filter Life': this.details.filter_life.toString(),
1759
+ 'Filter Life': (this.details.filter_life ?? 0).toString(),
1761
1760
  '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()
1761
+ 'Display On': this.details.display?.toString() ?? 'false',
1762
+ 'Child Lock': (this.details.child_lock ?? false).toString(),
1763
+ 'Night Light': (this.details.night_light ?? '').toString(),
1764
+ 'Display Set On': (this.details.screen_switch ?? false).toString(),
1765
+ 'Light Detection Enabled': (this.details.light_detection_switch ?? false).toString(),
1766
+ 'Environment Light State': (this.details.environment_light_state ?? false).toString()
1767
1767
  };
1768
1768
 
1769
1769
  if (this.hasFeature('air_quality')) {
@@ -1780,12 +1780,39 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1780
1780
 
1781
1781
  for (const [key, value] of Object.entries(everestKeys)) {
1782
1782
  if (key in this.details) {
1783
- details[value] = this.details[key].toString();
1783
+ details[value] = this.details[key]?.toString() ?? '';
1784
1784
  }
1785
1785
  }
1786
1786
 
1787
1787
  return JSON.stringify(details, null, 4);
1788
1788
  }
1789
+
1790
+ /**
1791
+ * Set oscillation state
1792
+ */
1793
+ async setOscillation(toggle: boolean): Promise<boolean> {
1794
+ logger.debug(`Setting oscillation to ${toggle ? 'on' : 'off'} for device: ${this.deviceName}`);
1795
+
1796
+ const [head, body] = this.buildApiDict('setSwitch');
1797
+ body.payload.data = {
1798
+ oscillationSwitch: toggle ? 1 : 0,
1799
+ switchIdx: 0
1800
+ };
1801
+
1802
+ const [response, status] = await this.callApi(
1803
+ '/cloud/v2/deviceManaged/bypassV2',
1804
+ 'post',
1805
+ body,
1806
+ head
1807
+ );
1808
+
1809
+ const success = this.checkResponse([response, status], 'setOscillation');
1810
+ if (success) {
1811
+ this.details.oscillationState = toggle;
1812
+ this.details.oscillationSwitch = toggle ? 1 : 0;
1813
+ }
1814
+ return success;
1815
+ }
1789
1816
  }
1790
1817
 
1791
1818
  /**
@@ -1793,7 +1820,6 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
1793
1820
  */
1794
1821
  export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1795
1822
  protected readonly towerModes = ['normal', 'advancedSleep', 'off'] as const;
1796
- protected readonly speeds = Array.from({length: 12}, (_, i) => i + 1);
1797
1823
 
1798
1824
  constructor(details: Record<string, any>, manager: VeSync) {
1799
1825
  super(details, manager);
@@ -1872,8 +1898,6 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1872
1898
  screenSwitch: deviceData.screenSwitch,
1873
1899
  oscillationState: deviceData.oscillationState,
1874
1900
  oscillationSwitch: deviceData.oscillationSwitch,
1875
- muteState: deviceData.muteState,
1876
- muteSwitch: deviceData.muteSwitch,
1877
1901
  timerRemain: deviceData.timerRemain,
1878
1902
  temperature: deviceData.temperature || 0,
1879
1903
  humidity: deviceData.humidity || 0,
@@ -1939,13 +1963,12 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1939
1963
  * Set tower fan mode
1940
1964
  */
1941
1965
  async mode_toggle(mode: string): Promise<boolean> {
1942
- const validModes = this.towerModes.map(m => m.toLowerCase());
1943
- if (!validModes.includes(mode.toLowerCase())) {
1944
- logger.debug(`Invalid tower fan mode used - ${mode}`);
1966
+ if (!this.towerModes.includes(mode as any)) {
1967
+ logger.debug(`Invalid mode: ${mode}. Must be one of: ${this.towerModes.join(', ')}`);
1945
1968
  return false;
1946
1969
  }
1947
1970
 
1948
- if (mode.toLowerCase() === 'off') {
1971
+ if (mode === 'off') {
1949
1972
  return this.turnOff();
1950
1973
  }
1951
1974
 
@@ -1966,7 +1989,7 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1966
1989
  if (success) {
1967
1990
  this.details.workMode = mode;
1968
1991
  } else {
1969
- logger.debug('Error setting tower fan mode');
1992
+ logger.error(`Failed to set ${mode} mode for device: ${this.deviceName}`);
1970
1993
  }
1971
1994
  return success;
1972
1995
  }
@@ -1975,8 +1998,9 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1975
1998
  * Change fan speed
1976
1999
  */
1977
2000
  async changeFanSpeed(speed: number): Promise<boolean> {
1978
- if (!this.speeds.includes(speed)) {
1979
- logger.debug(`Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`);
2001
+ const speeds = this.config.levels ?? [];
2002
+ if (!speeds.includes(speed)) {
2003
+ logger.debug(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')}`);
1980
2004
  return false;
1981
2005
  }
1982
2006
 
@@ -2112,27 +2136,6 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
2112
2136
  return this.details.errorCode || 0;
2113
2137
  }
2114
2138
 
2115
- /**
2116
- * Set oscillation state
2117
- */
2118
- async setOscillation(toggle: boolean): Promise<boolean> {
2119
- logger.debug(`Setting oscillation to ${toggle ? 'on' : 'off'} for device: ${this.deviceName}`);
2120
-
2121
- const [head, body] = this.buildApiDict('setSwitch');
2122
- body.payload.data = {
2123
- oscillationSwitch: toggle ? 1 : 0
2124
- };
2125
-
2126
- const [response, status] = await this.callApi(
2127
- '/cloud/v2/deviceManaged/bypassV2',
2128
- 'post',
2129
- body,
2130
- head
2131
- );
2132
-
2133
- return this.checkResponse([response, status], 'setOscillation');
2134
- }
2135
-
2136
2139
  /**
2137
2140
  * Turn oscillation on
2138
2141
  */