tsvesync 1.5.2 → 1.6.0
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/fans/air131.js +3 -2
- package/dist/lib/fans/airBaseV2.d.ts +8 -0
- package/dist/lib/fans/airBaseV2.js +41 -0
- package/dist/lib/fans/airBypass.js +2 -2
- package/dist/lib/vesyncFan.js +8 -4
- package/package.json +2 -1
- package/src/lib/fans/air131.ts +3 -2
- package/src/lib/fans/airBaseV2.ts +52 -0
- package/src/lib/fans/airBypass.ts +2 -2
- package/src/lib/vesyncFan.ts +8 -4
package/dist/lib/fans/air131.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
332
|
+
this.details.child_lock = enabled;
|
|
333
333
|
return true;
|
|
334
334
|
}
|
|
335
335
|
else {
|
package/dist/lib/vesyncFan.js
CHANGED
|
@@ -228,22 +228,26 @@ exports.fanConfig = {
|
|
|
228
228
|
// LTF Series
|
|
229
229
|
'LTF-F422S-KEU': {
|
|
230
230
|
module: 'VeSyncTowerFan',
|
|
231
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "1.6.0",
|
|
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",
|
package/src/lib/fans/air131.ts
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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}`);
|
package/src/lib/vesyncFan.ts
CHANGED
|
@@ -242,22 +242,26 @@ export const fanConfig: FanConfig = {
|
|
|
242
242
|
// LTF Series
|
|
243
243
|
'LTF-F422S-KEU': {
|
|
244
244
|
module: 'VeSyncTowerFan',
|
|
245
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|