Blog

Mando a distancia en Linux: LIRC + irexec

Mando a distancia en Linux: LIRC + irexec

Mando Asus DH con salida de irw junto con teclas pulsadas por irexec.

Tengo un mando que venía con mi vieja placa base, una Asus P5W DH Deluxe, y he estado investigado si funcionaba en Linux. Lo que viene a continuación está hecho en Fedora 31.

Me ha sido de especial ayuda este artículo, aunque aquí comento alguna cosa más como problemas de permisos y la creación de un nombre de dispositivo persistente.

LIRC

sudo dnf install lirc lirc-remotes

Modificamos /etc/lirc/lirc_options.conf

# These are the default options to lircd, if installed as
# /etc/lirc/lirc_options.conf. See the lircd(8) and lircmd(8)
# manpages for info on the different options.
#
# Some tools including mode2 and irw uses values such as
# driver, device, plugindir and loglevel as fallback values
# in not defined elsewhere.

[lircd]
nodaemon        = False
driver          = asusdh
device          = /dev/usb/hiddev1
output          = /var/run/lirc/lircd
pidfile         = /var/run/lirc/lircd.pid
plugindir       = /usr/lib64/lirc/plugins
permission      = 666
allow-simulate  = No
repeat-max      = 600
effective-user  = lirc
#listen         = [address:]port
#connect        = host[:port]
#loglevel       = 6
#release        = true
#release_suffix = _EVUP
#logfile        = ...
#driver-options = ...

[lircmd]
uinput          = False
nodaemon        = False

# [modinit]
# code = /usr/sbin/modprobe lirc_serial
# code1 = /usr/bin/setfacl -m g:lirc:rw /dev/uinput
# code2 = ...


# [lircd-uinput]
# add-release-events = False
# release-timeout    = 200
# release-suffix     = _EVUP

En mi caso tengo la suerte de que LIRC ya incluye un driver (no de kernel, sino de userspace) «asusdh» para mi receptor USB. Sólo he tenido que cambiar las opciones driver y device . He encontrado el device probando uno a uno los dispositivos, haciendo sudo cat /dev/usb/hiddev<X> y pulsando los botones del mando para comprobar si se producía una salida. En mi caso no se escriben caracteres, pero el cursor salta horizontal o verticalmente según qué botón. Posteriormente veremos cómo hacer una regla udev para dejar un nombre permanente.

A continuación nos situamos en el directorio /etc/lirc/lircd.conf.d y descargamos el fichero de configuración de lirc para mi mando:

 j  /  etc  lirc  lircd.conf.d  1  sudo irdb-get download asus/asusdh.lircd.conf
Downloaded https://sourceforge.net/p/lirc-remotes/code/ci/master/tree/remotes/asus/asusdh.lircd.conf as asusdh.lircd.conf

Arranqué LIRC en la propia consola (sudo /usr/sbin/lircd --nodaemon) junto con irw, un comando de pruebas para mostrar señales recibidas. Me fallaba el acceso al dispositivo al iniciar irw (a partir de la línea Notice: accepted new client on /var/run/lirc/lircd):

 j  ~  sudo /usr/sbin/lircd --nodaemon
lircd-0.10.0[4683]: Info: lircd:  Opening log, level: Info
lircd-0.10.0[4683]: Notice: Running as user lirc
[...]
lircd-0.10.0[4683]: Notice: lircd(asusdh) ready, using /var/run/lirc/lircd
lircd-0.10.0[4683]: Notice: accepted new client on /var/run/lirc/lircd
lircd-0.10.0[4683]: Info: initializing '/dev/usb/hiddev2'
lircd-0.10.0[4683]: Error: unable to open '/dev/usb/hiddev2'
lircd-0.10.0[4683]: Warning: Failed to initialize hardware
lircd-0.10.0[4683]: Error: unable to open '/dev/usb/hiddev2'

El problema era de permisos. /dev/usb/hiddev1 estaba a:

crw-------. 1 root root

Creé un fichero /etc/udev/rules.d/99-asus-dh-remote.rules para cambiarlos:

KERNEL=="hiddev*", ATTRS{idVendor}=="1130", ATTRS{idProduct}=="cc00", MODE="0666"

Podemos encontrar idVendor e idProduct con:

 j  /  etc  udev  rules.d  lsusb
[...]
Bus 003 Device 002: ID 1130:cc00 Tenx Technology, Inc. ASUS DH Remote
[...]

