UNPKG

homebridge-levoit-air-purifier

Version:
291 lines 13.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HumidifierBypassMethod = exports.BypassMethod = void 0; const axios_1 = __importDefault(require("axios")); const async_lock_1 = __importDefault(require("async-lock")); const crypto_1 = __importDefault(require("crypto")); const deviceTypes_1 = __importStar(require("./deviceTypes")); const VeSyncHumidifier_1 = __importDefault(require("./VeSyncHumidifier")); const VeSyncFan_1 = __importDefault(require("./VeSyncFan")); var BypassMethod; (function (BypassMethod) { BypassMethod["STATUS"] = "getPurifierStatus"; BypassMethod["MODE"] = "setPurifierMode"; BypassMethod["NIGHT"] = "setNightLight"; BypassMethod["DISPLAY"] = "setDisplay"; BypassMethod["LOCK"] = "setChildLock"; BypassMethod["SWITCH"] = "setSwitch"; BypassMethod["SPEED"] = "setLevel"; })(BypassMethod || (exports.BypassMethod = BypassMethod = {})); var HumidifierBypassMethod; (function (HumidifierBypassMethod) { HumidifierBypassMethod["HUMIDITY"] = "setTargetHumidity"; HumidifierBypassMethod["STATUS"] = "getHumidifierStatus"; HumidifierBypassMethod["MIST_LEVEL"] = "setVirtualLevel"; HumidifierBypassMethod["MODE"] = "setHumidityMode"; HumidifierBypassMethod["DISPLAY"] = "setDisplay"; HumidifierBypassMethod["SWITCH"] = "setSwitch"; HumidifierBypassMethod["LEVEL"] = "setLevel"; })(HumidifierBypassMethod || (exports.HumidifierBypassMethod = HumidifierBypassMethod = {})); const lock = new async_lock_1.default(); const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); class VeSync { constructor(email, password, debugMode, log) { this.email = email; this.password = password; this.debugMode = debugMode; this.log = log; this.VERSION = '1.3.1'; this.AGENT = `VeSync/VeSync 3.0.51(F5321;HomeBridge-VeSync ${this.VERSION})`; this.TIMEZONE = 'America/New_York'; this.OS = 'HomeBridge-VeSync'; this.LANG = 'en'; this.AXIOS_OPTIONS = { baseURL: 'https://smartapi.vesync.com', timeout: 30000 }; } generateDetailBody() { return { appVersion: this.VERSION, phoneBrand: this.OS, traceId: Date.now(), phoneOS: this.OS }; } generateBody(includeAuth = false) { return { acceptLanguage: this.LANG, timeZone: this.TIMEZONE, ...(includeAuth ? { accountID: this.accountId, token: this.token } : {}) }; } generateV2Body(fan, method, data = {}) { return { method: 'bypassV2', debugMode: false, deviceRegion: fan.region, cid: fan.cid, configModule: fan.configModule, payload: { data: { ...data }, method, source: 'APP' } }; } async sendCommand(fan, method, body = {}) { return lock.acquire('api-call', async () => { var _a; try { if (!this.api) { throw new Error('The user is not logged in!'); } this.debugMode.debug('[SEND COMMAND]', `Sending command ${method} to ${fan.name}`, `with (${JSON.stringify(body)})...`); const response = await this.api.put('cloud/v2/deviceManaged/bypassV2', { ...this.generateV2Body(fan, method, body), ...this.generateDetailBody(), ...this.generateBody(true) }); if (!(response === null || response === void 0 ? void 0 : response.data)) { this.debugMode.debug('[SEND COMMAND]', 'No response data!! JSON:', JSON.stringify(response)); } const isSuccess = ((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code) === 0; if (!isSuccess) { this.debugMode.debug('[SEND COMMAND]', `Failed to send command ${method} to ${fan.name}`, `with (${JSON.stringify(body)})!`, `Response: ${JSON.stringify(response)}`); } await delay(500); return isSuccess; } catch (error) { this.log.error(`Failed to send command ${method} to ${fan === null || fan === void 0 ? void 0 : fan.name}`, `Error: ${error === null || error === void 0 ? void 0 : error.message}`); return false; } }); } async getDeviceInfo(fan, humidifier = false) { return lock.acquire('api-call', async () => { try { if (!this.api) { throw new Error('The user is not logged in!'); } this.debugMode.debug('[GET DEVICE INFO]', 'Getting device info...'); const response = await this.api.post('cloud/v2/deviceManaged/bypassV2', { ...this.generateV2Body(fan, humidifier ? HumidifierBypassMethod.STATUS : BypassMethod.STATUS), ...this.generateDetailBody(), ...this.generateBody(true) }); if (!(response === null || response === void 0 ? void 0 : response.data)) { this.debugMode.debug('[GET DEVICE INFO]', 'No response data!! JSON:', JSON.stringify(response)); } await delay(500); this.debugMode.debug('[GET DEVICE INFO]', 'JSON:', JSON.stringify(response.data)); return response.data; } catch (error) { this.log.error(`Failed to get device info for ${fan === null || fan === void 0 ? void 0 : fan.name}`, `Error: ${error === null || error === void 0 ? void 0 : error.message}`); return null; } }); } async startSession() { this.debugMode.debug('[START SESSION]', 'Starting auth session...'); const firstLoginSuccess = await this.login(); setInterval(this.login.bind(this), 1000 * 60 * 55); return firstLoginSuccess; } async login() { return lock.acquire('api-call', async () => { try { if (!this.email || !this.password) { throw new Error('Email and password are required'); } this.debugMode.debug('[LOGIN]', 'Logging in...'); const pwdHashed = crypto_1.default .createHash('md5') .update(this.password) .digest('hex'); const response = await axios_1.default.post('cloud/v1/user/login', { email: this.email, password: pwdHashed, devToken: '', userType: 1, method: 'login', token: '', ...this.generateDetailBody(), ...this.generateBody() }, { ...this.AXIOS_OPTIONS }); if (!(response === null || response === void 0 ? void 0 : response.data)) { this.debugMode.debug('[LOGIN]', 'No response data!! JSON:', JSON.stringify(response)); return false; } const { result } = response.data; const { token, accountID } = result !== null && result !== void 0 ? result : {}; if (!token || !accountID) { this.debugMode.debug('[LOGIN]', 'The authentication failed!! JSON:', JSON.stringify(response.data)); return false; } this.debugMode.debug('[LOGIN]', 'The authentication success'); this.accountId = accountID; this.token = token; this.api = axios_1.default.create({ ...this.AXIOS_OPTIONS, headers: { 'content-type': 'application/json', 'accept-language': this.LANG, accountid: this.accountId, 'user-agent': this.AGENT, appversion: this.VERSION, tz: this.TIMEZONE, tk: this.token } }); await delay(500); return true; } catch (error) { this.log.error('Failed to login', `Error: ${error === null || error === void 0 ? void 0 : error.message}`); return false; } }); } async getDevices() { return lock.acquire('api-call', async () => { var _a, _b, _c; try { if (!this.api) { throw new Error('The user is not logged in!'); } const response = await this.api.post('cloud/v2/deviceManaged/devices', { method: 'devices', pageNo: 1, pageSize: 1000, ...this.generateDetailBody(), ...this.generateBody(true) }); if (!(response === null || response === void 0 ? void 0 : response.data)) { this.debugMode.debug('[GET DEVICES]', 'No response data!! JSON:', JSON.stringify(response)); return { purifiers: [], humidifiers: [] }; } if (!Array.isArray((_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.list)) { this.debugMode.debug('[GET DEVICES]', 'No list found!! JSON:', JSON.stringify(response.data)); return { purifiers: [], humidifiers: [] }; } const { list } = (_c = response.data.result) !== null && _c !== void 0 ? _c : { list: [] }; this.debugMode.debug('[GET DEVICES]', 'Device List -> JSON:', JSON.stringify(list)); let purifiers = list .filter(({ deviceType, type, extension }) => !!deviceTypes_1.default.find(({ isValid }) => isValid(deviceType)) && type === 'wifi-air' && !!(extension === null || extension === void 0 ? void 0 : extension.fanSpeedLevel)) .map(VeSyncFan_1.default.fromResponse(this)); // Newer Vital purifiers purifiers = purifiers.concat(list .filter(({ deviceType, type, deviceProp }) => !!deviceTypes_1.default.find(({ isValid }) => isValid(deviceType)) && type === 'wifi-air' && !!deviceProp) .map((fan) => ({ ...fan, extension: { ...fan.deviceProp, airQualityLevel: fan.deviceProp.AQLevel, mode: fan.deviceProp.workMode } })) .map(VeSyncFan_1.default.fromResponse(this))); const humidifiers = list .filter(({ deviceType, type, extension }) => !!deviceTypes_1.humidifierDeviceTypes.find(({ isValid }) => isValid(deviceType)) && type === 'wifi-air' && !extension) .map(VeSyncHumidifier_1.default.fromResponse(this)); await delay(1500); return { purifiers, humidifiers }; } catch (error) { this.log.error('Failed to get devices', `Error: ${error === null || error === void 0 ? void 0 : error.message}`); return { purifiers: [], humidifiers: [] }; } }); } } exports.default = VeSync; //# sourceMappingURL=VeSync.js.map