tsvesync 1.0.59 → 1.0.60
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/vesyncBaseDevice.d.ts +0 -5
- package/dist/lib/vesyncBaseDevice.js +1 -29
- package/dist/lib/vesyncFan.js +24 -58
- package/dist/lib/vesyncFanImpl.d.ts +0 -29
- package/dist/lib/vesyncFanImpl.js +55 -147
- package/package.json +1 -1
- package/src/lib/vesyncBaseDevice.ts +1 -33
- package/src/lib/vesyncFan.ts +24 -62
- package/src/lib/vesyncFanImpl.ts +52 -160
|
@@ -16,11 +16,6 @@ export declare abstract class VeSyncBaseDevice {
|
|
|
16
16
|
deviceCategory: string;
|
|
17
17
|
connectionStatus: string;
|
|
18
18
|
constructor(details: Record<string, any>, manager: VeSync);
|
|
19
|
-
/**
|
|
20
|
-
* Normalize device type to match configuration keys
|
|
21
|
-
* This handles variations in device type strings from the API
|
|
22
|
-
*/
|
|
23
|
-
protected normalizeDeviceType(deviceType: string): string;
|
|
24
19
|
/**
|
|
25
20
|
* Return formatted device info to stdout
|
|
26
21
|
*/
|
|
@@ -13,7 +13,7 @@ class VeSyncBaseDevice {
|
|
|
13
13
|
this.cid = details.cid || '';
|
|
14
14
|
this.deviceName = details.deviceName || '';
|
|
15
15
|
this.deviceStatus = details.deviceStatus || 'off';
|
|
16
|
-
this.deviceType =
|
|
16
|
+
this.deviceType = details.deviceType || '';
|
|
17
17
|
this.deviceRegion = details.deviceRegion || '';
|
|
18
18
|
this.uuid = details.uuid || '';
|
|
19
19
|
this.configModule = details.configModule || '';
|
|
@@ -21,34 +21,6 @@ class VeSyncBaseDevice {
|
|
|
21
21
|
this.deviceCategory = details.deviceCategory || '';
|
|
22
22
|
this.connectionStatus = details.connectionStatus || 'offline';
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Normalize device type to match configuration keys
|
|
26
|
-
* This handles variations in device type strings from the API
|
|
27
|
-
*/
|
|
28
|
-
normalizeDeviceType(deviceType) {
|
|
29
|
-
// Remove any region suffixes (e.g. -USA, -EU)
|
|
30
|
-
const baseType = deviceType.split('-')[0];
|
|
31
|
-
// Handle known prefix mappings
|
|
32
|
-
const prefixMap = {
|
|
33
|
-
'Core': 'Core',
|
|
34
|
-
'LAP': 'LAP',
|
|
35
|
-
'LTF': 'LTF',
|
|
36
|
-
'Classic': 'Classic',
|
|
37
|
-
'Dual': 'Dual',
|
|
38
|
-
'LUH': 'LUH',
|
|
39
|
-
'LEH': 'LEH',
|
|
40
|
-
'LV-PUR': 'LV-PUR',
|
|
41
|
-
'LV-RH': 'LV-RH'
|
|
42
|
-
};
|
|
43
|
-
// Check if the device type starts with any known prefix
|
|
44
|
-
for (const [prefix, normalized] of Object.entries(prefixMap)) {
|
|
45
|
-
if (deviceType.startsWith(prefix)) {
|
|
46
|
-
return deviceType; // Keep the full device type for exact matches
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// If no prefix match, return the base type
|
|
50
|
-
return baseType;
|
|
51
|
-
}
|
|
52
24
|
/**
|
|
53
25
|
* Return formatted device info to stdout
|
|
54
26
|
*/
|
package/dist/lib/vesyncFan.js
CHANGED
|
@@ -189,121 +189,98 @@ exports.fanConfig = {
|
|
|
189
189
|
// Classic Series
|
|
190
190
|
'Classic300S': {
|
|
191
191
|
module: 'VeSyncHumidifier',
|
|
192
|
-
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
193
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
192
|
+
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
194
193
|
},
|
|
195
194
|
'Classic200S': {
|
|
196
195
|
module: 'VeSyncHumidifier',
|
|
197
|
-
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
198
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
196
|
+
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
199
197
|
},
|
|
200
198
|
// Dual Series
|
|
201
199
|
'Dual200S': {
|
|
202
200
|
module: 'VeSyncHumidifier',
|
|
203
|
-
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
204
|
-
levels: [1, 2]
|
|
201
|
+
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
205
202
|
},
|
|
206
203
|
// LUH Series
|
|
207
204
|
'LUH-A601S-WUSB': {
|
|
208
205
|
module: 'VeSyncWarmHumidifier',
|
|
209
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
210
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
206
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
211
207
|
},
|
|
212
208
|
'LUH-A601S-AUSW': {
|
|
213
209
|
module: 'VeSyncWarmHumidifier',
|
|
214
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
215
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
210
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
216
211
|
},
|
|
217
212
|
'LUH-D301S-WUSR': {
|
|
218
213
|
module: 'VeSyncWarmHumidifier',
|
|
219
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
220
|
-
levels: [1, 2]
|
|
214
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
221
215
|
},
|
|
222
216
|
'LUH-D301S-WJP': {
|
|
223
217
|
module: 'VeSyncWarmHumidifier',
|
|
224
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
225
|
-
levels: [1, 2]
|
|
218
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
226
219
|
},
|
|
227
220
|
'LUH-D301S-WEU': {
|
|
228
221
|
module: 'VeSyncWarmHumidifier',
|
|
229
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
230
|
-
levels: [1, 2]
|
|
222
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
231
223
|
},
|
|
232
224
|
'LUH-A602S-WUSR': {
|
|
233
225
|
module: 'VeSyncWarmHumidifier',
|
|
234
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
235
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
226
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
236
227
|
},
|
|
237
228
|
'LUH-A602S-WUS': {
|
|
238
229
|
module: 'VeSyncWarmHumidifier',
|
|
239
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
240
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
230
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
241
231
|
},
|
|
242
232
|
'LUH-A602S-WEUR': {
|
|
243
233
|
module: 'VeSyncWarmHumidifier',
|
|
244
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
245
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
234
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
246
235
|
},
|
|
247
236
|
'LUH-A602S-WEU': {
|
|
248
237
|
module: 'VeSyncWarmHumidifier',
|
|
249
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
250
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
238
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
251
239
|
},
|
|
252
240
|
'LUH-A602S-WJP': {
|
|
253
241
|
module: 'VeSyncWarmHumidifier',
|
|
254
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
255
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
242
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
256
243
|
},
|
|
257
244
|
'LUH-A602S-WUSC': {
|
|
258
245
|
module: 'VeSyncWarmHumidifier',
|
|
259
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
260
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
246
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
261
247
|
},
|
|
262
248
|
'LUH-O451S-WEU': {
|
|
263
249
|
module: 'VeSyncWarmHumidifier',
|
|
264
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
265
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
250
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
266
251
|
},
|
|
267
252
|
'LUH-O451S-WUS': {
|
|
268
253
|
module: 'VeSyncWarmHumidifier',
|
|
269
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
270
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
254
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
271
255
|
},
|
|
272
256
|
'LUH-O451S-WUSR': {
|
|
273
257
|
module: 'VeSyncWarmHumidifier',
|
|
274
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
275
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
258
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
276
259
|
},
|
|
277
260
|
'LUH-O601S-WUS': {
|
|
278
261
|
module: 'VeSyncWarmHumidifier',
|
|
279
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
280
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
262
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
281
263
|
},
|
|
282
264
|
'LUH-O601S-KUS': {
|
|
283
265
|
module: 'VeSyncWarmHumidifier',
|
|
284
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
285
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
266
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
286
267
|
},
|
|
287
268
|
'LUH-M101S-WUS': {
|
|
288
269
|
module: 'VeSyncWarmHumidifier',
|
|
289
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
290
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
270
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
291
271
|
},
|
|
292
272
|
'LUH-M101S-WEUR': {
|
|
293
273
|
module: 'VeSyncWarmHumidifier',
|
|
294
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
295
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
274
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
296
275
|
},
|
|
297
276
|
// LEH Series
|
|
298
277
|
'LEH-S601S-WUS': {
|
|
299
278
|
module: 'VeSyncWarmHumidifier',
|
|
300
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
301
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
279
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
302
280
|
},
|
|
303
281
|
'LEH-S601S-WUSR': {
|
|
304
282
|
module: 'VeSyncWarmHumidifier',
|
|
305
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
306
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
283
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
307
284
|
},
|
|
308
285
|
// LV Series
|
|
309
286
|
'LV-PUR131S': {
|
|
@@ -328,22 +305,11 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
|
|
|
328
305
|
this.speed_dict = {};
|
|
329
306
|
this._timer = null;
|
|
330
307
|
this.details = details;
|
|
331
|
-
|
|
332
|
-
let config = exports.fanConfig[this.deviceType];
|
|
333
|
-
// If no exact match, try base type
|
|
334
|
-
if (!config) {
|
|
335
|
-
const baseType = this.deviceType.split('-')[0];
|
|
336
|
-
config = exports.fanConfig[baseType];
|
|
337
|
-
}
|
|
338
|
-
// If still no match, use default
|
|
339
|
-
this.config = config || {
|
|
308
|
+
this.config = exports.fanConfig[this.deviceType] || {
|
|
340
309
|
module: 'VeSyncAirBypass',
|
|
341
310
|
features: [],
|
|
342
311
|
levels: []
|
|
343
312
|
};
|
|
344
|
-
if (!config) {
|
|
345
|
-
logger_1.logger.debug(`No configuration found for device type: ${this.deviceType}`);
|
|
346
|
-
}
|
|
347
313
|
}
|
|
348
314
|
/**
|
|
349
315
|
* Check if feature is supported
|
|
@@ -91,40 +91,11 @@ export declare class VeSyncHumidifier extends VeSyncFan {
|
|
|
91
91
|
min: number;
|
|
92
92
|
max: number;
|
|
93
93
|
};
|
|
94
|
-
protected readonly states: readonly ["inactive", "idle", "humidifying"];
|
|
95
94
|
constructor(details: Record<string, any>, manager: VeSync);
|
|
96
|
-
/**
|
|
97
|
-
* Get current operational state
|
|
98
|
-
*/
|
|
99
|
-
get currentState(): string;
|
|
100
|
-
/**
|
|
101
|
-
* Get water level percentage
|
|
102
|
-
*/
|
|
103
|
-
get waterLevel(): number;
|
|
104
|
-
/**
|
|
105
|
-
* Get water tank status
|
|
106
|
-
*/
|
|
107
|
-
get waterTankLifted(): boolean;
|
|
108
|
-
/**
|
|
109
|
-
* Get oscillation state
|
|
110
|
-
*/
|
|
111
|
-
get oscillationEnabled(): boolean;
|
|
112
|
-
/**
|
|
113
|
-
* Set oscillation state
|
|
114
|
-
*/
|
|
115
|
-
setOscillation(enabled: boolean): Promise<boolean>;
|
|
116
95
|
/**
|
|
117
96
|
* Get device details
|
|
118
97
|
*/
|
|
119
98
|
getDetails(): Promise<Boolean>;
|
|
120
|
-
/**
|
|
121
|
-
* Display device info
|
|
122
|
-
*/
|
|
123
|
-
display(): void;
|
|
124
|
-
/**
|
|
125
|
-
* Return JSON details
|
|
126
|
-
*/
|
|
127
|
-
displayJSON(): string;
|
|
128
99
|
/**
|
|
129
100
|
* Turn device on
|
|
130
101
|
*/
|
|
@@ -26,7 +26,6 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
26
26
|
const body = {
|
|
27
27
|
...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
|
|
28
28
|
cid: this.cid,
|
|
29
|
-
deviceId: this.cid, // Add deviceId to match YAML spec
|
|
30
29
|
configModule: this.configModule,
|
|
31
30
|
payload: {
|
|
32
31
|
method,
|
|
@@ -211,13 +210,11 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
211
210
|
*/
|
|
212
211
|
async changeFanSpeed(speed) {
|
|
213
212
|
var _a;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// return false;
|
|
220
|
-
// }
|
|
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
|
+
}
|
|
221
218
|
logger_1.logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
|
|
222
219
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
223
220
|
...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
|
|
@@ -244,11 +241,10 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
244
241
|
* Set device mode
|
|
245
242
|
*/
|
|
246
243
|
async setMode(mode) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
logger_1.logger.
|
|
250
|
-
|
|
251
|
-
return false;
|
|
244
|
+
if (!this.modes.includes(mode)) {
|
|
245
|
+
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
246
|
+
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
247
|
+
throw new Error(error);
|
|
252
248
|
}
|
|
253
249
|
logger_1.logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
|
|
254
250
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
@@ -257,15 +253,15 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
257
253
|
configModule: this.configModule,
|
|
258
254
|
payload: {
|
|
259
255
|
data: {
|
|
260
|
-
|
|
256
|
+
mode
|
|
261
257
|
},
|
|
262
|
-
method: '
|
|
258
|
+
method: 'setPurifierMode',
|
|
263
259
|
source: 'APP'
|
|
264
260
|
}
|
|
265
261
|
}, helpers_1.Helpers.reqHeaderBypass());
|
|
266
262
|
const success = this.checkResponse([response, status], 'setMode');
|
|
267
263
|
if (!success) {
|
|
268
|
-
logger_1.logger.
|
|
264
|
+
logger_1.logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
|
|
269
265
|
}
|
|
270
266
|
return success;
|
|
271
267
|
}
|
|
@@ -492,7 +488,6 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
492
488
|
this.modes = ['auto', 'manual', 'sleep'];
|
|
493
489
|
this.displayModes = ['on', 'off'];
|
|
494
490
|
this.humidityRange = { min: 30, max: 80 };
|
|
495
|
-
this.states = ['inactive', 'idle', 'humidifying'];
|
|
496
491
|
// Set mist levels based on model
|
|
497
492
|
switch (this.deviceType) {
|
|
498
493
|
case 'Dual200S':
|
|
@@ -507,66 +502,6 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
507
502
|
}
|
|
508
503
|
logger_1.logger.debug(`Initialized VeSyncHumidifier device: ${this.deviceName}`);
|
|
509
504
|
}
|
|
510
|
-
/**
|
|
511
|
-
* Get current operational state
|
|
512
|
-
*/
|
|
513
|
-
get currentState() {
|
|
514
|
-
if (this.deviceStatus === 'off') {
|
|
515
|
-
return 'inactive';
|
|
516
|
-
}
|
|
517
|
-
if (this.details.water_lacks) {
|
|
518
|
-
return 'idle';
|
|
519
|
-
}
|
|
520
|
-
return 'humidifying';
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* Get water level percentage
|
|
524
|
-
*/
|
|
525
|
-
get waterLevel() {
|
|
526
|
-
var _a;
|
|
527
|
-
return (_a = this.details.water_level) !== null && _a !== void 0 ? _a : 0;
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Get water tank status
|
|
531
|
-
*/
|
|
532
|
-
get waterTankLifted() {
|
|
533
|
-
var _a;
|
|
534
|
-
return (_a = this.details.water_tank_lifted) !== null && _a !== void 0 ? _a : false;
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Get oscillation state
|
|
538
|
-
*/
|
|
539
|
-
get oscillationEnabled() {
|
|
540
|
-
var _a;
|
|
541
|
-
return (_a = this.details.oscillation_enabled) !== null && _a !== void 0 ? _a : false;
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Set oscillation state
|
|
545
|
-
*/
|
|
546
|
-
async setOscillation(enabled) {
|
|
547
|
-
if (!this.hasFeature('oscillation')) {
|
|
548
|
-
logger_1.logger.debug('Oscillation not supported');
|
|
549
|
-
return false;
|
|
550
|
-
}
|
|
551
|
-
logger_1.logger.debug(`Setting oscillation to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
|
|
552
|
-
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
553
|
-
...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
|
|
554
|
-
cid: this.cid,
|
|
555
|
-
configModule: this.configModule,
|
|
556
|
-
payload: {
|
|
557
|
-
data: {
|
|
558
|
-
oscillationEnabled: enabled
|
|
559
|
-
},
|
|
560
|
-
method: 'setSwing',
|
|
561
|
-
source: 'APP'
|
|
562
|
-
}
|
|
563
|
-
}, helpers_1.Helpers.reqHeaderBypass());
|
|
564
|
-
const success = this.checkResponse([response, status], 'setOscillation');
|
|
565
|
-
if (!success) {
|
|
566
|
-
logger_1.logger.debug('Error setting oscillation');
|
|
567
|
-
}
|
|
568
|
-
return success;
|
|
569
|
-
}
|
|
570
505
|
/**
|
|
571
506
|
* Get device details
|
|
572
507
|
*/
|
|
@@ -598,13 +533,12 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
598
533
|
humidity: result.humidity || 0,
|
|
599
534
|
mist_level: result.mistLevel || 0,
|
|
600
535
|
mist_virtual_level: result.virtualLevel || 0,
|
|
536
|
+
warm_level: result.warmLevel || 0,
|
|
601
537
|
water_lacks: result.waterLacks || false,
|
|
602
|
-
water_level: result.waterLevel || 0,
|
|
603
|
-
water_tank_lifted: result.waterTankLifted || false,
|
|
604
538
|
humidity_high: result.humidityHigh || false,
|
|
539
|
+
water_tank_lifted: result.waterTankLifted || false,
|
|
605
540
|
display: result.display || false,
|
|
606
541
|
automatic_stop: result.automaticStop || false,
|
|
607
|
-
oscillation_enabled: result.oscillationEnabled || false,
|
|
608
542
|
configuration: result.configuration || {},
|
|
609
543
|
connection_status: result.connectionStatus || null
|
|
610
544
|
};
|
|
@@ -612,34 +546,6 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
612
546
|
}
|
|
613
547
|
return success;
|
|
614
548
|
}
|
|
615
|
-
/**
|
|
616
|
-
* Display device info
|
|
617
|
-
*/
|
|
618
|
-
display() {
|
|
619
|
-
super.display();
|
|
620
|
-
const info = [
|
|
621
|
-
['Current State: ', this.currentState],
|
|
622
|
-
['Water Level: ', this.waterLevel, '%'],
|
|
623
|
-
['Water Tank: ', this.waterTankLifted ? 'Lifted' : 'Seated'],
|
|
624
|
-
['Oscillation: ', this.oscillationEnabled ? 'On' : 'Off']
|
|
625
|
-
];
|
|
626
|
-
for (const [key, value, unit = ''] of info) {
|
|
627
|
-
logger_1.logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Return JSON details
|
|
632
|
-
*/
|
|
633
|
-
displayJSON() {
|
|
634
|
-
const baseInfo = JSON.parse(super.displayJSON());
|
|
635
|
-
return JSON.stringify({
|
|
636
|
-
...baseInfo,
|
|
637
|
-
'Current State': this.currentState,
|
|
638
|
-
'Water Level': this.waterLevel,
|
|
639
|
-
'Water Tank': this.waterTankLifted ? 'Lifted' : 'Seated',
|
|
640
|
-
'Oscillation': this.oscillationEnabled ? 'On' : 'Off'
|
|
641
|
-
}, null, 4);
|
|
642
|
-
}
|
|
643
549
|
/**
|
|
644
550
|
* Turn device on
|
|
645
551
|
*/
|
|
@@ -704,9 +610,9 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
704
610
|
*/
|
|
705
611
|
async setMistLevel(level) {
|
|
706
612
|
if (!this.mistLevels.includes(level)) {
|
|
707
|
-
|
|
708
|
-
logger_1.logger.
|
|
709
|
-
|
|
613
|
+
const error = `Invalid mist level: ${level}. Must be one of: ${this.mistLevels.join(', ')}`;
|
|
614
|
+
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
615
|
+
throw new Error(error);
|
|
710
616
|
}
|
|
711
617
|
logger_1.logger.info(`Setting mist level to ${level} for device: ${this.deviceName}`);
|
|
712
618
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
@@ -715,9 +621,9 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
715
621
|
configModule: this.configModule,
|
|
716
622
|
payload: {
|
|
717
623
|
data: {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
624
|
+
id: 0,
|
|
625
|
+
level: level,
|
|
626
|
+
type: 'mist'
|
|
721
627
|
},
|
|
722
628
|
method: 'setVirtualLevel',
|
|
723
629
|
source: 'APP'
|
|
@@ -725,7 +631,7 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
725
631
|
}, helpers_1.Helpers.reqHeaderBypass());
|
|
726
632
|
const success = this.checkResponse([response, status], 'setMistLevel');
|
|
727
633
|
if (!success) {
|
|
728
|
-
logger_1.logger.
|
|
634
|
+
logger_1.logger.error(`Failed to set mist level to ${level} for device: ${this.deviceName}`);
|
|
729
635
|
}
|
|
730
636
|
return success;
|
|
731
637
|
}
|
|
@@ -740,11 +646,10 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
740
646
|
* Set device mode
|
|
741
647
|
*/
|
|
742
648
|
async setMode(mode) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
logger_1.logger.
|
|
746
|
-
|
|
747
|
-
return false;
|
|
649
|
+
if (!this.modes.includes(mode)) {
|
|
650
|
+
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
651
|
+
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
652
|
+
throw new Error(error);
|
|
748
653
|
}
|
|
749
654
|
logger_1.logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
|
|
750
655
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
@@ -753,7 +658,7 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
753
658
|
configModule: this.configModule,
|
|
754
659
|
payload: {
|
|
755
660
|
data: {
|
|
756
|
-
|
|
661
|
+
mode
|
|
757
662
|
},
|
|
758
663
|
method: 'setHumidifierMode',
|
|
759
664
|
source: 'APP'
|
|
@@ -761,7 +666,7 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
|
|
|
761
666
|
}, helpers_1.Helpers.reqHeaderBypass());
|
|
762
667
|
const success = this.checkResponse([response, status], 'setMode');
|
|
763
668
|
if (!success) {
|
|
764
|
-
logger_1.logger.
|
|
669
|
+
logger_1.logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
|
|
765
670
|
}
|
|
766
671
|
return success;
|
|
767
672
|
}
|
|
@@ -1011,11 +916,10 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1011
916
|
* Set device mode
|
|
1012
917
|
*/
|
|
1013
918
|
async setMode(mode) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
logger_1.logger.
|
|
1017
|
-
|
|
1018
|
-
return false;
|
|
919
|
+
if (!this.modes.includes(mode)) {
|
|
920
|
+
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
921
|
+
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
922
|
+
throw new Error(error);
|
|
1019
923
|
}
|
|
1020
924
|
logger_1.logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
|
|
1021
925
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
@@ -1024,15 +928,18 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1024
928
|
configModule: this.configModule,
|
|
1025
929
|
payload: {
|
|
1026
930
|
data: {
|
|
1027
|
-
|
|
931
|
+
mode
|
|
1028
932
|
},
|
|
1029
933
|
method: 'setHumidifierMode',
|
|
1030
934
|
source: 'APP'
|
|
1031
935
|
}
|
|
1032
936
|
}, helpers_1.Helpers.reqHeaderBypass());
|
|
1033
937
|
const success = this.checkResponse([response, status], 'setMode');
|
|
1034
|
-
if (
|
|
1035
|
-
|
|
938
|
+
if (success) {
|
|
939
|
+
this.details.mode = mode;
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
logger_1.logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
|
|
1036
943
|
}
|
|
1037
944
|
return success;
|
|
1038
945
|
}
|
|
@@ -1041,13 +948,14 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1041
948
|
*/
|
|
1042
949
|
async setWarmLevel(level) {
|
|
1043
950
|
if (!this.hasFeature('warm')) {
|
|
1044
|
-
|
|
1045
|
-
|
|
951
|
+
const error = 'Warm mist control not supported';
|
|
952
|
+
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
953
|
+
throw new Error(error);
|
|
1046
954
|
}
|
|
1047
955
|
if (!this.warmLevels.includes(level)) {
|
|
1048
|
-
|
|
1049
|
-
logger_1.logger.
|
|
1050
|
-
|
|
956
|
+
const error = `Invalid warm level: ${level}. Must be one of: ${this.warmLevels.join(', ')}`;
|
|
957
|
+
logger_1.logger.error(`${error} for device: ${this.deviceName}`);
|
|
958
|
+
throw new Error(error);
|
|
1051
959
|
}
|
|
1052
960
|
logger_1.logger.debug(`Setting warm level to ${level} for device: ${this.deviceName}`);
|
|
1053
961
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
@@ -1186,7 +1094,6 @@ class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1186
1094
|
const body = {
|
|
1187
1095
|
...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1188
1096
|
cid: this.cid,
|
|
1189
|
-
deviceId: this.cid, // Add deviceId to match YAML spec
|
|
1190
1097
|
configModule: this.configModule,
|
|
1191
1098
|
payload: {
|
|
1192
1099
|
method,
|
|
@@ -1304,11 +1211,12 @@ class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1304
1211
|
* Change fan speed
|
|
1305
1212
|
*/
|
|
1306
1213
|
async changeFanSpeed(speed) {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
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
|
+
}
|
|
1312
1220
|
logger_1.logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
|
|
1313
1221
|
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
|
|
1314
1222
|
...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
|
|
@@ -1594,7 +1502,6 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
|
1594
1502
|
const body = {
|
|
1595
1503
|
...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1596
1504
|
cid: this.cid,
|
|
1597
|
-
deviceId: this.cid, // Add deviceId to match YAML spec
|
|
1598
1505
|
configModule: this.configModule,
|
|
1599
1506
|
payload: {
|
|
1600
1507
|
method,
|
|
@@ -1712,11 +1619,12 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
|
1712
1619
|
* Change fan speed
|
|
1713
1620
|
*/
|
|
1714
1621
|
async changeFanSpeed(speed) {
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
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(', ')}`);
|
|
1626
|
+
return false;
|
|
1627
|
+
}
|
|
1720
1628
|
const [head, body] = this.buildApiDict('setLevel');
|
|
1721
1629
|
body.payload.data = {
|
|
1722
1630
|
levelIdx: 0,
|
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@ export abstract class VeSyncBaseDevice {
|
|
|
26
26
|
this.cid = details.cid || '';
|
|
27
27
|
this.deviceName = details.deviceName || '';
|
|
28
28
|
this.deviceStatus = details.deviceStatus || 'off';
|
|
29
|
-
this.deviceType =
|
|
29
|
+
this.deviceType = details.deviceType || '';
|
|
30
30
|
this.deviceRegion = details.deviceRegion || '';
|
|
31
31
|
this.uuid = details.uuid || '';
|
|
32
32
|
this.configModule = details.configModule || '';
|
|
@@ -35,38 +35,6 @@ export abstract class VeSyncBaseDevice {
|
|
|
35
35
|
this.connectionStatus = details.connectionStatus || 'offline';
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/**
|
|
39
|
-
* Normalize device type to match configuration keys
|
|
40
|
-
* This handles variations in device type strings from the API
|
|
41
|
-
*/
|
|
42
|
-
protected normalizeDeviceType(deviceType: string): string {
|
|
43
|
-
// Remove any region suffixes (e.g. -USA, -EU)
|
|
44
|
-
const baseType = deviceType.split('-')[0];
|
|
45
|
-
|
|
46
|
-
// Handle known prefix mappings
|
|
47
|
-
const prefixMap: Record<string, string> = {
|
|
48
|
-
'Core': 'Core',
|
|
49
|
-
'LAP': 'LAP',
|
|
50
|
-
'LTF': 'LTF',
|
|
51
|
-
'Classic': 'Classic',
|
|
52
|
-
'Dual': 'Dual',
|
|
53
|
-
'LUH': 'LUH',
|
|
54
|
-
'LEH': 'LEH',
|
|
55
|
-
'LV-PUR': 'LV-PUR',
|
|
56
|
-
'LV-RH': 'LV-RH'
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// Check if the device type starts with any known prefix
|
|
60
|
-
for (const [prefix, normalized] of Object.entries(prefixMap)) {
|
|
61
|
-
if (deviceType.startsWith(prefix)) {
|
|
62
|
-
return deviceType; // Keep the full device type for exact matches
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// If no prefix match, return the base type
|
|
67
|
-
return baseType;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
38
|
/**
|
|
71
39
|
* Return formatted device info to stdout
|
|
72
40
|
*/
|
package/src/lib/vesyncFan.ts
CHANGED
|
@@ -200,124 +200,101 @@ export const fanConfig: FanConfig = {
|
|
|
200
200
|
// Classic Series
|
|
201
201
|
'Classic300S': {
|
|
202
202
|
module: 'VeSyncHumidifier',
|
|
203
|
-
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
204
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
203
|
+
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
205
204
|
},
|
|
206
205
|
'Classic200S': {
|
|
207
206
|
module: 'VeSyncHumidifier',
|
|
208
|
-
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
209
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
207
|
+
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
210
208
|
},
|
|
211
209
|
|
|
212
210
|
// Dual Series
|
|
213
211
|
'Dual200S': {
|
|
214
212
|
module: 'VeSyncHumidifier',
|
|
215
|
-
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
216
|
-
levels: [1, 2]
|
|
213
|
+
features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
|
|
217
214
|
},
|
|
218
215
|
|
|
219
216
|
// LUH Series
|
|
220
217
|
'LUH-A601S-WUSB': {
|
|
221
218
|
module: 'VeSyncWarmHumidifier',
|
|
222
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
223
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
219
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
224
220
|
},
|
|
225
221
|
'LUH-A601S-AUSW': {
|
|
226
222
|
module: 'VeSyncWarmHumidifier',
|
|
227
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
228
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
223
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
229
224
|
},
|
|
230
225
|
'LUH-D301S-WUSR': {
|
|
231
226
|
module: 'VeSyncWarmHumidifier',
|
|
232
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
233
|
-
levels: [1, 2]
|
|
227
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
234
228
|
},
|
|
235
229
|
'LUH-D301S-WJP': {
|
|
236
230
|
module: 'VeSyncWarmHumidifier',
|
|
237
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
238
|
-
levels: [1, 2]
|
|
231
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
239
232
|
},
|
|
240
233
|
'LUH-D301S-WEU': {
|
|
241
234
|
module: 'VeSyncWarmHumidifier',
|
|
242
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
243
|
-
levels: [1, 2]
|
|
235
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
244
236
|
},
|
|
245
237
|
'LUH-A602S-WUSR': {
|
|
246
238
|
module: 'VeSyncWarmHumidifier',
|
|
247
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
248
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
239
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
249
240
|
},
|
|
250
241
|
'LUH-A602S-WUS': {
|
|
251
242
|
module: 'VeSyncWarmHumidifier',
|
|
252
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
253
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
243
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
254
244
|
},
|
|
255
245
|
'LUH-A602S-WEUR': {
|
|
256
246
|
module: 'VeSyncWarmHumidifier',
|
|
257
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
258
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
247
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
259
248
|
},
|
|
260
249
|
'LUH-A602S-WEU': {
|
|
261
250
|
module: 'VeSyncWarmHumidifier',
|
|
262
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
263
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
251
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
264
252
|
},
|
|
265
253
|
'LUH-A602S-WJP': {
|
|
266
254
|
module: 'VeSyncWarmHumidifier',
|
|
267
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
268
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
255
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
269
256
|
},
|
|
270
257
|
'LUH-A602S-WUSC': {
|
|
271
258
|
module: 'VeSyncWarmHumidifier',
|
|
272
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
273
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
259
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
274
260
|
},
|
|
275
261
|
'LUH-O451S-WEU': {
|
|
276
262
|
module: 'VeSyncWarmHumidifier',
|
|
277
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
278
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
263
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
279
264
|
},
|
|
280
265
|
'LUH-O451S-WUS': {
|
|
281
266
|
module: 'VeSyncWarmHumidifier',
|
|
282
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
283
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
267
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
284
268
|
},
|
|
285
269
|
'LUH-O451S-WUSR': {
|
|
286
270
|
module: 'VeSyncWarmHumidifier',
|
|
287
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
288
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
271
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
289
272
|
},
|
|
290
273
|
'LUH-O601S-WUS': {
|
|
291
274
|
module: 'VeSyncWarmHumidifier',
|
|
292
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
293
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
275
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
294
276
|
},
|
|
295
277
|
'LUH-O601S-KUS': {
|
|
296
278
|
module: 'VeSyncWarmHumidifier',
|
|
297
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
298
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
279
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
299
280
|
},
|
|
300
281
|
'LUH-M101S-WUS': {
|
|
301
282
|
module: 'VeSyncWarmHumidifier',
|
|
302
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
303
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
283
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
304
284
|
},
|
|
305
285
|
'LUH-M101S-WEUR': {
|
|
306
286
|
module: 'VeSyncWarmHumidifier',
|
|
307
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
308
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
287
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
|
|
309
288
|
},
|
|
310
289
|
|
|
311
290
|
// LEH Series
|
|
312
291
|
'LEH-S601S-WUS': {
|
|
313
292
|
module: 'VeSyncWarmHumidifier',
|
|
314
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
315
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
293
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
316
294
|
},
|
|
317
295
|
'LEH-S601S-WUSR': {
|
|
318
296
|
module: 'VeSyncWarmHumidifier',
|
|
319
|
-
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
320
|
-
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
297
|
+
features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
|
|
321
298
|
},
|
|
322
299
|
|
|
323
300
|
// LV Series
|
|
@@ -362,26 +339,11 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
362
339
|
constructor(details: Record<string, any>, manager: VeSync) {
|
|
363
340
|
super(details, manager);
|
|
364
341
|
this.details = details;
|
|
365
|
-
|
|
366
|
-
// Try exact match first
|
|
367
|
-
let config = fanConfig[this.deviceType];
|
|
368
|
-
|
|
369
|
-
// If no exact match, try base type
|
|
370
|
-
if (!config) {
|
|
371
|
-
const baseType = this.deviceType.split('-')[0];
|
|
372
|
-
config = fanConfig[baseType];
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// If still no match, use default
|
|
376
|
-
this.config = config || {
|
|
342
|
+
this.config = fanConfig[this.deviceType] || {
|
|
377
343
|
module: 'VeSyncAirBypass',
|
|
378
344
|
features: [],
|
|
379
345
|
levels: []
|
|
380
346
|
};
|
|
381
|
-
|
|
382
|
-
if (!config) {
|
|
383
|
-
logger.debug(`No configuration found for device type: ${this.deviceType}`);
|
|
384
|
-
}
|
|
385
347
|
}
|
|
386
348
|
|
|
387
349
|
/**
|
package/src/lib/vesyncFanImpl.ts
CHANGED
|
@@ -28,7 +28,6 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
28
28
|
const body = {
|
|
29
29
|
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
30
30
|
cid: this.cid,
|
|
31
|
-
deviceId: this.cid, // Add deviceId to match YAML spec
|
|
32
31
|
configModule: this.configModule,
|
|
33
32
|
payload: {
|
|
34
33
|
method,
|
|
@@ -258,14 +257,11 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
258
257
|
* Change fan speed
|
|
259
258
|
*/
|
|
260
259
|
async changeFanSpeed(speed: number): Promise<boolean> {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
// logger.debug(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')}`);
|
|
267
|
-
// return false;
|
|
268
|
-
// }
|
|
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;
|
|
264
|
+
}
|
|
269
265
|
|
|
270
266
|
logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
|
|
271
267
|
const [response, status] = await this.callApi(
|
|
@@ -300,11 +296,10 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
300
296
|
* Set device mode
|
|
301
297
|
*/
|
|
302
298
|
async setMode(mode: string): Promise<boolean> {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
logger.
|
|
306
|
-
|
|
307
|
-
return false;
|
|
299
|
+
if (!this.modes.includes(mode as any)) {
|
|
300
|
+
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
301
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
302
|
+
throw new Error(error);
|
|
308
303
|
}
|
|
309
304
|
|
|
310
305
|
logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
|
|
@@ -317,9 +312,9 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
317
312
|
configModule: this.configModule,
|
|
318
313
|
payload: {
|
|
319
314
|
data: {
|
|
320
|
-
|
|
315
|
+
mode
|
|
321
316
|
},
|
|
322
|
-
method: '
|
|
317
|
+
method: 'setPurifierMode',
|
|
323
318
|
source: 'APP'
|
|
324
319
|
}
|
|
325
320
|
},
|
|
@@ -328,7 +323,7 @@ export class VeSyncAirBypass extends VeSyncFan {
|
|
|
328
323
|
|
|
329
324
|
const success = this.checkResponse([response, status], 'setMode');
|
|
330
325
|
if (!success) {
|
|
331
|
-
logger.
|
|
326
|
+
logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
|
|
332
327
|
}
|
|
333
328
|
return success;
|
|
334
329
|
}
|
|
@@ -605,7 +600,6 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
605
600
|
protected readonly mistLevels: number[];
|
|
606
601
|
protected readonly displayModes = ['on', 'off'] as const;
|
|
607
602
|
protected readonly humidityRange = { min: 30, max: 80 };
|
|
608
|
-
protected readonly states = ['inactive', 'idle', 'humidifying'] as const;
|
|
609
603
|
|
|
610
604
|
constructor(details: Record<string, any>, manager: VeSync) {
|
|
611
605
|
super(details, manager);
|
|
@@ -624,75 +618,6 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
624
618
|
logger.debug(`Initialized VeSyncHumidifier device: ${this.deviceName}`);
|
|
625
619
|
}
|
|
626
620
|
|
|
627
|
-
/**
|
|
628
|
-
* Get current operational state
|
|
629
|
-
*/
|
|
630
|
-
get currentState(): string {
|
|
631
|
-
if (this.deviceStatus === 'off') {
|
|
632
|
-
return 'inactive';
|
|
633
|
-
}
|
|
634
|
-
if (this.details.water_lacks) {
|
|
635
|
-
return 'idle';
|
|
636
|
-
}
|
|
637
|
-
return 'humidifying';
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* Get water level percentage
|
|
642
|
-
*/
|
|
643
|
-
get waterLevel(): number {
|
|
644
|
-
return this.details.water_level ?? 0;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Get water tank status
|
|
649
|
-
*/
|
|
650
|
-
get waterTankLifted(): boolean {
|
|
651
|
-
return this.details.water_tank_lifted ?? false;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Get oscillation state
|
|
656
|
-
*/
|
|
657
|
-
get oscillationEnabled(): boolean {
|
|
658
|
-
return this.details.oscillation_enabled ?? false;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
/**
|
|
662
|
-
* Set oscillation state
|
|
663
|
-
*/
|
|
664
|
-
async setOscillation(enabled: boolean): Promise<boolean> {
|
|
665
|
-
if (!this.hasFeature('oscillation')) {
|
|
666
|
-
logger.debug('Oscillation not supported');
|
|
667
|
-
return false;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
logger.debug(`Setting oscillation to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
|
|
671
|
-
const [response, status] = await this.callApi(
|
|
672
|
-
'/cloud/v2/deviceManaged/bypassV2',
|
|
673
|
-
'post',
|
|
674
|
-
{
|
|
675
|
-
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
676
|
-
cid: this.cid,
|
|
677
|
-
configModule: this.configModule,
|
|
678
|
-
payload: {
|
|
679
|
-
data: {
|
|
680
|
-
oscillationEnabled: enabled
|
|
681
|
-
},
|
|
682
|
-
method: 'setSwing',
|
|
683
|
-
source: 'APP'
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
Helpers.reqHeaderBypass()
|
|
687
|
-
);
|
|
688
|
-
|
|
689
|
-
const success = this.checkResponse([response, status], 'setOscillation');
|
|
690
|
-
if (!success) {
|
|
691
|
-
logger.debug('Error setting oscillation');
|
|
692
|
-
}
|
|
693
|
-
return success;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
621
|
/**
|
|
697
622
|
* Get device details
|
|
698
623
|
*/
|
|
@@ -730,13 +655,12 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
730
655
|
humidity: result.humidity || 0,
|
|
731
656
|
mist_level: result.mistLevel || 0,
|
|
732
657
|
mist_virtual_level: result.virtualLevel || 0,
|
|
658
|
+
warm_level: result.warmLevel || 0,
|
|
733
659
|
water_lacks: result.waterLacks || false,
|
|
734
|
-
water_level: result.waterLevel || 0,
|
|
735
|
-
water_tank_lifted: result.waterTankLifted || false,
|
|
736
660
|
humidity_high: result.humidityHigh || false,
|
|
661
|
+
water_tank_lifted: result.waterTankLifted || false,
|
|
737
662
|
display: result.display || false,
|
|
738
663
|
automatic_stop: result.automaticStop || false,
|
|
739
|
-
oscillation_enabled: result.oscillationEnabled || false,
|
|
740
664
|
configuration: result.configuration || {},
|
|
741
665
|
connection_status: result.connectionStatus || null
|
|
742
666
|
};
|
|
@@ -745,37 +669,6 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
745
669
|
return success;
|
|
746
670
|
}
|
|
747
671
|
|
|
748
|
-
/**
|
|
749
|
-
* Display device info
|
|
750
|
-
*/
|
|
751
|
-
override display(): void {
|
|
752
|
-
super.display();
|
|
753
|
-
const info = [
|
|
754
|
-
['Current State: ', this.currentState],
|
|
755
|
-
['Water Level: ', this.waterLevel, '%'],
|
|
756
|
-
['Water Tank: ', this.waterTankLifted ? 'Lifted' : 'Seated'],
|
|
757
|
-
['Oscillation: ', this.oscillationEnabled ? 'On' : 'Off']
|
|
758
|
-
];
|
|
759
|
-
|
|
760
|
-
for (const [key, value, unit = ''] of info) {
|
|
761
|
-
logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
/**
|
|
766
|
-
* Return JSON details
|
|
767
|
-
*/
|
|
768
|
-
override displayJSON(): string {
|
|
769
|
-
const baseInfo = JSON.parse(super.displayJSON());
|
|
770
|
-
return JSON.stringify({
|
|
771
|
-
...baseInfo,
|
|
772
|
-
'Current State': this.currentState,
|
|
773
|
-
'Water Level': this.waterLevel,
|
|
774
|
-
'Water Tank': this.waterTankLifted ? 'Lifted' : 'Seated',
|
|
775
|
-
'Oscillation': this.oscillationEnabled ? 'On' : 'Off'
|
|
776
|
-
}, null, 4);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
672
|
/**
|
|
780
673
|
* Turn device on
|
|
781
674
|
*/
|
|
@@ -847,9 +740,9 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
847
740
|
*/
|
|
848
741
|
async setMistLevel(level: number): Promise<boolean> {
|
|
849
742
|
if (!this.mistLevels.includes(level)) {
|
|
850
|
-
|
|
851
|
-
logger.
|
|
852
|
-
|
|
743
|
+
const error = `Invalid mist level: ${level}. Must be one of: ${this.mistLevels.join(', ')}`;
|
|
744
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
745
|
+
throw new Error(error);
|
|
853
746
|
}
|
|
854
747
|
|
|
855
748
|
logger.info(`Setting mist level to ${level} for device: ${this.deviceName}`);
|
|
@@ -862,9 +755,9 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
862
755
|
configModule: this.configModule,
|
|
863
756
|
payload: {
|
|
864
757
|
data: {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
758
|
+
id: 0,
|
|
759
|
+
level: level,
|
|
760
|
+
type: 'mist'
|
|
868
761
|
},
|
|
869
762
|
method: 'setVirtualLevel',
|
|
870
763
|
source: 'APP'
|
|
@@ -875,7 +768,7 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
875
768
|
|
|
876
769
|
const success = this.checkResponse([response, status], 'setMistLevel');
|
|
877
770
|
if (!success) {
|
|
878
|
-
logger.
|
|
771
|
+
logger.error(`Failed to set mist level to ${level} for device: ${this.deviceName}`);
|
|
879
772
|
}
|
|
880
773
|
return success;
|
|
881
774
|
}
|
|
@@ -892,11 +785,10 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
892
785
|
* Set device mode
|
|
893
786
|
*/
|
|
894
787
|
async setMode(mode: string): Promise<boolean> {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
logger.
|
|
898
|
-
|
|
899
|
-
return false;
|
|
788
|
+
if (!this.modes.includes(mode as any)) {
|
|
789
|
+
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
790
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
791
|
+
throw new Error(error);
|
|
900
792
|
}
|
|
901
793
|
|
|
902
794
|
logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
|
|
@@ -909,7 +801,7 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
909
801
|
configModule: this.configModule,
|
|
910
802
|
payload: {
|
|
911
803
|
data: {
|
|
912
|
-
|
|
804
|
+
mode
|
|
913
805
|
},
|
|
914
806
|
method: 'setHumidifierMode',
|
|
915
807
|
source: 'APP'
|
|
@@ -920,7 +812,7 @@ export class VeSyncHumidifier extends VeSyncFan {
|
|
|
920
812
|
|
|
921
813
|
const success = this.checkResponse([response, status], 'setMode');
|
|
922
814
|
if (!success) {
|
|
923
|
-
logger.
|
|
815
|
+
logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
|
|
924
816
|
}
|
|
925
817
|
return success;
|
|
926
818
|
}
|
|
@@ -1225,11 +1117,10 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1225
1117
|
* Set device mode
|
|
1226
1118
|
*/
|
|
1227
1119
|
async setMode(mode: string): Promise<boolean> {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
logger.
|
|
1231
|
-
|
|
1232
|
-
return false;
|
|
1120
|
+
if (!this.modes.includes(mode as any)) {
|
|
1121
|
+
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
1122
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
1123
|
+
throw new Error(error);
|
|
1233
1124
|
}
|
|
1234
1125
|
|
|
1235
1126
|
logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
|
|
@@ -1242,7 +1133,7 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1242
1133
|
configModule: this.configModule,
|
|
1243
1134
|
payload: {
|
|
1244
1135
|
data: {
|
|
1245
|
-
|
|
1136
|
+
mode
|
|
1246
1137
|
},
|
|
1247
1138
|
method: 'setHumidifierMode',
|
|
1248
1139
|
source: 'APP'
|
|
@@ -1252,8 +1143,10 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1252
1143
|
);
|
|
1253
1144
|
|
|
1254
1145
|
const success = this.checkResponse([response, status], 'setMode');
|
|
1255
|
-
if (
|
|
1256
|
-
|
|
1146
|
+
if (success) {
|
|
1147
|
+
this.details.mode = mode;
|
|
1148
|
+
} else {
|
|
1149
|
+
logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
|
|
1257
1150
|
}
|
|
1258
1151
|
return success;
|
|
1259
1152
|
}
|
|
@@ -1263,14 +1156,15 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1263
1156
|
*/
|
|
1264
1157
|
async setWarmLevel(level: number): Promise<boolean> {
|
|
1265
1158
|
if (!this.hasFeature('warm')) {
|
|
1266
|
-
|
|
1267
|
-
|
|
1159
|
+
const error = 'Warm mist control not supported';
|
|
1160
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
1161
|
+
throw new Error(error);
|
|
1268
1162
|
}
|
|
1269
1163
|
|
|
1270
1164
|
if (!this.warmLevels.includes(level)) {
|
|
1271
|
-
|
|
1272
|
-
logger.
|
|
1273
|
-
|
|
1165
|
+
const error = `Invalid warm level: ${level}. Must be one of: ${this.warmLevels.join(', ')}`;
|
|
1166
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
1167
|
+
throw new Error(error);
|
|
1274
1168
|
}
|
|
1275
1169
|
|
|
1276
1170
|
logger.debug(`Setting warm level to ${level} for device: ${this.deviceName}`);
|
|
@@ -1436,7 +1330,6 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1436
1330
|
const body = {
|
|
1437
1331
|
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1438
1332
|
cid: this.cid,
|
|
1439
|
-
deviceId: this.cid, // Add deviceId to match YAML spec
|
|
1440
1333
|
configModule: this.configModule,
|
|
1441
1334
|
payload: {
|
|
1442
1335
|
method,
|
|
@@ -1576,11 +1469,11 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
|
1576
1469
|
* Change fan speed
|
|
1577
1470
|
*/
|
|
1578
1471
|
async changeFanSpeed(speed: number): Promise<boolean> {
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
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;
|
|
1476
|
+
}
|
|
1584
1477
|
|
|
1585
1478
|
logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
|
|
1586
1479
|
const [response, status] = await this.callApi(
|
|
@@ -1957,7 +1850,6 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
|
1957
1850
|
const body = {
|
|
1958
1851
|
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1959
1852
|
cid: this.cid,
|
|
1960
|
-
deviceId: this.cid, // Add deviceId to match YAML spec
|
|
1961
1853
|
configModule: this.configModule,
|
|
1962
1854
|
payload: {
|
|
1963
1855
|
method,
|
|
@@ -2106,11 +1998,11 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
|
2106
1998
|
* Change fan speed
|
|
2107
1999
|
*/
|
|
2108
2000
|
async changeFanSpeed(speed: number): Promise<boolean> {
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2001
|
+
const speeds = this.config.levels ?? [];
|
|
2002
|
+
if (!speeds.includes(speed)) {
|
|
2003
|
+
logger.debug(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')}`);
|
|
2004
|
+
return false;
|
|
2005
|
+
}
|
|
2114
2006
|
|
|
2115
2007
|
const [head, body] = this.buildApiDict('setLevel');
|
|
2116
2008
|
body.payload.data = {
|