… y recargamos udev:

 j  ~  sudo udevadm control --reload-rules
[sudo] password for j: 
 j  ~  sudo udevadm trigger

Dejando pasar unos segundos deberían cargarse los permisos 0666 que hemos mostrado arriba:

 j  ~  ls -l /dev/usb
total 0
crw-------. 1 root root 180, 96 Jul 13 20:04 hiddev0
crw-rw-rw-. 1 root root 180, 97 Jul 13 20:04 hiddev1
crw-------. 1 root root 180, 98 Jul 13 20:04 hiddev2

Ahora (con LIRC arrancado desde consola, como antes), irw mostraba las pulsaciones:

 j  ~  irw
ff00000000000001 00 KEY_POWER AsusDH
ff00000000000002 00 QUICK_POWER AsusDH
ff00000000000003 00 NOISE_OFF AsusDH
ff00000000000004 00 WIFI AsusDH
ff00000000000006 00 KEY_MAX AsusDH
ff00000000000005 00 AP_LAUNCH AsusDH
ff00000000000007 00 KEY_KPPLUS AsusDH
ff00000000000008 00 REV AsusDH
ff00000000000009 00 KEY_PLAYPAUSE AsusDH
ff0000000000000a 00 KEY_FORWARD AsusDH
ff0000000000000b 00 KEY_KPMINUS AsusDH
ff00000000000009 00 KEY_PLAYPAUSE AsusDH
ff00000000000009 01 KEY_PLAYPAUSE AsusDH
ff00000000000009 02 KEY_PLAYPAUSE AsusDH
ff00000000000009 03 KEY_PLAYPAUSE AsusDH
ff00000000000009 04 KEY_PLAYPAUSE AsusDH
ff00000000000009 05 KEY_PLAYPAUSE AsusDH
ff00000000000009 06 KEY_PLAYPAUSE AsusDH
ff00000000000009 07 KEY_PLAYPAUSE AsusDH

Si pulsamos rápidamente aparece un contador (de 00 a 07 en el ejemplo anterior para KEY_PLAYPAUSE).

Para dejar lircd permanentemente encendido (necesario para seguir con esta guía) paramos la instancia que teníamos en consola y lo arrancamos con systemctl:

sudo systemctl start lircd

y para que inicie al arranque del sistema:

sudo systemctl enable lircd

Nombre de dispositivo persistente

Es muy probable que el nombre /dev/usb/hiddev<X> cambie al reinicio del sistema. Podemos evitarlo utilizando un SYMLINK en nuestra regla udev:

KERNEL=="hiddev*", ATTRS{idVendor}=="1130", ATTRS{idProduct}=="cc00", SYMLINK+="usb/hiddev_asus_dh_remote", MODE="0666"

Recargamos udev nuevamente con

sudo udevadm control --reload-rules
sudo udevadm trigger

Deberíamos ver algo así:

 j  ~  ls -l /dev/usb
total 0
crw-------. 1 root root 180, 96 Jul 14 21:47 hiddev0
crw-rw-rw-. 1 root root 180, 97 Jul 14 21:47 hiddev1
crw-------. 1 root root 180, 98 Jul 14 21:47 hiddev2
lrwxrwxrwx. 1 root root       7 Jul 14 21:47 hiddev_asus_dh_remote -> hiddev1

Actualizamos la opción device de /etc/lirc/lirc_options.conf

[lircd]
nodaemon        = False
driver          = asusdh
device          = /dev/usb/hiddev_asus_dh_remote
[...]

Reiniciamos lircd:

sudo systemctl restart lircd

irexec

Ahora necesitamos convertir las señales de LIRC en algo útil. Para ello empleamos irexec (se instala automáticamente junto con lirc).

En la primera ejecución nos pide crear un fichero de configuración:

 j  ~  .config  irexec
irexec: could not open config files /home/j/.lircrc and /etc/lirc/lircrc
irexec: No such file or directory
Cannot parse config file

Puede ser global en /etc/lirc/lircrc si lo utilizamos como servicio (sudo systemctl start irexec), o en nuestro home ~/.lircrc si lo ejecutamos en nuestra sesión. He seguido con sesión porque según explica /etc/lirc/irexec.lircrc es preferible:

# Note that the system-wide service is useful only in corner-cases.
# Most scenarios are better off with a session service as described in the
# Configuration Guide. However, note that both can also be combined.

