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.
@@ -7,7 +7,9 @@ import { logger } from '../logger';
7
7
  * VeSync Air Purifier with Bypass
8
8
  */
9
9
  export class VeSyncAirBypass extends VeSyncFan {
10
- protected readonly modes = ['auto', 'manual', 'sleep'] as const;
10
+ protected get modes(): ReadonlyArray<string> {
11
+ return this.getConfigModes(['auto', 'manual', 'sleep']);
12
+ }
11
13
  protected readonly displayModes = ['on', 'off'] as const;
12
14
  protected readonly childLockModes = ['on', 'off'] as const;
13
15
 
@@ -77,17 +79,33 @@ export class VeSyncAirBypass extends VeSyncFan {
77
79
  }
78
80
  }
79
81
 
82
+ // Parse air quality with fallback support for multiple API response formats
83
+ // Some devices (like Core 400S) may use PascalCase (AQLevel, PM25)
84
+ // while others use snake_case (air_quality, air_quality_value)
85
+ const airQualityLevel = result.air_quality || result.AQLevel || 0;
86
+ const airQualityValue = result.air_quality_value || result.PM25 || 0;
87
+
88
+ // Debug logging to diagnose air quality field availability
89
+ logger.debug(`${this.deviceName}: Air quality API response fields:`, {
90
+ air_quality: result.air_quality,
91
+ AQLevel: result.AQLevel,
92
+ air_quality_value: result.air_quality_value,
93
+ PM25: result.PM25,
94
+ parsed_level: airQualityLevel,
95
+ parsed_value: airQualityValue
96
+ });
97
+
80
98
  this.details = {
81
99
  mode: result.mode || '',
82
100
  speed: result.level || 0,
83
101
  filter_life: filterLife,
84
102
  screen_status: result.display ? 'on' : 'off',
85
103
  child_lock: result.child_lock || false,
86
- air_quality: result.air_quality || 0,
87
- air_quality_value: result.air_quality_value || 0
104
+ air_quality: airQualityLevel,
105
+ air_quality_value: airQualityValue
88
106
  };
89
107
 
90
- const normalizedAirQuality = Helpers.normalizeAirQuality(result.air_quality);
108
+ const normalizedAirQuality = Helpers.normalizeAirQuality(airQualityLevel);
91
109
  if (normalizedAirQuality.level >= 1) {
92
110
  this.details.air_quality_level = normalizedAirQuality.level;
93
111
  }
