tsvesync 1.0.34 → 1.0.36

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.
@@ -0,0 +1,278 @@
1
+ automatic_stop_off:
2
+ headers:
3
+ Content-Type: application/json; charset=UTF-8
4
+ User-Agent: okhttp/3.12.1
5
+ json_object:
6
+ acceptLanguage: en
7
+ accountID: sample_id
8
+ appVersion: 2.8.6
9
+ cid: LUH-M101S-WEUR-CID
10
+ configModule: ConfigModule
11
+ debugMode: false
12
+ deviceRegion: EU
13
+ method: bypassV2
14
+ payload:
15
+ data:
16
+ autoStopSwitch: 0
17
+ method: setAutoStopSwitch
18
+ source: APP
19
+ phoneBrand: SM N9005
20
+ phoneOS: Android
21
+ timeZone: America/New_York
22
+ token: sample_tk
23
+ traceId: TRACE_ID
24
+ method: post
25
+ url: /cloud/v2/deviceManaged/bypassV2
26
+ automatic_stop_on:
27
+ headers:
28
+ Content-Type: application/json; charset=UTF-8
29
+ User-Agent: okhttp/3.12.1
30
+ json_object:
31
+ acceptLanguage: en
32
+ accountID: sample_id
33
+ appVersion: 2.8.6
34
+ cid: LUH-M101S-WEUR-CID
35
+ configModule: ConfigModule
36
+ debugMode: false
37
+ deviceRegion: EU
38
+ method: bypassV2
39
+ payload:
40
+ data:
41
+ autoStopSwitch: 1
42
+ method: setAutoStopSwitch
43
+ source: APP
44
+ phoneBrand: SM N9005
45
+ phoneOS: Android
46
+ timeZone: America/New_York
47
+ token: sample_tk
48
+ traceId: TRACE_ID
49
+ method: post
50
+ url: /cloud/v2/deviceManaged/bypassV2
51
+ set_auto_mode:
52
+ headers:
53
+ Content-Type: application/json; charset=UTF-8
54
+ User-Agent: okhttp/3.12.1
55
+ json_object:
56
+ acceptLanguage: en
57
+ accountID: sample_id
58
+ appVersion: 2.8.6
59
+ cid: LUH-M101S-WEUR-CID
60
+ configModule: ConfigModule
61
+ debugMode: false
62
+ deviceRegion: EU
63
+ method: bypassV2
64
+ payload:
65
+ data:
66
+ workMode: auto
67
+ method: setHumidityMode
68
+ source: APP
69
+ phoneBrand: SM N9005
70
+ phoneOS: Android
71
+ timeZone: America/New_York
72
+ token: sample_tk
73
+ traceId: TRACE_ID
74
+ method: post
75
+ url: /cloud/v2/deviceManaged/bypassV2
76
+ set_humidity:
77
+ headers:
78
+ Content-Type: application/json; charset=UTF-8
79
+ User-Agent: okhttp/3.12.1
80
+ json_object:
81
+ acceptLanguage: en
82
+ accountID: sample_id
83
+ appVersion: 2.8.6
84
+ cid: LUH-M101S-WEUR-CID
85
+ configModule: ConfigModule
86
+ debugMode: false
87
+ deviceRegion: EU
88
+ method: bypassV2
89
+ payload:
90
+ data:
91
+ targetHumidity: 50
92
+ method: setTargetHumidity
93
+ source: APP
94
+ phoneBrand: SM N9005
95
+ phoneOS: Android
96
+ timeZone: America/New_York
97
+ token: sample_tk
98
+ traceId: TRACE_ID
99
+ method: post
100
+ url: /cloud/v2/deviceManaged/bypassV2
101
+ set_manual_mode:
102
+ headers:
103
+ Content-Type: application/json; charset=UTF-8
104
+ User-Agent: okhttp/3.12.1
105
+ json_object:
106
+ acceptLanguage: en
107
+ accountID: sample_id
108
+ appVersion: 2.8.6
109
+ cid: LUH-M101S-WEUR-CID
110
+ configModule: ConfigModule
111
+ debugMode: false
112
+ deviceRegion: EU
113
+ method: bypassV2
114
+ payload:
115
+ data:
116
+ workMode: manual
117
+ method: setHumidityMode
118
+ source: APP
119
+ phoneBrand: SM N9005
120
+ phoneOS: Android
121
+ timeZone: America/New_York
122
+ token: sample_tk
123
+ traceId: TRACE_ID
124
+ method: post
125
+ url: /cloud/v2/deviceManaged/bypassV2
126
+ set_mist_level:
127
+ headers:
128
+ Content-Type: application/json; charset=UTF-8
129
+ User-Agent: okhttp/3.12.1
130
+ json_object:
131
+ acceptLanguage: en
132
+ accountID: sample_id
133
+ appVersion: 2.8.6
134
+ cid: LUH-M101S-WEUR-CID
135
+ configModule: ConfigModule
136
+ debugMode: false
137
+ deviceRegion: EU
138
+ method: bypassV2
139
+ payload:
140
+ data:
141
+ levelIdx: 0
142
+ levelType: mist
143
+ virtualLevel: 2
144
+ method: setVirtualLevel
145
+ source: APP
146
+ phoneBrand: SM N9005
147
+ phoneOS: Android
148
+ timeZone: America/New_York
149
+ token: sample_tk
150
+ traceId: TRACE_ID
151
+ method: post
152
+ url: /cloud/v2/deviceManaged/bypassV2
153
+ turn_off:
154
+ headers:
155
+ Content-Type: application/json; charset=UTF-8
156
+ User-Agent: okhttp/3.12.1
157
+ json_object:
158
+ acceptLanguage: en
159
+ accountID: sample_id
160
+ appVersion: 2.8.6
161
+ cid: LUH-M101S-WEUR-CID
162
+ configModule: ConfigModule
163
+ debugMode: false
164
+ deviceRegion: EU
165
+ method: bypassV2
166
+ payload:
167
+ data:
168
+ powerSwitch: 0
169
+ switchIdx: 0
170
+ method: setSwitch
171
+ source: APP
172
+ phoneBrand: SM N9005
173
+ phoneOS: Android
174
+ timeZone: America/New_York
175
+ token: sample_tk
176
+ traceId: TRACE_ID
177
+ method: post
178
+ url: /cloud/v2/deviceManaged/bypassV2
179
+ turn_off_display:
180
+ headers:
181
+ Content-Type: application/json; charset=UTF-8
182
+ User-Agent: okhttp/3.12.1
183
+ json_object:
184
+ acceptLanguage: en
185
+ accountID: sample_id
186
+ appVersion: 2.8.6
187
+ cid: LUH-M101S-WEUR-CID
188
+ configModule: ConfigModule
189
+ debugMode: false
190
+ deviceRegion: EU
191
+ method: bypassV2
192
+ payload:
193
+ data:
194
+ screenSwitch: 0
195
+ method: setDisplay
196
+ source: APP
197
+ phoneBrand: SM N9005
198
+ phoneOS: Android
199
+ timeZone: America/New_York
200
+ token: sample_tk
201
+ traceId: TRACE_ID
202
+ method: post
203
+ url: /cloud/v2/deviceManaged/bypassV2
204
+ turn_on:
205
+ headers:
206
+ Content-Type: application/json; charset=UTF-8
207
+ User-Agent: okhttp/3.12.1
208
+ json_object:
209
+ acceptLanguage: en
210
+ accountID: sample_id
211
+ appVersion: 2.8.6
212
+ cid: LUH-M101S-WEUR-CID
213
+ configModule: ConfigModule
214
+ debugMode: false
215
+ deviceRegion: EU
216
+ method: bypassV2
217
+ payload:
218
+ data:
219
+ powerSwitch: 1
220
+ switchIdx: 0
221
+ method: setSwitch
222
+ source: APP
223
+ phoneBrand: SM N9005
224
+ phoneOS: Android
225
+ timeZone: America/New_York
226
+ token: sample_tk
227
+ traceId: TRACE_ID
228
+ method: post
229
+ url: /cloud/v2/deviceManaged/bypassV2
230
+ turn_on_display:
231
+ headers:
232
+ Content-Type: application/json; charset=UTF-8
233
+ User-Agent: okhttp/3.12.1
234
+ json_object:
235
+ acceptLanguage: en
236
+ accountID: sample_id
237
+ appVersion: 2.8.6
238
+ cid: LUH-M101S-WEUR-CID
239
+ configModule: ConfigModule
240
+ debugMode: false
241
+ deviceRegion: EU
242
+ method: bypassV2
243
+ payload:
244
+ data:
245
+ screenSwitch: 1
246
+ method: setDisplay
247
+ source: APP
248
+ phoneBrand: SM N9005
249
+ phoneOS: Android
250
+ timeZone: America/New_York
251
+ token: sample_tk
252
+ traceId: TRACE_ID
253
+ method: post
254
+ url: /cloud/v2/deviceManaged/bypassV2
255
+ update:
256
+ headers:
257
+ Content-Type: application/json; charset=UTF-8
258
+ User-Agent: okhttp/3.12.1
259
+ json_object:
260
+ acceptLanguage: en
261
+ accountID: sample_id
262
+ appVersion: 2.8.6
263
+ cid: LUH-M101S-WEUR-CID
264
+ configModule: ConfigModule
265
+ debugMode: false
266
+ deviceRegion: EU
267
+ method: bypassV2
268
+ payload:
269
+ data: {}
270
+ method: getHumidifierStatus
271
+ source: APP
272
+ phoneBrand: SM N9005
273
+ phoneOS: Android
274
+ timeZone: America/New_York
275
+ token: sample_tk
276
+ traceId: TRACE_ID
277
+ method: post
278
+ url: /cloud/v2/deviceManaged/bypassV2
@@ -234,6 +234,10 @@ exports.fanConfig = {
234
234
  module: 'VeSyncWarmHumidifier',
235
235
  features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
236
236
  },
