linux-usb-scanner-client is a headless Debian/Ubuntu service for USB keyboard-wedge barcode scanners. It reads scan keystrokes from a configured /dev/input/event* device, buffers completed CR/LF-terminated scans in SQLite, and forwards them to industrial-scanner-logger over the same TCP framing used by the Windows USB scanner client: one UTF-8 barcode followed by CRLF.
The service intentionally opens and holds the TCP connection only while the configured USB scanner is available. If the scanner is unplugged, inaccessible, or not matched by config, the service must not connect to industrial-scanner-logger; this lets the server detect scanner availability through its connected TCP clients.
Every meaningful change must keep these files current:
AGENTS.mdfor agent-facing architecture, workflow, and operational instructions.README.mdfor user-facing install, config, operation, health, and troubleshooting instructions.CHANGELOG.mdfor notable changes.
src/linux_usb_scanner_client/config.pyloads and validates INI config from/etc/linux-usb-scanner-client.conf.src/linux_usb_scanner_client/device.pydiscovers matching Linux input devices throughevdev.src/linux_usb_scanner_client/keyboard.pytranslates keyboard events into scan frames and completes scans only on Enter/CR/LF.src/linux_usb_scanner_client/storage.pyowns the persistent SQLite queue and service status tables.src/linux_usb_scanner_client/storage_monitor.pyreports queue volume free space and database footprint.src/linux_usb_scanner_client/tcp_sender.pyowns the persistent TCP client connection and sendsbarcode + "\r\n".src/linux_usb_scanner_client/service.pycoordinates scanner monitoring, queueing, TCP connection gating, retry, and shutdown.src/linux_usb_scanner_client/health.pybuilds CLI health output from the service status table and queue state.src/linux_usb_scanner_client/auto_update.pychecks GitHub main for newer versions and applies updates through the root-owned update service.src/linux_usb_scanner_client/alert_monitor.pyruns the independent degraded-state beep monitor service.src/linux_usb_scanner_client/cli.pyis the command entry point.
- Never hardcode secrets, private hosts, tokens, or environment-specific values.
- Do not log raw barcode values. Health output may show timestamps, counts, device names, queue depth, attempts, and error summaries, but not full scan payloads.
- Require an explicit scanner matcher before processing input. Valid matchers are
device_path,vendor_id/product_id, ordevice_name. Do not default to reading every keyboard. - Keep
/etc/linux-usb-scanner-client.confroot-owned and readable by the service group only when installed. - Keep the SQLite queue under
/var/lib/linux-usb-scanner-client/with ownership limited to the service user. - Preserve the app directory on every uninstall, including purge.
- Preserve pending queued scans forever until they are delivered. Automated cleanup may remove only already-sent metadata. The only normal operator exceptions are
scripts/clear-scan-queue.shandscripts/uninstall.sh --purge. - Keep auto-update disabled by default. Enabling it allows a root systemd service to run
scripts/install.shfrom the configured Git repository branch, so it must remain restricted to trusted repositories.
Use the local virtual environment when available:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python -m pip install -e .
PYTHONPATH=src python -m unittest discover -s testsBefore editing scanner behavior, inspect the current industrial-scanner-logger TCP receiver and the Windows USB scanner client if the wire protocol might have changed. The expected protocol is a persistent TCP client connection to port 55256 by default, with each scan sent as UTF-8 text plus CRLF.
The app started at version 0.1.0; the current release version is 1.0.0. The auto-updater detects available updates by reading [project].version from pyproject.toml on the configured Git branch and comparing it to the installed package __version__.
For every app behavior, installer, config template, systemd unit, operational script, dependency, or user-facing workflow change that should reach installed systems through auto-update, advance the app version so pyproject.toml changes to a higher version. Keep pyproject.toml, src/linux_usb_scanner_client/__init__.py, README.md, and CHANGELOG.md aligned, and update the version consistency test. Agent-only documentation changes that should not trigger installed-system updates may leave the version unchanged.
- Update
config/linux-usb-scanner-client.confwhen adding or changing settings. - Update
systemd/linux-usb-scanner-client.service,systemd/linux-usb-scanner-client-monitor.service, andscripts/install.shtogether when service paths, users, permissions, alerting, or startup behavior change. - Keep the Debian/Ubuntu package dependency lists in
scripts/install.shaligned with runtime, build, auto-update, and alerting needs, and document any dependency changes inREADME.md. - Keep
scripts/install.shnon-destructive to/opt/linux-usb-scanner-client,/etc/linux-usb-scanner-client.conf,/var/lib/linux-usb-scanner-client, and/var/log/linux-usb-scanner-client.log; rerunning it on an installed system is an update path. - Keep
scripts/uninstall.shnon-destructive to/opt/linux-usb-scanner-client,/etc/linux-usb-scanner-client.conf, and/var/log/linux-usb-scanner-client.log;--purgemay remove/var/lib/linux-usb-scanner-clientSQLite state only. - Keep
scripts/install.shsafe to rerun from/opt/linux-usb-scanner-client; it must not copy the install tree onto itself. - Every shell script under
scripts/must require root before option parsing and fail immediately with a clear sudo reminder when run as a normal user. - Keep the installer responsible for granting and verifying service-user read access to both the source app directory and
/opt/linux-usb-scanner-client. - The installer must verify that the app service, alert monitor service, and update timer are enabled and active before reporting success.
- Run
bash -n scripts/install.sh scripts/uninstall.sh scripts/check-health.sh scripts/restart-services.sh scripts/clear-scan-queue.shafter shell script changes. - Run
PYTHONPATH=src python -m unittest discover -s testsafter Python changes.
linux-usb-scanner-client healthmust show scanner state, server connection state, queue depth, oldest pending scan, heartbeat freshness, storage free space, auto-update state, alert monitor state, and recent errors.scripts/check-health.shis the quick installed-system diagnostic wrapper for CLI health, USB input-device visibility, systemd unit state, queue/storage state, UTC today/yesterday scan totals, server state, alert state, update state, and log-file presence.scripts/restart-services.shis the installed-system restart helper for applying config, unit, script, or app-file changes without rebooting. It must reload systemd, reset failed state, restart the app service, restart the alert monitor service, and restart the auto-update timer. It must not run the root-owned one-shot updater unless the operator passes--run-update-check.scripts/clear-scan-queue.shis the only normal operator path for deleting queued scan payloads. It must require explicit confirmation unless--yesis passed, must stop/restart running services around the delete, and must preserve sent metadata unless--include-sentis passed.- Human-readable
linux-usb-scanner-client healthoutput is ANSI-colored by default; use--no-colorfor plain text and--jsonfor structured output. linux-usb-scanner-client list-devicesmust help identify the correct scanner matcher for/etc/linux-usb-scanner-client.conf.linux-usb-scanner-client monitoris a separate auto-starting service that plays the highest-priority active alert pattern: 5 quick beeps when the app service heartbeat is stale, 3 quick beeps when the USB scanner is unavailable, and 1 quick beep when the server is unreachable.- Alerting must prefer the audio card first, then the system speaker, with console bell only as a final fallback for
backend = auto. - When
backend = beepmakes an audible system-speaker beep but exits nonzero with no stderr/stdout, treat it as a best-effort beep and log at most one warning instead of putting the monitor in an error loop. - Keep the main scanner service and alert monitor service configured to restart continuously; the monitor unit must not be blocked by systemd start-rate limiting.
- Installed app, monitor, and updater logs must land in
/var/log/linux-usb-scanner-client.log; Python logging volume must respectlogging.log_level. - Server connection attempts are blocked whenever the scanner is unavailable.
- Backlog delivery is best effort: failed TCP connects or sends leave scans queued with attempt metadata and retry later.
- The scan queue database under
/var/lib/linux-usb-scanner-client/must remain persistent across service restarts, app updates, host reboots, and installer reruns. - All client-generated timestamps must be UTC ISO-8601 strings ending in
Z. The TCP wire protocol sends barcode frames only; server-side scan timestamps are controlled byindustrial-scanner-logger.