homebridge-eveatmo
Version:
Homebridge plugin which adds a Netatmo weatherstation as HomeKit device and tries to act like Elgato Eve Room/Weather
163 lines (139 loc) • 6.01 kB
JavaScript
'use strict';
var homebridge;
/* eslint-disable-next-line @typescript-eslint/no-require-imports */
var async = require('async');
module.exports = function (pHomebridge) {
homebridge = pHomebridge;
homebridge.registerPlatform('homebridge-eveatmo', 'eveatmo', EveatmoPlatform);
};
/* eslint-disable-next-line @typescript-eslint/no-require-imports */
var netatmo = require('./lib/netatmo-api');
class EveatmoPlatform {
constructor(log, config) {
this.log = log;
this.config = config || {};
this.config.auth = this.config.auth || {};
this.foundAccessories = [];
this.config.weatherstation = typeof config.weatherstation !== 'undefined' ? Boolean(config.weatherstation) : true;
this.config.extra_aq_sensor = typeof config.extra_aq_sensor !== 'undefined' ? Boolean(config.extra_aq_sensor) : true;
this.config.extra_co2_sensor = typeof config.extra_co2_sensor !== 'undefined' ? Boolean(config.extra_co2_sensor) : false;
this.config.co2_alert_threshold = typeof config.co2_alert_threshold !== 'undefined' ? parseInt(config.co2_alert_threshold) : 1000;
this.config.module_suffix = typeof config.module_suffix !== 'undefined' ? config.module_suffix : '';
this.config.log_info_msg = typeof config.log_info_msg !== 'undefined' ? Boolean(config.log_info_msg) : true;
// Normalize grant_type: default to 'refresh_token' when unset
if (!this.config.auth.grant_type) {
this.config.auth.grant_type = 'refresh_token';
}
// If this log message is not seen, most likely the config.js is not found.
this.log.debug('Creating EveatmoPlatform');
if (config.mockapi) {
this.log.warn('CAUTION! USING FAKE NETATMO API: ' + config.mockapi);
/* eslint-disable-next-line @typescript-eslint/no-require-imports */
this.api = require('./lib/netatmo-api-mock')(config.mockapi);
} else {
var badConfig = false;
const auth = this.config.auth;
if (!config.auth) {
this.log.error('\'auth\' section missing in config');
badConfig = true;
} else if (!auth.client_id || !auth.client_secret) {
this.log.error('\'client_id\' and \'client_secret\' are mandatory in \'auth\' section');
badConfig = true;
} else if (auth.grant_type && auth.grant_type !== 'refresh_token' && auth.grant_type !== 'password') {
this.log.error('Unsupported or missing grant_type. Please use \'password\' or \'refresh_token\'');
badConfig = true;
} else if (auth.grant_type === 'refresh_token') {
this.log.info('Authenticating using \'refresh_token\' grant');
if (auth.username || auth.password) {
this.log.error('\'username\' and \'password\' are not used in grant_type \'refresh_token\'');
} else if (!auth.refresh_token) {
this.log.error('\'refresh_token\' not set');
badConfig = true;
}
} else if (auth.grant_type === 'password') {
this.log.info('Authenticating using \'password\' grant');
if (!auth.username || !auth.password) {
this.log.error('\'username\' and \'password\' are mandatory when using grant_type \'password\'');
badConfig = true;
}
}
if (badConfig) {
this.log.error('Bad configuration. Please check the README and the sample config in the repository.');
} else {
this.api = new netatmo(this.config.auth, homebridge);
this.api.on('error', (error) => {
this.log.error('ERROR - Netatmo: ' + error);
});
this.api.on('warning', (error) => {
this.log.warn('WARN - Netatmo: ' + error);
});
}
}
}
accessories(callback) {
this.log.debug('Loading accessories');
// Suspected of deleting existing accessories when there are problems with the API during startup.
// if (!this.api) {
// this.log.error('No API instance available. Not loading any accessories.');
// callback(this.foundAccessories);
// return;
// }
var calls = this.loadDevices();
const collectResult = (result) => {
for (var i = 0; i < result.length; i++) {
for (var j = 0; j < result[i].length; j++) {
this.foundAccessories.push(result[i][j]);
}
}
};
async.parallel(calls, (err, result) => {
if (err) {
this.log.warn('Error loading accessories, retrying in 30s: ' + err);
setTimeout(() => {
var retryCalls = this.loadDevices();
async.parallel(retryCalls, (err2, result2) => {
if (err2) {
this.log.error('Retry also failed. No accessories loaded: ' + err2);
} else {
collectResult(result2);
}
callback(this.foundAccessories);
});
}, 30000);
return;
}
collectResult(result);
callback(this.foundAccessories);
});
}
loadDevices() {
var calls = [];
try {
if (this.config.weatherstation) {
calls.push((callback) => {
/* eslint-disable-next-line @typescript-eslint/no-require-imports */
var DeviceType = require('./device/weatherstation-device.js')(homebridge);
var devType = new DeviceType(this.log, this.api, this.config);
devType.buildAccessoriesForDevices((err, deviceAccessories) => {
callback(err, deviceAccessories);
});
});
}
if (this.config.airquality) {
calls.push((callback) => {
/* eslint-disable-next-line @typescript-eslint/no-require-imports */
var DeviceType = require('./device/airquality-device.js')(homebridge);
var devType = new DeviceType(this.log, this.api, this.config);
devType.buildAccessoriesForDevices((err, deviceAccessories) => {
callback(err, deviceAccessories);
});
});
}
} catch (err) {
this.log('Could not process device');
this.log(err);
this.log(err.stack);
}
return calls;
}
}