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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.63",
3
+ "version": "1.0.65",
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",
@@ -0,0 +1,276 @@
1
+ import { VeSyncAirBypass } from './airBypass';
2
+ import { VeSync } from '../vesync';
3
+ import { Helpers } from '../helpers';
4
+ import { logger } from '../logger';
5
+
6
+ /**
7
+ * VeSync Air Purifier with Bypass V2
8
+ */
9
+ export class VeSyncAirBaseV2 extends VeSyncAirBypass {
10
+ protected _lightDetection: boolean = false;
11
+ protected _lightDetectionState: boolean = false;
12
+ protected setSpeedLevel: number | null = null;
13
+ protected autoPreferences: string[] = ['default', 'efficient', 'quiet'];
14
+ protected enabled: boolean = false;
15
+ protected _mode: string = '';
16
+ protected _speed: number = 0;
17
+ protected _timer: { duration: number; action: string } | null = null;
18
+
19
+ constructor(details: Record<string, any>, manager: VeSync) {
20
+ super(details, manager);
21
+ logger.debug(`Initialized VeSyncAirBaseV2 device: ${this.deviceName}`);
22
+ }
23
+
24
+ protected buildConfigDict(configDict: Record<string, any>): void {
25
+ if (configDict) {
26
+ this.config = {
27
+ ...this.config, // Preserve existing config
28
+ ...configDict // Merge in new config
29
+ };
30
+ }
31
+ }
32
+
33
+ get mode(): string {
34
+ return this._mode;
35
+ }
36
+
37
+ set mode(value: string) {
38
+ this._mode = value;
39
+ }
40
+
41
+ get speed(): number {
42
+ return this._speed;
43
+ }
44
+
45
+ set speed(value: number) {
46
+ this._speed = value;
47
+ }
48
+
49
+ get timer(): { duration: number; action: string } | null {
50
+ return this._timer;
51
+ }
52
+
53
+ set timer(value: { duration: number; action: string } | null) {
54
+ this._timer = value;
55
+ }
56
+
57
+ /**
58
+ * Get device details
59
+ */
60
+ async getDetails(): Promise<Boolean> {
61
+ logger.debug(`Getting details for device: ${this.deviceName}`);
62
+ const [head, body] = this.buildApiDict('getPurifierStatus');
63
+
64
+ const [response, status] = await this.callApi(
65
+ '/cloud/v2/deviceManaged/bypassV2',
66
+ 'post',
67
+ body,
68
+ head
69
+ );
70
+
71
+ if (!this.checkResponse([response, status], 'getDetails') || !response?.result) {
72
+ logger.debug('Error getting purifier details');
73
+ this.connectionStatus = 'offline';
74
+ return false;
75
+ }
76
+
77
+ const innerResponse = response.result;
78
+ if (innerResponse.code !== 0 || !innerResponse.result) {
79
+ logger.debug('Error in inner response from purifier');
80
+ this.connectionStatus = 'offline';
81
+ return false;
82
+ }
83
+
84
+ const deviceData = innerResponse.result;
85
+ this.buildPurifierDict(deviceData);
86
+ if (deviceData.configuration) {
87
+ this.buildConfigDict(deviceData.configuration);
88
+ }
89
+ return true;
90
+ }
91
+
92
+ /**
93
+ * Build purifier dictionary
94
+ */
95
+ protected buildPurifierDict(devDict: Record<string, any>): void {
96
+ this.connectionStatus = 'online';
97
+ const powerSwitch = Boolean(devDict.powerSwitch ?? 0);
98
+ this.enabled = powerSwitch;
99
+ this.deviceStatus = powerSwitch ? 'on' : 'off';
100
+ this.mode = devDict.workMode ?? 'manual';
101
+
102
+ this.speed = devDict.fanSpeedLevel ?? 0;
103
+ this.setSpeedLevel = devDict.manualSpeedLevel ?? 1;
104
+
105
+ this.details = {
106
+ ...this.details,
107
+ filter_life: devDict.filterLifePercent ?? 0,
108
+ child_lock: Boolean(devDict.childLockSwitch ?? 0),
109
+ display: Boolean(devDict.screenState ?? 0),
110
+ light_detection_switch: Boolean(devDict.lightDetectionSwitch ?? 0),
111
+ environment_light_state: Boolean(devDict.environmentLightState ?? 0),
112
+ screen_switch: Boolean(devDict.screenSwitch ?? 0),
113
+ screenStatus: Boolean(devDict.screenSwitch ?? 0) ? 'on' : 'off'
114
+ };
115
+
116
+ if (this.hasFeature('air_quality')) {
117
+ this.details.air_quality_value = devDict.PM25 ?? 0;
118
+ this.details.air_quality = devDict.AQLevel ?? 0;
119
+ }
120
+
121
+ if ('PM1' in devDict) this.details.pm1 = devDict.PM1;
122
+ if ('PM10' in devDict) this.details.pm10 = devDict.PM10;
123
+ if ('AQPercent' in devDict) this.details.aq_percent = devDict.AQPercent;
124
+ if ('fanRotateAngle' in devDict) this.details.fan_rotate_angle = devDict.fanRotateAngle;
125
+ if ('filterOpenState' in devDict) this.details.filter_open_state = Boolean(devDict.filterOpenState);
126
+
127
+ if ((devDict.timerRemain ?? 0) > 0) {
128
+ this.timer = { duration: devDict.timerRemain, action: 'off' };
129
+ }
130
+
131
+ if (typeof devDict.autoPreference === 'object' && devDict.autoPreference) {
132
+ this.details.auto_preference_type = devDict.autoPreference?.autoPreferenceType ?? 'default';
133
+ } else {
134
+ this.details.auto_preference_type = null;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Set auto preference
140
+ */
141
+ async setAutoPreference(preference: string = 'default', roomSize: number = 600): Promise<boolean> {
142
+ if (!this.autoPreferences.includes(preference)) {
143
+ logger.debug(`Invalid preference: ${preference} - valid preferences are default, efficient, quiet`);
144
+ return false;
145
+ }
146
+
147
+ const [head, body] = this.buildApiDict('setAutoPreference');
148
+ body.payload.data = {
149
+ autoPreference: preference,
150
+ roomSize: roomSize
151
+ };
152
+
153
+ const [response, status] = await this.callApi(
154
+ '/cloud/v2/deviceManaged/bypassV2',
155
+ 'post',
156
+ body,
157
+ head
158
+ );
159
+
160
+ const success = this.checkResponse([response, status], 'setAutoPreference');
161
+ if (success) {
162
+ this.details.auto_preference = preference;
163
+ }
164
+ return success;
165
+ }
166
+
167
+ /**
168
+ * Set light detection
169
+ */
170
+ async setLightDetection(enabled: boolean): Promise<boolean> {
171
+ const [head, body] = this.buildApiDict('setLightDetection');
172
+ body.payload.data = {
173
+ lightDetectionSwitch: enabled ? 1 : 0
174
+ };
175
+
176
+ const [response, status] = await this.callApi(
177
+ '/cloud/v2/deviceManaged/bypassV2',
178
+ 'post',
179
+ body,
180
+ head
181
+ );
182
+
183
+ const success = this.checkResponse([response, status], 'setLightDetection');
184
+ if (success) {
185
+ this.details.light_detection_switch = enabled;
186
+ }
187
+ return success;
188
+ }
189
+
190
+ /**
191
+ * Get light detection status
192
+ */
193
+ get lightDetection(): boolean {
194
+ return Boolean(this.details.light_detection_switch);
195
+ }
196
+
197
+ /**
198
+ * Get light detection state
199
+ */
200
+ get lightDetectionState(): boolean {
201
+ return Boolean(this.details.environment_light_state);
202
+ }
203
+
204
+ /**
205
+ * Get auto preference type
206
+ */
207
+ get autoPreferenceType(): string | null {
208
+ return this.details.auto_preference_type;
209
+ }
210
+
211
+ /**
212
+ * Display JSON details
213
+ */
214
+ override displayJSON(): string {
215
+ const baseInfo = JSON.parse(super.displayJSON());
216
+ const details: Record<string, string> = {
217
+ ...baseInfo,
218
+ 'Mode': this.mode,
219
+ 'Filter Life': (this.details.filter_life ?? 0).toString(),
220
+ 'Fan Level': this.speed.toString(),
221
+ 'Display On': this.details.display?.toString() ?? 'false',
222
+ 'Child Lock': (this.details.child_lock ?? false).toString(),
223
+ 'Night Light': (this.details.night_light ?? '').toString(),
224
+ 'Display Set On': (this.details.screen_switch ?? false).toString(),
225
+ 'Light Detection Enabled': (this.details.light_detection_switch ?? false).toString(),
226
+ 'Environment Light State': (this.details.environment_light_state ?? false).toString()
227
+ };
228
+
229
+ if (this.hasFeature('air_quality')) {
230
+ details['Air Quality Level'] = (this.details.air_quality ?? '').toString();
231
+ details['Air Quality Value'] = (this.details.air_quality_value ?? '').toString();
232
+ }
233
+
234
+ const everestKeys: Record<string, string> = {
235
+ 'pm1': 'PM1',
236
+ 'pm10': 'PM10',
237
+ 'fan_rotate_angle': 'Fan Rotate Angle',
238
+ 'filter_open_state': 'Filter Open State'
239
+ };
240
+
241
+ for (const [key, value] of Object.entries(everestKeys)) {
242
+ if (key in this.details) {
243
+ details[value] = this.details[key]?.toString() ?? '';
244
+ }
245
+ }
246
+
247
+ return JSON.stringify(details, null, 4);
248
+ }
249
+
250
+ /**
251
+ * Set oscillation state
252
+ */
253
+ async setOscillation(toggle: boolean): Promise<boolean> {
254
+ logger.debug(`Setting oscillation to ${toggle ? 'on' : 'off'} for device: ${this.deviceName}`);
255
+
256
+ const [head, body] = this.buildApiDict('setSwitch');
257
+ body.payload.data = {
258
+ oscillationSwitch: toggle ? 1 : 0,
259
+ switchIdx: 0
260
+ };
261
+
262
+ const [response, status] = await this.callApi(
263
+ '/cloud/v2/deviceManaged/bypassV2',
264
+ 'post',
265
+ body,
266
+ head
267
+ );
268
+
269
+ const success = this.checkResponse([response, status], 'setOscillation');
270
+ if (success) {
271
+ this.details.oscillationState = toggle;
272
+ this.details.oscillationSwitch = toggle ? 1 : 0;
273
+ }
274
+ return success;
275
+ }
276
+ }