Smartcard library for Python providing PC/SC interface for smart card communication
—
Event-driven monitoring system for detecting card insertion/removal and reader connection/disconnection using the observer pattern. This enables applications to respond automatically to smart card and reader events.
CardMonitoring provides automatic detection of card insertion and removal events across all available readers.
class CardObserver:
def update(self, observable, handlers):
"""
Called when card insertion or removal events occur.
Args:
observable (CardMonitor): The card monitor that detected the event
handlers (tuple): (added_cards, removed_cards) where:
- added_cards (list[Card]): Cards that were inserted
- removed_cards (list[Card]): Cards that were removed
"""
class CardMonitor:
"""
Singleton monitor for card insertion and removal events.
Automatically monitors all available readers.
"""
def addObserver(self, observer):
"""
Add an observer to receive card events.
Args:
observer (CardObserver): Observer to add
"""
def deleteObserver(self, observer):
"""
Remove an observer from receiving card events.
Args:
observer (CardObserver): Observer to remove
"""ReaderMonitoring provides detection of reader connection and disconnection events, useful for applications that need to track available readers dynamically.
class ReaderObserver:
def update(self, observable, handlers):
"""
Called when reader connection or disconnection events occur.
Args:
observable (ReaderMonitor): The reader monitor that detected the event
handlers (tuple): (added_readers, removed_readers) where:
- added_readers (list[Reader]): Readers that were connected
- removed_readers (list[Reader]): Readers that were disconnected
"""
class ReaderMonitor:
def __init__(self, startOnDemand=True, readerProc=None, period=1):
"""
Initialize reader monitoring.
Args:
startOnDemand (bool): If True, monitoring starts when first observer is added
readerProc (callable, optional): Function to get reader list. Defaults to readers()
period (int): Polling period in seconds for reader detection
"""
def addObserver(self, observer):
"""
Add an observer to receive reader events.
Args:
observer (ReaderObserver): Observer to add
"""
def deleteObserver(self, observer):
"""
Remove an observer from receiving reader events.
Args:
observer (ReaderObserver): Observer to remove
"""Base classes for implementing the observer pattern used throughout the monitoring system.
class Observer:
def update(self, observable, handlers):
"""
Called when the observed object changes.
Args:
observable: The object being observed
handlers: Event-specific data
"""
class Observable:
def addObserver(self, observer):
"""Add an observer to this observable."""
def deleteObserver(self, observer):
"""Remove an observer from this observable."""
def deleteObservers(self):
"""Remove all observers."""
def notifyObservers(self, handlers=None):
"""
Notify all observers of a change.
Args:
handlers: Event data to pass to observers
"""
def setChanged(self):
"""Mark this observable as changed."""
def clearChanged(self):
"""Clear the changed flag."""
def hasChanged(self):
"""
Check if this observable has changed.
Returns:
bool: True if changed flag is set
"""
def countObservers(self):
"""
Get the number of observers.
Returns:
int: Observer count
"""from smartcard.CardMonitoring import CardMonitor, CardObserver
from smartcard.util import toHexString
class SimpleCardObserver(CardObserver):
def update(self, observable, actions):
(addedcards, removedcards) = actions
for card in addedcards:
print(f"Card inserted in {card.reader}")
print(f"ATR: {toHexString(card.atr)}")
for card in removedcards:
print(f"Card removed from {card.reader}")
print(f"ATR: {toHexString(card.atr)}")
# Create and start monitoring
monitor = CardMonitor()
observer = SimpleCardObserver()
monitor.addObserver(observer)
print("Monitoring for card events... (Press Ctrl+C to stop)")
try:
# Keep the program running to receive events
import time
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Stopping card monitoring")
monitor.deleteObserver(observer)import threading
from smartcard.CardMonitoring import CardMonitor, CardObserver
from smartcard.util import toHexString
class AutoConnectObserver(CardObserver):
def update(self, observable, actions):
(addedcards, removedcards) = actions
for card in addedcards:
# Automatically connect to inserted cards
threading.Thread(target=self.handle_card, args=(card,)).start()
def handle_card(self, card):
try:
connection = card.createConnection()
connection.connect()
print(f"Connected to card in {card.reader}")
# Send a simple command
GET_DATA = [0x00, 0xCA, 0x9F, 0x7F, 0x00]
response, sw1, sw2 = connection.transmit(GET_DATA)
print(f"GET DATA response: {toHexString(response)}")
print(f"Status: {sw1:02X} {sw2:02X}")
except Exception as e:
print(f"Error handling card: {e}")
finally:
if 'connection' in locals():
connection.disconnect()
# Set up monitoring
monitor = CardMonitor()
observer = AutoConnectObserver()
monitor.addObserver(observer)
print("Auto-connecting to inserted cards...")from smartcard.ReaderMonitoring import ReaderMonitor, ReaderObserver
class SimpleReaderObserver(ReaderObserver):
def update(self, observable, actions):
(addedreaders, removedreaders) = actions
for reader in addedreaders:
print(f"Reader connected: {reader}")
for reader in removedreaders:
print(f"Reader disconnected: {reader}")
# Create and start reader monitoring
monitor = ReaderMonitor()
observer = SimpleReaderObserver()
monitor.addObserver(observer)
print("Monitoring for reader events...")
try:
import time
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Stopping reader monitoring")
monitor.deleteObserver(observer)from smartcard.CardMonitoring import CardMonitor, CardObserver
from smartcard.ReaderMonitoring import ReaderMonitor, ReaderObserver
from smartcard.util import toHexString
class SmartCardSystemObserver(CardObserver, ReaderObserver):
def __init__(self):
self.active_cards = {}
def update(self, observable, actions):
# Handle both card and reader events
if isinstance(observable, CardMonitor):
self.handle_card_event(actions)
elif isinstance(observable, ReaderMonitor):
self.handle_reader_event(actions)
def handle_card_event(self, actions):
(addedcards, removedcards) = actions
for card in addedcards:
reader_name = str(card.reader)
self.active_cards[reader_name] = card
print(f"✓ Card inserted in {reader_name}")
print(f" ATR: {toHexString(card.atr)}")
for card in removedcards:
reader_name = str(card.reader)
if reader_name in self.active_cards:
del self.active_cards[reader_name]
print(f"✗ Card removed from {reader_name}")
def handle_reader_event(self, actions):
(addedreaders, removedreaders) = actions
for reader in addedreaders:
print(f"🔌 Reader connected: {reader}")
for reader in removedreaders:
reader_name = str(reader)
if reader_name in self.active_cards:
del self.active_cards[reader_name]
print(f"🔌 Reader disconnected: {reader}")
# Set up comprehensive monitoring
observer = SmartCardSystemObserver()
card_monitor = CardMonitor()
card_monitor.addObserver(observer)
reader_monitor = ReaderMonitor()
reader_monitor.addObserver(observer)
print("Monitoring smart card system events...")
print("Insert/remove cards and connect/disconnect readers to see events")
try:
import time
while True:
time.sleep(1)
# Show current status every 10 seconds
if int(time.time()) % 10 == 0:
print(f"\nCurrent active cards: {len(observer.active_cards)}")
for reader, card in observer.active_cards.items():
print(f" {reader}: {toHexString(card.atr, toHexString.PACK)}")
print()
except KeyboardInterrupt:
print("\nCleaning up monitors...")
card_monitor.deleteObserver(observer)
reader_monitor.deleteObserver(observer)from smartcard.CardMonitoring import CardMonitor, CardObserver
import contextlib
class ContextCardObserver(CardObserver):
def __init__(self):
self.events = []
def update(self, observable, actions):
(addedcards, removedcards) = actions
for card in addedcards:
self.events.append(('inserted', card))
for card in removedcards:
self.events.append(('removed', card))
@contextlib.contextmanager
def card_monitoring():
"""Context manager for card monitoring."""
monitor = CardMonitor()
observer = ContextCardObserver()
monitor.addObserver(observer)
try:
yield observer
finally:
monitor.deleteObserver(observer)
# Usage with context manager
with card_monitoring() as observer:
print("Monitoring cards for 10 seconds...")
import time
time.sleep(10)
print(f"Detected {len(observer.events)} events:")
for event_type, card in observer.events:
print(f" {event_type}: {card.reader}")# Card and Reader objects used in monitoring events
class Card:
def __init__(self, reader, atr):
"""
Card object containing reader and ATR information.
Args:
reader (Reader): Reader containing the card
atr (list[int]): Answer To Reset bytes
"""
@property
def reader(self):
"""Reader: The reader containing this card."""
@property
def atr(self):
"""list[int]: Answer To Reset bytes."""
class Reader:
def __init__(self, readername):
"""
Reader object.
Args:
readername (str): Name of the reader
"""
def __str__(self):
"""str: Reader name as string."""
# Monitoring event data types
EventHandlers = tuple[list[Card], list[Card]] # (added, removed)
ReaderEventHandlers = tuple[list[Reader], list[Reader]] # (added, removed)Install with Tessl CLI
npx tessl i tessl/pypi-pyscard