I have access to my friend's Raspbian (which is not entirely up to date). Still, I don't want to mess with the configuration of the device that is not mine, especially if it involves keyboard support; so the following answer is based on my work in Debian 9. I used Raspbian only to confirm the needed packages are available there. Some details may vary between Debian and Raspbian.
Preparation
You need these packages:
lirc
. Its common usage is to read from controllers (like infra red remotes) and send various commands to various compatible programs (like multimedia players). It can also run arbitrary system commands (e.g. scripts) via irexec
executable, this would be your case. In my Debian the lirc
package provides irexec.service
but in (outdated) Raspbian it seems to provide only the executable, not the service. I will address this later in the answer. And then there is…
inputlirc
, zeroconf LIRC daemon using input event devices (like keys on a regular keyboard). We will use its daemon rather than the large lircd.service
(lirc
package is still required because of irexec
we want to use).
input-utils
, utilities useful to set things up.
Install them:
sudo apt-get install lirc inputlirc input-utils
If you want to use a specific keyboard (rather than all available keyboards), find out which device it is:
sudo lsinput
In my case the device I wanted to use was a combo device, it registered as /dev/input/event8
and /dev/input/event9
. To see which one is the right device I used
sudo input-events 9 # for /dev/input/event9
I pressed keys I wanted to use and observed the output. I repeated for event8
. It turned out my device passes "normal" keys via event9
and multimedia keys via event8
.
There's no guarantee the same device will get the same number in the future, after reboot. The OS however supplies symlinks in /dev/input/by-id
. Examine them:
ls -l /dev/input/by-id
These paths shouldn't change, you should prefer them during further configuration.
Configuration
inputlircd
inputlircd
is the daemon from the inputlirc
package. We want to use it to read from keyboard. I think the large LIRC daemon is not really required, so disabling lircd.service
may seem like a good idea. However there are some dependencies which would run LIRC anyway. Rerouting them would make this answer overly complicated, there's no point; so let's keep it as it is.
In my Debian there is /etc/init.d/inputlirc
file. Upon examining I see it uses options from /etc/default/inputlirc
. Set the right values there. My /etc/defalut/inputlirc
now looks like this:
# Options to be passed to inputlirc.
EVENTS="/dev/input/by-id/usb-1ea7_2.4GHZ_Keyboard___Mouse_Combo-event-mouse /dev/input/by-id/usb-1ea7_2.4GHZ_Keyboard___Mouse_Combo-if01-event-kbd"
OPTIONS="-m 0"
Note I used two devices in a form /dev/input/by-id/something
, although to monitor all connected keyboards /dev/input/input*
should be right (it was the out-of-the-box setting). You may want to use -m
, -n
and/or other options. Read man 8 inputlircd
.
After saving the file, enable and (re)start the service:
systemctl enable inputlirc.service
systemctl restart inputlirc.service
and check if it's running:
systemctl status inputlirc.service
irexec.service
In my Debian I have /lib/systemd/system/irexec.service
. My friend's Raspbian lacks the file (even though lirc
package is installed and irexec
is available). If you need to create it manually, this is its original content from my Debian:
[Unit]
Documentation=man:irexec(1)
Documentation=http://lirc.org/html/configure.html
Documentation=http://lirc.org/html/configure.html#lircrc_format
Description=Handle events from IR remotes decoded by lircd(8)
[Service]
; user=lirc
; group=lirc
; Hardening opts, see systemd.exec(5). Doesn't add much unless
; not running as root. If these are applicable or not depends on
; what commands irexec.lircrc invokes.
;
; NoNewPrivileges=true
; MemoryDenyWriteExecute=true
; PrivateTmp=true
; ProtectHome=true
; ProtectSystem=full
Type=simple
ExecStart=/usr/bin/irexec /etc/lirc/irexec.lircrc
[Install]
WantedBy=multi-user.target
The file is owned by root:root
and the permissions are 644
. I guess it's good to add After=inputlirc.service
and Requires=inputlirc.service
in the [Unit]
section. I'm not an expert in dependencies, so this may be sub-optimal or not enough.
If you consult man 1 irexec
, you will see this /etc/lirc/irexec.lircrc
path that appears above is the config file. Put this snippet in the config file:
begin
prog = irexec
button = KEY_MUTE
config = beep -r 5
end
and invoke
systemctl restart irexec.service
to make irexec
start to react to the mute key. Instead of beep
you can use cd /scripts/ && ./toggle.sh
. Now it you press the key, the tool will pass the command to sh
to execute.
To know the key name (e.g. KEY_MUTE
) you can peek what inputlircd
passes through the socket:
socat UNIX-CONNECT:/var/run/lirc/lircd STDOUT
Press keys you want to use and note the output. Names to use are in the third column.
Notes
- In my Debian the default socket for
inputlircd
and irexec
is /var/run/lirc/lircd
(and /run/lirc/lircd
is the same because of the symlink /var/run -> /run
). If in your case the two tools use different sockets, make them use a single one, it's crucial. There are options for this, see the respective manuals.
For versions up to 0.9.1 irexec
used to wait until the executed program terminated. […] This is not required in 0.9.2+ which cannot wait for command completion.
(source)
This means you are able to run multiple instances of the script in parallel, just press the key fast enough. If this shouldn't happen, the command you run or the script itself must detect its previous instance(s) and wait or terminate. I would use a lockfile for this. The script itself may be the lockfile, like this:
config = cd /scripts/ && flock -w 1 ./scriptA.sh ./scriptA.sh
The solution doesn't suppress the "normal" action of the chosen key. You may be concerned about the key littering the login input on TTY1, or even password, if Enter gets hit. This may lead to login attempts. Workarounds:
- switch to unused TTY;
- mask
getty@tty1.service
(see this; I haven't tested this though, I don't know if the OS doesn't switch to a used one automatically);
- run some custom command instead (like in this answer, also not tested by me).
Pay attention as what user the relevant services run. There are also ProtectHome=
and ProtectSystem=
options. These (along with few others) will limit what your script can do. And if some filesystem is encrypted or unmounted, the script won't be able to use it until it gets mounted properly (e.g. if your home directory gets decrypted as late as at the moment you log in, the script won't be able to interact with it until you log in).
Should it always work? or after logging in? Logging in using tty1 or so? or is there any desktop environment you log into? – Kamil Maciorowski – 2019-04-05T22:28:51.223
It should always work and I don't use a desktop environment. Is it possible to have a script run before login? If not I can leave it logged in – Issy Szemeti – 2019-04-05T22:47:17.073
I was planning to use a numpad, as I can put stickers over the keycaps to describe what each key actually does. Also I plan for this keypad to be firmly mounted in one place all the time, which isn't possible for many remotes, as they have rounded shapes and stuff. And also weird small buttons which can't be replaced so you have to remember that the "ok" button is toggle.sh and the "2" button is reload.sh.... Not ideal but I'll look into it as an option – Issy Szemeti – 2019-04-07T11:41:02.330