UNPKG

@elshaer/homebridge-lg-thinq

Version:

A Homebridge plugin for controlling/monitoring LG ThinQ device via LG ThinQ platform.

260 lines 10.5 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.API = void 0; const constants = __importStar(require("./constants")); const url_1 = require("url"); const Session_1 = require("./Session"); const Gateway_1 = require("./Gateway"); const request_1 = require("./request"); const Auth_1 = require("./Auth"); const errors_1 = require("../errors"); const crypto_1 = __importDefault(require("crypto")); const axios_1 = __importDefault(require("axios")); function resolveUrl(from, to) { const url = new url_1.URL(to, from); return url.href; } class API { constructor(country = 'US', language = 'en-US') { this.country = country; this.language = language; this.session = new Session_1.Session('', '', 0); this.httpClient = request_1.requestClient; this.logger = console; } async getRequest(uri) { return await this.request('get', uri); } async postRequest(uri, data) { return await this.request('post', uri, data); } async request(method, uri, data, retry = false) { var _a, _b; // eslint-disable-next-line max-len const requestHeaders = (((_a = this._gateway) === null || _a === void 0 ? void 0 : _a.thinq1_url) && uri.startsWith(this._gateway.thinq1_url)) ? this.monitorHeaders : this.defaultHeaders; const url = resolveUrl((_b = this._gateway) === null || _b === void 0 ? void 0 : _b.thinq2_url, uri); return await this.httpClient.request({ method, url, data, headers: requestHeaders, }).then(res => res.data).catch(async (err) => { if (err instanceof errors_1.TokenExpiredError && !retry) { return await this.refreshNewToken().then(async () => { return await this.request(method, uri, data, true); }).catch((err) => { this.logger.debug('refresh new token error: ', err); return {}; }); } else if (err instanceof errors_1.ManualProcessNeeded) { this.logger.warn('Handling new term agreement... If you keep getting this message, ' + err.message); await this.auth.handleNewTerm(this.session.accessToken) .then(() => { this.logger.warn('LG new term agreement is accepted.'); }) .catch(err => { this.logger.debug(err); }); if (!retry) { // retry 1 times return await this.request(method, uri, data, true); } else { return {}; } } else { if (axios_1.default.isAxiosError(err)) { this.logger.debug('request error: ', err.response); } else if (!(err instanceof errors_1.NotConnectedError)) { this.logger.debug('request error: ', err); } return {}; } }); } get monitorHeaders() { var _a, _b; const monitorHeaders = { 'Accept': 'application/json', 'x-thinq-application-key': 'wideq', 'x-thinq-security-key': 'nuts_securitykey', }; if (typeof ((_a = this.session) === null || _a === void 0 ? void 0 : _a.accessToken) === 'string') { monitorHeaders['x-thinq-token'] = (_b = this.session) === null || _b === void 0 ? void 0 : _b.accessToken; } if (this.jsessionId) { monitorHeaders['x-thinq-jsessionId'] = this.jsessionId; } return monitorHeaders; } get defaultHeaders() { function random_string(length) { const result = []; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const charactersLength = characters.length; for (let i = 0; i < length; i++) { result.push(characters.charAt(Math.floor(Math.random() * charactersLength))); } return result.join(''); } const headers = {}; if (this.session.accessToken) { headers['x-emp-token'] = this.session.accessToken; } if (this.userNumber) { headers['x-user-no'] = this.userNumber; } headers['x-client-id'] = this.client_id || constants.API_CLIENT_ID; return { 'x-api-key': constants.API_KEY, 'x-thinq-app-ver': '3.6.1200', 'x-thinq-app-type': 'NUTS', 'x-thinq-app-level': 'PRD', 'x-thinq-app-os': 'ANDROID', 'x-thinq-app-logintype': 'LGE', 'x-service-code': 'SVC202', 'x-country-code': this.country, 'x-language-code': this.language, 'x-service-phase': 'OP', 'x-origin': 'app-native', 'x-model-name': 'samsung/SM-G930L', 'x-os-version': 'AOS/7.1.2', 'x-app-version': 'LG ThinQ/3.6.12110', 'x-message-id': random_string(22), 'user-agent': 'okhttp/3.14.9', ...headers, }; } async getSingleDevice(device_id) { return await this.getRequest('service/devices/' + device_id).then(data => data.result); } async getListDevices() { const homes = await this.getListHomes(); const devices = []; // get all devices in home for (let i = 0; i < homes.length; i++) { const resp = await this.getRequest('service/homes/' + homes[i].homeId); devices.push(...resp.result.devices); } return devices; } async getListHomes() { if (!this._homes) { this._homes = await this.getRequest('service/homes').then(data => data.result.item); } return this._homes; } async sendCommandToDevice(device_id, values, command, ctrlKey = 'basicCtrl', ctrlPath = 'control-sync') { return await this.postRequest('service/devices/' + device_id + '/' + ctrlPath, { ctrlKey, 'command': command, ...values, }); } async sendMonitorCommand(deviceId, cmdOpt, workId) { const data = { cmd: 'Mon', cmdOpt, deviceId, workId, }; return await this.thinq1PostRequest('rti/rtiMon', data); } async getMonitorResult(device_id, work_id) { return await this.thinq1PostRequest('rti/rtiResult', { workList: [{ deviceId: device_id, workId: work_id }] }) .then(data => { if (!('workList' in data) || !('returnCode' in data.workList)) { return null; } const workList = data.workList; if (workList.returnCode !== '0000') { throw new errors_1.MonitorError(data); } if (!('returnData' in workList)) { return null; } return Buffer.from(workList.returnData, 'base64'); }); } setRefreshToken(refreshToken) { this.session = new Session_1.Session('', refreshToken, 0); } setUsernamePassword(username, password) { this.username = username; this.password = password; } async gateway() { if (!this._gateway) { const gateway = await request_1.requestClient.get(constants.GATEWAY_URL, { headers: this.defaultHeaders }).then(res => res.data.result); this._gateway = new Gateway_1.Gateway(gateway); } return this._gateway; } async ready() { var _a; // get gateway first const gateway = await this.gateway(); if (!this.auth) { this.auth = new Auth_1.Auth(gateway); this.auth.logger = this.logger; } if (!this.session.hasToken() && this.username && this.password) { this.session = await this.auth.login(this.username, this.password); await this.refreshNewToken(this.session); } if (!this.session.hasValidToken() && !!this.session.refreshToken) { await this.refreshNewToken(this.session); } if (!this.jsessionId) { // get new jsessionid this.jsessionId = await this.auth.getJSessionId(this.session.accessToken); } if (!this.userNumber) { this.userNumber = await this.auth.getUserNumber((_a = this.session) === null || _a === void 0 ? void 0 : _a.accessToken); } if (!this.client_id) { const hash = crypto_1.default.createHash('sha256'); this.client_id = hash.update(this.userNumber + (new Date()).getTime()).digest('hex'); } } async refreshNewToken(session = null) { session = session || this.session; this.session = await this.auth.refreshNewToken(session); this.jsessionId = await this.auth.getJSessionId(this.session.accessToken); } async thinq1PostRequest(endpoint, data) { var _a; return await this.postRequest(((_a = this._gateway) === null || _a === void 0 ? void 0 : _a.thinq1_url) + endpoint, { lgedmRoot: data, }).then(data => data.lgedmRoot); } } exports.API = API; //# sourceMappingURL=API.js.map