UNPKG

homebridge-nibe

Version:
266 lines (265 loc) 9.78 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MyUplinkApiFetcher = void 0; const axios_1 = __importDefault(require("axios")); const events_1 = require("events"); const Cache_1 = require("../util/Cache"); const moment_1 = __importDefault(require("moment")); const consts = { baseUrl: 'https://api.myuplink.com', scope: 'READSYSTEM WRITESYSTEM', grantType: 'client_credentials', timeout: 45000, userAgent: 'homebridge-nibe', renewBeforeExpiry: 5 * 60 * 1000, allowedParameters: [40067, 40004, 44362, 40013, 40014, 40008, 40025, 40026, 40075, 40183, 48132, 43437], }; class MyUplinkApiFetcher extends events_1.EventEmitter { constructor(options, log) { super(); this.cache = new Cache_1.Cache(); this.currentlySetting = []; this.options = options; this.log = log; axios_1.default.defaults.baseURL = consts.baseUrl; axios_1.default.defaults.headers.common['user-agent'] = consts.userAgent; axios_1.default.defaults.timeout = consts.timeout; } start() { if (this.interval != null) { return; } this.active = false; const exec = () => { if (this.active) { return; } this.active = true; this.fetch().then(() => { this.active = false; }); }; this.interval = setInterval(exec, this.options.interval * 1000); exec(); } stop() { if (this.interval == null) { return; } clearInterval(this.interval); this.interval = null; } async fetch() { this.log.debug('Fetch data.'); try { if (this.isTokenExpired()) { this.log.debug('Token is expired / expires soon - refreshing'); const token = await this.getToken(); this.setSession(token); } if (this.systems == null) { this.systems = await this.fetchSystems(); } for (const system of this.systems) { const subscriptions = await this.fetchPremiumSubscriptions(system.systemId); for (const device of system.devices) { const deviceInfo = await this.fetchDeviceInfo(device.id); try { const parameters = await this.fetchData(device); const data = MyUplinkApiFetcher.mapData(system, subscriptions, device, deviceInfo, parameters); if (data) { this.log.debug(`Prepared data:\n${JSON.stringify(data)}`); this._onData(data); } } catch (error) { this._onError(error); } } } this.log.debug('All data fetched.'); } catch (error) { this._onError(error); } } async getToken() { var _a; this.log.debug('token()'); const body = { client_id: this.options.clientId, client_secret: this.options.clientSecret, grant_type: consts.grantType, scope: consts.scope, }; const url = '/oauth/token'; try { const now = Date.now(); const { data } = await axios_1.default.post(url, new URLSearchParams(body).toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }); if (this.options.showApiResponse) { this.log.info('Nibe data from ' + url + ': ' + JSON.stringify(data)); } const expiresIn = (_a = data.expires_in) !== null && _a !== void 0 ? _a : 3600; data.expires_at = now + expiresIn * 1000; return data; } catch (error) { throw this.checkError(url, error); } } async fetchSystems() { this.log.debug('Fetch units.'); const response = await this.cache.get('/v2/systems/me', 30, 'MINUTES', async () => { return await this.getFromMyUplink('/v2/systems/me'); }); this.log.debug(`${response.systems.length} units fetched.`); return response.systems; } async fetchDeviceInfo(id) { this.log.debug('Fetch device info.'); return await this.getFromMyUplink(`/v2/devices/${id}`); } async fetchPremiumSubscriptions(id) { this.log.debug('Fetch premium subscriptions info.'); const response = await this.cache.get(`/v2/systems/${id}/subscriptions`, 10, 'MINUTES', async () => { return await this.getFromMyUplink(`/v2/systems/${id}/subscriptions`); }); if (!response) { return []; } return response.subscriptions .filter(s => (0, moment_1.default)(s.validUntil).isAfter((0, moment_1.default)())) .map(s => s.type); } async fetchData(device) { this.log.debug('Fetch units.'); const response = await this.getFromMyUplink(`/v2/devices/${device.id}/points`, { parameters: consts.allowedParameters.join(','), }); this.log.debug(`${response.length} parameters fetched.`); return response; } static mapData(system, subscriptions, device, deviceInfo, response) { var _a, _b; return { system: { systemId: system.systemId, name: system.name, premiumSubscriptions: subscriptions, }, device: { id: device.id, name: device.product.name, serialNumber: device.product.serialNumber, firmwareUpdateAvailable: ((_a = deviceInfo.firmware) === null || _a === void 0 ? void 0 : _a.currentFwVersion) !== ((_b = deviceInfo.firmware) === null || _b === void 0 ? void 0 : _b.desiredFwVersion), }, parameters: response.map(p => { return { id: p.parameterId, name: p.parameterName, unit: p.parameterUnit, value: p.value, }; }), }; } async getFromMyUplink(url, params = {}) { this.log.debug(`GET ${url}, params: ${JSON.stringify(params)}`); try { const { data } = await axios_1.default.get(url, { headers: { Authorization: 'Bearer ' + this.getSession('access_token'), }, params, }); if (this.options.showApiResponse) { this.log.info('Nibe data from ' + url + ': ' + JSON.stringify(data)); } return data; } catch (error) { throw this.checkError(url, error); } } async setValue(deviceId, paramId, value) { const key = deviceId + paramId + JSON.stringify(value); if (this.currentlySetting[key]) { return; } this.active = true; this.currentlySetting[key] = true; const url = `/v2/devices/${deviceId}/points`; const body = {}; body[paramId] = value; this.log.debug(`PUT ${url}, params: ${JSON.stringify(body)}`); try { axios_1.default.patch(url, body, { headers: { Authorization: 'Bearer ' + this.getSession('access_token'), }, }).then(result => { if (this.options.showApiResponse) { this.log.info('Nibe data from ' + url + ': ' + JSON.stringify(result.data)); } }).finally(() => { delete this.currentlySetting[key]; this.active = false; this.fetch(); }); } catch (error) { this.log.error(`error from ${url}: ${JSON.stringify(error)}`); } } checkError(url, error) { this.log.error(`error from ${url}`); if (axios_1.default.isAxiosError(error)) { const axiosError = error; if (axiosError.response != null) { if (axiosError.response.status === 401) { this.clearSession(); } if (axiosError.response.data != null) { const responseText = JSON.stringify(axiosError.response.data, null, ' '); const errorMessage = `${axiosError.response.statusText}: ${responseText}`; return new Error(errorMessage); } else { return new Error(axiosError.response.statusText); } } } return error; } getSession(key) { this.log.debug('Get session.'); return this.auth ? this.auth[key] : null; } setSession(auth) { this.log.debug('Set session.'); this.auth = auth; } clearSession() { this.log.debug('Clear session.'); this.setSession({}); } isTokenExpired() { const expired = (Number(this.getSession('expires_at')) || 0) < Date.now() + consts.renewBeforeExpiry; this.log.debug('Is token expired: ' + expired); return expired; } _onData(data) { this.emit('data', data); } _onError(error) { this.emit('error', error); } } exports.MyUplinkApiFetcher = MyUplinkApiFetcher;