NFC-Tools/NfcReaderService.py
2026-04-29 12:14:37 +02:00

107 lines
2.7 KiB
Python

import time
from datetime import datetime, timedelta
from smartcard.CardRequest import CardRequest
from smartcard.Exceptions import CardConnectionException, NoCardException
from smartcard.CardConnection import CardConnection
from smartcard.util import toHexString
from pynput.keyboard import Controller
from card_reader import read_card
GET_UID = [0xFF, 0xCA, 0x00, 0x00, 0x00]
class NfcReaderService:
def __init__(self) -> None:
self._is_running = True
self._last_uid = None
self._keyboard = Controller()
self._last_write = datetime.min
def run(self) -> None:
print("Reader ready.")
while self._is_running:
try:
self._poll_card()
except (KeyboardInterrupt, SystemExit):
self.stop()
except Exception as e:
print(f"[ERROR] Unexpected failure: {e}")
time.sleep(1)
def _poll_card(self) -> None:
card_request = CardRequest(timeout=1)
try:
card_service = card_request.waitforcard()
except NoCardException:
self._last_uid = None
return
connection = card_service.connection
try:
connection.connect(CardConnection.T1_protocol)
uid_hex = self._read_uid(connection)
if uid_hex is None:
return
# Card still present and unchanged -> ignore
if uid_hex == self._last_uid:
return
self._last_uid = uid_hex
self._handle_card(connection)
except (CardConnectionException, NoCardException) as e:
print(f"[CARD ERROR] {e}")
finally:
try:
connection.disconnect()
except Exception:
pass
def _read_uid(self, connection) -> Optional[str]:
uid, sw1, sw2 = connection.transmit(GET_UID)
if sw1 != 0x90:
raise CardConnectionException("UID read failed")
return toHexString(uid)
def _handle_card(self, connection) -> None:
user_id = read_card(connection)
if user_id is None:
return
now = datetime.now()
# debounce keyboard output
if now - self._last_write < timedelta(seconds=2):
return
self._send_to_keyboard(user_id)
self._last_write = now
def _send_to_keyboard(self, user_id: str) -> None:
try:
self._keyboard.type(f"{user_id}\n")
except Exception as e:
print(f"[KEYBOARD ERROR] {e}")
def stop(self) -> None:
print("Stopping NFC service.")
self._is_running = False
if __name__ == "__main__":
service = NfcReaderService()
service.run()