UNPKG

homebridge-fibaro-home-center

Version:

Homebridge plugin for Fibaro Home Center (2, 2 Lite, 3, 3 Lite, Yubii Home).

373 lines 16.8 kB
"use strict"; // platform.ts 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 () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.FibaroHC = void 0; const fibaroAccessory_1 = require("./fibaroAccessory"); const fibaro_api_1 = require("./fibaro-api"); const setFunctions_1 = require("./setFunctions"); const getFunctions_1 = require("./getFunctions"); const constants = __importStar(require("./constants")); const pollerupdate_1 = require("./pollerupdate"); /** * HomebridgePlatform * This class is the main constructor for your plugin, this is where you should * parse the user config and discover/register accessories with Homebridge. */ class FibaroHC { constructor(log, config, api) { this.log = log; this.config = config; this.api = api; this.Service = this.api.hap.Service; this.Characteristic = this.api.hap.Characteristic; // this is used to track restored cached accessories this.accessories = []; this.updateSubscriptions = []; this.scenes = {}; this.climateZones = {}; this.info = {}; this.loginTimeout = null; this.rooms = []; if (!this.setupConfig()) { return; } this.initializeFibaroClient(); if (this.config.pollerperiod > 0) { this.poller = new pollerupdate_1.Poller(this, this.config.pollerperiod); } this.getFunctions = new getFunctions_1.GetFunctions(this); this.log.debug('Finished initializing platform:', this.config.name); api.on("didFinishLaunching" /* APIEvent.DID_FINISH_LAUNCHING */, async () => { log.debug('Executed didFinishLaunching callback'); this.login(); }); } setupConfig() { if (!this.config) { this.log.error('Fibaro HC configuration: cannot find configuration for the plugin'); return false; } this.setupPollerPeriod(); this.setupThermostatMaxTemp(); this.setupDefaultConfigValues(); this.setupUrl(); return true; } setupPollerPeriod() { let pollerPeriod = this.config.pollerperiod; if (pollerPeriod === undefined) { pollerPeriod = constants.DEFAULT_POLLER_PERIOD; } else { const parsedValue = typeof pollerPeriod === 'string' ? parseFloat(pollerPeriod) : pollerPeriod; if (isNaN(parsedValue) || parsedValue < 0 || parsedValue > 100) { pollerPeriod = constants.DEFAULT_POLLER_PERIOD; } else { pollerPeriod = parsedValue; } } this.config.pollerperiod = pollerPeriod; } setupThermostatMaxTemp() { const thermostatMaxTemp = this.config.thermostatmaxtemperature ? parseInt(this.config.thermostatmaxtemperature) : constants.DEFAULT_THERMOSTAT_MAX_TEMP; if (isNaN(thermostatMaxTemp) || thermostatMaxTemp < 0 || thermostatMaxTemp > constants.DEFAULT_THERMOSTAT_MAX_TEMP) { this.config.thermostatmaxtemperature = constants.DEFAULT_THERMOSTAT_MAX_TEMP; } } setupDefaultConfigValues() { var _a, _b, _c, _d, _e, _f, _g, _h; this.config.markDeadDevices = (_a = this.config.markDeadDevices) !== null && _a !== void 0 ? _a : false; this.config.thermostattimeout = (_b = this.config.thermostattimeout) !== null && _b !== void 0 ? _b : constants.TIME_OFFSET.toString(); this.config.switchglobalvariables = (_c = this.config.switchglobalvariables) !== null && _c !== void 0 ? _c : ''; this.config.dimmerglobalvariables = (_d = this.config.dimmerglobalvariables) !== null && _d !== void 0 ? _d : ''; this.config.securitysystem = this.config.securitysystem === 'enabled' ? 'enabled' : 'disabled'; this.config.FibaroTemperatureUnit = (_e = this.config.FibaroTemperatureUnit) !== null && _e !== void 0 ? _e : 'C'; this.config.addRoomNameToDeviceName = (_f = this.config.addRoomNameToDeviceName) !== null && _f !== void 0 ? _f : 'disabled'; this.config.logsLevel = (_g = this.config.logsLevel) !== null && _g !== void 0 ? _g : '1'; this.config.doorbellDeviceId = (_h = this.config.doorbellDeviceId) !== null && _h !== void 0 ? _h : '0'; } setupUrl() { if (validUrl(this.config.url)) { this.config.url = this.config.url.startsWith('http') ? this.config.url : 'http://' + this.config.url; } else if (validUrl(this.config.host)) { this.config.url = this.config.host.startsWith('http') ? this.config.host : 'http://' + this.config.host; } else { this.log.error('Fibaro HC configuration: cannot find valid url in configuration file.'); throw new Error('Invalid URL configuration'); } } initializeFibaroClient() { this.fibaroClient = new fibaro_api_1.FibaroClient(this.config.url, this.config.username, this.config.password, this.log, this.config.adminUsername, this.config.adminPassword); if (!this.fibaroClient || this.fibaroClient.status === false) { this.log.error('Cannot connect to Fibaro Home Center. Check credentials, url or ca.cer file'); throw new Error('Failed to initialize Fibaro Client'); } } async login() { try { if (!this.fibaroClient) { return; } await this.fetchInfo(); await this.processScenes(); await this.processZones(); this.setFunctions = new setFunctions_1.SetFunctions(this); await this.fetchDevicesAndRooms(); this.processDevices(); this.log.info('Successfully logged in.'); } catch (e) { this.handleLoginError(e); } } async fetchInfo() { this.info = (await this.fibaroClient.getInfo()).body; } async processScenes() { const { body: scenes } = await this.fibaroClient.getScenes(); scenes.forEach((s) => { this.scenes[s.name] = s.id; if (s.name.startsWith('_')) { const device = { name: s.name.substring(1), roomID: 0, id: s.id, type: 'scene' }; this.addAccessory(device); } }); } async processZones() { if (this.isOldApi()) { await this.processHeatingZones(); } else { await this.processClimateZones(); } } async processHeatingZones() { const { body: heatingZones } = await this.fibaroClient.getHeatingZones(); heatingZones.forEach((s) => { this.climateZones[s.name] = s.id; const device = { name: s.name, roomID: 0, id: s.id, type: 'heatingZone', properties: s.properties }; this.addAccessory(device); }); } async processClimateZones() { const { body: climateZones } = await this.fibaroClient.getClimateZones(); climateZones.forEach((s) => { this.climateZones[s.name] = s.id; const device = { name: s.name, roomID: 0, id: s.id, type: 'climateZone', properties: s.properties }; this.addAccessory(device); }); } async fetchDevicesAndRooms() { this.devices = (await this.fibaroClient.getDevices()).body; this.rooms = (await this.fibaroClient.getRooms()).body; } handleLoginError(e) { this.log.error('Error getting data from Home Center: ', e); this.log.error('Make sure you provide the correct data: URL or IP, username and password' + ' and that HC is enabled and available on the same network as Homebridge.' + ' Using https may be mandatory if you configured HC to use it.'); this.log.error('Next try in 5 minutes'); if (this.loginTimeout) { clearTimeout(this.loginTimeout); } this.loginTimeout = setTimeout(() => { this.login(); }, constants.RETRY_DELAY_LOGIN); } /** * This function is invoked when homebridge restores cached accessories from disk at startup. * It should be used to setup event handlers for characteristics and update respective values. */ configureAccessory(accessory) { if (this.config.logsLevel === constants.CONFIG_LOGS_LEVEL_VERBOSE) { this.log.info('Loading accessory from cache:', accessory.displayName); } // To enable the removing of cached accessories no more present on Fibaro Home Center accessory.context.reviewed = false; // add the restored accessory to the accessories cache so we can track if it has already been registered this.accessories.push(accessory); } processDevices() { this.log.debug('Processing devices'); this.devices.map((s) => { if (s.visible === true && !s.name.startsWith('_')) { if (this.config.addRoomNameToDeviceName === 'enabled' && this.rooms) { // patch device name with the room name const room = this.getRoomNameById(s.roomID); if (room !== undefined && room !== '') { s.name = s.name + ' - ' + room; } else { s.name = s.name + ' - ' + 'no-room'; } } else if (this.config.addRoomNameToDeviceName === 'enabledBefore' && this.rooms) { // patch device name with the room name const room = this.getRoomNameById(s.roomID); if (room !== undefined && room !== '') { s.name = room + ' ' + s.name; } } this.addAccessory(s); } }); // Create Global Variable Switches and Dimmers const createGlobalVariableDevices = (param, type) => { if (param && param !== '') { const globalVariables = param.split(','); for (let i = 0; i < globalVariables.length; i++) { const device = { name: globalVariables[i], roomID: 0, id: globalVariables[i], type: type }; this.addAccessory(device); } } }; createGlobalVariableDevices(this.config.switchglobalvariables, 'G'); createGlobalVariableDevices(this.config.dimmerglobalvariables, 'D'); // Create Security System accessory if (this.config.securitysystem === 'enabled') { const device = { name: 'FibaroSecuritySystem', roomID: 0, id: 0, type: 'securitySystem' }; this.addAccessory(device); } // Remove not reviewd accessories: cached accessories no more present in Home Center this.accessories.forEach((a) => { if (!a.context.reviewed) { this.removeAccessory(a); } }); // Start the poller update mechanism if (this.poller) { this.poller.poll(); } } addAccessory(device) { if (device === undefined) { return; } const uuidV1 = this.api.hap.uuid.generate(device.name + device.roomID); // add a new seed for uuidV2 to avoid conflicts with devices with same name in same room // manage collision with climate/heating zones (id starting from 1) const seed = device.id.toString() + ((device.type === 'heatingZone' || device.type === 'climateZone') ? 'hc' : ''); const uuidV2 = this.api.hap.uuid.generate(seed); // check if the accessory already exists based on old uuid seed (device.name + device.roomID) // if yes, keep it to preserve compatibility with HomeKit automations // if not, check if the accessory already exists based on new uuid seed (device.id) // if not, create a new accessory with new uuid seed (device.id) const existingAccessoryV1 = this.accessories.find(accessory => accessory.UUID === uuidV1); const existingAccessoryV2 = this.accessories.find(accessory => accessory.UUID === uuidV2); const existingAccessory = existingAccessoryV1 ? existingAccessoryV1 : existingAccessoryV2; if (existingAccessory) { // the accessory already exists if (this.config.logsLevel === constants.CONFIG_LOGS_LEVEL_VERBOSE) { this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName); } // Update context existingAccessory.context.device = device; existingAccessory.context.reviewed = true; this.api.updatePlatformAccessories([existingAccessory]); // Create accessory handler const fa = new fibaroAccessory_1.FibaroAccessory(this, existingAccessory, device); if (!fa.isValid) { this.removeAccessory(existingAccessory); } } else { // the accessory does not yet exist, so we need to create it const accessory = new this.api.platformAccessory(device.name, uuidV2); // Create context accessory.context.device = device; accessory.context.reviewed = true; // Create accessory handler const fa = new fibaroAccessory_1.FibaroAccessory(this, accessory, device); if (fa.isValid) { // link the accessory to the platform this.api.registerPlatformAccessories(constants.PLUGIN_NAME, constants.PLATFORM_NAME, [accessory]); this.accessories.push(accessory); this.log.info('Adding new accessory:', device.name); } } } removeAccessory(accessory) { this.log.info('Remove accessory: ', accessory.displayName); this.api.unregisterPlatformAccessories(constants.PLUGIN_NAME, constants.PLATFORM_NAME, [accessory]); this.accessories.forEach((element, index) => { if (element.UUID === accessory.uuid) { this.accessories.splice(index, 1); } }); } findServiceByName(name, service) { const a = this.accessories.find((accessory) => { const s = accessory.getService(service); if (s && s.displayName === name) { return true; } else { return false; } }); if (a) { return a.getService(service); } else { return undefined; } } isOldApi() { return this.info && this.info.serialNumber && (this.info.serialNumber.indexOf('HC2-') === 0 || this.info.serialNumber.indexOf('HCL-') === 0); } getRoomNameById(roomId) { var _a; return (_a = this.rooms.find(r => r.id === roomId)) === null || _a === void 0 ? void 0 : _a.name; } } exports.FibaroHC = FibaroHC; function validUrl(str) { const pattern = new RegExp('^(https?:\\/\\/)?' + // protocol '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string '(\\#[-a-z\\d_]*)?$', // fragment locator 'i'); return pattern.test(str); } //# sourceMappingURL=platform.js.map