tsvesync 1.0.71 → 1.0.73

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.
@@ -0,0 +1,360 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VeSyncSuperior6000S = void 0;
4
+ const vesyncFan_1 = require("../vesyncFan");
5
+ const helpers_1 = require("../helpers");
6
+ const logger_1 = require("../logger");
7
+ /**
8
+ * VeSync Superior 6000S Humidifier Class
9
+ * Implementation based on PyVeSync's VeSyncSuperior6000S class
10
+ */
11
+ class VeSyncSuperior6000S extends vesyncFan_1.VeSyncFan {
12
+ constructor(details, manager) {
13
+ super(details, manager);
14
+ this.modes = ['auto', 'manual', 'sleep', 'humidity'];
15
+ this.mistLevels = [1, 2, 3, 4, 5, 6, 7, 8, 9];
16
+ this.humidityRange = { min: 30, max: 80 };
17
+ this._apiModes = [
18
+ 'getHumidifierStatus',
19
+ 'setSwitch',
20
+ 'setVirtualLevel',
21
+ 'setTargetHumidity',
22
+ 'setHumidityMode',
23
+ 'setDisplay',
24
+ 'setDryingMode',
25
+ ];
26
+ // Initialize details object
27
+ this.details = {
28
+ mode: '',
29
+ speed: 0,
30
+ filter_life: 0,
31
+ screen_status: 'off',
32
+ child_lock: false,
33
+ humidity: 0,
34
+ target_humidity: 0,
35
+ mist_level: 0,
36
+ mist_virtual_level: 0,
37
+ water_lacks: false,
38
+ water_tank_lifted: false,
39
+ display: false,
40
+ filter_life_percentage: 0,
41
+ temperature: 0,
42
+ drying_mode: {},
43
+ configuration: {},
44
+ connection_status: null
45
+ };
46
+ // Update config for Superior 6000S
47
+ this.config = {
48
+ module: 'VeSyncAirBypass', // This doesn't matter as we're implementing directly
49
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode', 'drying'],
50
+ levels: this.mistLevels
51
+ };
52
+ logger_1.logger.debug(`Initialized VeSyncSuperior6000S device: ${this.deviceName}`);
53
+ }
54
+ /**
55
+ * Build API dictionary for humidifier
56
+ */
57
+ buildApiDict(method) {
58
+ if (!this._apiModes.includes(method)) {
59
+ logger_1.logger.debug(`Invalid mode - ${method}`);
60
+ throw new Error(`Invalid mode - ${method}`);
61
+ }
62
+ const head = helpers_1.Helpers.reqHeaderBypass();
63
+ const body = {
64
+ ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
65
+ cid: this.cid,
66
+ configModule: this.configModule,
67
+ payload: {
68
+ method: method,
69
+ source: 'APP',
70
+ data: {}
71
+ }
72
+ };
73
+ return [head, body];
74
+ }
75
+ /**
76
+ * Get device details
77
+ */
78
+ async getDetails() {
79
+ var _a;
80
+ logger_1.logger.debug(`Getting details for device: ${this.deviceName}`);
81
+ const [head, body] = this.buildApiDict('getHumidifierStatus');
82
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
83
+ const success = this.checkResponse([response, status], 'getDetails');
84
+ if (success && ((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.result)) {
85
+ const result = response.result.result;
86
+ // Update device status based on powerSwitch field
87
+ this.deviceStatus = result.powerSwitch === 1 ? 'on' : 'off';
88
+ // Build details object with Superior 6000S specific fields
89
+ this.details = {
90
+ mode: result.workMode === 'autoPro' ? 'auto' : result.workMode || '',
91
+ humidity: result.humidity || 0,
92
+ target_humidity: result.targetHumidity || 0,
93
+ mist_level: result.mistLevel || 0,
94
+ mist_virtual_level: result.virtualLevel || 0,
95
+ water_lacks: result.waterLacksState || false,
96
+ water_tank_lifted: result.waterTankLifted || false,
97
+ display: result.screenSwitch === 1,
98
+ filter_life_percentage: result.filterLifePercent || 0,
99
+ temperature: result.temperature || 0,
100
+ drying_mode: result.dryingMode || {},
101
+ configuration: result.configuration || {},
102
+ connection_status: result.connectionStatus || null
103
+ };
104
+ logger_1.logger.debug(`Successfully got details for device: ${this.deviceName}`);
105
+ }
106
+ return success;
107
+ }
108
+ /**
109
+ * Turn device on
110
+ */
111
+ async turnOn() {
112
+ return this.toggleSwitch(true);
113
+ }
114
+ /**
115
+ * Turn device off
116
+ */
117
+ async turnOff() {
118
+ return this.toggleSwitch(false);
119
+ }
120
+ /**
121
+ * Toggle device power
122
+ */
123
+ async toggleSwitch(enabled) {
124
+ logger_1.logger.info(`Setting device power to ${enabled ? 'on' : 'off'}: ${this.deviceName}`);
125
+ const [head, body] = this.buildApiDict('setSwitch');
126
+ body.payload.data = {
127
+ powerSwitch: enabled ? 1 : 0,
128
+ switchIdx: 0
129
+ };
130
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
131
+ const success = this.checkResponse([response, status], 'toggleSwitch');
132
+ if (success) {
133
+ this.deviceStatus = enabled ? 'on' : 'off';
134
+ return true;
135
+ }
136
+ logger_1.logger.error(`Failed to set device power to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
137
+ return false;
138
+ }
139
+ /**
140
+ * Set mist level
141
+ */
142
+ async setMistLevel(level) {
143
+ if (!this.mistLevels.includes(level)) {
144
+ const error = `Invalid mist level: ${level}. Must be one of: ${this.mistLevels.join(', ')}`;
145
+ logger_1.logger.error(`${error} for device: ${this.deviceName}`);
146
+ throw new Error(error);
147
+ }
148
+ logger_1.logger.info(`Setting mist level to ${level} for device: ${this.deviceName}`);
149
+ const [head, body] = this.buildApiDict('setVirtualLevel');
150
+ body.payload.data = {
151
+ levelIdx: 0,
152
+ virtualLevel: level,
153
+ levelType: 'mist'
154
+ };
155
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
156
+ const success = this.checkResponse([response, status], 'setMistLevel');
157
+ if (!success) {
158
+ logger_1.logger.error(`Failed to set mist level to ${level} for device: ${this.deviceName}`);
159
+ }
160
+ return success;
161
+ }
162
+ /**
163
+ * Change fan speed - Implemented to satisfy interface but redirects to setMistLevel
164
+ */
165
+ async changeFanSpeed(speed) {
166
+ logger_1.logger.debug(`Redirecting fan speed change to mist level for device: ${this.deviceName}`);
167
+ return this.setMistLevel(speed);
168
+ }
169
+ /**
170
+ * Set device mode
171
+ */
172
+ async setMode(mode) {
173
+ if (!this.modes.includes(mode)) {
174
+ const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
175
+ logger_1.logger.error(`${error} for device: ${this.deviceName}`);
176
+ throw new Error(error);
177
+ }
178
+ logger_1.logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
179
+ const [head, body] = this.buildApiDict('setHumidityMode');
180
+ body.payload.data = {
181
+ workMode: mode === 'auto' ? 'autoPro' : mode
182
+ };
183
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
184
+ const success = this.checkResponse([response, status], 'setMode');
185
+ if (success) {
186
+ this.details.mode = mode;
187
+ }
188
+ else {
189
+ logger_1.logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
190
+ }
191
+ return success;
192
+ }
193
+ /**
194
+ * Set target humidity
195
+ */
196
+ async setHumidity(humidity) {
197
+ if (humidity < this.humidityRange.min || humidity > this.humidityRange.max) {
198
+ const error = `Invalid humidity: ${humidity}. Must be between ${this.humidityRange.min} and ${this.humidityRange.max}`;
199
+ logger_1.logger.error(`${error} for device: ${this.deviceName}`);
200
+ throw new Error(error);
201
+ }
202
+ logger_1.logger.debug(`Setting target humidity to ${humidity}% for device: ${this.deviceName}`);
203
+ const [head, body] = this.buildApiDict('setTargetHumidity');
204
+ body.payload.data = {
205
+ targetHumidity: humidity
206
+ };
207
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
208
+ const success = this.checkResponse([response, status], 'setHumidity');
209
+ if (!success) {
210
+ logger_1.logger.error(`Failed to set target humidity to ${humidity}% for device: ${this.deviceName}`);
211
+ }
212
+ return success;
213
+ }
214
+ /**
215
+ * Set display status
216
+ */
217
+ async setDisplay(enabled) {
218
+ logger_1.logger.debug(`Setting display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
219
+ const [head, body] = this.buildApiDict('setDisplay');
220
+ body.payload.data = {
221
+ screenSwitch: enabled ? 1 : 0
222
+ };
223
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
224
+ const success = this.checkResponse([response, status], 'setDisplay');
225
+ if (!success) {
226
+ logger_1.logger.error(`Failed to set display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
227
+ }
228
+ return success;
229
+ }
230
+ /**
231
+ * Set drying mode enabled
232
+ */
233
+ async setDryingModeEnabled(enabled) {
234
+ logger_1.logger.debug(`Setting drying mode to ${enabled ? 'enabled' : 'disabled'} for device: ${this.deviceName}`);
235
+ const [head, body] = this.buildApiDict('setDryingMode');
236
+ body.payload.data = {
237
+ autoDryingSwitch: enabled ? 1 : 0
238
+ };
239
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
240
+ const success = this.checkResponse([response, status], 'setDryingModeEnabled');
241
+ if (!success) {
242
+ logger_1.logger.error(`Failed to set drying mode to ${enabled ? 'enabled' : 'disabled'} for device: ${this.deviceName}`);
243
+ }
244
+ return success;
245
+ }
246
+ /**
247
+ * Turn on display
248
+ */
249
+ async turnOnDisplay() {
250
+ logger_1.logger.debug(`Turning on display for device: ${this.deviceName}`);
251
+ return this.setDisplay(true);
252
+ }
253
+ /**
254
+ * Turn off display
255
+ */
256
+ async turnOffDisplay() {
257
+ logger_1.logger.debug(`Turning off display for device: ${this.deviceName}`);
258
+ return this.setDisplay(false);
259
+ }
260
+ /**
261
+ * Set auto mode
262
+ */
263
+ async setAutoMode() {
264
+ logger_1.logger.debug(`Setting auto mode for device: ${this.deviceName}`);
265
+ return this.setMode('auto');
266
+ }
267
+ /**
268
+ * Set manual mode
269
+ */
270
+ async setManualMode() {
271
+ logger_1.logger.debug(`Setting manual mode for device: ${this.deviceName}`);
272
+ return this.setMode('manual');
273
+ }
274
+ /**
275
+ * Get current humidity
276
+ * Provides access to the current humidity reading
277
+ */
278
+ get currentHumidity() {
279
+ return this.details.humidity || 0;
280
+ }
281
+ /**
282
+ * Get target humidity
283
+ * Provides access to the target humidity setting
284
+ */
285
+ get targetHumidity() {
286
+ return this.details.target_humidity || 0;
287
+ }
288
+ /**
289
+ * Get current temperature
290
+ * Provides access to the current temperature reading
291
+ */
292
+ get temperature() {
293
+ return this.details.temperature || 0;
294
+ }
295
+ /**
296
+ * Check if water tank is empty
297
+ * Returns true if water tank is empty and needs to be refilled
298
+ */
299
+ get waterLacks() {
300
+ return this.details.water_lacks || false;
301
+ }
302
+ /**
303
+ * Check if water tank is lifted
304
+ * Returns true if water tank is lifted/removed from the device
305
+ */
306
+ get waterTankLifted() {
307
+ return this.details.water_tank_lifted || false;
308
+ }
309
+ /**
310
+ * Get filter life percentage
311
+ * Returns the percentage of filter life remaining
312
+ */
313
+ get filterLifePercentage() {
314
+ return this.details.filter_life_percentage || 0;
315
+ }
316
+ /**
317
+ * Check if drying mode is enabled
318
+ * Returns true if drying mode is enabled
319
+ */
320
+ get dryingModeEnabled() {
321
+ var _a;
322
+ const enabled = (_a = this.details.drying_mode) === null || _a === void 0 ? void 0 : _a.autoDryingSwitch;
323
+ return enabled === 1;
324
+ }
325
+ /**
326
+ * Get drying mode state
327
+ * Returns 'on' if drying mode is active, 'off' if inactive, null if unknown
328
+ */
329
+ get dryingModeState() {
330
+ var _a;
331
+ const state = (_a = this.details.drying_mode) === null || _a === void 0 ? void 0 : _a.dryingState;
332
+ if (state === 1)
333
+ return 'on';
334
+ if (state === 2)
335
+ return 'off';
336
+ return null;
337
+ }
338
+ /**
339
+ * Get drying mode level
340
+ * Returns 'low' if level is 1, 'high' if level is 2, null if unknown
341
+ */
342
+ get dryingModeLevel() {
343
+ var _a;
344
+ const level = (_a = this.details.drying_mode) === null || _a === void 0 ? void 0 : _a.dryingLevel;
345
+ if (level === 1)
346
+ return 'low';
347
+ if (level === 2)
348
+ return 'high';
349
+ return null;
350
+ }
351
+ /**
352
+ * Get drying mode seconds remaining
353
+ * Returns the number of seconds remaining in drying mode
354
+ */
355
+ get dryingModeSecondsRemaining() {
356
+ var _a;
357
+ return ((_a = this.details.drying_mode) === null || _a === void 0 ? void 0 : _a.dryingRemain) || 0;
358
+ }
359
+ }
360
+ exports.VeSyncSuperior6000S = VeSyncSuperior6000S;
@@ -5,7 +5,7 @@ import { VeSyncBaseDevice } from './vesyncBaseDevice';
5
5
  import { VeSync } from './vesync';
6
6
  interface FanConfig {
7
7
  [key: string]: {
8
- module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2';
8
+ module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S';
9
9
  features: string[];
10
10
  levels?: number[];
11
11
  };
@@ -35,7 +35,7 @@ export declare abstract class VeSyncFan extends VeSyncBaseDevice {
35
35
  action: string;
36
36
  } | null;
37
37
  protected config: {
38
- module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2';
38
+ module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan' | 'VeSyncAirBaseV2' | 'VeSyncSuperior6000S' | 'VeSyncHumid1000S' | 'VeSyncHumid200S';
39
39
  features: string[];
40
40
  levels?: number[];
41
41
  };
@@ -44,6 +44,14 @@ export declare abstract class VeSyncFan extends VeSyncBaseDevice {
44
44
  * Check if feature is supported
45
45
  */
46
46
  hasFeature(feature: string): boolean;
47
+ /**
48
+ * Check if feature is supported in current mode
49
+ */
50
+ isFeatureSupportedInCurrentMode(feature: string): boolean;
51
+ /**
52
+ * Get maximum fan speed level
53
+ */
54
+ getMaxFanSpeed(): number;
47
55
  /**
48
56
  * Get current mode
49
57
  */
@@ -192,8 +192,9 @@ exports.fanConfig = {
192
192
  features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
193
193
  },
194
194
  'Classic200S': {
195
- module: 'VeSyncHumidifier',
196
- features: ['display', 'humidity', 'mist', 'timer', 'auto_mode']
195
+ module: 'VeSyncHumid200S',
196
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode'],
197
+ levels: [1, 2, 3]
197
198
  },
198
199
  // Dual Series
199
200
  'Dual200S': {
@@ -266,21 +267,25 @@ exports.fanConfig = {
266
267
  features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
267
268
  },
268
269
  'LUH-M101S-WUS': {
269
- module: 'VeSyncWarmHumidifier',
270
- features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
270
+ module: 'VeSyncHumid1000S',
271
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode', 'night_light'],
272
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
271
273
  },
272
274
  'LUH-M101S-WEUR': {
273
- module: 'VeSyncWarmHumidifier',
274
- features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
275
+ module: 'VeSyncHumid1000S',
276
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode', 'night_light'],
277
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
275
278
  },
276
- // LEH Series
279
+ // LEH Series - Superior 6000S
277
280
  'LEH-S601S-WUS': {
278
- module: 'VeSyncWarmHumidifier',
279
- features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
281
+ module: 'VeSyncSuperior6000S',
282
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode', 'drying', 'temperature'],
283
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
280
284
  },
281
285
  'LEH-S601S-WUSR': {
282
- module: 'VeSyncWarmHumidifier',
283
- features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode', 'drying']
286
+ module: 'VeSyncSuperior6000S',
287
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode', 'drying', 'temperature'],
288
+ levels: [1, 2, 3, 4, 5, 6, 7, 8, 9]
284
289
  },
285
290
  // LV Series
286
291
  'LV-PUR131S': {
@@ -317,6 +322,40 @@ class VeSyncFan extends vesyncBaseDevice_1.VeSyncBaseDevice {
317
322
  hasFeature(feature) {
318
323
  return this.config.features.includes(feature);
319
324
  }
325
+ /**
326
+ * Check if feature is supported in current mode
327
+ */
328
+ isFeatureSupportedInCurrentMode(feature) {
329
+ // First check if the feature is supported at all
330
+ if (!this.hasFeature(feature)) {
331
+ return false;
332
+ }
333
+ // Check mode-specific restrictions
334
+ const currentMode = this.mode;
335
+ // Some features are not supported in sleep mode
336
+ if (currentMode === 'sleep') {
337
+ // Display and child lock are not supported in sleep mode
338
+ if (feature === 'display' || feature === 'child_lock') {
339
+ return false;
340
+ }
341
+ }
342
+ // Fan speed is only supported in manual mode for some devices
343
+ if (feature === 'fan_speed' && this.deviceType.startsWith('LV-')) {
344
+ return currentMode === 'manual';
345
+ }
346
+ // Auto mode is not supported on Core200S
347
+ if (feature === 'auto_mode' && this.deviceType.includes('Core200S')) {
348
+ return false;
349
+ }
350
+ // Feature is supported in current mode
351
+ return true;
352
+ }
353
+ /**
354
+ * Get maximum fan speed level
355
+ */
356
+ getMaxFanSpeed() {
357
+ return this.config.levels ? Math.max(...this.config.levels) : 3;
358
+ }
320
359
  /**
321
360
  * Get current mode
322
361
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.71",
3
+ "version": "1.0.73",
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",
@@ -388,6 +388,13 @@ export class VeSyncAirBypass extends VeSyncFan {
388
388
  }
389
389
  }
390
390
 
391
+ // Special handling for Core200S auto mode
392
+ if (this.deviceType.includes('Core200S') && mode === 'auto') {
393
+ // Core200S doesn't support auto mode, so we'll set it to manual mode
394
+ logger.warn(`Auto mode not supported for ${this.deviceType}, using manual mode instead`);
395
+ return this.setMode('manual');
396
+ }
397
+
391
398
  // Standard implementation for other devices
392
399
  const [response, status] = await this.callApi(
393
400
  '/cloud/v2/deviceManaged/bypassV2',
@@ -408,10 +415,13 @@ export class VeSyncAirBypass extends VeSyncFan {
408
415
  );
409
416
 
410
417
  const success = this.checkResponse([response, status], 'setMode');
411
- if (!success) {
418
+ if (success) {
419
+ this.details.mode = mode;
420
+ return true;
421
+ } else {
412
422
  logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
423
+ return false;
413
424
  }
414
- return success;
415
425
  }
416
426
 
417
427
  /**
@@ -424,6 +434,11 @@ export class VeSyncAirBypass extends VeSyncFan {
424
434
  throw new Error(error);
425
435
  }
426
436
 
437
+ // Check if device is in sleep mode - display control may not work in sleep mode
438
+ if (this.details.mode === 'sleep') {
439
+ logger.warn(`Device ${this.deviceName} is in sleep mode, display control may not work`);
440
+ }
441
+
427
442
  logger.debug(`Setting display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
428
443
 
429
444
  // Special handling for LV series
@@ -479,12 +494,20 @@ export class VeSyncAirBypass extends VeSyncFan {
479
494
  );
480
495
 
481
496
  const success = this.checkResponse([response, status], 'setDisplay');
497
+
498
+ // Check for error code 11018000 (operation not supported in current mode)
499
+ if (response?.result?.code === 11018000) {
500
+ logger.warn(`Display control not supported in current mode for device: ${this.deviceName}`);
501
+ return false;
502
+ }
503
+
482
504
  if (success) {
483
505
  this.details.screenStatus = enabled ? 'on' : 'off';
506
+ return true;
484
507
  } else {
485
508
  logger.error(`Failed to set display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
509
+ return false;
486
510
  }
487
- return success;
488
511
  }
489
512
 
490
513
  /**
@@ -497,6 +520,11 @@ export class VeSyncAirBypass extends VeSyncFan {
497
520
  throw new Error(error);
498
521
  }
499
522
 
523
+ // Check if device is in sleep mode - child lock may not work in sleep mode
524
+ if (this.details.mode === 'sleep') {
525
+ logger.warn(`Device ${this.deviceName} is in sleep mode, child lock control may not work`);
526
+ }
527
+
500
528
  logger.debug(`Setting child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
501
529
 
502
530
  // Special handling for LV series
@@ -527,6 +555,11 @@ export class VeSyncAirBypass extends VeSyncFan {
527
555
  this.details.childLock = enabled;
528
556
  return true;
529
557
  } else {
558
+ // Check for error code 11000000 (feature not supported)
559
+ if (response?.code === 11000000) {
560
+ logger.warn(`Child lock control not supported via API for device: ${this.deviceName}`);
561
+ return false;
562
+ }
530
563
  logger.error(`Failed to set child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
531
564
  return false;
532
565
  }
@@ -552,12 +585,20 @@ export class VeSyncAirBypass extends VeSyncFan {
552
585
  );
553
586
 
554
587
  const success = this.checkResponse([response, status], 'setChildLock');
588
+
589
+ // Check for error code 11000000 (feature not supported)
590
+ if (response?.code === 11000000 || (response?.result?.code === 11000000)) {
591
+ logger.warn(`Child lock control not supported via API for device: ${this.deviceName}`);
592
+ return false;
593
+ }
594
+
555
595
  if (success) {
556
- this.details.child_lock = enabled;
596
+ this.details.childLock = enabled;
597
+ return true;
557
598
  } else {
558
599
  logger.error(`Failed to set child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
600
+ return false;
559
601
  }
560
- return success;
561
602
  }
562
603
 
563
604
  /**
@@ -597,10 +638,13 @@ export class VeSyncAirBypass extends VeSyncFan {
597
638
  );
598
639
 
599
640
  const success = this.checkResponse([response, status], 'setTimer');
600
- if (!success) {
641
+ if (success) {
642
+ this.timer = { duration: hours * 3600, action: 'off' };
643
+ return true;
644
+ } else {
601
645
  logger.error(`Failed to set timer to ${hours} hours for device: ${this.deviceName}`);
646
+ return false;
602
647
  }
603
- return success;
604
648
  }
605
649
 
606
650
  // Standard implementation for other devices
@@ -624,10 +668,20 @@ export class VeSyncAirBypass extends VeSyncFan {
624
668
  );
625
669
 
626
670
  const success = this.checkResponse([response, status], 'setTimer');
627
- if (!success) {
671
+
672
+ // Check for successful response with timer ID
673
+ if (success && response?.result?.result?.id) {
674
+ this.timer = { duration: hours * 3600, action: 'off' };
675
+ return true;
676
+ } else if (success) {
677
+ // API call succeeded but no timer ID returned
678
+ logger.warn(`Timer API call succeeded but no timer ID returned for device: ${this.deviceName}`);
679
+ this.timer = { duration: hours * 3600, action: 'off' };
680
+ return true;
681
+ } else {
628
682
  logger.error(`Failed to set timer to ${hours} hours for device: ${this.deviceName}`);
683
+ return false;
629
684
  }
630
- return success;
631
685
  }
632
686
 
633
687
  /**
@@ -640,6 +694,12 @@ export class VeSyncAirBypass extends VeSyncFan {
640
694
  throw new Error(error);
641
695
  }
642
696
 
697
+ // If no timer is set, return success
698
+ if (!this.timer) {
699
+ logger.debug(`No timer to clear for device: ${this.deviceName}`);
700
+ return true;
701
+ }
702
+
643
703
  logger.debug(`Clearing timer for device: ${this.deviceName}`);
644
704
 
645
705
  // Special handling for LV series
@@ -665,10 +725,13 @@ export class VeSyncAirBypass extends VeSyncFan {
665
725
  );
666
726
 
667
727
  const success = this.checkResponse([response, status], 'clearTimer');
668
- if (!success) {
728
+ if (success) {
729
+ this.timer = null;
730
+ return true;
731
+ } else {
669
732
  logger.error(`Failed to clear timer for device: ${this.deviceName}`);
733
+ return false;
670
734
  }
671
- return success;
672
735
  }
673
736
 
674
737
  // Standard implementation for other devices
@@ -689,10 +752,21 @@ export class VeSyncAirBypass extends VeSyncFan {
689
752
  );
690
753
 
691
754
  const success = this.checkResponse([response, status], 'clearTimer');
692
- if (!success) {
755
+
756
+ // Check for error code 11000000 (feature not supported or no timer to clear)
757
+ if (response?.code === 11000000 || (response?.result?.code === 11000000)) {
758
+ logger.warn(`No timer to clear or timer control not supported via API for device: ${this.deviceName}`);
759
+ this.timer = null;
760
+ return true;
761
+ }
762
+
763
+ if (success) {
764
+ this.timer = null;
765
+ return true;
766
+ } else {
693
767
  logger.error(`Failed to clear timer for device: ${this.deviceName}`);
768
+ return false;
694
769
  }
695
- return success;
696
770
  }
697
771
 
698
772
  /**