tsvesync 1.0.38 → 1.0.40

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.
@@ -40,7 +40,7 @@ export declare class Helpers {
40
40
  /**
41
41
  * Calculate MD5 hash
42
42
  */
43
- static md5(text: string): string;
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 hashPassword(text: string): string;
87
+ static md5(text: string): string;
88
88
  }
@@ -34,7 +34,7 @@ class Helpers {
34
34
  /**
35
35
  * Calculate MD5 hash
36
36
  */
37
- static md5(text) {
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 hashPassword(text) {
253
+ static md5(text) {
254
254
  return crypto_1.default.createHash('md5').update(text).digest('hex');
255
255
  }
256
256
  }
@@ -245,13 +245,13 @@ export declare class VeSyncTowerFan extends VeSyncAirBaseV2 {
245
245
  protected readonly speeds: number[];
246
246
  constructor(details: Record<string, any>, manager: VeSync);
247
247
  /**
248
- * Get device details
248
+ * Build API dictionary
249
249
  */
250
- getDetails(): Promise<Boolean>;
250
+ protected buildApiDict(method: string): [Record<string, any>, Record<string, any>];
251
251
  /**
252
- * Change fan speed
252
+ * Get device details
253
253
  */
254
- changeFanSpeed(speed: number): Promise<boolean>;
254
+ getDetails(): Promise<Boolean>;
255
255
  /**
256
256
  * Toggle device power
257
257
  */
@@ -268,6 +268,10 @@ export declare class VeSyncTowerFan extends VeSyncAirBaseV2 {
268
268
  * Set tower fan mode
269
269
  */
270
270
  mode_toggle(mode: string): Promise<boolean>;
271
+ /**
272
+ * Change fan speed
273
+ */
274
+ changeFanSpeed(speed: number): Promise<boolean>;
271
275
  /**
272
276
  * Set normal mode
273
277
  */
@@ -297,17 +301,53 @@ export declare class VeSyncTowerFan extends VeSyncAirBaseV2 {
297
301
  */
298
302
  get displayState(): boolean;
299
303
  /**
300
- * Get child lock status
304
+ * Get oscillation status
301
305
  */
302
- get childLock(): string;
306
+ get oscillationState(): boolean;
303
307
  /**
304
- * Get night light status
308
+ * Get mute status
305
309
  */
306
- get nightLight(): string;
310
+ get muteState(): boolean;
307
311
  /**
308
- * Get timer status
312
+ * Get timer status (in seconds)
309
313
  */
310
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
+ * Set oscillation state
341
+ */
342
+ setOscillation(toggle: boolean): Promise<boolean>;
343
+ /**
344
+ * Turn oscillation on
345
+ */
346
+ setOscillationOn(): Promise<boolean>;
347
+ /**
348
+ * Turn oscillation off
349
+ */
350
+ setOscillationOff(): Promise<boolean>;
311
351
  /**
312
352
  * Display device info
313
353
  */
@@ -1134,84 +1134,75 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1134
1134
  this.speeds = Array.from({ length: 12 }, (_, i) => i + 1);
1135
1135
  logger_1.logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
1136
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
+ }
1137
1154
  /**
1138
1155
  * Get device details
1139
1156
  */
1140
1157
  async getDetails() {
1141
- var _a;
1142
1158
  logger_1.logger.debug(`Getting details for device: ${this.deviceName}`);
1143
1159
  const [head, body] = this.buildApiDict('getTowerFanStatus');
1144
- if (!head || !body) {
1145
- logger_1.logger.debug('Error building API request');
1146
- return false;
1147
- }
1148
1160
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
1149
- if (!this.checkResponse([response, status], 'getDetails') || !response) {
1150
- logger_1.logger.debug('Error getting purifier details');
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');
1151
1163
  this.connectionStatus = 'offline';
1152
1164
  return false;
1153
1165
  }
1154
- const innerResult = (_a = response.result) === null || _a === void 0 ? void 0 : _a.result;
1155
- if (!innerResult) {
1166
+ const innerResponse = response.result;
1167
+ if (innerResponse.code !== 0 || !innerResponse.result) {
1168
+ logger_1.logger.debug('Error in inner response from tower fan');
1156
1169
  this.connectionStatus = 'offline';
1157
- logger_1.logger.debug('Error in inner result dict from purifier');
1158
1170
  return false;
1159
1171
  }
1160
- this.deviceStatus = innerResult.enabled ? 'on' : 'off';
1172
+ const deviceData = innerResponse.result;
1173
+ this.deviceStatus = deviceData.powerSwitch === 1 ? 'on' : 'off';
1161
1174
  this.details = {
1162
- mode: innerResult.mode || '',
1163
- speed: innerResult.speed || 0,
1164
- display: innerResult.display || false,
1165
- child_lock: innerResult.child_lock || false,
1166
- night_light: innerResult.night_light || 'off',
1167
- timer: innerResult.timer || 0,
1168
- configuration: innerResult.configuration || {},
1169
- light_detection: innerResult.light_detection || false,
1170
- light_detection_state: innerResult.light_detection_state || false
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
1171
1193
  };
1172
- if (innerResult.configuration) {
1173
- this.buildConfigDict(innerResult.configuration);
1174
- }
1175
- this._lightDetection = innerResult.light_detection || false;
1176
- this._lightDetectionState = innerResult.light_detection_state || false;
1177
1194
  logger_1.logger.debug(`Successfully got details for device: ${this.deviceName}`);
1178
1195
  return true;
1179
1196
  }
1180
- /**
1181
- * Change fan speed
1182
- */
1183
- async changeFanSpeed(speed) {
1184
- if (!this.speeds.includes(speed)) {
1185
- logger_1.logger.debug(`Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`);
1186
- return false;
1187
- }
1188
- const [head, body] = this.buildApiDict('setLevel');
1189
- if (!head || !body) {
1190
- return false;
1191
- }
1192
- body.payload.data = {
1193
- levelIdx: 0,
1194
- levelType: 'wind',
1195
- manualSpeedLevel: speed
1196
- };
1197
- const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
1198
- const success = this.checkResponse([response, status], 'changeFanSpeed');
1199
- if (success) {
1200
- this.details.speed = speed;
1201
- }
1202
- else {
1203
- logger_1.logger.debug('Error setting fan speed');
1204
- }
1205
- return success;
1206
- }
1207
1197
  /**
1208
1198
  * Toggle device power
1209
1199
  */
1210
1200
  async toggleSwitch(toggle) {
1211
- const [head, body] = this.buildApiDict('setSwitch');
1212
- if (!head || !body) {
1201
+ if (typeof toggle !== 'boolean') {
1202
+ logger_1.logger.debug('Invalid toggle value for tower fan switch');
1213
1203
  return false;
1214
1204
  }
1205
+ const [head, body] = this.buildApiDict('setSwitch');
1215
1206
  body.payload.data = {
1216
1207
  powerSwitch: toggle ? 1 : 0,
1217
1208
  switchIdx: 0
@@ -1222,7 +1213,7 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1222
1213
  this.deviceStatus = toggle ? 'on' : 'off';
1223
1214
  }
1224
1215
  else {
1225
- logger_1.logger.debug('Error toggling device power');
1216
+ logger_1.logger.debug('Error toggling tower fan power');
1226
1217
  }
1227
1218
  return success;
1228
1219
  }
@@ -1246,16 +1237,13 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1246
1237
  async mode_toggle(mode) {
1247
1238
  const validModes = this.towerModes.map(m => m.toLowerCase());
1248
1239
  if (!validModes.includes(mode.toLowerCase())) {
1249
- logger_1.logger.debug(`Invalid purifier mode used - ${mode}`);
1240
+ logger_1.logger.debug(`Invalid tower fan mode used - ${mode}`);
1250
1241
  return false;
1251
1242
  }
1252
1243
  if (mode.toLowerCase() === 'off') {
1253
1244
  return this.turnOff();
1254
1245
  }
1255
1246
  const [head, body] = this.buildApiDict('setTowerFanMode');
1256
- if (!head || !body) {
1257
- return false;
1258
- }
1259
1247
  body.deviceId = this.cid;
1260
1248
  body.payload.data = {
1261
1249
  workMode: mode
@@ -1263,10 +1251,34 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1263
1251
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
1264
1252
  const success = this.checkResponse([response, status], 'mode_toggle');
1265
1253
  if (success) {
1266
- this.details.mode = mode;
1254
+ this.details.workMode = mode;
1267
1255
  }
1268
1256
  else {
1269
- logger_1.logger.debug('Error setting purifier mode');
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');
1270
1282
  }
1271
1283
  return success;
1272
1284
  }
@@ -1308,31 +1320,91 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1308
1320
  * Get current speed
1309
1321
  */
1310
1322
  get speed() {
1311
- return this.details.speed || 0;
1323
+ return this.details.manualSpeedLevel || 0;
1312
1324
  }
1313
1325
  /**
1314
1326
  * Get display status
1315
1327
  */
1316
1328
  get displayState() {
1317
- return this.details.display || false;
1329
+ return Boolean(this.details.screenState);
1318
1330
  }
1319
1331
  /**
1320
- * Get child lock status
1332
+ * Get oscillation status
1321
1333
  */
1322
- get childLock() {
1323
- return this.details.child_lock ? 'on' : 'off';
1334
+ get oscillationState() {
1335
+ return Boolean(this.details.oscillationState);
1324
1336
  }
1325
1337
  /**
1326
- * Get night light status
1338
+ * Get mute status
1327
1339
  */
1328
- get nightLight() {
1329
- return this.details.night_light || 'off';
1340
+ get muteState() {
1341
+ return Boolean(this.details.muteState);
1330
1342
  }
1331
1343
  /**
1332
- * Get timer status
1344
+ * Get timer status (in seconds)
1333
1345
  */
1334
1346
  get timer() {
1335
- return this.details.timer || 0;
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
+ * Set oscillation state
1387
+ */
1388
+ async setOscillation(toggle) {
1389
+ logger_1.logger.debug(`Setting oscillation to ${toggle ? 'on' : 'off'} for device: ${this.deviceName}`);
1390
+ const [head, body] = this.buildApiDict('setSwitch');
1391
+ body.payload.data = {
1392
+ oscillationSwitch: toggle ? 1 : 0
1393
+ };
1394
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
1395
+ return this.checkResponse([response, status], 'setOscillation');
1396
+ }
1397
+ /**
1398
+ * Turn oscillation on
1399
+ */
1400
+ async setOscillationOn() {
1401
+ return this.setOscillation(true);
1402
+ }
1403
+ /**
1404
+ * Turn oscillation off
1405
+ */
1406
+ async setOscillationOff() {
1407
+ return this.setOscillation(false);
1336
1408
  }
1337
1409
  /**
1338
1410
  * Display device info
@@ -1340,14 +1412,26 @@ class VeSyncTowerFan extends VeSyncAirBaseV2 {
1340
1412
  display() {
1341
1413
  super.display();
1342
1414
  const info = [
1415
+ ['Mode: ', this.details.workMode || ''],
1343
1416
  ['Speed: ', this.speed],
1417
+ ['Fan Speed Level: ', this.details.fanSpeedLevel || 0],
1344
1418
  ['Display: ', this.displayState],
1345
- ['Child Lock: ', this.childLock],
1346
- ['Night Light: ', this.nightLight],
1347
- ['Timer: ', this.timer]
1419
+ ['Display Switch: ', this.details.screenSwitch],
1420
+ ['Oscillation: ', this.oscillationState],
1421
+ ['Oscillation Switch: ', this.details.oscillationSwitch],
1422
+ ['Mute: ', this.muteState],
1423
+ ['Mute Switch: ', this.details.muteSwitch],
1424
+ ['Timer: ', this.timer],
1425
+ ['Temperature: ', this.temperature / 10, '°C'],
1426
+ ['Humidity: ', this.humidity, '%'],
1427
+ ['Thermal Comfort: ', this.thermalComfort],
1428
+ ['Sleep Preference: ', JSON.stringify(this.sleepPreference)],
1429
+ ['Schedule Count: ', this.scheduleCount],
1430
+ ['Displaying Type: ', this.details.displayingType],
1431
+ ['Error Code: ', this.errorCode]
1348
1432
  ];
1349
- for (const [key, value] of info) {
1350
- logger_1.logger.info(`${key.toString().padEnd(30, '.')} ${value}`);
1433
+ for (const [key, value, unit = ''] of info) {
1434
+ logger_1.logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
1351
1435
  }
1352
1436
  }
1353
1437
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
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",
@@ -58,7 +58,7 @@ export class Helpers {
58
58
  /**
59
59
  * Calculate MD5 hash
60
60
  */
61
- static md5(text: string): string {
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 hashPassword(text: string): string {
305
+ static md5(text: string): string {
306
306
  return crypto.createHash('md5').update(text).digest('hex');
307
307
  }
308
308
  }
@@ -1391,16 +1391,30 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1391
1391
  logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
1392
1392
  }
1393
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
+
1394
1412
  /**
1395
1413
  * Get device details
1396
1414
  */
1397
1415
  async getDetails(): Promise<Boolean> {
1398
1416
  logger.debug(`Getting details for device: ${this.deviceName}`);
1399
1417
  const [head, body] = this.buildApiDict('getTowerFanStatus');
1400
- if (!head || !body) {
1401
- logger.debug('Error building API request');
1402
- return false;
1403
- }
1404
1418
 
1405
1419
  const [response, status] = await this.callApi(
1406
1420
  '/cloud/v2/deviceManaged/bypassV2',
@@ -1409,88 +1423,56 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1409
1423
  head
1410
1424
  );
1411
1425
 
1412
- if (!this.checkResponse([response, status], 'getDetails') || !response) {
1413
- logger.debug('Error getting purifier details');
1426
+ if (!this.checkResponse([response, status], 'getDetails') || !response?.result) {
1427
+ logger.debug('Error getting tower fan details');
1414
1428
  this.connectionStatus = 'offline';
1415
1429
  return false;
1416
1430
  }
1417
1431
 
1418
- const innerResult = response.result?.result;
1419
- if (!innerResult) {
1432
+ const innerResponse = response.result;
1433
+ if (innerResponse.code !== 0 || !innerResponse.result) {
1434
+ logger.debug('Error in inner response from tower fan');
1420
1435
  this.connectionStatus = 'offline';
1421
- logger.debug('Error in inner result dict from purifier');
1422
1436
  return false;
1423
1437
  }
1424
1438
 
1425
- this.deviceStatus = innerResult.enabled ? 'on' : 'off';
1439
+ const deviceData = innerResponse.result;
1440
+ this.deviceStatus = deviceData.powerSwitch === 1 ? 'on' : 'off';
1426
1441
  this.details = {
1427
- mode: innerResult.mode || '',
1428
- speed: innerResult.speed || 0,
1429
- display: innerResult.display || false,
1430
- child_lock: innerResult.child_lock || false,
1431
- night_light: innerResult.night_light || 'off',
1432
- timer: innerResult.timer || 0,
1433
- configuration: innerResult.configuration || {},
1434
- light_detection: innerResult.light_detection || false,
1435
- light_detection_state: innerResult.light_detection_state || false
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
1436
1460
  };
1437
1461
 
1438
- if (innerResult.configuration) {
1439
- this.buildConfigDict(innerResult.configuration);
1440
- }
1441
-
1442
- this._lightDetection = innerResult.light_detection || false;
1443
- this._lightDetectionState = innerResult.light_detection_state || false;
1444
-
1445
1462
  logger.debug(`Successfully got details for device: ${this.deviceName}`);
1446
1463
  return true;
1447
1464
  }
1448
1465
 
1449
- /**
1450
- * Change fan speed
1451
- */
1452
- async changeFanSpeed(speed: number): Promise<boolean> {
1453
- if (!this.speeds.includes(speed)) {
1454
- logger.debug(`Invalid speed: ${speed}. Must be one of: ${this.speeds.join(', ')}`);
1455
- return false;
1456
- }
1457
-
1458
- const [head, body] = this.buildApiDict('setLevel');
1459
- if (!head || !body) {
1460
- return false;
1461
- }
1462
-
1463
- body.payload.data = {
1464
- levelIdx: 0,
1465
- levelType: 'wind',
1466
- manualSpeedLevel: speed
1467
- };
1468
-
1469
- const [response, status] = await this.callApi(
1470
- '/cloud/v2/deviceManaged/bypassV2',
1471
- 'post',
1472
- body,
1473
- head
1474
- );
1475
-
1476
- const success = this.checkResponse([response, status], 'changeFanSpeed');
1477
- if (success) {
1478
- this.details.speed = speed;
1479
- } else {
1480
- logger.debug('Error setting fan speed');
1481
- }
1482
- return success;
1483
- }
1484
-
1485
1466
  /**
1486
1467
  * Toggle device power
1487
1468
  */
1488
1469
  async toggleSwitch(toggle: boolean): Promise<boolean> {
1489
- const [head, body] = this.buildApiDict('setSwitch');
1490
- if (!head || !body) {
1470
+ if (typeof toggle !== 'boolean') {
1471
+ logger.debug('Invalid toggle value for tower fan switch');
1491
1472
  return false;
1492
1473
  }
1493
1474
 
1475
+ const [head, body] = this.buildApiDict('setSwitch');
1494
1476
  body.payload.data = {
1495
1477
  powerSwitch: toggle ? 1 : 0,
1496
1478
  switchIdx: 0
@@ -1507,7 +1489,7 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1507
1489
  if (success) {
1508
1490
  this.deviceStatus = toggle ? 'on' : 'off';
1509
1491
  } else {
1510
- logger.debug('Error toggling device power');
1492
+ logger.debug('Error toggling tower fan power');
1511
1493
  }
1512
1494
  return success;
1513
1495
  }
@@ -1534,7 +1516,7 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1534
1516
  async mode_toggle(mode: string): Promise<boolean> {
1535
1517
  const validModes = this.towerModes.map(m => m.toLowerCase());
1536
1518
  if (!validModes.includes(mode.toLowerCase())) {
1537
- logger.debug(`Invalid purifier mode used - ${mode}`);
1519
+ logger.debug(`Invalid tower fan mode used - ${mode}`);
1538
1520
  return false;
1539
1521
  }
1540
1522
 
@@ -1543,10 +1525,6 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1543
1525
  }
1544
1526
 
1545
1527
  const [head, body] = this.buildApiDict('setTowerFanMode');
1546
- if (!head || !body) {
1547
- return false;
1548
- }
1549
-
1550
1528
  body.deviceId = this.cid;
1551
1529
  body.payload.data = {
1552
1530
  workMode: mode
@@ -1561,9 +1539,41 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1561
1539
 
1562
1540
  const success = this.checkResponse([response, status], 'mode_toggle');
1563
1541
  if (success) {
1564
- this.details.mode = mode;
1542
+ this.details.workMode = mode;
1565
1543
  } else {
1566
- logger.debug('Error setting purifier mode');
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');
1567
1577
  }
1568
1578
  return success;
1569
1579
  }
@@ -1611,35 +1621,112 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1611
1621
  * Get current speed
1612
1622
  */
1613
1623
  get speed(): number {
1614
- return this.details.speed || 0;
1624
+ return this.details.manualSpeedLevel || 0;
1615
1625
  }
1616
1626
 
1617
1627
  /**
1618
1628
  * Get display status
1619
1629
  */
1620
1630
  get displayState(): boolean {
1621
- return this.details.display || false;
1631
+ return Boolean(this.details.screenState);
1622
1632
  }
1623
1633
 
1624
1634
  /**
1625
- * Get child lock status
1635
+ * Get oscillation status
1626
1636
  */
1627
- override get childLock(): string {
1628
- return this.details.child_lock ? 'on' : 'off';
1637
+ get oscillationState(): boolean {
1638
+ return Boolean(this.details.oscillationState);
1629
1639
  }
1630
1640
 
1631
1641
  /**
1632
- * Get night light status
1642
+ * Get mute status
1633
1643
  */
1634
- get nightLight(): string {
1635
- return this.details.night_light || 'off';
1644
+ get muteState(): boolean {
1645
+ return Boolean(this.details.muteState);
1636
1646
  }
1637
1647
 
1638
1648
  /**
1639
- * Get timer status
1649
+ * Get timer status (in seconds)
1640
1650
  */
1641
1651
  get timer(): number {
1642
- return this.details.timer || 0;
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
+ * Set oscillation state
1699
+ */
1700
+ async setOscillation(toggle: boolean): Promise<boolean> {
1701
+ logger.debug(`Setting oscillation to ${toggle ? 'on' : 'off'} for device: ${this.deviceName}`);
1702
+
1703
+ const [head, body] = this.buildApiDict('setSwitch');
1704
+ body.payload.data = {
1705
+ oscillationSwitch: toggle ? 1 : 0
1706
+ };
1707
+
1708
+ const [response, status] = await this.callApi(
1709
+ '/cloud/v2/deviceManaged/bypassV2',
1710
+ 'post',
1711
+ body,
1712
+ head
1713
+ );
1714
+
1715
+ return this.checkResponse([response, status], 'setOscillation');
1716
+ }
1717
+
1718
+ /**
1719
+ * Turn oscillation on
1720
+ */
1721
+ async setOscillationOn(): Promise<boolean> {
1722
+ return this.setOscillation(true);
1723
+ }
1724
+
1725
+ /**
1726
+ * Turn oscillation off
1727
+ */
1728
+ async setOscillationOff(): Promise<boolean> {
1729
+ return this.setOscillation(false);
1643
1730
  }
1644
1731
 
1645
1732
  /**
@@ -1648,15 +1735,27 @@ export class VeSyncTowerFan extends VeSyncAirBaseV2 {
1648
1735
  override display(): void {
1649
1736
  super.display();
1650
1737
  const info = [
1738
+ ['Mode: ', this.details.workMode || ''],
1651
1739
  ['Speed: ', this.speed],
1740
+ ['Fan Speed Level: ', this.details.fanSpeedLevel || 0],
1652
1741
  ['Display: ', this.displayState],
1653
- ['Child Lock: ', this.childLock],
1654
- ['Night Light: ', this.nightLight],
1655
- ['Timer: ', this.timer]
1742
+ ['Display Switch: ', this.details.screenSwitch],
1743
+ ['Oscillation: ', this.oscillationState],
1744
+ ['Oscillation Switch: ', this.details.oscillationSwitch],
1745
+ ['Mute: ', this.muteState],
1746
+ ['Mute Switch: ', this.details.muteSwitch],
1747
+ ['Timer: ', this.timer],
1748
+ ['Temperature: ', this.temperature / 10, '°C'],
1749
+ ['Humidity: ', this.humidity, '%'],
1750
+ ['Thermal Comfort: ', this.thermalComfort],
1751
+ ['Sleep Preference: ', JSON.stringify(this.sleepPreference)],
1752
+ ['Schedule Count: ', this.scheduleCount],
1753
+ ['Displaying Type: ', this.details.displayingType],
1754
+ ['Error Code: ', this.errorCode]
1656
1755
  ];
1657
1756
 
1658
- for (const [key, value] of info) {
1659
- logger.info(`${key.toString().padEnd(30, '.')} ${value}`);
1757
+ for (const [key, value, unit = ''] of info) {
1758
+ logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
1660
1759
  }
1661
1760
  }
1662
1761
  }