@@ -98,6 +116,15 @@ export class VeSyncAirBypass extends VeSyncFan {
98
116
  if ('PM10' in result) this.details.pm10 = result.PM10;
99
117
  if ('AQPercent' in result) this.details.aq_percent = result.AQPercent;
100
118
 
119
+ // Debug log additional air quality fields for diagnostics
120
+ if (result.PM1 !== undefined || result.PM10 !== undefined || result.AQPercent !== undefined) {
121
+ logger.debug(`${this.deviceName}: Additional air quality data:`, {
122
+ PM1: result.PM1,
123
+ PM10: result.PM10,
124
+ AQPercent: result.AQPercent
125
+ });
126
+ }
127
+
101
128
  // Don't overwrite config as it contains features array
102
129
  if (result.configuration) {
103
130
  this.details.configuration = result.configuration;
@@ -216,16 +243,20 @@ export class VeSyncAirBypass extends VeSyncFan {
216
243
  * Set device mode
217
244
  */
218
245
  async setMode(mode: string): Promise<boolean> {
219
- if (!this.modes.includes(mode as any)) {
246
+ const requested = mode.toLowerCase();
247
+ const supported = this.modes.map(m => m.toLowerCase());
248
+ const allowsCore200AutoFallback = this.deviceType.includes('Core200S') && requested === 'auto';
249
+
250
+ if (!supported.includes(requested) && !allowsCore200AutoFallback) {
220
251
  const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
221
252
  logger.error(`${error} for device: ${this.deviceName}`);
222
253
  throw new Error(error);
223
254
  }
224
255
 
225
- logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
256
+ logger.debug(`Setting mode to ${requested} for device: ${this.deviceName}`);
226
257
 
227
258
  // Special handling for Core200S auto mode
228
- if (this.deviceType.includes('Core200S') && mode === 'auto') {
259
+ if (allowsCore200AutoFallback) {
229
260
  // Core200S doesn't support auto mode, so we'll set it to manual mode
230
261
  logger.warn(`Auto mode not supported for ${this.deviceType}, using manual mode instead`);
231
262
  return this.setMode('manual');
@@ -241,7 +272,7 @@ export class VeSyncAirBypass extends VeSyncFan {
241
272
  configModule: this.configModule,
242
273
  payload: {
243
274
  data: {
244
- mode
275
+ mode: requested
245
276
  },
246
277
  method: 'setPurifierMode',
247
278
  source: 'APP'
@@ -252,10 +283,10 @@ export class VeSyncAirBypass extends VeSyncFan {
252
283
 
253
284
  const success = this.checkResponse([response, status], 'setMode');
254
285
  if (success) {
255
- this.details.mode = mode;
286
+ this.details.mode = requested;
256
287
  return true;
257
288
  } else {
258
- logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
289
+ logger.error(`Failed to set mode to ${requested} for device: ${this.deviceName}`);
259
290
  return false;
260
291
  }
261
292
  }
@@ -7,12 +7,16 @@ import { VeSync } from './vesync';
7
7
  import { logger } from './logger';
8
8
  import { Helpers } from './helpers';
9
9
 
10
+ interface FanConfigEntry {
11
+ module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S' | 'VeSyncAir131';
12
+ features: string[];
13
+ levels?: number[];
14
+ modes?: ReadonlyArray<string>;
15
+ autoPreferences?: ReadonlyArray<string>;
16
+ }
17
+
10
18
  interface FanConfig {
11
- [key: string]: {
12
- module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S' | 'VeSyncAir131';
13
- features: string[];
14
- levels?: number[];
15
- };
19
+ [key: string]: FanConfigEntry;
16
20
  }
17
21
 
18
22
  // Fan configuration
@@ -21,159 +25,218 @@ export const fanConfig: FanConfig = {
21
25
  'Core200S': {
22
26
  module: 'VeSyncAirBypass',
23
27
  features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'sleep_mode', 'filter_life'],
24
- levels: [1, 2, 3, 4] // 4 levels including sleep mode, matching reference plugin
28
+ levels: [1, 2, 3, 4], // 4 levels including sleep mode, matching reference plugin
29
+ modes: ['sleep', 'manual']
25
30
  },
26
31
  'Core300S': {
27
32
  module: 'VeSyncAirBypass',
28
33
  features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
29
- levels: [1, 2, 3, 4]
34
+ levels: [1, 2, 3, 4],
35
+ modes: ['sleep', 'manual', 'auto'],
36
+ autoPreferences: ['default', 'efficient', 'quiet']
30
37
  },
31
38
  'Core400S': {
32
39
  module: 'VeSyncAirBypass',
33
40
  features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
34
- levels: [1, 2, 3, 4]
41
+ levels: [1, 2, 3, 4],
42
+ modes: ['sleep', 'manual', 'auto'],
43
+ autoPreferences: ['default', 'efficient', 'quiet']
35
44
  },
36
45
  'Core600S': {
37
46
  module: 'VeSyncAirBypass',
38
47
  features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
39
- levels: [1, 2, 3, 4]
48
+ levels: [1, 2, 3, 4],
49
+ modes: ['sleep', 'manual', 'auto'],
50
+ autoPreferences: ['default', 'efficient', 'quiet']
40
51
  },
41
52
 
42
53
  // LAP Series
43
54
  'LAP-C201S-AUSR': {
44
55
  module: 'VeSyncAirBypass',
45
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
46
- levels: [1, 2, 3]
56
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'sleep_mode', 'filter_life'],
57
+ levels: [1, 2, 3],
58
+ modes: ['sleep', 'manual']
47
59
  },
48
60
  'LAP-C202S-WUSR': {
49
61
  module: 'VeSyncAirBypass',
50
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
51
- levels: [1, 2, 3]
62
+ features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'sleep_mode', 'filter_life'],
63
+ levels: [1, 2, 3],
64
+ modes: ['sleep', 'manual']
52
65
  },
53
66
  'LAP-C301S-WJP': {
54
67
  module: 'VeSyncAirBypass',
55
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
56
- levels: [1, 2, 3, 4]
68
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
69
+ levels: [1, 2, 3, 4],
70
+ modes: ['sleep', 'manual', 'auto'],
71
+ autoPreferences: ['default', 'efficient', 'quiet']
57
72
  },
58
73
  'LAP-C302S-WUSB': {
59
74
  module: 'VeSyncAirBypass',
60
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
61
- levels: [1, 2, 3, 4]
75
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
76
+ levels: [1, 2, 3, 4],
77
+ modes: ['sleep', 'manual', 'auto'],
78
+ autoPreferences: ['default', 'efficient', 'quiet']
62
79
  },
63
80
  'LAP-C301S-WAAA': {
64
81
  module: 'VeSyncAirBypass',
65
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
66
- levels: [1, 2, 3, 4]
82
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
83
+ levels: [1, 2, 3, 4],
84
+ modes: ['sleep', 'manual', 'auto'],
85
+ autoPreferences: ['default', 'efficient', 'quiet']
67
86
  },
68
87
  'LAP-C401S-WJP': {
69
88
  module: 'VeSyncAirBypass',
70
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
71
- levels: [1, 2, 3, 4]
89
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
90
+ levels: [1, 2, 3, 4],
91
+ modes: ['sleep', 'manual', 'auto'],
92
+ autoPreferences: ['default', 'efficient', 'quiet']
72
93
  },
73
94
  'LAP-C401S-WUSR': {
74
95
  module: 'VeSyncAirBypass',
75
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
76
- levels: [1, 2, 3, 4]
96
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
97
+ levels: [1, 2, 3, 4],
98
+ modes: ['sleep', 'manual', 'auto'],
99
+ autoPreferences: ['default', 'efficient', 'quiet']
77
100
  },
78
101
  'LAP-C401S-WAAA': {
79
102
  module: 'VeSyncAirBypass',
80
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
81
- levels: [1, 2, 3, 4]
103
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
104
+ levels: [1, 2, 3, 4],
105
+ modes: ['sleep', 'manual', 'auto'],
106
+ autoPreferences: ['default', 'efficient', 'quiet']
82
107
  },
83
108
  'LAP-C601S-WUS': {
84
109
  module: 'VeSyncAirBypass',
85
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
86
- levels: [1, 2, 3, 4]
110
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
111
+ levels: [1, 2, 3, 4],
112
+ modes: ['sleep', 'manual', 'auto'],
113
+ autoPreferences: ['default', 'efficient', 'quiet']
87
114
  },
88
115
  'LAP-C601S-WUSR': {
89
116
  module: 'VeSyncAirBypass',
90
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
91
- levels: [1, 2, 3, 4]
117
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
118
+ levels: [1, 2, 3, 4],
119
+ modes: ['sleep', 'manual', 'auto'],
120
+ autoPreferences: ['default', 'efficient', 'quiet']
92
121
  },
93
122
  'LAP-C601S-WEU': {
94
123
  module: 'VeSyncAirBypass',
95
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
96
- levels: [1, 2, 3, 4]
124
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'filter_life'],
125
+ levels: [1, 2, 3, 4],
126
+ modes: ['sleep', 'manual', 'auto'],
127
+ autoPreferences: ['default', 'efficient', 'quiet']
97
128
  },
98
129
  'LAP-V102S-AASR': {
99
130
  module: 'VeSyncAirBaseV2',
100
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'filter_life'],
101
- levels: [1, 2, 3, 4]
131
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
132
+ levels: [1, 2, 3, 4],
133
+ modes: ['sleep', 'manual', 'auto', 'pet'],
134
+ autoPreferences: ['default', 'efficient', 'quiet']
102
135
  },
103
136
  'LAP-V102S-WUS': {
104
137
  module: 'VeSyncAirBaseV2',
105
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'filter_life'],
106
- levels: [1, 2, 3, 4]
138
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
139
+ levels: [1, 2, 3, 4],
140
+ modes: ['sleep', 'manual', 'auto', 'pet'],
141
+ autoPreferences: ['default', 'efficient', 'quiet']
107
142
  },
108
143
  'LAP-V102S-WEU': {
109
144
  module: 'VeSyncAirBaseV2',
110
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'filter_life'],
111
- levels: [1, 2, 3, 4]
145
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
146
+ levels: [1, 2, 3, 4],
147
+ modes: ['sleep', 'manual', 'auto', 'pet'],
148
+ autoPreferences: ['default', 'efficient', 'quiet']
112
149
  },
113
150
  'LAP-V102S-AUSR': {
114
151
  module: 'VeSyncAirBaseV2',
115
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'filter_life'],
116
- levels: [1, 2, 3, 4]
152
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
153
+ levels: [1, 2, 3, 4],
154
+ modes: ['sleep', 'manual', 'auto', 'pet'],
155
+ autoPreferences: ['default', 'efficient', 'quiet']
117
156
  },
118
157
  'LAP-V102S-WJP': {
119
158
  module: 'VeSyncAirBaseV2',
120
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'filter_life'],
121
- levels: [1, 2, 3, 4]
159
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
160
+ levels: [1, 2, 3, 4],
161
+ modes: ['sleep', 'manual', 'auto', 'pet'],
162
+ autoPreferences: ['default', 'efficient', 'quiet']
122
163
  },
123
164
  'LAP-V201S-AASR': {
124
165
  module: 'VeSyncAirBaseV2',
125
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'filter_life'],
126
- levels: [1, 2, 3, 4]
166
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
167
+ levels: [1, 2, 3, 4],
168
+ modes: ['sleep', 'manual', 'auto', 'pet'],
169
+ autoPreferences: ['default', 'efficient', 'quiet']
127
170
  },
