nuc/jellyfin-client: document host-side input hotplug auto-recovery

Add udev rule + oneshot service + script on nuc that restart the
container kiosk when the Logitech Unifying receiver is replugged, since
hotplug uevents don't cross into the container's netns and cage only
enumerates input at startup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Julien Lutran
2026-08-11 01:39:27 +02:00
co-authored by Claude Opus 4.8
parent 61e3606d33
commit 43ef0e5447
+58 -2
View File
@@ -231,6 +231,60 @@ incus exec "$CNAME" -- systemctl daemon-reload
incus exec "$CNAME" -- systemctl enable --now go-librespot
```
## Input hotplug auto-recovery (host side)
cage only enumerates input devices at startup, and udev hotplug events don't
cross into the container (see tricky-parts #2). So re-plugging the Unifying
receiver leaves the kiosk bound to the old, now-dead device node — no
keyboard/mouse until `jellyfin-kiosk` is restarted. These three files **on the
host** (`nuc`, not the container) automate that: the host *does* receive the
uevent, so a host udev rule fires on the receiver's USB `add` and restarts the
container's kiosk. Match is by vendor/product (`046d:c52b`), not sysfs path, so
it works regardless of which USB port the dongle lands on.
`/etc/udev/rules.d/99-jellyfin-kiosk-recover.rules`:
```
# Logitech Unifying receiver (re)plugged -> recover the jellyfin-client kiosk
ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="046d", ATTR{idProduct}=="c52b", RUN+="/usr/bin/systemctl --no-block start jellyfin-kiosk-recover.service"
```
`/etc/systemd/system/jellyfin-kiosk-recover.service` — oneshot, so a burst of
udev events during one plug merges into a single restart (natural debounce):
```
[Unit]
Description=Restart jellyfin-client kiosk after Logitech receiver hotplug
[Service]
Type=oneshot
ExecStart=/usr/local/bin/jellyfin-kiosk-recover.sh
```
`/usr/local/bin/jellyfin-kiosk-recover.sh` — waits for the receiver to enumerate
its paired K400 node, then restarts the kiosk only if the container is up (so
host-boot coldplug events are a no-op):
```bash
#!/bin/bash
# Restart the jellyfin-client Wayland kiosk after the Logitech Unifying
# receiver is (re)plugged, so cage/libinput re-enumerates the keyboard/mouse.
sleep 6
if ! incus info jellyfin-client 2>/dev/null | grep -qi 'Status:.*RUNNING'; then
exit 0
fi
incus exec jellyfin-client -- systemctl restart jellyfin-kiosk.service
logger -t jellyfin-kiosk-recover "Restarted jellyfin-kiosk after Logitech receiver hotplug"
```
Apply, then watch it work by replugging the dongle:
```sh
chmod +x /usr/local/bin/jellyfin-kiosk-recover.sh
udevadm control --reload-rules && systemctl daemon-reload
journalctl -t jellyfin-kiosk-recover -f
```
## Troubleshooting
```sh
@@ -251,8 +305,10 @@ incus exec jellyfin-client -- udevadm info /dev/input/event0 # udev db visibl
(#3); check `/etc/asound.conf` and the card name in `aplay -l`.
- **Keyboard plugged in after boot isn't seen** — the host udev db is live
through the bind, but udev hotplug *events* don't cross the container's
network namespace, so cage only enumerates at startup:
`systemctl restart jellyfin-kiosk` after plugging new input hardware.
network namespace, so cage only enumerates at startup. Re-plugging the
Unifying receiver is now handled automatically by the host-side recovery
rule (see "Input hotplug auto-recovery"); manual fallback for other input
hardware: `incus exec jellyfin-client -- systemctl restart jellyfin-kiosk`.
- **cage can't open card0** — something on the host holds DRM master; the
host must boot to `multi-user.target` with no display manager.
- **Jellyfin + Spotify at the same time** — the Pioneer PCM is exclusive;