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()