ACR122U is a 13.56 MHz PC/SC (CCID) reader, not HID. Browsers can't talk to PC/SC devices directly, so the kiosk JS can't see ACR122U taps the way it sees a USB-HID reader. This daemon bridges the gap: - Polls the ACR122U via pyscard - Reads UID via the standard ACS GET_UID APDU (FF CA 00 00 00) - Types UID + Enter into the focused window using pyautogui - Debounces re-reads of the same card (2s window) Output format matches FusionClockNfcKiosk._normalize_uid() expectations: colon-separated uppercase hex (04:10:5B:CA:FD:22:90 + Enter). The kiosk JS already has a keyboard-wedge listener (v19.0.3.2.0+), so no server-side or kiosk-side changes needed — wedge.py's keystrokes route through the same handleTap() path as a USB-HID reader, preserving photo verification + penalty + activity log. Setup docs include macOS, Windows, Linux instructions plus launchd/Task Scheduler/systemd snippets for running as a service. Strategic value: with this, ACR122U deployments support UA-Pockets (13.56 MHz DESFire EV3) for single-card door+clock setups in the premium tier of the standard product kit. The 125 kHz EM4100 USB-C HID reader remains the default tier.
167 lines
5.0 KiB
Markdown
167 lines
5.0 KiB
Markdown
# fusion_clock ACR122U Wedge
|
|
|
|
Turns an ACR122U (or any PC/SC ACS reader) into a USB-HID-style keyboard wedge so the fusion_clock NFC kiosk can use it.
|
|
|
|
## Why this exists
|
|
|
|
The ACR122U is a fantastic 13.56 MHz reader (better range than cheap HID readers, reads MIFARE Classic/DESFire/NTAG/FeliCa/ISO15693 — basically everything Ubiquiti UA-Pockets and most enterprise cards). **But it speaks PC/SC (CCID), not HID keyboard.** Browsers can't talk to PC/SC devices directly.
|
|
|
|
This daemon bridges the gap: it polls the ACR122U over PC/SC, reads tag UIDs, and types them into the focused window as keystrokes — exactly like a USB-HID reader would. The fusion_clock kiosk page's existing keyboard listener picks them up the same way.
|
|
|
|
```
|
|
ACR122U → PC/SC → wedge.py → keystrokes → kiosk page in Chrome
|
|
```
|
|
|
|
## Setup
|
|
|
|
### macOS
|
|
|
|
```bash
|
|
brew install swig
|
|
pip3 install -r requirements.txt
|
|
python3 wedge.py --verbose
|
|
```
|
|
|
|
The first time the daemon tries to type a UID, macOS will pop up an **Accessibility permission** prompt. Grant it to Terminal.app (or whatever shell ran the script). This is one-time.
|
|
|
|
If `pyscard` fails to install: ensure Xcode command-line tools are installed (`xcode-select --install`).
|
|
|
|
### Windows
|
|
|
|
```powershell
|
|
pip install -r requirements.txt
|
|
python wedge.py --verbose
|
|
```
|
|
|
|
No swig required — pyscard ships pre-built wheels on Windows.
|
|
|
|
### Linux
|
|
|
|
```bash
|
|
sudo apt install pcscd pcsc-tools libpcsclite-dev swig
|
|
sudo systemctl enable --now pcscd
|
|
pip3 install -r requirements.txt
|
|
python3 wedge.py --verbose
|
|
```
|
|
|
|
You may need to run with sudo or add your user to the `plugdev` group.
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
# Quiet mode — just types UIDs on tap
|
|
python3 wedge.py
|
|
|
|
# Verbose — prints every tap and reader event
|
|
python3 wedge.py --verbose
|
|
|
|
# Detect-only — prints UIDs but doesn't type them
|
|
# (use this to verify the reader is working before granting Accessibility perms)
|
|
python3 wedge.py --no-type --verbose
|
|
```
|
|
|
|
## Output format
|
|
|
|
UIDs are typed as colon-separated uppercase hex, followed by Enter:
|
|
|
|
```
|
|
04:10:5B:CA:FD:22:90<Enter>
|
|
```
|
|
|
|
This matches what `FusionClockNfcKiosk._normalize_uid()` expects, so the kiosk recognizes it without any server-side translation.
|
|
|
|
## Debounce
|
|
|
|
The daemon won't re-emit the same UID more than once within 2 seconds (so holding a card on the reader doesn't fire repeated clock-ins). Tap a different card, or wait 2 seconds, and the same card will re-emit.
|
|
|
|
## Running as a service
|
|
|
|
For kiosk deployments, you want this to start at boot and stay running.
|
|
|
|
### macOS — LaunchAgent
|
|
|
|
Create `~/Library/LaunchAgents/com.nexa.fusion-clock-acr-wedge.plist`:
|
|
|
|
```xml
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>Label</key>
|
|
<string>com.nexa.fusion-clock-acr-wedge</string>
|
|
<key>ProgramArguments</key>
|
|
<array>
|
|
<string>/usr/bin/python3</string>
|
|
<string>/Users/USERNAME/path/to/wedge.py</string>
|
|
</array>
|
|
<key>RunAtLoad</key>
|
|
<true/>
|
|
<key>KeepAlive</key>
|
|
<true/>
|
|
<key>StandardOutPath</key>
|
|
<string>/tmp/fusion-clock-wedge.log</string>
|
|
<key>StandardErrorPath</key>
|
|
<string>/tmp/fusion-clock-wedge.err</string>
|
|
</dict>
|
|
</plist>
|
|
```
|
|
|
|
Load it:
|
|
```bash
|
|
launchctl load ~/Library/LaunchAgents/com.nexa.fusion-clock-acr-wedge.plist
|
|
```
|
|
|
|
### Windows — Task Scheduler
|
|
|
|
Create a task to run `python wedge.py` at login. Or use NSSM to install it as a Windows service.
|
|
|
|
### Linux — systemd user service
|
|
|
|
Create `~/.config/systemd/user/fusion-clock-wedge.service`:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Fusion Clock ACR122U Wedge
|
|
After=graphical-session.target
|
|
|
|
[Service]
|
|
ExecStart=/usr/bin/python3 /home/USER/path/to/wedge.py
|
|
Restart=always
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
```
|
|
|
|
Enable it:
|
|
```bash
|
|
systemctl --user enable --now fusion-clock-wedge.service
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
**"No PC/SC readers detected"**
|
|
- macOS: PC/SC is built into the OS, no service to start. Just make sure the reader is plugged in.
|
|
- Linux: `sudo systemctl status pcscd` — make sure it's running.
|
|
- Windows: built-in.
|
|
|
|
**"pyautogui can't type on macOS"**
|
|
- Grant Accessibility permission to your terminal in System Settings → Privacy & Security → Accessibility.
|
|
|
|
**"Reader detected but no UID on tap"**
|
|
- Try `--verbose` to see error messages.
|
|
- The card might not respond to the standard GET_UID APDU. Most 13.56 MHz cards do; some FeliCa variants don't.
|
|
- Try `python3 wedge.py --no-type --verbose` to confirm detection works without involving the keyboard layer.
|
|
|
|
**Keystrokes appearing in the wrong window**
|
|
- pyautogui types into whatever window has focus. Make sure the kiosk page is focused when you tap.
|
|
- This is the same behavior as a USB-HID reader — they both depend on the OS focused window.
|
|
|
|
## Future packaging
|
|
|
|
This is the prototype. For client distribution we'll wrap it in:
|
|
- macOS .app bundle (PyInstaller + py2app)
|
|
- Windows .exe (PyInstaller)
|
|
- Linux AppImage
|
|
|
|
Plus autostart and an idle "no reader detected" status indicator. See parent module's todo list.
|