UNPKG

homebridge-velux-active

Version:

Homebridge plugin for controlling velux blinds and windows

193 lines 8.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.VeluxActivePlatform = void 0; const node_fetch_1 = __importDefault(require("node-fetch")); const settings_1 = require("./settings"); const VeluxAccessory_1 = require("./VeluxAccessory"); class VeluxActivePlatform { constructor(log, config, api) { this.log = log; this.config = config; this.api = api; this.baseUrl = 'https://app.velux-active.com/'; this.clientId = '5931426da127d981e76bdd3f'; // clientId of the app - not personal this.clientSecret = '6ae2d89d15e767ae5c56b456b452d319'; // clientSecret of the app - not personal this.Service = this.api.hap.Service; this.Characteristic = this.api.hap.Characteristic; // this is used to track restored cached accessories this.accessories = []; this.apiToken = ''; this.refreshToken = ''; this.homeId = ''; this.devices = []; this.lastTokenRefresh = undefined; this.tokenWillExpire = undefined; this.lastDeviceUpdate = undefined; this.refreshing = false; this.log.debug('Finished initializing platform:', this.config.name); this.api.on('didFinishLaunching', async () => { log.debug('Executed didFinishLaunching callback'); let success = false; let retries = 0; do { const retrievedApiKey = await this.retrieveApiKey(config.username, config.password); if (retrievedApiKey) { const retrievedHome = await this.retrieveHomeId(); if (retrievedHome) { success = await this.retrieveDevicesStatus(); } } await this.delay(5000); retries = retries + 1; } while (!success && retries <= 3); this.log.info('Init complete, creating devices'); this.createDevices(); }); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async setTokens(response) { const result = await response.json(); this.apiToken = result.access_token; this.refreshToken = result.refresh_token; this.lastTokenRefresh = new Date(); this.tokenWillExpire = new Date(); this.tokenWillExpire.setSeconds(this.tokenWillExpire.getSeconds() + result.expires_in); this.log.info('Token will expire: ', this.tokenWillExpire); } configureAccessory(accessory) { this.log.info('Loading accessory from cache:', accessory.displayName); this.accessories.push(accessory); } async retrieveApiKey(username, password) { var _a, _b; if (this.tokenWillExpire !== undefined && (((_a = this.tokenWillExpire) === null || _a === void 0 ? void 0 : _a.getTime()) - new Date().getTime()) > 0) { this.log.debug('Token still valid', new Date(), this.tokenWillExpire, (((_b = this.tokenWillExpire) === null || _b === void 0 ? void 0 : _b.getTime()) - new Date().getTime()) > 0); return true; } this.log.debug('Getting new token'); try { const encoded = encodeURIComponent(username); const response = await (0, node_fetch_1.default)(this.baseUrl + 'oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `grant_type=password&client_id=${this.clientId}&client_secret=${this.clientSecret}&username=${encoded}&password=${password}&user_prefix=velux` }); if (!response.ok) { this.log.error(`Could not retrieve api key. Status ${response.status}`); return false; } await this.setTokens(response); this.log.info('Successfully retrieved api token'); return true; } catch (e) { this.log.error('Something went wrong while trying to retrieve api key', e); return false; } } async retrieveNewToken() { var _a, _b; if (this.tokenWillExpire !== undefined && (((_a = this.tokenWillExpire) === null || _a === void 0 ? void 0 : _a.getTime()) - new Date().getTime()) > 0) { this.log.debug('Token still valid', new Date(), this.tokenWillExpire, (((_b = this.tokenWillExpire) === null || _b === void 0 ? void 0 : _b.getTime()) - new Date().getTime()) > 0); return true; } try { const response = await (0, node_fetch_1.default)(this.baseUrl + 'oauth2/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `grant_type=refresh_token&refresh_token=${this.refreshToken}&client_id=${this.clientId}&client_secret=${this.clientSecret}` }); await this.setTokens(response); this.log.info('Successfully refreshed token'); return true; } catch (e) { this.log.error('Could not refresh token'); return false; } } async retrieveHomeId() { try { const response = await (0, node_fetch_1.default)(this.baseUrl + 'api/gethomedata', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `access_token=${this.apiToken}` }); const result = await response.json(); this.homeId = result.body.homes[0].id; this.log.info('Successfully retrieved home id'); return true; } catch (e) { this.log.error('Could not retrieve home id'); return false; } } async retrieveDevicesStatus(delayTime = 2000) { if (this.refreshing) { this.log.debug('already refreshing state'); await this.delay(500); // wait for the update return true; } if (this.lastDeviceUpdate !== undefined && (new Date().getTime() - this.lastDeviceUpdate.getTime() < delayTime)) { this.log.debug('Using cached state as it is not older than 2s'); return true; } this.refreshing = true; try { await this.retrieveNewToken(); this.lastDeviceUpdate = new Date(); const response = await (0, node_fetch_1.default)(this.baseUrl + 'api/homestatus', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `access_token=${this.apiToken}&home_id=${this.homeId}` }); const result = await response.json(); this.log.debug('Got devices status', result); this.devices = result.body.home.modules.filter(m => m.type === 'NXO'); this.log.debug('Successfully retrieved devices from velux'); return true; } catch (e) { this.log.error('Could not retrieve devices from velux', e); return false; } finally { this.refreshing = false; } } createDevices() { this.devices.forEach(d => { const uuid = this.api.hap.uuid.generate(d.id); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (existingAccessory) { this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName); // eslint-disable-next-line no-new new VeluxAccessory_1.VeluxAccessory(this, existingAccessory, d); } else { this.log.info('Adding new accessory:', d.id); // eslint-disable-next-line new-cap const accessory = new this.api.platformAccessory(d.id, uuid); // eslint-disable-next-line no-new new VeluxAccessory_1.VeluxAccessory(this, accessory, d); this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]); } }); } } exports.VeluxActivePlatform = VeluxActivePlatform; //# sourceMappingURL=platform.js.map