tsvesync 1.0.66 → 1.0.68

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.
@@ -213,6 +213,44 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
213
213
  // return false;
214
214
  // }
215
215
  logger_1.logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
216
+ // Special handling for LV series
217
+ if (this.deviceType.startsWith('LV-')) {
218
+ // Check if device is in manual mode
219
+ if (this.details.mode !== 'manual') {
220
+ logger_1.logger.debug(`${this.deviceName} not in manual mode, cannot change speed`);
221
+ return false;
222
+ }
223
+ // Validate speed for LV series (1-3)
224
+ if (speed < 1 || speed > 3) {
225
+ logger_1.logger.error(`Invalid fan speed: ${speed}. Must be between 1 and 3 for device: ${this.deviceName}`);
226
+ return false;
227
+ }
228
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/updateSpeed', 'put', {
229
+ acceptLanguage: 'en',
230
+ accountID: this.manager.accountId,
231
+ level: speed,
232
+ timeZone: this.manager.timeZone,
233
+ token: this.manager.token,
234
+ uuid: this.uuid
235
+ }, {
236
+ 'accept-language': 'en',
237
+ 'accountId': this.manager.accountId,
238
+ 'appVersion': '2.8.6',
239
+ 'content-type': 'application/json',
240
+ 'tk': this.manager.token,
241
+ 'tz': this.manager.timeZone
242
+ });
243
+ const success = this.checkResponse([response, status], 'changeFanSpeed');
244
+ if (success) {
245
+ this.details.speed = speed;
246
+ return true;
247
+ }
248
+ else {
249
+ logger_1.logger.error(`Failed to change fan speed to ${speed} for device: ${this.deviceName}`);
250
+ return false;
251
+ }
252
+ }
253
+ // Standard implementation for other devices
216
254
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
217
255
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
218
256
  cid: this.cid,
@@ -244,6 +282,39 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
244
282
  throw new Error(error);
245
283
  }
246
284
  logger_1.logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
285
+ // Special handling for LV series
286
+ if (this.deviceType.startsWith('LV-')) {
287
+ const payload = {
288
+ acceptLanguage: 'en',
289
+ accountID: this.manager.accountId,
290
+ mode: mode,
291
+ timeZone: this.manager.timeZone,
292
+ token: this.manager.token,
293
+ uuid: this.uuid
294
+ };
295
+ // For manual mode, we need to set the level
296
+ if (mode === 'manual') {
297
+ payload.level = this.details.speed || 1;
298
+ }
299
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/updateMode', 'put', payload, {
300
+ 'accept-language': 'en',
301
+ 'accountId': this.manager.accountId,
302
+ 'appVersion': '2.8.6',
303
+ 'content-type': 'application/json',
304
+ 'tk': this.manager.token,
305
+ 'tz': this.manager.timeZone
306
+ });
307
+ const success = this.checkResponse([response, status], 'setMode');
308
+ if (success) {
309
+ this.details.mode = mode;
310
+ return true;
311
+ }
312
+ else {
313
+ logger_1.logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
314
+ return false;
315
+ }
316
+ }
317
+ // Standard implementation for other devices
247
318
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
248
319
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
249
320
  cid: this.cid,
@@ -272,6 +343,34 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
272
343
  throw new Error(error);
273
344
  }
274
345
  logger_1.logger.debug(`Setting display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
346
+ // Special handling for LV series
347
+ if (this.deviceType.startsWith('LV-')) {
348
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/updateScreen', 'put', {
349
+ acceptLanguage: 'en',
350
+ accountID: this.manager.accountId,
351
+ status: enabled ? 'on' : 'off',
352
+ timeZone: this.manager.timeZone,
353
+ token: this.manager.token,
354
+ uuid: this.uuid
355
+ }, {
356
+ 'accept-language': 'en',
357
+ 'accountId': this.manager.accountId,
358
+ 'appVersion': '2.8.6',
359
+ 'content-type': 'application/json',
360
+ 'tk': this.manager.token,
361
+ 'tz': this.manager.timeZone
362
+ });
363
+ const success = this.checkResponse([response, status], 'setDisplay');
364
+ if (success) {
365
+ this.details.screenStatus = enabled ? 'on' : 'off';
366
+ return true;
367
+ }
368
+ else {
369
+ logger_1.logger.error(`Failed to set display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
370
+ return false;
371
+ }
372
+ }
373
+ // Standard implementation for other devices
275
374
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
276
375
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
277
376
  cid: this.cid,
