tsvesync 1.0.19 → 1.0.20

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,40 @@ 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);
293
+ processDevices(deviceList) {
294
+ // Clear existing devices
295
+ for (const category of Object.keys(this._devList)) {
296
+ this._devList[category].length = 0;
288
297
  }
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;
299
- }
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`);
308
- }
309
- else if (category === 'unknown') {
310
- logger_1.logger.debug(`Unknown device type: ${devType}`);
311
- }
298
+ // Process each device
299
+ deviceList.forEach(dev => {
300
+ const [category, device] = objectFactory(dev, this);
301
+ // Handle outdoor plug sub-devices
302
+ if (dev.deviceType === 'ESO15-TB' && dev.subDevices) {
303
+ dev.subDevices.forEach((subDev) => {
304
+ const subDeviceDetails = {
305
+ ...dev,
306
+ deviceName: subDev.subDeviceName,
307
+ deviceStatus: subDev.subDeviceStatus,
308
+ subDeviceNo: subDev.subDeviceNo,
309
+ isSubDevice: true,
310
+ parentCid: dev.cid,
311
+ cid: `${dev.cid}_${subDev.subDeviceNo}`
312
+ };
313
+ const [subCategory, subDevice] = objectFactory(subDeviceDetails, this);
314
+ if (subDevice && subCategory in this._devList) {
315
+ this._devList[subCategory].push(subDevice);
316
+ }
317
+ });
312
318
  }
313
- catch (err) {
314
- logger_1.logger.error(`Error creating device ${devType}:`, err);
315
- continue;
319
+ else if (device && category in this._devList) {
320
+ this._devList[category].push(device);
316
321
  }
317
- }
318
- return true;
322
+ });
323
+ // Update device list reference
324
+ this.devices = Object.values(this._devList).flat();
319
325
  }
320
326
  /**
321
327
  * Get list of VeSync devices
@@ -332,9 +338,7 @@ class VeSync {
332
338
  const [response] = await helpers_1.Helpers.callApi('/cloud/v2/deviceManaged/devices', 'post', helpers_1.Helpers.reqBody(this, 'devicelist'), helpers_1.Helpers.reqHeaders(this));
333
339
  if ((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.list) {
334
340
  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();
341
+ this.processDevices(deviceList);
338
342
  // Log device discovery results
339
343
  logger_1.logger.debug('\n=== Device Discovery Summary ===');
340
344
  logger_1.logger.debug(`Total devices processed: ${deviceList.length}`);
@@ -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.20",
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,42 @@ 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);
361
+ private processDevices(deviceList: any[]): void {
362
+ // Clear existing devices
363
+ for (const category of Object.keys(this._devList)) {
364
+ this._devList[category].length = 0;
356
365
  }
357
366
 
358
- // Filter out devices that already exist
359
- const newDevices = devices.filter(dev => this.addDevTest(dev));
360
-
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;
370
- }
371
-
372
- const devType = dev.deviceType;
373
- try {
374
- // Create device instance using factory
375
- const [category, deviceObj] = objectFactory(dev, this);
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}`);
383
- }
384
- } catch (err) {
385
- logger.error(`Error creating device ${devType}:`, err);
386
- continue;
367
+ // Process each device
368
+ deviceList.forEach(dev => {
369
+ const [category, device] = objectFactory(dev, this);
370
+
371
+ // Handle outdoor plug sub-devices
372
+ if (dev.deviceType === 'ESO15-TB' && dev.subDevices) {
373
+ dev.subDevices.forEach((subDev: any) => {
374
+ const subDeviceDetails = {
375
+ ...dev,
376
+ deviceName: subDev.subDeviceName,
377
+ deviceStatus: subDev.subDeviceStatus,
378
+ subDeviceNo: subDev.subDeviceNo,
379
+ isSubDevice: true,
380
+ parentCid: dev.cid,
381
+ cid: `${dev.cid}_${subDev.subDeviceNo}`
382
+ };
383
+ const [subCategory, subDevice] = objectFactory(subDeviceDetails, this);
384
+ if (subDevice && subCategory in this._devList) {
385
+ this._devList[subCategory].push(subDevice);
386
+ }
387
+ });
388
+ } else if (device && category in this._devList) {
389
+ this._devList[category].push(device);
387
390
  }
388
- }
391
+ });
389
392
 
390
- return true;
393
+ // Update device list reference
394
+ this.devices = Object.values(this._devList).flat();
391
395
  }
392
396
 
393
397
  /**
@@ -412,10 +416,7 @@ export class VeSync {
412
416
 
413
417
  if (response?.result?.list) {
414
418
  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();
419
+ this.processDevices(deviceList);
419
420
 
420
421
  // Log device discovery results
421
422
  logger.debug('\n=== Device Discovery Summary ===');
@@ -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(