tsvesync 1.0.37 → 1.0.39
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/helpers.d.ts +2 -2
- package/dist/lib/helpers.js +2 -2
- package/dist/lib/vesyncFan.d.ts +1 -1
- package/dist/lib/vesyncFan.js +8 -8
- package/dist/lib/vesyncFanImpl.d.ts +132 -0
- package/dist/lib/vesyncFanImpl.js +390 -5
- package/package.json +1 -1
- package/src/lib/helpers.ts +2 -2
- package/src/lib/vesyncFan.ts +9 -9
- package/src/lib/vesyncFanImpl.ts +475 -4
package/dist/lib/helpers.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export declare class Helpers {
|
|
|
40
40
|
/**
|
|
41
41
|
* Calculate MD5 hash
|
|
42
42
|
*/
|
|
43
|
-
static
|
|
43
|
+
static hashPassword(text: string): string;
|
|
44
44
|
/**
|
|
45
45
|
* Build header for legacy api GET requests
|
|
46
46
|
*/
|
|
@@ -84,5 +84,5 @@ export declare class Helpers {
|
|
|
84
84
|
/**
|
|
85
85
|
* Calculate MD5 hash
|
|
86
86
|
*/
|
|
87
|
-
static
|
|
87
|
+
static md5(text: string): string;
|
|
88
88
|
}
|
package/dist/lib/helpers.js
CHANGED
|
@@ -34,7 +34,7 @@ class Helpers {
|
|
|
34
34
|
/**
|
|
35
35
|
* Calculate MD5 hash
|
|
36
36
|
*/
|
|
37
|
-
static
|
|
37
|
+
static hashPassword(text) {
|
|
38
38
|
return crypto_1.default.createHash('md5').update(text).digest('hex');
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
@@ -250,7 +250,7 @@ class Helpers {
|
|
|
250
250
|
/**
|
|
251
251
|
* Calculate MD5 hash
|
|
252
252
|
*/
|
|
253
|
-
static
|
|
253
|
+
static md5(text) {
|
|
254
254
|
return crypto_1.default.createHash('md5').update(text).digest('hex');
|
|
255
255
|
}
|
|
256
256
|
}
|
package/dist/lib/vesyncFan.d.ts
CHANGED
|
@@ -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';
|
|
8
|
+
module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan';
|
|
9
9
|
features: string[];
|
|
10
10
|
};
|
|
11
11
|
}
|
package/dist/lib/vesyncFan.js
CHANGED
|
@@ -136,20 +136,20 @@ exports.fanConfig = {
|
|
|
136
136
|
},
|
|
137
137
|
// LTF Series
|
|
138
138
|
'LTF-F422S-KEU': {
|
|
139
|
-
module: '
|
|
140
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
139
|
+
module: 'VeSyncTowerFan',
|
|
140
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
141
141
|
},
|
|
142
142
|
'LTF-F422S-WUSR': {
|
|
143
|
-
module: '
|
|
144
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
143
|
+
module: 'VeSyncTowerFan',
|
|
144
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
145
145
|
},
|
|
146
146
|
'LTF-F422_WJP': {
|
|
147
|
-
module: '
|
|
148
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
147
|
+
module: 'VeSyncTowerFan',
|
|
148
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
149
149
|
},
|
|
150
150
|
'LTF-F422S-WUS': {
|
|
151
|
-
module: '
|
|
152
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
151
|
+
module: 'VeSyncTowerFan',
|
|
152
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
153
153
|
},
|
|
154
154
|
// Classic Series
|
|
155
155
|
'Classic300S': {
|
|
@@ -209,4 +209,136 @@ export declare class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
209
209
|
*/
|
|
210
210
|
get dryingModeSecondsRemaining(): number;
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* VeSync Air Purifier with Bypass V2
|
|
214
|
+
*/
|
|
215
|
+
export declare class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
216
|
+
protected _lightDetection: boolean;
|
|
217
|
+
protected _lightDetectionState: boolean;
|
|
218
|
+
constructor(details: Record<string, any>, manager: VeSync);
|
|
219
|
+
/**
|
|
220
|
+
* Build API dictionary
|
|
221
|
+
*/
|
|
222
|
+
protected buildApiDict(method: string): [Record<string, any>, Record<string, any>];
|
|
223
|
+
get lightDetection(): boolean;
|
|
224
|
+
set lightDetection(toggle: boolean);
|
|
225
|
+
get lightDetectionState(): boolean;
|
|
226
|
+
setLightDetection(toggle: boolean): Promise<boolean>;
|
|
227
|
+
setLightDetectionOn(): Promise<boolean>;
|
|
228
|
+
setLightDetectionOff(): Promise<boolean>;
|
|
229
|
+
turboMode(): Promise<boolean>;
|
|
230
|
+
petMode(): Promise<boolean>;
|
|
231
|
+
/**
|
|
232
|
+
* Build configuration dictionary
|
|
233
|
+
*/
|
|
234
|
+
protected buildConfigDict(configDict: Record<string, any>): void;
|
|
235
|
+
/**
|
|
236
|
+
* Set mode
|
|
237
|
+
*/
|
|
238
|
+
protected mode_toggle(mode: string): Promise<boolean>;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* VeSync Tower Fan Implementation
|
|
242
|
+
*/
|
|
243
|
+
export declare class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
244
|
+
protected readonly towerModes: readonly ["normal", "advancedSleep", "off"];
|
|
245
|
+
protected readonly speeds: number[];
|
|
246
|
+
constructor(details: Record<string, any>, manager: VeSync);
|
|
247
|
+
/**
|
|
248
|
+
* Build API dictionary
|
|
249
|
+
*/
|
|
250
|
+
protected buildApiDict(method: string): [Record<string, any>, Record<string, any>];
|
|
251
|
+
/**
|
|
252
|
+
* Get device details
|
|
253
|
+
*/
|
|
254
|
+
getDetails(): Promise<Boolean>;
|
|
255
|
+
/**
|
|
256
|
+
* Toggle device power
|
|
257
|
+
*/
|
|
258
|
+
toggleSwitch(toggle: boolean): Promise<boolean>;
|
|
259
|
+
/**
|
|
260
|
+
* Turn device on
|
|
261
|
+
*/
|
|
262
|
+
turnOn(): Promise<boolean>;
|
|
263
|
+
/**
|
|
264
|
+
* Turn device off
|
|
265
|
+
*/
|
|
266
|
+
turnOff(): Promise<boolean>;
|
|
267
|
+
/**
|
|
268
|
+
* Set tower fan mode
|
|
269
|
+
*/
|
|
270
|
+
mode_toggle(mode: string): Promise<boolean>;
|
|
271
|
+
/**
|
|
272
|
+
* Change fan speed
|
|
273
|
+
*/
|
|
274
|
+
changeFanSpeed(speed: number): Promise<boolean>;
|
|
275
|
+
/**
|
|
276
|
+
* Set normal mode
|
|
277
|
+
*/
|
|
278
|
+
normal_mode(): Promise<boolean>;
|
|
279
|
+
/**
|
|
280
|
+
* Set manual mode - adapter to set mode to normal
|
|
281
|
+
*/
|
|
282
|
+
manual_mode(): Promise<boolean>;
|
|
283
|
+
/**
|
|
284
|
+
* Set advanced sleep mode
|
|
285
|
+
*/
|
|
286
|
+
advanced_sleep_mode(): Promise<boolean>;
|
|
287
|
+
/**
|
|
288
|
+
* Set sleep mode - adapter for advanced sleep mode
|
|
289
|
+
*/
|
|
290
|
+
sleep_mode(): Promise<boolean>;
|
|
291
|
+
/**
|
|
292
|
+
* Override setMode to handle tower fan specific modes
|
|
293
|
+
*/
|
|
294
|
+
setMode(mode: string): Promise<boolean>;
|
|
295
|
+
/**
|
|
296
|
+
* Get current speed
|
|
297
|
+
*/
|
|
298
|
+
get speed(): number;
|
|
299
|
+
/**
|
|
300
|
+
* Get display status
|
|
301
|
+
*/
|
|
302
|
+
get displayState(): boolean;
|
|
303
|
+
/**
|
|
304
|
+
* Get oscillation status
|
|
305
|
+
*/
|
|
306
|
+
get oscillationState(): boolean;
|
|
307
|
+
/**
|
|
308
|
+
* Get mute status
|
|
309
|
+
*/
|
|
310
|
+
get muteState(): boolean;
|
|
311
|
+
/**
|
|
312
|
+
* Get timer status (in seconds)
|
|
313
|
+
*/
|
|
314
|
+
get timer(): number;
|
|
315
|
+
/**
|
|
316
|
+
* Get temperature (in tenths of a degree)
|
|
317
|
+
*/
|
|
318
|
+
get temperature(): number;
|
|
319
|
+
/**
|
|
320
|
+
* Get humidity percentage
|
|
321
|
+
*/
|
|
322
|
+
get humidity(): number;
|
|
323
|
+
/**
|
|
324
|
+
* Get thermal comfort level
|
|
325
|
+
*/
|
|
326
|
+
get thermalComfort(): number;
|
|
327
|
+
/**
|
|
328
|
+
* Get sleep preferences
|
|
329
|
+
*/
|
|
330
|
+
get sleepPreference(): Record<string, any>;
|
|
331
|
+
/**
|
|
332
|
+
* Get schedule count
|
|
333
|
+
*/
|
|
334
|
+
get scheduleCount(): number;
|
|
335
|
+
/**
|
|
336
|
+
* Get error code
|
|
337
|
+
*/
|
|
338
|
+
get errorCode(): number;
|
|
339
|
+
/**
|
|
340
|
+
* Display device info
|
|
341
|
+
*/
|
|
342
|
+
display(): void;
|
|
343
|
+
}
|
|
212
344
|
export declare const fanModules: Record<string, new (details: Record<string, any>, manager: VeSync) => VeSyncFan>;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* VeSync Fan Implementations
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.fanModules = exports.VeSyncWarmHumidifier = exports.VeSyncHumidifier = exports.VeSyncAirBypass = void 0;
|
|
6
|
+
exports.fanModules = exports.VeSyncTowerFan = exports.VeSyncAirBaseV2 = exports.VeSyncWarmHumidifier = exports.VeSyncHumidifier = exports.VeSyncAirBypass = void 0;
|
|
7
7
|
const vesyncFan_1 = require("./vesyncFan");
|
|
8
8
|
const helpers_1 = require("./helpers");
|
|
9
9
|
const logger_1 = require("./logger");
|
|
@@ -1027,6 +1027,391 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1027
1027
|
}
|
|
1028
1028
|
}
|
|
1029
1029
|
exports.VeSyncWarmHumidifier = VeSyncWarmHumidifier;
|
|
1030
|
+
/**
|
|
1031
|
+
* VeSync Air Purifier with Bypass V2
|
|
1032
|
+
*/
|
|
1033
|
+
class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
1034
|
+
constructor(details, manager) {
|
|
1035
|
+
super(details, manager);
|
|
1036
|
+
this._lightDetection = false;
|
|
1037
|
+
this._lightDetectionState = false;
|
|
1038
|
+
logger_1.logger.debug(`Initialized VeSyncAirBaseV2 device: ${this.deviceName}`);
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Build API dictionary
|
|
1042
|
+
*/
|
|
1043
|
+
buildApiDict(method) {
|
|
1044
|
+
const head = helpers_1.Helpers.reqHeaderBypass();
|
|
1045
|
+
const body = {
|
|
1046
|
+
...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1047
|
+
deviceId: this.cid,
|
|
1048
|
+
configModule: this.configModule,
|
|
1049
|
+
payload: {
|
|
1050
|
+
method,
|
|
1051
|
+
source: 'APP',
|
|
1052
|
+
data: {}
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
return [head, body];
|
|
1056
|
+
}
|
|
1057
|
+
get lightDetection() {
|
|
1058
|
+
return this._lightDetection;
|
|
1059
|
+
}
|
|
1060
|
+
set lightDetection(toggle) {
|
|
1061
|
+
this._lightDetection = toggle;
|
|
1062
|
+
}
|
|
1063
|
+
get lightDetectionState() {
|
|
1064
|
+
return this._lightDetectionState;
|
|
1065
|
+
}
|
|
1066
|
+
async setLightDetection(toggle) {
|
|
1067
|
+
const [head, body] = this.buildApiDict('setLightDetection');
|
|
1068
|
+
if (!head || !body) {
|
|
1069
|
+
return false;
|
|
1070
|
+
}
|
|
1071
|
+
body.payload.data = { enabled: toggle };
|
|
1072
|
+
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
1073
|
+
const success = this.checkResponse([response, status], 'setLightDetection');
|
|
1074
|
+
if (success) {
|
|
1075
|
+
this._lightDetection = toggle;
|
|
1076
|
+
}
|
|
1077
|
+
return success;
|
|
1078
|
+
}
|
|
1079
|
+
async setLightDetectionOn() {
|
|
1080
|
+
return this.setLightDetection(true);
|
|
1081
|
+
}
|
|
1082
|
+
async setLightDetectionOff() {
|
|
1083
|
+
return this.setLightDetection(false);
|
|
1084
|
+
}
|
|
1085
|
+
async turboMode() {
|
|
1086
|
+
return this.mode_toggle('turbo');
|
|
1087
|
+
}
|
|
1088
|
+
async petMode() {
|
|
1089
|
+
return this.mode_toggle('pet');
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Build configuration dictionary
|
|
1093
|
+
*/
|
|
1094
|
+
buildConfigDict(configDict) {
|
|
1095
|
+
if (configDict) {
|
|
1096
|
+
this.config = configDict;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Set mode
|
|
1101
|
+
*/
|
|
1102
|
+
async mode_toggle(mode) {
|
|
1103
|
+
const validModes = this.modes.map(m => m.toLowerCase());
|
|
1104
|
+
if (!validModes.includes(mode.toLowerCase())) {
|
|
1105
|
+
logger_1.logger.debug(`Invalid mode used - ${mode}`);
|
|
1106
|
+
return false;
|
|
1107
|
+
}
|
|
1108
|
+
const [head, body] = this.buildApiDict('setPurifierMode');
|
|
1109
|
+
if (!head || !body) {
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
body.payload.data = {
|
|
1113
|
+
mode: mode
|
|
1114
|
+
};
|
|
1115
|
+
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
1116
|
+
const success = this.checkResponse([response, status], 'mode_toggle');
|
|
1117
|
+
if (success) {
|
|
1118
|
+
this.details.mode = mode;
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
logger_1.logger.debug('Error setting mode');
|
|
1122
|
+
}
|
|
1123
|
+
return success;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
exports.VeSyncAirBaseV2 = VeSyncAirBaseV2;
|
|
1127
|
+
/**
|
|
1128
|
+
* VeSync Tower Fan Implementation
|
|
1129
|
+
*/
|
|
1130
|
+
class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
1131
|
+
constructor(details, manager) {
|
|
1132
|
+
super(details, manager);
|
|
1133
|
+
this.towerModes = ['normal', 'advancedSleep', 'off'];
|
|
1134
|
+
this.speeds = Array.from({ length: 12 }, (_, i) => i + 1);
|
|
1135
|
+
logger_1.logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Build API dictionary
|
|
1139
|
+
*/
|
|
1140
|
+
buildApiDict(method) {
|
|
1141
|
+
const head = helpers_1.Helpers.reqHeaderBypass();
|
|
1142
|
+
const body = helpers_1.Helpers.reqBody(this.manager, 'bypassV2');
|
|
1143
|
+
body.cid = this.cid;
|
|
1144
|
+
body.deviceId = this.cid;
|
|
1145
|
+
body.configModule = this.configModule;
|
|
1146
|
+
body.configModel = this.configModule;
|
|
1147
|
+
body.payload = {
|
|
1148
|
+
method: method,
|
|
1149
|
+
source: 'APP',
|
|
1150
|
+
data: {}
|
|
1151
|
+
};
|
|
1152
|
+
return [head, body];
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Get device details
|
|
1156
|
+
*/
|
|
1157
|
+
async getDetails() {
|
|
1158
|
+
logger_1.logger.debug(`Getting details for device: ${this.deviceName}`);
|
|
1159
|
+
const [head, body] = this.buildApiDict('getTowerFanStatus');
|
|
1160
|
+
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
1161
|
+
if (!this.checkResponse([response, status], 'getDetails') || !(response === null || response === void 0 ? void 0 : response.result)) {
|
|
1162
|
+
logger_1.logger.debug('Error getting tower fan details');
|
|
1163
|
+
this.connectionStatus = 'offline';
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
const innerResponse = response.result;
|
|
1167
|
+
if (innerResponse.code !== 0 || !innerResponse.result) {
|
|
1168
|
+
logger_1.logger.debug('Error in inner response from tower fan');
|
|
1169
|
+
this.connectionStatus = 'offline';
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
const deviceData = innerResponse.result;
|
|
1173
|
+
this.deviceStatus = deviceData.powerSwitch === 1 ? 'on' : 'off';
|
|
1174
|
+
this.details = {
|
|
1175
|
+
powerSwitch: deviceData.powerSwitch,
|
|
1176
|
+
workMode: deviceData.workMode,
|
|
1177
|
+
manualSpeedLevel: deviceData.manualSpeedLevel,
|
|
1178
|
+
fanSpeedLevel: deviceData.fanSpeedLevel,
|
|
1179
|
+
screenState: deviceData.screenState,
|
|
1180
|
+
screenSwitch: deviceData.screenSwitch,
|
|
1181
|
+
oscillationState: deviceData.oscillationState,
|
|
1182
|
+
oscillationSwitch: deviceData.oscillationSwitch,
|
|
1183
|
+
muteState: deviceData.muteState,
|
|
1184
|
+
muteSwitch: deviceData.muteSwitch,
|
|
1185
|
+
timerRemain: deviceData.timerRemain,
|
|
1186
|
+
temperature: deviceData.temperature || 0,
|
|
1187
|
+
humidity: deviceData.humidity || 0,
|
|
1188
|
+
thermalComfort: deviceData.thermalComfort,
|
|
1189
|
+
sleepPreference: deviceData.sleepPreference || {},
|
|
1190
|
+
scheduleCount: deviceData.scheduleCount,
|
|
1191
|
+
displayingType: deviceData.displayingType,
|
|
1192
|
+
errorCode: deviceData.errorCode
|
|
1193
|
+
};
|
|
1194
|
+
logger_1.logger.debug(`Successfully got details for device: ${this.deviceName}`);
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Toggle device power
|
|
1199
|
+
*/
|
|
1200
|
+
async toggleSwitch(toggle) {
|
|
1201
|
+
if (typeof toggle !== 'boolean') {
|
|
1202
|
+
logger_1.logger.debug('Invalid toggle value for tower fan switch');
|
|
1203
|
+
return false;
|
|
1204
|
+
}
|
|
1205
|
+
const [head, body] = this.buildApiDict('setSwitch');
|
|
1206
|
+
body.payload.data = {
|
|
1207
|
+
powerSwitch: toggle ? 1 : 0,
|
|
1208
|
+
switchIdx: 0
|
|
1209
|
+
};
|
|
1210
|
+
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
1211
|
+
const success = this.checkResponse([response, status], 'toggleSwitch');
|
|
1212
|
+
if (success) {
|
|
1213
|
+
this.deviceStatus = toggle ? 'on' : 'off';
|
|
1214
|
+
}
|
|
1215
|
+
else {
|
|
1216
|
+
logger_1.logger.debug('Error toggling tower fan power');
|
|
1217
|
+
}
|
|
1218
|
+
return success;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Turn device on
|
|
1222
|
+
*/
|
|
1223
|
+
async turnOn() {
|
|
1224
|
+
logger_1.logger.debug(`Turning on device: ${this.deviceName}`);
|
|
1225
|
+
return this.toggleSwitch(true);
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Turn device off
|
|
1229
|
+
*/
|
|
1230
|
+
async turnOff() {
|
|
1231
|
+
logger_1.logger.debug(`Turning off device: ${this.deviceName}`);
|
|
1232
|
+
return this.toggleSwitch(false);
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Set tower fan mode
|
|
1236
|
+
*/
|
|
1237
|
+
async mode_toggle(mode) {
|
|
1238
|
+
const validModes = this.towerModes.map(m => m.toLowerCase());
|
|
1239
|
+
if (!validModes.includes(mode.toLowerCase())) {
|
|
1240
|
+
logger_1.logger.debug(`Invalid tower fan mode used - ${mode}`);
|
|
1241
|
+
return false;
|
|
1242
|
+
}
|
|
1243
|
+
if (mode.toLowerCase() === 'off') {
|
|
1244
|
+
return this.turnOff();
|
|
1245
|
+
}
|
|
1246
|
+
const [head, body] = this.buildApiDict('setTowerFanMode');
|
|
1247
|
+
body.deviceId = this.cid;
|
|
1248
|
+
body.payload.data = {
|
|
1249
|
+
workMode: mode
|
|
1250
|
+
};
|
|
1251
|
+
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
1252
|
+
const success = this.checkResponse([response, status], 'mode_toggle');
|
|
1253
|
+
if (success) {
|
|
1254
|
+
this.details.workMode = mode;
|
|
1255
|
+
}
|
|
1256
|
+
else {
|
|
1257
|
+
logger_1.logger.debug('Error setting tower fan mode');
|
|
1258
|
+
}
|
|
1259
|
+
return success;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Change fan speed
|
|
1263
|
+
*/
|
|
1264
|
+
async changeFanSpeed(speed) {
|
|
1265
|
+
if (!this.speeds.includes(speed)) {
|
|
1266
|
+
logger_1.logger.debug(`Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`);
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
const [head, body] = this.buildApiDict('setLevel');
|
|
1270
|
+
body.payload.data = {
|
|
1271
|
+
levelIdx: 0,
|
|
1272
|
+
levelType: 'wind',
|
|
1273
|
+
manualSpeedLevel: speed
|
|
1274
|
+
};
|
|
1275
|
+
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
1276
|
+
const success = this.checkResponse([response, status], 'changeFanSpeed');
|
|
1277
|
+
if (success) {
|
|
1278
|
+
this.details.manualSpeedLevel = speed;
|
|
1279
|
+
}
|
|
1280
|
+
else {
|
|
1281
|
+
logger_1.logger.debug('Error setting fan speed');
|
|
1282
|
+
}
|
|
1283
|
+
return success;
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Set normal mode
|
|
1287
|
+
*/
|
|
1288
|
+
async normal_mode() {
|
|
1289
|
+
logger_1.logger.debug(`Setting normal mode for device: ${this.deviceName}`);
|
|
1290
|
+
return this.mode_toggle('normal');
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Set manual mode - adapter to set mode to normal
|
|
1294
|
+
*/
|
|
1295
|
+
async manual_mode() {
|
|
1296
|
+
logger_1.logger.debug(`Setting manual mode for device: ${this.deviceName}`);
|
|
1297
|
+
return this.normal_mode();
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Set advanced sleep mode
|
|
1301
|
+
*/
|
|
1302
|
+
async advanced_sleep_mode() {
|
|
1303
|
+
logger_1.logger.debug(`Setting advanced sleep mode for device: ${this.deviceName}`);
|
|
1304
|
+
return this.mode_toggle('advancedSleep');
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Set sleep mode - adapter for advanced sleep mode
|
|
1308
|
+
*/
|
|
1309
|
+
async sleep_mode() {
|
|
1310
|
+
logger_1.logger.debug(`Setting sleep mode for device: ${this.deviceName}`);
|
|
1311
|
+
return this.advanced_sleep_mode();
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Override setMode to handle tower fan specific modes
|
|
1315
|
+
*/
|
|
1316
|
+
async setMode(mode) {
|
|
1317
|
+
return this.mode_toggle(mode);
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Get current speed
|
|
1321
|
+
*/
|
|
1322
|
+
get speed() {
|
|
1323
|
+
return this.details.manualSpeedLevel || 0;
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Get display status
|
|
1327
|
+
*/
|
|
1328
|
+
get displayState() {
|
|
1329
|
+
return Boolean(this.details.screenState);
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Get oscillation status
|
|
1333
|
+
*/
|
|
1334
|
+
get oscillationState() {
|
|
1335
|
+
return Boolean(this.details.oscillationState);
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Get mute status
|
|
1339
|
+
*/
|
|
1340
|
+
get muteState() {
|
|
1341
|
+
return Boolean(this.details.muteState);
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Get timer status (in seconds)
|
|
1345
|
+
*/
|
|
1346
|
+
get timer() {
|
|
1347
|
+
return this.details.timerRemain || 0;
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Get temperature (in tenths of a degree)
|
|
1351
|
+
*/
|
|
1352
|
+
get temperature() {
|
|
1353
|
+
return this.details.temperature || 0;
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Get humidity percentage
|
|
1357
|
+
*/
|
|
1358
|
+
get humidity() {
|
|
1359
|
+
return this.details.humidity || 0;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Get thermal comfort level
|
|
1363
|
+
*/
|
|
1364
|
+
get thermalComfort() {
|
|
1365
|
+
return this.details.thermalComfort || 0;
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Get sleep preferences
|
|
1369
|
+
*/
|
|
1370
|
+
get sleepPreference() {
|
|
1371
|
+
return this.details.sleepPreference || {};
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Get schedule count
|
|
1375
|
+
*/
|
|
1376
|
+
get scheduleCount() {
|
|
1377
|
+
return this.details.scheduleCount || 0;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Get error code
|
|
1381
|
+
*/
|
|
1382
|
+
get errorCode() {
|
|
1383
|
+
return this.details.errorCode || 0;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Display device info
|
|
1387
|
+
*/
|
|
1388
|
+
display() {
|
|
1389
|
+
super.display();
|
|
1390
|
+
const info = [
|
|
1391
|
+
['Mode: ', this.details.workMode || ''],
|
|
1392
|
+
['Speed: ', this.speed],
|
|
1393
|
+
['Fan Speed Level: ', this.details.fanSpeedLevel || 0],
|
|
1394
|
+
['Display: ', this.displayState],
|
|
1395
|
+
['Display Switch: ', this.details.screenSwitch],
|
|
1396
|
+
['Oscillation: ', this.oscillationState],
|
|
1397
|
+
['Oscillation Switch: ', this.details.oscillationSwitch],
|
|
1398
|
+
['Mute: ', this.muteState],
|
|
1399
|
+
['Mute Switch: ', this.details.muteSwitch],
|
|
1400
|
+
['Timer: ', this.timer],
|
|
1401
|
+
['Temperature: ', this.temperature / 10, '°C'],
|
|
1402
|
+
['Humidity: ', this.humidity, '%'],
|
|
1403
|
+
['Thermal Comfort: ', this.thermalComfort],
|
|
1404
|
+
['Sleep Preference: ', JSON.stringify(this.sleepPreference)],
|
|
1405
|
+
['Schedule Count: ', this.scheduleCount],
|
|
1406
|
+
['Displaying Type: ', this.details.displayingType],
|
|
1407
|
+
['Error Code: ', this.errorCode]
|
|
1408
|
+
];
|
|
1409
|
+
for (const [key, value, unit = ''] of info) {
|
|
1410
|
+
logger_1.logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
exports.VeSyncTowerFan = VeSyncTowerFan;
|
|
1030
1415
|
// Export fan modules dictionary
|
|
1031
1416
|
exports.fanModules = {
|
|
1032
1417
|
// Core Series
|
|
@@ -1063,10 +1448,10 @@ exports.fanModules = {
|
|
|
1063
1448
|
'LAP-EL551S-WEU': VeSyncAirBypass,
|
|
1064
1449
|
'LAP-EL551S-WUS': VeSyncAirBypass,
|
|
1065
1450
|
// LTF Series
|
|
1066
|
-
'LTF-F422S-KEU':
|
|
1067
|
-
'LTF-F422S-WUSR':
|
|
1068
|
-
'LTF-F422_WJP':
|
|
1069
|
-
'LTF-F422S-WUS':
|
|
1451
|
+
'LTF-F422S-KEU': VeSyncTowerFan,
|
|
1452
|
+
'LTF-F422S-WUSR': VeSyncTowerFan,
|
|
1453
|
+
'LTF-F422_WJP': VeSyncTowerFan,
|
|
1454
|
+
'LTF-F422S-WUS': VeSyncTowerFan,
|
|
1070
1455
|
// Classic Series
|
|
1071
1456
|
'Classic300S': VeSyncHumidifier,
|
|
1072
1457
|
'Classic200S': VeSyncHumidifier,
|
package/package.json
CHANGED
package/src/lib/helpers.ts
CHANGED
|
@@ -58,7 +58,7 @@ export class Helpers {
|
|
|
58
58
|
/**
|
|
59
59
|
* Calculate MD5 hash
|
|
60
60
|
*/
|
|
61
|
-
static
|
|
61
|
+
static hashPassword(text: string): string {
|
|
62
62
|
return crypto.createHash('md5').update(text).digest('hex');
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -302,7 +302,7 @@ export class Helpers {
|
|
|
302
302
|
/**
|
|
303
303
|
* Calculate MD5 hash
|
|
304
304
|
*/
|
|
305
|
-
static
|
|
305
|
+
static md5(text: string): string {
|
|
306
306
|
return crypto.createHash('md5').update(text).digest('hex');
|
|
307
307
|
}
|
|
308
308
|
}
|
package/src/lib/vesyncFan.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { logger } from './logger';
|
|
|
8
8
|
|
|
9
9
|
interface FanConfig {
|
|
10
10
|
[key: string]: {
|
|
11
|
-
module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier';
|
|
11
|
+
module: 'VeSyncAirBypass' | 'VeSyncHumidifier' | 'VeSyncWarmHumidifier' | 'VeSyncTowerFan';
|
|
12
12
|
features: string[];
|
|
13
13
|
};
|
|
14
14
|
}
|
|
@@ -145,20 +145,20 @@ export const fanConfig: FanConfig = {
|
|
|
145
145
|
|
|
146
146
|
// LTF Series
|
|
147
147
|
'LTF-F422S-KEU': {
|
|
148
|
-
module: '
|
|
149
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
148
|
+
module: 'VeSyncTowerFan',
|
|
149
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
150
150
|
},
|
|
151
151
|
'LTF-F422S-WUSR': {
|
|
152
|
-
module: '
|
|
153
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
152
|
+
module: 'VeSyncTowerFan',
|
|
153
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
154
154
|
},
|
|
155
155
|
'LTF-F422_WJP': {
|
|
156
|
-
module: '
|
|
157
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
156
|
+
module: 'VeSyncTowerFan',
|
|
157
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
158
158
|
},
|
|
159
159
|
'LTF-F422S-WUS': {
|
|
160
|
-
module: '
|
|
161
|
-
features: ['display', 'child_lock', 'night_light', 'timer']
|
|
160
|
+
module: 'VeSyncTowerFan',
|
|
161
|
+
features: ['display', 'child_lock', 'night_light', 'timer', 'fan_speeds', 'tower_modes']
|
|
162
162
|
},
|
|
163
163
|
|
|
164
164
|
// Classic Series
|
package/src/lib/vesyncFanImpl.ts
CHANGED
|
@@ -1254,6 +1254,477 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
|
1254
1254
|
}
|
|
1255
1255
|
}
|
|
1256
1256
|
|
|
1257
|
+
/**
|
|
1258
|
+
* VeSync Air Purifier with Bypass V2
|
|
1259
|
+
*/
|
|
1260
|
+
export class VeSyncAirBaseV2 extends VeSyncAirBypass {
|
|
1261
|
+
protected _lightDetection: boolean = false;
|
|
1262
|
+
protected _lightDetectionState: boolean = false;
|
|
1263
|
+
|
|
1264
|
+
constructor(details: Record<string, any>, manager: VeSync) {
|
|
1265
|
+
super(details, manager);
|
|
1266
|
+
logger.debug(`Initialized VeSyncAirBaseV2 device: ${this.deviceName}`);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* Build API dictionary
|
|
1271
|
+
*/
|
|
1272
|
+
protected buildApiDict(method: string): [Record<string, any>, Record<string, any>] {
|
|
1273
|
+
const head = Helpers.reqHeaderBypass();
|
|
1274
|
+
const body = {
|
|
1275
|
+
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
1276
|
+
deviceId: this.cid,
|
|
1277
|
+
configModule: this.configModule,
|
|
1278
|
+
payload: {
|
|
1279
|
+
method,
|
|
1280
|
+
source: 'APP',
|
|
1281
|
+
data: {}
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
return [head, body];
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
get lightDetection(): boolean {
|
|
1288
|
+
return this._lightDetection;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
set lightDetection(toggle: boolean) {
|
|
1292
|
+
this._lightDetection = toggle;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
get lightDetectionState(): boolean {
|
|
1296
|
+
return this._lightDetectionState;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
async setLightDetection(toggle: boolean): Promise<boolean> {
|
|
1300
|
+
const [head, body] = this.buildApiDict('setLightDetection');
|
|
1301
|
+
if (!head || !body) {
|
|
1302
|
+
return false;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
body.payload.data = { enabled: toggle };
|
|
1306
|
+
|
|
1307
|
+
const [response, status] = await this.callApi(
|
|
1308
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1309
|
+
'post',
|
|
1310
|
+
body,
|
|
1311
|
+
head
|
|
1312
|
+
);
|
|
1313
|
+
|
|
1314
|
+
const success = this.checkResponse([response, status], 'setLightDetection');
|
|
1315
|
+
if (success) {
|
|
1316
|
+
this._lightDetection = toggle;
|
|
1317
|
+
}
|
|
1318
|
+
return success;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
async setLightDetectionOn(): Promise<boolean> {
|
|
1322
|
+
return this.setLightDetection(true);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
async setLightDetectionOff(): Promise<boolean> {
|
|
1326
|
+
return this.setLightDetection(false);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
async turboMode(): Promise<boolean> {
|
|
1330
|
+
return this.mode_toggle('turbo');
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
async petMode(): Promise<boolean> {
|
|
1334
|
+
return this.mode_toggle('pet');
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Build configuration dictionary
|
|
1339
|
+
*/
|
|
1340
|
+
protected buildConfigDict(configDict: Record<string, any>): void {
|
|
1341
|
+
if (configDict) {
|
|
1342
|
+
this.config = configDict;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* Set mode
|
|
1348
|
+
*/
|
|
1349
|
+
protected async mode_toggle(mode: string): Promise<boolean> {
|
|
1350
|
+
const validModes = this.modes.map(m => m.toLowerCase());
|
|
1351
|
+
if (!validModes.includes(mode.toLowerCase())) {
|
|
1352
|
+
logger.debug(`Invalid mode used - ${mode}`);
|
|
1353
|
+
return false;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
const [head, body] = this.buildApiDict('setPurifierMode');
|
|
1357
|
+
if (!head || !body) {
|
|
1358
|
+
return false;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
body.payload.data = {
|
|
1362
|
+
mode: mode
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
const [response, status] = await this.callApi(
|
|
1366
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1367
|
+
'post',
|
|
1368
|
+
body,
|
|
1369
|
+
head
|
|
1370
|
+
);
|
|
1371
|
+
|
|
1372
|
+
const success = this.checkResponse([response, status], 'mode_toggle');
|
|
1373
|
+
if (success) {
|
|
1374
|
+
this.details.mode = mode;
|
|
1375
|
+
} else {
|
|
1376
|
+
logger.debug('Error setting mode');
|
|
1377
|
+
}
|
|
1378
|
+
return success;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
/**
|
|
1383
|
+
* VeSync Tower Fan Implementation
|
|
1384
|
+
*/
|
|
1385
|
+
export class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
1386
|
+
protected readonly towerModes = ['normal', 'advancedSleep', 'off'] as const;
|
|
1387
|
+
protected readonly speeds = Array.from({length: 12}, (_, i) => i + 1);
|
|
1388
|
+
|
|
1389
|
+
constructor(details: Record<string, any>, manager: VeSync) {
|
|
1390
|
+
super(details, manager);
|
|
1391
|
+
logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
/**
|
|
1395
|
+
* Build API dictionary
|
|
1396
|
+
*/
|
|
1397
|
+
protected buildApiDict(method: string): [Record<string, any>, Record<string, any>] {
|
|
1398
|
+
const head = Helpers.reqHeaderBypass();
|
|
1399
|
+
const body = Helpers.reqBody(this.manager, 'bypassV2');
|
|
1400
|
+
body.cid = this.cid;
|
|
1401
|
+
body.deviceId = this.cid;
|
|
1402
|
+
body.configModule = this.configModule;
|
|
1403
|
+
body.configModel = this.configModule;
|
|
1404
|
+
body.payload = {
|
|
1405
|
+
method: method,
|
|
1406
|
+
source: 'APP',
|
|
1407
|
+
data: {}
|
|
1408
|
+
};
|
|
1409
|
+
return [head, body];
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Get device details
|
|
1414
|
+
*/
|
|
1415
|
+
async getDetails(): Promise<Boolean> {
|
|
1416
|
+
logger.debug(`Getting details for device: ${this.deviceName}`);
|
|
1417
|
+
const [head, body] = this.buildApiDict('getTowerFanStatus');
|
|
1418
|
+
|
|
1419
|
+
const [response, status] = await this.callApi(
|
|
1420
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1421
|
+
'post',
|
|
1422
|
+
body,
|
|
1423
|
+
head
|
|
1424
|
+
);
|
|
1425
|
+
|
|
1426
|
+
if (!this.checkResponse([response, status], 'getDetails') || !response?.result) {
|
|
1427
|
+
logger.debug('Error getting tower fan details');
|
|
1428
|
+
this.connectionStatus = 'offline';
|
|
1429
|
+
return false;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
const innerResponse = response.result;
|
|
1433
|
+
if (innerResponse.code !== 0 || !innerResponse.result) {
|
|
1434
|
+
logger.debug('Error in inner response from tower fan');
|
|
1435
|
+
this.connectionStatus = 'offline';
|
|
1436
|
+
return false;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
const deviceData = innerResponse.result;
|
|
1440
|
+
this.deviceStatus = deviceData.powerSwitch === 1 ? 'on' : 'off';
|
|
1441
|
+
this.details = {
|
|
1442
|
+
powerSwitch: deviceData.powerSwitch,
|
|
1443
|
+
workMode: deviceData.workMode,
|
|
1444
|
+
manualSpeedLevel: deviceData.manualSpeedLevel,
|
|
1445
|
+
fanSpeedLevel: deviceData.fanSpeedLevel,
|
|
1446
|
+
screenState: deviceData.screenState,
|
|
1447
|
+
screenSwitch: deviceData.screenSwitch,
|
|
1448
|
+
oscillationState: deviceData.oscillationState,
|
|
1449
|
+
oscillationSwitch: deviceData.oscillationSwitch,
|
|
1450
|
+
muteState: deviceData.muteState,
|
|
1451
|
+
muteSwitch: deviceData.muteSwitch,
|
|
1452
|
+
timerRemain: deviceData.timerRemain,
|
|
1453
|
+
temperature: deviceData.temperature || 0,
|
|
1454
|
+
humidity: deviceData.humidity || 0,
|
|
1455
|
+
thermalComfort: deviceData.thermalComfort,
|
|
1456
|
+
sleepPreference: deviceData.sleepPreference || {},
|
|
1457
|
+
scheduleCount: deviceData.scheduleCount,
|
|
1458
|
+
displayingType: deviceData.displayingType,
|
|
1459
|
+
errorCode: deviceData.errorCode
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1462
|
+
logger.debug(`Successfully got details for device: ${this.deviceName}`);
|
|
1463
|
+
return true;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
/**
|
|
1467
|
+
* Toggle device power
|
|
1468
|
+
*/
|
|
1469
|
+
async toggleSwitch(toggle: boolean): Promise<boolean> {
|
|
1470
|
+
if (typeof toggle !== 'boolean') {
|
|
1471
|
+
logger.debug('Invalid toggle value for tower fan switch');
|
|
1472
|
+
return false;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
const [head, body] = this.buildApiDict('setSwitch');
|
|
1476
|
+
body.payload.data = {
|
|
1477
|
+
powerSwitch: toggle ? 1 : 0,
|
|
1478
|
+
switchIdx: 0
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1481
|
+
const [response, status] = await this.callApi(
|
|
1482
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1483
|
+
'post',
|
|
1484
|
+
body,
|
|
1485
|
+
head
|
|
1486
|
+
);
|
|
1487
|
+
|
|
1488
|
+
const success = this.checkResponse([response, status], 'toggleSwitch');
|
|
1489
|
+
if (success) {
|
|
1490
|
+
this.deviceStatus = toggle ? 'on' : 'off';
|
|
1491
|
+
} else {
|
|
1492
|
+
logger.debug('Error toggling tower fan power');
|
|
1493
|
+
}
|
|
1494
|
+
return success;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
/**
|
|
1498
|
+
* Turn device on
|
|
1499
|
+
*/
|
|
1500
|
+
async turnOn(): Promise<boolean> {
|
|
1501
|
+
logger.debug(`Turning on device: ${this.deviceName}`);
|
|
1502
|
+
return this.toggleSwitch(true);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* Turn device off
|
|
1507
|
+
*/
|
|
1508
|
+
async turnOff(): Promise<boolean> {
|
|
1509
|
+
logger.debug(`Turning off device: ${this.deviceName}`);
|
|
1510
|
+
return this.toggleSwitch(false);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Set tower fan mode
|
|
1515
|
+
*/
|
|
1516
|
+
async mode_toggle(mode: string): Promise<boolean> {
|
|
1517
|
+
const validModes = this.towerModes.map(m => m.toLowerCase());
|
|
1518
|
+
if (!validModes.includes(mode.toLowerCase())) {
|
|
1519
|
+
logger.debug(`Invalid tower fan mode used - ${mode}`);
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
if (mode.toLowerCase() === 'off') {
|
|
1524
|
+
return this.turnOff();
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
const [head, body] = this.buildApiDict('setTowerFanMode');
|
|
1528
|
+
body.deviceId = this.cid;
|
|
1529
|
+
body.payload.data = {
|
|
1530
|
+
workMode: mode
|
|
1531
|
+
};
|
|
1532
|
+
|
|
1533
|
+
const [response, status] = await this.callApi(
|
|
1534
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1535
|
+
'post',
|
|
1536
|
+
body,
|
|
1537
|
+
head
|
|
1538
|
+
);
|
|
1539
|
+
|
|
1540
|
+
const success = this.checkResponse([response, status], 'mode_toggle');
|
|
1541
|
+
if (success) {
|
|
1542
|
+
this.details.workMode = mode;
|
|
1543
|
+
} else {
|
|
1544
|
+
logger.debug('Error setting tower fan mode');
|
|
1545
|
+
}
|
|
1546
|
+
return success;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
/**
|
|
1550
|
+
* Change fan speed
|
|
1551
|
+
*/
|
|
1552
|
+
async changeFanSpeed(speed: number): Promise<boolean> {
|
|
1553
|
+
if (!this.speeds.includes(speed)) {
|
|
1554
|
+
logger.debug(`Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`);
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
const [head, body] = this.buildApiDict('setLevel');
|
|
1559
|
+
body.payload.data = {
|
|
1560
|
+
levelIdx: 0,
|
|
1561
|
+
levelType: 'wind',
|
|
1562
|
+
manualSpeedLevel: speed
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
const [response, status] = await this.callApi(
|
|
1566
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
1567
|
+
'post',
|
|
1568
|
+
body,
|
|
1569
|
+
head
|
|
1570
|
+
);
|
|
1571
|
+
|
|
1572
|
+
const success = this.checkResponse([response, status], 'changeFanSpeed');
|
|
1573
|
+
if (success) {
|
|
1574
|
+
this.details.manualSpeedLevel = speed;
|
|
1575
|
+
} else {
|
|
1576
|
+
logger.debug('Error setting fan speed');
|
|
1577
|
+
}
|
|
1578
|
+
return success;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/**
|
|
1582
|
+
* Set normal mode
|
|
1583
|
+
*/
|
|
1584
|
+
async normal_mode(): Promise<boolean> {
|
|
1585
|
+
logger.debug(`Setting normal mode for device: ${this.deviceName}`);
|
|
1586
|
+
return this.mode_toggle('normal');
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* Set manual mode - adapter to set mode to normal
|
|
1591
|
+
*/
|
|
1592
|
+
async manual_mode(): Promise<boolean> {
|
|
1593
|
+
logger.debug(`Setting manual mode for device: ${this.deviceName}`);
|
|
1594
|
+
return this.normal_mode();
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Set advanced sleep mode
|
|
1599
|
+
*/
|
|
1600
|
+
async advanced_sleep_mode(): Promise<boolean> {
|
|
1601
|
+
logger.debug(`Setting advanced sleep mode for device: ${this.deviceName}`);
|
|
1602
|
+
return this.mode_toggle('advancedSleep');
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
/**
|
|
1606
|
+
* Set sleep mode - adapter for advanced sleep mode
|
|
1607
|
+
*/
|
|
1608
|
+
async sleep_mode(): Promise<boolean> {
|
|
1609
|
+
logger.debug(`Setting sleep mode for device: ${this.deviceName}`);
|
|
1610
|
+
return this.advanced_sleep_mode();
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/**
|
|
1614
|
+
* Override setMode to handle tower fan specific modes
|
|
1615
|
+
*/
|
|
1616
|
+
async setMode(mode: string): Promise<boolean> {
|
|
1617
|
+
return this.mode_toggle(mode);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
/**
|
|
1621
|
+
* Get current speed
|
|
1622
|
+
*/
|
|
1623
|
+
get speed(): number {
|
|
1624
|
+
return this.details.manualSpeedLevel || 0;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* Get display status
|
|
1629
|
+
*/
|
|
1630
|
+
get displayState(): boolean {
|
|
1631
|
+
return Boolean(this.details.screenState);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
/**
|
|
1635
|
+
* Get oscillation status
|
|
1636
|
+
*/
|
|
1637
|
+
get oscillationState(): boolean {
|
|
1638
|
+
return Boolean(this.details.oscillationState);
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
* Get mute status
|
|
1643
|
+
*/
|
|
1644
|
+
get muteState(): boolean {
|
|
1645
|
+
return Boolean(this.details.muteState);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Get timer status (in seconds)
|
|
1650
|
+
*/
|
|
1651
|
+
get timer(): number {
|
|
1652
|
+
return this.details.timerRemain || 0;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* Get temperature (in tenths of a degree)
|
|
1657
|
+
*/
|
|
1658
|
+
get temperature(): number {
|
|
1659
|
+
return this.details.temperature || 0;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* Get humidity percentage
|
|
1664
|
+
*/
|
|
1665
|
+
get humidity(): number {
|
|
1666
|
+
return this.details.humidity || 0;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
/**
|
|
1670
|
+
* Get thermal comfort level
|
|
1671
|
+
*/
|
|
1672
|
+
get thermalComfort(): number {
|
|
1673
|
+
return this.details.thermalComfort || 0;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
/**
|
|
1677
|
+
* Get sleep preferences
|
|
1678
|
+
*/
|
|
1679
|
+
get sleepPreference(): Record<string, any> {
|
|
1680
|
+
return this.details.sleepPreference || {};
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Get schedule count
|
|
1685
|
+
*/
|
|
1686
|
+
get scheduleCount(): number {
|
|
1687
|
+
return this.details.scheduleCount || 0;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
/**
|
|
1691
|
+
* Get error code
|
|
1692
|
+
*/
|
|
1693
|
+
get errorCode(): number {
|
|
1694
|
+
return this.details.errorCode || 0;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Display device info
|
|
1699
|
+
*/
|
|
1700
|
+
override display(): void {
|
|
1701
|
+
super.display();
|
|
1702
|
+
const info = [
|
|
1703
|
+
['Mode: ', this.details.workMode || ''],
|
|
1704
|
+
['Speed: ', this.speed],
|
|
1705
|
+
['Fan Speed Level: ', this.details.fanSpeedLevel || 0],
|
|
1706
|
+
['Display: ', this.displayState],
|
|
1707
|
+
['Display Switch: ', this.details.screenSwitch],
|
|
1708
|
+
['Oscillation: ', this.oscillationState],
|
|
1709
|
+
['Oscillation Switch: ', this.details.oscillationSwitch],
|
|
1710
|
+
['Mute: ', this.muteState],
|
|
1711
|
+
['Mute Switch: ', this.details.muteSwitch],
|
|
1712
|
+
['Timer: ', this.timer],
|
|
1713
|
+
['Temperature: ', this.temperature / 10, '°C'],
|
|
1714
|
+
['Humidity: ', this.humidity, '%'],
|
|
1715
|
+
['Thermal Comfort: ', this.thermalComfort],
|
|
1716
|
+
['Sleep Preference: ', JSON.stringify(this.sleepPreference)],
|
|
1717
|
+
['Schedule Count: ', this.scheduleCount],
|
|
1718
|
+
['Displaying Type: ', this.details.displayingType],
|
|
1719
|
+
['Error Code: ', this.errorCode]
|
|
1720
|
+
];
|
|
1721
|
+
|
|
1722
|
+
for (const [key, value, unit = ''] of info) {
|
|
1723
|
+
logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1257
1728
|
// Export fan modules dictionary
|
|
1258
1729
|
export const fanModules: Record<string, new (details: Record<string, any>, manager: VeSync) => VeSyncFan> = {
|
|
1259
1730
|
// Core Series
|
|
@@ -1292,10 +1763,10 @@ export const fanModules: Record<string, new (details: Record<string, any>, manag
|
|
|
1292
1763
|
'LAP-EL551S-WUS': VeSyncAirBypass,
|
|
1293
1764
|
|
|
1294
1765
|
// LTF Series
|
|
1295
|
-
'LTF-F422S-KEU':
|
|
1296
|
-
'LTF-F422S-WUSR':
|
|
1297
|
-
'LTF-F422_WJP':
|
|
1298
|
-
'LTF-F422S-WUS':
|
|
1766
|
+
'LTF-F422S-KEU': VeSyncTowerFan,
|
|
1767
|
+
'LTF-F422S-WUSR': VeSyncTowerFan,
|
|
1768
|
+
'LTF-F422_WJP': VeSyncTowerFan,
|
|
1769
|
+
'LTF-F422S-WUS': VeSyncTowerFan,
|
|
1299
1770
|
|
|
1300
1771
|
// Classic Series
|
|
1301
1772
|
'Classic300S': VeSyncHumidifier,
|