237
+ 'LUH-M101S-WEUR': {
238
+ module: 'VeSyncWarmHumidifier',
239
+ features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
240
+ },
237
241
  // LEH Series
238
242
  'LEH-S601S-WUS': {
239
243
  module: 'VeSyncWarmHumidifier',
@@ -93,6 +93,22 @@ export declare class VeSyncHumidifier extends VeSyncFan {
93
93
  * Get device details
94
94
  */
95
95
  getDetails(): Promise<Boolean>;
96
+ /**
97
+ * Turn device on
98
+ */
99
+ turnOn(): Promise<boolean>;
100
+ /**
101
+ * Turn device off
102
+ */
103
+ turnOff(): Promise<boolean>;
104
+ /**
105
+ * Get model-specific toggle payload
106
+ */
107
+ protected getTogglePayload(enabled: boolean): Record<string, any>;
108
+ /**
109
+ * Toggle device power
110
+ */
111
+ toggleSwitch(enabled: boolean): Promise<boolean>;
96
112
  /**
97
113
  * Set mist level
98
114
  */
@@ -105,14 +121,6 @@ export declare class VeSyncHumidifier extends VeSyncFan {
105
121
  * Set device mode
106
122
  */
107
123
  setMode(mode: string): Promise<boolean>;
108
- /**
109
- * Turn device on
110
- */
111
- turnOn(): Promise<boolean>;
112
- /**
113
- * Turn device off
114
- */
115
- turnOff(): Promise<boolean>;
116
124
  /**
117
125
  * Set target humidity
118
126
  */
@@ -160,6 +168,10 @@ export declare class VeSyncHumidifier extends VeSyncFan {
160
168
  export declare class VeSyncWarmHumidifier extends VeSyncHumidifier {
161
169
  protected readonly warmLevels: number[];
162
170
  constructor(details: Record<string, any>, manager: VeSync);
171
+ /**
172
+ * Get device details
173
+ */
174
+ getDetails(): Promise<Boolean>;
163
175
  /**
164
176
  * Change fan speed - Implemented to satisfy interface but redirects to setMistLevel
165
177
  */
@@ -176,5 +188,25 @@ export declare class VeSyncWarmHumidifier extends VeSyncHumidifier {
176
188
  * Set drying mode enabled
177
189
  */
178
190
  setDryingModeEnabled(enabled: boolean): Promise<boolean>;
191
+ /**
192
+ * Get warm mist enabled status
193
+ */
194
+ get warmMistEnabled(): boolean;
195
+ /**
196
+ * Get drying mode enabled status
197
+ */
198
+ get dryingModeEnabled(): boolean;
199
+ /**
200
+ * Get drying mode state
201
+ */
202
+ get dryingModeState(): string;
203
+ /**
204
+ * Get drying mode level
205
+ */
206
+ get dryingModeLevel(): number;
207
+ /**
208
+ * Get drying mode seconds remaining
209
+ */
210
+ get dryingModeSecondsRemaining(): number;
179
211
  }
180
212
  export declare const fanModules: Record<string, new (details: Record<string, any>, manager: VeSync) => VeSyncFan>;
@@ -23,8 +23,45 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
23
23
  * Get device details
24
24
  */
25
25
  async getDetails() {
26
- var _a;
26
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
27
27
  logger_1.logger.debug(`Getting details for device: ${this.deviceName}`);
28
+ // Special handling for LV series
29
+ if (this.deviceType.startsWith('LV-')) {
30
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/devicedetail', 'post', {
31
+ acceptLanguage: 'en',
32
+ accountID: this.manager.accountId,
33
+ appVersion: '2.8.6',
34
+ method: 'devicedetail',
35
+ mobileId: '1234567890123456',
36
+ phoneBrand: 'SM N9005',
37
+ phoneOS: 'Android',
38
+ timeZone: this.manager.timeZone,
39
+ token: this.manager.token,
40
+ uuid: this.uuid
41
+ }, {
42
+ 'accept-language': 'en',
43
+ 'accountId': this.manager.accountId,
44
+ 'appVersion': '2.8.6',
45
+ 'content-type': 'application/json',
46
+ 'tk': this.manager.token,
47
+ 'tz': this.manager.timeZone
48
+ });
49
+ const success = this.checkResponse([response, status], 'getDetails');
50
+ if (success && response) {
51
+ this.deviceStatus = ((_b = (_a = response.result) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.deviceStatus) === 'on' ? 'on' : 'off';
52
+ this.details = {
53
+ mode: ((_d = (_c = response.result) === null || _c === void 0 ? void 0 : _c.result) === null || _d === void 0 ? void 0 : _d.mode) || '',
54
+ speed: ((_f = (_e = response.result) === null || _e === void 0 ? void 0 : _e.result) === null || _f === void 0 ? void 0 : _f.level) || 0,
55
+ filterLife: ((_h = (_g = response.result) === null || _g === void 0 ? void 0 : _g.result) === null || _h === void 0 ? void 0 : _h.filterLife) || 0,
56
+ screenStatus: ((_k = (_j = response.result) === null || _j === void 0 ? void 0 : _j.result) === null || _k === void 0 ? void 0 : _k.display) || false,
57
+ childLock: ((_m = (_l = response.result) === null || _l === void 0 ? void 0 : _l.result) === null || _m === void 0 ? void 0 : _m.childLock) || false,
58
+ airQuality: ((_p = (_o = response.result) === null || _o === void 0 ? void 0 : _o.result) === null || _p === void 0 ? void 0 : _p.airQuality) || 0
59
+ };
60
+ return true;
61
+ }
62
+ return false;
63
+ }
64
+ // Original implementation for other devices
28
65
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
29
66
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
30
67
  cid: this.cid,
@@ -37,11 +74,9 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
37
74
  }, helpers_1.Helpers.reqHeaderBypass());
38
75
  const success = this.checkResponse([response, status], 'getDetails');
39
76
  if (success) {
40
- const result = (_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.result;
77
+ const result = (_q = response === null || response === void 0 ? void 0 : response.result) === null || _q === void 0 ? void 0 : _q.result;
41
78
  if (result) {
42
- // Update device status
43
79
  this.deviceStatus = result.enabled ? 'on' : 'off';
44
- // Store device settings
45
80
  this.details = {
46
81
  mode: result.mode || '',
47
82
  speed: result.level || 0,
@@ -50,7 +85,6 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
50
85
  childLock: result.child_lock || false,
51
86
  airQuality: result.air_quality || 0
52
87
  };
53
- // Store configuration if available
54
88
  if (result.configuration) {
55
89
  this.config = result.configuration;
56
90
  }
@@ -64,6 +98,30 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
64
98
  */
65
99
  async turnOn() {
66
100
  logger_1.logger.info(`Turning on device: ${this.deviceName}`);
101
+ // Special handling for LV series
102
+ if (this.deviceType.startsWith('LV-')) {
103
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/deviceStatus', 'put', {
104
+ acceptLanguage: 'en',
105
+ accountID: this.manager.accountId,
106
+ status: 'on',
107
+ timeZone: this.manager.timeZone,
108
+ token: this.manager.token,
109
+ uuid: this.uuid
110
+ }, {
111
+ 'accept-language': 'en',
112
+ 'accountId': this.manager.accountId,
113
+ 'appVersion': '2.8.6',
114
+ 'content-type': 'application/json',
115
+ 'tk': this.manager.token,
116
+ 'tz': this.manager.timeZone
117
+ });
118
+ const success = this.checkResponse([response, status], 'turnOn');
119
+ if (!success) {
120
+ logger_1.logger.error(`Failed to turn on device: ${this.deviceName}`);
121
+ }
122
+ return success;
123
+ }
124
+ // Original implementation for other devices
67
125
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
68
126
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
69
127
  cid: this.cid,
@@ -88,6 +146,30 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
88
146
  */
89
147
  async turnOff() {
90
148
  logger_1.logger.info(`Turning off device: ${this.deviceName}`);
149
+ // Special handling for LV series
150
+ if (this.deviceType.startsWith('LV-')) {
151
+ const [response, status] = await this.callApi('/131airPurifier/v1/device/deviceStatus', 'put', {
152
+ acceptLanguage: 'en',
153
+ accountID: this.manager.accountId,
154
+ status: 'off',
155
+ timeZone: this.manager.timeZone,
156
+ token: this.manager.token,
157
+ uuid: this.uuid
158
+ }, {
159
+ 'accept-language': 'en',
160
+ 'accountId': this.manager.accountId,
161
+ 'appVersion': '2.8.6',
162
+ 'content-type': 'application/json',
163
+ 'tk': this.manager.token,
164
+ 'tz': this.manager.timeZone
165
+ });
166
+ const success = this.checkResponse([response, status], 'turnOff');
167
+ if (!success) {
168
+ logger_1.logger.error(`Failed to turn off device: ${this.deviceName}`);
169
+ }
170
+ return success;
171
+ }
172
+ // Original implementation for other devices
91
173
  const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
92
174
  ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
93
175
  cid: this.cid,
@@ -421,11 +503,91 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
421
503
  }, helpers_1.Helpers.reqHeaderBypass());
422
504
  const success = this.checkResponse([response, status], 'getDetails');
423
505
  if (success && ((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.result)) {
424
- this.details = response.result.result;
506
+ const result = response.result.result;
507
+ // Handle model-specific status fields
508
+ if (this.deviceType.startsWith('Classic') || this.deviceType === 'Dual200S') {
509
+ this.deviceStatus = result.enabled ? 'on' : 'off';
510
+ }
511
+ else {
512
+ this.deviceStatus = result.powerSwitch === 1 ? 'on' : 'off';
513
+ }
514
+ this.details = {
515
+ mode: result.mode || '',
516
+ humidity: result.humidity || 0,
517
+ mist_level: result.mistLevel || 0,
518
+ mist_virtual_level: result.virtualLevel || 0,
519
+ warm_level: result.warmLevel || 0,
520
+ water_lacks: result.waterLacks || false,
521
+ humidity_high: result.humidityHigh || false,
522
+ water_tank_lifted: result.waterTankLifted || false,
523
+ display: result.display || false,
524
+ automatic_stop: result.automaticStop || false,
525
+ configuration: result.configuration || {},
526
+ connection_status: result.connectionStatus || null
527
+ };
425
528
  logger_1.logger.debug(`Successfully got details for device: ${this.deviceName}`);
426
529
  }
427
530
  return success;
428
531
  }
532
+ /**
533
+ * Turn device on
534
+ */
535
+ async turnOn() {
536
+ return this.toggleSwitch(true);
537
+ }
538
+ /**
539
+ * Turn device off
540
+ */
541
+ async turnOff() {
542
+ return this.toggleSwitch(false);
543
+ }
544
+ /**
545
+ * Get model-specific toggle payload
546
+ */
547
+ getTogglePayload(enabled) {
548
+ // Model-specific toggle payload based on PyVeSync implementation
549
+ if (this.deviceType.startsWith('Classic') || this.deviceType === 'Dual200S') {
550
+ return {
551
+ enabled: enabled,
552
+ id: 0
553
+ };
554
+ }
555
+ else if (this.deviceType.startsWith('Superior')) {
556
+ return {
557
+ powerSwitch: enabled ? 1 : 0,
558
+ switchIdx: 0
559
+ };
560
+ }
561
+ else {
562
+ return {
563
+ powerSwitch: enabled ? 1 : 0
564
+ };
565
+ }
566
+ }
567
+ /**
568
+ * Toggle device power
569
+ */
570
+ async toggleSwitch(enabled) {
571
+ logger_1.logger.info(`Setting device power to ${enabled ? 'on' : 'off'}: ${this.deviceName}`);
572
+ const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
573
+ ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
574
+ cid: this.cid,
575
+ configModule: this.configModule,
576
+ payload: {
577
+ data: this.getTogglePayload(enabled),
578
+ method: 'setSwitch',
579
+ source: 'APP'
580
+ }
581
+ }, helpers_1.Helpers.reqHeaderBypass());
582
+ const success = this.checkResponse([response, status], 'toggleSwitch');
583
+ if (success) {
584
+ this.deviceStatus = enabled ? 'on' : 'off';
585
+ }
586
+ else {
587
+ logger_1.logger.error(`Failed to set device power to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
588
+ }
589
+ return success;
590
+ }
429
591
  /**
430
592
  * Set mist level
431
593
  */
@@ -491,54 +653,6 @@ class VeSyncHumidifier extends vesyncFan_1.VeSyncFan {
491
653
  }
492
654
  return success;
493
655
  }
494
- /**
495
- * Turn device on
496
- */
497
- async turnOn() {
498
- logger_1.logger.info(`Turning on device: ${this.deviceName}`);
499
- const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
500
- ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
501
- cid: this.cid,
502
- configModule: this.configModule,
503
- payload: {
504
- data: {
505
- enabled: true,
506
- id: 0
507
- },
508
- method: 'setSwitch',
509
- source: 'APP'
510
- }
511
- }, helpers_1.Helpers.reqHeaderBypass());
512
- const success = this.checkResponse([response, status], 'turnOn');
513
- if (!success) {
514
- logger_1.logger.error(`Failed to turn on device: ${this.deviceName}`);
515
- }
516
- return success;
517
- }
518
- /**
519
- * Turn device off
520
- */
521
- async turnOff() {
522
- logger_1.logger.info(`Turning off device: ${this.deviceName}`);
523
- const [response, status] = await this.callApi('/cloud/v2/deviceManaged/bypassV2', 'post', {
524
- ...helpers_1.Helpers.reqBody(this.manager, 'bypassV2'),
525
- cid: this.cid,
526
- configModule: this.configModule,
527
- payload: {
528
- data: {
529
- enabled: false,
530
- id: 0
531
- },
532
- method: 'setSwitch',
533
- source: 'APP'
534
- }
535
- }, helpers_1.Helpers.reqHeaderBypass());
536
- const success = this.checkResponse([response, status], 'turnOff');
537
- if (!success) {
538
- logger_1.logger.error(`Failed to turn off device: ${this.deviceName}`);
539
- }
540
- return success;
541
- }
542
656
  /**
543
657
  * Set target humidity
544
658
  */
@@ -756,6 +870,24 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
756
870
  this.warmLevels = [1, 2, 3];
757
871
  logger_1.logger.debug(`Initialized VeSyncWarmHumidifier device: ${this.deviceName}`);
758
872
  }
873
+ /**
874
+ * Get device details
875
+ */
876
+ async getDetails() {
877
+ const success = await super.getDetails();
878
+ if (success && this.details) {
879
+ // Add warm humidifier specific fields
880
+ this.details = {
881
+ ...this.details,
882
+ warm_enabled: this.details.warm_level > 0,
883
+ drying_mode_enabled: this.details.autoDryingSwitch === 1,
884
+ drying_mode_state: this.details.dryingMode || '',
885
+ drying_mode_level: this.details.dryingLevel || 0,
886
+ drying_mode_seconds_remaining: this.details.dryingRemainTime || 0
887
+ };
888
+ }
889
+ return success;
890
+ }
759
891
  /**
760
892
  * Change fan speed - Implemented to satisfy interface but redirects to setMistLevel
761
893
  */
@@ -786,7 +918,10 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
786
918
  }
787
919
  }, helpers_1.Helpers.reqHeaderBypass());
788
920
  const success = this.checkResponse([response, status], 'setMode');
789
- if (!success) {
921
+ if (success) {
922
+ this.details.mode = mode;
923
+ }
924
+ else {
790
925
  logger_1.logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
791
926
  }
792
927
  return success;
@@ -819,7 +954,11 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
819
954
  }
820
955
  }, helpers_1.Helpers.reqHeaderBypass());
821
956
  const success = this.checkResponse([response, status], 'setWarmLevel');
822
- if (!success) {
957
+ if (success) {
958
+ this.details.warm_level = level;
959
+ this.details.warm_enabled = level > 0;
960
+ }
961
+ else {
823
962
  logger_1.logger.error(`Failed to set warm level to ${level} for device: ${this.deviceName}`);
824
963
  }
825
964
  return success;
@@ -847,11 +986,45 @@ class VeSyncWarmHumidifier extends VeSyncHumidifier {
847
986
  }
848
987
  }, helpers_1.Helpers.reqHeaderBypass());
849
988
  const success = this.checkResponse([response, status], 'setDryingModeEnabled');
850
- if (!success) {
989
+ if (success) {
990
+ this.details.drying_mode_enabled = enabled;
991
+ this.details.autoDryingSwitch = enabled ? 1 : 0;
992
+ }
993
+ else {
851
994
  logger_1.logger.error(`Failed to set drying mode to ${enabled} for device: ${this.deviceName}`);
852
995
  }
853
996
  return success;
854
997
  }
998
+ /**
999
+ * Get warm mist enabled status
1000
+ */
1001
+ get warmMistEnabled() {
1002
+ return this.details.warm_enabled || false;
1003
+ }
1004
+ /**
1005
+ * Get drying mode enabled status
1006
+ */
1007
+ get dryingModeEnabled() {
1008
+ return this.details.drying_mode_enabled || false;
1009
+ }
1010
+ /**
1011
+ * Get drying mode state
1012
+ */
1013
+ get dryingModeState() {
1014
+ return this.details.drying_mode_state || '';
1015
+ }
1016
+ /**
1017
+ * Get drying mode level
1018
+ */
1019
+ get dryingModeLevel() {
1020
+ return this.details.drying_mode_level || 0;
1021
+ }
1022
+ /**
1023
+ * Get drying mode seconds remaining
1024
+ */
1025
+ get dryingModeSecondsRemaining() {
1026
+ return this.details.drying_mode_seconds_remaining || 0;
1027
+ }
855
1028
  }
856
1029
  exports.VeSyncWarmHumidifier = VeSyncWarmHumidifier;
857
1030
  // Export fan modules dictionary
@@ -917,6 +1090,7 @@ exports.fanModules = {
917
1090
  'LUH-O601S-WUS': VeSyncWarmHumidifier,
918
1091
  'LUH-O601S-KUS': VeSyncWarmHumidifier,
919
1092
  'LUH-M101S-WUS': VeSyncWarmHumidifier,
1093
+ 'LUH-M101S-WEUR': VeSyncWarmHumidifier,
920
1094
  // LEH Series
921
1095
  'LEH-S601S-WUS': VeSyncWarmHumidifier,
922
1096
  'LEH-S601S-WUSR': VeSyncWarmHumidifier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
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",
@@ -246,6 +246,10 @@ export const fanConfig: FanConfig = {
246
246
  module: 'VeSyncWarmHumidifier',
247
247
  features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
248
248
  },
249
+ 'LUH-M101S-WEUR': {
250
+ module: 'VeSyncWarmHumidifier',
251
+ features: ['display', 'humidity', 'mist', 'warm', 'timer', 'auto_mode']
252
+ },
249
253
 
250
254
  // LEH Series
251
255
  'LEH-S601S-WUS': {
@@ -26,6 +26,51 @@ export class VeSyncAirBypass extends VeSyncFan {
26
26
  */
27
27
  async getDetails(): Promise<Boolean> {
28
28
  logger.debug(`Getting details for device: ${this.deviceName}`);
29
+
30
+ // Special handling for LV series
31
+ if (this.deviceType.startsWith('LV-')) {
32
+ const [response, status] = await this.callApi(
33
+ '/131airPurifier/v1/device/devicedetail',
34
+ 'post',
35
+ {
36
+ acceptLanguage: 'en',
37
+ accountID: this.manager.accountId!,
38
+ appVersion: '2.8.6',
39
+ method: 'devicedetail',
40
+ mobileId: '1234567890123456',
41
+ phoneBrand: 'SM N9005',
42
+ phoneOS: 'Android',
43
+ timeZone: this.manager.timeZone!,
44
+ token: this.manager.token!,
45
+ uuid: this.uuid
46
+ },
47
+ {
48
+ 'accept-language': 'en',
49
+ 'accountId': this.manager.accountId!,
50
+ 'appVersion': '2.8.6',
51
+ 'content-type': 'application/json',
52
+ 'tk': this.manager.token!,
53
+ 'tz': this.manager.timeZone!
54
+ }
55
+ );
56
+
57
+ const success = this.checkResponse([response, status], 'getDetails');
58
+ if (success && response) {
59
+ this.deviceStatus = response.result?.result?.deviceStatus === 'on' ? 'on' : 'off';
60
+ this.details = {
61
+ mode: response.result?.result?.mode || '',
62
+ speed: response.result?.result?.level || 0,
63
+ filterLife: response.result?.result?.filterLife || 0,
64
+ screenStatus: response.result?.result?.display || false,
65
+ childLock: response.result?.result?.childLock || false,
66
+ airQuality: response.result?.result?.airQuality || 0
67
+ };
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+
73
+ // Original implementation for other devices
29
74
  const [response, status] = await this.callApi(
30
75
  '/cloud/v2/deviceManaged/bypassV2',
31
76
  'post',
@@ -46,10 +91,7 @@ export class VeSyncAirBypass extends VeSyncFan {
46
91
  if (success) {
47
92
  const result = response?.result?.result;
48
93
  if (result) {
49
- // Update device status
50
94
  this.deviceStatus = result.enabled ? 'on' : 'off';
51
-
52
- // Store device settings
53
95
  this.details = {
54
96
  mode: result.mode || '',
55
97
  speed: result.level || 0,
@@ -59,7 +101,6 @@ export class VeSyncAirBypass extends VeSyncFan {
59
101
  airQuality: result.air_quality || 0
60
102
  };
61
103
 
62
- // Store configuration if available
63
104
  if (result.configuration) {
64
105
  this.config = result.configuration;
65
106
  }
@@ -74,6 +115,38 @@ export class VeSyncAirBypass extends VeSyncFan {
74
115
  */
75
116
  async turnOn(): Promise<boolean> {
76
117
  logger.info(`Turning on device: ${this.deviceName}`);
118
+
119
+ // Special handling for LV series
120
+ if (this.deviceType.startsWith('LV-')) {
121
+ const [response, status] = await this.callApi(
122
+ '/131airPurifier/v1/device/deviceStatus',
123
+ 'put',
124
+ {
125
+ acceptLanguage: 'en',
126
+ accountID: this.manager.accountId!,
127
+ status: 'on',
128
+ timeZone: this.manager.timeZone!,
129
+ token: this.manager.token!,
130
+ uuid: this.uuid
131
+ },
132
+ {
133
+ 'accept-language': 'en',
134
+ 'accountId': this.manager.accountId!,
135
+ 'appVersion': '2.8.6',
136
+ 'content-type': 'application/json',
137
+ 'tk': this.manager.token!,
138
+ 'tz': this.manager.timeZone!
139
+ }
140
+ );
141
+
142
+ const success = this.checkResponse([response, status], 'turnOn');
143
+ if (!success) {
144
+ logger.error(`Failed to turn on device: ${this.deviceName}`);
145
+ }
146
+ return success;
147
+ }
148
+
149
+ // Original implementation for other devices
77
150
  const [response, status] = await this.callApi(
78
151
  '/cloud/v2/deviceManaged/bypassV2',
79
152
  'post',
@@ -105,6 +178,38 @@ export class VeSyncAirBypass extends VeSyncFan {
105
178
  */
106
179
  async turnOff(): Promise<boolean> {
107
180
  logger.info(`Turning off device: ${this.deviceName}`);
181
+
182
+ // Special handling for LV series
183
+ if (this.deviceType.startsWith('LV-')) {
184
+ const [response, status] = await this.callApi(
185
+ '/131airPurifier/v1/device/deviceStatus',
186
+ 'put',
187
+ {
188
+ acceptLanguage: 'en',
189
+ accountID: this.manager.accountId!,
190
+ status: 'off',
191
+ timeZone: this.manager.timeZone!,
192
+ token: this.manager.token!,
193
+ uuid: this.uuid
194
+ },
195
+ {
196
+ 'accept-language': 'en',
197
+ 'accountId': this.manager.accountId!,
198
+ 'appVersion': '2.8.6',
199
+ 'content-type': 'application/json',
200
+ 'tk': this.manager.token!,
201
+ 'tz': this.manager.timeZone!
202
+ }
203
+ );
204
+
205
+ const success = this.checkResponse([response, status], 'turnOff');
206
+ if (!success) {
207
+ logger.error(`Failed to turn off device: ${this.deviceName}`);
208
+ }
209
+ return success;
210
+ }
211
+
212
+ // Original implementation for other devices
108
213
  const [response, status] = await this.callApi(
109
214
  '/cloud/v2/deviceManaged/bypassV2',
110
215
  'post',
@@ -519,12 +624,100 @@ export class VeSyncHumidifier extends VeSyncFan {
519
624
 
520
625
  const success = this.checkResponse([response, status], 'getDetails');
521
626
  if (success && response?.result?.result) {
522
- this.details = response.result.result;
627
+ const result = response.result.result;
628
+
629
+ // Handle model-specific status fields
630
+ if (this.deviceType.startsWith('Classic') || this.deviceType === 'Dual200S') {
631
+ this.deviceStatus = result.enabled ? 'on' : 'off';
632
+ } else {
633
+ this.deviceStatus = result.powerSwitch === 1 ? 'on' : 'off';
634
+ }
635
+
636
+ this.details = {
637
+ mode: result.mode || '',
638
+ humidity: result.humidity || 0,
639
+ mist_level: result.mistLevel || 0,
640
+ mist_virtual_level: result.virtualLevel || 0,
641
+ warm_level: result.warmLevel || 0,
642
+ water_lacks: result.waterLacks || false,
643
+ humidity_high: result.humidityHigh || false,
644
+ water_tank_lifted: result.waterTankLifted || false,
645
+ display: result.display || false,
646
+ automatic_stop: result.automaticStop || false,
647
+ configuration: result.configuration || {},
648
+ connection_status: result.connectionStatus || null
649
+ };
523
650
  logger.debug(`Successfully got details for device: ${this.deviceName}`);
524
651
  }
525
652
  return success;
526
653
  }
527
654
 
655
+ /**
656
+ * Turn device on
657
+ */
658
+ async turnOn(): Promise<boolean> {
659
+ return this.toggleSwitch(true);
660
+ }
661
+
662
+ /**
663
+ * Turn device off
664
+ */
665
+ async turnOff(): Promise<boolean> {
666
+ return this.toggleSwitch(false);
667
+ }
668
+
669
+ /**
670
+ * Get model-specific toggle payload
671
+ */
672
+ protected getTogglePayload(enabled: boolean): Record<string, any> {
673
+ // Model-specific toggle payload based on PyVeSync implementation
674
+ if (this.deviceType.startsWith('Classic') || this.deviceType === 'Dual200S') {
675
+ return {
676
+ enabled: enabled,
677
+ id: 0
678
+ };
679
+ } else if (this.deviceType.startsWith('Superior')) {
680
+ return {
681
+ powerSwitch: enabled ? 1 : 0,
682
+ switchIdx: 0
683
+ };
684
+ } else {
685
+ return {
686
+ powerSwitch: enabled ? 1 : 0
687
+ };
688
+ }
689
+ }
690
+
691
+ /**
692
+ * Toggle device power
693
+ */
694
+ async toggleSwitch(enabled: boolean): Promise<boolean> {
695
+ logger.info(`Setting device power to ${enabled ? 'on' : 'off'}: ${this.deviceName}`);
696
+ const [response, status] = await this.callApi(
697
+ '/cloud/v2/deviceManaged/bypassV2',
698
+ 'post',
699
+ {
700
+ ...Helpers.reqBody(this.manager, 'bypassV2'),
701
+ cid: this.cid,
702
+ configModule: this.configModule,
703
+ payload: {
704
+ data: this.getTogglePayload(enabled),
705
+ method: 'setSwitch',
706
+ source: 'APP'
707
+ }
708
+ },
709
+ Helpers.reqHeaderBypass()
710
+ );
711
+
712
+ const success = this.checkResponse([response, status], 'toggleSwitch');
713
+ if (success) {
714
+ this.deviceStatus = enabled ? 'on' : 'off';
715
+ } else {
716
+ logger.error(`Failed to set device power to ${enabled ? 'on' : 'off'} for device: ${this.deviceName}`);
717
+ }
718
+ return success;
719
+ }
720
+
528
721
  /**
529
722
  * Set mist level
530
723
  */
@@ -607,68 +800,6 @@ export class VeSyncHumidifier extends VeSyncFan {
607
800
  return success;
608
801
  }
609
802
 
610
- /**
611
- * Turn device on
612
- */
613
- async turnOn(): Promise<boolean> {
614
- logger.info(`Turning on device: ${this.deviceName}`);
615
- const [response, status] = await this.callApi(
616
- '/cloud/v2/deviceManaged/bypassV2',
617
- 'post',
618
- {
619
- ...Helpers.reqBody(this.manager, 'bypassV2'),
620
- cid: this.cid,
621
- configModule: this.configModule,
622
- payload: {
623
- data: {
624
- enabled: true,
625
- id: 0
626
- },
627
- method: 'setSwitch',
628
- source: 'APP'
629
- }
630
- },
631
- Helpers.reqHeaderBypass()
632
- );
633
-
634
- const success = this.checkResponse([response, status], 'turnOn');
635
- if (!success) {
636
- logger.error(`Failed to turn on device: ${this.deviceName}`);
637
- }
638
- return success;
639
- }
640
-
641
- /**
642
- * Turn device off
643
- */
644
- async turnOff(): Promise<boolean> {
645
- logger.info(`Turning off device: ${this.deviceName}`);
646
- const [response, status] = await this.callApi(
647
- '/cloud/v2/deviceManaged/bypassV2',
648
- 'post',
649
- {
650
- ...Helpers.reqBody(this.manager, 'bypassV2'),
651
- cid: this.cid,
652
- configModule: this.configModule,
653
- payload: {
654
- data: {
655
- enabled: false,
656
- id: 0
657
- },
658
- method: 'setSwitch',
659
- source: 'APP'
660
- }
661
- },
662
- Helpers.reqHeaderBypass()
663
- );
664
-
665
- const success = this.checkResponse([response, status], 'turnOff');
666
- if (!success) {
667
- logger.error(`Failed to turn off device: ${this.deviceName}`);
668
- }
669
- return success;
670
- }
671
-
672
803
  /**
673
804
  * Set target humidity
674
805
  */
@@ -938,6 +1069,25 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
938
1069
  logger.debug(`Initialized VeSyncWarmHumidifier device: ${this.deviceName}`);
939
1070
  }
940
1071
 
1072
+ /**
1073
+ * Get device details
1074
+ */
1075
+ async getDetails(): Promise<Boolean> {
1076
+ const success = await super.getDetails();
1077
+ if (success && this.details) {
1078
+ // Add warm humidifier specific fields
1079
+ this.details = {
1080
+ ...this.details,
1081
+ warm_enabled: this.details.warm_level > 0,
1082
+ drying_mode_enabled: this.details.autoDryingSwitch === 1,
1083
+ drying_mode_state: this.details.dryingMode || '',
1084
+ drying_mode_level: this.details.dryingLevel || 0,
1085
+ drying_mode_seconds_remaining: this.details.dryingRemainTime || 0
1086
+ };
1087
+ }
1088
+ return success;
1089
+ }
1090
+
941
1091
  /**
942
1092
  * Change fan speed - Implemented to satisfy interface but redirects to setMistLevel
943
1093
  */
@@ -976,7 +1126,9 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
976
1126
  );
977
1127
 
978
1128
  const success = this.checkResponse([response, status], 'setMode');
979
- if (!success) {
1129
+ if (success) {
1130
+ this.details.mode = mode;
1131
+ } else {
980
1132
  logger.error(`Failed to set mode to ${mode} for device: ${this.deviceName}`);
981
1133
  }
982
1134
  return success;
@@ -1018,7 +1170,10 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
1018
1170
  );
1019
1171
 
1020
1172
  const success = this.checkResponse([response, status], 'setWarmLevel');
1021
- if (!success) {
1173
+ if (success) {
1174
+ this.details.warm_level = level;
1175
+ this.details.warm_enabled = level > 0;
1176
+ } else {
1022
1177
  logger.error(`Failed to set warm level to ${level} for device: ${this.deviceName}`);
1023
1178
  }
1024
1179
  return success;
@@ -1054,11 +1209,49 @@ export class VeSyncWarmHumidifier extends VeSyncHumidifier {
1054
1209
  );
1055
1210
 
1056
1211
  const success = this.checkResponse([response, status], 'setDryingModeEnabled');
1057
- if (!success) {
1212
+ if (success) {
1213
+ this.details.drying_mode_enabled = enabled;
1214
+ this.details.autoDryingSwitch = enabled ? 1 : 0;
1215
+ } else {
1058
1216
  logger.error(`Failed to set drying mode to ${enabled} for device: ${this.deviceName}`);
1059
1217
  }
1060
1218
  return success;
1061
1219
  }
1220
+
1221
+ /**
1222
+ * Get warm mist enabled status
1223
+ */
1224
+ get warmMistEnabled(): boolean {
1225
+ return this.details.warm_enabled || false;
1226
+ }
1227
+
1228
+ /**
1229
+ * Get drying mode enabled status
1230
+ */
1231
+ get dryingModeEnabled(): boolean {
1232
+ return this.details.drying_mode_enabled || false;
1233
+ }
1234
+
1235
+ /**
1236
+ * Get drying mode state
1237
+ */
1238
+ get dryingModeState(): string {
1239
+ return this.details.drying_mode_state || '';
1240
+ }
1241
+
1242
+ /**
1243
+ * Get drying mode level
1244
+ */
1245
+ get dryingModeLevel(): number {
1246
+ return this.details.drying_mode_level || 0;
1247
+ }
1248
+
1249
+ /**
1250
+ * Get drying mode seconds remaining
1251
+ */
1252
+ get dryingModeSecondsRemaining(): number {
1253
+ return this.details.drying_mode_seconds_remaining || 0;
1254
+ }
1062
1255
  }
1063
1256
 
1064
1257
  // Export fan modules dictionary
@@ -1129,6 +1322,7 @@ export const fanModules: Record<string, new (details: Record<string, any>, manag
1129
1322
  'LUH-O601S-WUS': VeSyncWarmHumidifier,
1130
1323
  'LUH-O601S-KUS': VeSyncWarmHumidifier,
1131
1324
  'LUH-M101S-WUS': VeSyncWarmHumidifier,
1325
+ 'LUH-M101S-WEUR': VeSyncWarmHumidifier,
1132
1326
 
1133
1327
  // LEH Series
1134
1328
  'LEH-S601S-WUS': VeSyncWarmHumidifier,