tsvesync 1.0.11

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.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/api/vesync/VeSync.yaml +33 -0
  4. package/api/vesyncbulb/ESL100.yaml +74 -0
  5. package/api/vesyncbulb/ESL100CW.yaml +123 -0
  6. package/api/vesyncbulb/ESL100MC.yaml +169 -0
  7. package/api/vesyncbulb/XYD0001.yaml +193 -0
  8. package/api/vesyncfan/Classic200S.yaml +280 -0
  9. package/api/vesyncfan/Classic300S.yaml +278 -0
  10. package/api/vesyncfan/Core200S.yaml +256 -0
  11. package/api/vesyncfan/Core300S.yaml +281 -0
  12. package/api/vesyncfan/Core400S.yaml +281 -0
  13. package/api/vesyncfan/Core600S.yaml +281 -0
  14. package/api/vesyncfan/Dual200S.yaml +278 -0
  15. package/api/vesyncfan/LAP-C201S-AUSR.yaml +256 -0
  16. package/api/vesyncfan/LAP-C202S-WUSR.yaml +256 -0
  17. package/api/vesyncfan/LAP-C301S-WAAA.yaml +281 -0
  18. package/api/vesyncfan/LAP-C301S-WJP.yaml +281 -0
  19. package/api/vesyncfan/LAP-C302S-WUSB.yaml +281 -0
  20. package/api/vesyncfan/LAP-C401S-WAAA.yaml +281 -0
  21. package/api/vesyncfan/LAP-C401S-WJP.yaml +281 -0
  22. package/api/vesyncfan/LAP-C401S-WUSR.yaml +281 -0
  23. package/api/vesyncfan/LAP-C601S-WEU.yaml +281 -0
  24. package/api/vesyncfan/LAP-C601S-WUS.yaml +281 -0
  25. package/api/vesyncfan/LAP-C601S-WUSR.yaml +281 -0
  26. package/api/vesyncfan/LAP-EL551S-AEUR.yaml +167 -0
  27. package/api/vesyncfan/LAP-EL551S-AUS.yaml +167 -0
  28. package/api/vesyncfan/LAP-EL551S-WEU.yaml +167 -0
  29. package/api/vesyncfan/LAP-EL551S-WUS.yaml +167 -0
  30. package/api/vesyncfan/LAP-V102S-AASR.yaml +167 -0
  31. package/api/vesyncfan/LAP-V102S-AUSR.yaml +167 -0
  32. package/api/vesyncfan/LAP-V102S-WEU.yaml +167 -0
  33. package/api/vesyncfan/LAP-V102S-WJP.yaml +167 -0
  34. package/api/vesyncfan/LAP-V102S-WUS.yaml +167 -0
  35. package/api/vesyncfan/LAP-V201-AUSR.yaml +167 -0
  36. package/api/vesyncfan/LAP-V201S-AASR.yaml +167 -0
  37. package/api/vesyncfan/LAP-V201S-AEUR.yaml +167 -0
  38. package/api/vesyncfan/LAP-V201S-AUSR.yaml +167 -0
  39. package/api/vesyncfan/LAP-V201S-WEU.yaml +167 -0
  40. package/api/vesyncfan/LAP-V201S-WJP.yaml +167 -0
  41. package/api/vesyncfan/LAP-V201S-WUS.yaml +167 -0
  42. package/api/vesyncfan/LEH-S601S-WUS.yaml +303 -0
  43. package/api/vesyncfan/LEH-S601S-WUSR.yaml +82 -0
  44. package/api/vesyncfan/LTF-F422S-KEU.yaml +165 -0
  45. package/api/vesyncfan/LTF-F422S-WUS.yaml +165 -0
  46. package/api/vesyncfan/LTF-F422S-WUSR.yaml +167 -0
  47. package/api/vesyncfan/LTF-F422_WJP.yaml +165 -0
  48. package/api/vesyncfan/LUH-A601S-AUSW.yaml +82 -0
  49. package/api/vesyncfan/LUH-A601S-WUSB.yaml +305 -0
  50. package/api/vesyncfan/LUH-A602S-WEU.yaml +305 -0
  51. package/api/vesyncfan/LUH-A602S-WEUR.yaml +305 -0
  52. package/api/vesyncfan/LUH-A602S-WJP.yaml +305 -0
  53. package/api/vesyncfan/LUH-A602S-WUS.yaml +305 -0
  54. package/api/vesyncfan/LUH-A602S-WUSC.yaml +305 -0
  55. package/api/vesyncfan/LUH-A602S-WUSR.yaml +305 -0
  56. package/api/vesyncfan/LUH-D301S-WEU.yaml +305 -0
  57. package/api/vesyncfan/LUH-D301S-WJP.yaml +305 -0
  58. package/api/vesyncfan/LUH-D301S-WUSR.yaml +305 -0
  59. package/api/vesyncfan/LUH-M101S-WUS.yaml +278 -0
  60. package/api/vesyncfan/LUH-O451S-WEU.yaml +278 -0
  61. package/api/vesyncfan/LUH-O451S-WUS.yaml +278 -0
  62. package/api/vesyncfan/LUH-O451S-WUSR.yaml +305 -0
  63. package/api/vesyncfan/LUH-O601S-KUS.yaml +305 -0
  64. package/api/vesyncfan/LUH-O601S-WUS.yaml +305 -0
  65. package/api/vesyncfan/LV-PUR131S.yaml +108 -0
  66. package/api/vesyncfan/LV-RH131S.yaml +108 -0
  67. package/api/vesyncoutlet/ESO15-TB.yaml +124 -0
  68. package/api/vesyncoutlet/ESW01-EU.yaml +122 -0
  69. package/api/vesyncoutlet/ESW03-USA.yaml +122 -0
  70. package/api/vesyncoutlet/ESW15-USA.yaml +156 -0
  71. package/api/vesyncoutlet/wifi-switch-1.3.yaml +60 -0
  72. package/api/vesyncswitch/ESWD16.yaml +128 -0
  73. package/api/vesyncswitch/ESWL01.yaml +56 -0
  74. package/api/vesyncswitch/ESWL03.yaml +56 -0
  75. package/dist/lib/helpers.d.ts +87 -0
  76. package/dist/lib/helpers.js +222 -0
  77. package/dist/lib/helpers.js.map +1 -0
  78. package/dist/lib/vesync.d.ts +47 -0
  79. package/dist/lib/vesync.js +309 -0
  80. package/dist/lib/vesync.js.map +1 -0
  81. package/dist/lib/vesyncBaseDevice.d.ts +68 -0
  82. package/dist/lib/vesyncBaseDevice.js +116 -0
  83. package/dist/lib/vesyncBaseDevice.js.map +1 -0
  84. package/dist/lib/vesyncBulb.d.ts +0 -0
  85. package/dist/lib/vesyncBulb.js +2 -0
  86. package/dist/lib/vesyncBulb.js.map +1 -0
  87. package/dist/lib/vesyncFan.d.ts +92 -0
  88. package/dist/lib/vesyncFan.js +265 -0
  89. package/dist/lib/vesyncFan.js.map +1 -0
  90. package/dist/lib/vesyncKitchen.d.ts +0 -0
  91. package/dist/lib/vesyncKitchen.js +2 -0
  92. package/dist/lib/vesyncKitchen.js.map +1 -0
  93. package/dist/lib/vesyncOutlet.d.ts +0 -0
  94. package/dist/lib/vesyncOutlet.js +2 -0
  95. package/dist/lib/vesyncOutlet.js.map +1 -0
  96. package/dist/lib/vesyncSwitch.d.ts +0 -0
  97. package/dist/lib/vesyncSwitch.js +2 -0
  98. package/dist/lib/vesyncSwitch.js.map +1 -0
  99. package/dist/src/lib/helpers.d.ts +88 -0
  100. package/dist/src/lib/helpers.js +244 -0
  101. package/dist/src/lib/vesync.d.ts +89 -0
  102. package/dist/src/lib/vesync.js +430 -0
  103. package/dist/src/lib/vesyncBaseDevice.d.ts +43 -0
  104. package/dist/src/lib/vesyncBaseDevice.js +87 -0
  105. package/dist/src/lib/vesyncBulb.d.ts +79 -0
  106. package/dist/src/lib/vesyncBulb.js +198 -0
  107. package/dist/src/lib/vesyncBulbImpl.d.ts +38 -0
  108. package/dist/src/lib/vesyncBulbImpl.js +161 -0
  109. package/dist/src/lib/vesyncFan.d.ts +80 -0
  110. package/dist/src/lib/vesyncFan.js +406 -0
  111. package/dist/src/lib/vesyncFanImpl.d.ts +137 -0
  112. package/dist/src/lib/vesyncFanImpl.js +643 -0
  113. package/dist/src/lib/vesyncOutlet.d.ts +53 -0
  114. package/dist/src/lib/vesyncOutlet.js +98 -0
  115. package/dist/src/lib/vesyncOutletImpl.d.ts +70 -0
  116. package/dist/src/lib/vesyncOutletImpl.js +206 -0
  117. package/dist/src/lib/vesyncSwitch.d.ts +169 -0
  118. package/dist/src/lib/vesyncSwitch.js +414 -0
  119. package/dist/src/lib/vesyncSwitchImpl.d.ts +82 -0
  120. package/dist/src/lib/vesyncSwitchImpl.js +290 -0
  121. package/dist/test/testFeatures.d.ts +4 -0
  122. package/dist/test/testFeatures.js +394 -0
  123. package/dist/test/testService.d.ts +62 -0
  124. package/dist/test/testService.js +540 -0
  125. package/dist/test/testSpecial.d.ts +5 -0
  126. package/dist/test/testSpecial.js +227 -0
  127. package/package.json +53 -0
  128. package/src/lib/helpers.ts +290 -0
  129. package/src/lib/vesync.ts +530 -0
  130. package/src/lib/vesyncBaseDevice.ts +114 -0
  131. package/src/lib/vesyncBulb.ts +252 -0
  132. package/src/lib/vesyncBulbImpl.ts +215 -0
  133. package/src/lib/vesyncFan.ts +447 -0
  134. package/src/lib/vesyncFanImpl.ts +848 -0
  135. package/src/lib/vesyncOutlet.ts +148 -0
  136. package/src/lib/vesyncOutletImpl.ts +284 -0
  137. package/src/lib/vesyncSwitch.ts +546 -0
  138. package/src/lib/vesyncSwitchImpl.ts +408 -0
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ /**
3
+ * Special test for VeSync API
4
+ * Uses credentials from .env file in test directory
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ // Load environment variables first
41
+ const dotenv = __importStar(require("dotenv"));
42
+ // Load .env file from test directory before any other imports
43
+ dotenv.config({ path: '.env' });
44
+ // Now import the rest of the modules
45
+ const vesync_1 = require("../src/lib/vesync");
46
+ const helpers_1 = require("../src/lib/helpers");
47
+ const helpers_2 = require("../src/lib/helpers");
48
+ /**
49
+ * Print detailed device status
50
+ */
51
+ async function printDeviceStatus(device) {
52
+ console.log(`\n${device.deviceName} Status:`);
53
+ console.log('-'.repeat(device.deviceName.length + 8));
54
+ console.log('Device Name:', device.deviceName);
55
+ console.log('Device Type:', device.deviceType);
56
+ console.log('Config Module:', device.configModule);
57
+ console.log('Device Status:', device.deviceStatus);
58
+ console.log('UUID:', device.uuid);
59
+ console.log('MAC ID:', device.macId || 'Not available');
60
+ // Get latest device details
61
+ await device.update();
62
+ // Handle power devices (outlets and switches)
63
+ if (['ESO15-TB', 'ESW03-USA', 'wifi-switch-1.3'].includes(device.deviceType)) {
64
+ try {
65
+ // Get device details based on type
66
+ if (device.deviceType === 'wifi-switch-1.3') {
67
+ const [response] = await helpers_2.Helpers.callApi(`/v1/device/${device.cid}/detail`, 'get', null, helpers_2.Helpers.reqHeaders(device.manager));
68
+ if (response?.code === 0) {
69
+ console.log('Power:', response.power ? `${response.power} W` : 'Not available');
70
+ console.log('Voltage:', response.voltage ? `${response.voltage} V` : 'Not available');
71
+ console.log('Today\'s Energy:', response.energy ? `${response.energy} kWh` : 'Not available');
72
+ console.log('Active Time:', response.activeTime ? `${response.activeTime} minutes` : 'Not available');
73
+ }
74
+ }
75
+ else {
76
+ // For 10A and 15A outlets
77
+ const body = {
78
+ ...helpers_2.Helpers.reqBody(device.manager, 'devicedetail'),
79
+ uuid: device.uuid
80
+ };
81
+ const endpoint = device.deviceType === 'ESW03-USA' ? '/10a/v1/device/devicedetail' : '/15a/v1/device/devicedetail';
82
+ const [response] = await helpers_2.Helpers.callApi(endpoint, 'post', body, helpers_2.Helpers.reqHeaders(device.manager));
83
+ if (response?.code === 0) {
84
+ console.log('Power:', response.power ? `${response.power} W` : 'Not available');
85
+ console.log('Voltage:', response.voltage ? `${response.voltage} V` : 'Not available');
86
+ console.log('Today\'s Energy:', response.energy ? `${response.energy} kWh` : 'Not available');
87
+ console.log('Active Time:', response.activeTime ? `${response.activeTime} minutes` : 'Not available');
88
+ }
89
+ }
90
+ // Get energy history
91
+ console.log('Energy Usage:');
92
+ // Weekly energy
93
+ const weekBody = {
94
+ ...helpers_2.Helpers.reqBody(device.manager, 'energy_week'),
95
+ uuid: device.uuid
96
+ };
97
+ const weekEndpoint = device.deviceType === 'ESW03-USA' ? '/10a/v1/device/energyweek' : '/15a/v1/device/energyweek';
98
+ const [weekResponse] = await helpers_2.Helpers.callApi(weekEndpoint, 'post', weekBody, helpers_2.Helpers.reqHeaders(device.manager));
99
+ console.log(' Week:', weekResponse?.code === 0 ? `${weekResponse.totalEnergy} kWh` : 'Not available');
100
+ // Monthly energy
101
+ const monthBody = {
102
+ ...helpers_2.Helpers.reqBody(device.manager, 'energy_month'),
103
+ uuid: device.uuid
104
+ };
105
+ const monthEndpoint = device.deviceType === 'ESW03-USA' ? '/10a/v1/device/energymonth' : '/15a/v1/device/energymonth';
106
+ const [monthResponse] = await helpers_2.Helpers.callApi(monthEndpoint, 'post', monthBody, helpers_2.Helpers.reqHeaders(device.manager));
107
+ console.log(' Month:', monthResponse?.code === 0 ? `${monthResponse.totalEnergy} kWh` : 'Not available');
108
+ // Yearly energy
109
+ const yearBody = {
110
+ ...helpers_2.Helpers.reqBody(device.manager, 'energy_year'),
111
+ uuid: device.uuid
112
+ };
113
+ const yearEndpoint = device.deviceType === 'ESW03-USA' ? '/10a/v1/device/energyyear' : '/15a/v1/device/energyyear';
114
+ const [yearResponse] = await helpers_2.Helpers.callApi(yearEndpoint, 'post', yearBody, helpers_2.Helpers.reqHeaders(device.manager));
115
+ console.log(' Year:', yearResponse?.code === 0 ? `${yearResponse.totalEnergy} kWh` : 'Not available');
116
+ }
117
+ catch (error) {
118
+ console.log('Power: Error retrieving data');
119
+ console.log('Voltage: Error retrieving data');
120
+ console.log('Energy Usage:');
121
+ console.log(' Week: Error retrieving data');
122
+ console.log(' Month: Error retrieving data');
123
+ console.log(' Year: Error retrieving data');
124
+ console.log('Error details:', error);
125
+ }
126
+ }
127
+ // Print fan-specific details if available
128
+ if (device.deviceType.toLowerCase().includes('core')) {
129
+ try {
130
+ console.log('Fan Details:');
131
+ console.log(' Mode:', device.details?.mode || 'Not available');
132
+ console.log(' Fan Speed:', device.details?.speed || 'Not available');
133
+ console.log(' Filter Life:', device.details?.filterLife ? `${device.details.filterLife}%` : 'Not available');
134
+ console.log(' Screen Status:', device.details?.screenStatus !== undefined ? device.details.screenStatus : 'Not available');
135
+ console.log(' Child Lock:', device.details?.childLock !== undefined ? device.details.childLock : 'Not available');
136
+ if (device.deviceType === 'Core300S') {
137
+ console.log(' Air Quality:', device.details?.airQuality !== undefined ? device.details.airQuality : 'Not available');
138
+ }
139
+ }
140
+ catch (error) {
141
+ console.log(' Error getting fan details');
142
+ }
143
+ }
144
+ }
145
+ async function runTest() {
146
+ // Get credentials from .env
147
+ const username = process.env.VESYNC_USERNAME;
148
+ const password = process.env.VESYNC_PASSWORD;
149
+ const apiUrl = process.env.VESYNC_API_URL || 'https://smartapi.vesync.com';
150
+ if (!username || !password) {
151
+ console.error('Error: VESYNC_USERNAME and VESYNC_PASSWORD must be set in .env file');
152
+ process.exit(1);
153
+ }
154
+ console.log('Using credentials from .env');
155
+ console.log(`Username: ${username}`);
156
+ console.log(`Password: ${password.replace(/./g, '*')}`);
157
+ // Set API URL
158
+ (0, helpers_1.setApiBaseUrl)(apiUrl);
159
+ console.log(`Using API URL: ${apiUrl}`);
160
+ // Create VeSync manager
161
+ const manager = new vesync_1.VeSync(username, password);
162
+ // Attempt login
163
+ console.log('\nAttempting login...');
164
+ if (!await manager.login()) {
165
+ console.error('Login failed');
166
+ process.exit(1);
167
+ }
168
+ console.log('Login successful');
169
+ // Get devices
170
+ console.log('\nGetting device list...');
171
+ await manager.getDevices();
172
+ if (!manager.devices || manager.devices.length === 0) {
173
+ console.log('No devices found');
174
+ process.exit(0);
175
+ }
176
+ // Print device discovery summary
177
+ console.log('\n=== Device Discovery Summary ===');
178
+ console.log('Total devices processed:', manager.devices.length);
179
+ // Get unique device types
180
+ const deviceTypes = [...new Set(manager.devices.map(d => d.deviceType))];
181
+ console.log('\nDevice types found:', JSON.stringify(deviceTypes, null, 2));
182
+ // Group devices by category
183
+ console.log('\nDevices by Category:');
184
+ console.log('---------------------\n');
185
+ // Fans
186
+ const fans = manager.devices.filter(d => d.deviceType.toLowerCase().includes('core'));
187
+ if (fans.length > 0) {
188
+ console.log('FANS (' + fans.length + ' devices):');
189
+ fans.forEach(d => {
190
+ console.log(` • ${d.deviceName}`);
191
+ console.log(` Type: ${d.deviceType}`);
192
+ console.log(` Status: ${d.deviceStatus}`);
193
+ console.log(` ID: ${d.cid}`);
194
+ });
195
+ }
196
+ // Outlets
197
+ const outlets = manager.devices.filter(d => ['ESO15-TB', 'ESW03-USA', 'wifi-switch-1.3'].includes(d.deviceType));
198
+ if (outlets.length > 0) {
199
+ console.log('\nOUTLETS (' + outlets.length + ' devices):');
200
+ outlets.forEach(d => {
201
+ console.log(` • ${d.deviceName}`);
202
+ console.log(` Type: ${d.deviceType}`);
203
+ console.log(` Status: ${d.deviceStatus}`);
204
+ console.log(` ID: ${d.cid}`);
205
+ });
206
+ }
207
+ // Print summary statistics
208
+ console.log('\nSummary Statistics:');
209
+ console.log('-------------------');
210
+ console.log('Total Devices:', manager.devices.length);
211
+ console.log('fans:', fans.length, 'devices');
212
+ console.log('outlets:', outlets.length, 'devices');
213
+ console.log('switches:', manager.devices.filter(d => d.deviceType.toLowerCase().includes('switch')).length, 'devices');
214
+ console.log('bulbs:', manager.devices.filter(d => d.deviceType.toLowerCase().includes('bulb')).length, 'devices');
215
+ console.log('\n=== End of Device Discovery ===\n');
216
+ // Print detailed status for all devices
217
+ console.log('\n=== Device Status Summary ===');
218
+ for (const device of manager.devices) {
219
+ await printDeviceStatus(device);
220
+ }
221
+ console.log('\nTest complete');
222
+ }
223
+ // Run the test
224
+ runTest().catch(error => {
225
+ console.error('Test failed:', error);
226
+ process.exit(1);
227
+ });
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "tsvesync",
3
+ "version": "1.0.11",
4
+ "description": "A TypeScript library for interacting with VeSync smart home devices",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "clean": "rm -rf dist",
10
+ "prepare": "npm run build",
11
+ "version": "git add -A src",
12
+ "postversion": "git push && git push --tags"
13
+ },
14
+ "keywords": [
15
+ "vesync",
16
+ "iot",
17
+ "smart-home",
18
+ "typescript",
19
+ "air-purifier",
20
+ "humidifier",
21
+ "smart-plug",
22
+ "smart-bulb"
23
+ ],
24
+ "author": "Mick Giles",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/mickgiles/tsvesync.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/mickgiles/tsvesync/issues"
32
+ },
33
+ "homepage": "https://github.com/mickgiles/tsvesync#readme",
34
+ "engines": {
35
+ "node": ">=14.0.0"
36
+ },
37
+ "dependencies": {
38
+ "axios": "^1.7.9",
39
+ "js-yaml": "^4.1.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/js-yaml": "^4.0.9",
43
+ "@types/node": "^22.10.2",
44
+ "dotenv": "^16.4.7",
45
+ "ts-node": "^10.9.2",
46
+ "typescript": "^5.7.2"
47
+ },
48
+ "files": [
49
+ "dist/**/*",
50
+ "src/**/*",
51
+ "api/**/*"
52
+ ]
53
+ }
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Helper functions for VeSync API
3
+ */
4
+
5
+ import axios from 'axios';
6
+ import crypto from 'crypto';
7
+ import { VeSync } from './vesync';
8
+
9
+ // API configuration
10
+ let _apiBaseUrl = 'https://smartapi.vesync.com';
11
+
12
+ export function getApiBaseUrl(): string {
13
+ return _apiBaseUrl;
14
+ }
15
+
16
+ export function setApiBaseUrl(url: string): void {
17
+ _apiBaseUrl = url;
18
+ }
19
+
20
+ export const API_RATE_LIMIT = 30;
21
+ export const API_TIMEOUT = 5000;
22
+
23
+ export const APP_VERSION = '2.8.6';
24
+ export const PHONE_BRAND = 'SM N9005';
25
+ export const PHONE_OS = 'Android';
26
+ export const USER_TYPE = '1';
27
+ export const DEFAULT_TZ = 'America/New_York';
28
+ export const DEFAULT_REGION = 'US';
29
+ export const MOBILE_ID = '1234567890123456';
30
+ export const BYPASS_HEADER_UA = 'okhttp/3.12.1';
31
+
32
+ export interface RequestBody {
33
+ acceptLanguage?: string;
34
+ accountID?: string;
35
+ appVersion?: string;
36
+ cid?: string;
37
+ configModule?: string;
38
+ debugMode?: boolean;
39
+ deviceRegion?: string;
40
+ email?: string;
41
+ method?: string;
42
+ password?: string;
43
+ phoneBrand?: string;
44
+ phoneOS?: string;
45
+ timeZone?: string;
46
+ token?: string;
47
+ traceId?: string;
48
+ userType?: string;
49
+ uuid?: string;
50
+ status?: string;
51
+ [key: string]: any;
52
+ }
53
+
54
+ export class Helpers {
55
+ static shouldRedact = true;
56
+
57
+ /**
58
+ * Calculate MD5 hash
59
+ */
60
+ static md5(text: string): string {
61
+ return crypto.createHash('md5').update(text).digest('hex');
62
+ }
63
+
64
+ /**
65
+ * Build header for legacy api GET requests
66
+ */
67
+ static reqHeaders(manager: VeSync): Record<string, string> {
68
+ if (!manager.accountId || !manager.token) {
69
+ throw new Error('Manager accountId and token must be set');
70
+ }
71
+ return {
72
+ 'accept-language': 'en',
73
+ 'accountId': manager.accountId,
74
+ 'appVersion': APP_VERSION,
75
+ 'content-type': 'application/json',
76
+ 'tk': manager.token,
77
+ 'tz': manager.timeZone
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Build header for api requests on 'bypass' endpoint
83
+ */
84
+ static reqHeaderBypass(): Record<string, string> {
85
+ return {
86
+ 'Content-Type': 'application/json; charset=UTF-8',
87
+ 'User-Agent': BYPASS_HEADER_UA
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Return universal keys for body of api requests
93
+ */
94
+ static reqBodyBase(manager: VeSync): Record<string, string> {
95
+ return {
96
+ 'timeZone': manager.timeZone,
97
+ 'acceptLanguage': 'en'
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Keys for authenticating api requests
103
+ */
104
+ static reqBodyAuth(manager: VeSync): Record<string, any> {
105
+ if (!manager.accountId || !manager.token) {
106
+ throw new Error('Manager accountId and token must be set');
107
+ }
108
+ return {
109
+ 'accountID': manager.accountId,
110
+ 'token': manager.token
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Detail keys for api requests
116
+ */
117
+ static reqBodyDetails(): Record<string, string> {
118
+ return {
119
+ 'appVersion': APP_VERSION,
120
+ 'phoneBrand': PHONE_BRAND,
121
+ 'phoneOS': PHONE_OS,
122
+ 'traceId': Date.now().toString()
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Builder for body of api requests
128
+ */
129
+ static reqBody(manager: VeSync, type: string): Record<string, any> {
130
+ const body = {
131
+ ...this.reqBodyBase(manager)
132
+ };
133
+
134
+ if (type === 'login') {
135
+ return {
136
+ ...body,
137
+ ...this.reqBodyDetails(),
138
+ email: manager.username,
139
+ password: this.hashPassword(manager.password),
140
+ devToken: '',
141
+ userType: USER_TYPE,
142
+ method: 'login'
143
+ };
144
+ }
145
+
146
+ const authBody = {
147
+ ...body,
148
+ ...this.reqBodyAuth(manager)
149
+ };
150
+
151
+ if (type === 'devicestatus') {
152
+ return authBody;
153
+ }
154
+
155
+ const fullBody = {
156
+ ...authBody,
157
+ ...this.reqBodyDetails()
158
+ };
159
+
160
+ switch (type) {
161
+ case 'devicelist':
162
+ return {
163
+ ...fullBody,
164
+ method: 'devices',
165
+ pageNo: '1',
166
+ pageSize: '100'
167
+ };
168
+ case 'devicedetail':
169
+ return {
170
+ ...fullBody,
171
+ method: 'devicedetail',
172
+ mobileId: MOBILE_ID
173
+ };
174
+ case 'bypass':
175
+ return {
176
+ ...fullBody,
177
+ method: 'bypass'
178
+ };
179
+ case 'bypassV2':
180
+ return {
181
+ ...fullBody,
182
+ deviceRegion: DEFAULT_REGION,
183
+ method: 'bypassV2'
184
+ };
185
+ case 'bypass_config':
186
+ return {
187
+ ...fullBody,
188
+ method: 'firmwareUpdateInfo'
189
+ };
190
+ default:
191
+ return fullBody;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Call VeSync API
197
+ */
198
+ static async callApi(
199
+ endpoint: string,
200
+ method: string,
201
+ data: any = null,
202
+ headers: Record<string, string> = {}
203
+ ): Promise<[any, number]> {
204
+ try {
205
+ const url = _apiBaseUrl + endpoint;
206
+ const response = await axios({
207
+ method,
208
+ url,
209
+ data,
210
+ headers,
211
+ timeout: API_TIMEOUT
212
+ });
213
+
214
+ return [response.data, response.status];
215
+ } catch (error: any) {
216
+ if (error.response) {
217
+ return [error.response.data, error.response.status];
218
+ }
219
+ console.error('API call failed:', error.message);
220
+ return [null, 0];
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Calculate hex value from energy response
226
+ */
227
+ static calculateHex(hexStr: string): string {
228
+ if (!hexStr || !hexStr.includes(':')) {
229
+ return '0';
230
+ }
231
+
232
+ const [prefix, value] = hexStr.split(':');
233
+ if (!prefix || !value) {
234
+ return '0';
235
+ }
236
+
237
+ try {
238
+ const decoded = Buffer.from(value, 'hex');
239
+ return decoded.readFloatBE(0).toString();
240
+ } catch (error) {
241
+ console.debug('Error decoding hex value:', error);
242
+ return '0';
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Build energy dictionary from API response
248
+ */
249
+ static buildEnergyDict(result: any): Record<string, any> {
250
+ if (!result) {
251
+ return {};
252
+ }
253
+
254
+ return {
255
+ energy_consumption_of_today: result.energyConsumptionOfToday || 0,
256
+ cost_per_kwh: result.costPerKWH || 0,
257
+ max_energy: result.maxEnergy || 0,
258
+ total_energy: result.totalEnergy || 0,
259
+ data: result.data || [],
260
+ energy_consumption: result.energy || 0,
261
+ start_time: result.startTime || '',
262
+ end_time: result.endTime || ''
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Build configuration dictionary from API response
268
+ */
269
+ static buildConfigDict(result: any): Record<string, any> {
270
+ if (!result) {
271
+ return {};
272
+ }
273
+
274
+ return {
275
+ configModule: result.configModule || '',
276
+ firmwareVersion: result.currentFirmVersion || '',
277
+ deviceRegion: result.deviceRegion || '',
278
+ debugMode: result.debugMode || false,
279
+ deviceTimezone: result.deviceTimezone || DEFAULT_TZ,
280
+ ...result
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Calculate MD5 hash
286
+ */
287
+ static hashPassword(text: string): string {
288
+ return crypto.createHash('md5').update(text).digest('hex');
289
+ }
290
+ }