homebridge-lg-ac
Version:
A Homebridge plugin for controlling/monitoring LG AirConditioning device via LG ThinQ platform.
208 lines • 8.38 kB
JavaScript
"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, headers) {
return await this.request('get', uri, headers);
}
async postRequest(uri, data, headers) {
return await this.request('post', uri, data, headers);
}
async request(method, uri, data, headers, retry = false) {
var _a;
const requestHeaders = headers || this.defaultHeaders;
const url = resolveUrl((_a = this._gateway) === null || _a === void 0 ? void 0 : _a.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, headers, 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, headers, 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 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') {
return await this.postRequest('service/devices/' + device_id + '/control-sync', {
ctrlKey,
'command': command,
...values,
});
}
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.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);
}
}
exports.API = API;
//# sourceMappingURL=API.js.map