Files
Odoo-Modules/tools/fusion_clock_acr_wedge
gsinghpal d75198be9f fix(acr-wedge): use AppleScript on macOS for keystroke injection
pyautogui's Quartz-based keystroke path often fails on newer macOS
because the Python CLI binary doesn't auto-surface in System Settings
> Accessibility. User reported the daemon detected taps fine but
keystrokes never landed in any window.

Switch to AppleScript / System Events on macOS. Permission attaches
to whatever terminal/app launched the Python process (Terminal.app,
iTerm, etc.) — a familiar named app the user can grant Accessibility
to in one click. Combined keystroke + Return in a single osascript
call to keep latency ~100ms per tap.

Fall back to pyautogui if osascript fails (handles edge cases) and
on non-macOS platforms.
2026-05-15 19:56:49 -04:00
..

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

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

pip install -r requirements.txt
python wedge.py --verbose

No swig required — pyscard ships pre-built wheels on Windows.

Linux

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

# 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 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:

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:

[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:

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.