@boozilla/homebridge-shome 1.0.11 → 1.0.13

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
@@ -1,5 +1,7 @@
1
1
  # Homebridge sHome
2
2
 
3
+ [![verified-by-homebridge](https://img.shields.io/badge/homebridge-verified-blueviolet?color=%23491F59&style=for-the-badge&logoColor=%23FFFFFF&logo=homebridge)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
4
+
3
5
  `homebridge-shome` is a [Homebridge](https://homebridge.io/) plugin that allows you to integrate devices from the
4
6
  Samsung Smart Home (sHome) platform into Apple HomeKit.
5
7
 
@@ -15,13 +17,14 @@ door locks, using the Home app and Siri on your Apple devices.
15
17
  * **Digital Door Lock**: Check the current state of your door lock (locked/unlocked) and unlock it remotely.
16
18
  * **Individual Accessory Support**: Each sub-device registered in your system, such as 'Living Room Light' or 'Master
17
19
  Bedroom Thermostat', is added as an independent HomeKit accessory for granular control.
20
+ * **Visitor Notifications**: Automatically creates a "Visitor Alert" motion sensor in HomeKit. When a new visitor is detected, this sensor is triggered, sending a notification to your Apple devices (if you have notifications enabled for the sensor in the Home app).
18
21
 
19
22
  ## Installation
20
23
 
21
24
  If you have a running Homebridge setup, install the plugin using the following command:
22
25
 
23
26
  ```sh
24
- npm install -g homebridge-shome
27
+ npm install -g @boozilla/homebridge-shome
25
28
  ```
26
29
 
27
30
  ## Configuration
@@ -0,0 +1,9 @@
1
+ import { PlatformAccessory } from 'homebridge';
2
+ import { ShomePlatform } from '../platform.js';
3
+ export declare class DoorbellAccessory {
4
+ private readonly platform;
5
+ private readonly accessory;
6
+ private service;
7
+ constructor(platform: ShomePlatform, accessory: PlatformAccessory);
8
+ ring(visitorLabel: string): void;
9
+ }
@@ -0,0 +1,24 @@
1
+ export class DoorbellAccessory {
2
+ platform;
3
+ accessory;
4
+ service;
5
+ constructor(platform, accessory) {
6
+ this.platform = platform;
7
+ this.accessory = accessory;
8
+ // set accessory information
9
+ this.accessory.getService(this.platform.Service.AccessoryInformation)
10
+ .setCharacteristic(this.platform.Characteristic.Manufacturer, 'sHome')
11
+ .setCharacteristic(this.platform.Characteristic.Model, 'sHome Doorbell')
12
+ .setCharacteristic(this.platform.Characteristic.SerialNumber, 'shome-doorbell');
13
+ this.service = this.accessory.getService(this.platform.Service.StatelessProgrammableSwitch)
14
+ || this.accessory.addService(this.platform.Service.StatelessProgrammableSwitch);
15
+ this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
16
+ this.service.getCharacteristic(this.platform.Characteristic.ProgrammableSwitchEvent);
17
+ }
18
+ ring(visitorLabel) {
19
+ this.platform.log.info(`Triggered Doorbell event from [${visitorLabel}]`);
20
+ this.service.getCharacteristic(this.platform.Characteristic.ProgrammableSwitchEvent)
21
+ .updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
22
+ }
23
+ }
24
+ //# sourceMappingURL=doorbellAccessory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doorbellAccessory.js","sourceRoot":"","sources":["../../src/accessories/doorbellAccessory.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,iBAAiB;IAIL;IACA;IAJf,OAAO,CAAU;IAEzB,YACuB,QAAuB,EACvB,SAA4B;QAD5B,aAAQ,GAAR,QAAQ,CAAe;QACvB,cAAS,GAAT,SAAS,CAAmB;QAG7C,4BAA4B;QAC5B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAE;aACnE,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,EAAE,OAAO,CAAC;aACrE,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,EAAE,gBAAgB,CAAC;aACvE,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC;QAClF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,2BAA2B,CAAC;eACpF,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;QACpF,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QACzF,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,CAAC,CAAC;IAC3F,CAAC;IAEM,IAAI,CAAC,YAAoB;QAC9B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,kCAAkC,YAAY,GAAG,CAAC,CAAC;QAE1E,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,CAAC;aACjF,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAC;IACpF,CAAC;CACF"}
@@ -9,12 +9,16 @@ export declare class ShomePlatform implements DynamicPlatformPlugin {
9
9
  readonly accessories: PlatformAccessory[];
10
10
  readonly shomeClient: ShomeClient;
11
11
  private readonly accessoryHandlers;
12
- private pollingInterval;
12
+ private readonly pollingInterval;
13
13
  private pollingTimer?;
14
+ private lastCheckedTimestamp;
14
15
  constructor(log: Logger, config: PlatformConfig, api: API);
15
16
  configureAccessory(accessory: PlatformAccessory): void;
16
17
  discoverDevices(): Promise<void>;
17
18
  setupAccessory(mainDevice: MainDevice, subDevice: SubDevice | null, uuid: string): PlatformAccessory;
18
- createAccessory(accessory: PlatformAccessory): void;
19
+ createAccessoryHandler(accessory: PlatformAccessory): void;
19
20
  startPolling(): void;
21
+ pollDeviceUpdates(): Promise<void>;
22
+ checkForNewVisitors(): Promise<void>;
23
+ private parseRecodDt;
20
24
  }
package/dist/platform.js CHANGED
@@ -4,6 +4,7 @@ import { LightAccessory } from './accessories/lightAccessory.js';
4
4
  import { VentilatorAccessory } from './accessories/ventilatorAccessory.js';
5
5
  import { HeaterAccessory } from './accessories/heaterAccessory.js';
6
6
  import { DoorlockAccessory } from './accessories/doorlockAccessory.js';
7
+ import { DoorbellAccessory } from './accessories/doorbellAccessory.js';
7
8
  const CONTROLLABLE_MULTI_DEVICE_TYPES = ['LIGHT', 'HEATER', 'VENTILATOR'];
8
9
  const SPECIAL_CONTROLLABLE_TYPES = ['DOORLOCK'];
9
10
  export class ShomePlatform {
@@ -17,6 +18,7 @@ export class ShomePlatform {
17
18
  accessoryHandlers = new Map();
18
19
  pollingInterval;
19
20
  pollingTimer;
21
+ lastCheckedTimestamp = new Date();
20
22
  constructor(log, config, api) {
21
23
  this.log = log;
22
24
  this.config = config;
@@ -31,6 +33,7 @@ export class ShomePlatform {
31
33
  return;
32
34
  }
33
35
  this.shomeClient = new ShomeClient(this.log, this.config.username, this.config.password, this.config.deviceId);
36
+ this.log.info(`Doorbell notification service is active. Baseline time: ${this.lastCheckedTimestamp.toISOString()}`);
34
37
  this.api.on('didFinishLaunching', () => {
35
38
  this.discoverDevices();
36
39
  if (this.pollingInterval > 0) {
@@ -54,9 +57,6 @@ export class ShomePlatform {
54
57
  this.log.info('Discovering devices...');
55
58
  const devices = await this.shomeClient.getDeviceList();
56
59
  const foundAccessories = [];
57
- if (!devices || devices.length === 0) {
58
- this.log.warn('No devices found on your sHome account.');
59
- }
60
60
  for (const device of devices) {
61
61
  if (CONTROLLABLE_MULTI_DEVICE_TYPES.includes(device.thngModelTypeName)) {
62
62
  const deviceInfoList = await this.shomeClient.getDeviceInfo(device.thngId, device.thngModelTypeName);
@@ -73,10 +73,11 @@ export class ShomePlatform {
73
73
  const accessory = this.setupAccessory(device, null, uuid);
74
74
  foundAccessories.push(accessory);
75
75
  }
76
- else {
77
- this.log.info(`Ignoring device: ${device.nickname} (Type: ${device.thngModelTypeName})`);
78
- }
79
76
  }
77
+ const doorbellUUID = this.api.hap.uuid.generate('shome-doorbell-accessory');
78
+ const doorbellDevice = { thngModelTypeName: 'DOORBELL', nickname: 'Doorbell', thngId: 'shome-doorbell' };
79
+ const doorbellAccessory = this.setupAccessory(doorbellDevice, null, doorbellUUID);
80
+ foundAccessories.push(doorbellAccessory);
80
81
  const accessoriesToRemove = this.accessories.filter(cachedAccessory => !foundAccessories.some(foundAccessory => foundAccessory.UUID === cachedAccessory.UUID));
81
82
  if (accessoriesToRemove.length > 0) {
82
83
  this.log.info('Removing stale accessories:', accessoriesToRemove.map(a => a.displayName));
@@ -92,28 +93,27 @@ export class ShomePlatform {
92
93
  setupAccessory(mainDevice, subDevice, uuid) {
93
94
  const displayName = subDevice ? subDevice.nickname : mainDevice.nickname;
94
95
  const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
96
+ const accessory = existingAccessory ?? new this.api.platformAccessory(displayName, uuid);
97
+ accessory.context.device = mainDevice;
98
+ accessory.context.subDevice = subDevice;
95
99
  if (existingAccessory) {
96
100
  this.log.info('Restoring existing accessory from cache:', displayName);
97
- existingAccessory.context.device = mainDevice;
98
- existingAccessory.context.subDevice = subDevice;
99
- this.createAccessory(existingAccessory);
100
- return existingAccessory;
101
101
  }
102
102
  else {
103
103
  this.log.info('Adding new accessory:', displayName);
104
- const accessory = new this.api.platformAccessory(displayName, uuid);
105
- accessory.context.device = mainDevice;
106
- accessory.context.subDevice = subDevice;
107
- this.createAccessory(accessory);
108
104
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
109
- return accessory;
110
105
  }
106
+ this.createAccessoryHandler(accessory);
107
+ return accessory;
111
108
  }
112
- createAccessory(accessory) {
109
+ createAccessoryHandler(accessory) {
113
110
  const device = accessory.context.device;
114
111
  const accessoryType = device.thngModelTypeName;
115
- if (!this.accessoryHandlers.has(accessory.UUID)) {
116
- switch (accessoryType) {
112
+ if (this.accessoryHandlers.has(accessory.UUID)) {
113
+ return;
114
+ }
115
+ if (accessory.context.subDevice) {
116
+ switch (device.thngModelTypeName) {
117
117
  case 'LIGHT':
118
118
  this.accessoryHandlers.set(accessory.UUID, new LightAccessory(this, accessory));
119
119
  break;
@@ -123,44 +123,101 @@ export class ShomePlatform {
123
123
  case 'HEATER':
124
124
  this.accessoryHandlers.set(accessory.UUID, new HeaterAccessory(this, accessory));
125
125
  break;
126
- case 'DOORLOCK':
127
- this.accessoryHandlers.set(accessory.UUID, new DoorlockAccessory(this, accessory));
128
- break;
129
126
  }
127
+ return;
128
+ }
129
+ switch (accessoryType) {
130
+ case 'DOORLOCK':
131
+ this.accessoryHandlers.set(accessory.UUID, new DoorlockAccessory(this, accessory));
132
+ break;
133
+ case 'DOORBELL':
134
+ this.accessoryHandlers.set(accessory.UUID, new DoorbellAccessory(this, accessory));
135
+ break;
130
136
  }
131
137
  }
132
138
  startPolling() {
133
- this.log.info(`Starting periodic device state polling every ${this.pollingInterval / 1000} seconds.`);
139
+ this.log.info(`Starting periodic state polling every ${this.pollingInterval / 1000} seconds.`);
134
140
  this.pollingTimer = setInterval(async () => {
135
- this.log.debug('Polling for device updates...');
141
+ this.log.debug('Polling for updates...');
136
142
  try {
137
- const devices = await this.shomeClient.getDeviceList();
138
- for (const device of devices) {
139
- if (CONTROLLABLE_MULTI_DEVICE_TYPES.includes(device.thngModelTypeName)) {
140
- const deviceInfoList = await this.shomeClient.getDeviceInfo(device.thngId, device.thngModelTypeName);
141
- if (deviceInfoList) {
142
- for (const subDevice of deviceInfoList) {
143
- const subUuid = this.api.hap.uuid.generate(`${device.thngId}-${subDevice.deviceId}`);
144
- const handler = this.accessoryHandlers.get(subUuid);
145
- if (handler) {
146
- handler.updateState(subDevice);
147
- }
148
- }
143
+ await this.pollDeviceUpdates();
144
+ await this.checkForNewVisitors();
145
+ }
146
+ catch (error) {
147
+ this.log.error('An error occurred during polling:', error);
148
+ }
149
+ }, this.pollingInterval);
150
+ }
151
+ async pollDeviceUpdates() {
152
+ const devices = await this.shomeClient.getDeviceList();
153
+ for (const device of devices) {
154
+ if (CONTROLLABLE_MULTI_DEVICE_TYPES.includes(device.thngModelTypeName)) {
155
+ const deviceInfoList = await this.shomeClient.getDeviceInfo(device.thngId, device.thngModelTypeName);
156
+ if (deviceInfoList) {
157
+ for (const subDevice of deviceInfoList) {
158
+ const deviceId = `${device.thngId}-${subDevice.deviceId}`;
159
+ if (this.shomeClient.isDeviceBusy(deviceId)) {
160
+ this.log.debug(`Skipping polling update for ${subDevice.nickname} as it has a pending request.`);
161
+ continue;
149
162
  }
150
- }
151
- else if (SPECIAL_CONTROLLABLE_TYPES.includes(device.thngModelTypeName)) {
152
- const uuid = this.api.hap.uuid.generate(device.thngId);
153
- const handler = this.accessoryHandlers.get(uuid);
163
+ const subUuid = this.api.hap.uuid.generate(deviceId);
164
+ const handler = this.accessoryHandlers.get(subUuid);
154
165
  if (handler) {
155
- handler.updateState(device);
166
+ handler.updateState(subDevice);
156
167
  }
157
168
  }
158
169
  }
159
170
  }
160
- catch (error) {
161
- this.log.error('An error occurred during polling:', error);
171
+ else if (SPECIAL_CONTROLLABLE_TYPES.includes(device.thngModelTypeName)) {
172
+ const deviceId = device.thngId;
173
+ if (this.shomeClient.isDeviceBusy(deviceId)) {
174
+ this.log.debug(`Skipping polling update for ${device.nickname} as it has a pending request.`);
175
+ continue;
176
+ }
177
+ const uuid = this.api.hap.uuid.generate(deviceId);
178
+ const handler = this.accessoryHandlers.get(uuid);
179
+ if (handler) {
180
+ handler.updateState(device);
181
+ }
162
182
  }
163
- }, this.pollingInterval);
183
+ }
184
+ }
185
+ async checkForNewVisitors() {
186
+ this.log.debug('Checking for new doorbell events...');
187
+ const visitorList = await this.shomeClient.getVisitorHistory();
188
+ const newVisitors = [];
189
+ for (const visitor of visitorList) {
190
+ const visitorTime = this.parseRecodDt(visitor.recodDt);
191
+ if (visitorTime > this.lastCheckedTimestamp) {
192
+ newVisitors.push(visitor);
193
+ }
194
+ }
195
+ if (newVisitors.length > 0) {
196
+ this.log.info(`Found ${newVisitors.length} new doorbell event(s).`);
197
+ newVisitors.sort((a, b) => a.recodDt.localeCompare(b.recodDt));
198
+ const doorbellUUID = this.api.hap.uuid.generate('shome-doorbell-accessory');
199
+ const doorbellHandler = this.accessoryHandlers.get(doorbellUUID);
200
+ if (doorbellHandler) {
201
+ for (const visitor of newVisitors) {
202
+ doorbellHandler.ring(visitor.deviceLabel);
203
+ }
204
+ }
205
+ else {
206
+ this.log.warn('Doorbell accessory handler not found.');
207
+ }
208
+ const latestVisitor = newVisitors[newVisitors.length - 1];
209
+ this.lastCheckedTimestamp = this.parseRecodDt(latestVisitor.recodDt);
210
+ this.log.debug(`Updated last checked timestamp to: ${this.lastCheckedTimestamp.toISOString()}`);
211
+ }
212
+ }
213
+ parseRecodDt(recodDt) {
214
+ const year = parseInt(recodDt.substring(0, 4), 10);
215
+ const month = parseInt(recodDt.substring(4, 6), 10) - 1;
216
+ const day = parseInt(recodDt.substring(6, 8), 10);
217
+ const hour = parseInt(recodDt.substring(8, 10), 10);
218
+ const minute = parseInt(recodDt.substring(10, 12), 10);
219
+ const second = parseInt(recodDt.substring(12, 14), 10);
220
+ return new Date(year, month, day, hour, minute, second);
164
221
  }
165
222
  }
166
223
  //# sourceMappingURL=platform.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,12 @@ export interface SubDevice {
10
10
  nickname: string;
11
11
  [key: string]: unknown;
12
12
  }
13
+ export interface Visitor {
14
+ sttId: string;
15
+ thumbNail: string;
16
+ recodDt: string;
17
+ deviceLabel: string;
18
+ }
13
19
  export declare class ShomeClient {
14
20
  private readonly log;
15
21
  private readonly username;
@@ -17,19 +23,24 @@ export declare class ShomeClient {
17
23
  private readonly deviceId;
18
24
  private cachedAccessToken;
19
25
  private ihdId;
26
+ private homeId;
20
27
  private tokenExpiry;
21
- private requestQueue;
22
- private isProcessing;
28
+ private putQueue;
29
+ private isProcessingPut;
30
+ private loginPromise;
31
+ private pendingPutRequests;
23
32
  constructor(log: Logger, username: string, password: string, deviceId: string);
24
- private enqueue;
25
- private processQueue;
26
- private executeTaskWithRetries;
27
- login(): Promise<string | null>;
33
+ private login;
28
34
  private performLogin;
35
+ private enqueuePut;
36
+ private processPutQueue;
37
+ private executeWithRetries;
38
+ isDeviceBusy(deviceId: string): boolean;
29
39
  getDeviceList(): Promise<MainDevice[]>;
30
40
  getDeviceInfo(thingId: string, type: string): Promise<SubDevice[] | null>;
31
- setDevice(thingId: string, deviceId: string, type: string, controlType: string, state: string, nickname?: string): Promise<boolean>;
41
+ setDevice(thingId: string, subDeviceId: string, type: string, controlType: string, state: string, nickname?: string): Promise<boolean>;
32
42
  unlockDoorlock(thingId: string, nickname?: string): Promise<boolean>;
43
+ getVisitorHistory(): Promise<Visitor[]>;
33
44
  private sha512;
34
45
  private isTokenExpired;
35
46
  private getDateTime;
@@ -14,71 +14,39 @@ export class ShomeClient {
14
14
  deviceId;
15
15
  cachedAccessToken = null;
16
16
  ihdId = null;
17
+ homeId = null;
17
18
  tokenExpiry = 0;
18
19
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- requestQueue = [];
20
- isProcessing = false;
20
+ putQueue = [];
21
+ isProcessingPut = false;
22
+ loginPromise = null;
23
+ pendingPutRequests = new Set();
21
24
  constructor(log, username, password, deviceId) {
22
25
  this.log = log;
23
26
  this.username = username;
24
27
  this.password = password;
25
28
  this.deviceId = deviceId;
26
29
  }
27
- enqueue(request) {
28
- return new Promise((resolve, reject) => {
29
- this.requestQueue.push({ request, resolve, reject, authRetry: false });
30
- this.processQueue();
31
- });
32
- }
33
- async processQueue() {
34
- if (this.isProcessing) {
35
- return; // A processing loop is already running
30
+ login() {
31
+ if (!this.isTokenExpired()) {
32
+ return Promise.resolve(this.cachedAccessToken);
36
33
  }
37
- this.isProcessing = true;
38
- while (this.requestQueue.length > 0) {
39
- const task = this.requestQueue.shift();
40
- try {
41
- const result = await this.executeTaskWithRetries(task);
42
- task.resolve(result);
43
- }
44
- catch (error) {
45
- task.reject(error);
46
- }
47
- // Add a delay between requests to avoid overwhelming the server
48
- await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS));
34
+ if (this.loginPromise) {
35
+ return this.loginPromise;
49
36
  }
50
- this.isProcessing = false;
51
- }
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- async executeTaskWithRetries(task) {
54
- let retries = 0;
55
- while (true) {
56
- try {
57
- const result = await task.request();
58
- return result;
59
- }
60
- catch (error) {
61
- const isAuthError = axios.isAxiosError(error) && error.response?.status === 401;
62
- if (isAuthError && !task.authRetry) {
63
- this.log.warn('API authentication failed (401). Retrying after refreshing token.');
64
- this.cachedAccessToken = null;
65
- this.tokenExpiry = 0;
66
- task.authRetry = true;
67
- continue; // Immediately retry the request
68
- }
69
- if (retries >= MAX_RETRIES) {
70
- this.log.error(`Request failed after ${MAX_RETRIES} retries. Giving up.`, error);
71
- throw error; // Throw final error
72
- }
73
- retries++;
74
- const backoffTime = INITIAL_BACKOFF_MS * Math.pow(2, retries - 1);
75
- this.log.warn(`Request failed. Retrying in ${backoffTime}ms... (Attempt ${retries}/${MAX_RETRIES})`);
76
- await new Promise(resolve => setTimeout(resolve, backoffTime));
77
- }
78
- }
79
- }
80
- async login() {
81
- return this.enqueue(() => this.performLogin());
37
+ this.loginPromise = new Promise((resolve, reject) => {
38
+ this.putQueue.unshift({
39
+ request: () => this.performLogin(),
40
+ resolve,
41
+ reject,
42
+ });
43
+ this.processPutQueue();
44
+ });
45
+ // Clean up the promise once it's settled
46
+ this.loginPromise.finally(() => {
47
+ this.loginPromise = null;
48
+ });
49
+ return this.loginPromise;
82
50
  }
83
51
  async performLogin() {
84
52
  if (!this.isTokenExpired()) {
@@ -104,6 +72,7 @@ export class ShomeClient {
104
72
  if (response.data && response.data.accessToken) {
105
73
  this.cachedAccessToken = response.data.accessToken;
106
74
  this.ihdId = response.data.ihdId;
75
+ this.homeId = response.data.homeId;
107
76
  const payload = JSON.parse(Buffer.from(this.cachedAccessToken.split('.')[1], 'base64').toString());
108
77
  this.tokenExpiry = payload.exp * 1000;
109
78
  this.log.info('Successfully logged in to sHome API.');
@@ -119,9 +88,74 @@ export class ShomeClient {
119
88
  throw error;
120
89
  }
121
90
  }
91
+ enqueuePut(request, deviceId) {
92
+ return new Promise((resolve, reject) => {
93
+ this.putQueue.push({ request, resolve, reject, deviceId });
94
+ this.processPutQueue();
95
+ });
96
+ }
97
+ async processPutQueue() {
98
+ if (this.isProcessingPut) {
99
+ return;
100
+ }
101
+ this.isProcessingPut = true;
102
+ while (this.putQueue.length > 0) {
103
+ const task = this.putQueue.shift();
104
+ if (task.deviceId) {
105
+ this.pendingPutRequests.add(task.deviceId);
106
+ }
107
+ try {
108
+ const result = await this.executeWithRetries(task.request, true);
109
+ task.resolve(result);
110
+ }
111
+ catch (error) {
112
+ task.reject(error);
113
+ }
114
+ finally {
115
+ if (task.deviceId) {
116
+ this.pendingPutRequests.delete(task.deviceId);
117
+ }
118
+ }
119
+ await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS));
120
+ }
121
+ this.isProcessingPut = false;
122
+ }
123
+ async executeWithRetries(request, isQueued = false) {
124
+ let retries = 0;
125
+ while (true) {
126
+ try {
127
+ if (!isQueued) {
128
+ await this.login();
129
+ }
130
+ return await request();
131
+ }
132
+ catch (error) {
133
+ const isAuthError = axios.isAxiosError(error) && error.response?.status === 401;
134
+ if (isAuthError) {
135
+ this.log.warn('API authentication failed (401). Invalidating token.');
136
+ this.cachedAccessToken = null;
137
+ this.tokenExpiry = 0;
138
+ }
139
+ if (retries >= MAX_RETRIES) {
140
+ this.log.error(`Request failed after ${MAX_RETRIES} retries. Giving up.`, error);
141
+ throw error;
142
+ }
143
+ retries++;
144
+ const backoffTime = INITIAL_BACKOFF_MS * Math.pow(2, retries - 1);
145
+ if (!isAuthError) {
146
+ this.log.warn(`Request failed. Retrying in ${backoffTime}ms... (Attempt ${retries}/${MAX_RETRIES})`);
147
+ }
148
+ await new Promise(resolve => setTimeout(resolve, backoffTime));
149
+ await this.login();
150
+ }
151
+ }
152
+ }
153
+ isDeviceBusy(deviceId) {
154
+ return this.pendingPutRequests.has(deviceId);
155
+ }
122
156
  async getDeviceList() {
123
- return this.enqueue(async () => {
124
- const token = await this.performLogin();
157
+ return this.executeWithRetries(async () => {
158
+ const token = this.cachedAccessToken;
125
159
  if (!token || !this.ihdId) {
126
160
  return [];
127
161
  }
@@ -135,8 +169,8 @@ export class ShomeClient {
135
169
  });
136
170
  }
137
171
  async getDeviceInfo(thingId, type) {
138
- return this.enqueue(async () => {
139
- const token = await this.performLogin();
172
+ return this.executeWithRetries(async () => {
173
+ const token = this.cachedAccessToken;
140
174
  if (!token) {
141
175
  return null;
142
176
  }
@@ -150,17 +184,18 @@ export class ShomeClient {
150
184
  return response.data.deviceInfoList || null;
151
185
  });
152
186
  }
153
- async setDevice(thingId, deviceId, type, controlType, state, nickname) {
154
- return this.enqueue(async () => {
155
- const token = await this.performLogin();
187
+ async setDevice(thingId, subDeviceId, type, controlType, state, nickname) {
188
+ const deviceId = `${thingId}-${subDeviceId}`;
189
+ const request = async () => {
190
+ const token = this.cachedAccessToken;
156
191
  if (!token) {
157
192
  return false;
158
193
  }
159
194
  const createDate = this.getDateTime();
160
- const hashData = this.sha512(`IHRESTAPI${thingId}${deviceId}${state}${createDate}`);
195
+ const hashData = this.sha512(`IHRESTAPI${thingId}${subDeviceId}${state}${createDate}`);
161
196
  const typePath = type.toLowerCase().replace(/_/g, '');
162
197
  const controlPath = controlType.toLowerCase().replace(/_/g, '-');
163
- await axios.put(`${BASE_URL}/v18/settings/${typePath}/${thingId}/${deviceId}/${controlPath}`, null, {
198
+ await axios.put(`${BASE_URL}/v18/settings/${typePath}/${thingId}/${subDeviceId}/${controlPath}`, null, {
164
199
  params: {
165
200
  createDate,
166
201
  [controlType === 'WINDSPEED' ? 'mode' : 'state']: state,
@@ -168,14 +203,16 @@ export class ShomeClient {
168
203
  },
169
204
  headers: { 'Authorization': `Bearer ${token}` },
170
205
  });
171
- const displayName = nickname || `${thingId}/${deviceId}`;
206
+ const displayName = nickname || deviceId;
172
207
  this.log.info(`[${displayName}] state set to ${state}.`);
173
208
  return true;
174
- });
209
+ };
210
+ return this.enqueuePut(request, deviceId);
175
211
  }
176
212
  async unlockDoorlock(thingId, nickname) {
177
- return this.enqueue(async () => {
178
- const token = await this.performLogin();
213
+ const deviceId = thingId;
214
+ const request = async () => {
215
+ const token = this.cachedAccessToken;
179
216
  if (!token) {
180
217
  return false;
181
218
  }
@@ -192,6 +229,24 @@ export class ShomeClient {
192
229
  const displayName = nickname || thingId;
193
230
  this.log.info(`Unlocked [${displayName}].`);
194
231
  return true;
232
+ };
233
+ return this.enqueuePut(request, deviceId);
234
+ }
235
+ async getVisitorHistory() {
236
+ return this.executeWithRetries(async () => {
237
+ const token = this.cachedAccessToken;
238
+ if (!token || !this.homeId) {
239
+ this.log.error('Cannot fetch visitor history: Not logged in or homeId is missing.');
240
+ return [];
241
+ }
242
+ const createDate = this.getDateTime();
243
+ const offset = 0;
244
+ const hashData = this.sha512(`IHRESTAPI${this.homeId}${offset}${createDate}`);
245
+ const response = await axios.get(`${BASE_URL}/v16/histories/${this.homeId}/video-histories`, {
246
+ params: { createDate, hashData, offset },
247
+ headers: { 'Authorization': `Bearer ${token}` },
248
+ });
249
+ return response.data.videoList || [];
195
250
  });
196
251
  }
197
252
  sha512(input) {
@@ -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.11",
4
+ "version": "1.0.13",
5
5
  "private": false,
6
6
  "description": "A Homebridge plugin for Samsung Smart Home",
7
7
  "author": "boozilla",
@@ -0,0 +1,29 @@
1
+ import { PlatformAccessory, Service } from 'homebridge';
2
+ import { ShomePlatform } from '../platform.js';
3
+
4
+ export class DoorbellAccessory {
5
+ private service: Service;
6
+
7
+ constructor(
8
+ private readonly platform: ShomePlatform,
9
+ private readonly accessory: PlatformAccessory,
10
+ ) {
11
+
12
+ // set accessory information
13
+ this.accessory.getService(this.platform.Service.AccessoryInformation)!
14
+ .setCharacteristic(this.platform.Characteristic.Manufacturer, 'sHome')
15
+ .setCharacteristic(this.platform.Characteristic.Model, 'sHome Doorbell')
16
+ .setCharacteristic(this.platform.Characteristic.SerialNumber, 'shome-doorbell');
17
+ this.service = this.accessory.getService(this.platform.Service.StatelessProgrammableSwitch)
18
+ || this.accessory.addService(this.platform.Service.StatelessProgrammableSwitch);
19
+ this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
20
+ this.service.getCharacteristic(this.platform.Characteristic.ProgrammableSwitchEvent);
21
+ }
22
+
23
+ public ring(visitorLabel: string) {
24
+ this.platform.log.info(`Triggered Doorbell event from [${visitorLabel}]`);
25
+
26
+ this.service.getCharacteristic(this.platform.Characteristic.ProgrammableSwitchEvent)
27
+ .updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
28
+ }
29
+ }
package/src/platform.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  import { API, Characteristic, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service } from 'homebridge';
2
2
  import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
3
- import { ShomeClient, MainDevice, SubDevice } from './shomeClient.js';
3
+ import { ShomeClient, MainDevice, SubDevice, Visitor } from './shomeClient.js';
4
4
  import { LightAccessory } from './accessories/lightAccessory.js';
5
5
  import { VentilatorAccessory } from './accessories/ventilatorAccessory.js';
6
6
  import { HeaterAccessory } from './accessories/heaterAccessory.js';
7
7
  import { DoorlockAccessory } from './accessories/doorlockAccessory.js';
8
+ import { DoorbellAccessory } from './accessories/doorbellAccessory.js';
8
9
 
9
10
  const CONTROLLABLE_MULTI_DEVICE_TYPES = ['LIGHT', 'HEATER', 'VENTILATOR'];
10
11
  const SPECIAL_CONTROLLABLE_TYPES = ['DOORLOCK'];
11
12
 
12
- // Define a type for our accessory handlers
13
- type AccessoryHandler = LightAccessory | VentilatorAccessory | HeaterAccessory | DoorlockAccessory;
13
+ type AccessoryHandler = LightAccessory | VentilatorAccessory | HeaterAccessory | DoorlockAccessory | DoorbellAccessory;
14
14
 
15
15
  export class ShomePlatform implements DynamicPlatformPlugin {
16
16
  public readonly Service: typeof Service;
@@ -18,8 +18,10 @@ export class ShomePlatform implements DynamicPlatformPlugin {
18
18
  public readonly accessories: PlatformAccessory[] = [];
19
19
  public readonly shomeClient: ShomeClient;
20
20
  private readonly accessoryHandlers = new Map<string, AccessoryHandler>();
21
- private pollingInterval: number;
21
+ private readonly pollingInterval: number;
22
+
22
23
  private pollingTimer?: NodeJS.Timeout;
24
+ private lastCheckedTimestamp: Date = new Date();
23
25
 
24
26
  constructor(
25
27
  public readonly log: Logger,
@@ -44,6 +46,8 @@ export class ShomePlatform implements DynamicPlatformPlugin {
44
46
  this.config.deviceId,
45
47
  );
46
48
 
49
+ this.log.info(`Doorbell notification service is active. Baseline time: ${this.lastCheckedTimestamp.toISOString()}`);
50
+
47
51
  this.api.on('didFinishLaunching', () => {
48
52
  this.discoverDevices();
49
53
  if (this.pollingInterval > 0) {
@@ -72,10 +76,6 @@ export class ShomePlatform implements DynamicPlatformPlugin {
72
76
  const devices = await this.shomeClient.getDeviceList();
73
77
  const foundAccessories: PlatformAccessory[] = [];
74
78
 
75
- if (!devices || devices.length === 0) {
76
- this.log.warn('No devices found on your sHome account.');
77
- }
78
-
79
79
  for (const device of devices) {
80
80
  if (CONTROLLABLE_MULTI_DEVICE_TYPES.includes(device.thngModelTypeName)) {
81
81
  const deviceInfoList = await this.shomeClient.getDeviceInfo(device.thngId, device.thngModelTypeName);
@@ -90,11 +90,14 @@ export class ShomePlatform implements DynamicPlatformPlugin {
90
90
  const uuid = this.api.hap.uuid.generate(device.thngId);
91
91
  const accessory = this.setupAccessory(device, null, uuid);
92
92
  foundAccessories.push(accessory);
93
- } else {
94
- this.log.info(`Ignoring device: ${device.nickname} (Type: ${device.thngModelTypeName})`);
95
93
  }
96
94
  }
97
95
 
96
+ const doorbellUUID = this.api.hap.uuid.generate('shome-doorbell-accessory');
97
+ const doorbellDevice = { thngModelTypeName: 'DOORBELL', nickname: 'Doorbell', thngId: 'shome-doorbell' } as MainDevice;
98
+ const doorbellAccessory = this.setupAccessory(doorbellDevice, null, doorbellUUID);
99
+ foundAccessories.push(doorbellAccessory);
100
+
98
101
  const accessoriesToRemove = this.accessories.filter(cachedAccessory =>
99
102
  !foundAccessories.some(foundAccessory => foundAccessory.UUID === cachedAccessory.UUID),
100
103
  );
@@ -114,29 +117,31 @@ export class ShomePlatform implements DynamicPlatformPlugin {
114
117
  const displayName = subDevice ? subDevice.nickname : mainDevice.nickname;
115
118
  const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
116
119
 
120
+ const accessory = existingAccessory ?? new this.api.platformAccessory(displayName, uuid);
121
+ accessory.context.device = mainDevice;
122
+ accessory.context.subDevice = subDevice;
123
+
117
124
  if (existingAccessory) {
118
125
  this.log.info('Restoring existing accessory from cache:', displayName);
119
- existingAccessory.context.device = mainDevice;
120
- existingAccessory.context.subDevice = subDevice;
121
- this.createAccessory(existingAccessory);
122
- return existingAccessory;
123
126
  } else {
124
127
  this.log.info('Adding new accessory:', displayName);
125
- const accessory = new this.api.platformAccessory(displayName, uuid);
126
- accessory.context.device = mainDevice;
127
- accessory.context.subDevice = subDevice;
128
- this.createAccessory(accessory);
129
128
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
130
- return accessory;
131
129
  }
130
+
131
+ this.createAccessoryHandler(accessory);
132
+ return accessory;
132
133
  }
133
134
 
134
- createAccessory(accessory: PlatformAccessory) {
135
+ createAccessoryHandler(accessory: PlatformAccessory) {
135
136
  const device = accessory.context.device;
136
137
  const accessoryType = device.thngModelTypeName;
137
138
 
138
- if (!this.accessoryHandlers.has(accessory.UUID)) {
139
- switch (accessoryType) {
139
+ if (this.accessoryHandlers.has(accessory.UUID)) {
140
+ return;
141
+ }
142
+
143
+ if (accessory.context.subDevice) {
144
+ switch (device.thngModelTypeName) {
140
145
  case 'LIGHT':
141
146
  this.accessoryHandlers.set(accessory.UUID, new LightAccessory(this, accessory));
142
147
  break;
@@ -146,42 +151,107 @@ export class ShomePlatform implements DynamicPlatformPlugin {
146
151
  case 'HEATER':
147
152
  this.accessoryHandlers.set(accessory.UUID, new HeaterAccessory(this, accessory));
148
153
  break;
149
- case 'DOORLOCK':
150
- this.accessoryHandlers.set(accessory.UUID, new DoorlockAccessory(this, accessory));
151
- break;
152
154
  }
155
+ return;
156
+ }
157
+
158
+ switch (accessoryType) {
159
+ case 'DOORLOCK':
160
+ this.accessoryHandlers.set(accessory.UUID, new DoorlockAccessory(this, accessory));
161
+ break;
162
+ case 'DOORBELL':
163
+ this.accessoryHandlers.set(accessory.UUID, new DoorbellAccessory(this, accessory));
164
+ break;
153
165
  }
154
166
  }
155
167
 
156
168
  startPolling() {
157
- this.log.info(`Starting periodic device state polling every ${this.pollingInterval / 1000} seconds.`);
169
+ this.log.info(`Starting periodic state polling every ${this.pollingInterval / 1000} seconds.`);
158
170
  this.pollingTimer = setInterval(async () => {
159
- this.log.debug('Polling for device updates...');
171
+ this.log.debug('Polling for updates...');
160
172
  try {
161
- const devices = await this.shomeClient.getDeviceList();
162
- for (const device of devices) {
163
- if (CONTROLLABLE_MULTI_DEVICE_TYPES.includes(device.thngModelTypeName)) {
164
- const deviceInfoList = await this.shomeClient.getDeviceInfo(device.thngId, device.thngModelTypeName);
165
- if (deviceInfoList) {
166
- for (const subDevice of deviceInfoList) {
167
- const subUuid = this.api.hap.uuid.generate(`${device.thngId}-${subDevice.deviceId}`);
168
- const handler = this.accessoryHandlers.get(subUuid) as LightAccessory | HeaterAccessory | VentilatorAccessory;
169
- if (handler) {
170
- handler.updateState(subDevice);
171
- }
172
- }
173
+ await this.pollDeviceUpdates();
174
+ await this.checkForNewVisitors();
175
+ } catch (error) {
176
+ this.log.error('An error occurred during polling:', error);
177
+ }
178
+ }, this.pollingInterval);
179
+ }
180
+
181
+ async pollDeviceUpdates() {
182
+ const devices = await this.shomeClient.getDeviceList();
183
+ for (const device of devices) {
184
+ if (CONTROLLABLE_MULTI_DEVICE_TYPES.includes(device.thngModelTypeName)) {
185
+ const deviceInfoList = await this.shomeClient.getDeviceInfo(device.thngId, device.thngModelTypeName);
186
+ if (deviceInfoList) {
187
+ for (const subDevice of deviceInfoList) {
188
+ const deviceId = `${device.thngId}-${subDevice.deviceId}`;
189
+ if (this.shomeClient.isDeviceBusy(deviceId)) {
190
+ this.log.debug(`Skipping polling update for ${subDevice.nickname} as it has a pending request.`);
191
+ continue;
173
192
  }
174
- } else if (SPECIAL_CONTROLLABLE_TYPES.includes(device.thngModelTypeName)) {
175
- const uuid = this.api.hap.uuid.generate(device.thngId);
176
- const handler = this.accessoryHandlers.get(uuid) as DoorlockAccessory;
193
+ const subUuid = this.api.hap.uuid.generate(deviceId);
194
+ const handler = this.accessoryHandlers.get(subUuid) as LightAccessory | HeaterAccessory | VentilatorAccessory;
177
195
  if (handler) {
178
- handler.updateState(device);
196
+ handler.updateState(subDevice);
179
197
  }
180
198
  }
181
199
  }
182
- } catch (error) {
183
- this.log.error('An error occurred during polling:', error);
200
+ } else if (SPECIAL_CONTROLLABLE_TYPES.includes(device.thngModelTypeName)) {
201
+ const deviceId = device.thngId;
202
+ if (this.shomeClient.isDeviceBusy(deviceId)) {
203
+ this.log.debug(`Skipping polling update for ${device.nickname} as it has a pending request.`);
204
+ continue;
205
+ }
206
+ const uuid = this.api.hap.uuid.generate(deviceId);
207
+ const handler = this.accessoryHandlers.get(uuid) as DoorlockAccessory;
208
+ if (handler) {
209
+ handler.updateState(device);
210
+ }
184
211
  }
185
- }, this.pollingInterval);
212
+ }
213
+ }
214
+
215
+ async checkForNewVisitors() {
216
+ this.log.debug('Checking for new doorbell events...');
217
+ const visitorList = await this.shomeClient.getVisitorHistory();
218
+ const newVisitors: Visitor[] = [];
219
+
220
+ for (const visitor of visitorList) {
221
+ const visitorTime = this.parseRecodDt(visitor.recodDt);
222
+ if (visitorTime > this.lastCheckedTimestamp) {
223
+ newVisitors.push(visitor);
224
+ }
225
+ }
226
+
227
+ if (newVisitors.length > 0) {
228
+ this.log.info(`Found ${newVisitors.length} new doorbell event(s).`);
229
+ newVisitors.sort((a, b) => a.recodDt.localeCompare(b.recodDt));
230
+
231
+ const doorbellUUID = this.api.hap.uuid.generate('shome-doorbell-accessory');
232
+ const doorbellHandler = this.accessoryHandlers.get(doorbellUUID) as DoorbellAccessory | undefined;
233
+
234
+ if (doorbellHandler) {
235
+ for (const visitor of newVisitors) {
236
+ doorbellHandler.ring(visitor.deviceLabel);
237
+ }
238
+ } else {
239
+ this.log.warn('Doorbell accessory handler not found.');
240
+ }
241
+
242
+ const latestVisitor = newVisitors[newVisitors.length - 1];
243
+ this.lastCheckedTimestamp = this.parseRecodDt(latestVisitor.recodDt);
244
+ this.log.debug(`Updated last checked timestamp to: ${this.lastCheckedTimestamp.toISOString()}`);
245
+ }
246
+ }
247
+
248
+ private parseRecodDt(recodDt: string): Date {
249
+ const year = parseInt(recodDt.substring(0, 4), 10);
250
+ const month = parseInt(recodDt.substring(4, 6), 10) - 1;
251
+ const day = parseInt(recodDt.substring(6, 8), 10);
252
+ const hour = parseInt(recodDt.substring(8, 10), 10);
253
+ const minute = parseInt(recodDt.substring(10, 12), 10);
254
+ const second = parseInt(recodDt.substring(12, 14), 10);
255
+ return new Date(year, month, day, hour, minute, second);
186
256
  }
187
257
  }
@@ -24,20 +24,30 @@ export interface SubDevice {
24
24
  [key: string]: unknown;
25
25
  }
26
26
 
27
+ export interface Visitor {
28
+ sttId: string;
29
+ thumbNail: string;
30
+ recodDt: string;
31
+ deviceLabel: string;
32
+ }
33
+
27
34
  type QueueTask<T = unknown> = {
28
35
  request: () => Promise<T>;
29
36
  resolve: (value: T | PromiseLike<T>) => void;
30
37
  reject: (reason?: unknown) => void;
31
- authRetry: boolean;
38
+ deviceId?: string;
32
39
  };
33
40
 
34
41
  export class ShomeClient {
35
42
  private cachedAccessToken: string | null = null;
36
43
  private ihdId: string | null = null;
44
+ private homeId: string | null = null;
37
45
  private tokenExpiry: number = 0;
38
46
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- private requestQueue: QueueTask<any>[] = [];
40
- private isProcessing = false;
47
+ private putQueue: QueueTask<any>[] = [];
48
+ private isProcessingPut = false;
49
+ private loginPromise: Promise<string | null> | null = null;
50
+ private pendingPutRequests = new Set<string>();
41
51
 
42
52
  constructor(
43
53
  private readonly log: Logger,
@@ -47,67 +57,30 @@ export class ShomeClient {
47
57
  ) {
48
58
  }
49
59
 
50
- private enqueue<T>(request: () => Promise<T>): Promise<T> {
51
- return new Promise<T>((resolve, reject) => {
52
- this.requestQueue.push({ request, resolve, reject, authRetry: false });
53
- this.processQueue();
54
- });
55
- }
56
-
57
- private async processQueue(): Promise<void> {
58
- if (this.isProcessing) {
59
- return; // A processing loop is already running
60
+ private login(): Promise<string | null> {
61
+ if (!this.isTokenExpired()) {
62
+ return Promise.resolve(this.cachedAccessToken);
60
63
  }
61
- this.isProcessing = true;
62
64
 
63
- while (this.requestQueue.length > 0) {
64
- const task = this.requestQueue.shift()!;
65
- try {
66
- const result = await this.executeTaskWithRetries(task);
67
- task.resolve(result);
68
- } catch (error) {
69
- task.reject(error);
70
- }
71
- // Add a delay between requests to avoid overwhelming the server
72
- await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS));
65
+ if (this.loginPromise) {
66
+ return this.loginPromise;
73
67
  }
74
68
 
75
- this.isProcessing = false;
76
- }
77
-
78
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
- private async executeTaskWithRetries(task: QueueTask<any>): Promise<any> {
80
- let retries = 0;
81
- while (true) {
82
- try {
83
- const result = await task.request();
84
- return result;
85
- } catch (error) {
86
- const isAuthError = axios.isAxiosError(error) && error.response?.status === 401;
87
-
88
- if (isAuthError && !task.authRetry) {
89
- this.log.warn('API authentication failed (401). Retrying after refreshing token.');
90
- this.cachedAccessToken = null;
91
- this.tokenExpiry = 0;
92
- task.authRetry = true;
93
- continue; // Immediately retry the request
94
- }
95
-
96
- if (retries >= MAX_RETRIES) {
97
- this.log.error(`Request failed after ${MAX_RETRIES} retries. Giving up.`, error);
98
- throw error; // Throw final error
99
- }
69
+ this.loginPromise = new Promise((resolve, reject) => {
70
+ this.putQueue.unshift({ // Prioritize login by adding to the front of the queue
71
+ request: () => this.performLogin(),
72
+ resolve,
73
+ reject,
74
+ });
75
+ this.processPutQueue();
76
+ });
100
77
 
101
- retries++;
102
- const backoffTime = INITIAL_BACKOFF_MS * Math.pow(2, retries - 1);
103
- this.log.warn(`Request failed. Retrying in ${backoffTime}ms... (Attempt ${retries}/${MAX_RETRIES})`);
104
- await new Promise(resolve => setTimeout(resolve, backoffTime));
105
- }
106
- }
107
- }
78
+ // Clean up the promise once it's settled
79
+ this.loginPromise.finally(() => {
80
+ this.loginPromise = null;
81
+ });
108
82
 
109
- async login(): Promise<string | null> {
110
- return this.enqueue(() => this.performLogin());
83
+ return this.loginPromise;
111
84
  }
112
85
 
113
86
  private async performLogin(): Promise<string | null> {
@@ -137,6 +110,7 @@ export class ShomeClient {
137
110
  if (response.data && response.data.accessToken) {
138
111
  this.cachedAccessToken = response.data.accessToken;
139
112
  this.ihdId = response.data.ihdId;
113
+ this.homeId = response.data.homeId;
140
114
 
141
115
  const payload = JSON.parse(Buffer.from(this.cachedAccessToken!.split('.')[1], 'base64').toString());
142
116
  this.tokenExpiry = payload.exp * 1000;
@@ -153,9 +127,81 @@ export class ShomeClient {
153
127
  }
154
128
  }
155
129
 
130
+ private enqueuePut<T>(request: () => Promise<T>, deviceId?: string): Promise<T> {
131
+ return new Promise<T>((resolve, reject) => {
132
+ this.putQueue.push({ request, resolve, reject, deviceId });
133
+ this.processPutQueue();
134
+ });
135
+ }
136
+
137
+ private async processPutQueue(): Promise<void> {
138
+ if (this.isProcessingPut) {
139
+ return;
140
+ }
141
+ this.isProcessingPut = true;
142
+
143
+ while (this.putQueue.length > 0) {
144
+ const task = this.putQueue.shift()!;
145
+ if (task.deviceId) {
146
+ this.pendingPutRequests.add(task.deviceId);
147
+ }
148
+ try {
149
+ const result = await this.executeWithRetries(task.request, true);
150
+ task.resolve(result);
151
+ } catch (error) {
152
+ task.reject(error);
153
+ } finally {
154
+ if (task.deviceId) {
155
+ this.pendingPutRequests.delete(task.deviceId);
156
+ }
157
+ }
158
+ await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY_MS));
159
+ }
160
+
161
+ this.isProcessingPut = false;
162
+ }
163
+
164
+ private async executeWithRetries<T>(request: () => Promise<T>, isQueued = false): Promise<T> {
165
+ let retries = 0;
166
+ while (true) {
167
+ try {
168
+ if (!isQueued) {
169
+ await this.login();
170
+ }
171
+ return await request();
172
+ } catch (error) {
173
+ const isAuthError = axios.isAxiosError(error) && error.response?.status === 401;
174
+
175
+ if (isAuthError) {
176
+ this.log.warn('API authentication failed (401). Invalidating token.');
177
+ this.cachedAccessToken = null;
178
+ this.tokenExpiry = 0;
179
+ }
180
+
181
+ if (retries >= MAX_RETRIES) {
182
+ this.log.error(`Request failed after ${MAX_RETRIES} retries. Giving up.`, error);
183
+ throw error;
184
+ }
185
+
186
+ retries++;
187
+ const backoffTime = INITIAL_BACKOFF_MS * Math.pow(2, retries - 1);
188
+ if (!isAuthError) {
189
+ this.log.warn(`Request failed. Retrying in ${backoffTime}ms... (Attempt ${retries}/${MAX_RETRIES})`);
190
+ }
191
+ await new Promise(resolve => setTimeout(resolve, backoffTime));
192
+
193
+ await this.login();
194
+ }
195
+ }
196
+ }
197
+
198
+ public isDeviceBusy(deviceId: string): boolean {
199
+ return this.pendingPutRequests.has(deviceId);
200
+ }
201
+
156
202
  async getDeviceList(): Promise<MainDevice[]> {
157
- return this.enqueue(async () => {
158
- const token = await this.performLogin();
203
+ return this.executeWithRetries(async () => {
204
+ const token = this.cachedAccessToken;
159
205
  if (!token || !this.ihdId) {
160
206
  return [];
161
207
  }
@@ -173,8 +219,8 @@ export class ShomeClient {
173
219
  }
174
220
 
175
221
  async getDeviceInfo(thingId: string, type: string): Promise<SubDevice[] | null> {
176
- return this.enqueue(async () => {
177
- const token = await this.performLogin();
222
+ return this.executeWithRetries(async () => {
223
+ const token = this.cachedAccessToken;
178
224
  if (!token) {
179
225
  return null;
180
226
  }
@@ -192,19 +238,20 @@ export class ShomeClient {
192
238
  });
193
239
  }
194
240
 
195
- async setDevice(thingId: string, deviceId: string, type: string, controlType: string, state: string, nickname?: string): Promise<boolean> {
196
- return this.enqueue(async () => {
197
- const token = await this.performLogin();
241
+ async setDevice(thingId: string, subDeviceId: string, type: string, controlType: string, state: string, nickname?: string): Promise<boolean> {
242
+ const deviceId = `${thingId}-${subDeviceId}`;
243
+ const request = async () => {
244
+ const token = this.cachedAccessToken;
198
245
  if (!token) {
199
246
  return false;
200
247
  }
201
248
 
202
249
  const createDate = this.getDateTime();
203
- const hashData = this.sha512(`IHRESTAPI${thingId}${deviceId}${state}${createDate}`);
250
+ const hashData = this.sha512(`IHRESTAPI${thingId}${subDeviceId}${state}${createDate}`);
204
251
  const typePath = type.toLowerCase().replace(/_/g, '');
205
252
  const controlPath = controlType.toLowerCase().replace(/_/g, '-');
206
253
 
207
- await axios.put(`${BASE_URL}/v18/settings/${typePath}/${thingId}/${deviceId}/${controlPath}`, null, {
254
+ await axios.put(`${BASE_URL}/v18/settings/${typePath}/${thingId}/${subDeviceId}/${controlPath}`, null, {
208
255
  params: {
209
256
  createDate,
210
257
  [controlType === 'WINDSPEED' ? 'mode' : 'state']: state,
@@ -213,15 +260,18 @@ export class ShomeClient {
213
260
  headers: { 'Authorization': `Bearer ${token}` },
214
261
  });
215
262
 
216
- const displayName = nickname || `${thingId}/${deviceId}`;
263
+ const displayName = nickname || deviceId;
217
264
  this.log.info(`[${displayName}] state set to ${state}.`);
218
265
  return true;
219
- });
266
+ };
267
+
268
+ return this.enqueuePut(request, deviceId);
220
269
  }
221
270
 
222
271
  async unlockDoorlock(thingId: string, nickname?: string): Promise<boolean> {
223
- return this.enqueue(async () => {
224
- const token = await this.performLogin();
272
+ const deviceId = thingId;
273
+ const request = async () => {
274
+ const token = this.cachedAccessToken;
225
275
  if (!token) {
226
276
  return false;
227
277
  }
@@ -241,9 +291,31 @@ export class ShomeClient {
241
291
  const displayName = nickname || thingId;
242
292
  this.log.info(`Unlocked [${displayName}].`);
243
293
  return true;
294
+ };
295
+ return this.enqueuePut(request, deviceId);
296
+ }
297
+
298
+ async getVisitorHistory(): Promise<Visitor[]> {
299
+ return this.executeWithRetries(async () => {
300
+ const token = this.cachedAccessToken;
301
+ if (!token || !this.homeId) {
302
+ this.log.error('Cannot fetch visitor history: Not logged in or homeId is missing.');
303
+ return [];
304
+ }
305
+
306
+ const createDate = this.getDateTime();
307
+ const offset = 0;
308
+ const hashData = this.sha512(`IHRESTAPI${this.homeId}${offset}${createDate}`);
309
+ const response = await axios.get(`${BASE_URL}/v16/histories/${this.homeId}/video-histories`, {
310
+ params: { createDate, hashData, offset },
311
+ headers: { 'Authorization': `Bearer ${token}` },
312
+ });
313
+
314
+ return response.data.videoList || [];
244
315
  });
245
316
  }
246
317
 
318
+
247
319
  private sha512(input: string): string {
248
320
  return CryptoJS.SHA512(input).toString();
249
321
  }