@@ -303,6 +402,34 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
303
402
  throw new Error(error);
304
403
  }
305
404
  logger_1.logger.debug(`Setting child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
405
+ // Special handling for LV series
406
+ if (this.deviceType.startsWith('LV-')) {
407
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/updateChildLock', 'put', {
408
+ acceptLanguage: 'en',
409
+ accountID: this.manager.accountId,
410
+ status: enabled ? 'on' : 'off',
411
+ timeZone: this.manager.timeZone,
412
+ token: this.manager.token,
413
+ uuid: this.uuid
414
+ }, {
415
+ 'accept-language': 'en',
416
+ 'accountId': this.manager.accountId,
417
+ 'appVersion': '2.8.6',
418
+ 'content-type': 'application/json',
419
+ 'tk': this.manager.token,
420
+ 'tz': this.manager.timeZone
421
+ });
422
+ const success = this.checkResponse([response, status], 'setChildLock');
423
+ if (success) {
424
+ this.details.childLock = enabled;
425
+ return true;
426
+ }
427
+ else {
428
+ logger_1.logger.error(`Failed to set child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
429
+ return false;
430
+ }
431
+ }
432
+ // Standard implementation for other devices
306
433
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
307
434
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
308
435
  cid: this.cid,
@@ -334,6 +461,31 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
334
461
  throw new Error(error);
335
462
  }
336
463
  logger_1.logger.debug(`Setting timer to ${hours} hours for device: ${this.deviceName}`);
464
+ // Special handling for LV series
465
+ if (this.deviceType.startsWith('LV-')) {
466
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/updateTimer', 'put', {
467
+ acceptLanguage: 'en',
468
+ accountID: this.manager.accountId,
469
+ action: 'off',
470
+ duration: hours,
471
+ timeZone: this.manager.timeZone,
472
+ token: this.manager.token,
473
+ uuid: this.uuid
474
+ }, {
475
+ 'accept-language': 'en',
476
+ 'accountId': this.manager.accountId,
477
+ 'appVersion': '2.8.6',
478
+ 'content-type': 'application/json',
479
+ 'tk': this.manager.token,
480
+ 'tz': this.manager.timeZone
481
+ });
482
+ const success = this.checkResponse([response, status], 'setTimer');
483
+ if (!success) {
484
+ logger_1.logger.error(`Failed to set timer to ${hours} hours for device: ${this.deviceName}`);
485
+ }
486
+ return success;
487
+ }
488
+ // Standard implementation for other devices
337
489
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
338
490
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
339
491
  cid: this.cid,
@@ -363,6 +515,29 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
363
515
  throw new Error(error);
364
516
  }
365
517
  logger_1.logger.debug(`Clearing timer for device: ${this.deviceName}`);
518
+ // Special handling for LV series
519
+ if (this.deviceType.startsWith('LV-')) {
520
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/cancelTimer', 'put', {
521
+ acceptLanguage: 'en',
522
+ accountID: this.manager.accountId,
523
+ timeZone: this.manager.timeZone,
524
+ token: this.manager.token,
525
+ uuid: this.uuid
526
+ }, {
527
+ 'accept-language': 'en',
528
+ 'accountId': this.manager.accountId,
529
+ 'appVersion': '2.8.6',
530
+ 'content-type': 'application/json',
531
+ 'tk': this.manager.token,
532
+ 'tz': this.manager.timeZone
533
+ });
534
+ const success = this.checkResponse([response, status], 'clearTimer');
535
+ if (!success) {
536
+ logger_1.logger.error(`Failed to clear timer for device: ${this.deviceName}`);
537
+ }
538
+ return success;
539
+ }
540
+ // Standard implementation for other devices
366
541
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
367
542
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
368
543
  cid: this.cid,
@@ -38,4 +38,12 @@ export declare class VeSyncHumid200300S extends VeSyncHumidifier {
38
38
  * Get automatic stop configuration
39
39
  */
40
40
  get automaticStopConfigured(): boolean;
41
+ /**
42
+ * Get night light brightness
43
+ */
44
+ get nightLightBrightness(): number;
45
+ /**
46
+ * Set night light brightness
47
+ */
48
+ setNightLightBrightness(brightness: number): Promise<boolean>;
41
49
  }
