UNPKG

homebridge-vesync-v2

Version:

A Homebridge plugin for controlling VeSync smart devices including outlets, air purifiers, and humidifiers

270 lines (257 loc) 10.1 kB
"use strict"; const moment = require('moment'); const HyperRequest = require('./hrequest'); const crypto = require('crypto'); module.exports = class EtekCityClient { constructor(log, exclude) { this.client = new HyperRequest({ baseUrl: 'https://smartapi.vesync.com', enablePipe : false, respondWithProperty: false, failWhenBadCode : false, parserFunction: function (data) { try { return JSON.parse(data.replace(/\\/g, '').replace('"[', '[').replace(']"', ']')); } catch (e) { log(data); return null; } } }); this.log = log; this.exclude = exclude; this.lastLogin = moment('2000-01-01'); this.traceId = Math.random(); this.terminalId = crypto.randomBytes(16).toString('hex').toUpperCase(); } async login(username, password) { try { if (this.lastLogin.isAfter(moment().subtract(24, 'hours'))) { return Promise.resolve(); } const md5passwd = crypto.createHash('md5').update(password).digest('hex'); const response = await this.client.post('/user/api/accountManage/v3/appLoginV3', { headers: { accept: '*/*', 'content-type': 'application/json', 'user-agent': 'VeSync/5.0.50 (iOS 16.7.2)' }, body: { data: { userType: 1, email: username, password: md5passwd, }, context: { token: '.', terminalId: this.terminalId, osInfo: 'iOS16.7.2', clientInfo: 'Homebridge', traceId: this.traceId, accountID: '.', clientType: 'vesyncApp', userCountryCode: 'US', method: 'appLoginV3', clientVersion: 'VeSync 5.0.50', acceptLanguage: 'en', timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone, debugMode: false, } } }); if (!response?.result?.token) { throw new Error("Login failed: Invalid credentials"); } this.token = response.result.token; this.uniqueId = response.result.accountID; this.lastLogin = moment(); } catch (error) { this.log(`Login error: ${error.message}`); throw error; } } getDevices() { return this.client.post('/cloud/v2/deviceManaged/devices', { headers: { 'content-type': 'application/json', 'user-agent': 'HomeBridge-Vesync' }, body: { 'acceptLanguage': 'en', 'accountID': this.uniqueId, 'appVersion': '1.1', 'method': 'devices', 'pageNo': 1, 'pageSize': 1000, 'phoneBrand': 'HomeBridge-Vesync', 'phoneOS': 'HomeBridge-Vesync', 'timeZone': 'America/Chicago', 'token': this.token, 'traceId': this.traceId, } }).then((response) => { if (response == null || response.result == null) { this.log("Connecting to Vesync failed!"); return null; } else { let devices = response.result.list.filter((dev) => { return !this.exclude.includes(dev.deviceType); }).map((device) => { let id = device.cid; if (device.subDeviceNo) { id = device.cid + device.subDeviceNo; } return { id: id, name: device.deviceName, type: device.deviceType, status: device.deviceStatus, uuid: device.uuid, subDeviceNo: device.subDeviceNo, cid: device.cid, region: device.deviceRegion, configModule: device.configModule }; }); return devices; } }); } turnDevice(device, on_off) { let uri = '/10a/v1/device/devicestatus'; let body = { 'accountID': this.uniqueId, 'timeZone': 'America/Chicago', 'token': this.token, 'status': on_off, 'uuid': device.uuid, 'switchNo': device.subDeviceNo }; // Original device-type logic remains the same: if (device.type === 'Classic300S' || device.type === 'Classic200S' || device.type === 'Dual200S' || device.type === 'OasisMist500S' || device.type === 'LUH-D301S-WUS' || device.type === 'LV600S' || device.type === 'Dual100S' || device.type === 'LUH-A601S-WUSR') { uri = '/cloud/v2/deviceManaged/bypassV2'; body = { 'accountID': this.uniqueId, 'timeZone': 'America/Chicago', 'token': this.token, 'uuid': device.uuid, 'deviceType': device.type, 'method': 'bypassV2', 'data': { 'enabled': on_off === 'on', 'id': 0, 'type': 'power' } }; } else if (device.type === 'LTF-F422S-WUSR' || device.type === 'LTF-F411S-WUS') { uri = '/cloud/v2/deviceManaged/bypassV2'; body = { 'accountID': this.uniqueId, 'timeZone': 'America/Chicago', 'token': this.token, 'uuid': device.uuid, 'deviceType': device.type, 'method': 'bypass', 'data': { 'powerSwitch': on_off === 'on' ? 1 : 0 } }; } else if (device.type === 'LV-PUR131S' || device.type === 'Core200S' || device.type === 'Core300S' || device.type === 'Core400S' || device.type === 'Core600S' || device.type === 'Core100S' || device.type === 'LAP-C201S-AUSR' || device.type === 'LAP-C202S-WUSR' || device.type === 'Vital100S' || device.type === 'Vital200S') { uri = '/cloud/v2/deviceManaged/bypassV2'; body = { method: 'bypassV2', accountID: this.uniqueId, token: this.token, timeZone: 'America/Chicago', deviceRegion: device.region, cid: device.cid, configModule: device.configModule, payload: { data: { enabled: on_off === 'on', id: 0, type: 'power' }, method: 'setSwitch', source: 'APP' } }; } else if (device.type === 'wifi-switch-1.3') { uri = '/v1/wifi-switch-1.3/' + device.id + '/status/' + on_off; } else if (device.type === 'ESO15-TB') { uri = '/outdoorsocket15a/v1/device/devicestatus'; } else if (device.type === 'ESW15-USA' || device.type === 'ESW15-EU') { uri = '/15a/v1/device/devicestatus'; } else if (device.type === 'ESW03-USA' || device.type === 'ESW03-EU' || device.type === 'ESW01-EU') { uri = '/10a/v1/device/devicestatus'; } else if (device.type === 'BSD29' || device.type === 'BSD33' || device.type === 'BSD37') { uri = '/smartplug/v1/device/devicestatus'; } else if (device.type === 'ESWD16') { uri = '/dimmer/v1/device/devicestatus'; } else if (device.type === 'ESWL01' || device.type === 'ESWL03' || device.type === 'WS02') { uri = '/inwallswitch/v1/device/devicestatus'; } else if (device.type === 'WiFiPowerStrip' || device.type === 'ESW03-04') { uri = '/powerstrip/v1/device/devicestatus'; } else if (device.type.startsWith('ESL100') || device.type === 'ESL100CW' || device.type === 'ESL100MC') { uri = '/smartlight/v1/device/devicestatus'; } return this.client.put(uri, { headers: { tk: this.token, accountid: this.uniqueId, 'content-type': 'application/json', 'tz': 'America/Chicago', 'user-agent': 'HomeBridge-Vesync' }, body: body }).then((response) => { return response; }); } async bypassV2(params) { if (!this.token || !this.uniqueId) { throw new Error('Not logged in'); } const body = { method: 'bypassV2', accountID: this.uniqueId, token: this.token, timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone, deviceRegion: params.deviceRegion, cid: params.cid, configModule: params.configModule, payload: params.payload }; return this.client.post('/cloud/v2/deviceManaged/bypassV2', { headers: { 'content-type': 'application/json', 'user-agent': 'HomeBridge-Vesync', tk: this.token, accountid: this.uniqueId }, body: body }); } };