CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyscard

Smartcard library for Python providing PC/SC interface for smart card communication

Pending
Overview
Eval results
Files

monitoring.mddocs/

Monitoring

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.

Capabilities

Card Monitoring

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
        """

Reader Monitoring

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
        """

Observer Pattern Support

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
        """

Usage Examples

Basic Card Monitoring

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)

Card Monitoring with Automatic Connection

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...")

Reader Monitoring

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)

Combined Card and Reader Monitoring

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)

Context Manager for Monitoring

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}")

Related Types

# 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

docs

atr-card-types.md

card-connections.md

gui-components.md

index.md

monitoring.md

pcsc-interface.md

reader-management.md

session-api.md

status-word-handling.md

utilities.md

tile.json