@@ -11,7 +11,7 @@ class VeSyncHumid200300S extends humidifier_1.VeSyncHumidifier {
11
11
  constructor(details, manager) {
12
12
  super(details, manager);
13
13
  this.modes = ['auto', 'manual', 'sleep'];
14
- this.features = ['humidity', 'mist', 'display', 'timer', 'auto_mode'];
14
+ this.features = ['humidity', 'mist', 'display', 'timer', 'auto_mode', 'night_light'];
15
15
  this.mistLevels = [1, 2, 3, 4, 5, 6, 7, 8, 9];
16
16
  this.humidityRange = { min: 30, max: 80 };
17
17
  logger_1.logger.debug(`Initialized VeSyncHumid200300S device: ${this.deviceName}`);
@@ -51,7 +51,8 @@ class VeSyncHumid200300S extends humidifier_1.VeSyncHumidifier {
51
51
  automatic_stop_configured: ((_b = result.configuration) === null || _b === void 0 ? void 0 : _b.automatic_stop) || false,
52
52
  auto_target_humidity: ((_c = result.configuration) === null || _c === void 0 ? void 0 : _c.auto_target_humidity) || 0,
53
53
  configuration: result.configuration || {},
54
- connection_status: result.connection_status || null
54
+ connection_status: result.connection_status || null,
55
+ night_light_brightness: result.night_light_brightness || 0
55
56
  };
56
57
  // Log raw response for debugging
57
58
  logger_1.logger.debug(`Raw API Response: ${JSON.stringify(response)}`);
@@ -125,5 +126,51 @@ class VeSyncHumid200300S extends humidifier_1.VeSyncHumidifier {
125
126
  get automaticStopConfigured() {
126
127
  return this.details.automatic_stop_configured || false;
127
128
  }
129
+ /**
130
+ * Get night light brightness
131
+ */
132
+ get nightLightBrightness() {
133
+ return this.details.night_light_brightness || 0;
134
+ }
135
+ /**
136
+ * Set night light brightness
137
+ */
138
+ async setNightLightBrightness(brightness) {
139
+ var _a;
140
+ if (brightness < 0 || brightness > 100) {
141
+ const error = `Invalid brightness: ${brightness}. Must be between 0 and 100`;
142
+ logger_1.logger.error(`${error} for device: ${this.deviceName}`);
143
+ throw new Error(error);
144
+ }
145
+ logger_1.logger.debug(`Setting night light brightness to ${brightness} for device: ${this.deviceName}`);
146
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
147
+ ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
148
+ cid: this.cid,
149
+ configModule: this.configModule,
150
+ payload: {
151
+ data: {
152
+ brightness
153
+ },
154
+ method: 'setNightLight',
155
+ source: 'APP'
156
+ }
157
+ }, helpers_1.Helpers.reqHeaderBypass());
158
+ // Check for error code 11000000 which indicates feature not supported
159
+ if (((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.code) === 11000000) {
160
+ logger_1.logger.warn(`Night light control not supported by device: ${this.deviceName} (${this.deviceType})`);
161
+ return false;
162
+ }
163
+ const success = this.checkResponse([response, status], 'setNightLightBrightness');
164
+ if (success) {
165
+ this.details.night_light_brightness = brightness;
166
+ // Get latest state and log it
167
+ await this.getDetails();
168
+ logger_1.logger.debug(`Device state after night light brightness change: ${JSON.stringify(this.details)}`);
169
+ }
170
+ else {
171
+ logger_1.logger.error(`Failed to set night light brightness to ${brightness} for device: ${this.deviceName}`);
172
+ }
173
+ return success;
174
+ }
128
175
  }
129
176
  exports.VeSyncHumid200300S = VeSyncHumid200300S;
@@ -203,7 +203,7 @@ exports.fanConfig = {
203
203
  // LUH Series
204
204
  'LUH-A601S-WUSB': {
205
205
  module: 'VeSyncWarmHumidifier',
206
- features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
206
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode', 'night_light']
207
207
  },
208
208
  'LUH-A601S-AUSW': {
209
209
  module: 'VeSyncWarmHumidifier',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.66",
3
+ "version": "1.0.68",
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",
@@ -261,6 +261,53 @@ export class VeSyncAirBypass extends VeSyncFan {
261
261
  // }
262
262
 
263
263
  logger.info(`Changing fan speed to ${speed} for device: ${this.deviceName}`);
264
+
265
+ // Special handling for LV series
266
+ if (this.deviceType.startsWith('LV-')) {
267
+ // Check if device is in manual mode
268
+ if (this.details.mode !== 'manual') {
269
+ logger.debug(`${this.deviceName} not in manual mode, cannot change speed`);
270
+ return false;
271
+ }
272
+
273
+ // Validate speed for LV series (1-3)
274
+ if (speed < 1 || speed > 3) {
275
+ logger.error(`Invalid fan speed: ${speed}. Must be between 1 and 3 for device: ${this.deviceName}`);
276
+ return false;
277
+ }
278
+
279
+ const [response, status] = await this.callApi(
280
+ '/131airPurifier/v1/device/updateSpeed',
281
+ 'put',
282
+ {
283
+ acceptLanguage: 'en',
284
+ accountID: this.manager.accountId!,
285
+ level: speed,
286
+ timeZone: this.manager.timeZone!,
287
+ token: this.manager.token!,
288
+ uuid: this.uuid
289
+ },
290
+ {
291
+ 'accept-language': 'en',
292
+ 'accountId': this.manager.accountId!,
293
+ 'appVersion': '2.8.6',
294
+ 'content-type': 'application/json',
295
+ 'tk': this.manager.token!,
296
+ 'tz': this.manager.timeZone!
297
+ }
298
+ );
299
+
300
+ const success = this.checkResponse([response, status], 'changeFanSpeed');
301
+ if (success) {
302
+ this.details.speed = speed;
303
+ return true;
304
+ } else {
305
+ logger.error(`Failed to change fan speed to ${speed} for device: ${this.deviceName}`);
306
+ return false;
307
+ }
308
+ }
309
+
310
+ // Standard implementation for other devices
264
311
  const [response, status] = await this.callApi(
265
312
  '/cloud/v2/deviceManaged/bypassV2',
266
313
  'post',
@@ -300,6 +347,48 @@ export class VeSyncAirBypass extends VeSyncFan {
300
347
  }
301
348
 
302
349
  logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
350
+
351
+ // Special handling for LV series
352
+ if (this.deviceType.startsWith('LV-')) {
353
+ const payload: Record<string, any> = {
354
+ acceptLanguage: 'en',
355
+ accountID: this.manager.accountId!,
356
+ mode: mode,
357
+ timeZone: this.manager.timeZone!,
358
+ token: this.manager.token!,
359
+ uuid: this.uuid
360
+ };
361
+
362
+ // For manual mode, we need to set the level
363
+ if (mode === 'manual') {
364
+ payload.level = this.details.speed || 1;
365
+ }
366
+
367
+ const [response, status] = await this.callApi(
368
+ '/131airPurifier/v1/device/updateMode',
369
+ 'put',
370
+ payload,
371
+ {
372
+ 'accept-language': 'en',
373
+ 'accountId': this.manager.accountId!,
374
+ 'appVersion': '2.8.6',
375
+ 'content-type': 'application/json',
376
+ 'tk': this.manager.token!,
377
+ 'tz': this.manager.timeZone!
378
+ }
379
+ );
380
+
381
+ const success = this.checkResponse([response, status], 'setMode');
382
+ if (success) {
383
+ this.details.mode = mode;
384
+ return true;
385
+ } else {
386
+ logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
387
+ return false;
388
+ }
389
+ }
390
+
391
+ // Standard implementation for other devices
303
392
  const [response, status] = await this.callApi(
304
393
  '/cloud/v2/deviceManaged/bypassV2',
305
394
  'post',
@@ -336,6 +425,41 @@ export class VeSyncAirBypass extends VeSyncFan {
336
425
  }
337
426
 
338
427
  logger.debug(`Setting display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
428
+
429
+ // Special handling for LV series
430
+ if (this.deviceType.startsWith('LV-')) {
431
+ const [response, status] = await this.callApi(
432
+ '/131airPurifier/v1/device/updateScreen',
433
+ 'put',
434
+ {
435
+ acceptLanguage: 'en',
436
+ accountID: this.manager.accountId!,
437
+ status: enabled ? 'on' : 'off',
438
+ timeZone: this.manager.timeZone!,
439
+ token: this.manager.token!,
440
+ uuid: this.uuid
441
+ },
442
+ {
443
+ 'accept-language': 'en',
444
+ 'accountId': this.manager.accountId!,
445
+ 'appVersion': '2.8.6',
446
+ 'content-type': 'application/json',
447
+ 'tk': this.manager.token!,
448
+ 'tz': this.manager.timeZone!
449
+ }
450
+ );
451
+
452
+ const success = this.checkResponse([response, status], 'setDisplay');
453
+ if (success) {
454
+ this.details.screenStatus = enabled ? 'on' : 'off';
455
+ return true;
456
+ } else {
457
+ logger.error(`Failed to set display to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
458
+ return false;
459
+ }
460
+ }
461
+
462
+ // Standard implementation for other devices
339
463
  const [response, status] = await this.callApi(
340
464
  '/cloud/v2/deviceManaged/bypassV2',
341
465
  'post',
@@ -374,6 +498,41 @@ export class VeSyncAirBypass extends VeSyncFan {
374
498
  }
375
499
 
376
500
  logger.debug(`Setting child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
501
+
502
+ // Special handling for LV series
503
+ if (this.deviceType.startsWith('LV-')) {
504
+ const [response, status] = await this.callApi(
505
+ '/131airPurifier/v1/device/updateChildLock',
506
+ 'put',
507
+ {
508
+ acceptLanguage: 'en',
509
+ accountID: this.manager.accountId!,
510
+ status: enabled ? 'on' : 'off',
511
+ timeZone: this.manager.timeZone!,
512
+ token: this.manager.token!,
513
+ uuid: this.uuid
514
+ },
515
+ {
516
+ 'accept-language': 'en',
517
+ 'accountId': this.manager.accountId!,
518
+ 'appVersion': '2.8.6',
519
+ 'content-type': 'application/json',
520
+ 'tk': this.manager.token!,
521
+ 'tz': this.manager.timeZone!
522
+ }
523
+ );
524
+
525
+ const success = this.checkResponse([response, status], 'setChildLock');
526
+ if (success) {
527
+ this.details.childLock = enabled;
528
+ return true;
529
+ } else {
530
+ logger.error(`Failed to set child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
531
+ return false;
532
+ }
533
+ }
534
+
535
+ // Standard implementation for other devices
377
536
  const [response, status] = await this.callApi(
378
537
  '/cloud/v2/deviceManaged/bypassV2',
379
538
  'post',
@@ -412,6 +571,39 @@ export class VeSyncAirBypass extends VeSyncFan {
412
571
  }
413
572
 
414
573
  logger.debug(`Setting timer to ${hours} hours for device: ${this.deviceName}`);
574
+
575
+ // Special handling for LV series
576
+ if (this.deviceType.startsWith('LV-')) {
577
+ const [response, status] = await this.callApi(
578
+ '/131airPurifier/v1/device/updateTimer',
579
+ 'put',
580
+ {
581
+ acceptLanguage: 'en',
582
+ accountID: this.manager.accountId!,
583
+ action: 'off',
584
+ duration: hours,
585
+ timeZone: this.manager.timeZone!,
586
+ token: this.manager.token!,
587
+ uuid: this.uuid
588
+ },
589
+ {
590
+ 'accept-language': 'en',
591
+ 'accountId': this.manager.accountId!,
592
+ 'appVersion': '2.8.6',
593
+ 'content-type': 'application/json',
594
+ 'tk': this.manager.token!,
595
+ 'tz': this.manager.timeZone!
596
+ }
597
+ );
598
+
599
+ const success = this.checkResponse([response, status], 'setTimer');
600
+ if (!success) {
601
+ logger.error(`Failed to set timer to ${hours} hours for device: ${this.deviceName}`);
602
+ }
603
+ return success;
604
+ }
605
+
606
+ // Standard implementation for other devices
415
607
  const [response, status] = await this.callApi(
416
608
  '/cloud/v2/deviceManaged/bypassV2',
417
609
  'post',
@@ -449,6 +641,37 @@ export class VeSyncAirBypass extends VeSyncFan {
449
641
  }
450
642
 
451
643
  logger.debug(`Clearing timer for device: ${this.deviceName}`);
644
+
645
+ // Special handling for LV series
646
+ if (this.deviceType.startsWith('LV-')) {
647
+ const [response, status] = await this.callApi(
648
+ '/131airPurifier/v1/device/cancelTimer',
649
+ 'put',
650
+ {
651
+ acceptLanguage: 'en',
652
+ accountID: this.manager.accountId!,
653
+ timeZone: this.manager.timeZone!,
654
+ token: this.manager.token!,
655
+ uuid: this.uuid
656
+ },
657
+ {
658
+ 'accept-language': 'en',
659
+ 'accountId': this.manager.accountId!,
660
+ 'appVersion': '2.8.6',
661
+ 'content-type': 'application/json',
662
+ 'tk': this.manager.token!,
663
+ 'tz': this.manager.timeZone!
664
+ }
665
+ );
666
+
667
+ const success = this.checkResponse([response, status], 'clearTimer');
668
+ if (!success) {
669
+ logger.error(`Failed to clear timer for device: ${this.deviceName}`);
670
+ }
671
+ return success;
672
+ }
673
+
674
+ // Standard implementation for other devices
452
675
  const [response, status] = await this.callApi(
453
676
  '/cloud/v2/deviceManaged/bypassV2',
454
677
  'post',
@@ -8,7 +8,7 @@ import { logger } from '../logger';
8
8
  */
9
9
  export class VeSyncHumid200300S extends VeSyncHumidifier {
10
10
  protected readonly modes = ['auto', 'manual', 'sleep'] as const;
11
- protected readonly features = ['humidity', 'mist', 'display', 'timer', 'auto_mode'];
11
+ protected readonly features = ['humidity', 'mist', 'display', 'timer', 'auto_mode', 'night_light'];
12
12
  protected readonly mistLevels = [1, 2, 3, 4, 5, 6, 7, 8, 9];
13
13
  protected readonly humidityRange = { min: 30, max: 80 };
14
14
 
@@ -59,7 +59,8 @@ export class VeSyncHumid200300S extends VeSyncHumidifier {
59
59
  automatic_stop_configured: result.configuration?.automatic_stop || false,
60
60
  auto_target_humidity: result.configuration?.auto_target_humidity || 0,
61
61
  configuration: result.configuration || {},
62
- connection_status: result.connection_status || null
62
+ connection_status: result.connection_status || null,
63
+ night_light_brightness: result.night_light_brightness || 0
63
64
  };
64
65
 
65
66
  // Log raw response for debugging
@@ -145,4 +146,58 @@ export class VeSyncHumid200300S extends VeSyncHumidifier {
145
146
  get automaticStopConfigured(): boolean {
146
147
  return this.details.automatic_stop_configured || false;
147
148
  }
149
+
150
+ /**
151
+ * Get night light brightness
152
+ */
153
+ get nightLightBrightness(): number {
154
+ return this.details.night_light_brightness || 0;
155
+ }
156
+
157
+ /**
158
+ * Set night light brightness
159
+ */
160
+ async setNightLightBrightness(brightness: number): Promise<boolean> {
161
+ if (brightness < 0 || brightness > 100) {
162
+ const error = `Invalid brightness: ${brightness}. Must be between 0 and 100`;
163
+ logger.error(`${error} for device: ${this.deviceName}`);
164
+ throw new Error(error);
165
+ }
166
+
167
+ logger.debug(`Setting night light brightness to ${brightness} for device: ${this.deviceName}`);
168
+ const [response, status] = await this.callApi(
169
+ '/cloud/v2/deviceManaged/bypassV2',
170
+ 'post',
171
+ {
172
+ ...Helpers.reqBody(this.manager, 'bypassV2'),
173
+ cid: this.cid,
174
+ configModule: this.configModule,
175
+ payload: {
176
+ data: {
177
+ brightness
178
+ },
179
+ method: 'setNightLight',
180
+ source: 'APP'
181
+ }
182
+ },
183
+ Helpers.reqHeaderBypass()
184
+ );
185
+
186
+ // Check for error code 11000000 which indicates feature not supported
187
+ if (response?.result?.code === 11000000) {
188
+ logger.warn(`Night light control not supported by device: ${this.deviceName} (${this.deviceType})`);
189
+ return false;
190
+ }
191
+
192
+ const success = this.checkResponse([response, status], 'setNightLightBrightness');
193
+ if (success) {
194
+ this.details.night_light_brightness = brightness;
195
+ // Get latest state and log it
196
+ await this.getDetails();
197
+ logger.debug(`Device state after night light brightness change: ${JSON.stringify(this.details)}`);
198
+ } else {
199
+ logger.error(`Failed to set night light brightness to ${brightness} for device: ${this.deviceName}`);
200
+ }
201
+ return success;
202
+ }
148
203
  }
@@ -216,7 +216,7 @@ export const fanConfig: FanConfig = {
216
216
  // LUH Series
217
217
  'LUH-A601S-WUSB': {
218
218
  module: 'VeSyncWarmHumidifier',
219
- features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
219
+ features: ['display', 'humidity', 'mist', 'timer', 'auto_mode', 'night_light']
220
220
  },
221
221
  'LUH-A601S-AUSW': {
222
222
  module: 'VeSyncWarmHumidifier',