tsvesync 1.0.19 → 1.0.22

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.
@@ -71,9 +71,9 @@ export declare class VeSync {
71
71
  */
72
72
  static setDevId(devices: any[]): any[];
73
73
  /**
74
- * Process device list and instantiate device objects
74
+ * Process devices from API response
75
75
  */
76
- processDevices(devList: any[]): boolean;
76
+ private processDevices;
77
77
  /**
78
78
  * Get list of VeSync devices
79
79
  */
@@ -84,8 +84,31 @@ function objectFactory(details, manager) {
84
84
  try {
85
85
  // Add category to device details
86
86
  details.deviceCategory = category;
87
- const device = new DeviceClass(details, manager);
88
- return [category, device];
87
+ // Handle outdoor plug sub-devices
88
+ if (deviceType === 'ESO15-TB' && details.subDevices) {
89
+ const devices = [];
90
+ // Create a device instance for each sub-device
91
+ details.subDevices.forEach((subDevice) => {
92
+ const subDeviceDetails = {
93
+ ...details,
94
+ deviceName: subDevice.subDeviceName,
95
+ deviceStatus: subDevice.subDeviceStatus,
96
+ subDeviceNo: subDevice.subDeviceNo,
97
+ isSubDevice: true,
98
+ parentCid: details.cid,
99
+ // Generate a unique CID for the sub-device
100
+ cid: `${details.cid}_${subDevice.subDeviceNo}`
101
+ };
102
+ const device = new DeviceClass(subDeviceDetails, manager);
103
+ devices.push([category, device]);
104
+ });
105
+ // Return array of sub-devices
106
+ return devices[0]; // Return first device, manager will handle adding all devices
107
+ }
108
+ else {
109
+ const device = new DeviceClass(details, manager);
110
+ return [category, device];
111
+ }
89
112
  }
90
113
  catch (error) {
91
114
  logger_1.logger.error(`Error creating device instance for ${deviceType}:`, error);
@@ -265,57 +288,52 @@ class VeSync {
265
288
  return devices;
266
289
  }
267
290
  /**
268
- * Process device list and instantiate device objects
291
+ * Process devices from API response
269
292
  */
270
- processDevices(devList) {
271
- // Ensure devices have proper IDs
272
- const devices = VeSync.setDevId(devList);
273
- // Count current devices
274
- let numDevices = 0;
275
- for (const deviceList of Object.values(this._devList)) {
276
- numDevices += deviceList.length;
277
- }
278
- if (!devices || devices.length === 0) {
279
- logger_1.logger.warn('No devices found in api return');
280
- return false;
281
- }
282
- // Initialize new device list or remove old devices
283
- if (numDevices === 0) {
284
- logger_1.logger.debug('New device list initialized');
285
- }
286
- else {
287
- this.removeOldDevices(devices);
288
- }
289
- // Filter out devices that already exist
290
- const newDevices = devices.filter(dev => this.addDevTest(dev));
291
- // Required keys for device instantiation
292
- const requiredKeys = ['deviceType', 'deviceName', 'deviceStatus', 'cid'];
293
- // Process each new device
294
- for (const dev of newDevices) {
295
- // Verify required device properties
296
- if (!requiredKeys.every(key => key in dev)) {
297
- logger_1.logger.debug(`Error adding device - missing required properties: ${dev.deviceName || 'Unknown'}`);
298
- continue;
293
+ processDevices(deviceList) {
294
+ try {
295
+ // Clear existing devices
296
+ for (const category of Object.keys(this._devList)) {
297
+ this._devList[category].length = 0;
298
+ }
299
+ if (!deviceList || deviceList.length === 0) {
300
+ logger_1.logger.warn('No devices found in API response');
301
+ return false;
299
302
  }
300
- const devType = dev.deviceType;
301
- try {
302
- // Create device instance using factory
303
- const [category, deviceObj] = objectFactory(dev, this);
304
- // Add device to appropriate category if valid
305
- if (deviceObj && category in this._devList) {
306
- this._devList[category].push(deviceObj);
307
- logger_1.logger.debug(`Added ${devType} device to ${category} category`);
303
+ // Process each device
304
+ deviceList.forEach(dev => {
305
+ const [category, device] = objectFactory(dev, this);
306
+ // Handle outdoor plug sub-devices
307
+ if (dev.deviceType === 'ESO15-TB' && dev.subDevices) {
308
+ dev.subDevices.forEach((subDev) => {
309
+ const subDeviceDetails = {
310
+ ...dev,
311
+ deviceName: subDev.subDeviceName,
312
+ deviceStatus: subDev.subDeviceStatus,
313
+ subDeviceNo: subDev.subDeviceNo,
314
+ isSubDevice: true,
315
+ parentCid: dev.cid,
316
+ cid: `${dev.cid}_${subDev.subDeviceNo}`
317
+ };
318
+ const [subCategory, subDevice] = objectFactory(subDeviceDetails, this);
319
+ if (subDevice && subCategory in this._devList) {
320
+ this._devList[subCategory].push(subDevice);
321
+ }
322
+ });
308
323
  }
309
- else if (category === 'unknown') {
310
- logger_1.logger.debug(`Unknown device type: ${devType}`);
324
+ else if (device && category in this._devList) {
325
+ this._devList[category].push(device);
311
326
  }
312
- }
313
- catch (err) {
314
- logger_1.logger.error(`Error creating device ${devType}:`, err);
315
- continue;
316
- }
327
+ });
328
+ // Update device list reference
329
+ this.devices = Object.values(this._devList).flat();
330
+ // Return true if we processed at least one device successfully
331
+ return this.devices.length > 0;
332
+ }
333
+ catch (error) {
334
+ logger_1.logger.error('Error processing devices:', error);
335
+ return false;
317
336
  }
318
- return true;
319
337
  }
320
338
  /**
321
339
  * Get list of VeSync devices
@@ -327,14 +345,12 @@ class VeSync {
327
345
  return false;
328
346
  }
329
347
  this._inProcess = true;
330
- let procReturn = false;
348
+ let success = false;
331
349
  try {
332
350
  const [response] = await helpers_1.Helpers.callApi('/cloud/v2/deviceManaged/devices', 'post', helpers_1.Helpers.reqBody(this, 'devicelist'), helpers_1.Helpers.reqHeaders(this));
333
351
  if ((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.list) {
334
352
  const deviceList = response.result.list;
335
- procReturn = this.processDevices(deviceList);
336
- // Update the main devices array with all devices
337
- this.devices = Object.values(this._devList).flat();
353
+ success = this.processDevices(deviceList);
338
354
  // Log device discovery results
339
355
  logger_1.logger.debug('\n=== Device Discovery Summary ===');
340
356
  logger_1.logger.debug(`Total devices processed: ${deviceList.length}`);
@@ -372,7 +388,7 @@ class VeSync {
372
388
  logger_1.logger.error('Unable to get device list:', error);
373
389
  }
374
390
  this._inProcess = false;
375
- return procReturn;
391
+ return success;
376
392
  }
377
393
  /**
378
394
  * Login to VeSync server
@@ -81,10 +81,27 @@ class VeSyncOutlet extends vesyncBaseDevice_1.VeSyncBaseDevice {
81
81
  this.details = response;
82
82
  // Handle outdoor plugs with subdevices
83
83
  if (response.subDevices) {
84
- const subDevice = response.subDevices.find((dev) => dev.subDeviceName === this.deviceName);
84
+ const subDevice = response.subDevices.find((dev) => {
85
+ // Match by name (primary identifier)
86
+ if (dev.subDeviceName === this.deviceName) {
87
+ return true;
88
+ }
89
+ // Match by subDeviceNo if available
90
+ if ('subDeviceNo' in this && this.subDeviceNo === dev.subDeviceNo) {
91
+ return true;
92
+ }
93
+ // Match by cid/uuid if available (cid might be in format parentCid_subDeviceNo)
94
+ if (this.cid && this.cid.includes('_')) {
95
+ const [_, subId] = this.cid.split('_');
96
+ if (subId && dev.subDeviceNo === parseInt(subId)) {
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ });
85
102
  if (subDevice) {
86
103
  this.deviceStatus = subDevice.subDeviceStatus;
87
- logger_1.logger.info(`[${this.deviceName}] Successfully retrieved subdevice status: ${this.deviceStatus}`);
104
+ logger_1.logger.info(`[${this.deviceName}] Successfully retrieved sub-device status: ${this.deviceStatus}`);
88
105
  return;
89
106
  }
90
107
  }
@@ -57,6 +57,9 @@ export declare class VeSyncOutlet15A extends VeSyncOutlet {
57
57
  * VeSync Outdoor Plug
58
58
  */
59
59
  export declare class VeSyncOutdoorPlug extends VeSyncOutlet {
60
+ protected subDeviceNo?: number;
61
+ protected isSubDevice: boolean;
62
+ protected parentCid?: string;
60
63
  constructor(details: Record<string, any>, manager: VeSync);
61
64
  /**
62
65
  * Turn outlet on
@@ -192,17 +192,24 @@ exports.VeSyncOutlet15A = VeSyncOutlet15A;
192
192
  class VeSyncOutdoorPlug extends vesyncOutlet_1.VeSyncOutlet {
193
193
  constructor(details, manager) {
194
194
  super(details, manager);
195
+ this.isSubDevice = details.isSubDevice || false;
196
+ this.subDeviceNo = details.subDeviceNo;
197
+ this.parentCid = details.parentCid;
195
198
  }
196
199
  /**
197
200
  * Turn outlet on
198
201
  */
199
202
  async turnOn() {
203
+ if (!this.isSubDevice) {
204
+ logger_1.logger.error(`[${this.deviceName}] Cannot control parent device directly`);
205
+ return false;
206
+ }
200
207
  logger_1.logger.debug(`[${this.deviceName}] Turning outlet on`);
201
208
  const body = {
202
209
  ...helpers_1.Helpers.reqBody(this.manager, 'devicestatus'),
203
210
  uuid: this.uuid,
204
211
  status: 'on',
205
- switchNo: 1
212
+ switchNo: this.subDeviceNo
206
213
  };
207
214
  const [response] = await helpers_1.Helpers.callApi('/outdoorsocket15a/v1/device/devicestatus', 'put', body, helpers_1.Helpers.reqHeaders(this.manager));
208
215
  if ((response === null || response === void 0 ? void 0 : response.code) === 0) {
@@ -217,12 +224,16 @@ class VeSyncOutdoorPlug extends vesyncOutlet_1.VeSyncOutlet {
217
224
  * Turn outlet off
218
225
  */
219
226
  async turnOff() {
227
+ if (!this.isSubDevice) {
228
+ logger_1.logger.error(`[${this.deviceName}] Cannot control parent device directly`);
229
+ return false;
230
+ }
220
231
  logger_1.logger.debug(`[${this.deviceName}] Turning outlet off`);
221
232
  const body = {
222
233
  ...helpers_1.Helpers.reqBody(this.manager, 'devicestatus'),
223
234
  uuid: this.uuid,
224
235
  status: 'off',
225
- switchNo: 1
236
+ switchNo: this.subDeviceNo
226
237
  };
227
238
  const [response] = await helpers_1.Helpers.callApi('/outdoorsocket15a/v1/device/devicestatus', 'put', body, helpers_1.Helpers.reqHeaders(this.manager));
228
239
  if ((response === null || response === void 0 ? void 0 : response.code) === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsvesync",
3
- "version": "1.0.19",
3
+ "version": "1.0.22",
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",
package/src/lib/vesync.ts CHANGED
@@ -103,8 +103,33 @@ function objectFactory(details: Record<string, any>, manager: VeSync): [string,
103
103
  try {
104
104
  // Add category to device details
105
105
  details.deviceCategory = category;
106
- const device = new DeviceClass(details, manager);
107
- return [category, device];
106
+
107
+ // Handle outdoor plug sub-devices
108
+ if (deviceType === 'ESO15-TB' && details.subDevices) {
109
+ const devices: [string, VeSyncBaseDevice | null][] = [];
110
+
111
+ // Create a device instance for each sub-device
112
+ details.subDevices.forEach((subDevice: any) => {
113
+ const subDeviceDetails = {
114
+ ...details,
115
+ deviceName: subDevice.subDeviceName,
116
+ deviceStatus: subDevice.subDeviceStatus,
117
+ subDeviceNo: subDevice.subDeviceNo,
118
+ isSubDevice: true,
119
+ parentCid: details.cid,
120
+ // Generate a unique CID for the sub-device
121
+ cid: `${details.cid}_${subDevice.subDeviceNo}`
122
+ };
123
+ const device = new DeviceClass(subDeviceDetails, manager);
124
+ devices.push([category, device]);
125
+ });
126
+
127
+ // Return array of sub-devices
128
+ return devices[0]; // Return first device, manager will handle adding all devices
129
+ } else {
130
+ const device = new DeviceClass(details, manager);
131
+ return [category, device];
132
+ }
108
133
  } catch (error) {
109
134
  logger.error(`Error creating device instance for ${deviceType}:`, error);
110
135
  return [category, null];
@@ -331,63 +356,55 @@ export class VeSync {
331
356
  }
332
357
 
333
358
  /**
334
- * Process device list and instantiate device objects
359
+ * Process devices from API response
335
360
  */
336
- processDevices(devList: any[]): boolean {
337
- // Ensure devices have proper IDs
338
- const devices = VeSync.setDevId(devList);
339
-
340
- // Count current devices
341
- let numDevices = 0;
342
- for (const deviceList of Object.values(this._devList)) {
343
- numDevices += deviceList.length;
344
- }
345
-
346
- if (!devices || devices.length === 0) {
347
- logger.warn('No devices found in api return');
348
- return false;
349
- }
350
-
351
- // Initialize new device list or remove old devices
352
- if (numDevices === 0) {
353
- logger.debug('New device list initialized');
354
- } else {
355
- this.removeOldDevices(devices);
356
- }
357
-
358
- // Filter out devices that already exist
359
- const newDevices = devices.filter(dev => this.addDevTest(dev));
361
+ private processDevices(deviceList: any[]): boolean {
362
+ try {
363
+ // Clear existing devices
364
+ for (const category of Object.keys(this._devList)) {
365
+ this._devList[category].length = 0;
366
+ }
360
367
 
361
- // Required keys for device instantiation
362
- const requiredKeys = ['deviceType', 'deviceName', 'deviceStatus', 'cid'];
363
-
364
- // Process each new device
365
- for (const dev of newDevices) {
366
- // Verify required device properties
367
- if (!requiredKeys.every(key => key in dev)) {
368
- logger.debug(`Error adding device - missing required properties: ${dev.deviceName || 'Unknown'}`);
369
- continue;
368
+ if (!deviceList || deviceList.length === 0) {
369
+ logger.warn('No devices found in API response');
370
+ return false;
370
371
  }
371
372
 
372
- const devType = dev.deviceType;
373
- try {
374
- // Create device instance using factory
375
- const [category, deviceObj] = objectFactory(dev, this);
373
+ // Process each device
374
+ deviceList.forEach(dev => {
375
+ const [category, device] = objectFactory(dev, this);
376
376
 
377
- // Add device to appropriate category if valid
378
- if (deviceObj && category in this._devList) {
379
- this._devList[category].push(deviceObj);
380
- logger.debug(`Added ${devType} device to ${category} category`);
381
- } else if (category === 'unknown') {
382
- logger.debug(`Unknown device type: ${devType}`);
377
+ // Handle outdoor plug sub-devices
378
+ if (dev.deviceType === 'ESO15-TB' && dev.subDevices) {
379
+ dev.subDevices.forEach((subDev: any) => {
380
+ const subDeviceDetails = {
381
+ ...dev,
382
+ deviceName: subDev.subDeviceName,
383
+ deviceStatus: subDev.subDeviceStatus,
384
+ subDeviceNo: subDev.subDeviceNo,
385
+ isSubDevice: true,
386
+ parentCid: dev.cid,
387
+ cid: `${dev.cid}_${subDev.subDeviceNo}`
388
+ };
389
+ const [subCategory, subDevice] = objectFactory(subDeviceDetails, this);
390
+ if (subDevice && subCategory in this._devList) {
391
+ this._devList[subCategory].push(subDevice);
392
+ }
393
+ });
394
+ } else if (device && category in this._devList) {
395
+ this._devList[category].push(device);
383
396
  }
384
- } catch (err) {
385
- logger.error(`Error creating device ${devType}:`, err);
386
- continue;
387
- }
388
- }
397
+ });
389
398
 
390
- return true;
399
+ // Update device list reference
400
+ this.devices = Object.values(this._devList).flat();
401
+
402
+ // Return true if we processed at least one device successfully
403
+ return this.devices.length > 0;
404
+ } catch (error) {
405
+ logger.error('Error processing devices:', error);
406
+ return false;
407
+ }
391
408
  }
392
409
 
393
410
  /**
@@ -400,7 +417,7 @@ export class VeSync {
400
417
  }
401
418
 
402
419
  this._inProcess = true;
403
- let procReturn = false;
420
+ let success = false;
404
421
 
405
422
  try {
406
423
  const [response] = await Helpers.callApi(
@@ -412,10 +429,7 @@ export class VeSync {
412
429
 
413
430
  if (response?.result?.list) {
414
431
  const deviceList = response.result.list;
415
- procReturn = this.processDevices(deviceList);
416
-
417
- // Update the main devices array with all devices
418
- this.devices = Object.values(this._devList).flat();
432
+ success = this.processDevices(deviceList);
419
433
 
420
434
  // Log device discovery results
421
435
  logger.debug('\n=== Device Discovery Summary ===');
@@ -457,7 +471,7 @@ export class VeSync {
457
471
  }
458
472
 
459
473
  this._inProcess = false;
460
- return procReturn;
474
+ return success;
461
475
  }
462
476
 
463
477
  /**
@@ -101,12 +101,31 @@ export abstract class VeSyncOutlet extends VeSyncBaseDevice {
101
101
 
102
102
  // Handle outdoor plugs with subdevices
103
103
  if (response.subDevices) {
104
- const subDevice = response.subDevices.find(
105
- (dev: any) => dev.subDeviceName === this.deviceName
106
- );
104
+ const subDevice = response.subDevices.find((dev: any) => {
105
+ // Match by name (primary identifier)
106
+ if (dev.subDeviceName === this.deviceName) {
107
+ return true;
108
+ }
109
+
110
+ // Match by subDeviceNo if available
111
+ if ('subDeviceNo' in this && (this as any).subDeviceNo === dev.subDeviceNo) {
112
+ return true;
113
+ }
114
+
115
+ // Match by cid/uuid if available (cid might be in format parentCid_subDeviceNo)
116
+ if (this.cid && this.cid.includes('_')) {
117
+ const [_, subId] = this.cid.split('_');
118
+ if (subId && dev.subDeviceNo === parseInt(subId)) {
119
+ return true;
120
+ }
121
+ }
122
+
123
+ return false;
124
+ });
125
+
107
126
  if (subDevice) {
108
127
  this.deviceStatus = subDevice.subDeviceStatus;
109
- logger.info(`[${this.deviceName}] Successfully retrieved subdevice status: ${this.deviceStatus}`);
128
+ logger.info(`[${this.deviceName}] Successfully retrieved sub-device status: ${this.deviceStatus}`);
110
129
  return;
111
130
  }
112
131
  }
@@ -254,20 +254,32 @@ export class VeSyncOutlet15A extends VeSyncOutlet {
254
254
  * VeSync Outdoor Plug
255
255
  */
256
256
  export class VeSyncOutdoorPlug extends VeSyncOutlet {
257
+ protected subDeviceNo?: number;
258
+ protected isSubDevice: boolean;
259
+ protected parentCid?: string;
260
+
257
261
  constructor(details: Record<string, any>, manager: VeSync) {
258
262
  super(details, manager);
263
+ this.isSubDevice = details.isSubDevice || false;
264
+ this.subDeviceNo = details.subDeviceNo;
265
+ this.parentCid = details.parentCid;
259
266
  }
260
267
 
261
268
  /**
262
269
  * Turn outlet on
263
270
  */
264
271
  async turnOn(): Promise<boolean> {
272
+ if (!this.isSubDevice) {
273
+ logger.error(`[${this.deviceName}] Cannot control parent device directly`);
274
+ return false;
275
+ }
276
+
265
277
  logger.debug(`[${this.deviceName}] Turning outlet on`);
266
278
  const body = {
267
279
  ...Helpers.reqBody(this.manager, 'devicestatus'),
268
280
  uuid: this.uuid,
269
281
  status: 'on',
270
- switchNo: 1
282
+ switchNo: this.subDeviceNo
271
283
  };
272
284
 
273
285
  const [response] = await Helpers.callApi(
@@ -290,12 +302,17 @@ export class VeSyncOutdoorPlug extends VeSyncOutlet {
290
302
  * Turn outlet off
291
303
  */
292
304
  async turnOff(): Promise<boolean> {
305
+ if (!this.isSubDevice) {
306
+ logger.error(`[${this.deviceName}] Cannot control parent device directly`);
307
+ return false;
308
+ }
309
+
293
310
  logger.debug(`[${this.deviceName}] Turning outlet off`);
294
311
  const body = {
295
312
  ...Helpers.reqBody(this.manager, 'devicestatus'),
296
313
  uuid: this.uuid,
297
314
  status: 'off',
298
- switchNo: 1
315
+ switchNo: this.subDeviceNo
299
316
  };
300
317
 
301
318
  const [response] = await Helpers.callApi(