Quiero que irexec pulse ciertas teclas, emulando un teclado, que es lo que funciona para YouTube y Netflix dentro de un navegador. Utilizo un script «keycomb» (extraído de aquí) que emplea evemu-event. Me gusta más evemu que xdotool o xte porque no sólo funciona en X11 sino también en Wayland.

El único cambio respecto al original del enlace ha sido la variable EVDEVICE. Creamos el fichero en /usr/local/bin/keycomb

#!/bin/bash
# keycomb.sh

EVDEVICE=/dev/input/by-id/usb-Cypress_USB_Keyboard-event-kbd

for key in $@; do
    sudo evemu-event $EVDEVICE --type EV_KEY --code KEY_$key --value 1 --sync
done


# reverse order
for (( idx=${#@}; idx>0; idx-- )); do
    sudo evemu-event $EVDEVICE --type EV_KEY --code KEY_${!idx} --value 0 --sync
done

He emplado /dev/input/by-id/usb-Cypress_USB_Keyboard-event-kbd en lugar de su equivalente en mi caso /dev/input/event4 por si me cambia al reiniciar.

Un problemilla es que evemu-event necesita permisos de root. Para que funcione automáticamente en irexec sin necesidad meter contraseñas, he modificado /etc/sudoers (con sudo visudo; cambie «j» por su nombre de usuario):

j     ALL = NOPASSWD:/usr/local/bin/keycomb

Ahora usamos keycomb con irexec. Creamos ~/.lircrc

begin
    prog   = irexec
    button = KEY_POWER
    config = sudo systemctl suspend
end

begin
    prog   = irexec
    button = QUICK_POWER
    repeat = 1
    config = sudo keycomb HOME
end

begin
    prog   = irexec
    button = NOISE_OFF
    repeat = 1
    config = sudo keycomb ESC
end

begin
    prog   = irexec
    button = WIFI
    repeat = 1
    config = sudo keycomb END
end

begin
    prog   = irexec
    button = KEY_MAX
    repeat = 1
    config = sudo keycomb F
end

begin
    prog   = irexec
    button = AP_LAUNCH
    repeat = 1
    config = sudo keycomb LEFTMETA KP7
end


begin
    prog   = irexec
    button = KEY_KPPLUS
    repeat = 1
    config = sudo keycomb UP
end

begin
    prog   = irexec
    button = REV
    repeat = 1
    config = sudo keycomb LEFT
end

begin
    prog   = irexec
    button = KEY_PLAYPAUSE
    repeat = 1
    config = sudo keycomb SPACE
end

begin
    prog   = irexec
    button = KEY_FORWARD
    repeat = 1
    config = sudo keycomb RIGHT
end

begin
    prog   = irexec
    button = KEY_KPMINUS
    repeat = 1
    config = sudo keycomb DOWN
end

La opción repeat es necesaria para que se acepten varias pulsaciones rápidas. La he utilizado en todas menos en la primera, la de suspender, donde no tiene sentido.

Ejecutando irw vemos tanto el mensaje recibido como la pulsación de tecla que manda keycomb:

 j  ~  irw
ff00000000000002 00 QUICK_POWER AsusDH
^[[H
ff00000000000003 00 NOISE_OFF AsusDH
^[
ff00000000000004 00 WIFI AsusDH
^[[F
ff00000000000006 00 KEY_MAX AsusDH
f
ff00000000000005 00 AP_LAUNCH AsusDH
ff00000000000007 00 KEY_KPPLUS AsusDH
^[[A
ff00000000000008 00 REV AsusDH
^[[D
ff00000000000009 00 KEY_PLAYPAUSE AsusDH
 
ff0000000000000a 00 KEY_FORWARD AsusDH
^[[C
ff0000000000000b 00 KEY_KPMINUS AsusDH
^[[B

He ido saltando de línea para que se distinga claramente cada pulsación.

He probado con YouTube y Netflix y funciona. Evidementemente, la ventana del vídeo tiene que tener el foco.

Por último, queda arrancar irexec al inicio de sesión. En mi caso, que utilizo XFCE (junto con i3), se hace desde el menú Applications -> Settings -> Session and Startup -> Pestaña Application Autostart -> Add, con comando «irexec --daemon» y trigger «on login«:

Añadiendo irexec al inicio de sesión de XFCE

NOTA: si reiniciamos lircd, irexec se para, y tendremos que rearrancarlo con irexec --daemon