128
171
  'LAP-V201S-WJP': {
129
172
  module: 'VeSyncAirBaseV2',
130
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'filter_life'],
131
- levels: [1, 2, 3, 4]
173
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
174
+ levels: [1, 2, 3, 4],
175
+ modes: ['sleep', 'manual', 'auto', 'pet'],
176
+ autoPreferences: ['default', 'efficient', 'quiet']
132
177
  },
133
178
  'LAP-V201S-WEU': {
134
179
  module: 'VeSyncAirBaseV2',
135
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'filter_life'],
136
- levels: [1, 2, 3, 4]
180
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
181
+ levels: [1, 2, 3, 4],
182
+ modes: ['sleep', 'manual', 'auto', 'pet'],
183
+ autoPreferences: ['default', 'efficient', 'quiet']
137
184
  },
138
185
  'LAP-V201S-WUS': {
139
186
  module: 'VeSyncAirBaseV2',
140
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'filter_life'],
141
- levels: [1, 2, 3, 4]
187
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
188
+ levels: [1, 2, 3, 4],
189
+ modes: ['sleep', 'manual', 'auto', 'pet'],
190
+ autoPreferences: ['default', 'efficient', 'quiet']
142
191
  },
143
192
  'LAP-V201-AUSR': {
144
193
  module: 'VeSyncAirBaseV2',
145
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'filter_life'],
146
- levels: [1, 2, 3, 4]
194
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
195
+ levels: [1, 2, 3, 4],
196
+ modes: ['sleep', 'manual', 'auto', 'pet'],
197
+ autoPreferences: ['default', 'efficient', 'quiet']
147
198
  },
