@boozilla/homebridge-shome 1.0.2 → 1.0.4

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.
@@ -18,8 +18,14 @@ export declare class ShomeClient {
18
18
  private cachedAccessToken;
19
19
  private ihdId;
20
20
  private tokenExpiry;
21
+ private requestQueue;
22
+ private isProcessing;
21
23
  constructor(log: Logger, username: string, password: string, deviceId: string);
24
+ private enqueue;
25
+ private processQueue;
26
+ private executeTaskWithRetries;
22
27
  login(): Promise<string | null>;
28
+ private performLogin;
23
29
  getDeviceList(): Promise<MainDevice[]>;
24
30
  getDeviceInfo(thingId: string, type: string): Promise<SubDevice[] | null>;
25
31
  setDevice(thingId: string, deviceId: string, type: string, controlType: string, state: string, nickname?: string): Promise<boolean>;
@@ -4,6 +4,8 @@ const BASE_URL = 'https://shome-api.samsung-ihp.com';
4
4
  const APP_REGST_ID = '6110736314d9eef6baf393f3e43a5342f9ccde6ef300d878385acd9264cf14d5';
5
5
  const CHINA_APP_REGST_ID = 'SHOME==6110736314d9eef6baf393f3e43a5342f9ccde6ef300d878385acd9264cf14d5';
6
6
  const LANGUAGE = 'KOR';
7
+ const MAX_RETRIES = 3;
8
+ const INITIAL_BACKOFF_MS = 1000;
7
9
  export class ShomeClient {
8
10
  log;
9
11
  username;
@@ -12,13 +14,70 @@ export class ShomeClient {
12
14
  cachedAccessToken = null;
13
15
  ihdId = null;
14
16
  tokenExpiry = 0;
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ requestQueue = [];
19
+ isProcessing = false;
15
20
  constructor(log, username, password, deviceId) {
16
21
  this.log = log;
17
22
  this.username = username;
18
23
  this.password = password;
19
24
  this.deviceId = deviceId;
20
25
  }
26
+ enqueue(request) {
27
+ return new Promise((resolve, reject) => {
28
+ this.requestQueue.push({ request, resolve, reject, authRetry: false });
29
+ this.processQueue();
30
+ });
31
+ }
32
+ async processQueue() {
33
+ if (this.isProcessing) {
34
+ return; // A processing loop is already running
35
+ }
36
+ this.isProcessing = true;
37
+ while (this.requestQueue.length > 0) {
38
+ const task = this.requestQueue.shift();
39
+ try {
40
+ const result = await this.executeTaskWithRetries(task);
41
+ task.resolve(result);
42
+ }
43
+ catch (error) {
44
+ task.reject(error);
45
+ }
46
+ }
47
+ this.isProcessing = false;
48
+ }
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ async executeTaskWithRetries(task) {
51
+ let retries = 0;
52
+ while (true) {
53
+ try {
54
+ const result = await task.request();
55
+ return result;
56
+ }
57
+ catch (error) {
58
+ const isAuthError = axios.isAxiosError(error) && error.response?.status === 401;
59
+ if (isAuthError && !task.authRetry) {
60
+ this.log.warn('API authentication failed (401). Retrying after refreshing token.');
61
+ this.cachedAccessToken = null;
62
+ this.tokenExpiry = 0;
63
+ task.authRetry = true;
64
+ continue; // Immediately retry the request
65
+ }
66
+ if (retries >= MAX_RETRIES) {
67
+ this.log.error(`Request failed after ${MAX_RETRIES} retries. Giving up.`, error);
68
+ throw error; // Throw final error
69
+ }
70
+ retries++;
71
+ const backoffTime = INITIAL_BACKOFF_MS * Math.pow(2, retries - 1);
72
+ this.log.warn(`Request failed. Retrying in ${backoffTime}ms... (Attempt ${retries}/${MAX_RETRIES})`);
73
+ await new Promise(resolve => setTimeout(resolve, backoffTime));
74
+ }
75
+ }
76
+ }
21
77
  async login() {
78
+ return this.enqueue(() => this.performLogin());
79
+ }
80
+ async performLogin() {
22
81
  if (!this.isTokenExpired()) {
23
82
  return this.cachedAccessToken;
24
83
  }
@@ -42,7 +101,6 @@ export class ShomeClient {
42
101
  if (response.data && response.data.accessToken) {
43
102
  this.cachedAccessToken = response.data.accessToken;
44
103
  this.ihdId = response.data.ihdId;
45
- // Decode token to find expiry
46
104
  const payload = JSON.parse(Buffer.from(this.cachedAccessToken.split('.')[1], 'base64').toString());
47
105
  this.tokenExpiry = payload.exp * 1000;
48
106
  this.log.info('Successfully logged in to sHome API.');
@@ -55,15 +113,15 @@ export class ShomeClient {
55
113
  }
56
114
  catch (error) {
57
115
  this.log.error(`Login error: ${error}`);
58
- return null;
116
+ throw error;
59
117
  }
60
118
  }
61
119
  async getDeviceList() {
62
- const token = await this.login();
63
- if (!token || !this.ihdId) {
64
- return [];
65
- }
66
- try {
120
+ return this.enqueue(async () => {
121
+ const token = await this.performLogin();
122
+ if (!token || !this.ihdId) {
123
+ return [];
124
+ }
67
125
  const createDate = this.getDateTime();
68
126
  const hashData = this.sha512(`IHRESTAPI${this.ihdId}${createDate}`);
69
127
  const response = await axios.get(`${BASE_URL}/v16/settings/${this.ihdId}/devices/`, {
@@ -71,18 +129,14 @@ export class ShomeClient {
71
129
  headers: { 'Authorization': `Bearer ${token}` },
72
130
  });
73
131
  return response.data.deviceList || [];
74
- }
75
- catch (error) {
76
- this.log.error(`Error getting device list: ${error}`);
77
- return [];
78
- }
132
+ });
79
133
  }
80
134
  async getDeviceInfo(thingId, type) {
81
- const token = await this.login();
82
- if (!token) {
83
- return null;
84
- }
85
- try {
135
+ return this.enqueue(async () => {
136
+ const token = await this.performLogin();
137
+ if (!token) {
138
+ return null;
139
+ }
86
140
  const createDate = this.getDateTime();
87
141
  const hashData = this.sha512(`IHRESTAPI${thingId}${createDate}`);
88
142
  const typePath = type.toLowerCase().replace(/_/g, '');
@@ -90,19 +144,15 @@ export class ShomeClient {
90
144
  params: { createDate, hashData },
91
145
  headers: { 'Authorization': `Bearer ${token}` },
92
146
  });
93
- return response.data.deviceInfoList || null;
94
- }
95
- catch (error) {
96
- this.log.error(`Error getting device info for ${thingId}: ${error}`);
97
- return null;
98
- }
147
+ return response.data.deviceList || null;
148
+ });
99
149
  }
100
150
  async setDevice(thingId, deviceId, type, controlType, state, nickname) {
101
- const token = await this.login();
102
- if (!token) {
103
- return false;
104
- }
105
- try {
151
+ return this.enqueue(async () => {
152
+ const token = await this.performLogin();
153
+ if (!token) {
154
+ return false;
155
+ }
106
156
  const createDate = this.getDateTime();
107
157
  const hashData = this.sha512(`IHRESTAPI${thingId}${deviceId}${state}${createDate}`);
108
158
  const typePath = type.toLowerCase().replace(/_/g, '');
@@ -118,19 +168,14 @@ export class ShomeClient {
118
168
  const displayName = nickname || `${thingId}/${deviceId}`;
119
169
  this.log.info(`[${displayName}] state set to ${state}.`);
120
170
  return true;
121
- }
122
- catch (error) {
123
- const displayName = nickname || `${thingId}/${deviceId}`;
124
- this.log.error(`Error setting device [${displayName}]: ${error}`);
125
- return false;
126
- }
171
+ });
127
172
  }
128
173
  async unlockDoorlock(thingId, nickname) {
129
- const token = await this.login();
130
- if (!token) {
131
- return false;
132
- }
133
- try {
174
+ return this.enqueue(async () => {
175
+ const token = await this.performLogin();
176
+ if (!token) {
177
+ return false;
178
+ }
134
179
  const createDate = this.getDateTime();
135
180
  const hashData = this.sha512(`IHRESTAPI${thingId}${createDate}`);
136
181
  await axios.put(`${BASE_URL}/v16/settings/doorlocks/${thingId}/open-mode`, null, {
@@ -144,12 +189,7 @@ export class ShomeClient {
144
189
  const displayName = nickname || thingId;
145
190
  this.log.info(`Unlocked [${displayName}].`);
146
191
  return true;
147
- }
148
- catch (error) {
149
- const displayName = nickname || thingId;
150
- this.log.error(`Error unlocking [${displayName}]: ${error}`);
151
- return false;
152
- }
192
+ });
153
193
  }
154
194
  sha512(input) {
155
195
  return CryptoJS.SHA512(input).toString();
@@ -1 +1 @@
1
- {"version":3,"file":"shomeClient.js","sourceRoot":"","sources":["../src/shomeClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shomeClient.js","sourceRoot":"","sources":["../src/shomeClient.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@boozilla/homebridge-shome",
3
3
  "displayName": "sHome Plugin",
4
- "version": "1.0.2",
4
+ "version": "1.0.4",
5
5
  "private": false,
6
6
  "description": "A Homebridge plugin for Samsung Smart Home",
7
7
  "author": "boozilla",
@@ -6,6 +6,8 @@ const BASE_URL = 'https://shome-api.samsung-ihp.com';
6
6
  const APP_REGST_ID = '6110736314d9eef6baf393f3e43a5342f9ccde6ef300d878385acd9264cf14d5';
7
7
  const CHINA_APP_REGST_ID = 'SHOME==6110736314d9eef6baf393f3e43a5342f9ccde6ef300d878385acd9264cf14d5';
8
8
  const LANGUAGE = 'KOR';
9
+ const MAX_RETRIES = 3;
10
+ const INITIAL_BACKOFF_MS = 1000;
9
11
 
10
12
  // Define and export interfaces for device types
11
13
  export interface MainDevice {
@@ -21,10 +23,20 @@ export interface SubDevice {
21
23
  [key: string]: unknown;
22
24
  }
23
25
 
26
+ type QueueTask<T = unknown> = {
27
+ request: () => Promise<T>;
28
+ resolve: (value: T | PromiseLike<T>) => void;
29
+ reject: (reason?: unknown) => void;
30
+ authRetry: boolean;
31
+ };
32
+
24
33
  export class ShomeClient {
25
34
  private cachedAccessToken: string | null = null;
26
35
  private ihdId: string | null = null;
27
36
  private tokenExpiry: number = 0;
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ private requestQueue: QueueTask<any>[] = [];
39
+ private isProcessing = false;
28
40
 
29
41
  constructor(
30
42
  private readonly log: Logger,
@@ -34,7 +46,68 @@ export class ShomeClient {
34
46
  ) {
35
47
  }
36
48
 
49
+ private enqueue<T>(request: () => Promise<T>): Promise<T> {
50
+ return new Promise<T>((resolve, reject) => {
51
+ this.requestQueue.push({ request, resolve, reject, authRetry: false });
52
+ this.processQueue();
53
+ });
54
+ }
55
+
56
+ private async processQueue(): Promise<void> {
57
+ if (this.isProcessing) {
58
+ return; // A processing loop is already running
59
+ }
60
+ this.isProcessing = true;
61
+
62
+ while (this.requestQueue.length > 0) {
63
+ const task = this.requestQueue.shift()!;
64
+ try {
65
+ const result = await this.executeTaskWithRetries(task);
66
+ task.resolve(result);
67
+ } catch (error) {
68
+ task.reject(error);
69
+ }
70
+ }
71
+
72
+ this.isProcessing = false;
73
+ }
74
+
75
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
+ private async executeTaskWithRetries(task: QueueTask<any>): Promise<any> {
77
+ let retries = 0;
78
+ while (true) {
79
+ try {
80
+ const result = await task.request();
81
+ return result;
82
+ } catch (error) {
83
+ const isAuthError = axios.isAxiosError(error) && error.response?.status === 401;
84
+
85
+ if (isAuthError && !task.authRetry) {
86
+ this.log.warn('API authentication failed (401). Retrying after refreshing token.');
87
+ this.cachedAccessToken = null;
88
+ this.tokenExpiry = 0;
89
+ task.authRetry = true;
90
+ continue; // Immediately retry the request
91
+ }
92
+
93
+ if (retries >= MAX_RETRIES) {
94
+ this.log.error(`Request failed after ${MAX_RETRIES} retries. Giving up.`, error);
95
+ throw error; // Throw final error
96
+ }
97
+
98
+ retries++;
99
+ const backoffTime = INITIAL_BACKOFF_MS * Math.pow(2, retries - 1);
100
+ this.log.warn(`Request failed. Retrying in ${backoffTime}ms... (Attempt ${retries}/${MAX_RETRIES})`);
101
+ await new Promise(resolve => setTimeout(resolve, backoffTime));
102
+ }
103
+ }
104
+ }
105
+
37
106
  async login(): Promise<string | null> {
107
+ return this.enqueue(() => this.performLogin());
108
+ }
109
+
110
+ private async performLogin(): Promise<string | null> {
38
111
  if (!this.isTokenExpired()) {
39
112
  return this.cachedAccessToken;
40
113
  }
@@ -62,7 +135,6 @@ export class ShomeClient {
62
135
  this.cachedAccessToken = response.data.accessToken;
63
136
  this.ihdId = response.data.ihdId;
64
137
 
65
- // Decode token to find expiry
66
138
  const payload = JSON.parse(Buffer.from(this.cachedAccessToken!.split('.')[1], 'base64').toString());
67
139
  this.tokenExpiry = payload.exp * 1000;
68
140
 
@@ -74,17 +146,17 @@ export class ShomeClient {
74
146
  }
75
147
  } catch (error) {
76
148
  this.log.error(`Login error: ${error}`);
77
- return null;
149
+ throw error;
78
150
  }
79
151
  }
80
152
 
81
153
  async getDeviceList(): Promise<MainDevice[]> {
82
- const token = await this.login();
83
- if (!token || !this.ihdId) {
84
- return [];
85
- }
154
+ return this.enqueue(async () => {
155
+ const token = await this.performLogin();
156
+ if (!token || !this.ihdId) {
157
+ return [];
158
+ }
86
159
 
87
- try {
88
160
  const createDate = this.getDateTime();
89
161
  const hashData = this.sha512(`IHRESTAPI${this.ihdId}${createDate}`);
90
162
 
@@ -92,21 +164,17 @@ export class ShomeClient {
92
164
  params: { createDate, hashData },
93
165
  headers: { 'Authorization': `Bearer ${token}` },
94
166
  });
95
-
96
167
  return response.data.deviceList || [];
97
- } catch (error) {
98
- this.log.error(`Error getting device list: ${error}`);
99
- return [];
100
- }
168
+ });
101
169
  }
102
170
 
103
171
  async getDeviceInfo(thingId: string, type: string): Promise<SubDevice[] | null> {
104
- const token = await this.login();
105
- if (!token) {
106
- return null;
107
- }
172
+ return this.enqueue(async () => {
173
+ const token = await this.performLogin();
174
+ if (!token) {
175
+ return null;
176
+ }
108
177
 
109
- try {
110
178
  const createDate = this.getDateTime();
111
179
  const hashData = this.sha512(`IHRESTAPI${thingId}${createDate}`);
112
180
  const typePath = type.toLowerCase().replace(/_/g, '');
@@ -115,21 +183,17 @@ export class ShomeClient {
115
183
  params: { createDate, hashData },
116
184
  headers: { 'Authorization': `Bearer ${token}` },
117
185
  });
118
-
119
- return response.data.deviceInfoList || null;
120
- } catch (error) {
121
- this.log.error(`Error getting device info for ${thingId}: ${error}`);
122
- return null;
123
- }
186
+ return response.data.deviceList || null;
187
+ });
124
188
  }
125
189
 
126
190
  async setDevice(thingId: string, deviceId: string, type: string, controlType: string, state: string, nickname?: string): Promise<boolean> {
127
- const token = await this.login();
128
- if (!token) {
129
- return false;
130
- }
191
+ return this.enqueue(async () => {
192
+ const token = await this.performLogin();
193
+ if (!token) {
194
+ return false;
195
+ }
131
196
 
132
- try {
133
197
  const createDate = this.getDateTime();
134
198
  const hashData = this.sha512(`IHRESTAPI${thingId}${deviceId}${state}${createDate}`);
135
199
  const typePath = type.toLowerCase().replace(/_/g, '');
@@ -147,20 +211,16 @@ export class ShomeClient {
147
211
  const displayName = nickname || `${thingId}/${deviceId}`;
148
212
  this.log.info(`[${displayName}] state set to ${state}.`);
149
213
  return true;
150
- } catch (error) {
151
- const displayName = nickname || `${thingId}/${deviceId}`;
152
- this.log.error(`Error setting device [${displayName}]: ${error}`);
153
- return false;
154
- }
214
+ });
155
215
  }
156
216
 
157
217
  async unlockDoorlock(thingId: string, nickname?: string): Promise<boolean> {
158
- const token = await this.login();
159
- if (!token) {
160
- return false;
161
- }
218
+ return this.enqueue(async () => {
219
+ const token = await this.performLogin();
220
+ if (!token) {
221
+ return false;
222
+ }
162
223
 
163
- try {
164
224
  const createDate = this.getDateTime();
165
225
  const hashData = this.sha512(`IHRESTAPI${thingId}${createDate}`);
166
226
 
@@ -176,11 +236,7 @@ export class ShomeClient {
176
236
  const displayName = nickname || thingId;
177
237
  this.log.info(`Unlocked [${displayName}].`);
178
238
  return true;
179
- } catch (error) {
180
- const displayName = nickname || thingId;
181
- this.log.error(`Error unlocking [${displayName}]: ${error}`);
182
- return false;
183
- }
239
+ });
184
240
  }
185
241
 
186
242
  private sha512(input: string): string {