tsvesync 1.0.89 → 1.0.91
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 +1 -36
- package/dist/__tests__/fans/vesyncAir131.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAir131.test.js +365 -0
- package/dist/__tests__/fans/vesyncAirBaseV2.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAirBaseV2.test.js +283 -0
- package/dist/__tests__/fans/vesyncAirBypass.test.d.ts +4 -0
- package/dist/__tests__/fans/vesyncAirBypass.test.js +257 -0
- package/dist/__tests__/vesyncFan.test.d.ts +4 -0
- package/dist/__tests__/vesyncFan.test.js +263 -0
- package/dist/lib/fans/air131.js +5 -4
- package/dist/lib/fans/airBypass.js +12 -4
- package/dist/lib/helpers.d.ts +0 -13
- package/dist/lib/helpers.js +4 -158
- package/dist/lib/vesync.d.ts +1 -1
- package/dist/lib/vesync.js +5 -5
- package/dist/lib/vesyncFan.d.ts +30 -4
- package/dist/lib/vesyncFan.js +70 -5
- package/package.json +1 -1
- package/src/lib/fans/air131.ts +5 -4
- package/src/lib/fans/airBypass.ts +10 -4
- package/src/lib/helpers.ts +3 -169
- package/src/lib/vesync.ts +6 -6
- package/src/lib/vesyncFan.ts +79 -7
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for VeSyncFan base class and air quality features
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vesyncFan_1 = require("../lib/vesyncFan");
|
|
7
|
+
// Mock VeSync manager
|
|
8
|
+
const mockManager = {
|
|
9
|
+
username: 'test@example.com',
|
|
10
|
+
password: 'testpass',
|
|
11
|
+
token: 'testtoken',
|
|
12
|
+
accountID: 'testaccount',
|
|
13
|
+
apiKey: 'testapikey',
|
|
14
|
+
apiBase: 'testbase',
|
|
15
|
+
timezone: 'UTC'
|
|
16
|
+
};
|
|
17
|
+
// Test implementation of abstract VeSyncFan class
|
|
18
|
+
class TestVeSyncFan extends vesyncFan_1.VeSyncFan {
|
|
19
|
+
async getDetails() {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
async turnOn() {
|
|
23
|
+
this.deviceStatus = 'on';
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
async turnOff() {
|
|
27
|
+
this.deviceStatus = 'off';
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
async changeFanSpeed(level) {
|
|
31
|
+
this.details.speed = level;
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
async changeMode(mode) {
|
|
35
|
+
this.details.mode = mode;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
describe('VeSyncFan Base Class', () => {
|
|
40
|
+
let fan;
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
const deviceDetails = {
|
|
43
|
+
deviceName: 'Test Fan',
|
|
44
|
+
deviceType: 'Core300S',
|
|
45
|
+
cid: 'test-cid',
|
|
46
|
+
uuid: 'test-uuid',
|
|
47
|
+
deviceStatus: 'off',
|
|
48
|
+
deviceRegion: 'US',
|
|
49
|
+
configModule: 'VeSyncAirBypass',
|
|
50
|
+
macId: '00:11:22:33:44:55',
|
|
51
|
+
deviceCategory: 'fan',
|
|
52
|
+
connectionStatus: 'online'
|
|
53
|
+
};
|
|
54
|
+
fan = new TestVeSyncFan(deviceDetails, mockManager);
|
|
55
|
+
});
|
|
56
|
+
describe('Air Quality Getters', () => {
|
|
57
|
+
it('should return default values when air quality data is not available', () => {
|
|
58
|
+
expect(fan.airQualityValue).toBe(0);
|
|
59
|
+
expect(fan.pm1).toBe(0);
|
|
60
|
+
expect(fan.pm10).toBe(0);
|
|
61
|
+
expect(fan.aqPercent).toBe(0);
|
|
62
|
+
expect(fan.airQualityLevel).toBe('');
|
|
63
|
+
});
|
|
64
|
+
it('should return correct air quality values when data is available', () => {
|
|
65
|
+
fan.details.air_quality_value = 25;
|
|
66
|
+
fan.details.pm1 = 15;
|
|
67
|
+
fan.details.pm10 = 30;
|
|
68
|
+
fan.details.aq_percent = 80;
|
|
69
|
+
fan.details.air_quality = 'Good';
|
|
70
|
+
expect(fan.airQualityValue).toBe(25);
|
|
71
|
+
expect(fan.pm1).toBe(15);
|
|
72
|
+
expect(fan.pm10).toBe(30);
|
|
73
|
+
expect(fan.aqPercent).toBe(80);
|
|
74
|
+
expect(fan.airQualityLevel).toBe('Good');
|
|
75
|
+
});
|
|
76
|
+
it('should handle numeric air quality levels', () => {
|
|
77
|
+
fan.details.air_quality = 2;
|
|
78
|
+
expect(fan.airQualityLevel).toBe(2);
|
|
79
|
+
});
|
|
80
|
+
it('should maintain backward compatibility with airQuality getter', () => {
|
|
81
|
+
fan.details.air_quality = 'Excellent';
|
|
82
|
+
expect(fan.airQuality).toBe('Excellent');
|
|
83
|
+
expect(fan.airQuality).toBe(fan.airQualityLevel);
|
|
84
|
+
});
|
|
85
|
+
it('should handle extreme air quality values', () => {
|
|
86
|
+
fan.details.air_quality_value = 999;
|
|
87
|
+
fan.details.pm1 = 500;
|
|
88
|
+
fan.details.pm10 = 1000;
|
|
89
|
+
fan.details.aq_percent = 100;
|
|
90
|
+
expect(fan.airQualityValue).toBe(999);
|
|
91
|
+
expect(fan.pm1).toBe(500);
|
|
92
|
+
expect(fan.pm10).toBe(1000);
|
|
93
|
+
expect(fan.aqPercent).toBe(100);
|
|
94
|
+
});
|
|
95
|
+
it('should handle zero air quality values', () => {
|
|
96
|
+
fan.details.air_quality_value = 0;
|
|
97
|
+
fan.details.pm1 = 0;
|
|
98
|
+
fan.details.pm10 = 0;
|
|
99
|
+
fan.details.aq_percent = 0;
|
|
100
|
+
expect(fan.airQualityValue).toBe(0);
|
|
101
|
+
expect(fan.pm1).toBe(0);
|
|
102
|
+
expect(fan.pm10).toBe(0);
|
|
103
|
+
expect(fan.aqPercent).toBe(0);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('Filter Life Handling', () => {
|
|
107
|
+
it('should return 0 when filter life is not available', () => {
|
|
108
|
+
expect(fan.filterLife).toBe(0);
|
|
109
|
+
});
|
|
110
|
+
it('should handle number format filter life (VeSyncAirBypass)', () => {
|
|
111
|
+
fan.details.filter_life = 85;
|
|
112
|
+
expect(fan.filterLife).toBe(85);
|
|
113
|
+
});
|
|
114
|
+
it('should handle object format filter life (VeSyncAir131)', () => {
|
|
115
|
+
fan.details.filter_life = { percent: 72 };
|
|
116
|
+
expect(fan.filterLife).toBe(72);
|
|
117
|
+
});
|
|
118
|
+
it('should handle object format with missing percent property', () => {
|
|
119
|
+
fan.details.filter_life = { other: 'value' };
|
|
120
|
+
expect(fan.filterLife).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
it('should handle null filter life object', () => {
|
|
123
|
+
fan.details.filter_life = null;
|
|
124
|
+
expect(fan.filterLife).toBe(0);
|
|
125
|
+
});
|
|
126
|
+
it('should handle undefined filter life', () => {
|
|
127
|
+
fan.details.filter_life = undefined;
|
|
128
|
+
expect(fan.filterLife).toBe(0);
|
|
129
|
+
});
|
|
130
|
+
it('should handle zero filter life values', () => {
|
|
131
|
+
fan.details.filter_life = 0;
|
|
132
|
+
expect(fan.filterLife).toBe(0);
|
|
133
|
+
fan.details.filter_life = { percent: 0 };
|
|
134
|
+
expect(fan.filterLife).toBe(0);
|
|
135
|
+
});
|
|
136
|
+
it('should handle 100% filter life', () => {
|
|
137
|
+
fan.details.filter_life = 100;
|
|
138
|
+
expect(fan.filterLife).toBe(100);
|
|
139
|
+
fan.details.filter_life = { percent: 100 };
|
|
140
|
+
expect(fan.filterLife).toBe(100);
|
|
141
|
+
});
|
|
142
|
+
it('should handle invalid filter life formats gracefully', () => {
|
|
143
|
+
fan.details.filter_life = 'invalid';
|
|
144
|
+
expect(fan.filterLife).toBe(0);
|
|
145
|
+
fan.details.filter_life = [];
|
|
146
|
+
expect(fan.filterLife).toBe(0);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('Feature Detection', () => {
|
|
150
|
+
it('should detect air quality feature for supported devices', () => {
|
|
151
|
+
expect(fan.hasFeature('air_quality')).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
it('should detect supported features based on device type', () => {
|
|
154
|
+
expect(fan.hasFeature('display')).toBe(true);
|
|
155
|
+
expect(fan.hasFeature('child_lock')).toBe(true);
|
|
156
|
+
expect(fan.hasFeature('night_light')).toBe(true);
|
|
157
|
+
expect(fan.hasFeature('timer')).toBe(true);
|
|
158
|
+
expect(fan.hasFeature('fan_speed')).toBe(true);
|
|
159
|
+
expect(fan.hasFeature('auto_mode')).toBe(true);
|
|
160
|
+
expect(fan.hasFeature('sleep_mode')).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
it('should return false for unsupported features', () => {
|
|
163
|
+
expect(fan.hasFeature('unsupported_feature')).toBe(false);
|
|
164
|
+
});
|
|
165
|
+
it('should handle devices without configuration', () => {
|
|
166
|
+
const unknownDevice = new TestVeSyncFan({
|
|
167
|
+
deviceName: 'Unknown Device',
|
|
168
|
+
deviceType: 'UnknownType',
|
|
169
|
+
cid: 'test-cid',
|
|
170
|
+
uuid: 'test-uuid'
|
|
171
|
+
}, mockManager);
|
|
172
|
+
expect(unknownDevice.hasFeature('air_quality')).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe('Feature Support in Current Mode', () => {
|
|
176
|
+
it('should support air quality in all modes', () => {
|
|
177
|
+
fan.details.mode = 'auto';
|
|
178
|
+
expect(fan.isFeatureSupportedInCurrentMode('air_quality')).toBe(true);
|
|
179
|
+
fan.details.mode = 'manual';
|
|
180
|
+
expect(fan.isFeatureSupportedInCurrentMode('air_quality')).toBe(true);
|
|
181
|
+
fan.details.mode = 'sleep';
|
|
182
|
+
expect(fan.isFeatureSupportedInCurrentMode('air_quality')).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
it('should disable display and child lock in sleep mode', () => {
|
|
185
|
+
fan.details.mode = 'sleep';
|
|
186
|
+
expect(fan.isFeatureSupportedInCurrentMode('display')).toBe(false);
|
|
187
|
+
expect(fan.isFeatureSupportedInCurrentMode('child_lock')).toBe(false);
|
|
188
|
+
});
|
|
189
|
+
it('should support display and child lock in other modes', () => {
|
|
190
|
+
fan.details.mode = 'auto';
|
|
191
|
+
expect(fan.isFeatureSupportedInCurrentMode('display')).toBe(true);
|
|
192
|
+
expect(fan.isFeatureSupportedInCurrentMode('child_lock')).toBe(true);
|
|
193
|
+
fan.details.mode = 'manual';
|
|
194
|
+
expect(fan.isFeatureSupportedInCurrentMode('display')).toBe(true);
|
|
195
|
+
expect(fan.isFeatureSupportedInCurrentMode('child_lock')).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
it('should return false for unsupported features regardless of mode', () => {
|
|
198
|
+
fan.details.mode = 'auto';
|
|
199
|
+
expect(fan.isFeatureSupportedInCurrentMode('unsupported')).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
describe('Device Capabilities', () => {
|
|
203
|
+
it('should return correct maximum fan speed', () => {
|
|
204
|
+
expect(fan.getMaxFanSpeed()).toBe(4); // Core300S has 4 speeds
|
|
205
|
+
});
|
|
206
|
+
it('should handle devices without speed levels configured', () => {
|
|
207
|
+
const deviceWithoutLevels = new TestVeSyncFan({
|
|
208
|
+
deviceName: 'Basic Device',
|
|
209
|
+
deviceType: 'UnknownType',
|
|
210
|
+
cid: 'test-cid',
|
|
211
|
+
uuid: 'test-uuid'
|
|
212
|
+
}, mockManager);
|
|
213
|
+
expect(deviceWithoutLevels.getMaxFanSpeed()).toBe(3); // Default fallback
|
|
214
|
+
});
|
|
215
|
+
it('should return current mode and speed', () => {
|
|
216
|
+
fan.details.mode = 'auto';
|
|
217
|
+
fan.details.speed = 3;
|
|
218
|
+
expect(fan.mode).toBe('auto');
|
|
219
|
+
expect(fan.speed).toBe(3);
|
|
220
|
+
});
|
|
221
|
+
it('should handle missing mode and speed', () => {
|
|
222
|
+
expect(fan.mode).toBe('');
|
|
223
|
+
expect(fan.speed).toBe(0);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe('Edge Cases and Error Handling', () => {
|
|
227
|
+
it('should handle missing details object', () => {
|
|
228
|
+
const minimalFan = new TestVeSyncFan({
|
|
229
|
+
deviceName: 'Minimal Device',
|
|
230
|
+
deviceType: 'Core300S',
|
|
231
|
+
cid: 'test-cid',
|
|
232
|
+
uuid: 'test-uuid'
|
|
233
|
+
}, mockManager);
|
|
234
|
+
// Details should be initialized as empty object
|
|
235
|
+
expect(minimalFan.airQualityValue).toBe(0);
|
|
236
|
+
expect(minimalFan.filterLife).toBe(0);
|
|
237
|
+
expect(minimalFan.mode).toBe('');
|
|
238
|
+
expect(minimalFan.speed).toBe(0);
|
|
239
|
+
});
|
|
240
|
+
it('should handle null and undefined values gracefully', () => {
|
|
241
|
+
fan.details.air_quality_value = null;
|
|
242
|
+
fan.details.filter_life = null;
|
|
243
|
+
fan.details.mode = null;
|
|
244
|
+
fan.details.speed = null;
|
|
245
|
+
expect(fan.airQualityValue).toBe(0);
|
|
246
|
+
expect(fan.filterLife).toBe(0);
|
|
247
|
+
expect(fan.mode).toBe('');
|
|
248
|
+
expect(fan.speed).toBe(0);
|
|
249
|
+
});
|
|
250
|
+
it('should handle invalid data types', () => {
|
|
251
|
+
fan.details.air_quality_value = 'invalid';
|
|
252
|
+
fan.details.pm1 = 'invalid';
|
|
253
|
+
fan.details.pm10 = 'invalid';
|
|
254
|
+
fan.details.aq_percent = 'invalid';
|
|
255
|
+
fan.details.speed = 'invalid';
|
|
256
|
+
expect(fan.airQualityValue).toBe(0);
|
|
257
|
+
expect(fan.pm1).toBe(0);
|
|
258
|
+
expect(fan.pm10).toBe(0);
|
|
259
|
+
expect(fan.aqPercent).toBe(0);
|
|
260
|
+
expect(fan.speed).toBe(0);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
package/dist/lib/fans/air131.js
CHANGED
|
@@ -56,10 +56,11 @@ class VeSyncAir131 extends vesyncFan_1.VeSyncFan {
|
|
|
56
56
|
this.details = {
|
|
57
57
|
mode: data.mode || 'manual', // Default to manual if not specified
|
|
58
58
|
speed: data.level || 0,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
filter_life: data.filterLife || data.filter_life || 0,
|
|
60
|
+
screen_status: data.screenStatus || 'off',
|
|
61
|
+
child_lock: data.childLock || false,
|
|
62
|
+
air_quality: data.airQuality || 'unknown',
|
|
63
|
+
air_quality_value: data.air_quality_value || 0,
|
|
63
64
|
active_time: data.activeTime || 0
|
|
64
65
|
};
|
|
65
66
|
// Store last known speed when device is on and has a valid speed
|
|
@@ -56,11 +56,19 @@ class VeSyncAirBypass extends vesyncFan_1.VeSyncFan {
|
|
|
56
56
|
this.details = {
|
|
57
57
|
mode: result.mode || '',
|
|
58
58
|
speed: result.level || 0,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
filter_life: result.filter_life || 0,
|
|
60
|
+
screen_status: result.display ? 'on' : 'off',
|
|
61
|
+
child_lock: result.child_lock || false,
|
|
62
|
+
air_quality: result.air_quality || 0,
|
|
63
|
+
air_quality_value: result.air_quality_value || 0
|
|
63
64
|
};
|
|
65
|
+
// Capture additional air quality properties if available
|
|
66
|
+
if ('PM1' in result)
|
|
67
|
+
this.details.pm1 = result.PM1;
|
|
68
|
+
if ('PM10' in result)
|
|
69
|
+
this.details.pm10 = result.PM10;
|
|
70
|
+
if ('AQPercent' in result)
|
|
71
|
+
this.details.aq_percent = result.AQPercent;
|
|
64
72
|
// Don't overwrite config as it contains features array
|
|
65
73
|
if (result.configuration) {
|
|
66
74
|
this.details.configuration = result.configuration;
|
package/dist/lib/helpers.d.ts
CHANGED
|
@@ -2,21 +2,8 @@
|
|
|
2
2
|
* Helper functions for VeSync API
|
|
3
3
|
*/
|
|
4
4
|
import { VeSync } from './vesync';
|
|
5
|
-
export declare const REGIONAL_ENDPOINTS: {
|
|
6
|
-
readonly US: "https://smartapi.vesync.com";
|
|
7
|
-
readonly EU: "https://smartapi.vesync.eu";
|
|
8
|
-
readonly ASIA: "https://smartapi.vesync.com.cn";
|
|
9
|
-
};
|
|
10
5
|
export declare function getApiBaseUrl(): string;
|
|
11
6
|
export declare function setApiBaseUrl(url: string): void;
|
|
12
|
-
/**
|
|
13
|
-
* Get regional API endpoint based on timezone
|
|
14
|
-
*/
|
|
15
|
-
export declare function getRegionalEndpoint(timeZone: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Set API endpoint based on timezone if not explicitly set
|
|
18
|
-
*/
|
|
19
|
-
export declare function setRegionalEndpoint(timeZone: string): void;
|
|
20
7
|
export declare const API_RATE_LIMIT = 30;
|
|
21
8
|
export declare const API_TIMEOUT = 15000;
|
|
22
9
|
export declare const APP_VERSION = "2.8.6";
|
package/dist/lib/helpers.js
CHANGED
|
@@ -6,174 +6,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Helpers = exports.BYPASS_HEADER_UA = exports.MOBILE_ID = exports.DEFAULT_REGION = exports.DEFAULT_TZ = exports.USER_TYPE = exports.PHONE_OS = exports.PHONE_BRAND = exports.APP_VERSION = exports.API_TIMEOUT = exports.API_RATE_LIMIT =
|
|
9
|
+
exports.Helpers = exports.BYPASS_HEADER_UA = exports.MOBILE_ID = exports.DEFAULT_REGION = exports.DEFAULT_TZ = exports.USER_TYPE = exports.PHONE_OS = exports.PHONE_BRAND = exports.APP_VERSION = exports.API_TIMEOUT = exports.API_RATE_LIMIT = void 0;
|
|
10
10
|
exports.getApiBaseUrl = getApiBaseUrl;
|
|
11
11
|
exports.setApiBaseUrl = setApiBaseUrl;
|
|
12
|
-
exports.getRegionalEndpoint = getRegionalEndpoint;
|
|
13
|
-
exports.setRegionalEndpoint = setRegionalEndpoint;
|
|
14
12
|
const axios_1 = __importDefault(require("axios"));
|
|
15
13
|
const crypto_1 = __importDefault(require("crypto"));
|
|
16
14
|
const logger_1 = require("./logger");
|
|
17
|
-
// API configuration
|
|
15
|
+
// API configuration - Always use US endpoint
|
|
18
16
|
let _apiBaseUrl = 'https://smartapi.vesync.com';
|
|
19
|
-
// Regional API endpoints
|
|
20
|
-
exports.REGIONAL_ENDPOINTS = {
|
|
21
|
-
US: 'https://smartapi.vesync.com',
|
|
22
|
-
EU: 'https://smartapi.vesync.eu',
|
|
23
|
-
ASIA: 'https://smartapi.vesync.com.cn'
|
|
24
|
-
};
|
|
25
|
-
// Timezone to region mapping for API endpoint detection
|
|
26
|
-
const TIMEZONE_REGION_MAP = {
|
|
27
|
-
// European timezones - Western Europe
|
|
28
|
-
'Europe/Berlin': 'EU',
|
|
29
|
-
'Europe/London': 'EU',
|
|
30
|
-
'Europe/Paris': 'EU',
|
|
31
|
-
'Europe/Rome': 'EU',
|
|
32
|
-
'Europe/Madrid': 'EU',
|
|
33
|
-
'Europe/Amsterdam': 'EU',
|
|
34
|
-
'Europe/Vienna': 'EU',
|
|
35
|
-
'Europe/Brussels': 'EU',
|
|
36
|
-
'Europe/Copenhagen': 'EU',
|
|
37
|
-
'Europe/Dublin': 'EU',
|
|
38
|
-
'Europe/Helsinki': 'EU',
|
|
39
|
-
'Europe/Lisbon': 'EU',
|
|
40
|
-
'Europe/Luxembourg': 'EU',
|
|
41
|
-
'Europe/Oslo': 'EU',
|
|
42
|
-
'Europe/Prague': 'EU',
|
|
43
|
-
'Europe/Stockholm': 'EU',
|
|
44
|
-
'Europe/Warsaw': 'EU',
|
|
45
|
-
'Europe/Zurich': 'EU',
|
|
46
|
-
// European timezones - Eastern Europe
|
|
47
|
-
'Europe/Athens': 'EU',
|
|
48
|
-
'Europe/Budapest': 'EU',
|
|
49
|
-
'Europe/Bucharest': 'EU',
|
|
50
|
-
'Europe/Sofia': 'EU',
|
|
51
|
-
'Europe/Kiev': 'EU',
|
|
52
|
-
'Europe/Riga': 'EU',
|
|
53
|
-
'Europe/Vilnius': 'EU',
|
|
54
|
-
'Europe/Tallinn': 'EU',
|
|
55
|
-
'Europe/Zagreb': 'EU',
|
|
56
|
-
'Europe/Ljubljana': 'EU',
|
|
57
|
-
'Europe/Bratislava': 'EU',
|
|
58
|
-
'Europe/Belgrade': 'EU',
|
|
59
|
-
'Europe/Sarajevo': 'EU',
|
|
60
|
-
'Europe/Podgorica': 'EU',
|
|
61
|
-
'Europe/Skopje': 'EU',
|
|
62
|
-
'Europe/Tirane': 'EU',
|
|
63
|
-
// European timezones - Nordic/Baltic
|
|
64
|
-
'Europe/Reykjavik': 'EU',
|
|
65
|
-
'Europe/Moscow': 'EU',
|
|
66
|
-
'Europe/Kaliningrad': 'EU',
|
|
67
|
-
'Europe/Minsk': 'EU',
|
|
68
|
-
// European timezones - Other
|
|
69
|
-
'Europe/Malta': 'EU',
|
|
70
|
-
'Europe/Monaco': 'EU',
|
|
71
|
-
'Europe/Vatican': 'EU',
|
|
72
|
-
'Europe/San_Marino': 'EU',
|
|
73
|
-
'Europe/Andorra': 'EU',
|
|
74
|
-
'Europe/Gibraltar': 'EU',
|
|
75
|
-
'Europe/Isle_of_Man': 'EU',
|
|
76
|
-
'Europe/Jersey': 'EU',
|
|
77
|
-
'Europe/Guernsey': 'EU',
|
|
78
|
-
// Asian timezones - East Asia
|
|
79
|
-
'Asia/Shanghai': 'ASIA',
|
|
80
|
-
'Asia/Beijing': 'ASIA',
|
|
81
|
-
'Asia/Hong_Kong': 'ASIA',
|
|
82
|
-
'Asia/Tokyo': 'ASIA',
|
|
83
|
-
'Asia/Seoul': 'ASIA',
|
|
84
|
-
'Asia/Singapore': 'ASIA',
|
|
85
|
-
'Asia/Taipei': 'ASIA',
|
|
86
|
-
'Asia/Macau': 'ASIA',
|
|
87
|
-
// Asian timezones - Southeast Asia
|
|
88
|
-
'Asia/Bangkok': 'ASIA',
|
|
89
|
-
'Asia/Jakarta': 'ASIA',
|
|
90
|
-
'Asia/Manila': 'ASIA',
|
|
91
|
-
'Asia/Ho_Chi_Minh': 'ASIA',
|
|
92
|
-
'Asia/Kuala_Lumpur': 'ASIA',
|
|
93
|
-
'Asia/Phnom_Penh': 'ASIA',
|
|
94
|
-
'Asia/Vientiane': 'ASIA',
|
|
95
|
-
'Asia/Yangon': 'ASIA',
|
|
96
|
-
'Asia/Brunei': 'ASIA',
|
|
97
|
-
// Asian timezones - South Asia
|
|
98
|
-
'Asia/Mumbai': 'ASIA',
|
|
99
|
-
'Asia/Delhi': 'ASIA',
|
|
100
|
-
'Asia/Kolkata': 'ASIA',
|
|
101
|
-
'Asia/Dhaka': 'ASIA',
|
|
102
|
-
'Asia/Karachi': 'ASIA',
|
|
103
|
-
'Asia/Colombo': 'ASIA',
|
|
104
|
-
'Asia/Kathmandu': 'ASIA',
|
|
105
|
-
'Asia/Thimphu': 'ASIA',
|
|
106
|
-
// Asian timezones - Central Asia
|
|
107
|
-
'Asia/Almaty': 'ASIA',
|
|
108
|
-
'Asia/Bishkek': 'ASIA',
|
|
109
|
-
'Asia/Dushanbe': 'ASIA',
|
|
110
|
-
'Asia/Tashkent': 'ASIA',
|
|
111
|
-
'Asia/Ashgabat': 'ASIA',
|
|
112
|
-
// Asian timezones - West Asia / Middle East
|
|
113
|
-
'Asia/Dubai': 'ASIA',
|
|
114
|
-
'Asia/Riyadh': 'ASIA',
|
|
115
|
-
'Asia/Tehran': 'ASIA',
|
|
116
|
-
'Asia/Baghdad': 'ASIA',
|
|
117
|
-
'Asia/Kuwait': 'ASIA',
|
|
118
|
-
'Asia/Qatar': 'ASIA',
|
|
119
|
-
'Asia/Bahrain': 'ASIA',
|
|
120
|
-
'Asia/Muscat': 'ASIA',
|
|
121
|
-
'Asia/Baku': 'ASIA',
|
|
122
|
-
'Asia/Yerevan': 'ASIA',
|
|
123
|
-
'Asia/Tbilisi': 'ASIA',
|
|
124
|
-
// Oceania (using ASIA endpoint for now)
|
|
125
|
-
'Australia/Sydney': 'ASIA',
|
|
126
|
-
'Australia/Melbourne': 'ASIA',
|
|
127
|
-
'Australia/Brisbane': 'ASIA',
|
|
128
|
-
'Australia/Perth': 'ASIA',
|
|
129
|
-
'Australia/Adelaide': 'ASIA',
|
|
130
|
-
'Australia/Hobart': 'ASIA',
|
|
131
|
-
'Australia/Darwin': 'ASIA',
|
|
132
|
-
'Australia/Canberra': 'ASIA',
|
|
133
|
-
'Pacific/Auckland': 'ASIA',
|
|
134
|
-
'Pacific/Wellington': 'ASIA',
|
|
135
|
-
'Pacific/Fiji': 'ASIA',
|
|
136
|
-
'Pacific/Honolulu': 'US',
|
|
137
|
-
// North America (Canada)
|
|
138
|
-
'America/Toronto': 'US',
|
|
139
|
-
'America/Montreal': 'US',
|
|
140
|
-
'America/Vancouver': 'US',
|
|
141
|
-
'America/Calgary': 'US',
|
|
142
|
-
'America/Edmonton': 'US',
|
|
143
|
-
'America/Winnipeg': 'US',
|
|
144
|
-
'America/Halifax': 'US',
|
|
145
|
-
'America/St_Johns': 'US',
|
|
146
|
-
// North America (Mexico)
|
|
147
|
-
'America/Mexico_City': 'US',
|
|
148
|
-
'America/Tijuana': 'US',
|
|
149
|
-
'America/Cancun': 'US',
|
|
150
|
-
'America/Merida': 'US',
|
|
151
|
-
'America/Monterrey': 'US'
|
|
152
|
-
// US and other timezones default to US endpoint
|
|
153
|
-
};
|
|
154
17
|
function getApiBaseUrl() {
|
|
155
18
|
return _apiBaseUrl;
|
|
156
19
|
}
|
|
157
20
|
function setApiBaseUrl(url) {
|
|
158
21
|
_apiBaseUrl = url;
|
|
159
22
|
}
|
|
160
|
-
/**
|
|
161
|
-
* Get regional API endpoint based on timezone
|
|
162
|
-
*/
|
|
163
|
-
function getRegionalEndpoint(timeZone) {
|
|
164
|
-
const region = TIMEZONE_REGION_MAP[timeZone] || 'US';
|
|
165
|
-
return exports.REGIONAL_ENDPOINTS[region];
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Set API endpoint based on timezone if not explicitly set
|
|
169
|
-
*/
|
|
170
|
-
function setRegionalEndpoint(timeZone) {
|
|
171
|
-
const regionalUrl = getRegionalEndpoint(timeZone);
|
|
172
|
-
if (_apiBaseUrl === exports.REGIONAL_ENDPOINTS.US && regionalUrl !== exports.REGIONAL_ENDPOINTS.US) {
|
|
173
|
-
logger_1.logger.debug(`Setting regional API endpoint for ${timeZone}: ${regionalUrl}`);
|
|
174
|
-
setApiBaseUrl(regionalUrl);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
23
|
exports.API_RATE_LIMIT = 30;
|
|
178
24
|
exports.API_TIMEOUT = 15000;
|
|
179
25
|
exports.APP_VERSION = '2.8.6';
|
|
@@ -320,8 +166,8 @@ class Helpers {
|
|
|
320
166
|
try {
|
|
321
167
|
// Ensure API base URL is properly set
|
|
322
168
|
if (!_apiBaseUrl || _apiBaseUrl === 'undefined') {
|
|
323
|
-
logger_1.logger.error('API base URL is not properly configured. Setting
|
|
324
|
-
|
|
169
|
+
logger_1.logger.error('API base URL is not properly configured. Setting to default US endpoint...');
|
|
170
|
+
setApiBaseUrl('https://smartapi.vesync.com');
|
|
325
171
|
}
|
|
326
172
|
const url = _apiBaseUrl + endpoint;
|
|
327
173
|
logger_1.logger.debug(`Making API call to: ${url}`);
|
package/dist/lib/vesync.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export declare class VeSync {
|
|
|
43
43
|
* Initialize VeSync Manager
|
|
44
44
|
* @param username - VeSync account username
|
|
45
45
|
* @param password - VeSync account password
|
|
46
|
-
* @param timeZone - Optional timezone (defaults to America/New_York)
|
|
46
|
+
* @param timeZone - Optional timezone for device operations (defaults to America/New_York)
|
|
47
47
|
* @param debug - Optional debug mode flag
|
|
48
48
|
* @param redact - Optional redact mode flag
|
|
49
49
|
* @param apiUrl - Optional API base URL override
|
package/dist/lib/vesync.js
CHANGED
|
@@ -124,7 +124,7 @@ class VeSync {
|
|
|
124
124
|
* Initialize VeSync Manager
|
|
125
125
|
* @param username - VeSync account username
|
|
126
126
|
* @param password - VeSync account password
|
|
127
|
-
* @param timeZone - Optional timezone (defaults to America/New_York)
|
|
127
|
+
* @param timeZone - Optional timezone for device operations (defaults to America/New_York)
|
|
128
128
|
* @param debug - Optional debug mode flag
|
|
129
129
|
* @param redact - Optional redact mode flag
|
|
130
130
|
* @param apiUrl - Optional API base URL override
|
|
@@ -173,13 +173,13 @@ class VeSync {
|
|
|
173
173
|
this.timeZone = helpers_1.DEFAULT_TZ;
|
|
174
174
|
logger_1.logger.debug('Time zone is not a string');
|
|
175
175
|
}
|
|
176
|
-
// Set custom API URL if provided, otherwise use
|
|
176
|
+
// Set custom API URL if provided, otherwise use default US endpoint
|
|
177
177
|
if (apiUrl) {
|
|
178
178
|
(0, helpers_1.setApiBaseUrl)(apiUrl);
|
|
179
179
|
}
|
|
180
180
|
else {
|
|
181
|
-
//
|
|
182
|
-
(0, helpers_1.
|
|
181
|
+
// Always use US endpoint
|
|
182
|
+
(0, helpers_1.setApiBaseUrl)('https://smartapi.vesync.com');
|
|
183
183
|
}
|
|
184
184
|
// Set custom logger if provided
|
|
185
185
|
if (customLogger) {
|
|
@@ -428,7 +428,7 @@ class VeSync {
|
|
|
428
428
|
switch (response.code) {
|
|
429
429
|
case -11012022:
|
|
430
430
|
logger_1.logger.error('App version too low error. Current version:', body.appVersion);
|
|
431
|
-
logger_1.logger.error('This typically indicates
|
|
431
|
+
logger_1.logger.error('This typically indicates an API version compatibility issue.');
|
|
432
432
|
break;
|
|
433
433
|
case -11003:
|
|
434
434
|
logger_1.logger.error('Authentication failed - check credentials');
|
package/dist/lib/vesyncFan.d.ts
CHANGED
|
@@ -18,10 +18,16 @@ export declare abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
18
18
|
protected details: {
|
|
19
19
|
mode?: string;
|
|
20
20
|
speed?: number;
|
|
21
|
-
filter_life?: number
|
|
21
|
+
filter_life?: number | {
|
|
22
|
+
percent: number;
|
|
23
|
+
};
|
|
22
24
|
screen_status?: 'on' | 'off';
|
|
23
25
|
child_lock?: boolean;
|
|
24
|
-
air_quality?: string;
|
|
26
|
+
air_quality?: string | number;
|
|
27
|
+
air_quality_value?: number;
|
|
28
|
+
pm1?: number;
|
|
29
|
+
pm10?: number;
|
|
30
|
+
aq_percent?: number;
|
|
25
31
|
humidity?: number;
|
|
26
32
|
mist_level?: number;
|
|
27
33
|
warm_level?: number;
|
|
@@ -73,9 +79,29 @@ export declare abstract class VeSyncFan extends VeSyncBaseDevice {
|
|
|
73
79
|
*/
|
|
74
80
|
get childLock(): boolean;
|
|
75
81
|
/**
|
|
76
|
-
* Get air quality (if supported)
|
|
82
|
+
* Get air quality level (if supported)
|
|
77
83
|
*/
|
|
78
|
-
get
|
|
84
|
+
get airQualityLevel(): string | number;
|
|
85
|
+
/**
|
|
86
|
+
* Get air quality (alias for airQualityLevel for backward compatibility)
|
|
87
|
+
*/
|
|
88
|
+
get airQuality(): string | number;
|
|
89
|
+
/**
|
|
90
|
+
* Get air quality value - PM2.5 in μg/m³ (if supported)
|
|
91
|
+
*/
|
|
92
|
+
get airQualityValue(): number;
|
|
93
|
+
/**
|
|
94
|
+
* Get PM1 value (if supported)
|
|
95
|
+
*/
|
|
96
|
+
get pm1(): number;
|
|
97
|
+
/**
|
|
98
|
+
* Get PM10 value (if supported)
|
|
99
|
+
*/
|
|
100
|
+
get pm10(): number;
|
|
101
|
+
/**
|
|
102
|
+
* Get air quality percentage (if supported)
|
|
103
|
+
*/
|
|
104
|
+
get aqPercent(): number;
|
|
79
105
|
/**
|
|
80
106
|
* Get humidity level (if supported)
|
|
81
107
|
*/
|