148
199
  'LAP-V201S-AUSR': {
149
200
  module: 'VeSyncAirBaseV2',
150
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'filter_life'],
151
- levels: [1, 2, 3, 4]
201
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
202
+ levels: [1, 2, 3, 4],
203
+ modes: ['sleep', 'manual', 'auto', 'pet'],
204
+ autoPreferences: ['default', 'efficient', 'quiet']
152
205
  },
153
206
  'LAP-V201S-AEUR': {
154
207
  module: 'VeSyncAirBaseV2',
155
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'filter_life'],
156
- levels: [1, 2, 3, 4]
208
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'light_detection', 'fan_speed', 'auto_mode', 'sleep_mode', 'pet_mode', 'filter_life'],
209
+ levels: [1, 2, 3, 4],
210
+ modes: ['sleep', 'manual', 'auto', 'pet'],
211
+ autoPreferences: ['default', 'efficient', 'quiet']
157
212
  },
158
213
  'LAP-EL551S-AUS': {
159
214
  module: 'VeSyncAirBaseV2',
160
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'filter_life'],
161
- levels: [1, 2, 3]
215
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'sleep_mode', 'turbo_mode', 'light_detection', 'filter_life'],
216
+ levels: [1, 2, 3],
217
+ modes: ['sleep', 'manual', 'auto', 'turbo'],
218
+ autoPreferences: ['default', 'efficient', 'quiet']
162
219
  },
