| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| """Generic foot pedal listener using evdev. |
| |
| Callers supply a callback receiving the pressed key code (e.g. ``"KEY_A"``) |
| and an optional device path. The listener runs in a daemon thread and |
| silently no-ops when :mod:`evdev` is not installed or the device is |
| unavailable. Strategy-specific key mapping logic lives in the caller. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import logging |
| import threading |
| from collections.abc import Callable |
|
|
| logger = logging.getLogger(__name__) |
|
|
| DEFAULT_PEDAL_DEVICE = "/dev/input/by-id/usb-PCsensor_FootSwitch-event-kbd" |
|
|
|
|
| def start_pedal_listener( |
| on_press: Callable[[str], None], |
| device_path: str = DEFAULT_PEDAL_DEVICE, |
| ) -> threading.Thread | None: |
| """Spawn a daemon thread that forwards pedal key-press codes to ``on_press``. |
| |
| Parameters |
| ---------- |
| on_press: |
| Callback invoked with the pressed key code string (e.g. ``"KEY_A"``) |
| on each pedal press event. The callback runs in the listener thread |
| and must be thread-safe. |
| device_path: |
| Linux input device path (e.g. ``/dev/input/by-id/...``). |
| |
| Returns |
| ------- |
| The started daemon :class:`threading.Thread`, or ``None`` when |
| :mod:`evdev` is not installed (optional dependency; silent no-op). |
| """ |
| try: |
| from evdev import InputDevice, categorize, ecodes |
| except ImportError: |
| return None |
|
|
| def pedal_reader() -> None: |
| try: |
| dev = InputDevice(device_path) |
| logger.info("Pedal connected: %s", dev.name) |
| for ev in dev.read_loop(): |
| if ev.type != ecodes.EV_KEY: |
| continue |
| key = categorize(ev) |
| code = key.keycode |
| if isinstance(code, (list, tuple)): |
| code = code[0] |
| if key.keystate != 1: |
| continue |
| try: |
| on_press(code) |
| except Exception as cb_err: |
| logger.warning("Pedal callback error: %s", cb_err) |
| except (FileNotFoundError, PermissionError): |
| pass |
| except Exception as e: |
| logger.warning("Pedal error: %s", e) |
|
|
| thread = threading.Thread(target=pedal_reader, daemon=True, name="PedalListener") |
| thread.start() |
| return thread |
|
|