Commit Graph

13 Commits

Author SHA1 Message Date
ske087 84569ec9ae Install the GPIO pin backend, and add the Word guide
install.sh created the virtualenv with --system-site-packages so it could
see the pin backend, but never installed one. On a Pi OS image whose
python3-gpiozero does not pull it in, gpiozero then raised BadPinFactory,
the app fell back to a no-op LED, and the reader ran with the light dark
and nothing failing - the failure that is easiest to miss. Install
python3-lgpio (Bookworm and later) or python3-rpi.gpio (older releases)
when neither backend is importable, best-effort like the packages above.

Adds the installation and API guide (DOCX, 36 pages, 4 figures, 25
tables) covering enabling the serial port, wiring, mounting, installing on
Raspberry Pi OS and the complete HTTP API, plus the header diagrams it
references.

.gitignore now excludes the release build outputs: they are generated from
the source tree, and committing them would duplicate the code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-06 16:39:37 +03:00
ske087 74c9e0f8fa Make LAN access installable, with a token
The status page could not be reached at all from another machine:
the unit runs --serve with no --host, and the app defaults to
127.0.0.1, so nothing listens on the Pi's LAN address. Confirmed by
binding both ways - from the LAN, loopback-bound gives connection
refused. Even once bound, a non-loopback caller needs a token and
install.sh had no way to set one, so the page would load with every
panel empty and every button doing nothing (401).

Adds --host, --allow-remote and --token. The token is written to
bridge.env at mode 0600 and referenced via EnvironmentFile rather
than sitting on the ExecStart line, where 'systemctl cat' would
reveal it and the process list would show it.

Exposing the bridge stays opt-in and deliberate: the installer
refuses a non-loopback --host without --allow-remote, and warns when
no token is given, because off loopback anyone reaching the port can
read taps and switch the LEDs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 22:44:14 +03:00
ske087 c2b3eda649 Add manual LED control to the status page
"When the light does not come on" has two very different causes -
the pin is wrong, or the wiring is wrong - and nothing so far could
tell them apart. The status page now has an LED panel with buttons
to switch either LED on or off by hand, which takes card events out
of the picture entirely: if the light comes on, the pin, backend and
permissions are all fine.

Backed by GET/POST /api/led. A forced state latches, holding through
card events and the error pulse, until released - otherwise the next
tap would immediately undo it and the test would be useless. A button
whose LED has no usable pin is disabled rather than silently doing
nothing, and the POST returns 409 rather than a misleading 200 when
there is no pin to drive.

Both LED states are recorded at the moment a request is served, not
only when the worker thread gets to it, so a snapshot cannot
contradict the response that was just sent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 22:30:28 +03:00
ske087 a399542a6e Add a dedicated error LED on GPIO 24
One LED cannot say two things at once, and in the default presence
mode the status LED is already carrying "a card is here". The result
was that a bad read - a checksum failure - was reported not at all:
verified by driving the LED worker, which showed presence mode
producing no light on invalid_card while error mode blinked.

A second LED on its own pin (GPIO 24 = physical pin 18, matching the
workstation app) now pulses for --error-pulse seconds on a bad read,
leaving the status light to keep tracking the card. This also gives
ERROR_BLINK_SECONDS, previously dead, a real use.

  --error-led-pin N / --error-led-active-low / --error-pulse S
  --no-error-led  restores the single-LED blink

install.sh rewrites the unit's ExecStart for all of it, verified for
the default, active-low and no-error-led cases.

Also extracts the repeated backend-opening code into _open_led(),
which keeps the BadPinFactory-is-an-ImportError subtlety in one place
instead of two, and treats the error pin colliding with the status
pin as "no error LED" rather than opening one pin twice.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 22:14:49 +03:00
ske087 b357b5755a Add --led-active-low for inverted LED wiring
active_high was hardcoded to True, so an LED wired the other way
round - cathode to the pin, anode to 3.3 V, or an inverting
transistor driver - had no configuration at all. The symptom is easy
to misread as a dark LED: the light actually does the opposite, on
with no card and off with one.

