tsvesync 1.0.96 → 1.0.98

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 CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  A TypeScript library for interacting with VeSync smart home devices. This library provides a modern, type-safe interface for controlling VeSync devices including air purifiers, outlets, and switches.
4
4
 
5
+ ## 🆕 Recent Updates
6
+
7
+ - **Enhanced Authentication**: Now supports the new VeSync authentication flow (pyvesync PR #340) with automatic fallback to legacy authentication
8
+ - **Regional API Support**: Automatic detection and routing for US (`smartapi.vesync.com`) and EU (`smartapi.vesync.eu`) endpoints
9
+ - **Improved Error Handling**: Better recovery from API changes and authentication errors
10
+ - **Cross-Region Support**: Handles cross-region authentication errors automatically
11
+
5
12
  ## Features
6
13
 
7
14
  - Full TypeScript support with type definitions
@@ -4,16 +4,32 @@
4
4
  import { VeSync } from './vesync';
5
5
  export declare function getApiBaseUrl(): string;
6
6
  export declare function setApiBaseUrl(url: string): void;
7
+ export declare function getCurrentRegion(): string;
8
+ export declare function setCurrentRegion(region: string): void;
9
+ export declare function getRegionFromCountryCode(countryCode: string): string;
7
10
  export declare const API_RATE_LIMIT = 30;
8
11
  export declare const API_TIMEOUT = 15000;
9
- export declare const APP_VERSION = "2.8.6";
12
+ export declare const APP_VERSION = "5.6.60";
10
13
  export declare const PHONE_BRAND = "SM N9005";
11
14
  export declare const PHONE_OS = "Android";
15
+ export declare const CLIENT_INFO = "SM N9005";
12
16
  export declare const USER_TYPE = "1";
13
17
  export declare const DEFAULT_TZ = "America/New_York";
14
18
  export declare const DEFAULT_REGION = "US";
15
19
  export declare const MOBILE_ID = "1234567890123456";
16
20
  export declare const BYPASS_HEADER_UA = "okhttp/3.12.1";
21
+ export declare const CLIENT_VERSION = "VeSync 5.6.60";
22
+ export declare const REGION_ENDPOINTS: {
23
+ US: string;
24
+ CA: string;
25
+ MX: string;
26
+ JP: string;
27
+ EU: string;
28
+ };
29
+ export declare const CROSS_REGION_ERROR_CODE = -11260022;
30
+ export declare const APP_VERSION_TOO_LOW_ERROR_CODE = -11012022;
31
+ export declare function generateAppId(): string;
32
+ export declare function generateTerminalId(): string;
17
33
  export interface RequestBody {
18
34
  acceptLanguage?: string;
19
35
  accountID?: string;
@@ -33,8 +49,37 @@ export interface RequestBody {
33
49
  userType?: string;
34
50
  uuid?: string;
35
51
  status?: string;
52
+ authorizeCode?: string;
53
+ bizToken?: string;
54
+ regionChange?: string;
55
+ userCountryCode?: string;
56
+ authProtocolType?: string;
57
+ clientType?: string;
58
+ sourceAppID?: string;
59
+ appID?: string;
60
+ clientVersion?: string;
36
61
  [key: string]: any;
37
62
  }
63
+ export interface AuthResponse {
64
+ code: number;
65
+ msg: string;
66
+ result?: {
67
+ authorizeCode?: string;
68
+ bizToken?: string;
69
+ userCountryCode?: string;
70
+ [key: string]: any;
71
+ };
72
+ }
73
+ export interface LoginResponse {
74
+ code: number;
75
+ msg: string;
76
+ result?: {
77
+ token?: string;
78
+ accountID?: string;
79
+ countryCode?: string;
80
+ [key: string]: any;
81
+ };
82
+ }
38
83
  export declare class Helpers {
39
84
  static shouldRedact: boolean;
40
85
  /**
@@ -49,6 +94,10 @@ export declare class Helpers {
49
94
  * Build header for api requests on 'bypass' endpoint
50
95
  */
51
96
  static reqHeaderBypass(): Record<string, string>;
97
+ /**
98
+ * Build header for new authentication endpoints
99
+ */
100
+ static reqHeaderAuth(): Record<string, string>;
52
101
  /**
53
102
  * Return universal keys for body of api requests
54
103
  */
@@ -61,6 +110,14 @@ export declare class Helpers {
61
110
  * Detail keys for api requests
62
111
  */
63
112
  static reqBodyDetails(): Record<string, string>;
113
+ /**
114
+ * Build request body for initial authentication step
115
+ */
116
+ static reqBodyAuthStep1(manager: VeSync, appId: string, terminalId: string): Record<string, any>;
117
+ /**
118
+ * Build request body for second authentication step (login with authorize code)
119
+ */
120
+ static reqBodyAuthStep2(authorizeCode: string, bizToken: string | null, appId: string, terminalId: string, userCountryCode?: string): Record<string, any>;
64
121
  /**
65
122
  * Builder for body of api requests
66
123
  */
@@ -85,4 +142,12 @@ export declare class Helpers {
85
142
  * Calculate MD5 hash
86
143
  */
87
144
  static md5(text: string): string;
145
+ /**
146
+ * Perform new two-step authentication flow
147
+ */
148
+ static authNewFlow(manager: VeSync, appId: string, region?: string): Promise<[boolean, string | null, string | null, string | null]>;
149
+ /**
150
+ * Perform legacy authentication (fallback)
151
+ */
152
+ static authLegacyFlow(manager: VeSync): Promise<[boolean, string | null, string | null, string | null]>;
88
153
  }
@@ -6,30 +6,90 @@ 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 = void 0;
9
+ exports.Helpers = exports.APP_VERSION_TOO_LOW_ERROR_CODE = exports.CROSS_REGION_ERROR_CODE = exports.REGION_ENDPOINTS = exports.CLIENT_VERSION = exports.BYPASS_HEADER_UA = exports.MOBILE_ID = exports.DEFAULT_REGION = exports.DEFAULT_TZ = exports.USER_TYPE = exports.CLIENT_INFO = 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.getCurrentRegion = getCurrentRegion;
13
+ exports.setCurrentRegion = setCurrentRegion;
14
+ exports.getRegionFromCountryCode = getRegionFromCountryCode;
15
+ exports.generateAppId = generateAppId;
16
+ exports.generateTerminalId = generateTerminalId;
12
17
  const axios_1 = __importDefault(require("axios"));
13
18
  const crypto_1 = __importDefault(require("crypto"));
14
19
  const logger_1 = require("./logger");
15
- // API configuration - Always use US endpoint
20
+ // API configuration - Support regional endpoints
16
21
  let _apiBaseUrl = 'https://smartapi.vesync.com';
22
+ let _currentRegion = 'US';
17
23
  function getApiBaseUrl() {
18
24
  return _apiBaseUrl;
19
25
  }
20
26
  function setApiBaseUrl(url) {
21
27
  _apiBaseUrl = url;
22
28
  }
29
+ function getCurrentRegion() {
30
+ return _currentRegion;
31
+ }
32
+ function setCurrentRegion(region) {
33
+ _currentRegion = region;
34
+ if (region in exports.REGION_ENDPOINTS) {
35
+ _apiBaseUrl = exports.REGION_ENDPOINTS[region];
36
+ }
37
+ }
38
+ // Determine region from country code
39
+ function getRegionFromCountryCode(countryCode) {
40
+ const euCountries = [
41
+ 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR',
42
+ 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL',
43
+ 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'GB', 'IS', 'LI',
44
+ 'NO', 'CH'
45
+ ];
46
+ if (euCountries.includes(countryCode)) {
47
+ return 'EU';
48
+ }
49
+ // Default to US for other countries
50
+ return 'US';
51
+ }
23
52
  exports.API_RATE_LIMIT = 30;
24
53
  exports.API_TIMEOUT = 15000;
25
- exports.APP_VERSION = '2.8.6';
54
+ exports.APP_VERSION = '5.6.60';
26
55
  exports.PHONE_BRAND = 'SM N9005';
27
56
  exports.PHONE_OS = 'Android';
57
+ exports.CLIENT_INFO = 'SM N9005';
28
58
  exports.USER_TYPE = '1';
29
59
  exports.DEFAULT_TZ = 'America/New_York';
30
60
  exports.DEFAULT_REGION = 'US';
31
61
  exports.MOBILE_ID = '1234567890123456';
32
62
  exports.BYPASS_HEADER_UA = 'okhttp/3.12.1';
63
+ exports.CLIENT_VERSION = 'VeSync 5.6.60';
64
+ // Regional API endpoints
65
+ exports.REGION_ENDPOINTS = {
66
+ 'US': 'https://smartapi.vesync.com',
67
+ 'CA': 'https://smartapi.vesync.com',
68
+ 'MX': 'https://smartapi.vesync.com',
69
+ 'JP': 'https://smartapi.vesync.com',
70
+ 'EU': 'https://smartapi.vesync.eu'
71
+ };
72
+ // Cross-region error code
73
+ exports.CROSS_REGION_ERROR_CODE = -11260022;
74
+ exports.APP_VERSION_TOO_LOW_ERROR_CODE = -11012022;
75
+ // Generate unique APP_ID for each session
76
+ function generateAppId() {
77
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
78
+ let result = '';
79
+ for (let i = 0; i < 8; i++) {
80
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
81
+ }
82
+ return result;
83
+ }
84
+ // Generate unique terminal ID for authentication
85
+ function generateTerminalId() {
86
+ const chars = 'abcdef0123456789';
87
+ let result = '';
88
+ for (let i = 0; i < 16; i++) {
89
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
90
+ }
91
+ return result;
92
+ }
33
93
  class Helpers {
34
94
  /**
35
95
  * Calculate MD5 hash
@@ -64,6 +124,16 @@ class Helpers {
64
124
  'User-Agent': exports.BYPASS_HEADER_UA
65
125
  };
66
126
  }
127
+ /**
128
+ * Build header for new authentication endpoints
129
+ */
130
+ static reqHeaderAuth() {
131
+ return {
132
+ 'Content-Type': 'application/json; charset=UTF-8',
133
+ 'User-Agent': exports.BYPASS_HEADER_UA,
134
+ 'accept-language': 'en'
135
+ };
136
+ }
67
137
  /**
68
138
  * Return universal keys for body of api requests
69
139
  */
@@ -96,6 +166,56 @@ class Helpers {
96
166
  'traceId': Date.now().toString()
97
167
  };
98
168
  }
169
+ /**
170
+ * Build request body for initial authentication step
171
+ */
172
+ static reqBodyAuthStep1(manager, appId, terminalId) {
173
+ return {
174
+ 'email': manager.username,
175
+ 'method': 'authByPWDOrOTM',
176
+ 'password': this.hashPassword(manager.password),
177
+ 'acceptLanguage': 'en',
178
+ 'accountID': '',
179
+ 'authProtocolType': 'generic',
180
+ 'clientInfo': exports.CLIENT_INFO,
181
+ 'clientType': 'vesyncApp',
182
+ 'clientVersion': exports.CLIENT_VERSION,
183
+ 'debugMode': false,
184
+ 'osInfo': exports.PHONE_OS,
185
+ 'terminalId': terminalId,
186
+ 'timeZone': manager.timeZone || exports.DEFAULT_TZ,
187
+ 'token': '',
188
+ 'userCountryCode': 'US',
189
+ 'appID': appId,
190
+ 'sourceAppID': appId,
191
+ 'traceId': `APP${appId}${Math.floor(Date.now() / 1000)}`
192
+ };
193
+ }
194
+ /**
195
+ * Build request body for second authentication step (login with authorize code)
196
+ */
197
+ static reqBodyAuthStep2(authorizeCode, bizToken, appId, terminalId, userCountryCode) {
198
+ const body = {
199
+ 'method': 'loginByAuthorizeCode4Vesync',
200
+ 'authorizeCode': authorizeCode,
201
+ 'acceptLanguage': 'en',
202
+ 'clientInfo': exports.CLIENT_INFO,
203
+ 'clientType': 'vesyncApp',
204
+ 'clientVersion': exports.CLIENT_VERSION,
205
+ 'debugMode': false,
206
+ 'emailSubscriptions': false,
207
+ 'osInfo': exports.PHONE_OS,
208
+ 'terminalId': terminalId,
209
+ 'timeZone': exports.DEFAULT_TZ,
210
+ 'userCountryCode': userCountryCode || 'US',
211
+ 'traceId': `APP${appId}${Math.floor(Date.now() / 1000)}`
212
+ };
213
+ // Only include bizToken if it's not null
214
+ if (bizToken) {
215
+ body.bizToken = bizToken;
216
+ }
217
+ return body;
218
+ }
99
219
  /**
100
220
  * Builder for body of api requests
101
221
  */
@@ -270,6 +390,108 @@ class Helpers {
270
390
  static md5(text) {
271
391
  return crypto_1.default.createHash('md5').update(text).digest('hex');
272
392
  }
393
+ /**
394
+ * Perform new two-step authentication flow
395
+ */
396
+ static async authNewFlow(manager, appId, region = 'US') {
397
+ try {
398
+ // Generate terminal ID to be used in both steps
399
+ const terminalId = generateTerminalId();
400
+ // Step 1: Get authorization code
401
+ const step1Body = this.reqBodyAuthStep1(manager, appId, terminalId);
402
+ const step1Headers = this.reqHeaderAuth();
403
+ // Set the correct regional endpoint
404
+ const originalUrl = getApiBaseUrl();
405
+ if (region in exports.REGION_ENDPOINTS) {
406
+ setApiBaseUrl(exports.REGION_ENDPOINTS[region]);
407
+ }
408
+ logger_1.logger.debug('Step 1: Getting authorization code...', {
409
+ region,
410
+ endpoint: getApiBaseUrl(),
411
+ body: { ...step1Body, password: '[REDACTED]' }
412
+ });
413
+ const [authResponse, authStatus] = await this.callApi('/globalPlatform/api/accountAuth/v1/authByPWDOrOTM', 'post', step1Body, step1Headers, manager);
414
+ if (!authResponse || authStatus !== 200) {
415
+ logger_1.logger.error('Step 1 failed:', authResponse);
416
+ setApiBaseUrl(originalUrl); // Restore original URL
417
+ return [false, null, null, null];
418
+ }
419
+ if (authResponse.code !== 0) {
420
+ logger_1.logger.error('Step 1 error code:', authResponse.code, 'message:', authResponse.msg);
421
+ // Handle cross-region error
422
+ if (authResponse.code === exports.CROSS_REGION_ERROR_CODE) {
423
+ logger_1.logger.debug('Cross-region error detected, will try different region');
424
+ setApiBaseUrl(originalUrl); // Restore original URL
425
+ return [false, null, null, 'cross_region'];
426
+ }
427
+ setApiBaseUrl(originalUrl); // Restore original URL
428
+ return [false, null, null, null];
429
+ }
430
+ const { authorizeCode, bizToken, userCountryCode } = authResponse.result || {};
431
+ if (!authorizeCode) {
432
+ logger_1.logger.error('Missing authorization code in step 1 response:', authResponse.result);
433
+ setApiBaseUrl(originalUrl); // Restore original URL
434
+ return [false, null, null, null];
435
+ }
436
+ logger_1.logger.debug('Step 1 successful, got authorization code');
437
+ // Step 2: Login with authorization code
438
+ const step2Body = this.reqBodyAuthStep2(authorizeCode, bizToken, appId, terminalId, userCountryCode);
439
+ logger_1.logger.debug('Step 2: Logging in with authorization code...');
440
+ const [loginResponse, loginStatus] = await this.callApi('/user/api/accountManage/v1/loginByAuthorizeCode4Vesync', 'post', step2Body, step1Headers, manager);
441
+ setApiBaseUrl(originalUrl); // Restore original URL
442
+ if (!loginResponse || loginStatus !== 200) {
443
+ logger_1.logger.error('Step 2 failed:', loginResponse);
444
+ return [false, null, null, null];
445
+ }
446
+ if (loginResponse.code !== 0) {
447
+ logger_1.logger.error('Step 2 error code:', loginResponse.code, 'message:', loginResponse.msg);
448
+ return [false, null, null, null];
449
+ }
450
+ const { token, accountID, countryCode } = loginResponse.result || {};
451
+ if (!token || !accountID) {
452
+ logger_1.logger.error('Missing required fields in step 2 response:', loginResponse.result);
453
+ return [false, null, null, null];
454
+ }
455
+ logger_1.logger.debug('Step 2 successful, got token and accountID');
456
+ return [true, token, accountID, countryCode || userCountryCode];
457
+ }
458
+ catch (error) {
459
+ logger_1.logger.error('New authentication flow error:', error);
460
+ return [false, null, null, null];
461
+ }
462
+ }
463
+ /**
464
+ * Perform legacy authentication (fallback)
465
+ */
466
+ static async authLegacyFlow(manager) {
467
+ try {
468
+ const body = this.reqBody(manager, 'login');
469
+ logger_1.logger.debug('Legacy login attempt...', {
470
+ endpoint: getApiBaseUrl(),
471
+ body: { ...body, password: '[REDACTED]' }
472
+ });
473
+ const [response, status] = await this.callApi('/cloud/v1/user/login', 'post', body, {}, manager);
474
+ if (!response || status !== 200) {
475
+ logger_1.logger.error('Legacy login failed:', response);
476
+ return [false, null, null, null];
477
+ }
478
+ if (response.code && response.code !== 0) {
479
+ logger_1.logger.error('Legacy login error code:', response.code, 'message:', response.msg);
480
+ return [false, null, null, null];
481
+ }
482
+ const { token, accountID, countryCode } = response.result || {};
483
+ if (!token || !accountID) {
484
+ logger_1.logger.error('Missing required fields in legacy login response:', response.result);
485
+ return [false, null, null, null];
486
+ }
487
+ logger_1.logger.debug('Legacy login successful');
488
+ return [true, token, accountID, countryCode];
489
+ }
490
+ catch (error) {
491
+ logger_1.logger.error('Legacy authentication flow error:', error);
492
+ return [false, null, null, null];
493
+ }
494
+ }
273
495
  }
274
496
  exports.Helpers = Helpers;
275
497
  Helpers.shouldRedact = true;
@@ -25,6 +25,8 @@ export declare class VeSync {
25
25
  private _lastUpdateTs;
26
26
  private _inProcess;
27
27
  private _excludeConfig;
28
+ private _appId;
29
+ private _region;
28
30
  username: string;
29
31
  password: string;
30
32
  token: string | null;
@@ -66,6 +68,18 @@ export declare class VeSync {
66
68
  */
67
69
  get energyUpdateInterval(): number;
68
70
  set energyUpdateInterval(interval: number);
71
+ /**
72
+ * Get current App ID
73
+ */
74
+ get appId(): string;
75
+ /**
76
+ * Get current region
77
+ */
78
+ get region(): string;
79
+ /**
80
+ * Set region and update API endpoint
81
+ */
82
+ set region(region: string);
69
83
  /**
70
84
  * Test if device should be removed
71
85
  */
@@ -91,7 +105,7 @@ export declare class VeSync {
91
105
  */
92
106
  getDevices(): Promise<boolean>;
93
107
  /**
94
- * Login to VeSync server
108
+ * Login to VeSync server with new authentication flow and backwards compatibility
95
109
  */
96
110
  login(retryAttempts?: number, initialDelayMs?: number): Promise<boolean>;
97
111
  /**
@@ -139,6 +139,8 @@ class VeSync {
139
139
  this._lastUpdateTs = null;
140
140
  this._inProcess = false;
141
141
  this._excludeConfig = excludeConfig || null;
142
+ this._appId = (0, helpers_1.generateAppId)();
143
+ this._region = 'US';
142
144
  this.username = username;
143
145
  this.password = password;
144
146
  this.token = null;
@@ -178,8 +180,9 @@ class VeSync {
178
180
  (0, helpers_1.setApiBaseUrl)(apiUrl);
179
181
  }
180
182
  else {
181
- // Always use US endpoint
183
+ // Start with US endpoint as default
182
184
  (0, helpers_1.setApiBaseUrl)('https://smartapi.vesync.com');
185
+ (0, helpers_1.setCurrentRegion)('US');
183
186
  }
184
187
  // Set custom logger if provided
185
188
  if (customLogger) {
@@ -222,6 +225,27 @@ class VeSync {
222
225
  this._energyUpdateInterval = interval;
223
226
  }
224
227
  }
228
+ /**
229
+ * Get current App ID
230
+ */
231
+ get appId() {
232
+ return this._appId;
233
+ }
234
+ /**
235
+ * Get current region
236
+ */
237
+ get region() {
238
+ return this._region;
239
+ }
240
+ /**
241
+ * Set region and update API endpoint
242
+ */
243
+ set region(region) {
244
+ if (region in helpers_1.REGION_ENDPOINTS) {
245
+ this._region = region;
246
+ (0, helpers_1.setCurrentRegion)(region);
247
+ }
248
+ }
225
249
  /**
226
250
  * Test if device should be removed
227
251
  */
@@ -408,62 +432,71 @@ class VeSync {
408
432
  return success;
409
433
  }
410
434
  /**
411
- * Login to VeSync server
435
+ * Login to VeSync server with new authentication flow and backwards compatibility
412
436
  */
413
437
  async login(retryAttempts = 3, initialDelayMs = 1000) {
414
- var _a;
415
- const body = helpers_1.Helpers.reqBody(this, 'login');
438
+ logger_1.logger.debug('Starting VeSync authentication...', {
439
+ username: this.username,
440
+ appId: this._appId,
441
+ region: this._region
442
+ });
416
443
  for (let attempt = 0; attempt < retryAttempts; attempt++) {
417
444
  try {
418
- logger_1.logger.debug('Login attempt', {
419
- attempt: attempt + 1,
420
- apiUrl: (0, helpers_1.getApiBaseUrl)(),
421
- timeZone: this.timeZone,
422
- appVersion: body.appVersion
423
- });
424
- const [response, status] = await helpers_1.Helpers.callApi('/cloud/v1/user/login', 'post', body, {}, this);
425
- logger_1.logger.debug('Login response:', { status, response });
426
- // Handle specific error codes
427
- if (response && response.code) {
428
- switch (response.code) {
429
- case -11012022:
430
- logger_1.logger.error('App version too low error. Current version:', body.appVersion);
431
- logger_1.logger.error('This typically indicates an API version compatibility issue.');
432
- break;
433
- case -11003:
434
- logger_1.logger.error('Authentication failed - check credentials');
435
- break;
436
- case -11001:
437
- logger_1.logger.error('Invalid request format');
438
- break;
439
- default:
440
- logger_1.logger.error('API error code:', response.code, 'message:', response.msg);
445
+ logger_1.logger.debug(`Authentication attempt ${attempt + 1} of ${retryAttempts}`);
446
+ // Try new authentication flow first
447
+ let [success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, this._region);
448
+ if (!success) {
449
+ // Check if we need to try a different region
450
+ if (countryCode === 'cross_region') {
451
+ logger_1.logger.debug('Cross-region error detected, trying EU region...');
452
+ this._region = 'EU';
453
+ (0, helpers_1.setCurrentRegion)('EU');
454
+ [success, token, accountId, countryCode] = await helpers_1.Helpers.authNewFlow(this, this._appId, 'EU');
455
+ }
456
+ // If new flow still fails, try legacy authentication as fallback
457
+ if (!success) {
458
+ logger_1.logger.debug('New authentication flow failed, trying legacy flow...');
459
+ [success, token, accountId, countryCode] = await helpers_1.Helpers.authLegacyFlow(this);
441
460
  }
442
461
  }
443
- if ((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.token) {
444
- this.token = response.result.token;
445
- this.accountId = response.result.accountID;
446
- this.countryCode = response.result.countryCode;
462
+ if (success && token && accountId) {
463
+ this.token = token;
464
+ this.accountId = accountId;
465
+ this.countryCode = countryCode;
447
466
  this.enabled = true;
448
- logger_1.logger.debug('Login successful for region:', this.countryCode);
467
+ // Set region based on country code if we got one
468
+ if (countryCode) {
469
+ const detectedRegion = (0, helpers_1.getRegionFromCountryCode)(countryCode);
470
+ if (detectedRegion !== this._region) {
471
+ logger_1.logger.debug(`Updating region from ${this._region} to ${detectedRegion} based on country code: ${countryCode}`);
472
+ this._region = detectedRegion;
473
+ (0, helpers_1.setCurrentRegion)(detectedRegion);
474
+ }
475
+ }
476
+ logger_1.logger.debug('Authentication successful!', {
477
+ region: this._region,
478
+ countryCode: countryCode,
479
+ endpoint: (0, helpers_1.getApiBaseUrl)()
480
+ });
449
481
  return true;
450
482
  }
451
- // If we reach here, login failed but didn't throw an error
483
+ // If we reach here, authentication failed
452
484
  const delay = initialDelayMs * Math.pow(2, attempt);
453
- logger_1.logger.debug(`Login attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
485
+ logger_1.logger.debug(`Authentication attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
454
486
  await new Promise(resolve => setTimeout(resolve, delay));
455
487
  }
456
488
  catch (error) {
489
+ logger_1.logger.error(`Authentication attempt ${attempt + 1} error:`, error);
457
490
  if (attempt === retryAttempts - 1) {
458
- logger_1.logger.error('Login error after all retry attempts:', error);
491
+ logger_1.logger.error('Authentication failed after all retry attempts');
459
492
  return false;
460
493
  }
461
494
  const delay = initialDelayMs * Math.pow(2, attempt);
462
- logger_1.logger.debug(`Login attempt ${attempt + 1} failed with error, retrying in ${delay}ms...`, error);
495
+ logger_1.logger.debug(`Retrying authentication in ${delay}ms...`);
463
496
  await new Promise(resolve => setTimeout(resolve, delay));
464
497
  }
465
498
  }
466
- logger_1.logger.error('Unable to login with supplied credentials after all retry attempts');
499
+ logger_1.logger.error('Unable to authenticate with supplied credentials after all retry attempts');
467
500
  return false;
468
501
  }
469
502
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.96",
3
+ "version": "1.0.98",
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",