tsvesync 1.6.1 → 1.7.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/README.md +9 -1
- package/api/vesyncfan/LTF-F422S-KEU.yaml +152 -108
- package/api/vesyncfan/LTF-F422S-WJP.yaml +209 -0
- package/api/vesyncfan/LTF-F422S-WUS.yaml +152 -108
- package/api/vesyncfan/LTF-F422S-WUSR.yaml +155 -113
- package/api/vesyncfan/LTF-F422_WJP.yaml +152 -108
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/lib/fans/index.js +1 -0
- package/dist/lib/fans/towerFan.d.ts +29 -81
- package/dist/lib/fans/towerFan.js +158 -222
- package/dist/lib/vesyncFan.js +14 -4
- package/package.json +3 -2
- package/src/index.ts +3 -0
- package/src/lib/fans/index.ts +1 -0
- package/src/lib/fans/towerFan.ts +133 -260
- package/src/lib/vesyncFan.ts +14 -4
|
@@ -3,254 +3,190 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.VeSyncTowerFan = void 0;
|
|
4
4
|
const airBaseV2_1 = require("./airBaseV2");
|
|
5
5
|
const logger_1 = require("../logger");
|
|
6
|
-
/**
|
|
7
|
-
* VeSync Tower Fan Implementation
|
|
8
|
-
*/
|
|
6
|
+
/** LTF-F422S tower fans use different commands from bypassV2 purifiers. */
|
|
9
7
|
class VeSyncTowerFan extends airBaseV2_1.VeSyncAirBaseV2 {
|
|
10
8
|
constructor(details, manager) {
|
|
11
9
|
super(details, manager);
|
|
12
|
-
this.towerModes = ['normal', 'advancedSleep', 'off'];
|
|
13
|
-
logger_1.logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
|
|
10
|
+
this.towerModes = ['normal', 'auto', 'advancedSleep', 'turbo', 'off'];
|
|
14
11
|
}
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
logger_1.logger.debug('Error getting tower fan details');
|
|
24
|
-
this.connectionStatus = 'offline';
|
|
12
|
+
/** A successful cloud envelope can still contain a rejected device command. */
|
|
13
|
+
checkResponse(response, method) {
|
|
14
|
+
var _a, _b, _c;
|
|
15
|
+
if (!super.checkResponse(response, method))
|
|
16
|
+
return false;
|
|
17
|
+
const result = (_a = response[0]) === null || _a === void 0 ? void 0 : _a.result;
|
|
18
|
+
if (!result || result.code !== 0) {
|
|
19
|
+
this.logError(method, `Device error code ${(_b = result === null || result === void 0 ? void 0 : result.code) !== null && _b !== void 0 ? _b : 'missing'}: ${(_c = result === null || result === void 0 ? void 0 : result.msg) !== null && _c !== void 0 ? _c : 'Invalid device response'}`);
|
|
25
20
|
return false;
|
|
26
21
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
async request(method, data = {}) {
|
|
25
|
+
var _a;
|
|
26
|
+
const [head, body] = this.buildApiDict(method);
|
|
27
|
+
body.payload.data = data;
|
|
28
|
+
const response = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
29
|
+
if (!this.checkResponse(response, method))
|
|
30
|
+
return null;
|
|
31
|
+
this.connectionStatus = 'online';
|
|
32
|
+
return (_a = response[0].result.result) !== null && _a !== void 0 ? _a : {};
|
|
33
|
+
}
|
|
34
|
+
async getDetails() {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
const data = await this.request('getTowerFanStatus');
|
|
37
|
+
if (!data || (data.powerSwitch !== 0 && data.powerSwitch !== 1)) {
|
|
30
38
|
this.connectionStatus = 'offline';
|
|
31
39
|
return false;
|
|
32
40
|
}
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
screenState: deviceData.screenState,
|
|
41
|
-
screenSwitch: deviceData.screenSwitch,
|
|
42
|
-
oscillationState: deviceData.oscillationState,
|
|
43
|
-
oscillationSwitch: deviceData.oscillationSwitch,
|
|
44
|
-
timerRemain: deviceData.timerRemain,
|
|
45
|
-
temperature: deviceData.temperature || 0,
|
|
46
|
-
humidity: deviceData.humidity || 0,
|
|
47
|
-
thermalComfort: deviceData.thermalComfort,
|
|
48
|
-
sleepPreference: deviceData.sleepPreference || {},
|
|
49
|
-
scheduleCount: deviceData.scheduleCount,
|
|
50
|
-
displayingType: deviceData.displayingType,
|
|
51
|
-
errorCode: deviceData.errorCode
|
|
52
|
-
};
|
|
53
|
-
logger_1.logger.debug(`Successfully got details for device: ${this.deviceName}`);
|
|
41
|
+
this.deviceStatus = data.powerSwitch === 1 ? 'on' : 'off';
|
|
42
|
+
this.details = { ...data };
|
|
43
|
+
this.mode = (_a = data.workMode) !== null && _a !== void 0 ? _a : 'normal';
|
|
44
|
+
// A status poll cannot identify the timer. It may have been replaced in
|
|
45
|
+
// the app, so never carry an older command's id into cancellation.
|
|
46
|
+
this.timerId = undefined;
|
|
47
|
+
this.updateTimerState((_b = data.timerRemain) !== null && _b !== void 0 ? _b : 0, this.deviceStatus === 'on' ? 'off' : 'on');
|
|
54
48
|
return true;
|
|
55
49
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Toggle device power
|
|
58
|
-
*/
|
|
59
50
|
async toggleSwitch(toggle) {
|
|
60
|
-
if (typeof toggle !== 'boolean')
|
|
61
|
-
logger_1.logger.debug('Invalid toggle value for tower fan switch');
|
|
51
|
+
if (typeof toggle !== 'boolean')
|
|
62
52
|
return false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
70
|
-
const success = this.checkResponse([response, status], 'toggleSwitch');
|
|
71
|
-
if (success) {
|
|
72
|
-
this.deviceStatus = toggle ? 'on' : 'off';
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
logger_1.logger.debug('Error toggling tower fan power');
|
|
76
|
-
}
|
|
77
|
-
return success;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Turn device on
|
|
81
|
-
*/
|
|
82
|
-
async turnOn() {
|
|
83
|
-
logger_1.logger.debug(`Turning on device: ${this.deviceName}`);
|
|
84
|
-
return this.toggleSwitch(true);
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Turn device off
|
|
88
|
-
*/
|
|
89
|
-
async turnOff() {
|
|
90
|
-
logger_1.logger.debug(`Turning off device: ${this.deviceName}`);
|
|
91
|
-
return this.toggleSwitch(false);
|
|
53
|
+
if (!await this.request('setSwitch', { powerSwitch: Number(toggle), switchIdx: 0 }))
|
|
54
|
+
return false;
|
|
55
|
+
this.deviceStatus = toggle ? 'on' : 'off';
|
|
56
|
+
this.details.powerSwitch = Number(toggle);
|
|
57
|
+
return true;
|
|
92
58
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
*/
|
|
59
|
+
async turnOn() { return this.toggleSwitch(true); }
|
|
60
|
+
async turnOff() { return this.toggleSwitch(false); }
|
|
96
61
|
async mode_toggle(mode) {
|
|
97
|
-
|
|
98
|
-
|
|
62
|
+
const apiMode = mode === 'sleep' ? 'advancedSleep' : mode === 'manual' ? 'normal' : mode;
|
|
63
|
+
if (!this.towerModes.includes(apiMode))
|
|
99
64
|
return false;
|
|
100
|
-
|
|
101
|
-
if (mode === 'off') {
|
|
65
|
+
if (apiMode === 'off')
|
|
102
66
|
return this.turnOff();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
workMode: mode
|
|
108
|
-
};
|
|
109
|
-
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
110
|
-
const success = this.checkResponse([response, status], 'mode_toggle');
|
|
111
|
-
if (success) {
|
|
112
|
-
this.details.workMode = mode;
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
logger_1.logger.error(`Failed to set ${mode} mode for device: ${this.deviceName}`);
|
|
116
|
-
}
|
|
117
|
-
return success;
|
|
67
|
+
if (!await this.request('setTowerFanMode', { workMode: apiMode }))
|
|
68
|
+
return false;
|
|
69
|
+
this.mode = apiMode;
|
|
70
|
+
return true;
|
|
118
71
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
72
|
+
async setMode(mode) { return this.mode_toggle(mode); }
|
|
73
|
+
async normal_mode() { return this.setMode('normal'); }
|
|
74
|
+
async manual_mode() { return this.normal_mode(); }
|
|
75
|
+
async advanced_sleep_mode() { return this.setMode('sleep'); }
|
|
76
|
+
async sleep_mode() { return this.advanced_sleep_mode(); }
|
|
122
77
|
async changeFanSpeed(speed) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
levelIdx: 0,
|
|
131
|
-
levelType: 'wind',
|
|
132
|
-
manualSpeedLevel: speed
|
|
133
|
-
};
|
|
134
|
-
const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', body, head);
|
|
135
|
-
const success = this.checkResponse([response, status], 'changeFanSpeed');
|
|
136
|
-
if (success) {
|
|
137
|
-
this.details.manualSpeedLevel = speed;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
logger_1.logger.debug('Error setting fan speed');
|
|
141
|
-
}
|
|
142
|
-
return success;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Set normal mode
|
|
146
|
-
*/
|
|
147
|
-
async normal_mode() {
|
|
148
|
-
logger_1.logger.debug(`Setting normal mode for device: ${this.deviceName}`);
|
|
149
|
-
return this.mode_toggle('normal');
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Set manual mode - adapter to set mode to normal
|
|
153
|
-
*/
|
|
154
|
-
async manual_mode() {
|
|
155
|
-
logger_1.logger.debug(`Setting manual mode for device: ${this.deviceName}`);
|
|
156
|
-
return this.normal_mode();
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Set advanced sleep mode
|
|
160
|
-
*/
|
|
161
|
-
async advanced_sleep_mode() {
|
|
162
|
-
logger_1.logger.debug(`Setting advanced sleep mode for device: ${this.deviceName}`);
|
|
163
|
-
return this.mode_toggle('advancedSleep');
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Set sleep mode - adapter for advanced sleep mode
|
|
167
|
-
*/
|
|
168
|
-
async sleep_mode() {
|
|
169
|
-
logger_1.logger.debug(`Setting sleep mode for device: ${this.deviceName}`);
|
|
170
|
-
return this.advanced_sleep_mode();
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Override setMode to handle tower fan specific modes
|
|
174
|
-
*/
|
|
175
|
-
async setMode(mode) {
|
|
176
|
-
return this.mode_toggle(mode);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Get current speed
|
|
180
|
-
*/
|
|
181
|
-
get speed() {
|
|
182
|
-
return this.details.manualSpeedLevel || 0;
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Get display status
|
|
186
|
-
*/
|
|
187
|
-
get displayState() {
|
|
188
|
-
return Boolean(this.details.screenState);
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Get oscillation status
|
|
192
|
-
*/
|
|
193
|
-
get oscillationState() {
|
|
194
|
-
return Boolean(this.details.oscillationState);
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Get mute status
|
|
198
|
-
*/
|
|
199
|
-
get muteState() {
|
|
200
|
-
return Boolean(this.details.muteState);
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Get temperature (in tenths of a degree)
|
|
204
|
-
*/
|
|
205
|
-
get temperature() {
|
|
206
|
-
return this.details.temperature || 0;
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Get humidity percentage
|
|
210
|
-
*/
|
|
211
|
-
get humidity() {
|
|
212
|
-
return this.details.humidity || 0;
|
|
78
|
+
var _a;
|
|
79
|
+
if (!Number.isInteger(speed) || !((_a = this.config.levels) !== null && _a !== void 0 ? _a : []).includes(speed))
|
|
80
|
+
return false;
|
|
81
|
+
if (!await this.request('setLevel', { levelIdx: 0, levelType: 'wind', manualSpeedLevel: speed }))
|
|
82
|
+
return false;
|
|
83
|
+
this.details.manualSpeedLevel = speed;
|
|
84
|
+
return true;
|
|
213
85
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
86
|
+
async setOscillation(toggle) {
|
|
87
|
+
if (typeof toggle !== 'boolean')
|
|
88
|
+
return false;
|
|
89
|
+
if (!await this.request('setOscillationSwitch', { oscillationSwitch: Number(toggle) }))
|
|
90
|
+
return false;
|
|
91
|
+
this.details.oscillationSwitch = Number(toggle);
|
|
92
|
+
this.details.oscillationState = Number(toggle);
|
|
93
|
+
return true;
|
|
219
94
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
95
|
+
async setOscillationOn() { return this.setOscillation(true); }
|
|
96
|
+
async setOscillationOff() { return this.setOscillation(false); }
|
|
97
|
+
async setDisplay(enabled) {
|
|
98
|
+
if (typeof enabled !== 'boolean')
|
|
99
|
+
return false;
|
|
100
|
+
if (!await this.request('setDisplay', { screenSwitch: Number(enabled) }))
|
|
101
|
+
return false;
|
|
102
|
+
this.details.screenSwitch = Number(enabled);
|
|
103
|
+
this.details.screenState = Number(enabled);
|
|
104
|
+
return true;
|
|
225
105
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
106
|
+
async setMute(enabled) {
|
|
107
|
+
if (typeof enabled !== 'boolean')
|
|
108
|
+
return false;
|
|
109
|
+
if (!await this.request('setMuteSwitch', { muteSwitch: Number(enabled) }))
|
|
110
|
+
return false;
|
|
111
|
+
this.details.muteSwitch = Number(enabled);
|
|
112
|
+
this.details.muteState = Number(enabled);
|
|
113
|
+
return true;
|
|
231
114
|
}
|
|
232
|
-
/**
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
115
|
+
/** Public timer duration remains hours, like the other tsvesync devices. */
|
|
116
|
+
async setTimer(hours, action = this.deviceStatus === 'on' ? 'off' : 'on') {
|
|
117
|
+
const seconds = Math.round(hours * 3600);
|
|
118
|
+
if (!Number.isFinite(hours) || hours <= 0 || hours > 12 || seconds < 1 || !['on', 'off'].includes(action))
|
|
119
|
+
return false;
|
|
120
|
+
const result = await this.request('setTimer', { action, total: seconds });
|
|
121
|
+
if (!result || !Number.isInteger(result.id))
|
|
122
|
+
return false;
|
|
123
|
+
this.timerId = result.id;
|
|
124
|
+
this.updateTimerState(seconds, action);
|
|
125
|
+
return true;
|
|
237
126
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
127
|
+
async getTimer() {
|
|
128
|
+
const result = await this.request('getTimer');
|
|
129
|
+
if (!result || !('timers' in result) || (result.timers !== null && !Array.isArray(result.timers)))
|
|
130
|
+
return false;
|
|
131
|
+
if (result.timers === null || result.timers.length === 0) {
|
|
132
|
+
this.updateTimerState(0, 'off');
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
const timer = result.timers[0];
|
|
136
|
+
if (!Number.isInteger(timer.id) || !Number.isFinite(timer.remain) || timer.remain < 0 || !['on', 'off'].includes(timer.action))
|
|
137
|
+
return false;
|
|
138
|
+
this.timerId = timer.id;
|
|
139
|
+
this.updateTimerState(timer.remain, timer.action);
|
|
140
|
+
return true;
|
|
243
141
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
142
|
+
async clearTimer() {
|
|
143
|
+
// Status responses include the remaining duration but not the timer id.
|
|
144
|
+
if (this.timerRemaining > 0 && this.timerId === undefined && !await this.getTimer())
|
|
145
|
+
return false;
|
|
146
|
+
if (this.timerId === undefined)
|
|
147
|
+
return this.timerRemaining === 0;
|
|
148
|
+
if (!await this.request('clearTimer', { id: this.timerId }))
|
|
149
|
+
return false;
|
|
150
|
+
this.updateTimerState(0, 'off');
|
|
151
|
+
return true;
|
|
249
152
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
153
|
+
updateTimerState(seconds, action) {
|
|
154
|
+
const remaining = Number.isFinite(seconds) ? Math.max(0, seconds) : 0;
|
|
155
|
+
this.details.timerRemain = remaining;
|
|
156
|
+
this._timer = remaining > 0 ? { duration: remaining, action } : null;
|
|
157
|
+
this.timerDeadline = remaining > 0 ? Date.now() + remaining * 1000 : undefined;
|
|
158
|
+
if (remaining === 0)
|
|
159
|
+
this.timerId = undefined;
|
|
160
|
+
}
|
|
161
|
+
get timerRemaining() {
|
|
162
|
+
return this.timerDeadline === undefined ? 0 : Math.max(0, Math.ceil((this.timerDeadline - Date.now()) / 1000));
|
|
163
|
+
}
|
|
164
|
+
get mode() { var _a; return this.details.workMode === 'advancedSleep' ? 'sleep' : (_a = this.details.workMode) !== null && _a !== void 0 ? _a : 'normal'; }
|
|
165
|
+
set mode(value) {
|
|
166
|
+
this.details.workMode = value === 'sleep' ? 'advancedSleep' : value === 'manual' ? 'normal' : value;
|
|
167
|
+
this.details.mode = this.mode;
|
|
168
|
+
}
|
|
169
|
+
get speed() { var _a, _b; return (_b = (_a = this.details.manualSpeedLevel) !== null && _a !== void 0 ? _a : this.details.fanSpeedLevel) !== null && _b !== void 0 ? _b : 0; }
|
|
170
|
+
set speed(value) { this.details.manualSpeedLevel = value; }
|
|
171
|
+
get displayState() { return this.details.screenState === 1; }
|
|
172
|
+
get displayEnabled() { return this.details.screenSwitch === 1; }
|
|
173
|
+
get screenStatus() { return this.displayEnabled ? 'on' : 'off'; }
|
|
174
|
+
get oscillationState() { return this.details.oscillationState === 1; }
|
|
175
|
+
get oscillationEnabled() { return this.details.oscillationSwitch === 1; }
|
|
176
|
+
get muteState() { return this.details.muteState === 1; }
|
|
177
|
+
/** Raw API temperature is Fahrenheit times ten, retained for compatibility. */
|
|
178
|
+
get temperature() { var _a; return (_a = this.details.temperature) !== null && _a !== void 0 ? _a : 0; }
|
|
179
|
+
get temperatureCelsius() {
|
|
180
|
+
const raw = this.details.temperature;
|
|
181
|
+
return typeof raw === 'number' && Number.isFinite(raw) ? (raw / 10 - 32) * 5 / 9 : undefined;
|
|
182
|
+
}
|
|
183
|
+
get humidity() { var _a; return (_a = this.details.humidity) !== null && _a !== void 0 ? _a : 0; }
|
|
184
|
+
get thermalComfort() { var _a; return (_a = this.details.thermalComfort) !== null && _a !== void 0 ? _a : 0; }
|
|
185
|
+
get sleepPreference() { var _a; return (_a = this.details.sleepPreference) !== null && _a !== void 0 ? _a : {}; }
|
|
186
|
+
get scheduleCount() { var _a; return (_a = this.details.scheduleCount) !== null && _a !== void 0 ? _a : 0; }
|
|
187
|
+
get errorCode() { var _a; return (_a = this.details.errorCode) !== null && _a !== void 0 ? _a : 0; }
|
|
253
188
|
display() {
|
|
189
|
+
var _a;
|
|
254
190
|
super.display();
|
|
255
191
|
const info = [
|
|
256
192
|
['Mode: ', this.details.workMode || ''],
|
|
@@ -263,7 +199,7 @@ class VeSyncTowerFan extends airBaseV2_1.VeSyncAirBaseV2 {
|
|
|
263
199
|
['Mute: ', this.muteState],
|
|
264
200
|
['Mute Switch: ', this.details.muteSwitch],
|
|
265
201
|
['Timer: ', this.timer],
|
|
266
|
-
['Temperature: ', this.
|
|
202
|
+
['Temperature: ', (_a = this.temperatureCelsius) !== null && _a !== void 0 ? _a : 'unknown', '°C'],
|
|
267
203
|
['Humidity: ', this.humidity, '%'],
|
|
268
204
|
['Thermal Comfort: ', this.thermalComfort],
|
|
269
205
|
['Sleep Preference: ', JSON.stringify(this.sleepPreference)],
|
package/dist/lib/vesyncFan.js
CHANGED
|
@@ -229,25 +229,35 @@ exports.fanConfig = {
|
|
|
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', '
|
|
232
|
+
features: ['display', 'timer', 'fan_speeds', 'tower_modes', 'oscillation', 'temperature', 'mute'],
|
|
233
|
+
modes: ['normal', 'auto', 'sleep', 'turbo'],
|
|
233
234
|
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
234
235
|
},
|
|
235
236
|
'LTF-F422S-WUSR': {
|
|
236
237
|
module: 'VeSyncTowerFan',
|
|
237
238
|
// No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
|
|
238
|
-
features: ['display', '
|
|
239
|
+
features: ['display', 'timer', 'fan_speeds', 'tower_modes', 'oscillation', 'temperature', 'mute'],
|
|
240
|
+
modes: ['normal', 'auto', 'sleep', 'turbo'],
|
|
241
|
+
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
242
|
+
},
|
|
243
|
+
'LTF-F422S-WJP': {
|
|
244
|
+
module: 'VeSyncTowerFan',
|
|
245
|
+
features: ['display', 'timer', 'fan_speeds', 'tower_modes', 'oscillation', 'temperature', 'mute'],
|
|
246
|
+
modes: ['normal', 'auto', 'sleep', 'turbo'],
|
|
239
247
|
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
240
248
|
},
|
|
241
249
|
'LTF-F422_WJP': {
|
|
242
250
|
module: 'VeSyncTowerFan',
|
|
243
251
|
// No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
|
|
244
|
-
features: ['display', '
|
|
252
|
+
features: ['display', 'timer', 'fan_speeds', 'tower_modes', 'oscillation', 'temperature', 'mute'],
|
|
253
|
+
modes: ['normal', 'auto', 'sleep', 'turbo'],
|
|
245
254
|
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
246
255
|
},
|
|
247
256
|
'LTF-F422S-WUS': {
|
|
248
257
|
module: 'VeSyncTowerFan',
|
|
249
258
|
// No child_lock: the LTF-F422S API neither reports nor accepts it (pyvesync parity)
|
|
250
|
-
features: ['display', '
|
|
259
|
+
features: ['display', 'timer', 'fan_speeds', 'tower_modes', 'oscillation', 'temperature', 'mute'],
|
|
260
|
+
modes: ['normal', 'auto', 'sleep', 'turbo'],
|
|
251
261
|
levels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
|
252
262
|
},
|
|
253
263
|
// Classic Series
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsvesync",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"prepare": "npm run build",
|
|
13
13
|
"version": "git add -A src",
|
|
14
14
|
"postversion": "git push && git push --tags",
|
|
15
|
-
"test:token-refresh": "ts-node test/token-refresh-retry.test.ts"
|
|
15
|
+
"test:token-refresh": "ts-node test/token-refresh-retry.test.ts",
|
|
16
|
+
"test:tower-fan": "ts-node test/tower-fan-parity.test.ts"
|
|
16
17
|
},
|
|
17
18
|
"keywords": [
|
|
18
19
|
"vesync",
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,9 @@ export { VeSyncOutlet7A, VeSyncOutlet10A, VeSyncOutlet15A, VeSyncOutdoorPlug } f
|
|
|
7
7
|
export { VeSyncBulb } from './lib/vesyncBulb';
|
|
8
8
|
export { VeSyncWallSwitch, VeSyncDimmerSwitch } from './lib/vesyncSwitchImpl';
|
|
9
9
|
|
|
10
|
+
// Tower fan API
|
|
11
|
+
export { VeSyncTowerFan } from './lib/fans/towerFan';
|
|
12
|
+
|
|
10
13
|
// Export base device class
|
|
11
14
|
export { VeSyncBaseDevice } from './lib/vesyncBaseDevice';
|
|
12
15
|
|
package/src/lib/fans/index.ts
CHANGED
|
@@ -69,6 +69,7 @@ export const fanModules: Record<string, new (details: Record<string, any>, manag
|
|
|
69
69
|
// LTF Series
|
|
70
70
|
'LTF-F422S-KEU': VeSyncTowerFan,
|
|
71
71
|
'LTF-F422S-WUSR': VeSyncTowerFan,
|
|
72
|
+
'LTF-F422S-WJP': VeSyncTowerFan,
|
|
72
73
|
'LTF-F422_WJP': VeSyncTowerFan,
|
|
73
74
|
'LTF-F422S-WUS': VeSyncTowerFan,
|
|
74
75
|
|