163
220
  'LAP-EL551S-AEUR': {
164
221
  module: 'VeSyncAirBaseV2',
165
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'filter_life'],
166
- levels: [1, 2, 3]
222
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'sleep_mode', 'turbo_mode', 'light_detection', 'filter_life'],
223
+ levels: [1, 2, 3],
224
+ modes: ['sleep', 'manual', 'auto', 'turbo'],
225
+ autoPreferences: ['default', 'efficient', 'quiet']
167
226
  },
168
227
  'LAP-EL551S-WEU': {
169
228
  module: 'VeSyncAirBaseV2',
170
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'filter_life'],
171
- levels: [1, 2, 3]
229
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'sleep_mode', 'turbo_mode', 'light_detection', 'filter_life'],
230
+ levels: [1, 2, 3],
231
+ modes: ['sleep', 'manual', 'auto', 'turbo'],
232
+ autoPreferences: ['default', 'efficient', 'quiet']
172
233
  },
173
234
  'LAP-EL551S-WUS': {
174
235
  module: 'VeSyncAirBaseV2',
175
- features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'filter_life'],
176
- levels: [1, 2, 3]
236
+ features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_rotate', 'fan_speed', 'auto_mode', 'sleep_mode', 'turbo_mode', 'light_detection', 'filter_life'],
237
+ levels: [1, 2, 3],
238
+ modes: ['sleep', 'manual', 'auto', 'turbo'],
239
+ autoPreferences: ['default', 'efficient', 'quiet']
177
240
  },
178
241
 
179
242
  // LTF Series
@@ -342,20 +405,77 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
342
405
  protected mode_dict: Record<string, any> = {};
343
406
  protected speed_dict: Record<string, any> = {};
344
407
  protected _timer: number | { duration: number; action: string } | null = null;
345
- protected config: {
346
- module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S' | 'VeSyncAir131';
347
- features: string[];
348
- levels?: number[];
349
- };
408
+ protected config: FanConfigEntry;
350
409
 
