Skip to content

How to control odio with a remote control

A remote can drive the node directly, with no application or browser in between: a key press becomes play/pause, next, volume or mute on the active player and the default output. The odio API reads the remote itself, through its input backend.

RemoteHow it is picked up
Infrared receiver (gpio-ir, MCE USB receiver)Automatically
TV remote over HDMI-CECWith input.cec, which the odio installer turns on
GPIO buttons (gpio-keys)Automatically
Bluetooth or USB remoteBy name

Media keys need no configuration: play/pause, play, pause, stop, next, previous, fast forward, rewind, volume up, volume down and mute. Any other key can be bound to an action.

On a headless odio node, remote controls are enabled by default: there is nothing to do. The installer also puts the odio user in the input group, which reading a remote requires.

On a node with a graphical desktop they are off. Enable them in a drop-in, for example ~/.config/odio-api/conf.d/50-input.yml:

input:
enabled: true
cec: true # only for the TV remote over HDMI-CEC

then restart the API:

Terminal window
systemctl --user restart odio-api

The same drop-in with enabled: false turns them off on a headless node, and cec: false alone leaves the HDMI-CEC adapter out. For a standalone go-odio-api, see the system setup.

Infrared receivers and GPIO buttons are handled from there. HDMI-CEC has its own switch, cec, because a Raspberry Pi always has the adapter, listed as vc4-hdmi, whether a TV is plugged in or not. With it on, a TV that forwards its remote keys over CEC can drive odio.

GET /input lists what is handled:

Terminal window
curl http://localhost:8018/input
{
"devices": [
{ "path": "/dev/input/event0", "name": "vc4-hdmi", "kind": "cec", "state": "active" }
],
"bindings": [
{ "key": "KEY_MUTE", "action": "volume.mute" },
{ "key": "KEY_PLAYPAUSE", "action": "player.playpause" }
]
}

A device in state active is being read. denied means the user running the API cannot read the device, see the system setup. A remote that is not listed at all is not picked up automatically, and has to be added by name.

These remotes look like any keyboard to the system, so odio only handles the ones you name. Find the name with the remote plugged in or connected:

Terminal window
grep -H . /sys/class/input/event*/device/name
/sys/class/input/event0/device/name:vc4-hdmi
/sys/class/input/event2/device/name:Logitech K400

Add it to the drop-in, then restart the API:

input:
enabled: true
devices:
- "Logitech K400"

The whole name must match, case aside. Some remotes show up as several devices, for instance one for the regular keys and a Consumer Control one for the media keys: list each one you need.

A remote that is unplugged, or a Bluetooth remote that goes to sleep, is picked up again when it comes back, without a restart.

To find what a key is called, set logLevel: debug in the drop-in, restart the API and follow its log while pressing the key:

Terminal window
journalctl --user -u odio-api -f
[input] unmapped KEY_RED (gpio_ir_recv)

Then bind it:

input:
enabled: true
bindings:
- { key: KEY_POWER, action: power.poweroff }
- { key: KEY_RED, action: service.restart, unit: mpd.service }
- { key: KEY_MUTE, action: none }

none unmaps a default key. A service.* action needs a unit from the systemd whitelist, power.* needs the power backend enabled, and bluetooth.* the Bluetooth backend. The full list of actions is in the input reference.

Set logLevel back to info once the keys are found.

On a Raspberry Pi, an infrared receiver or push buttons wired to the GPIO header need an overlay in /boot/firmware/config.txt, one line each, then a reboot. odio-api picks them up from there, nothing else to configure.

Pins are given in BCM numbering, and always given: the overlays’ own defaults collide with a DAC. With an I2S audio HAT, keep clear of GPIO 2 and 3 (I2C) and GPIO 18 to 21 (I2S), which leaves:

4 5 6 7 8 9 10 11 12 13 14 15 16 17 22 23 24 25 26 27

The gpio-ir overlay, with the pin after gpio_pin:

dtoverlay=gpio-ir,gpio_pin=25

Its default pin, GPIO 18, is the bit clock of an I2S DAC.

The kernel decodes infrared itself, and needs the keymap of your remote loaded to turn its codes into key names. ir-keytable lists the receiver, loads a keymap and, with -t, shows the keys as they are received. Without the right keymap, the receiver is handled but its key presses produce nothing.

The gpio-key overlay, once per button, with its pin after gpio and the key it sends after keycode:

dtoverlay=gpio-key,gpio=17,keycode=164
dtoverlay=gpio-key,gpio=27,keycode=163
dtoverlay=gpio-key,gpio=22,keycode=115,autorepeat
dtoverlay=gpio-key,gpio=23,keycode=114,autorepeat

Wire each button between its pin and ground: the overlay turns the pin’s pull-up on and reads a press as the pin going low. autorepeat lets a held button repeat, which odio acts on for volume, fast forward and rewind.

The key codes are the kernel’s input event codes, as numbers. The media keys odio handles by default:

CodeKey
164Play/pause
207Play
119Pause
166Stop
163Next
165Previous
208Fast forward
168Rewind
115Volume up
114Volume down
113Mute

Any other code can be bound to an action. Always give both parameters: the overlay defaults to GPIO 3, an I2C pin, and to code 116, the power key.