tsvesync 1.0.103 → 1.0.104
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 +66 -2
- package/dist/lib/helpers.d.ts +10 -1
- package/dist/lib/helpers.js +53 -4
- package/dist/lib/vesync.d.ts +2 -0
- package/dist/lib/vesync.js +48 -2
- package/package.json +1 -1
- package/src/lib/helpers.ts +56 -4
- package/src/lib/vesync.ts +55 -3
package/README.md
CHANGED
|
@@ -6,8 +6,10 @@ A TypeScript library for interacting with VeSync smart home devices. This librar
|
|
|
6
6
|
|
|
7
7
|
- **Enhanced Authentication**: Now supports the new VeSync authentication flow (pyvesync PR #340) with automatic fallback to legacy authentication
|
|
8
8
|
- **Regional API Support**: Automatic detection and routing for US (`smartapi.vesync.com`) and EU (`smartapi.vesync.eu`) endpoints
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
9
|
+
- **International Account Support**: Full support for accounts worldwide including Australia, New Zealand, Japan, and all European countries
|
|
10
|
+
- **Country Code Configuration**: Specify your country code for proper authentication with international accounts
|
|
11
|
+
- **Improved Error Handling**: Better recovery from API changes and authentication errors with helpful guidance
|
|
12
|
+
- **Cross-Region Support**: Automatic handling of cross-region authentication errors
|
|
11
13
|
|
|
12
14
|
## Features
|
|
13
15
|
|
|
@@ -176,6 +178,68 @@ await manager.login();
|
|
|
176
178
|
await manager.getDevices();
|
|
177
179
|
```
|
|
178
180
|
|
|
181
|
+
### International Account Support
|
|
182
|
+
|
|
183
|
+
For accounts created outside the United States, you may need to specify your country code:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { VeSync } from 'tsvesync';
|
|
187
|
+
|
|
188
|
+
// For Australian users
|
|
189
|
+
const manager = new VeSync(username, password, 'America/Sydney', {
|
|
190
|
+
countryCode: 'AU'
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// For German users
|
|
194
|
+
const manager = new VeSync(username, password, 'Europe/Berlin', {
|
|
195
|
+
countryCode: 'DE'
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// For UK users
|
|
199
|
+
const manager = new VeSync(username, password, 'Europe/London', {
|
|
200
|
+
countryCode: 'GB'
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
await manager.login();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Supported Country Codes
|
|
207
|
+
|
|
208
|
+
The library supports all country codes. Common ones include:
|
|
209
|
+
|
|
210
|
+
**Americas:**
|
|
211
|
+
- `US` - United States (default)
|
|
212
|
+
- `CA` - Canada
|
|
213
|
+
- `MX` - Mexico
|
|
214
|
+
|
|
215
|
+
**Europe:** (uses EU endpoint)
|
|
216
|
+
- `GB` - United Kingdom
|
|
217
|
+
- `DE` - Germany
|
|
218
|
+
- `FR` - France
|
|
219
|
+
- `IT` - Italy
|
|
220
|
+
- `ES` - Spain
|
|
221
|
+
- `NL` - Netherlands
|
|
222
|
+
- `SE` - Sweden
|
|
223
|
+
- `NO` - Norway
|
|
224
|
+
- `DK` - Denmark
|
|
225
|
+
- `FI` - Finland
|
|
226
|
+
- `PL` - Poland
|
|
227
|
+
- And all other European countries
|
|
228
|
+
|
|
229
|
+
**Asia-Pacific:** (uses US endpoint)
|
|
230
|
+
- `AU` - Australia
|
|
231
|
+
- `NZ` - New Zealand
|
|
232
|
+
- `JP` - Japan
|
|
233
|
+
- `SG` - Singapore
|
|
234
|
+
- `CN` - China
|
|
235
|
+
- `KR` - South Korea
|
|
236
|
+
|
|
237
|
+
**Important Notes:**
|
|
238
|
+
- If you get an authentication error about "country code mismatch" or "cross-region error", you need to set your country code
|
|
239
|
+
- The country code should match the country where your VeSync account was created
|
|
240
|
+
- European countries automatically use the EU endpoint (`smartapi.vesync.eu`)
|
|
241
|
+
- All other countries use the US endpoint (`smartapi.vesync.com`)
|
|
242
|
+
|
|
179
243
|
### Working with Devices
|
|
180
244
|
|
|
181
245
|
```typescript
|
package/dist/lib/helpers.d.ts
CHANGED
|
@@ -33,6 +33,15 @@ export declare const AUTH_ERROR_CODES: {
|
|
|
33
33
|
CROSS_REGION: number;
|
|
34
34
|
CROSS_REGION_ALT: number;
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Get list of country codes that use the US endpoint
|
|
38
|
+
* These are tried when we get a cross-region error
|
|
39
|
+
*/
|
|
40
|
+
export declare function getUSEndpointCountryCodes(): string[];
|
|
41
|
+
/**
|
|
42
|
+
* Get the appropriate API endpoint based on country code
|
|
43
|
+
*/
|
|
44
|
+
export declare function getEndpointForCountryCode(countryCode: string): 'US' | 'EU';
|
|
36
45
|
/**
|
|
37
46
|
* Detect user's home region from email domain or country hints
|
|
38
47
|
*/
|
|
@@ -158,7 +167,7 @@ export declare class Helpers {
|
|
|
158
167
|
/**
|
|
159
168
|
* Perform new two-step authentication flow
|
|
160
169
|
*/
|
|
161
|
-
static authNewFlow(manager: VeSync, appId: string, region?: string): Promise<[boolean, string | null, string | null, string | null]>;
|
|
170
|
+
static authNewFlow(manager: VeSync, appId: string, region?: string, countryCodeOverride?: string): Promise<[boolean, string | null, string | null, string | null]>;
|
|
162
171
|
/**
|
|
163
172
|
* Perform legacy authentication (fallback)
|
|
164
173
|
*/
|
package/dist/lib/helpers.js
CHANGED
|
@@ -12,6 +12,8 @@ exports.setApiBaseUrl = setApiBaseUrl;
|
|
|
12
12
|
exports.getCurrentRegion = getCurrentRegion;
|
|
13
13
|
exports.setCurrentRegion = setCurrentRegion;
|
|
14
14
|
exports.getRegionFromCountryCode = getRegionFromCountryCode;
|
|
15
|
+
exports.getUSEndpointCountryCodes = getUSEndpointCountryCodes;
|
|
16
|
+
exports.getEndpointForCountryCode = getEndpointForCountryCode;
|
|
15
17
|
exports.detectUserRegion = detectUserRegion;
|
|
16
18
|
exports.getCountryCodeFromRegion = getCountryCodeFromRegion;
|
|
17
19
|
exports.generateAppId = generateAppId;
|
|
@@ -82,6 +84,38 @@ exports.AUTH_ERROR_CODES = {
|
|
|
82
84
|
CROSS_REGION: -11260022,
|
|
83
85
|
CROSS_REGION_ALT: -11261022
|
|
84
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* Get list of country codes that use the US endpoint
|
|
89
|
+
* These are tried when we get a cross-region error
|
|
90
|
+
*/
|
|
91
|
+
function getUSEndpointCountryCodes() {
|
|
92
|
+
// Countries that use the US endpoint but might need their own country code
|
|
93
|
+
return ['US', 'AU', 'NZ', 'JP', 'CA', 'MX', 'SG'];
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get the appropriate API endpoint based on country code
|
|
97
|
+
*/
|
|
98
|
+
function getEndpointForCountryCode(countryCode) {
|
|
99
|
+
// European country codes that should use the EU endpoint
|
|
100
|
+
const euCountryCodes = [
|
|
101
|
+
// Western Europe
|
|
102
|
+
'GB', 'DE', 'FR', 'IT', 'ES', 'NL', 'BE', 'AT', 'CH', 'IE', 'PT', 'LU',
|
|
103
|
+
// Nordic countries
|
|
104
|
+
'SE', 'NO', 'DK', 'FI', 'IS',
|
|
105
|
+
// Eastern Europe
|
|
106
|
+
'PL', 'CZ', 'HU', 'RO', 'BG', 'HR', 'SI', 'SK', 'LT', 'LV', 'EE',
|
|
107
|
+
// Southern Europe
|
|
108
|
+
'GR', 'CY', 'MT',
|
|
109
|
+
// Other European
|
|
110
|
+
'AL', 'BA', 'RS', 'ME', 'MK', 'MD', 'UA', 'BY'
|
|
111
|
+
];
|
|
112
|
+
// Check if country code is in EU list
|
|
113
|
+
if (euCountryCodes.includes(countryCode)) {
|
|
114
|
+
return 'EU';
|
|
115
|
+
}
|
|
116
|
+
// Everything else uses US endpoint (including AU, NZ, JP, CA, MX, etc.)
|
|
117
|
+
return 'US';
|
|
118
|
+
}
|
|
85
119
|
/**
|
|
86
120
|
* Detect user's home region from email domain or country hints
|
|
87
121
|
*/
|
|
@@ -456,7 +490,7 @@ class Helpers {
|
|
|
456
490
|
/**
|
|
457
491
|
* Perform new two-step authentication flow
|
|
458
492
|
*/
|
|
459
|
-
static async authNewFlow(manager, appId, region = 'US') {
|
|
493
|
+
static async authNewFlow(manager, appId, region = 'US', countryCodeOverride) {
|
|
460
494
|
try {
|
|
461
495
|
// Generate terminal ID to be used in both steps
|
|
462
496
|
const terminalId = generateTerminalId();
|
|
@@ -504,8 +538,9 @@ class Helpers {
|
|
|
504
538
|
}
|
|
505
539
|
logger_1.logger.debug('Step 1 successful, got authorization code');
|
|
506
540
|
// Step 2: Login with authorization code
|
|
507
|
-
// Use the
|
|
508
|
-
|
|
541
|
+
// Use the override if provided, otherwise default to 'US'
|
|
542
|
+
// Users should specify their actual country code in the configuration
|
|
543
|
+
const countryCodeForStep2 = countryCodeOverride || 'US';
|
|
509
544
|
const step2Body = this.reqBodyAuthStep2(authorizeCode, bizToken, appId, terminalId, countryCodeForStep2);
|
|
510
545
|
logger_1.logger.debug('Step 2: Logging in with authorization code...');
|
|
511
546
|
const [loginResponse, loginStatus] = await this.callApi('/user/api/accountManage/v1/loginByAuthorizeCode4Vesync', 'post', step2Body, step1Headers, manager);
|
|
@@ -524,7 +559,21 @@ class Helpers {
|
|
|
524
559
|
logger_1.logger.debug('Step 2 error, code:', loginResponse.code, 'checking if cross-region...');
|
|
525
560
|
// Handle cross-region error - for EU accounts, we need to switch regions entirely
|
|
526
561
|
if (constants_1.CROSS_REGION_ERROR_CODES.includes(loginResponse.code)) {
|
|
527
|
-
|
|
562
|
+
// Log helpful message about country code mismatch
|
|
563
|
+
logger_1.logger.warn('═══════════════════════════════════════════════════════════════');
|
|
564
|
+
logger_1.logger.warn('COUNTRY CODE MISMATCH DETECTED');
|
|
565
|
+
logger_1.logger.warn(`Your account expects a different country code than '${countryCodeForStep2}'`);
|
|
566
|
+
logger_1.logger.warn('This usually means:');
|
|
567
|
+
logger_1.logger.warn(' 1. You need to set the correct country code in your config');
|
|
568
|
+
logger_1.logger.warn(' 2. Your account was created in a different country/region');
|
|
569
|
+
logger_1.logger.warn('');
|
|
570
|
+
logger_1.logger.warn('To fix this:');
|
|
571
|
+
logger_1.logger.warn(' - In Homebridge UI: Select your correct country from the dropdown');
|
|
572
|
+
logger_1.logger.warn(' - In config.json: Add "countryCode": "YOUR_COUNTRY_CODE"');
|
|
573
|
+
logger_1.logger.warn('');
|
|
574
|
+
logger_1.logger.warn('Common country codes: US, CA, GB, DE, FR, AU, NZ, JP');
|
|
575
|
+
logger_1.logger.warn('═══════════════════════════════════════════════════════════════');
|
|
576
|
+
logger_1.logger.debug('Cross-region error detected in Step 2 - will try different endpoint');
|
|
528
577
|
// For cross-region errors, we need to retry the entire flow with a different region
|
|
529
578
|
// The authorization code from Step 1 is tied to the endpoint it was obtained from
|
|
530
579
|
setApiBaseUrl(originalUrl); // Restore original URL
|
package/dist/lib/vesync.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export declare class VeSync {
|
|
|
27
27
|
private _excludeConfig;
|
|
28
28
|
private _appId;
|
|
29
29
|
private _region;
|
|
30
|
+
private _countryCodeOverride;
|
|
30
31
|
username: string;
|
|
31
32
|
password: string;
|
|
32
33
|
token: string | null;
|
|
@@ -61,6 +62,7 @@ export declare class VeSync {
|
|
|
61
62
|
customLogger?: Logger;
|
|
62
63
|
excludeConfig?: ExcludeConfig;
|
|
63
64
|
region?: string;
|
|
65
|
+
countryCode?: string;
|
|
64
66
|
debugMode?: boolean;
|
|
65
67
|
}, redact?: boolean, apiUrl?: string, customLogger?: Logger, excludeConfig?: ExcludeConfig);
|
|
66
68
|
/**
|
package/dist/lib/vesync.js
CHANGED
|
@@ -141,6 +141,7 @@ class VeSync {
|
|
|
141
141
|
this._redact = options.redact !== undefined ? options.redact : true;
|
|
142
142
|
this._excludeConfig = options.excludeConfig || null;
|
|
143
143
|
this._region = options.region || 'US';
|
|
144
|
+
this._countryCodeOverride = options.countryCode || null;
|
|
144
145
|
customLogger = options.customLogger;
|
|
145
146
|
apiUrl = options.apiUrl;
|
|
146
147
|
}
|
|
@@ -150,6 +151,7 @@ class VeSync {
|
|
|
150
151
|
this._redact = redact;
|
|
151
152
|
this._excludeConfig = excludeConfig || null;
|
|
152
153
|
this._region = 'US';
|
|
154
|
+
this._countryCodeOverride = null;
|
|
153
155
|
}
|
|
154
156
|
this._energyUpdateInterval = DEFAULT_ENERGY_UPDATE_INTERVAL;
|
|
155
157
|
this._energyCheck = true;
|
|
@@ -195,6 +197,12 @@ class VeSync {
|
|
|
195
197
|
(0, helpers_1.setApiBaseUrl)(apiUrl);
|
|
196
198
|
}
|
|
197
199
|
else {
|
|
200
|
+
// If country code is provided, use it to determine the endpoint
|
|
201
|
+
if (this._countryCodeOverride) {
|
|
202
|
+
const endpointRegion = (0, helpers_1.getEndpointForCountryCode)(this._countryCodeOverride);
|
|
203
|
+
this._region = endpointRegion;
|
|
204
|
+
logger_1.logger.debug(`Country code ${this._countryCodeOverride} maps to ${endpointRegion} endpoint`);
|
|
205
|
+
}
|
|
198
206
|
// Set endpoint based on region
|
|
199
207
|
const regionEndpoints = {
|
|
200
208
|
'US': 'https://smartapi.vesync.com',
|
|
@@ -458,13 +466,15 @@ class VeSync {
|
|
|
458
466
|
appId: this._appId,
|
|
459
467
|
region: this._region
|
|
460
468
|
});
|
|
469
|
+
// Track which regions have been attempted to prevent infinite loops
|
|
470
|
+
const triedRegions = new Set();
|
|
461
471
|
for (let attempt = 0; attempt < retryAttempts; attempt++) {
|
|
462
472
|
try {
|
|
463
473
|
logger_1.logger.debug(`Authentication attempt ${attempt + 1} of ${retryAttempts}`);
|
|
464
474
|
// Skip email-based region detection for now since we'll handle cross-region errors
|
|
465
475
|
// The cross-region error handling below will automatically switch to the correct region
|
|
466
476
|
// Try new authentication flow first with detected region
|
|
467
|
-
let [success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, this._region);
|
|
477
|
+
let [success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, this._region, this._countryCodeOverride || undefined);
|
|
468
478
|
// Track if new flow succeeded
|
|
469
479
|
if (success) {
|
|
470
480
|
this.authFlowUsed = 'new';
|
|
@@ -477,8 +487,36 @@ class VeSync {
|
|
|
477
487
|
}
|
|
478
488
|
// Check if we need to try a different region
|
|
479
489
|
if (countryCode === 'cross_region' || countryCode === 'cross_region_retry') {
|
|
490
|
+
// Mark current region as tried
|
|
491
|
+
triedRegions.add(this._region);
|
|
480
492
|
logger_1.logger.debug('Cross-region error detected, trying opposite region...');
|
|
481
493
|
const alternateRegion = this._region === 'US' ? 'EU' : 'US';
|
|
494
|
+
// Check if we've already tried this region to prevent infinite loops
|
|
495
|
+
if (triedRegions.has(alternateRegion)) {
|
|
496
|
+
logger_1.logger.error('═══════════════════════════════════════════════════════════════');
|
|
497
|
+
logger_1.logger.error('AUTHENTICATION FAILED: COUNTRY CODE REQUIRED');
|
|
498
|
+
logger_1.logger.error('');
|
|
499
|
+
logger_1.logger.error('Both US and EU endpoints rejected your account.');
|
|
500
|
+
logger_1.logger.error('This means you MUST specify your country code.');
|
|
501
|
+
logger_1.logger.error('');
|
|
502
|
+
logger_1.logger.error('SOLUTION:');
|
|
503
|
+
logger_1.logger.error('1. In Homebridge UI:');
|
|
504
|
+
logger_1.logger.error(' - Go to plugin settings');
|
|
505
|
+
logger_1.logger.error(' - Select your country from the "Country Code" dropdown');
|
|
506
|
+
logger_1.logger.error('');
|
|
507
|
+
logger_1.logger.error('2. In config.json:');
|
|
508
|
+
logger_1.logger.error(' Add: "countryCode": "YOUR_COUNTRY_CODE"');
|
|
509
|
+
logger_1.logger.error('');
|
|
510
|
+
logger_1.logger.error('Common non-US/EU country codes:');
|
|
511
|
+
logger_1.logger.error(' 🇦🇺 Australia: "AU"');
|
|
512
|
+
logger_1.logger.error(' 🇳🇿 New Zealand: "NZ"');
|
|
513
|
+
logger_1.logger.error(' 🇯🇵 Japan: "JP"');
|
|
514
|
+
logger_1.logger.error(' 🇨🇦 Canada: "CA"');
|
|
515
|
+
logger_1.logger.error(' 🇸🇬 Singapore: "SG"');
|
|
516
|
+
logger_1.logger.error(' 🇲🇽 Mexico: "MX"');
|
|
517
|
+
logger_1.logger.error('═══════════════════════════════════════════════════════════════');
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
482
520
|
logger_1.logger.debug(`Switching from ${this._region} to ${alternateRegion} region`);
|
|
483
521
|
this._region = alternateRegion;
|
|
484
522
|
(0, helpers_1.setCurrentRegion)(alternateRegion);
|
|
@@ -492,11 +530,19 @@ class VeSync {
|
|
|
492
530
|
logger_1.logger.debug(`API endpoint set to ${regionEndpoints[alternateRegion]}`);
|
|
493
531
|
}
|
|
494
532
|
// Retry with the alternate region
|
|
495
|
-
[success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, alternateRegion);
|
|
533
|
+
[success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, alternateRegion, this._countryCodeOverride || undefined);
|
|
496
534
|
if (success) {
|
|
497
535
|
this.authFlowUsed = 'new';
|
|
498
536
|
logger_1.logger.debug(`Authentication successful with ${alternateRegion} region`);
|
|
499
537
|
}
|
|
538
|
+
else if (countryCode === 'cross_region' || countryCode === 'cross_region_retry') {
|
|
539
|
+
// Both regions failed with cross-region error
|
|
540
|
+
logger_1.logger.error('Authentication failed: Account rejected by both US and EU regions');
|
|
541
|
+
logger_1.logger.error('This may indicate your account was created in a region not yet supported');
|
|
542
|
+
logger_1.logger.error('Please contact VeSync support to determine your account region');
|
|
543
|
+
// Don't retry further attempts as we've exhausted both regions
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
500
546
|
}
|
|
501
547
|
// If new flow still fails, try legacy authentication as fallback
|
|
502
548
|
if (!success) {
|
package/package.json
CHANGED
package/src/lib/helpers.ts
CHANGED
|
@@ -84,6 +84,42 @@ export const AUTH_ERROR_CODES = {
|
|
|
84
84
|
CROSS_REGION_ALT: -11261022
|
|
85
85
|
};
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Get list of country codes that use the US endpoint
|
|
89
|
+
* These are tried when we get a cross-region error
|
|
90
|
+
*/
|
|
91
|
+
export function getUSEndpointCountryCodes(): string[] {
|
|
92
|
+
// Countries that use the US endpoint but might need their own country code
|
|
93
|
+
return ['US', 'AU', 'NZ', 'JP', 'CA', 'MX', 'SG'];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get the appropriate API endpoint based on country code
|
|
98
|
+
*/
|
|
99
|
+
export function getEndpointForCountryCode(countryCode: string): 'US' | 'EU' {
|
|
100
|
+
// European country codes that should use the EU endpoint
|
|
101
|
+
const euCountryCodes = [
|
|
102
|
+
// Western Europe
|
|
103
|
+
'GB', 'DE', 'FR', 'IT', 'ES', 'NL', 'BE', 'AT', 'CH', 'IE', 'PT', 'LU',
|
|
104
|
+
// Nordic countries
|
|
105
|
+
'SE', 'NO', 'DK', 'FI', 'IS',
|
|
106
|
+
// Eastern Europe
|
|
107
|
+
'PL', 'CZ', 'HU', 'RO', 'BG', 'HR', 'SI', 'SK', 'LT', 'LV', 'EE',
|
|
108
|
+
// Southern Europe
|
|
109
|
+
'GR', 'CY', 'MT',
|
|
110
|
+
// Other European
|
|
111
|
+
'AL', 'BA', 'RS', 'ME', 'MK', 'MD', 'UA', 'BY'
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
// Check if country code is in EU list
|
|
115
|
+
if (euCountryCodes.includes(countryCode)) {
|
|
116
|
+
return 'EU';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Everything else uses US endpoint (including AU, NZ, JP, CA, MX, etc.)
|
|
120
|
+
return 'US';
|
|
121
|
+
}
|
|
122
|
+
|
|
87
123
|
/**
|
|
88
124
|
* Detect user's home region from email domain or country hints
|
|
89
125
|
*/
|
|
@@ -557,7 +593,7 @@ export class Helpers {
|
|
|
557
593
|
/**
|
|
558
594
|
* Perform new two-step authentication flow
|
|
559
595
|
*/
|
|
560
|
-
static async authNewFlow(manager: VeSync, appId: string, region: string = 'US'): Promise<[boolean, string | null, string | null, string | null]> {
|
|
596
|
+
static async authNewFlow(manager: VeSync, appId: string, region: string = 'US', countryCodeOverride?: string): Promise<[boolean, string | null, string | null, string | null]> {
|
|
561
597
|
try {
|
|
562
598
|
// Generate terminal ID to be used in both steps
|
|
563
599
|
const terminalId = generateTerminalId();
|
|
@@ -624,8 +660,9 @@ export class Helpers {
|
|
|
624
660
|
logger.debug('Step 1 successful, got authorization code');
|
|
625
661
|
|
|
626
662
|
// Step 2: Login with authorization code
|
|
627
|
-
// Use the
|
|
628
|
-
|
|
663
|
+
// Use the override if provided, otherwise default to 'US'
|
|
664
|
+
// Users should specify their actual country code in the configuration
|
|
665
|
+
const countryCodeForStep2 = countryCodeOverride || 'US';
|
|
629
666
|
const step2Body = this.reqBodyAuthStep2(authorizeCode, bizToken, appId, terminalId, countryCodeForStep2);
|
|
630
667
|
|
|
631
668
|
logger.debug('Step 2: Logging in with authorization code...');
|
|
@@ -655,7 +692,22 @@ export class Helpers {
|
|
|
655
692
|
logger.debug('Step 2 error, code:', loginResponse.code, 'checking if cross-region...');
|
|
656
693
|
// Handle cross-region error - for EU accounts, we need to switch regions entirely
|
|
657
694
|
if (CROSS_REGION_ERROR_CODES.includes(loginResponse.code)) {
|
|
658
|
-
|
|
695
|
+
// Log helpful message about country code mismatch
|
|
696
|
+
logger.warn('═══════════════════════════════════════════════════════════════');
|
|
697
|
+
logger.warn('COUNTRY CODE MISMATCH DETECTED');
|
|
698
|
+
logger.warn(`Your account expects a different country code than '${countryCodeForStep2}'`);
|
|
699
|
+
logger.warn('This usually means:');
|
|
700
|
+
logger.warn(' 1. You need to set the correct country code in your config');
|
|
701
|
+
logger.warn(' 2. Your account was created in a different country/region');
|
|
702
|
+
logger.warn('');
|
|
703
|
+
logger.warn('To fix this:');
|
|
704
|
+
logger.warn(' - In Homebridge UI: Select your correct country from the dropdown');
|
|
705
|
+
logger.warn(' - In config.json: Add "countryCode": "YOUR_COUNTRY_CODE"');
|
|
706
|
+
logger.warn('');
|
|
707
|
+
logger.warn('Common country codes: US, CA, GB, DE, FR, AU, NZ, JP');
|
|
708
|
+
logger.warn('═══════════════════════════════════════════════════════════════');
|
|
709
|
+
|
|
710
|
+
logger.debug('Cross-region error detected in Step 2 - will try different endpoint');
|
|
659
711
|
// For cross-region errors, we need to retry the entire flow with a different region
|
|
660
712
|
// The authorization code from Step 1 is tied to the endpoint it was obtained from
|
|
661
713
|
setApiBaseUrl(originalUrl); // Restore original URL
|
package/src/lib/vesync.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* VeSync API Device Library
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { Helpers, API_RATE_LIMIT, DEFAULT_TZ, setApiBaseUrl, getApiBaseUrl, generateAppId, getRegionFromCountryCode, setCurrentRegion, getCurrentRegion, REGION_ENDPOINTS, getCountryCodeFromRegion } from './helpers';
|
|
5
|
+
import { Helpers, API_RATE_LIMIT, DEFAULT_TZ, setApiBaseUrl, getApiBaseUrl, generateAppId, getRegionFromCountryCode, setCurrentRegion, getCurrentRegion, REGION_ENDPOINTS, getCountryCodeFromRegion, getEndpointForCountryCode } from './helpers';
|
|
6
6
|
import { VeSyncBaseDevice } from './vesyncBaseDevice';
|
|
7
7
|
import { fanModules } from './vesyncFanImpl';
|
|
8
8
|
import { outletModules } from './vesyncOutletImpl';
|
|
@@ -161,6 +161,7 @@ export class VeSync {
|
|
|
161
161
|
private _excludeConfig: ExcludeConfig | null;
|
|
162
162
|
private _appId: string;
|
|
163
163
|
private _region: string;
|
|
164
|
+
private _countryCodeOverride: string | null;
|
|
164
165
|
|
|
165
166
|
username: string;
|
|
166
167
|
password: string;
|
|
@@ -202,6 +203,7 @@ export class VeSync {
|
|
|
202
203
|
customLogger?: Logger;
|
|
203
204
|
excludeConfig?: ExcludeConfig;
|
|
204
205
|
region?: string;
|
|
206
|
+
countryCode?: string; // Override country code for Step 2 authentication
|
|
205
207
|
debugMode?: boolean; // Alias for debug
|
|
206
208
|
} = false,
|
|
207
209
|
redact = true,
|
|
@@ -218,6 +220,7 @@ export class VeSync {
|
|
|
218
220
|
this._redact = options.redact !== undefined ? options.redact : true;
|
|
219
221
|
this._excludeConfig = options.excludeConfig || null;
|
|
220
222
|
this._region = options.region || 'US';
|
|
223
|
+
this._countryCodeOverride = options.countryCode || null;
|
|
221
224
|
customLogger = options.customLogger;
|
|
222
225
|
apiUrl = options.apiUrl;
|
|
223
226
|
} else {
|
|
@@ -226,6 +229,7 @@ export class VeSync {
|
|
|
226
229
|
this._redact = redact;
|
|
227
230
|
this._excludeConfig = excludeConfig || null;
|
|
228
231
|
this._region = 'US';
|
|
232
|
+
this._countryCodeOverride = null;
|
|
229
233
|
}
|
|
230
234
|
|
|
231
235
|
this._energyUpdateInterval = DEFAULT_ENERGY_UPDATE_INTERVAL;
|
|
@@ -274,6 +278,13 @@ export class VeSync {
|
|
|
274
278
|
if (apiUrl) {
|
|
275
279
|
setApiBaseUrl(apiUrl);
|
|
276
280
|
} else {
|
|
281
|
+
// If country code is provided, use it to determine the endpoint
|
|
282
|
+
if (this._countryCodeOverride) {
|
|
283
|
+
const endpointRegion = getEndpointForCountryCode(this._countryCodeOverride);
|
|
284
|
+
this._region = endpointRegion;
|
|
285
|
+
logger.debug(`Country code ${this._countryCodeOverride} maps to ${endpointRegion} endpoint`);
|
|
286
|
+
}
|
|
287
|
+
|
|
277
288
|
// Set endpoint based on region
|
|
278
289
|
const regionEndpoints: Record<string, string> = {
|
|
279
290
|
'US': 'https://smartapi.vesync.com',
|
|
@@ -571,6 +582,9 @@ export class VeSync {
|
|
|
571
582
|
region: this._region
|
|
572
583
|
});
|
|
573
584
|
|
|
585
|
+
// Track which regions have been attempted to prevent infinite loops
|
|
586
|
+
const triedRegions = new Set<string>();
|
|
587
|
+
|
|
574
588
|
for (let attempt = 0; attempt < retryAttempts; attempt++) {
|
|
575
589
|
try {
|
|
576
590
|
logger.debug(`Authentication attempt ${attempt + 1} of ${retryAttempts}`);
|
|
@@ -579,7 +593,7 @@ export class VeSync {
|
|
|
579
593
|
// The cross-region error handling below will automatically switch to the correct region
|
|
580
594
|
|
|
581
595
|
// Try new authentication flow first with detected region
|
|
582
|
-
let [success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, this._region);
|
|
596
|
+
let [success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, this._region, this._countryCodeOverride || undefined);
|
|
583
597
|
|
|
584
598
|
// Track if new flow succeeded
|
|
585
599
|
if (success) {
|
|
@@ -595,8 +609,39 @@ export class VeSync {
|
|
|
595
609
|
|
|
596
610
|
// Check if we need to try a different region
|
|
597
611
|
if (countryCode === 'cross_region' || countryCode === 'cross_region_retry') {
|
|
612
|
+
// Mark current region as tried
|
|
613
|
+
triedRegions.add(this._region);
|
|
614
|
+
|
|
598
615
|
logger.debug('Cross-region error detected, trying opposite region...');
|
|
599
616
|
const alternateRegion = this._region === 'US' ? 'EU' : 'US';
|
|
617
|
+
|
|
618
|
+
// Check if we've already tried this region to prevent infinite loops
|
|
619
|
+
if (triedRegions.has(alternateRegion)) {
|
|
620
|
+
logger.error('═══════════════════════════════════════════════════════════════');
|
|
621
|
+
logger.error('AUTHENTICATION FAILED: COUNTRY CODE REQUIRED');
|
|
622
|
+
logger.error('');
|
|
623
|
+
logger.error('Both US and EU endpoints rejected your account.');
|
|
624
|
+
logger.error('This means you MUST specify your country code.');
|
|
625
|
+
logger.error('');
|
|
626
|
+
logger.error('SOLUTION:');
|
|
627
|
+
logger.error('1. In Homebridge UI:');
|
|
628
|
+
logger.error(' - Go to plugin settings');
|
|
629
|
+
logger.error(' - Select your country from the "Country Code" dropdown');
|
|
630
|
+
logger.error('');
|
|
631
|
+
logger.error('2. In config.json:');
|
|
632
|
+
logger.error(' Add: "countryCode": "YOUR_COUNTRY_CODE"');
|
|
633
|
+
logger.error('');
|
|
634
|
+
logger.error('Common non-US/EU country codes:');
|
|
635
|
+
logger.error(' 🇦🇺 Australia: "AU"');
|
|
636
|
+
logger.error(' 🇳🇿 New Zealand: "NZ"');
|
|
637
|
+
logger.error(' 🇯🇵 Japan: "JP"');
|
|
638
|
+
logger.error(' 🇨🇦 Canada: "CA"');
|
|
639
|
+
logger.error(' 🇸🇬 Singapore: "SG"');
|
|
640
|
+
logger.error(' 🇲🇽 Mexico: "MX"');
|
|
641
|
+
logger.error('═══════════════════════════════════════════════════════════════');
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
|
|
600
645
|
logger.debug(`Switching from ${this._region} to ${alternateRegion} region`);
|
|
601
646
|
|
|
602
647
|
this._region = alternateRegion;
|
|
@@ -613,10 +658,17 @@ export class VeSync {
|
|
|
613
658
|
}
|
|
614
659
|
|
|
615
660
|
// Retry with the alternate region
|
|
616
|
-
[success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, alternateRegion);
|
|
661
|
+
[success, token, accountId, countryCode] = await Helpers.authNewFlow(this, this._appId, alternateRegion, this._countryCodeOverride || undefined);
|
|
617
662
|
if (success) {
|
|
618
663
|
this.authFlowUsed = 'new';
|
|
619
664
|
logger.debug(`Authentication successful with ${alternateRegion} region`);
|
|
665
|
+
} else if (countryCode === 'cross_region' || countryCode === 'cross_region_retry') {
|
|
666
|
+
// Both regions failed with cross-region error
|
|
667
|
+
logger.error('Authentication failed: Account rejected by both US and EU regions');
|
|
668
|
+
logger.error('This may indicate your account was created in a region not yet supported');
|
|
669
|
+
logger.error('Please contact VeSync support to determine your account region');
|
|
670
|
+
// Don't retry further attempts as we've exhausted both regions
|
|
671
|
+
return false;
|
|
620
672
|
}
|
|
621
673
|
}
|
|
622
674
|
|