From 6aade27a5a64a2f7b40fa6dec697b0d78efd4ac2 Mon Sep 17 00:00:00 2001 From: velvettear Date: Wed, 16 Feb 2022 03:28:17 +0100 Subject: [PATCH] added variable replacement, extended readme, implemented graceful shutdown --- README.md | 2 +- config.json | 2 +- libs/keyfilter.js | 18 ++++++++++++---- libs/watcher.js | 13 ++++++----- libs/watchers.js | 55 +++++++++++++++++++++++++++-------------------- ninwa.js | 24 +++++++++++++++++++-- 6 files changed, 78 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index d669d83..8e661b8 100644 --- a/README.md +++ b/README.md @@ -55,4 +55,4 @@ configuration is done entirely within the file `config.json`. - type: [*string*] type of the key event; either `keyup`, `keydown` or `keyhold` - delay: [*number*] time in milliseconds until the key will be registered again (mostly useful for keyhold-events) - command: [*string*] command to execute on key press - - args: [*string-array*] arguments to pass to the executed command \ No newline at end of file + - args: [*string-array*] arguments to pass to the executed command; variables `{{ key }}` and `{{ type }}` used in args will be replaced \ No newline at end of file diff --git a/config.json b/config.json index dd0bc65..d8a1e16 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "log": { - "level": "debug", + "level": "info", "timestamp": "DD.MM.YYYY HH:mm:ss:SS" }, "watchers": [ diff --git a/libs/keyfilter.js b/libs/keyfilter.js index 18b6056..32fd71d 100644 --- a/libs/keyfilter.js +++ b/libs/keyfilter.js @@ -6,6 +6,9 @@ const ACTION_KEYUP = { id: 0, action: 'keyup' }; const ACTION_KEYDOWN = { id: 1, action: 'keydown' }; const ACTION_KEYHOLD = { id: 2, action: 'keyhold' }; +const VARIABLE_KEY = '{{ key }}'; +const VARIABLE_TYPE = '{{ type }}'; + class Keyfilter { constructor(config) { if (config == undefined || config.length == 0) { @@ -66,16 +69,23 @@ class Keyfilter { if (this.shouldBeDelayed(action)) { return { key: key, type: action.type.action, delayed: true }; } - action.executed = new Date().getTime(); - return { key: key, type: action.type.action, command: action.command, args: action.args }; + action.captured = new Date().getTime(); + return this.replaceVariables({ key: key, type: action.type.action, command: action.command, args: action.args }); } } } + replaceVariables(filtered) { + for (var index = 0; index < filtered.args.length; index++) { + filtered.args[index] = filtered.args[index].replace(VARIABLE_KEY, filtered.key); + filtered.args[index] = filtered.args[index].replace(VARIABLE_TYPE, filtered.type); + } + return filtered; + } shouldBeDelayed(action) { - if (action.delay == undefined || action.delay == 0 || action.executed == undefined) { + if (action.delay == undefined || action.delay == 0 || action.captured == undefined) { return false; } - return new Date().getTime() - action.executed < action.delay; + return new Date().getTime() - action.captured < action.delay; } isValid() { return this.actions != undefined && this.actions.size > 0; diff --git a/libs/watcher.js b/libs/watcher.js index 62bba09..2b592b3 100644 --- a/libs/watcher.js +++ b/libs/watcher.js @@ -25,6 +25,7 @@ class Watcher { logger.debug('starting watcher \'' + this.device + '\'...'); this.process = spawn("evtest", [this.device]); this.attachListeners(); + logger.info('watcher \'' + this.device + '\' initialized and capturing configured events'); } stop() { if (this.process == undefined) { @@ -32,9 +33,7 @@ class Watcher { } logger.debug('stopping watcher \'' + this.device + '\'...'); this.process.kill(); - if (this.callback != undefined) { - this.callback(); - } + logger.info('watcher \'' + this.device + '\' stopped'); } attachListeners() { if (this.process == undefined) { @@ -84,9 +83,13 @@ class Watcher { } logger.debug('adding close listener to watcher \'' + this.device + '\'...'); this.process.on('close', (code) => { + if (code == undefined) { + code = 0; + } + this.code = code; logger.info('watcher \'' + this.device + '\' finished with exit code ' + code); if (this.callback != undefined) { - this.callback(null, this.device, code); + this.callback(this.device); } }); } @@ -110,7 +113,7 @@ class Watcher { logger.info('resolved watcher for device \'' + this.device + '\' to \'' + result.path + '\'') } this.device = result.path; - resolve(); + resolve(this); }) .catch(reject); }); diff --git a/libs/watchers.js b/libs/watchers.js index 227d539..5b3f2e8 100644 --- a/libs/watchers.js +++ b/libs/watchers.js @@ -11,48 +11,57 @@ function initialize() { } var tmp = [] for (var index = 0; index < configJSON.watchers.length; index++) { - tmp.push(new Watcher(configJSON.watchers[index])); + tmp.push(new Watcher(configJSON.watchers[index], watcherCallback)); } - Promise.all(tmp.map(check)).then(resolve); + Promise.all(tmp.map(check)) + .then(resolve) + .catch(reject); }); } function check(watcher) { - return new Promise(function (resolve, reject) { + return new Promise((resolve, reject) => { if (watcher == undefined) { reject(); } watcher.check() .then(() => { - logger.info('watcher \'' + watcher.device + '\' initialized and capturing configured events...'); - watcher.start(); - watchers.push(watcher); + watchers.push(watcher) + logger.debug('added watcher \'' + watcher.device + '\' to internal map'); }) - .catch((err) => { - logger.error(err); - }); + .then(resolve) + .catch(reject); }); } function start() { - logger.debug('starting ' + watchers.size + ' watcher(s)...'); - for (const watcher of watchers.entries()) { - watcher[1].start(); - } + return new Promise((resolve, reject) => { + logger.info('starting ' + watchers.length + ' watcher(s)...'); + var promises = []; + for (var index = 0; index < watchers.length; index++) { + promises.push(watchers[index].start()); + } + Promise.all(promises) + .then(resolve) + .catch(reject); + }); } function stop() { - const count = watchers.size; - logger.info('stopping all ' + count + ' watchers...'); - for (var index = 0; index < count; index++) { - var watcher = watchers[index]; - logger.debug('stopping watcher ' + watcher.id + '...'); - watcher.kill(); - } + return new Promise((resolve, reject) => { + logger.info('stopping ' + watchers.length + ' watcher(s)...'); + var promises = []; + for (var index = 0; index < watchers.length; index++) { + promises.push(watchers[index].stop()); + } + Promise.all(promises) + .then(resolve) + .catch(reject); + }); } -function callback(err, id, code) { - this.watchers.delete(id); - logger.debug('removed watcher \'' + id + '\''); +function watcherCallback(watcher) { + watchers.splice(watchers.findIndex((foundWatcher) => foundWatcher.device == watcher), 1); + logger.debug('removed watcher \'' + watcher + '\' from internal map'); } module.exports = { diff --git a/ninwa.js b/ninwa.js index a2c48db..7602e58 100644 --- a/ninwa.js +++ b/ninwa.js @@ -2,11 +2,31 @@ const watchers = require('./libs/watchers.js'); const logger = require('./libs/logger.js'); const packageJSON = require('./package.json'); -logger.info(packageJSON.name + ' ' + packageJSON.version); +const INTERRUPTS = ['SIGINT', 'SIGTERM']; + +logger.info(packageJSON.name + ' ' + packageJSON.version + ' starting...'); + +handleInterrupts(); watchers.initialize() .then(watchers.start) .catch((err) => { logger.error(err); - process.exit(1); + exit(1); }); + +function exit(code) { + code = code || 0; + logger.info(packageJSON.name + ' ' + packageJSON.version + ' exiting with code \'' + code + '\'...'); + process.exit(code); +} + +function handleInterrupts() { + for (var index = 0; index < INTERRUPTS.length; index++) { + process.once(INTERRUPTS[index], (code) => { + watchers.stop() + .then(exit(code)) + .catch(exit(code)); + }); + } +}