added variable replacement, extended readme, implemented graceful shutdown

This commit is contained in:
Daniel Sommer 2022-02-16 03:28:17 +01:00
parent e68313270f
commit 6aade27a5a
6 changed files with 78 additions and 36 deletions

View file

@ -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` - 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) - 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 - command: [*string*] command to execute on key press
- args: [*string-array*] arguments to pass to the executed command - args: [*string-array*] arguments to pass to the executed command; variables `{{ key }}` and `{{ type }}` used in args will be replaced

View file

@ -1,6 +1,6 @@
{ {
"log": { "log": {
"level": "debug", "level": "info",
"timestamp": "DD.MM.YYYY HH:mm:ss:SS" "timestamp": "DD.MM.YYYY HH:mm:ss:SS"
}, },
"watchers": [ "watchers": [

View file

@ -6,6 +6,9 @@ const ACTION_KEYUP = { id: 0, action: 'keyup' };
const ACTION_KEYDOWN = { id: 1, action: 'keydown' }; const ACTION_KEYDOWN = { id: 1, action: 'keydown' };
const ACTION_KEYHOLD = { id: 2, action: 'keyhold' }; const ACTION_KEYHOLD = { id: 2, action: 'keyhold' };
const VARIABLE_KEY = '{{ key }}';
const VARIABLE_TYPE = '{{ type }}';
class Keyfilter { class Keyfilter {
constructor(config) { constructor(config) {
if (config == undefined || config.length == 0) { if (config == undefined || config.length == 0) {
@ -66,16 +69,23 @@ class Keyfilter {
if (this.shouldBeDelayed(action)) { if (this.shouldBeDelayed(action)) {
return { key: key, type: action.type.action, delayed: true }; return { key: key, type: action.type.action, delayed: true };
} }
action.executed = new Date().getTime(); action.captured = new Date().getTime();
return { key: key, type: action.type.action, command: action.command, args: action.args }; 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) { 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 false;
} }
return new Date().getTime() - action.executed < action.delay; return new Date().getTime() - action.captured < action.delay;
} }
isValid() { isValid() {
return this.actions != undefined && this.actions.size > 0; return this.actions != undefined && this.actions.size > 0;

View file

@ -25,6 +25,7 @@ class Watcher {
logger.debug('starting watcher \'' + this.device + '\'...'); logger.debug('starting watcher \'' + this.device + '\'...');
this.process = spawn("evtest", [this.device]); this.process = spawn("evtest", [this.device]);
this.attachListeners(); this.attachListeners();
logger.info('watcher \'' + this.device + '\' initialized and capturing configured events');
} }
stop() { stop() {
if (this.process == undefined) { if (this.process == undefined) {
@ -32,9 +33,7 @@ class Watcher {
} }
logger.debug('stopping watcher \'' + this.device + '\'...'); logger.debug('stopping watcher \'' + this.device + '\'...');
this.process.kill(); this.process.kill();
if (this.callback != undefined) { logger.info('watcher \'' + this.device + '\' stopped');
this.callback();
}
} }
attachListeners() { attachListeners() {
if (this.process == undefined) { if (this.process == undefined) {
@ -84,9 +83,13 @@ class Watcher {
} }
logger.debug('adding close listener to watcher \'' + this.device + '\'...'); logger.debug('adding close listener to watcher \'' + this.device + '\'...');
this.process.on('close', (code) => { this.process.on('close', (code) => {
if (code == undefined) {
code = 0;
}
this.code = code;
logger.info('watcher \'' + this.device + '\' finished with exit code ' + code); logger.info('watcher \'' + this.device + '\' finished with exit code ' + code);
if (this.callback != undefined) { 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 + '\'') logger.info('resolved watcher for device \'' + this.device + '\' to \'' + result.path + '\'')
} }
this.device = result.path; this.device = result.path;
resolve(); resolve(this);
}) })
.catch(reject); .catch(reject);
}); });

View file

@ -11,48 +11,57 @@ function initialize() {
} }
var tmp = [] var tmp = []
for (var index = 0; index < configJSON.watchers.length; index++) { 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) { function check(watcher) {
return new Promise(function (resolve, reject) { return new Promise((resolve, reject) => {
if (watcher == undefined) { if (watcher == undefined) {
reject(); reject();
} }
watcher.check() watcher.check()
.then(() => { .then(() => {
logger.info('watcher \'' + watcher.device + '\' initialized and capturing configured events...'); watchers.push(watcher)
watcher.start(); logger.debug('added watcher \'' + watcher.device + '\' to internal map');
watchers.push(watcher);
}) })
.catch((err) => { .then(resolve)
logger.error(err); .catch(reject);
});
}); });
} }
function start() { function start() {
logger.debug('starting ' + watchers.size + ' watcher(s)...'); return new Promise((resolve, reject) => {
for (const watcher of watchers.entries()) { logger.info('starting ' + watchers.length + ' watcher(s)...');
watcher[1].start(); var promises = [];
for (var index = 0; index < watchers.length; index++) {
promises.push(watchers[index].start());
} }
Promise.all(promises)
.then(resolve)
.catch(reject);
});
} }
function stop() { function stop() {
const count = watchers.size; return new Promise((resolve, reject) => {
logger.info('stopping all ' + count + ' watchers...'); logger.info('stopping ' + watchers.length + ' watcher(s)...');
for (var index = 0; index < count; index++) { var promises = [];
var watcher = watchers[index]; for (var index = 0; index < watchers.length; index++) {
logger.debug('stopping watcher ' + watcher.id + '...'); promises.push(watchers[index].stop());
watcher.kill();
} }
Promise.all(promises)
.then(resolve)
.catch(reject);
});
} }
function callback(err, id, code) { function watcherCallback(watcher) {
this.watchers.delete(id); watchers.splice(watchers.findIndex((foundWatcher) => foundWatcher.device == watcher), 1);
logger.debug('removed watcher \'' + id + '\''); logger.debug('removed watcher \'' + watcher + '\' from internal map');
} }
module.exports = { module.exports = {

View file

@ -2,11 +2,31 @@ const watchers = require('./libs/watchers.js');
const logger = require('./libs/logger.js'); const logger = require('./libs/logger.js');
const packageJSON = require('./package.json'); 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() watchers.initialize()
.then(watchers.start) .then(watchers.start)
.catch((err) => { .catch((err) => {
logger.error(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));
});
}
}