tsvesync 1.0.11 → 1.0.12

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.
@@ -1,104 +1,115 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var _a;
36
- Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.VeSync = void 0;
38
2
  /**
39
3
  * VeSync API Device Library
40
4
  */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.VeSync = void 0;
41
7
  const helpers_1 = require("./helpers");
42
- const bulbMods = __importStar(require("./vesyncBulb"));
43
- const fanMods = __importStar(require("./vesyncFan"));
44
- const outletMods = __importStar(require("./vesyncOutlet"));
45
- const switchMods = __importStar(require("./vesyncSwitch"));
46
- const kitchenMods = __importStar(require("./vesyncKitchen"));
8
+ const vesyncFanImpl_1 = require("./vesyncFanImpl");
9
+ const vesyncOutletImpl_1 = require("./vesyncOutletImpl");
10
+ const vesyncSwitchImpl_1 = require("./vesyncSwitchImpl");
11
+ const vesyncBulbImpl_1 = require("./vesyncBulbImpl");
47
12
  const DEFAULT_ENERGY_UPDATE_INTERVAL = 21600;
48
- function objectFactory(devType, config, manager) {
49
- const fans = (devType, config, manager) => {
50
- const fanCls = fanMods.fanModules[devType];
51
- return ['fans', new fanCls(config, manager)];
52
- };
53
- const outlets = (devType, config, manager) => {
54
- const outletCls = outletMods.outletModules[devType];
55
- return ['outlets', new outletCls(config, manager)];
56
- };
57
- const switches = (devType, config, manager) => {
58
- const switchCls = switchMods.switchModules[devType];
59
- return ['switches', new switchCls(config, manager)];
60
- };
61
- const bulbs = (devType, config, manager) => {
62
- const bulbCls = bulbMods.bulbModules[devType];
63
- return ['bulbs', new bulbCls(config, manager)];
64
- };
65
- const kitchen = (devType, config, manager) => {
66
- const kitchenCls = kitchenMods.kitchenModules[devType];
67
- return ['kitchen', new kitchenCls(config, manager)];
13
+ /**
14
+ * Create device instance based on type
15
+ */
16
+ function objectFactory(details, manager) {
17
+ const deviceType = details.deviceType;
18
+ let DeviceClass = null;
19
+ let category = 'unknown';
20
+ // Map of device categories to their module dictionaries
21
+ const allModules = {
22
+ outlets: vesyncOutletImpl_1.outletModules,
23
+ fans: vesyncFanImpl_1.fanModules,
24
+ bulbs: vesyncBulbImpl_1.bulbModules,
25
+ switches: vesyncSwitchImpl_1.switchModules
68
26
  };
69
- if (devType in fanMods.fanModules) {
70
- return fans(devType, config, manager);
71
- }
72
- else if (devType in outletMods.outletModules) {
73
- return outlets(devType, config, manager);
74
- }
75
- else if (devType in switchMods.switchModules) {
76
- return switches(devType, config, manager);
27
+ // First try exact match in all modules
28
+ for (const [cat, modules] of Object.entries(allModules)) {
29
+ if (deviceType in modules) {
30
+ DeviceClass = modules[deviceType];
31
+ category = cat;
32
+ console.debug(`Found exact match for ${deviceType} in ${cat} modules`);
33
+ break;
34
+ }
77
35
  }
78
- else if (devType in bulbMods.bulbModules) {
79
- return bulbs(devType, config, manager);
36
+ // If no exact match, try to find a base class
37
+ if (!DeviceClass) {
38
+ // Device type prefix mapping
39
+ const prefixMap = {
40
+ // Fans
41
+ 'Core': 'fans',
42
+ 'LAP': 'fans',
43
+ 'LTF': 'fans',
44
+ 'Classic': 'fans',
45
+ 'Dual': 'fans',
46
+ 'LUH': 'fans',
47
+ 'LEH': 'fans',
48
+ 'LV-PUR': 'fans',
49
+ 'LV-RH': 'fans',
50
+ // Outlets
51
+ 'wifi-switch': 'outlets',
52
+ 'ESW03': 'outlets',
53
+ 'ESW01': 'outlets',
54
+ 'ESW15': 'outlets',
55
+ 'ESO': 'outlets',
56
+ // Switches
57
+ 'ESWL': 'switches',
58
+ 'ESWD': 'switches',
59
+ // Bulbs
60
+ 'ESL': 'bulbs',
61
+ 'XYD': 'bulbs'
62
+ };
63
+ // Find category based on device type prefix
64
+ for (const [prefix, cat] of Object.entries(prefixMap)) {
65
+ if (deviceType.startsWith(prefix)) {
66
+ category = cat;
67
+ console.debug(`Device type ${deviceType} matched prefix ${prefix} -> category ${cat}`);
68
+ // Try to find a base class in this category's modules
69
+ const modules = allModules[cat];
70
+ for (const [moduleType, ModuleClass] of Object.entries(modules)) {
71
+ const baseType = moduleType.split('-')[0]; // e.g., ESL100 from ESL100-USA
72
+ if (deviceType.startsWith(baseType)) {
73
+ DeviceClass = ModuleClass;
74
+ console.debug(`Found base type match: ${deviceType} -> ${baseType}`);
75
+ break;
76
+ }
77
+ }
78
+ break;
79
+ }
80
+ }
80
81
  }
81
- else if (devType in kitchenMods.kitchenModules) {
82
- return kitchen(devType, config, manager);
82
+ if (DeviceClass) {
83
+ try {
84
+ // Add category to device details
85
+ details.deviceCategory = category;
86
+ const device = new DeviceClass(details, manager);
87
+ return [category, device];
88
+ }
89
+ catch (error) {
90
+ console.error(`Error creating device instance for ${deviceType}:`, error);
91
+ return [category, null];
92
+ }
83
93
  }
84
94
  else {
85
- console.debug('Unknown device named %s model %s', config.deviceName || '', config.deviceType || '');
86
- return ['unknown', null];
95
+ console.warn(`No device class found for type: ${deviceType}`);
96
+ return [category, null];
87
97
  }
88
98
  }
99
+ /**
100
+ * VeSync Manager Class
101
+ */
89
102
  class VeSync {
103
+ /**
104
+ * Initialize VeSync Manager
105
+ */
90
106
  constructor(username, password, timeZone = helpers_1.DEFAULT_TZ, debug = false, redact = true) {
91
- this.body = helpers_1.Helpers.reqBody(this, 'devicelist');
92
- this[_a] = await helpers_1.Helpers.callApi('/cloud/v1/deviceManaged/devices', 'post', body, helpers_1.Helpers.reqHeaders(this));
93
107
  this._debug = debug;
94
- if (debug) {
95
- // Set debug logging levels
96
- console.debug('Debug logging enabled');
97
- }
98
108
  this._redact = redact;
99
- if (redact) {
100
- helpers_1.Helpers.redact = redact;
101
- }
109
+ this._energyUpdateInterval = DEFAULT_ENERGY_UPDATE_INTERVAL;
110
+ this._energyCheck = true;
111
+ this._lastUpdateTs = null;
112
+ this._inProcess = false;
102
113
  this.username = username;
103
114
  this.password = password;
104
115
  this.token = null;
@@ -107,29 +118,22 @@ class VeSync {
107
118
  this.devices = null;
108
119
  this.enabled = false;
109
120
  this.updateInterval = helpers_1.API_RATE_LIMIT;
110
- this.lastUpdateTs = null;
111
- this.inProcess = false;
112
- this._energyUpdateInterval = DEFAULT_ENERGY_UPDATE_INTERVAL;
113
- this._energyCheck = true;
114
121
  this.fans = [];
115
122
  this.outlets = [];
116
123
  this.switches = [];
117
124
  this.bulbs = [];
118
125
  this.scales = [];
119
- this.kitchen = [];
120
126
  this._devList = {
121
127
  fans: this.fans,
122
128
  outlets: this.outlets,
123
129
  switches: this.switches,
124
- bulbs: this.bulbs,
125
- kitchen: this.kitchen,
126
- unknown: []
130
+ bulbs: this.bulbs
127
131
  };
128
132
  if (typeof timeZone === 'string' && timeZone) {
129
133
  const regTest = /[^a-zA-Z/_]/;
130
134
  if (regTest.test(timeZone)) {
131
135
  this.timeZone = helpers_1.DEFAULT_TZ;
132
- console.debug('Invalid characters in time zone - %s', timeZone);
136
+ console.debug('Invalid characters in time zone - ', timeZone);
133
137
  }
134
138
  else {
135
139
  this.timeZone = timeZone;
@@ -139,39 +143,45 @@ class VeSync {
139
143
  this.timeZone = helpers_1.DEFAULT_TZ;
140
144
  console.debug('Time zone is not a string');
141
145
  }
146
+ if (debug) {
147
+ this.debug = true;
148
+ }
149
+ if (redact) {
150
+ this.redact = true;
151
+ }
142
152
  }
153
+ /**
154
+ * Get/Set debug mode
155
+ */
143
156
  get debug() {
144
157
  return this._debug;
145
158
  }
146
- set debug(newFlag) {
147
- this._debug = newFlag;
148
- // Set logging levels based on debug flag
149
- if (newFlag) {
150
- console.debug('Debug logging enabled');
151
- }
159
+ set debug(flag) {
160
+ this._debug = flag;
152
161
  }
162
+ /**
163
+ * Get/Set redact mode
164
+ */
153
165
  get redact() {
154
166
  return this._redact;
155
167
  }
156
- set redact(newFlag) {
157
- if (newFlag) {
158
- helpers_1.Helpers.redact = true;
159
- }
160
- else {
161
- helpers_1.Helpers.redact = false;
162
- }
163
- this._redact = newFlag;
168
+ set redact(flag) {
169
+ this._redact = flag;
170
+ helpers_1.Helpers.shouldRedact = flag;
164
171
  }
172
+ /**
173
+ * Get/Set energy update interval
174
+ */
165
175
  get energyUpdateInterval() {
166
176
  return this._energyUpdateInterval;
167
177
  }
168
- set energyUpdateInterval(newEnergyUpdate) {
169
- if (newEnergyUpdate > 0) {
170
- this._energyUpdateInterval = newEnergyUpdate;
178
+ set energyUpdateInterval(interval) {
179
+ if (interval > 0) {
180
+ this._energyUpdateInterval = interval;
171
181
  }
172
182
  }
173
183
  /**
174
- * Test if device should be removed - false = Remove
184
+ * Test if device should be removed
175
185
  */
176
186
  static removeDevTest(device, newList) {
177
187
  if (Array.isArray(newList) && device.cid) {
@@ -180,130 +190,243 @@ class VeSync {
180
190
  return true;
181
191
  }
182
192
  }
193
+ console.debug(`Device removed - ${device.deviceName} - ${device.deviceType}`);
194
+ return false;
183
195
  }
184
- return false;
196
+ return true;
185
197
  }
186
198
  /**
187
- * Login to VeSync server
199
+ * Test if new device should be added
188
200
  */
189
- async login() {
190
- const body = helpers_1.Helpers.reqBody(this, 'login');
191
- const [response] = await helpers_1.Helpers.callApi('/cloud/v1/user/login', 'post', body);
192
- if (!response || response.code !== 0 || !response.result) {
193
- console.error('Error logging in to VeSync');
194
- return false;
201
+ addDevTest(newDev) {
202
+ if ('cid' in newDev) {
203
+ for (const devices of Object.values(this._devList)) {
204
+ for (const dev of devices) {
205
+ if (dev.cid === newDev.cid) {
206
+ return false;
207
+ }
208
+ }
209
+ }
195
210
  }
196
- this.token = response.result.token;
197
- this.accountId = response.result.accountID;
198
- this.countryCode = response.result.countryCode;
199
- this.enabled = true;
200
211
  return true;
201
212
  }
202
213
  /**
203
- * Get list of VeSync devices
214
+ * Remove devices not found in device list return
204
215
  */
205
- async getDevices() {
206
- if (!this.enabled)
207
- : console.error('Not logged in');
208
- return false;
216
+ removeOldDevices(devices) {
217
+ for (const [key, deviceList] of Object.entries(this._devList)) {
218
+ const before = deviceList.length;
219
+ this._devList[key] = deviceList.filter(device => VeSync.removeDevTest(device, devices));
220
+ const after = this._devList[key].length;
221
+ if (before !== after) {
222
+ console.debug(`${before - after} ${key} removed`);
223
+ }
224
+ }
225
+ return true;
209
226
  }
210
- if(, response) { }
211
- }
212
- exports.VeSync = VeSync;
213
- _a = response;
214
- || response.code !== 0;
215
- {
216
- console.error('Error getting device list');
217
- return false;
218
- }
219
- this.devices = response.result.list || [];
220
- return true;
221
- /**
222
- * Process devices after getting list of devices
223
- */
224
- processDevices();
225
- void {
226
- : .devices
227
- };
228
- {
229
- console.error('Device list not fetched');
230
- return;
231
- }
232
- const deviceList = [...this.devices];
233
- const toRemove = [];
234
- // Build list of devices to remove
235
- for (const devList of Object.values(this._devList)) {
236
- for (const device of devList) {
237
- if (!VeSync.removeDevTest(device, deviceList)) {
238
- toRemove.push(device);
227
+ /**
228
+ * Correct devices without cid or uuid
229
+ */
230
+ static setDevId(devices) {
231
+ const devRem = [];
232
+ devices.forEach((dev, index) => {
233
+ if (!dev.cid) {
234
+ if (dev.macID) {
235
+ dev.cid = dev.macID;
236
+ }
237
+ else if (dev.uuid) {
238
+ dev.cid = dev.uuid;
239
+ }
240
+ else {
241
+ devRem.push(index);
242
+ console.warn(`Device with no ID - ${dev.deviceName || ''}`);
243
+ }
244
+ }
245
+ });
246
+ if (devRem.length > 0) {
247
+ return devices.filter((_, index) => !devRem.includes(index));
239
248
  }
249
+ return devices;
240
250
  }
241
- }
242
- // Remove devices not in device list
243
- for (const device of toRemove) {
244
- for (const [key, devList] of Object.entries(this._devList)) {
245
- const index = devList.indexOf(device);
246
- if (index > -1) {
247
- this._devList[key].splice(index, 1);
251
+ /**
252
+ * Process device list and instantiate device objects
253
+ */
254
+ processDevices(devList) {
255
+ // Ensure devices have proper IDs
256
+ const devices = VeSync.setDevId(devList);
257
+ // Count current devices
258
+ let numDevices = 0;
259
+ for (const deviceList of Object.values(this._devList)) {
260
+ numDevices += deviceList.length;
261
+ }
262
+ if (!devices || devices.length === 0) {
263
+ console.warn('No devices found in api return');
264
+ return false;
265
+ }
266
+ // Initialize new device list or remove old devices
267
+ if (numDevices === 0) {
268
+ console.debug('New device list initialized');
269
+ }
270
+ else {
271
+ this.removeOldDevices(devices);
248
272
  }
273
+ // Filter out devices that already exist
274
+ const newDevices = devices.filter(dev => this.addDevTest(dev));
275
+ // Required keys for device instantiation
276
+ const requiredKeys = ['deviceType', 'deviceName', 'deviceStatus', 'cid'];
277
+ // Process each new device
278
+ for (const dev of newDevices) {
279
+ // Verify required device properties
280
+ if (!requiredKeys.every(key => key in dev)) {
281
+ console.debug(`Error adding device - missing required properties: ${dev.deviceName || 'Unknown'}`);
282
+ continue;
283
+ }
284
+ const devType = dev.deviceType;
285
+ try {
286
+ // Create device instance using factory
287
+ const [category, deviceObj] = objectFactory(dev, this);
288
+ // Add device to appropriate category if valid
289
+ if (deviceObj && category in this._devList) {
290
+ this._devList[category].push(deviceObj);
291
+ console.debug(`Added ${devType} device to ${category} category`);
292
+ }
293
+ else if (category === 'unknown') {
294
+ console.debug(`Unknown device type: ${devType}`);
295
+ }
296
+ }
297
+ catch (err) {
298
+ console.error(`Error creating device ${devType}:`, err);
299
+ continue;
300
+ }
301
+ }
302
+ return true;
249
303
  }
250
- }
251
- // Add new devices
252
- for (const device of deviceList) {
253
- let foundDevice = false;
254
- // Look for existing devices
255
- for (const devList of Object.values(this._devList)) {
256
- if (devList.some(dev => dev.cid === device.cid)) {
257
- foundDevice = true;
258
- break;
304
+ /**
305
+ * Get list of VeSync devices
306
+ */
307
+ async getDevices() {
308
+ var _a, _b;
309
+ if (!this.enabled) {
310
+ console.error('Not logged in to VeSync');
311
+ return false;
312
+ }
313
+ this._inProcess = true;
314
+ let procReturn = false;
315
+ try {
316
+ const [response] = await helpers_1.Helpers.callApi('/cloud/v2/deviceManaged/devices', 'post', helpers_1.Helpers.reqBody(this, 'devicelist'), helpers_1.Helpers.reqHeaders(this));
317
+ if ((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.list) {
318
+ const deviceList = response.result.list;
319
+ procReturn = this.processDevices(deviceList);
320
+ // Update the main devices array with all devices
321
+ this.devices = Object.values(this._devList).flat();
322
+ // Log device discovery results
323
+ console.log('\n=== Device Discovery Summary ===');
324
+ console.log(`Total devices processed: ${deviceList.length}`);
325
+ // Log device types found
326
+ const deviceTypes = deviceList.map((d) => d.deviceType);
327
+ console.log('\nDevice types found:', deviceTypes);
328
+ // Log devices by category with details
329
+ console.log('\nDevices by Category:');
330
+ console.log('---------------------');
331
+ for (const [category, devices] of Object.entries(this._devList)) {
332
+ if (devices.length > 0) {
333
+ console.log(`\n${category.toUpperCase()} (${devices.length} devices):`);
334
+ devices.forEach((d) => {
335
+ console.log(` • ${d.deviceName}`);
336
+ console.log(` Type: ${d.deviceType}`);
337
+ console.log(` Status: ${d.deviceStatus}`);
338
+ console.log(` ID: ${d.cid}`);
339
+ });
340
+ }
341
+ }
342
+ // Log summary statistics
343
+ console.log('\nSummary Statistics:');
344
+ console.log('-------------------');
345
+ console.log(`Total Devices: ${((_b = this.devices) === null || _b === void 0 ? void 0 : _b.length) || 0}`);
346
+ for (const [category, devices] of Object.entries(this._devList)) {
347
+ console.log(`${category}: ${devices.length} devices`);
348
+ }
349
+ console.log('\n=== End of Device Discovery ===\n');
350
+ }
351
+ else {
352
+ console.error('No devices found in response');
353
+ }
354
+ }
355
+ catch (error) {
356
+ console.error('Unable to get device list:', error);
259
357
  }
358
+ this._inProcess = false;
359
+ return procReturn;
260
360
  }
261
- // If device not found, add it
262
- if (!foundDevice) {
263
- const [deviceType, deviceObj] = objectFactory(device.deviceType, device, this);
264
- if (deviceObj) {
265
- this._devList[deviceType].push(deviceObj);
361
+ /**
362
+ * Login to VeSync server
363
+ */
364
+ async login() {
365
+ var _a;
366
+ const body = helpers_1.Helpers.reqBody(this, 'login');
367
+ console.log('Login request:', {
368
+ url: `${process.env.VESYNC_API_URL}/cloud/v1/user/login`,
369
+ body
370
+ });
371
+ try {
372
+ const [response, status] = await helpers_1.Helpers.callApi('/cloud/v1/user/login', 'post', body);
373
+ console.log('Login response:', { status, response });
374
+ if ((_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.token) {
375
+ this.token = response.result.token;
376
+ this.accountId = response.result.accountID;
377
+ this.countryCode = response.result.countryCode;
378
+ this.enabled = true;
379
+ return true;
380
+ }
381
+ console.error('Unable to login with supplied credentials');
382
+ return false;
383
+ }
384
+ catch (error) {
385
+ console.error('Login error:', error);
386
+ return false;
266
387
  }
267
388
  }
268
- }
269
- /**
270
- * Update all devices
271
- */
272
- async;
273
- update();
274
- Promise < void > {
275
- : .enabled
276
- };
277
- {
278
- console.error('Not logged in');
279
- return;
280
- }
281
- if (this.inProcess) {
282
- console.debug('Update already in process');
283
- return;
284
- }
285
- const currentTime = Date.now();
286
- if (this.lastUpdateTs &&
287
- currentTime - this.lastUpdateTs < this.updateInterval * 1000) {
288
- console.debug('Update rate limit - waiting %d seconds', this.updateInterval - (currentTime - this.lastUpdateTs) / 1000);
289
- return;
290
- }
291
- this.inProcess = true;
292
- this.lastUpdateTs = currentTime;
293
- try {
294
- await this.getDevices();
295
- this.processDevices();
296
- const updatePromises = [];
297
- for (const deviceList of Object.values(this._devList)) {
298
- for (const device of deviceList) {
299
- if ('update' in device && typeof device.update === 'function') {
300
- updatePromises.push(device.update());
389
+ /**
390
+ * Test if update interval has been exceeded
391
+ */
392
+ deviceTimeCheck() {
393
+ return (this._lastUpdateTs === null ||
394
+ (Date.now() - this._lastUpdateTs) / 1000 > this.updateInterval);
395
+ }
396
+ /**
397
+ * Update device list and details
398
+ */
399
+ async update() {
400
+ if (this.deviceTimeCheck()) {
401
+ if (!this.enabled) {
402
+ console.error('Not logged in to VeSync');
403
+ return;
301
404
  }
405
+ await this.getDevices();
406
+ console.debug('Start updating the device details one by one');
407
+ for (const deviceList of Object.values(this._devList)) {
408
+ for (const device of deviceList) {
409
+ try {
410
+ await device.getDetails();
411
+ }
412
+ catch (error) {
413
+ console.error(`Error updating ${device.deviceName}:`, error);
414
+ }
415
+ }
416
+ }
417
+ this._lastUpdateTs = Date.now();
302
418
  }
303
419
  }
304
- await Promise.all(updatePromises);
305
- }
306
- finally {
307
- this.inProcess = false;
420
+ /**
421
+ * Create device instance from details
422
+ */
423
+ createDevice(details) {
424
+ const deviceType = details.deviceType;
425
+ const deviceClass = vesyncFanImpl_1.fanModules[deviceType];
426
+ if (deviceClass) {
427
+ return new deviceClass(details, this);
428
+ }
429
+ return null;
430
+ }
308
431
  }
309
- //# sourceMappingURL=vesync.js.map
432
+ exports.VeSync = VeSync;