tsvesync 1.0.63 → 1.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/fans/airBaseV2.d.ts +68 -0
- package/dist/lib/fans/airBaseV2.js +226 -0
- package/dist/lib/fans/airBypass.d.ts +79 -0
- package/dist/lib/fans/airBypass.js +484 -0
- package/dist/lib/fans/humid200300S.d.ts +50 -0
- package/dist/lib/fans/humid200300S.js +200 -0
- package/dist/lib/fans/humidifier.d.ts +83 -0
- package/dist/lib/fans/humidifier.js +388 -0
- package/dist/lib/fans/index.d.ts +9 -0
- package/dist/lib/fans/index.js +95 -0
- package/dist/lib/fans/towerFan.d.ts +105 -0
- package/dist/lib/fans/towerFan.js +279 -0
- package/dist/lib/fans/warmHumidifier.d.ts +49 -0
- package/dist/lib/fans/warmHumidifier.js +174 -0
- package/dist/lib/vesyncFan.d.ts +2 -2
- package/dist/lib/vesyncFan.js +2 -2
- package/dist/lib/vesyncFanImpl.d.ts +1 -436
- package/dist/lib/vesyncFanImpl.js +16 -1845
- package/package.json +1 -1
- package/src/lib/fans/airBaseV2.ts +276 -0
- package/src/lib/fans/airBypass.ts +594 -0
- package/src/lib/fans/humid200300S.ts +234 -0
- package/src/lib/fans/humidifier.ts +470 -0
- package/src/lib/fans/index.ts +101 -0
- package/src/lib/fans/towerFan.ts +335 -0
- package/src/lib/fans/warmHumidifier.ts +202 -0
- package/src/lib/vesyncFan.ts +5 -5
- package/src/lib/vesyncFanImpl.ts +2 -2260
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { VeSyncAirBypass } from './airBypass';
|
|
2
|
+
import { VeSyncAirBaseV2 } from './airBaseV2';
|
|
3
|
+
import { VeSyncTowerFan } from './towerFan';
|
|
4
|
+
import { VeSyncHumidifier } from './humidifier';
|
|
5
|
+
import { VeSyncWarmHumidifier } from './warmHumidifier';
|
|
6
|
+
import { VeSyncHumid200300S } from './humid200300S';
|
|
7
|
+
import { VeSyncFan } from '../vesyncFan';
|
|
8
|
+
import { VeSync } from '../vesync';
|
|
9
|
+
|
|
10
|
+
// Re-export all classes
|
|
11
|
+
export { VeSyncAirBypass } from './airBypass';
|
|
12
|
+
export { VeSyncAirBaseV2 } from './airBaseV2';
|
|
13
|
+
export { VeSyncTowerFan } from './towerFan';
|
|
14
|
+
export { VeSyncHumidifier } from './humidifier';
|
|
15
|
+
export { VeSyncWarmHumidifier } from './warmHumidifier';
|
|
16
|
+
export { VeSyncHumid200300S } from './humid200300S';
|
|
17
|
+
|
|
18
|
+
// Export fan modules dictionary
|
|
19
|
+
export const fanModules: Record<string, new (details: Record<string, any>, manager: VeSync) => VeSyncFan> = {
|
|
20
|
+
// Core Series
|
|
21
|
+
'Core200S': VeSyncAirBypass,
|
|
22
|
+
'Core300S': VeSyncAirBypass,
|
|
23
|
+
'Core400S': VeSyncAirBypass,
|
|
24
|
+
'Core600S': VeSyncAirBypass,
|
|
25
|
+
|
|
26
|
+
// LAP Series
|
|
27
|
+
'LAP-C201S-AUSR': VeSyncAirBypass,
|
|
28
|
+
'LAP-C202S-WUSR': VeSyncAirBypass,
|
|
29
|
+
'LAP-C301S-WJP': VeSyncAirBypass,
|
|
30
|
+
'LAP-C302S-WUSB': VeSyncAirBypass,
|
|
31
|
+
'LAP-C301S-WAAA': VeSyncAirBypass,
|
|
32
|
+
'LAP-C401S-WJP': VeSyncAirBypass,
|
|
33
|
+
'LAP-C401S-WUSR': VeSyncAirBypass,
|
|
34
|
+
'LAP-C401S-WAAA': VeSyncAirBypass,
|
|
35
|
+
'LAP-C601S-WUS': VeSyncAirBypass,
|
|
36
|
+
'LAP-C601S-WUSR': VeSyncAirBypass,
|
|
37
|
+
'LAP-C601S-WEU': VeSyncAirBypass,
|
|
38
|
+
|
|
39
|
+
// Vital100S Series - Using VeSyncAirBaseV2
|
|
40
|
+
'LAP-V102S-AASR': VeSyncAirBaseV2,
|
|
41
|
+
'LAP-V102S-WUS': VeSyncAirBaseV2,
|
|
42
|
+
'LAP-V102S-WEU': VeSyncAirBaseV2,
|
|
43
|
+
'LAP-V102S-AUSR': VeSyncAirBaseV2,
|
|
44
|
+
'LAP-V102S-WJP': VeSyncAirBaseV2,
|
|
45
|
+
|
|
46
|
+
// Vital200S Series - Using VeSyncAirBaseV2
|
|
47
|
+
'LAP-V201S-AASR': VeSyncAirBaseV2,
|
|
48
|
+
'LAP-V201S-WJP': VeSyncAirBaseV2,
|
|
49
|
+
'LAP-V201S-WEU': VeSyncAirBaseV2,
|
|
50
|
+
'LAP-V201S-WUS': VeSyncAirBaseV2,
|
|
51
|
+
'LAP-V201-AUSR': VeSyncAirBaseV2,
|
|
52
|
+
'LAP-V201S-AUSR': VeSyncAirBaseV2,
|
|
53
|
+
'LAP-V201S-AEUR': VeSyncAirBaseV2,
|
|
54
|
+
|
|
55
|
+
// EverestAir Series - Using VeSyncAirBaseV2
|
|
56
|
+
'LAP-EL551S-AUS': VeSyncAirBaseV2,
|
|
57
|
+
'LAP-EL551S-AEUR': VeSyncAirBaseV2,
|
|
58
|
+
'LAP-EL551S-WEU': VeSyncAirBaseV2,
|
|
59
|
+
'LAP-EL551S-WUS': VeSyncAirBaseV2,
|
|
60
|
+
|
|
61
|
+
// LTF Series
|
|
62
|
+
'LTF-F422S-KEU': VeSyncTowerFan,
|
|
63
|
+
'LTF-F422S-WUSR': VeSyncTowerFan,
|
|
64
|
+
'LTF-F422_WJP': VeSyncTowerFan,
|
|
65
|
+
'LTF-F422S-WUS': VeSyncTowerFan,
|
|
66
|
+
|
|
67
|
+
// Classic Series
|
|
68
|
+
'Classic300S': VeSyncHumidifier,
|
|
69
|
+
'Classic200S': VeSyncHumidifier,
|
|
70
|
+
|
|
71
|
+
// Dual Series
|
|
72
|
+
'Dual200S': VeSyncHumidifier,
|
|
73
|
+
|
|
74
|
+
// LUH Series
|
|
75
|
+
'LUH-A601S-WUSB': VeSyncHumid200300S,
|
|
76
|
+
'LUH-A601S-AUSW': VeSyncHumid200300S,
|
|
77
|
+
'LUH-D301S-WUSR': VeSyncHumid200300S,
|
|
78
|
+
'LUH-D301S-WJP': VeSyncHumid200300S,
|
|
79
|
+
'LUH-D301S-WEU': VeSyncHumid200300S,
|
|
80
|
+
'LUH-A602S-WUSR': VeSyncHumid200300S,
|
|
81
|
+
'LUH-A602S-WUS': VeSyncHumid200300S,
|
|
82
|
+
'LUH-A602S-WEUR': VeSyncHumid200300S,
|
|
83
|
+
'LUH-A602S-WEU': VeSyncHumid200300S,
|
|
84
|
+
'LUH-A602S-WJP': VeSyncHumid200300S,
|
|
85
|
+
'LUH-A602S-WUSC': VeSyncHumid200300S,
|
|
86
|
+
'LUH-O451S-WEU': VeSyncHumid200300S,
|
|
87
|
+
'LUH-O451S-WUS': VeSyncHumid200300S,
|
|
88
|
+
'LUH-O451S-WUSR': VeSyncHumid200300S,
|
|
89
|
+
'LUH-O601S-WUS': VeSyncHumid200300S,
|
|
90
|
+
'LUH-O601S-KUS': VeSyncHumid200300S,
|
|
91
|
+
'LUH-M101S-WUS': VeSyncHumid200300S,
|
|
92
|
+
'LUH-M101S-WEUR': VeSyncHumid200300S,
|
|
93
|
+
|
|
94
|
+
// LEH Series
|
|
95
|
+
'LEH-S601S-WUS': VeSyncHumid200300S,
|
|
96
|
+
'LEH-S601S-WUSR': VeSyncHumid200300S,
|
|
97
|
+
|
|
98
|
+
// LV Series
|
|
99
|
+
'LV-PUR131S': VeSyncAirBypass,
|
|
100
|
+
'LV-RH131S': VeSyncAirBypass
|
|
101
|
+
};
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { VeSyncAirBaseV2 } from './airBaseV2';
|
|
2
|
+
import { VeSync } from '../vesync';
|
|
3
|
+
import { logger } from '../logger';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* VeSync Tower Fan Implementation
|
|
7
|
+
*/
|
|
8
|
+
export class VeSyncTowerFan extends VeSyncAirBaseV2 {
|
|
9
|
+
protected readonly towerModes = ['normal', 'advancedSleep', 'off'] as const;
|
|
10
|
+
|
|
11
|
+
constructor(details: Record<string, any>, manager: VeSync) {
|
|
12
|
+
super(details, manager);
|
|
13
|
+
logger.debug(`Initialized VeSyncTowerFan device: ${this.deviceName}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get device details
|
|
18
|
+
*/
|
|
19
|
+
async getDetails(): Promise<Boolean> {
|
|
20
|
+
logger.debug(`Getting details for device: ${this.deviceName}`);
|
|
21
|
+
const [head, body] = this.buildApiDict('getTowerFanStatus');
|
|
22
|
+
|
|
23
|
+
const [response, status] = await this.callApi(
|
|
24
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
25
|
+
'post',
|
|
26
|
+
body,
|
|
27
|
+
head
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (!this.checkResponse([response, status], 'getDetails') || !response?.result) {
|
|
31
|
+
logger.debug('Error getting tower fan details');
|
|
32
|
+
this.connectionStatus = 'offline';
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const innerResponse = response.result;
|
|
37
|
+
if (innerResponse.code !== 0 || !innerResponse.result) {
|
|
38
|
+
logger.debug('Error in inner response from tower fan');
|
|
39
|
+
this.connectionStatus = 'offline';
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const deviceData = innerResponse.result;
|
|
44
|
+
this.deviceStatus = deviceData.powerSwitch === 1 ? 'on' : 'off';
|
|
45
|
+
this.details = {
|
|
46
|
+
powerSwitch: deviceData.powerSwitch,
|
|
47
|
+
workMode: deviceData.workMode,
|
|
48
|
+
manualSpeedLevel: deviceData.manualSpeedLevel,
|
|
49
|
+
fanSpeedLevel: deviceData.fanSpeedLevel,
|
|
50
|
+
screenState: deviceData.screenState,
|
|
51
|
+
screenSwitch: deviceData.screenSwitch,
|
|
52
|
+
oscillationState: deviceData.oscillationState,
|
|
53
|
+
oscillationSwitch: deviceData.oscillationSwitch,
|
|
54
|
+
timerRemain: deviceData.timerRemain,
|
|
55
|
+
temperature: deviceData.temperature || 0,
|
|
56
|
+
humidity: deviceData.humidity || 0,
|
|
57
|
+
thermalComfort: deviceData.thermalComfort,
|
|
58
|
+
sleepPreference: deviceData.sleepPreference || {},
|
|
59
|
+
scheduleCount: deviceData.scheduleCount,
|
|
60
|
+
displayingType: deviceData.displayingType,
|
|
61
|
+
errorCode: deviceData.errorCode
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
logger.debug(`Successfully got details for device: ${this.deviceName}`);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Toggle device power
|
|
70
|
+
*/
|
|
71
|
+
async toggleSwitch(toggle: boolean): Promise<boolean> {
|
|
72
|
+
if (typeof toggle !== 'boolean') {
|
|
73
|
+
logger.debug('Invalid toggle value for tower fan switch');
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const [head, body] = this.buildApiDict('setSwitch');
|
|
78
|
+
body.payload.data = {
|
|
79
|
+
powerSwitch: toggle ? 1 : 0,
|
|
80
|
+
switchIdx: 0
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const [response, status] = await this.callApi(
|
|
84
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
85
|
+
'post',
|
|
86
|
+
body,
|
|
87
|
+
head
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const success = this.checkResponse([response, status], 'toggleSwitch');
|
|
91
|
+
if (success) {
|
|
92
|
+
this.deviceStatus = toggle ? 'on' : 'off';
|
|
93
|
+
} else {
|
|
94
|
+
logger.debug('Error toggling tower fan power');
|
|
95
|
+
}
|
|
96
|
+
return success;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Turn device on
|
|
101
|
+
*/
|
|
102
|
+
async turnOn(): Promise<boolean> {
|
|
103
|
+
logger.debug(`Turning on device: ${this.deviceName}`);
|
|
104
|
+
return this.toggleSwitch(true);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Turn device off
|
|
109
|
+
*/
|
|
110
|
+
async turnOff(): Promise<boolean> {
|
|
111
|
+
logger.debug(`Turning off device: ${this.deviceName}`);
|
|
112
|
+
return this.toggleSwitch(false);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Set tower fan mode
|
|
117
|
+
*/
|
|
118
|
+
async mode_toggle(mode: string): Promise<boolean> {
|
|
119
|
+
if (!this.towerModes.includes(mode as any)) {
|
|
120
|
+
logger.debug(`Invalid mode: ${mode}. Must be one of: ${this.towerModes.join(', ')}`);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (mode === 'off') {
|
|
125
|
+
return this.turnOff();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const [head, body] = this.buildApiDict('setTowerFanMode');
|
|
129
|
+
body.deviceId = this.cid;
|
|
130
|
+
body.payload.data = {
|
|
131
|
+
workMode: mode
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const [response, status] = await this.callApi(
|
|
135
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
136
|
+
'post',
|
|
137
|
+
body,
|
|
138
|
+
head
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const success = this.checkResponse([response, status], 'mode_toggle');
|
|
142
|
+
if (success) {
|
|
143
|
+
this.details.workMode = mode;
|
|
144
|
+
} else {
|
|
145
|
+
logger.error(`Failed to set ${mode} mode for device: ${this.deviceName}`);
|
|
146
|
+
}
|
|
147
|
+
return success;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Change fan speed
|
|
152
|
+
*/
|
|
153
|
+
async changeFanSpeed(speed: number): Promise<boolean> {
|
|
154
|
+
// const speeds = this.config.levels ?? [];
|
|
155
|
+
// if (!speeds.includes(speed)) {
|
|
156
|
+
// logger.debug(`Invalid speed: ${speed}. Must be one of: ${speeds.join(', ')}`);
|
|
157
|
+
// return false;
|
|
158
|
+
// }
|
|
159
|
+
|
|
160
|
+
const [head, body] = this.buildApiDict('setLevel');
|
|
161
|
+
body.payload.data = {
|
|
162
|
+
levelIdx: 0,
|
|
163
|
+
levelType: 'wind',
|
|
164
|
+
manualSpeedLevel: speed
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const [response, status] = await this.callApi(
|
|
168
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
169
|
+
'post',
|
|
170
|
+
body,
|
|
171
|
+
head
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const success = this.checkResponse([response, status], 'changeFanSpeed');
|
|
175
|
+
if (success) {
|
|
176
|
+
this.details.manualSpeedLevel = speed;
|
|
177
|
+
} else {
|
|
178
|
+
logger.debug('Error setting fan speed');
|
|
179
|
+
}
|
|
180
|
+
return success;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Set normal mode
|
|
185
|
+
*/
|
|
186
|
+
async normal_mode(): Promise<boolean> {
|
|
187
|
+
logger.debug(`Setting normal mode for device: ${this.deviceName}`);
|
|
188
|
+
return this.mode_toggle('normal');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Set manual mode - adapter to set mode to normal
|
|
193
|
+
*/
|
|
194
|
+
async manual_mode(): Promise<boolean> {
|
|
195
|
+
logger.debug(`Setting manual mode for device: ${this.deviceName}`);
|
|
196
|
+
return this.normal_mode();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Set advanced sleep mode
|
|
201
|
+
*/
|
|
202
|
+
async advanced_sleep_mode(): Promise<boolean> {
|
|
203
|
+
logger.debug(`Setting advanced sleep mode for device: ${this.deviceName}`);
|
|
204
|
+
return this.mode_toggle('advancedSleep');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Set sleep mode - adapter for advanced sleep mode
|
|
209
|
+
*/
|
|
210
|
+
async sleep_mode(): Promise<boolean> {
|
|
211
|
+
logger.debug(`Setting sleep mode for device: ${this.deviceName}`);
|
|
212
|
+
return this.advanced_sleep_mode();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Override setMode to handle tower fan specific modes
|
|
217
|
+
*/
|
|
218
|
+
async setMode(mode: string): Promise<boolean> {
|
|
219
|
+
return this.mode_toggle(mode);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get current speed
|
|
224
|
+
*/
|
|
225
|
+
get speed(): number {
|
|
226
|
+
return this.details.manualSpeedLevel || 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get display status
|
|
231
|
+
*/
|
|
232
|
+
get displayState(): boolean {
|
|
233
|
+
return Boolean(this.details.screenState);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get oscillation status
|
|
238
|
+
*/
|
|
239
|
+
get oscillationState(): boolean {
|
|
240
|
+
return Boolean(this.details.oscillationState);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get mute status
|
|
245
|
+
*/
|
|
246
|
+
get muteState(): boolean {
|
|
247
|
+
return Boolean(this.details.muteState);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get temperature (in tenths of a degree)
|
|
252
|
+
*/
|
|
253
|
+
get temperature(): number {
|
|
254
|
+
return this.details.temperature || 0;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get humidity percentage
|
|
259
|
+
*/
|
|
260
|
+
get humidity(): number {
|
|
261
|
+
return this.details.humidity || 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get thermal comfort level
|
|
266
|
+
*/
|
|
267
|
+
get thermalComfort(): number {
|
|
268
|
+
return this.details.thermalComfort || 0;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Get sleep preferences
|
|
273
|
+
*/
|
|
274
|
+
get sleepPreference(): Record<string, any> {
|
|
275
|
+
return this.details.sleepPreference || {};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get schedule count
|
|
280
|
+
*/
|
|
281
|
+
get scheduleCount(): number {
|
|
282
|
+
return this.details.scheduleCount || 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get error code
|
|
287
|
+
*/
|
|
288
|
+
get errorCode(): number {
|
|
289
|
+
return this.details.errorCode || 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Turn oscillation on
|
|
294
|
+
*/
|
|
295
|
+
async setOscillationOn(): Promise<boolean> {
|
|
296
|
+
return this.setOscillation(true);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Turn oscillation off
|
|
301
|
+
*/
|
|
302
|
+
async setOscillationOff(): Promise<boolean> {
|
|
303
|
+
return this.setOscillation(false);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Display device info
|
|
308
|
+
*/
|
|
309
|
+
override display(): void {
|
|
310
|
+
super.display();
|
|
311
|
+
const info = [
|
|
312
|
+
['Mode: ', this.details.workMode || ''],
|
|
313
|
+
['Speed: ', this.speed],
|
|
314
|
+
['Fan Speed Level: ', this.details.fanSpeedLevel || 0],
|
|
315
|
+
['Display: ', this.displayState],
|
|
316
|
+
['Display Switch: ', this.details.screenSwitch],
|
|
317
|
+
['Oscillation: ', this.oscillationState],
|
|
318
|
+
['Oscillation Switch: ', this.details.oscillationSwitch],
|
|
319
|
+
['Mute: ', this.muteState],
|
|
320
|
+
['Mute Switch: ', this.details.muteSwitch],
|
|
321
|
+
['Timer: ', this.timer],
|
|
322
|
+
['Temperature: ', this.temperature / 10, '°C'],
|
|
323
|
+
['Humidity: ', this.humidity, '%'],
|
|
324
|
+
['Thermal Comfort: ', this.thermalComfort],
|
|
325
|
+
['Sleep Preference: ', JSON.stringify(this.sleepPreference)],
|
|
326
|
+
['Schedule Count: ', this.scheduleCount],
|
|
327
|
+
['Displaying Type: ', this.details.displayingType],
|
|
328
|
+
['Error Code: ', this.errorCode]
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
for (const [key, value, unit = ''] of info) {
|
|
332
|
+
logger.info(`${key.toString().padEnd(30, '.')} ${value}${unit}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { VeSyncHumidifier } from './humidifier';
|
|
2
|
+
import { VeSync } from '../vesync';
|
|
3
|
+
import { Helpers } from '../helpers';
|
|
4
|
+
import { logger } from '../logger';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* VeSync Warm Humidifier Class
|
|
8
|
+
*/
|
|
9
|
+
export class VeSyncWarmHumidifier extends VeSyncHumidifier {
|
|
10
|
+
protected readonly warmLevels = [1, 2, 3];
|
|
11
|
+
|
|
12
|
+
constructor(details: Record<string, any>, manager: VeSync) {
|
|
13
|
+
super(details, manager);
|
|
14
|
+
logger.debug(`Initialized VeSyncWarmHumidifier device: ${this.deviceName}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get device details
|
|
19
|
+
*/
|
|
20
|
+
async getDetails(): Promise<Boolean> {
|
|
21
|
+
const success = await super.getDetails();
|
|
22
|
+
if (success && this.details) {
|
|
23
|
+
// Add warm humidifier specific fields
|
|
24
|
+
this.details = {
|
|
25
|
+
...this.details,
|
|
26
|
+
warm_enabled: Boolean(this.details.warm_level && this.details.warm_level > 0),
|
|
27
|
+
drying_mode_enabled: this.details.autoDryingSwitch === 1,
|
|
28
|
+
drying_mode_state: this.details.dryingMode || '',
|
|
29
|
+
drying_mode_level: this.details.dryingLevel || 0,
|
|
30
|
+
drying_mode_seconds_remaining: this.details.dryingRemainTime || 0
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return success;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Change fan speed - Implemented to satisfy interface but redirects to setMistLevel
|
|
38
|
+
*/
|
|
39
|
+
async changeFanSpeed(speed: number): Promise<boolean> {
|
|
40
|
+
logger.debug(`Redirecting fan speed change to mist level for device: ${this.deviceName}`);
|
|
41
|
+
return this.setMistLevel(speed);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Set device mode
|
|
46
|
+
*/
|
|
47
|
+
async setMode(mode: string): Promise<boolean> {
|
|
48
|
+
if (!this.modes.includes(mode as any)) {
|
|
49
|
+
const error = `Invalid mode: ${mode}. Must be one of: ${this.modes.join(', ')}`;
|
|
50
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
51
|
+
throw new Error(error);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
logger.debug(`Setting mode to ${mode} for device: ${this.deviceName}`);
|
|
55
|
+
const [response, status] = await this.callApi(
|
|
56
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
57
|
+
'post',
|
|
58
|
+
{
|
|
59
|
+
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
60
|
+
cid: this.cid,
|
|
61
|
+
configModule: this.configModule,
|
|
62
|
+
payload: {
|
|
63
|
+
data: {
|
|
64
|
+
mode
|
|
65
|
+
},
|
|
66
|
+
method: 'setHumidityMode',
|
|
67
|
+
source: 'APP'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
Helpers.reqHeaderBypass()
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const success = this.checkResponse([response, status], 'setMode');
|
|
74
|
+
if (success) {
|
|
75
|
+
this.details.mode = mode;
|
|
76
|
+
} else {
|
|
77
|
+
logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
|
|
78
|
+
}
|
|
79
|
+
return success;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Set warm mist level
|
|
84
|
+
*/
|
|
85
|
+
async setWarmLevel(level: number): Promise<boolean> {
|
|
86
|
+
if (!this.hasFeature('warm')) {
|
|
87
|
+
const error = 'Warm mist control not supported';
|
|
88
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
89
|
+
throw new Error(error);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!this.warmLevels.includes(level)) {
|
|
93
|
+
const error = `Invalid warm level: ${level}. Must be one of: ${this.warmLevels.join(', ')}`;
|
|
94
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
95
|
+
throw new Error(error);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
logger.debug(`Setting warm level to ${level} for device: ${this.deviceName}`);
|
|
99
|
+
const [response, status] = await this.callApi(
|
|
100
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
101
|
+
'post',
|
|
102
|
+
{
|
|
103
|
+
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
104
|
+
cid: this.cid,
|
|
105
|
+
configModule: this.configModule,
|
|
106
|
+
payload: {
|
|
107
|
+
data: {
|
|
108
|
+
id: 0,
|
|
109
|
+
level: level,
|
|
110
|
+
type: 'warm'
|
|
111
|
+
},
|
|
112
|
+
method: 'setLevel',
|
|
113
|
+
source: 'APP'
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
Helpers.reqHeaderBypass()
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const success = this.checkResponse([response, status], 'setWarmLevel');
|
|
120
|
+
if (success) {
|
|
121
|
+
this.details.warm_level = level;
|
|
122
|
+
this.details.warm_enabled = level > 0;
|
|
123
|
+
} else {
|
|
124
|
+
logger.error(`Failed to set warm level to ${level} for device: ${this.deviceName}`);
|
|
125
|
+
}
|
|
126
|
+
return success;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Set drying mode enabled
|
|
131
|
+
*/
|
|
132
|
+
async setDryingModeEnabled(enabled: boolean): Promise<boolean> {
|
|
133
|
+
if (!this.hasFeature('drying')) {
|
|
134
|
+
const error = 'Drying mode not supported';
|
|
135
|
+
logger.error(`${error} for device: ${this.deviceName}`);
|
|
136
|
+
throw new Error(error);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
logger.debug(`Setting drying mode to ${enabled} for device: ${this.deviceName}`);
|
|
140
|
+
const [response, status] = await this.callApi(
|
|
141
|
+
'/cloud/v2/deviceManaged/bypassV2',
|
|
142
|
+
'post',
|
|
143
|
+
{
|
|
144
|
+
...Helpers.reqBody(this.manager, 'bypassV2'),
|
|
145
|
+
cid: this.cid,
|
|
146
|
+
configModule: this.configModule,
|
|
147
|
+
payload: {
|
|
148
|
+
data: {
|
|
149
|
+
autoDryingSwitch: enabled ? 1 : 0
|
|
150
|
+
},
|
|
151
|
+
method: 'setDryingMode',
|
|
152
|
+
source: 'APP'
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
Helpers.reqHeaderBypass()
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const success = this.checkResponse([response, status], 'setDryingModeEnabled');
|
|
159
|
+
if (success) {
|
|
160
|
+
this.details.drying_mode_enabled = enabled;
|
|
161
|
+
this.details.autoDryingSwitch = enabled ? 1 : 0;
|
|
162
|
+
} else {
|
|
163
|
+
logger.error(`Failed to set drying mode to ${enabled} for device: ${this.deviceName}`);
|
|
164
|
+
}
|
|
165
|
+
return success;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get warm mist enabled status
|
|
170
|
+
*/
|
|
171
|
+
get warmMistEnabled(): boolean {
|
|
172
|
+
return this.details.warm_enabled || false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get drying mode enabled status
|
|
177
|
+
*/
|
|
178
|
+
get dryingModeEnabled(): boolean {
|
|
179
|
+
return this.details.drying_mode_enabled || false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get drying mode state
|
|
184
|
+
*/
|
|
185
|
+
get dryingModeState(): string {
|
|
186
|
+
return this.details.drying_mode_state || '';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get drying mode level
|
|
191
|
+
*/
|
|
192
|
+
get dryingModeLevel(): number {
|
|
193
|
+
return this.details.drying_mode_level || 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get drying mode seconds remaining
|
|
198
|
+
*/
|
|
199
|
+
get dryingModeSecondsRemaining(): number {
|
|
200
|
+
return this.details.drying_mode_seconds_remaining || 0;
|
|
201
|
+
}
|
|
202
|
+
}
|
package/src/lib/vesyncFan.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const fanConfig: FanConfig = {
|
|
|
19
19
|
// Core Series
|
|
20
20
|
'Core200S': {
|
|
21
21
|
module: 'VeSyncAirBypass',
|
|
22
|
-
features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer'],
|
|
22
|
+
features: ['display', 'child_lock', 'night_light', 'air_quality', 'timer', 'fan_speed', 'auto_mode'],
|
|
23
23
|
levels: [1, 2, 3]
|
|
24
24
|
},
|
|
25
25
|
'Core300S': {
|
|
@@ -318,7 +318,7 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
318
318
|
mode?: string;
|
|
319
319
|
speed?: number;
|
|
320
320
|
filter_life?: number;
|
|
321
|
-
screen_status?:
|
|
321
|
+
screen_status?: 'on' | 'off';
|
|
322
322
|
child_lock?: boolean;
|
|
323
323
|
air_quality?: string;
|
|
324
324
|
humidity?: number;
|
|
@@ -377,8 +377,8 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
377
377
|
/**
|
|
378
378
|
* Get screen status
|
|
379
379
|
*/
|
|
380
|
-
get screenStatus():
|
|
381
|
-
return this.details.screen_status ?? '';
|
|
380
|
+
get screenStatus(): 'on' | 'off' {
|
|
381
|
+
return this.details.screen_status ?? 'off';
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
/**
|
|
@@ -519,4 +519,4 @@ export abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
519
519
|
abstract turnOff(): Promise<boolean>;
|
|
520
520
|
abstract changeFanSpeed(speed: number): Promise<boolean>;
|
|
521
521
|
abstract setMode(mode: string): Promise<boolean>;
|
|
522
|
-
}
|
|
522
|
+
}
|