diff --git a/config.json b/config.json index 6d01576..05fa704 100644 --- a/config.json +++ b/config.json @@ -19,5 +19,25 @@ "modep": { "host": "192.168.1.24", "port": 80 + }, + "blinky": { + "enabled": true, + "host": "192.168.1.31", + "port": 3000, + "events": { + "started": { + "mode": "set", + "args": { + "blinkstick": "square", + "color": "#0f0f0f" + } + }, + "stopped": { + "mode": "poweroff", + "args": { + "blinkstick": "square" + } + } + } } } \ No newline at end of file diff --git a/libs/blinky.js b/libs/blinky.js new file mode 100644 index 0000000..5a29741 --- /dev/null +++ b/libs/blinky.js @@ -0,0 +1,34 @@ +const logger = require('./logger.js'); +const { httpPOST } = require('./util.js'); + +let enabled; + +async function initialize() { + enabled = global.config.blinky !== undefined && + global.config.blinky.enabled && + global.config.blinky.host !== undefined && + global.config.blinky.port !== undefined && + global.config.blinky.events !== undefined; +} + +async function handleEvent(eventName) { + if (!enabled || eventName === undefined) { + return; + } + const event = global.config.blinky.events[eventName]; + if (event === undefined || event.mode === undefined) { + return; + } + logger.info('handling blinky event \'' + eventName + '\'...'); + try { + await httpPOST(global.config.blinky.host, global.config.blinky.port, event.mode, event.args); + logger.debug('blinky event \'' + eventName + '\' successfully handled'); + } catch (err) { + logger.error('blinky event \'' + eventName + '\' encountered an error: ' + err); + } +} + +module.exports = { + initialize, + handleEvent +} \ No newline at end of file diff --git a/libs/logger.js b/libs/logger.js index caba8ad..b15231d 100644 --- a/libs/logger.js +++ b/libs/logger.js @@ -108,6 +108,20 @@ function error(message) { if (loglevel > LOGLEVEL_ERROR) { return; } + if (message.stack) { + trace(message.stack, 'error'); + return; + } + if (message.errors !== undefined) { + for (let index = 0; index < message.errors.length; index++) { + trace(message.errors[index], 'error'); + } + return; + } + if (message.message) { + trace(message.message, 'error'); + return; + } trace(message, 'error'); } diff --git a/libs/server.js b/libs/server.js index aeeacb4..56a1252 100644 --- a/libs/server.js +++ b/libs/server.js @@ -1,21 +1,20 @@ const config = require('../config.json'); const logger = require('./logger.js'); const api = require('./api.js') -const constants = require('./constants.js'); const modep = require('./modep.js'); +const blinky = require('./blinky.js'); const http = require('http'); var server; -function start() { - return new Promise(function (resolve, reject) { - if (!server) { - server = http.createServer(); - } - server.listen(config.server.port, config.server.listen).on('listening', function () { - logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...'); - handleRequests(); - }); +async function start() { + if (!server) { + server = http.createServer(); + } + server.listen(config.server.port, config.server.listen).on('listening', function () { + logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...'); + blinky.handleEvent('started'); + handleRequests(); }); } diff --git a/libs/util.js b/libs/util.js index 9b08a5a..374927e 100644 --- a/libs/util.js +++ b/libs/util.js @@ -2,6 +2,7 @@ const logger = require('./logger.js'); const realpath = require('fs/promises').realpath; const stat = require('fs/promises').stat; const http = require('http'); +const querystring = require('querystring'); const { HTTP_GET, HTTP_POST } = require('./constants.js'); function timeDiff(startTime) { @@ -32,13 +33,20 @@ function httpRequest(host, port, path, method, args) { if (!path.startsWith("/")) { path = "/" + path; } - logger.debug('sending http \'' + method + '\' request to \'' + host + ':' + port + path + '\'...'); - const request = http.request({ + const options = { hostname: host, port: port, path: path, method: method - }, function (response) { + }; + if (args !== undefined) { + args = new URLSearchParams(args).toString(); + options.headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + } + logger.debug('sending http \'' + method + '\' request to \'' + host + ':' + port + path + '\'...'); + const request = http.request(options, function (response) { if (!response) { return reject('error: no response from host for http \'' + method + '\' request \'' + host + ':' + port + path + '\''); } @@ -60,12 +68,15 @@ function httpRequest(host, port, path, method, args) { } catch (err) { return fn(responseData); } - + }); }); request.on('error', function (err) { - return reject('http \'' + method + '\' request \'' + host + ':' + port + path + '\' returned an error >>> ' + err); + return reject('http \'' + method + '\' request \'' + host + ':' + port + path + '\' returned an error >>> ' + err.message); }); + if (args !== undefined) { + request.write(args); + } request.end(); }); } @@ -90,7 +101,7 @@ function fileExists(file) { .then((path) => { stat(path) .then((stats) => { - resolve({path, stats}); + resolve({ path, stats }); }) }) .catch(reject); @@ -110,6 +121,22 @@ function resolvePath(file) { }); } +function exit(err, code) { + if (code === undefined) { + code = 0; + if (err !== undefined) { + code = 1; + } + } + if (err) { + logger.error(err); + logger.error(global.appName + ' ' + global.appVersion + ' ended due to an error'); + } else { + logger.info(global.appName + ' ' + global.appVersion + ' shutting down gracefully') + } + process.exit(code); +} + module.exports = { timeDiff, clone, @@ -118,5 +145,6 @@ module.exports = { toHex, sortById, fileExists, - resolvePath + resolvePath, + exit } \ No newline at end of file diff --git a/pbc.js b/pbc.js index fb88077..cc2dda3 100644 --- a/pbc.js +++ b/pbc.js @@ -2,10 +2,13 @@ const logger = require('./libs/logger.js'); const api = require('./libs/api.js'); const server = require('./libs/server.js') const util = require('./libs/util.js'); +const blinky = require('./libs/blinky.js'); const packageJSON = require('./package.json'); const INTERRUPTS = ['SIGINT', 'SIGTERM']; +global.appName = packageJSON.name; +global.appVersion = packageJSON.version; global.config = process.argv[2] || __dirname + '/config.json'; handleInterrupts(); @@ -16,22 +19,21 @@ util.fileExists(config) global.config.path = result.path; }) .then(logger.initialize) + .then(blinky.initialize) .then(() => { logger.info("launching " + packageJSON.name + " " + packageJSON.version); }) .then(api.setupEndpoints) .then(server.start) .catch((err) => { - logger.error(err); - process.exit(1); + util.exit(err); }); function handleInterrupts() { for (var index = 0; index < INTERRUPTS.length; index++) { - process.once(INTERRUPTS[index], (code) => { - watchers.stop() - .then(exit(code)) - .catch(exit(code)); + process.once(INTERRUPTS[index], async (code) => { + await blinky.handleEvent('stopped'); + util.exit(undefined, code); }); } } \ No newline at end of file