iobroker.lovelace
Version:
With this adapter you can build visualization for ioBroker with Home Assistant Lovelace UI
257 lines (232 loc) • 10.1 kB
JavaScript
;
const express = require('express');
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
const IoBWebServer = require('@iobroker/webserver');
const ApiServer = require('./lib/server');
const words = require('./admin/words');
/**
* The adapter instance
*
*/
let adapter;
/**
* Starts the adapter instance
*
* @param {Partial<ioBroker.AdapterOptions>} [options] The adapter options
*/
function startAdapter(options) {
// Create the adapter and define its methods
return (adapter = utils.adapter(
Object.assign({}, options, {
name: 'lovelace',
// The ready callback is called when databases are connected and adapter received configuration.
// start here!
ready: () => main(adapter), // Main method defined below for readability
// is called when adapter shuts down - callback has to be called under any circumstances!
unload: callback => {
try {
adapter.log.info('cleaned everything up...');
adapter.apiServer && adapter.apiServer.destroy();
if (adapter.webServer && typeof adapter.webServer.close === 'function') {
adapter.webServer.close(callback);
adapter.webServer = null;
} else {
callback();
}
} catch (e) {
adapter.log.error(`Error on unload: ${e} - ${e?.stack}`);
callback();
}
},
// is called if a subscribed object changes
objectChange: (id, obj) => {
adapter.apiServer.onObjectChange(id, obj);
},
// is called if a subscribed state changes
stateChange: (id, state) => {
if (state) {
// The state was changed
adapter.apiServer.onStateChange(id, state);
} else {
// The state was deleted
adapter.log.info(`state ${id} deleted`);
}
},
message: obj => {
if (obj.command === 'browse') {
obj.callback &&
adapter.sendTo(obj.from, obj.command, adapter.apiServer.getHassStates(), obj.callback);
} else if (obj.command === 'send') {
//*cough*
adapter.apiServer
.onStateChange(`${adapter.namespace}.notifications.add`, { val: obj.message, ack: false })
.then(list => obj.callback && adapter.sendTo(obj.from, obj.command, list, obj.callback));
} else if (obj.command === 'checkIdForDuplicates') {
if (obj.callback) {
if (obj.message) {
const entities = adapter.apiServer.getHassStates();
const params = obj.message;
const entityId = `${params.entity}.${params.name}`;
const objectId = params.objectId;
const entity = entities.find(e => e.entity_id === entityId);
if (entity) {
if (entity.isManual) {
if (entity.context.id === objectId) {
adapter.sendTo(obj.from, obj.command, '', obj.callback);
} else {
adapter.sendTo(obj.from, obj.command, 'labelDuplicateId', obj.callback);
}
} else {
adapter.sendTo(obj.from, obj.command, 'labelOverwriteAutoEntity', obj.callback);
}
} else {
adapter.sendTo(obj.from, obj.command, '', obj.callback);
}
} else {
adapter.sendTo(obj.from, obj.command, 'Internal error - Message null', obj.callback);
}
}
}
},
// Some message was sent to adapter instance over message box. Used by email, pushover, text2speech, ...
// requires "common.message" property to be set to true in io-package.json
// message: (obj) => {
// if (typeof obj === "object" && obj.message) {
// if (obj.command === "send") {
// // e.g. send email or pushover or whatever
// adapter.log.info("send command");
// // Send response in callback if required
// if (obj.callback) adapter.sendTo(obj.from, obj.command, "Message received", obj.callback);
// }
// }
// },
}),
));
}
async function initWebServer(settings) {
const server = {
app: express(),
server: null,
api: null,
io: null,
settings: settings,
};
settings.port = parseInt(settings.port, 10);
if (settings.port) {
if (settings.secure && !adapter.config.certificates) {
return null;
}
try {
const webserver = new IoBWebServer.WebServer({ app: server.app, adapter, secure: settings.secure });
server.server = await webserver.init();
} catch (err) {
adapter.log.error(`Cannot create web-server: ${err}`);
adapter.terminate
? adapter.terminate(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
: process.exit(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
return;
}
if (!server.server) {
adapter.log.error(`Cannot create web-server`);
adapter.terminate
? adapter.terminate(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
: process.exit(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
return;
}
server.server.__server = server;
} else {
adapter.log.error('port missing');
if (adapter.terminate) {
adapter.terminate(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
} else {
process.exit(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
}
}
if (server.server) {
let serverListening = false;
let serverPort = server.settings.port;
server.server.on('error', e => {
if (e.toString().includes('EACCES') && serverPort <= 1024) {
adapter.log.error(
`node.js process has no rights to start server on the port ${serverPort}.\n` +
`Do you know that on linux you need special permissions for ports under 1024?\n` +
`You can call in shell following scrip to allow it for node.js: "iobroker fix"`,
);
} else {
adapter.log.error(`Cannot start server on ${settings.bind || '0.0.0.0'}:${serverPort}: ${e}`);
}
if (!serverListening) {
adapter.terminate
? adapter.terminate(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION)
: process.exit(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
}
});
adapter.getPort(
server.settings.port,
!server.settings.bind || server.settings.bind === '0.0.0.0' ? undefined : server.settings.bind || undefined,
port => {
if (port !== server.settings.port && !adapter.config.findNextPort) {
adapter.log.error(`port ${server.settings.port} already in use`);
if (adapter.terminate) {
adapter.terminate(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
} else {
process.exit(utils.EXIT_CODES.ADAPTER_REQUESTED_TERMINATION);
}
}
serverPort = port;
server.server.listen(
port,
!server.settings.bind || server.settings.bind === '0.0.0.0'
? undefined
: server.settings.bind || undefined,
() => {
serverListening = true;
adapter.log.info(`http${server.settings.secure ? 's' : ''} server listening on port ${port}`);
},
);
},
);
}
if (server.server) {
return server;
}
return null;
}
async function main(adapter) {
if (adapter.config.secure) {
// subscribe on changes of permissions
adapter.subscribeForeignObjects('system.group.*');
adapter.subscribeForeignObjects('system.user.*');
// Load certificates
adapter.getCertificates(async (err, certificates, leConfig) => {
adapter.config.certificates = certificates;
adapter.config.leConfig = leConfig;
adapter.webServer = await initWebServer(adapter.config);
adapter.apiServer = new ApiServer({
adapter,
server: adapter.webServer.server,
app: adapter.webServer.app,
words,
});
});
} else {
adapter.webServer = await initWebServer(adapter.config);
adapter.apiServer = new ApiServer({
adapter,
server: adapter.webServer.server,
app: adapter.webServer.app,
words,
});
}
// examples for the checkPassword/checkGroup functions
/*adapter.checkPassword('admin', 'iobroker', (res) => {
adapter.log.info('check user admin pw iobroker: ' + res);
});*/
}
if (module.parent) {
// Export startAdapter in compact mode
module.exports = startAdapter;
} else {
// otherwise start the instance directly
startAdapter();
}