Add keyboard-wedge typing and LED event support

- keyboard.py: emulate a commercial USB RFID reader by typing card ids
  into the focused window via a /dev/uinput virtual keyboard
- led.py / bridge.py: drive the status LED from card events
- cli.py, card.py: wire up the new options and event flow
- deploy: harden install.sh and the systemd unit
- README: document keyboard mode, LED support, and deployment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-10-05 21:14:58 +03:00
parent 48e800adc1
commit c0a354b98e
9 changed files with 973 additions and 67 deletions
+20 -9
View File
@@ -2,8 +2,10 @@
Description=RDM card reader HTTP bridge (serves card taps for web login)
Documentation=file:/opt/rdm-card-reader/README.md
# The serial device must exist before we start, otherwise the reader opens the
# wrong tty or none at all.
After=dev-serial.device network-online.target
# wrong tty or none at all. install.sh rewrites this line for the device given
# with --device, since only it knows whether the reader is on the on-board UART
# (/dev/serial0, the default) or a USB adapter (/dev/rdm-reader).
After=dev-serial0.device network-online.target
Wants=network-online.target
# Do not restart forever if the reader is genuinely absent. These are [Unit]
@@ -17,22 +19,29 @@ User=rdmcard
Group=dialout
WorkingDirectory=/opt/rdm-card-reader
# --serve exposes the card on http://127.0.0.1:8765/api/card
# --port points at the udev alias, so the reader is the same device every boot.
# --serve runs the HTTP bridge, which is what makes the reader available as an
# API on http://127.0.0.1:8765. --port points at the device the reader is
# actually wired to; install.sh rewrites this line from --device, and only it
# knows whether that is a USB adapter (/dev/rdm-reader) or the on-board UART
# (/dev/serial0). --led-pin is rewritten from --led-pin for the same reason.
# Autodetect refuses to guess if more than one USB adapter is present, which is
# the safe behaviour: a wrong guess means authenticating the wrong person.
# The wheels for rdm6300 and pyserial are bundled inside the package, so no
# --wheel-dir is needed here: the install works with no internet.
ExecStart=/opt/rdm-card-reader/.venv/bin/rdm-card-reader \
--serve \
--port /dev/rdm-reader \
--port /dev/serial0 \
--mode login \
--allow-origin https://your.web.app
--led-pin 23 \
--led-mode presence \
--heartbeat 2.0
Restart=always
RestartSec=3
# The bridge only needs to read the serial port and listen on loopback.
# The bridge reads the serial port, drives the status LED and listens on
# loopback. DeviceAllow is rewritten by install.sh alongside --port: with
# DevicePolicy=closed a device missing from this list is denied outright, and
# omitting the GPIO chip leaves the LED dark while everything else works.
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
@@ -44,7 +53,9 @@ RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
DeviceAllow=/dev/rdm-reader rw
DeviceAllow=/dev/serial0 rw
DeviceAllow=/dev/gpiochip0 rw
DeviceAllow=/dev/gpiochip4 rw
DevicePolicy=closed
# stdout/stderr go here; watch with: journalctl -u rdm-card-reader -f