2025-09-12 08:36:02 +02:00
|
|
|
const { EventEmitter } = require('events');
|
2021-02-01 21:30:44 +01:00
|
|
|
|
|
|
|
|
const Configuration = require('./Configuration');
|
|
|
|
|
const Logger = require('./Logger');
|
|
|
|
|
const PlejdApi = require('./PlejdApi');
|
2021-02-20 07:55:26 +01:00
|
|
|
const PlejdDeviceCommunication = require('./PlejdDeviceCommunication');
|
2021-02-01 21:30:44 +01:00
|
|
|
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;
|
2021-02-20 07:55:26 +01:00
|
|
|
plejdDeviceCommunication;
|
2021-02-01 21:30:44 +01:00
|
|
|
mqttClient;
|
2021-02-20 15:33:06 +01:00
|
|
|
processCleanupFunc;
|
2021-02-01 21:30:44 +01:00
|
|
|
sceneManager;
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
|
|
|
|
|
this.config = Configuration.getOptions();
|
|
|
|
|
this.deviceRegistry = new DeviceRegistry();
|
|
|
|
|
|
|
|
|
|
this.plejdApi = new PlejdApi(this.deviceRegistry);
|
2021-02-20 07:55:26 +01:00
|
|
|
this.plejdDeviceCommunication = new PlejdDeviceCommunication(this.deviceRegistry);
|
|
|
|
|
this.sceneManager = new SceneManager(this.deviceRegistry, this.plejdDeviceCommunication);
|
2021-02-01 21:30:44 +01:00
|
|
|
this.mqttClient = new MqttClient(this.deviceRegistry);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-20 15:33:06 +01:00
|
|
|
cleanup() {
|
|
|
|
|
this.mqttClient.cleanup();
|
|
|
|
|
this.mqttClient.removeAllListeners();
|
|
|
|
|
this.plejdDeviceCommunication.cleanup();
|
|
|
|
|
this.plejdDeviceCommunication.removeAllListeners();
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-01 21:30:44 +01:00
|
|
|
async init() {
|
|
|
|
|
logger.info('Main Plejd addon init()...');
|
|
|
|
|
|
|
|
|
|
await this.plejdApi.init();
|
|
|
|
|
this.sceneManager.init();
|
|
|
|
|
|
2021-02-20 15:33:06 +01:00
|
|
|
this.processCleanupFunc = () => {
|
|
|
|
|
this.cleanup();
|
|
|
|
|
this.processCleanupFunc = () => {};
|
|
|
|
|
this.mqttClient.disconnect(() => process.exit(0));
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-01 21:30:44 +01:00
|
|
|
['SIGINT', 'SIGHUP', 'SIGTERM'].forEach((signal) => {
|
2021-02-20 15:33:06 +01:00
|
|
|
process.on(signal, this.processCleanupFunc);
|
2021-02-01 21:30:44 +01:00
|
|
|
});
|
|
|
|
|
|
2025-09-12 08:36:02 +02:00
|
|
|
// Eagerly send discovery as soon as possible
|
|
|
|
|
try {
|
|
|
|
|
logger.verbose('Eagerly sending discovery to Home Assistant.');
|
|
|
|
|
this.mqttClient.sendDiscoveryToHomeAssistant();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.error('Error in eager discovery send', err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send discovery again on MQTT connect to ensure Home Assistant receives device info after reconnects or broker restarts.
|
2021-02-20 15:33:06 +01:00
|
|
|
this.mqttClient.on(MqttClient.EVENTS.connected, () => {
|
2021-02-01 21:30:44 +01:00
|
|
|
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
|
2021-03-31 20:04:45 +02:00
|
|
|
this.mqttClient.on(
|
|
|
|
|
MqttClient.EVENTS.stateChanged,
|
|
|
|
|
/** @param device {import('./types/DeviceRegistry').OutputDevice} */
|
|
|
|
|
(device, command) => {
|
|
|
|
|
try {
|
|
|
|
|
const { uniqueId } = device;
|
|
|
|
|
|
2025-09-12 08:36:02 +02:00
|
|
|
if (device.typeName === MqttClient.DEVICE_TYPES.SCENE) {
|
2021-03-31 20:04:45 +02:00
|
|
|
// we're triggering a scene, lets do that and jump out.
|
|
|
|
|
// since scenes aren't "real" devices.
|
|
|
|
|
this.sceneManager.executeScene(uniqueId);
|
2021-05-20 13:49:44 +02:00
|
|
|
|
|
|
|
|
// since the scene doesn't get any updates on whether it's executed or not,
|
|
|
|
|
// we fake this by directly send the sceneTriggered back to HA in order for
|
|
|
|
|
// it continue to acto on the scene (for non-plejd devices).
|
|
|
|
|
try {
|
|
|
|
|
this.mqttClient.sceneTriggered(uniqueId);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.error('Error in PlejdService.sceneTriggered callback', err);
|
|
|
|
|
}
|
2021-03-31 20:04:45 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let state = false;
|
|
|
|
|
let commandObj = {};
|
|
|
|
|
|
|
|
|
|
if (typeof command === 'string') {
|
|
|
|
|
// switch command
|
2025-09-12 08:36:02 +02:00
|
|
|
state = command === MqttClient.STATE.ON;
|
2021-03-31 20:04:45 +02:00
|
|
|
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.updateOutputState(uniqueId, {
|
|
|
|
|
state,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// eslint-disable-next-line prefer-destructuring
|
2025-09-12 08:36:02 +02:00
|
|
|
state = command.state === MqttClient.STATE.ON;
|
2021-03-31 20:04:45 +02:00
|
|
|
commandObj = command;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state) {
|
|
|
|
|
this.plejdDeviceCommunication.turnOn(uniqueId, commandObj);
|
|
|
|
|
} else {
|
|
|
|
|
this.plejdDeviceCommunication.turnOff(uniqueId, commandObj);
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.error('Error in MqttClient.stateChanged callback', err);
|
2021-02-01 21:30:44 +01:00
|
|
|
}
|
2021-03-31 20:04:45 +02:00
|
|
|
},
|
|
|
|
|
);
|
2021-02-01 21:30:44 +01:00
|
|
|
|
|
|
|
|
this.mqttClient.init();
|
|
|
|
|
|
|
|
|
|
// subscribe to changes from Plejd
|
2021-02-20 15:33:06 +01:00
|
|
|
this.plejdDeviceCommunication.on(
|
|
|
|
|
PlejdDeviceCommunication.EVENTS.stateChanged,
|
2021-03-31 20:04:45 +02:00
|
|
|
(uniqueOutputId, command) => {
|
2021-02-20 15:33:06 +01:00
|
|
|
try {
|
2021-03-31 20:04:45 +02:00
|
|
|
this.mqttClient.updateOutputState(uniqueOutputId, command);
|
2021-02-20 15:33:06 +01:00
|
|
|
} catch (err) {
|
2021-02-22 09:50:06 +01:00
|
|
|
logger.error('Error in PlejdService.stateChanged callback', err);
|
2021-02-20 15:33:06 +01:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2021-05-06 07:58:06 +02:00
|
|
|
this.plejdDeviceCommunication.on(
|
|
|
|
|
PlejdDeviceCommunication.EVENTS.buttonPressed,
|
|
|
|
|
(deviceId, deviceInput) => {
|
|
|
|
|
try {
|
|
|
|
|
this.mqttClient.buttonPressed(deviceId, deviceInput);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.error('Error in PlejdService.buttonPressed callback', err);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
2021-05-01 19:41:29 +02:00
|
|
|
|
2021-02-27 09:57:29 +01:00
|
|
|
this.plejdDeviceCommunication.on(PlejdDeviceCommunication.EVENTS.sceneTriggered, (sceneId) => {
|
|
|
|
|
try {
|
|
|
|
|
this.mqttClient.sceneTriggered(sceneId);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
logger.error('Error in PlejdService.sceneTriggered callback', err);
|
|
|
|
|
}
|
|
|
|
|
});
|
2021-02-01 21:30:44 +01:00
|
|
|
|
2021-02-20 07:55:26 +01:00
|
|
|
await this.plejdDeviceCommunication.init();
|
2021-02-08 19:54:24 +01:00
|
|
|
logger.info('Main init done');
|
2021-02-01 21:30:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = PlejdAddon;
|