tsvesync 1.5.2 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -85,7 +85,8 @@ class VeSyncAir131 extends vesyncFan_1.VeSyncFan {
85
85
  speed: data.level || 0,
86
86
  filter_life: filterLife,
87
87
  screen_status: data.screenStatus || 'off',
88
- child_lock: data.childLock || false,
88
+ // The 131 API reports childLock as the string 'on'/'off'
89
+ child_lock: data.childLock === 'on' || data.childLock === true,
89
90
  air_quality: data.airQuality || 'unknown',
90
91
  air_quality_value: data.air_quality_value || 0,
91
92
  air_quality_level: normalizedAirQuality.level >= 1 ? normalizedAirQuality.level : undefined,
@@ -343,7 +344,7 @@ class VeSyncAir131 extends vesyncFan_1.VeSyncFan {
343
344
  });
344
345
  const success = this.checkResponse([response, status], 'setChildLock');
345
346
  if (success) {
346
- this.details.childLock = enabled;
347
+ this.details.child_lock = enabled;
347
348
  return true;
348
349
  }
349
350
  else {
@@ -54,6 +54,14 @@ export declare class VeSyncAirBaseV2 extends VeSyncAirBypass {
54
54
  * Override turn off to use the powerSwitch payload required by bypassV2 purifiers.
55
55
  */
56
56
  turnOff(): Promise<boolean>;
57
+ /**
58
+ * Override setChildLock to send the childLockSwitch payload required by
59
+ * bypassV2 purifiers (Vital 100S/200S, Everest Air). These devices report
60
+ * child lock as childLockSwitch 0|1 and reject the legacy child_lock
61
+ * boolean payload used by the Core series (matches pyvesync, which sends
62
+ * {'childLockSwitch': int} for this family).
63
+ */
64
+ setChildLock(enabled: boolean): Promise<boolean>;
57
65
  /**
58
66
  * Override setMode to send workMode payloads for bypassV2 purifiers.
59
67
  */
@@ -264,6 +264,47 @@ class VeSyncAirBaseV2 extends airBypass_1.VeSyncAirBypass {
264
264
  return super.turnOff();
265
265
  }
266
266
  }
267
+ /**
268
+ * Override setChildLock to send the childLockSwitch payload required by
269
+ * bypassV2 purifiers (Vital 100S/200S, Everest Air). These devices report
270
+ * child lock as childLockSwitch 0|1 and reject the legacy child_lock
271
+ * boolean payload used by the Core series (matches pyvesync, which sends
272
+ * {'childLockSwitch': int} for this family).
273
+ */
274
+ async setChildLock(enabled) {
275
+ var _a;
276
+ if (!this.requiresPowerSwitchPayload()) {
277
+ // Use parent implementation for non-bypassV2 models
278
+ return super.setChildLock(enabled);
279
+ }
280
+ if (!this.hasFeature('child_lock')) {
281
+ const error = 'Child lock not supported';
282
+ logger_1.logger.error(`${error} for device: ${this.deviceName}`);
283
+ throw new Error(error);
284
+ }
285
+ logger_1.logger.debug(`Setting child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
286
+ const [head, body] = this.buildApiDict('setChildLock');
287
+ body.payload.data = {
288
+ childLockSwitch: enabled ? 1 : 0
289
+ };
290
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
291
+ const success = this.checkV2Response(response, status, 'setChildLock');
292
+ // Code 11000000 marks the feature unsupported even when the envelope
293
+ // succeeds, so it must override the lenient inner-code handling
294
+ if ((response === null || response === void 0 ? void 0 : response.code) === 11000000 || (((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.code) === 11000000)) {
295
+ logger_1.logger.warn(`Child lock control not supported via API for device: ${this.deviceName}`);
296
+ return false;
297
+ }
298
+ if (success) {
299
+ // No deferred details refresh here: pyvesync and the Core path
300
+ // both rely on the optimistic update plus the next regular poll
301
+ this.details.child_lock = enabled;
302
+ }
303
+ else {
304
+ logger_1.logger.error(`Failed to set child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
305
+ }
306
+ return success;
307
+ }
267
308
  /**
268
309
  * Override setMode to send workMode payloads for bypassV2 purifiers.
269
310
  */
@@ -316,7 +316,7 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
316
316
  configModule: this.configModule,
317
317
  payload: {
318
318
  data: {
319
- state: enabled
319
+ child_lock: enabled
320
320
  },
321
321
  method: 'setChildLock',
322
322
  source: 'APP'
@@ -329,7 +329,7 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
329
329
  return false;
330
330
  }
331
331
  if (success) {
332
- this.details.childLock = enabled;
332
+ this.details.child_lock = enabled;
333
333
  return true;
334
334
  }
335
335
  else {
@@ -228,22 +228,26 @@ exports.fanConfig = {
228
228
  // LTF Series
229
229
  'LTF-F422S-KEU': {
230
230
  module: 'VeSyncTowerFan',
231
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
231
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
232
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
232
233
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
233
234
  },
234
235
  'LTF-F422S-WUSR': {
235
236
  module: 'VeSyncTowerFan',
236
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
237
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
238
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
237
239
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
238
240
  },
239
241
  'LTF-F422_WJP': {
240
242
  module: 'VeSyncTowerFan',
241
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
243
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
244
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
242
245
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
243
246
  },
244
247
  'LTF-F422S-WUS': {
245
248
  module: 'VeSyncTowerFan',
246
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
249
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
250
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
247
251
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
248
252
  },
249
253
  // Classic Series
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.5.2",
3
+ "version": "1.6.1",
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",
@@ -8,6 +8,7 @@
8
8
  "build": "tsc",
9
9
  "clean": "rm -rf dist",
10
10
  "test:everest": "ts-node test/everest-air-parity.test.ts",
11
+ "test:child-lock": "ts-node test/child-lock-parity.test.ts",
11
12
  "prepare": "npm run build",
12
13
  "version": "git add -A src",
13
14
  "postversion": "git push && git push --tags",
@@ -95,7 +95,8 @@ export class VeSyncAir131 extends VeSyncFan {
95
95
  speed: data.level || 0,
96
96
  filter_life: filterLife,
97
97
  screen_status: data.screenStatus || 'off',
98
- child_lock: data.childLock || false,
98
+ // The 131 API reports childLock as the string 'on'/'off'
99
+ child_lock: data.childLock === 'on' || data.childLock === true,
99
100
  air_quality: data.airQuality || 'unknown',
100
101
  air_quality_value: data.air_quality_value || 0,
101
102
  air_quality_level: normalizedAirQuality.level >= 1 ? normalizedAirQuality.level : undefined,
@@ -413,7 +414,7 @@ export class VeSyncAir131 extends VeSyncFan {
413
414
 
414
415
  const success = this.checkResponse([response, status], 'setChildLock');
415
416
  if (success) {
416
- this.details.childLock = enabled;
417
+ this.details.child_lock = enabled;
417
418
  return true;
418
419
  } else {
419
420
  // Check for error code 11000000 (feature not supported)
@@ -311,6 +311,58 @@ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
311
311
  }
312
312
  }
313
313
 
314
+ /**
315
+ * Override setChildLock to send the childLockSwitch payload required by
316
+ * bypassV2 purifiers (Vital 100S/200S, Everest Air). These devices report
317
+ * child lock as childLockSwitch 0|1 and reject the legacy child_lock
318
+ * boolean payload used by the Core series (matches pyvesync, which sends
319
+ * {'childLockSwitch': int} for this family).
320
+ */
321
+ override async setChildLock(enabled: boolean): Promise<boolean> {
322
+ if (!this.requiresPowerSwitchPayload()) {
323
+ // Use parent implementation for non-bypassV2 models
324
+ return super.setChildLock(enabled);
325
+ }
326
+
327
+ if (!this.hasFeature('child_lock')) {
328
+ const error = 'Child lock not supported';
329
+ logger.error(`${error} for device: ${this.deviceName}`);
330
+ throw new Error(error);
331
+ }
332
+
333
+ logger.debug(`Setting child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
334
+
335
+ const [head, body] = this.buildApiDict('setChildLock');
336
+ body.payload.data = {
337
+ childLockSwitch: enabled ? 1 : 0
338
+ };
339
+
340
+ const [response, status] = await this.callApi(
341
+ '/cloud/v2/deviceManaged/bypassV2',
342
+ 'post',
343
+ body,
344
+ head
345
+ );
346
+
347
+ const success = this.checkV2Response(response, status, 'setChildLock');
348
+
349
+ // Code 11000000 marks the feature unsupported even when the envelope
350
+ // succeeds, so it must override the lenient inner-code handling
351
+ if (response?.code === 11000000 || (response?.result?.code === 11000000)) {
352
+ logger.warn(`Child lock control not supported via API for device: ${this.deviceName}`);
353
+ return false;
354
+ }
355
+
356
+ if (success) {
357
+ // No deferred details refresh here: pyvesync and the Core path
358
+ // both rely on the optimistic update plus the next regular poll
359
+ this.details.child_lock = enabled;
360
+ } else {
361
+ logger.error(`Failed to set child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
362
+ }
363
+ return success;
364
+ }
365
+
314
366
  /**
315
367
  * Override setMode to send workMode payloads for bypassV2 purifiers.
316
368
  */
@@ -376,7 +376,7 @@ export class VeSyncAirBypass extends VeSyncFan {
376
376
  configModule: this.configModule,
377
377
  payload: {
378
378
  data: {
379
- state: enabled
379
+ child_lock: enabled
380
380
  },
381
381
  method: 'setChildLock',
382
382
  source: 'APP'
@@ -394,7 +394,7 @@ export class VeSyncAirBypass extends VeSyncFan {
394
394
  }
395
395
 
396
396
  if (success) {
397
- this.details.childLock = enabled;
397
+ this.details.child_lock = enabled;
398
398
  return true;
399
399
  } else {
400
400
  logger.error(`Failed to set child lock to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
@@ -242,22 +242,26 @@ export const fanConfig: FanConfig = {
242
242
  // LTF Series
243
243
  'LTF-F422S-KEU': {
244
244
  module: 'VeSyncTowerFan',
245
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
245
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
246
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
246
247
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
247
248
  },
248
249
  'LTF-F422S-WUSR': {
249
250
  module: 'VeSyncTowerFan',
250
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
251
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
252
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
251
253
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
252
254
  },
253
255
  'LTF-F422_WJP': {
254
256
  module: 'VeSyncTowerFan',
255
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
257
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
258
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
256
259
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
257
260
  },
258
261
  'LTF-F422S-WUS': {
259
262
  module: 'VeSyncTowerFan',
260
- features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
263
+ // No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
264
+ features: ['display', 'night_light', 'timer', 'fan_speeds', 'tower_modes'],
261
265
  levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
262
266
  },
263
267