Exposed as --led-active-low on the app and on install.sh, which
rewrites the unit's ExecStart like the other LED options. The
startup line now names the polarity, so the journal says which
configuration is actually running:

  LED on GPIO 23 (presence mode, active-high, gpiozero)

Default is unchanged, so existing installs behave identically.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 22:11:27 +03:00
ske087 04f0ab9efa Expand the LED pin numbering table
--led-pin is the BCM GPIO number, not the physical header pin, and
getting it wrong produces a dark LED with no error at all: the app
still reports "LED on GPIO 23" and looks healthy. The three-row table
did not cover most of the header, so the fix for a wire on any other
pin was left as guesswork.

Lists all 22 usable pins both ways round, gives the concrete
"physical pin 23 needs --led-pin 11" case, names the pins that must
not be used (the reader's own UART on 8/10, reserved ID_SD/ID_SC on
27/28, and the power pins), and adds a blink-each-candidate script
that finds the pin by experiment. Verified the table against the
standard 40-pin pinout.

Also drops a duplicated sentence about /dev/serial0 in Wiring.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 22:06:03 +03:00
ske087 bbd2861897 Add an end-to-end Pi quick start guide
Replaces the "Running at boot" section with a five-step guide that
starts from enabling the UART, the step that is easiest to miss and
the one that produces the "device not found" failure:

  1. enable the serial port with raspi-config (and answer No to the
     login shell, or the console holds the port)
  2. confirm which device exists, with a table mapping what you see
     to the --device value to pass
  3. install the service, with both possible failure messages and
     what each one means
  4. verify via systemd, the journal and the status page
  5. fall back to a foreground run and --list-ports when cards are
     not detected

Also fixes a contradiction: the section claimed the installer
defaults --device to /dev/rdm-reader when it is /dev/serial0, while
the options table three lines below said /dev/serial0. That mismatch
is exactly what leads someone to install against the wrong device.

Notes that --no-service does not skip the device checks, verified
against the script's control flow, and drops the raspi-config menu
number rather than assert one that varies by release.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 21:57:58 +03:00
ske087 882b89d6da Serve a read-only status page on the bridge port
GET / now returns a dashboard for the reader: mode, port, uptime,
card presence, the current card, event counters and a live event log
seeded from /api/taps and updated over the /api/events SSE stream.

The page is read-only on purpose - it has no claim button, because
POST /api/card/claim spends the tap and merely viewing the status
would otherwise consume a real login.

Serving the page requires allowing same-origin browser requests: a
browser sends Origin even for a request to the same origin, so
_cors_ok() used to reject the page's own API calls with 403 unless
--allow-origin was passed. Loopback and same-host origins are now
accepted, and the Access-Control-Allow-Origin echo is applied
consistently in one helper instead of four copies.

--no-page reverts POST-free deployments to API-only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 21:34:17 +03:00
ske087 c720f4ca67 Remove dead code and use the event constants consistently
- drop unused BAR import, _VALID_MODES, _is_optional(),
  OPTIONAL_IMPORTS and is_loopback()
- replace hand-written event name strings with EVENT_INSERTED,
  EVENT_REMOVED and EVENT_INVALID, so a typo cannot silently
  disable an event path
- drop the now-moot pytest/coverage entries from .gitignore

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 21:24:45 +03:00
ske087 15b43d476a Remove the unused keyboard wedge module
keyboard.py was never wired in: no module imported it, the CLI had no
--type flag, and neither the docs nor install.sh referenced it, so the
virtual-keyboard path could never run.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 21:22:27 +03:00
ske087 a54b79e488 Remove the test suite
The tests are not part of what ships to a device, and nothing in
pyproject.toml, the deploy scripts or the docs referenced them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 21:20:19 +03:00
ske087 c0a354b98e 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>
2026-10-05 21:15:00 +03:00
ske087 48e800adc1 Initializare dupa curatare folder 2026-10-05 11:46:46 +03:00