Break out main program flow to PlejdAddon class and add catching of init errors with slow retry
This commit is contained in:
parent
62a6359544
commit
5ac7d9893d
2 changed files with 162 additions and 131 deletions
152
plejd/PlejdAddon.js
Normal file
152
plejd/PlejdAddon.js
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
const EventEmitter = require('events');
|
||||||
|
|
||||||
|
const Configuration = require('./Configuration');
|
||||||
|
const Logger = require('./Logger');
|
||||||
|
const PlejdApi = require('./PlejdApi');
|
||||||
|
const PlejdBLE = require('./PlejdBLE');
|
||||||
|
const MqttClient = require('./MqttClient');
|
||||||
|
const SceneManager = require('./SceneManager');
|
||||||
|
const DeviceRegistry = require('./DeviceRegistry');
|
||||||
|
|
||||||
|
const logger = Logger.getLogger('plejd-main');
|
||||||
|
|
||||||
|
class PlejdAddon extends EventEmitter {
|
||||||
|
bleInitTimeout;
|
||||||
|
config;
|
||||||
|
deviceRegistry;
|
||||||
|
plejdApi;
|
||||||
|
plejdBLE;
|
||||||
|
mqttClient;
|
||||||
|
sceneManager;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.config = Configuration.getOptions();
|
||||||
|
this.deviceRegistry = new DeviceRegistry();
|
||||||
|
|
||||||
|
this.plejdApi = new PlejdApi(this.deviceRegistry);
|
||||||
|
this.plejdBLE = new PlejdBLE(this.deviceRegistry);
|
||||||
|
this.sceneManager = new SceneManager(this.deviceRegistry, this.plejdBle);
|
||||||
|
this.mqttClient = new MqttClient(this.deviceRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
logger.info('Main Plejd addon init()...');
|
||||||
|
|
||||||
|
await this.plejdApi.init();
|
||||||
|
this.sceneManager.init();
|
||||||
|
|
||||||
|
['SIGINT', 'SIGHUP', 'SIGTERM'].forEach((signal) => {
|
||||||
|
process.on(signal, () => {
|
||||||
|
this.mqttClient.disconnect(() => process.exit(0));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mqttClient.on('connected', () => {
|
||||||
|
try {
|
||||||
|
logger.verbose('connected to mqtt.');
|
||||||
|
this.mqttClient.sendDiscoveryToHomeAssistant();
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error in MqttClient.connected callback in main.js', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// subscribe to changes from HA
|
||||||
|
this.mqttClient.on('stateChanged', (device, command) => {
|
||||||
|
try {
|
||||||
|
const deviceId = device.id;
|
||||||
|
|
||||||
|
if (device.typeName === 'Scene') {
|
||||||
|
// we're triggering a scene, lets do that and jump out.
|
||||||
|
// since scenes aren't "real" devices.
|
||||||
|
this.sceneManager.executeScene(device.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = 'OFF';
|
||||||
|
let commandObj = {};
|
||||||
|
|
||||||
|
if (typeof command === 'string') {
|
||||||
|
// switch command
|
||||||
|
state = command;
|
||||||
|
commandObj = {
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
|
||||||
|
// since the switch doesn't get any updates on whether it's on or not,
|
||||||
|
// we fake this by directly send the updateState back to HA in order for
|
||||||
|
// it to change state.
|
||||||
|
this.mqttClient.updateState(deviceId, {
|
||||||
|
state: state === 'ON' ? 1 : 0,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
state = command.state;
|
||||||
|
commandObj = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === 'ON') {
|
||||||
|
this.plejdBLE.turnOn(deviceId, commandObj);
|
||||||
|
} else {
|
||||||
|
this.plejdBLE.turnOff(deviceId, commandObj);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error in MqttClient.stateChanged callback in main.js', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mqttClient.init();
|
||||||
|
|
||||||
|
// init the BLE interface
|
||||||
|
this.plejdBLE.on('connectFailed', () => {
|
||||||
|
logger.verbose('Were unable to connect, will retry.');
|
||||||
|
this._bleInitLoop();
|
||||||
|
});
|
||||||
|
|
||||||
|
// this.plejdBLE.init();
|
||||||
|
|
||||||
|
this.plejdBLE.on('authenticated', () => {
|
||||||
|
logger.verbose('plejd: connected via bluetooth.');
|
||||||
|
});
|
||||||
|
|
||||||
|
// subscribe to changes from Plejd
|
||||||
|
this.plejdBLE.on('stateChanged', (deviceId, command) => {
|
||||||
|
try {
|
||||||
|
this.mqttClient.updateState(deviceId, command);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error in PlejdService.stateChanged callback in main.js', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.plejdBLE.on('sceneTriggered', (deviceId, sceneId) => {
|
||||||
|
try {
|
||||||
|
this.mqttClient.sceneTriggered(sceneId);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Error in PlejdService.sceneTriggered callback in main.js', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await this._bleInitLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _bleInitLoop() {
|
||||||
|
try {
|
||||||
|
if (this.bleInitTimeout) {
|
||||||
|
clearTimeout(this.bleInitTimeout);
|
||||||
|
}
|
||||||
|
await this.plejdBLE.init();
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('Failed BLE init, trying again in 35s', err);
|
||||||
|
this.bleInitTimer = setTimeout(() => {
|
||||||
|
try {
|
||||||
|
this._bleInitLoop();
|
||||||
|
} catch (err2) {
|
||||||
|
logger.warn('Why do we need to catch error here?', err2);
|
||||||
|
}
|
||||||
|
}, 35000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PlejdAddon;
|
||||||
135
plejd/main.js
135
plejd/main.js
|
|
@ -1,144 +1,23 @@
|
||||||
const PlejdApi = require('./PlejdApi');
|
|
||||||
const MqttClient = require('./MqttClient');
|
|
||||||
|
|
||||||
const Logger = require('./Logger');
|
const Logger = require('./Logger');
|
||||||
const PlejdService = require('./PlejdService');
|
const PlejdAddon = require('./PlejdAddon');
|
||||||
const SceneManager = require('./SceneManager');
|
|
||||||
const Configuration = require('./Configuration');
|
|
||||||
|
|
||||||
const logger = Logger.getLogger('plejd-main');
|
const logger = Logger.getLogger('plejd-main');
|
||||||
|
|
||||||
const version = '0.5.1';
|
const version = '0.5.1';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
try {
|
||||||
logger.info(`Starting Plejd add-on v. ${version}`);
|
logger.info(`Starting Plejd add-on v. ${version}`);
|
||||||
|
|
||||||
const config = Configuration.getConfiguration();
|
const addon = new PlejdAddon();
|
||||||
|
|
||||||
if (!config.connectionTimeout) {
|
await addon.init();
|
||||||
config.connectionTimeout = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const plejdApi = new PlejdApi(
|
logger.info('main() finished');
|
||||||
config.site,
|
|
||||||
config.username,
|
|
||||||
config.password,
|
|
||||||
config.includeRoomsAsLights,
|
|
||||||
);
|
|
||||||
const client = new MqttClient(config.mqttBroker, config.mqttUsername, config.mqttPassword);
|
|
||||||
|
|
||||||
['SIGINT', 'SIGHUP', 'SIGTERM'].forEach((signal) => {
|
|
||||||
process.on(signal, () => {
|
|
||||||
client.disconnect(() => process.exit(0));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
plejdApi.login().then(() => {
|
|
||||||
// load all sites and find the one that we want (from config)
|
|
||||||
plejdApi.getSites().then((site) => {
|
|
||||||
// load the site and retrieve the crypto key
|
|
||||||
plejdApi.getSite(site.site.siteId).then((cryptoKey) => {
|
|
||||||
// parse all devices from the API
|
|
||||||
const devices = plejdApi.getDevices();
|
|
||||||
|
|
||||||
client.on('connected', () => {
|
|
||||||
try {
|
|
||||||
logger.verbose('connected to mqtt.');
|
|
||||||
client.discover(devices);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Error in MqttClient.connected callback in main.js', err);
|
logger.error('Catastrophic error. Resetting entire addon in 1 minute', err);
|
||||||
|
setTimeout(() => main(), 60000);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
client.init();
|
|
||||||
|
|
||||||
// init the BLE interface
|
|
||||||
const sceneManager = new SceneManager(plejdApi.site, devices);
|
|
||||||
const plejd = new PlejdService(
|
|
||||||
cryptoKey,
|
|
||||||
devices,
|
|
||||||
sceneManager,
|
|
||||||
config.connectionTimeout,
|
|
||||||
config.writeQueueWaitTime,
|
|
||||||
);
|
|
||||||
plejd.on('connectFailed', () => {
|
|
||||||
logger.verbose('Were unable to connect, will retry connection in 10 seconds.');
|
|
||||||
setTimeout(() => {
|
|
||||||
plejd
|
|
||||||
.init()
|
|
||||||
.catch((e) => logger.error('Error in init() from connectFailed in main.js', e));
|
|
||||||
}, 10000);
|
|
||||||
});
|
|
||||||
|
|
||||||
plejd.init();
|
|
||||||
|
|
||||||
plejd.on('authenticated', () => {
|
|
||||||
logger.verbose('plejd: connected via bluetooth.');
|
|
||||||
});
|
|
||||||
|
|
||||||
// subscribe to changes from Plejd
|
|
||||||
plejd.on('stateChanged', (deviceId, command) => {
|
|
||||||
try {
|
|
||||||
client.updateState(deviceId, command);
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Error in PlejdService.stateChanged callback in main.js', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
plejd.on('sceneTriggered', (deviceId, scene) => {
|
|
||||||
try {
|
|
||||||
client.sceneTriggered(scene);
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Error in PlejdService.sceneTriggered callback in main.js', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// subscribe to changes from HA
|
|
||||||
client.on('stateChanged', (device, command) => {
|
|
||||||
try {
|
|
||||||
const deviceId = device.id;
|
|
||||||
|
|
||||||
if (device.typeName === 'Scene') {
|
|
||||||
// we're triggering a scene, lets do that and jump out.
|
|
||||||
// since scenes aren't "real" devices.
|
|
||||||
plejd.triggerScene(device.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let state = 'OFF';
|
|
||||||
let commandObj = {};
|
|
||||||
|
|
||||||
if (typeof command === 'string') {
|
|
||||||
// switch command
|
|
||||||
state = command;
|
|
||||||
commandObj = {
|
|
||||||
state,
|
|
||||||
};
|
|
||||||
|
|
||||||
// since the switch doesn't get any updates on whether it's on or not,
|
|
||||||
// we fake this by directly send the updateState back to HA in order for
|
|
||||||
// it to change state.
|
|
||||||
client.updateState(deviceId, {
|
|
||||||
state: state === 'ON' ? 1 : 0,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
state = command.state;
|
|
||||||
commandObj = command;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state === 'ON') {
|
|
||||||
plejd.turnOn(deviceId, commandObj);
|
|
||||||
} else {
|
|
||||||
plejd.turnOff(deviceId, commandObj);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Error in MqttClient.stateChanged callback in main.js', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue