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.
- package/LICENSE +21 -0
- package/README.md +227 -0
- package/api/vesync/VeSync.yaml +33 -0
- package/api/vesyncbulb/ESL100.yaml +74 -0
- package/api/vesyncbulb/ESL100CW.yaml +123 -0
- package/api/vesyncbulb/ESL100MC.yaml +169 -0
- package/api/vesyncbulb/XYD0001.yaml +193 -0
- package/api/vesyncfan/Classic200S.yaml +280 -0
- package/api/vesyncfan/Classic300S.yaml +278 -0
- package/api/vesyncfan/Core200S.yaml +256 -0
- package/api/vesyncfan/Core300S.yaml +281 -0
- package/api/vesyncfan/Core400S.yaml +281 -0
- package/api/vesyncfan/Core600S.yaml +281 -0
- package/api/vesyncfan/Dual200S.yaml +278 -0
- package/api/vesyncfan/LAP-C201S-AUSR.yaml +256 -0
- package/api/vesyncfan/LAP-C202S-WUSR.yaml +256 -0
- package/api/vesyncfan/LAP-C301S-WAAA.yaml +281 -0
- package/api/vesyncfan/LAP-C301S-WJP.yaml +281 -0
- package/api/vesyncfan/LAP-C302S-WUSB.yaml +281 -0
- package/api/vesyncfan/LAP-C401S-WAAA.yaml +281 -0
- package/api/vesyncfan/LAP-C401S-WJP.yaml +281 -0
- package/api/vesyncfan/LAP-C401S-WUSR.yaml +281 -0
- package/api/vesyncfan/LAP-C601S-WEU.yaml +281 -0
- package/api/vesyncfan/LAP-C601S-WUS.yaml +281 -0
- package/api/vesyncfan/LAP-C601S-WUSR.yaml +281 -0
- package/api/vesyncfan/LAP-EL551S-AEUR.yaml +167 -0
- package/api/vesyncfan/LAP-EL551S-AUS.yaml +167 -0
- package/api/vesyncfan/LAP-EL551S-WEU.yaml +167 -0
- package/api/vesyncfan/LAP-EL551S-WUS.yaml +167 -0
- package/api/vesyncfan/LAP-V102S-AASR.yaml +167 -0
- package/api/vesyncfan/LAP-V102S-AUSR.yaml +167 -0
- package/api/vesyncfan/LAP-V102S-WEU.yaml +167 -0
- package/api/vesyncfan/LAP-V102S-WJP.yaml +167 -0
- package/api/vesyncfan/LAP-V102S-WUS.yaml +167 -0
- package/api/vesyncfan/LAP-V201-AUSR.yaml +167 -0
- package/api/vesyncfan/LAP-V201S-AASR.yaml +167 -0
- package/api/vesyncfan/LAP-V201S-AEUR.yaml +167 -0
- package/api/vesyncfan/LAP-V201S-AUSR.yaml +167 -0
- package/api/vesyncfan/LAP-V201S-WEU.yaml +167 -0
- package/api/vesyncfan/LAP-V201S-WJP.yaml +167 -0
- package/api/vesyncfan/LAP-V201S-WUS.yaml +167 -0
- package/api/vesyncfan/LEH-S601S-WUS.yaml +303 -0
- package/api/vesyncfan/LEH-S601S-WUSR.yaml +82 -0
- package/api/vesyncfan/LTF-F422S-KEU.yaml +165 -0
- package/api/vesyncfan/LTF-F422S-WUS.yaml +165 -0
- package/api/vesyncfan/LTF-F422S-WUSR.yaml +167 -0
- package/api/vesyncfan/LTF-F422_WJP.yaml +165 -0
- package/api/vesyncfan/LUH-A601S-AUSW.yaml +82 -0
- package/api/vesyncfan/LUH-A601S-WUSB.yaml +305 -0
- package/api/vesyncfan/LUH-A602S-WEU.yaml +305 -0
- package/api/vesyncfan/LUH-A602S-WEUR.yaml +305 -0
- package/api/vesyncfan/LUH-A602S-WJP.yaml +305 -0
- package/api/vesyncfan/LUH-A602S-WUS.yaml +305 -0
- package/api/vesyncfan/LUH-A602S-WUSC.yaml +305 -0
- package/api/vesyncfan/LUH-A602S-WUSR.yaml +305 -0
- package/api/vesyncfan/LUH-D301S-WEU.yaml +305 -0
- package/api/vesyncfan/LUH-D301S-WJP.yaml +305 -0
- package/api/vesyncfan/LUH-D301S-WUSR.yaml +305 -0
- package/api/vesyncfan/LUH-M101S-WUS.yaml +278 -0
- package/api/vesyncfan/LUH-O451S-WEU.yaml +278 -0
- package/api/vesyncfan/LUH-O451S-WUS.yaml +278 -0
- package/api/vesyncfan/LUH-O451S-WUSR.yaml +305 -0
- package/api/vesyncfan/LUH-O601S-KUS.yaml +305 -0
- package/api/vesyncfan/LUH-O601S-WUS.yaml +305 -0
- package/api/vesyncfan/LV-PUR131S.yaml +108 -0
- package/api/vesyncfan/LV-RH131S.yaml +108 -0
- package/api/vesyncoutlet/ESO15-TB.yaml +124 -0
- package/api/vesyncoutlet/ESW01-EU.yaml +122 -0
- package/api/vesyncoutlet/ESW03-USA.yaml +122 -0
- package/api/vesyncoutlet/ESW15-USA.yaml +156 -0
- package/api/vesyncoutlet/wifi-switch-1.3.yaml +60 -0
- package/api/vesyncswitch/ESWD16.yaml +128 -0
- package/api/vesyncswitch/ESWL01.yaml +56 -0
- package/api/vesyncswitch/ESWL03.yaml +56 -0
- package/dist/lib/helpers.d.ts +87 -0
- package/dist/lib/helpers.js +222 -0
- package/dist/lib/helpers.js.map +1 -0
- package/dist/lib/vesync.d.ts +47 -0
- package/dist/lib/vesync.js +309 -0
- package/dist/lib/vesync.js.map +1 -0
- package/dist/lib/vesyncBaseDevice.d.ts +68 -0
- package/dist/lib/vesyncBaseDevice.js +116 -0
- package/dist/lib/vesyncBaseDevice.js.map +1 -0
- package/dist/lib/vesyncBulb.d.ts +0 -0
- package/dist/lib/vesyncBulb.js +2 -0
- package/dist/lib/vesyncBulb.js.map +1 -0
- package/dist/lib/vesyncFan.d.ts +92 -0
- package/dist/lib/vesyncFan.js +265 -0
- package/dist/lib/vesyncFan.js.map +1 -0
- package/dist/lib/vesyncKitchen.d.ts +0 -0
- package/dist/lib/vesyncKitchen.js +2 -0
- package/dist/lib/vesyncKitchen.js.map +1 -0
- package/dist/lib/vesyncOutlet.d.ts +0 -0
- package/dist/lib/vesyncOutlet.js +2 -0
- package/dist/lib/vesyncOutlet.js.map +1 -0
- package/dist/lib/vesyncSwitch.d.ts +0 -0
- package/dist/lib/vesyncSwitch.js +2 -0
- package/dist/lib/vesyncSwitch.js.map +1 -0
- package/dist/src/lib/helpers.d.ts +88 -0
- package/dist/src/lib/helpers.js +244 -0
- package/dist/src/lib/vesync.d.ts +89 -0
- package/dist/src/lib/vesync.js +430 -0
- package/dist/src/lib/vesyncBaseDevice.d.ts +43 -0
- package/dist/src/lib/vesyncBaseDevice.js +87 -0
- package/dist/src/lib/vesyncBulb.d.ts +79 -0
- package/dist/src/lib/vesyncBulb.js +198 -0
- package/dist/src/lib/vesyncBulbImpl.d.ts +38 -0
- package/dist/src/lib/vesyncBulbImpl.js +161 -0
- package/dist/src/lib/vesyncFan.d.ts +80 -0
- package/dist/src/lib/vesyncFan.js +406 -0
- package/dist/src/lib/vesyncFanImpl.d.ts +137 -0
- package/dist/src/lib/vesyncFanImpl.js +643 -0
- package/dist/src/lib/vesyncOutlet.d.ts +53 -0
- package/dist/src/lib/vesyncOutlet.js +98 -0
- package/dist/src/lib/vesyncOutletImpl.d.ts +70 -0
- package/dist/src/lib/vesyncOutletImpl.js +206 -0
- package/dist/src/lib/vesyncSwitch.d.ts +169 -0
- package/dist/src/lib/vesyncSwitch.js +414 -0
- package/dist/src/lib/vesyncSwitchImpl.d.ts +82 -0
- package/dist/src/lib/vesyncSwitchImpl.js +290 -0
- package/dist/test/testFeatures.d.ts +4 -0
- package/dist/test/testFeatures.js +394 -0
- package/dist/test/testService.d.ts +62 -0
- package/dist/test/testService.js +540 -0
- package/dist/test/testSpecial.d.ts +5 -0
- package/dist/test/testSpecial.js +227 -0
- package/package.json +53 -0
- package/src/lib/helpers.ts +290 -0
- package/src/lib/vesync.ts +530 -0
- package/src/lib/vesyncBaseDevice.ts +114 -0
- package/src/lib/vesyncBulb.ts +252 -0
- package/src/lib/vesyncBulbImpl.ts +215 -0
- package/src/lib/vesyncFan.ts +447 -0
- package/src/lib/vesyncFanImpl.ts +848 -0
- package/src/lib/vesyncOutlet.ts +148 -0
- package/src/lib/vesyncOutletImpl.ts +284 -0
- package/src/lib/vesyncSwitch.ts +546 -0
- 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
|
+
}
|