Skip to content

Commit 07208e6

Browse files
vottipaberaVito Zanotellis-martin
authored
Event Device Support (evdev) (#1943)
* Add event device plugin This implements an "event device" listener plugin, that enables the user to configure the phoniebox to respond to events from an "event device" (device under /dev/input). This incluces eg button presses from an USB controller or keyboard. * Adds documentation for the event device plugin Includes a detailed how-to as well as example config * Allow empty button config This is an actual usecase in case someone wants to setup a device and figure out the button ids by looking at the logs * Update README.rst * Improve README - Remove duplicated section as suggested - Add Zero Delay Arcade USB Encoder usecase * Give more meaningfull name to listener thread variable * Link to old documentation * Remove README and fix typo The documentation is now part of the markdown documentation * Remove unecessary plain=1 * Remove indents in example code There was some left over indentation from the old rtf format in the python code examples. * Add example how to access evdev in docker * Example for new Evdev config structure This is tighly modeled after gpio. In contrast to GPIO, this is a list of devices containing each one or more input/output devices. Eg a Joystick has keys but potentially also rumble or leds. Currently only very simply input devices (buttons) are supported. This structer should enable to extend this easily. * Move evdev config to separate file As suggested this should be a separate file, similar to the gpioz config * Adapt evdev code to new config Now the config for evdev is akin to GPIO. The big difference is that multiple devices with each multiple input/output devices are supported. Note that the backend is still the old event device listener backend. Thus to support more features, the backend would need to be quite drastically overhauled. * Update documentation for new evdev config format * Fix wrong reference * Fix bug to correctly check supported input device type * Add more tests for evdev init * Fix wrong attribute name The attribute 'device_request' of EvDevKeyListener is now called 'device_name_request' * Validate input config better Fails if there is no device_name and setts proper default for 'exact' * Add pyzmq installation for github action This is required for some tests. Following the instructions from issue #2050 Specifically: #2050 (comment) * Revert "Add pyzmq installation for github action" This reverts commit 75161b5. As this was not working * Mock `jukebox.publishing` in tests to avoid zmq Currently it is hard to install zmq (#2050) and for CI pytest `zmq` is not available. As the test do not really require `jukebox.publishing` running, mocking it in the test avoids it being imported. Thus the tests do not require `zmq` to be installed. --------- Co-authored-by: pabera <[email protected]> Co-authored-by: Vito Zanotelli <[email protected]> Co-authored-by: s-martin <[email protected]>
1 parent e1e8416 commit 07208e6

File tree

7 files changed

+602
-2
lines changed

7 files changed

+602
-2
lines changed

documentation/builders/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* [Soundcards](./components/soundcards/)
2727
* [HiFiBerry Boards](./components/soundcards/hifiberry.md)
2828
* [RFID Readers](./../developers/rfid/README.md)
29+
* [Event devices (USB and other buttons)](./event-devices.md)
2930

3031
## Web Application
3132

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Event devices
2+
3+
## Background
4+
Event devices are generic input devices that are exposed in `/dev/input`.
5+
This includes USB peripherals (Keyboards, Controllers, Joysticks or Mouse) as well as potentially bluetooth devices.
6+
7+
A specific usecase for this could be, if a Zero Delay Arcade USB Encoder is used to wire arcade buttons instead of using GPIO pins.
8+
9+
These device interface support various kinds of input events, such as press, movements and potentially also outputs (eg. rumble, led lights, ...). Currently only the usage of button presses as input is supported.
10+
11+
This functionality was previously implemented under the name of [USB buttons](https://github.com/MiczFlor/RPi-Jukebox-RFID/blob/develop/components/controls/buttons_usb_encoder/README.md).
12+
13+
The devices and their button mappings need to be mapped in the configuration file.
14+
15+
## Configuration
16+
17+
To configure event devices, first add the plugin as an entry to the module list of your main configuration file ``shared/settings/jukebox.yaml``:
18+
19+
``` yaml
20+
modules:
21+
named:
22+
event_devices: controls.event_devices
23+
```
24+
25+
And add the following section with the plugin specific configuration:
26+
``` yaml
27+
evdev:
28+
enabled: true
29+
config_file: ../../shared/settings/evdev.yaml
30+
```
31+
32+
The actual configuration itself is stored in a separate file. In this case in ``../../shared/settings/evdev.yaml``.
33+
34+
The configuration is structured akin to the configuration of the [GPIO devices](./gpio.md).
35+
36+
In contrast to `gpio`, multiple devices (eg arcade controllser, keyboards, joysticks, mice, ...) are supported, each with their own `input_devices` (=buttons). `output_devices` or actions other than `on_press` are currently not yet supported.
37+
38+
``` yaml
39+
devices: # list of devices to listen for
40+
{device nickname}: # config for a specific device
41+
device_name: {device_name} # name of the device
42+
exact: False/True # optional to require exact match. Otherwise it is sufficient that a part of the name matches
43+
input_devices: # list of buttons to listen for for this device
44+
{button nickname}:
45+
type: Button
46+
kwargs:
47+
key_code: {key-code}: # evdev event id
48+
actions:
49+
on_press: # Currently only the on_press action is supported
50+
{rpc_command_definition} # eg `alias: toggle`
51+
```
52+
The `{device nickname}` is only for your own orientation and can be choosen freely.
53+
For each device you need to figure out the `{device_name}` and the `{event_id}` corresponding to key strokes, as indicated in the sections below.
54+
55+
### Identifying the `{device_name}`
56+
57+
The `{device_name}` can be identified using the following Python snippet:
58+
59+
``` Python
60+
import evdev
61+
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
62+
for device in devices:
63+
print(device.path, device.name, device.phys)
64+
```
65+
66+
The output could be in the style of:
67+
68+
```
69+
/dev/input/event1 Dell Dell USB Keyboard usb-0000:00:12.1-2/input0
70+
/dev/input/event0 Dell USB Optical Mouse usb-0000:00:12.0-2/input0
71+
```
72+
73+
In this example, the `{device_name}` could be `DELL USB Optical Mouse`.
74+
Note that if you use the option `exact: False`, it would be sufficient to add a substring such as `USB Keyboard`.
75+
76+
### Identifying the `{key-code}`
77+
78+
The key code for a button press can be determined using the following code snippet:
79+
80+
``` Python
81+
import evdev
82+
device = evdev.InputDevice('/dev/input/event0')
83+
device.capabilities(verbose=True)[('EV_KEY', evdev.ecodes.EV_KEY)]
84+
```
85+
86+
With the `InputDevice` corresponding to the path from the output of the section `{device_name}` (eg. in the example `/dev/input/event0`
87+
would correspond to `Dell Dell USB Keyboard`).
88+
89+
If the naming is not clear, it is also possible to empirically check for the key code by listening for events:
90+
91+
``` Python
92+
from evdev import InputDevice, categorize, ecodes
93+
dev = InputDevice('/dev/input/event1')
94+
print(dev)
95+
for event in dev.read_loop():
96+
if event.type == ecodes.EV_KEY:
97+
print(categorize(event))
98+
```
99+
The output could be of the form:
100+
```
101+
device /dev/input/event1, name "DragonRise Inc. Generic USB Joystick ", phys "usb-3f980000.usb-1.2/input0"
102+
key event at 1672569673.124168, 297 (BTN_BASE4), down
103+
key event at 1672569673.385170, 297 (BTN_BASE4), up
104+
```
105+
106+
In this example output, the `{key-code}` would be `297`
107+
108+
Alternatively, the device could also be setup without a mapping.
109+
Afterwards, when pressing keys, the key codes can be found in the log files. Press various buttons on your device,
110+
while watching the logs with `tail -f shared/logs/app.log`.
111+
Look for entries like `No callback registered for button ...`.
112+
113+
### Specifying the `{rpc_command_definition}`
114+
115+
The RPC command follows the regular RPC command rules as defined in the [following documentation](./rpc-commands.md).
116+
117+
118+
## Full example config
119+
120+
A complete configuration example for a USB Joystick controller can be found in the [examples](../../resources/default-settings/evdev.example.yaml).

documentation/developers/docker.md

+14
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,20 @@ $ docker run -it --rm \
291291
--name jukebox jukebox
292292
```
293293

294+
## Testing EVDEV devices in Linux
295+
To test the [event device capabilities](../builders/event-devices.md) in docker, the device needs to be made available to the container.
296+
297+
### Linux
298+
Mount the device into the container by configuring the appropriate device in a `devices` section of the `jukebox` service in the docker compose file. For example:
299+
300+
```yaml
301+
jukebox:
302+
...
303+
devices:
304+
- /dev/input/event3:/dev/input/event3
305+
```
306+
307+
294308
### Resources
295309

296310
#### Mac
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
devices: # A list of evdev devices each containing one or multiple input/output devices
2+
joystick: # A nickname for a device
3+
device_name: DragonRise Inc. Generic USB # Device name
4+
exact: false # If true, the device name must match exactly, otherwise it is sufficient to contain the name
5+
input_devices:
6+
TogglePlayback:
7+
type: Button
8+
kwargs:
9+
key_code: 299
10+
actions:
11+
on_press:
12+
alias: toggle
13+
NextSong:
14+
type: Button
15+
kwargs:
16+
key_code: 298
17+
actions:
18+
on_press:
19+
alias: next_song
20+
PrevSong:
21+
type: Button
22+
kwargs:
23+
key_code: 297
24+
actions:
25+
on_press:
26+
alias: prev_song
27+
VolumeUp:
28+
type: Button
29+
kwargs:
30+
key_code: 296
31+
actions:
32+
on_press:
33+
alias: change_volume
34+
args: 5
35+
VolumeDown:
36+
type: Button
37+
kwargs:
38+
key_code: 295
39+
actions:
40+
on_press:
41+
alias: change_volume
42+
args: -5
43+
VolumeReset:
44+
type: Button
45+
kwargs:
46+
key_code: 291
47+
actions:
48+
on_press:
49+
package: volume
50+
plugin: ctrl
51+
method: set_volume
52+
args: [18]
53+
Shutdown:
54+
type: Button
55+
kwargs:
56+
key_code: 292
57+
actions:
58+
on_press:
59+
alias: shutdown

src/jukebox/components/controls/bluetooth_audio_buttons/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ def activate(device_name: str, exact: bool = True, open_initial_delay: float = 0
4949
# Do a bit of housekeeping: Delete dead threads
5050
listener = list(filter(lambda x: x.is_alive(), listener))
5151
# Check that there is no running thread for this device already
52-
for ll in listener:
53-
if ll.device_request == device_name and ll.is_alive():
52+
for thread in listener:
53+
if thread.device_request == device_name and thread.is_alive():
5454
logger.debug(f"Button listener thread already active for '{device_name}'")
5555
return
5656

0 commit comments

Comments
 (0)