351
410
  constructor(details: Record<string, any>, manager: VeSync) {
352
411
  super(details, manager);
353
412
  this.details = details;
354
- this.config = fanConfig[this.deviceType] || {
355
- module: 'VeSyncAirBypass',
356
- features: [],
357
- levels: []
358
- };
413
+
414
+ // Try exact match first
415
+ let entry: FanConfigEntry | undefined = fanConfig[this.deviceType];
416
+
417
+ // If no exact match, try substring matching for LAP variants
418
+ // This handles cases where device reports as "LAP-C401S-WUSR" but we only have "Core400S" config
419
+ if (!entry) {
420
+ entry = this.findConfigBySubstring(this.deviceType);
421
+ }
422
+
423
+ if (entry) {
424
+ this.config = {
425
+ ...entry,
426
+ features: [...entry.features],
427
+ levels: entry.levels ? [...entry.levels] : undefined,
428
+ modes: entry.modes ? [...entry.modes] : undefined,
429
+ autoPreferences: entry.autoPreferences ? [...entry.autoPreferences] : undefined
430
+ };
431
+ } else {
432
+ this.config = {
433
+ module: 'VeSyncAirBypass',
434
+ features: [],
435
+ levels: [],
436
+ modes: [],
437
+ autoPreferences: []
438
+ };
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Find config entry by substring matching for variant device types
444
+ * Maps LAP variants to their base model configs (e.g., LAP-C401S-WUSR → Core400S)
445
+ */
446
+ private findConfigBySubstring(deviceType: string): FanConfigEntry | undefined {
447
+ // Define model number mappings
448
+ const modelMappings: Array<{ patterns: string[]; configKey: string }> = [
449
+ // Core series air purifiers
450
+ { patterns: ['Core300S', '301S', '302S'], configKey: 'Core300S' },
451
+ { patterns: ['Core400S', '401S'], configKey: 'Core400S' },
452
+ { patterns: ['Core600S', '601S', '602S'], configKey: 'Core600S' },
453
+ { patterns: ['Core200S', '201S', '202S'], configKey: 'Core200S' },
454
+
455
+ // Vital series
456
+ { patterns: ['V102S'], configKey: 'LAP-V102S-AASR' },
457
+ { patterns: ['V201S'], configKey: 'LAP-V201S-AASR' },
458
+
459
+ // LV-PUR series
460
+ { patterns: ['LV-PUR131S', 'LV-RH131S'], configKey: 'LV-PUR131S' },
461
+
462
+ // Everest series
463
+ { patterns: ['LAP-EL551S'], configKey: 'LAP-EL551S-AUS' },
464
+ ];
465
+
466
+ // Check each mapping
467
+ for (const mapping of modelMappings) {
468
+ // If device type contains any of the patterns, use the config key
469
+ if (mapping.patterns.some(pattern => deviceType.includes(pattern))) {
470
+ const config = fanConfig[mapping.configKey];
471
+ if (config) {
472
+ logger.debug(`${deviceType}: Using fallback config from ${mapping.configKey} via substring match`);
473
+ return config;
474
+ }
475
+ }
476
+ }
477
+
478
+ return undefined;
359
479
  }
360
480
 
361
481
  /**
@@ -365,6 +485,40 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
365
485
  return this.config.features.includes(feature);
366
486
  }
367
487
 
488
+ /**
489
+ * Get configured modes for the device, falling back to provided defaults.
490
+ */
491
+ protected getConfigModes(defaultModes: ReadonlyArray<string> = []): ReadonlyArray<string> {
492
+ if (Array.isArray(this.config.modes) && this.config.modes.length > 0) {
493
+ return this.config.modes;
494
+ }
495
+ return defaultModes;
496
+ }
497
+
498
+ /**
499
+ * Get configured auto preferences for the device, falling back to provided defaults.
500
+ */
501
+ protected getConfigAutoPreferences(defaultPreferences: ReadonlyArray<string> = []): ReadonlyArray<string> {
502
+ if (Array.isArray(this.config.autoPreferences) && this.config.autoPreferences.length > 0) {
503
+ return this.config.autoPreferences;
504
+ }
505
+ return defaultPreferences;
506
+ }
507
+
508
+ /**
509
+ * Public accessor for supported modes for consumers that need to inspect capability metadata.
510
+ */
511
+ public getSupportedModes(defaultModes: ReadonlyArray<string> = []): ReadonlyArray<string> {
512
+ return this.getConfigModes(defaultModes);
513
+ }
514
+
515
+ /**
516
+ * Public accessor for supported auto preference profiles.
517
+ */
518
+ public getSupportedAutoPreferences(defaultPreferences: ReadonlyArray<string> = []): ReadonlyArray<string> {
519
+ return this.getConfigAutoPreferences(defaultPreferences);
520
+ }
521
+
368
522
  /**
369
523
  * Check if feature is supported in current mode
370
524
  */
@@ -403,7 +557,10 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
403
557
  * Get maximum fan speed level
404
558
  */
405
559
  getMaxFanSpeed(): number {
406
- return this.config.levels ? Math.max(...this.config.levels) : 3;
560
+ if (this.config.levels && this.config.levels.length > 0) {
561
+ return Math.max(...this.config.levels);
562
+ }
563
+ return 3;
407
564
  }
408
565
 
409
566
  /**