CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-plover

Open Source Stenography Software providing real-time stenographic typing, machine support, and plugin architecture.

Pending
Overview
Eval results
Files

machines.mddocs/

Machine Interface System

Plover's machine interface system provides abstracted communication with stenotype hardware through a unified API. It supports various connection methods including serial, USB, and keyboard input, enabling integration with both commercial stenotype machines and alternative input devices.

Capabilities

Base Machine Interface

Abstract base class defining the standard interface for all stenotype machine implementations.

class StenotypeBase:
    """Base class for stenotype machine interfaces."""
    
    KEYS_LAYOUT: str = ''
    """String describing the physical key layout of the machine."""
    
    ACTIONS: tuple = ()
    """Tuple of available machine-specific actions."""
    
    KEYMAP_MACHINE_TYPE: str = None
    """Machine type identifier for keymap compatibility."""
    
    def __init__(self):
        """
        Initialize machine interface.
        
        Sets up machine instance with default configuration,
        ready for keymap assignment and capture initialization.
        """
    
    def set_keymap(self, keymap: dict) -> None:
        """
        Set key mapping configuration.
        
        Args:
            keymap: Dictionary mapping physical keys to steno keys
            
        Configures how physical machine keys map to stenographic keys.
        """
    
    def start_capture(self) -> None:
        """
        Start capturing strokes from machine.
        
        Begins communication with machine hardware and starts
        processing input for stroke detection.
        
        Raises:
            ConnectionError: If unable to connect to machine
        """
    
    def stop_capture(self) -> None:
        """
        Stop capturing strokes from machine.
        
        Ceases communication with machine hardware and stops
        all input processing.
        """
    
    def add_stroke_callback(self, callback) -> None:
        """
        Add callback for stroke events.
        
        Args:
            callback: Function to call when stroke detected
            
        Callback signature: callback(stroke_keys: list[str])
        """
    
    def remove_stroke_callback(self, callback) -> None:
        """
        Remove previously added stroke callback.
        
        Args:
            callback: Function to remove from callbacks
        """
    
    def add_state_callback(self, callback) -> None:
        """
        Add callback for machine state changes.
        
        Args:
            callback: Function to call when state changes
            
        Callback signature: callback(state: str)
        States: 'stopped', 'initializing', 'connected', 'disconnected'
        """
    
    def remove_state_callback(self, callback) -> None:
        """
        Remove previously added state callback.
        
        Args:
            callback: Function to remove from callbacks
        """
    
    def set_suppression(self, enabled: bool) -> None:
        """
        Set output suppression state.
        
        Args:
            enabled: True to suppress machine output, False to allow
            
        Controls whether machine generates its own output
        in addition to Plover's processing.
        """
    
    def suppress_last_stroke(self, send_backspaces: bool) -> None:
        """
        Suppress the last stroke's output.
        
        Args:
            send_backspaces: Whether to send backspaces to undo output
            
        Undoes the effect of the most recent stroke.
        """
    
    @classmethod
    def get_actions(cls) -> tuple:
        """
        Get available machine actions.
        
        Returns:
            Tuple of action strings available for this machine type
            
        Actions vary by machine and may include special functions.
        """
    
    @classmethod
    def get_keys(cls) -> tuple:
        """
        Get available machine keys.
        
        Returns:
            Tuple of key strings available on this machine
            
        Keys correspond to physical keys that can be mapped.
        """
    
    @classmethod
    def get_option_info(cls) -> dict:
        """
        Get machine-specific option information.
        
        Returns:
            Dictionary describing available configuration options
            
        Provides metadata for GUI configuration interfaces.
        """

Threaded Machine Base

Base class adding threading support for machines requiring background processing.

class ThreadedStenotypeBase(StenotypeBase, threading.Thread):
    """Base class with threading support for background processing."""
    
    def run(self) -> None:
        """
        Main thread execution method.
        
        Override this method to implement machine-specific
        background processing loop.
        """

Serial Machine Base

Specialized base class for machines using serial port communication.

class SerialStenotypeBase(ThreadedStenotypeBase):
    """Base class for serial port stenotype machines."""
    
    SERIAL_PARAMS: dict = {
        'baudrate': 9600,
        'bytesize': 8,
        'parity': 'N',
        'stopbits': 1,
        'timeout': 2.0,
        'xonxoff': False,
        'rtscts': False
    }
    """Default serial port parameters."""

Machine State Constants

Standard machine state identifiers used throughout the system.

STATE_STOPPED: str = 'stopped'
"""Machine is not running or connected."""

STATE_INITIALIZING: str = 'initializing'  
"""Machine is starting up or connecting."""

STATE_RUNNING: str = 'connected'
"""Machine is connected and receiving strokes."""

STATE_ERROR: str = 'disconnected'
"""Machine encountered error or lost connection."""

Available Machine Types

Keyboard Machine

Uses computer keyboard as stenotype input device.

Plugin Name: Keyboard Connection: Direct keyboard input capture Configuration: Keyboard layout selection, key mapping Use Case: Practice, accessibility, no dedicated hardware

TX Bolt Protocol Machines

Supports machines using the TX Bolt communication protocol.

Plugin Name: TX Bolt
Connection: Serial port communication Protocol: TX Bolt binary format Machines: Many commercial stenotype machines

Gemini PR Protocol Machines

Supports machines using the Gemini PR communication protocol.

Plugin Name: Gemini PR Connection: Serial or USB communication
Protocol: Gemini PR packet format Machines: Neutrino Group machines, some others

ProCAT Machines

Support for ProCAT stenotype machines.

Plugin Name: ProCAT Connection: Serial port communication Protocol: ProCAT-specific format Machines: ProCAT stenotype models

Stentura Machines

Support for Stentura stenotype machines.

Plugin Name: Stentura Connection: Serial port communication Protocol: Stentura-specific format Machines: Stentura stenotype models

Passport Machines

Support for Passport stenotype machines.

Plugin Name: Passport Connection: Serial port communication
Protocol: Passport-specific format Machines: Passport stenotype models

Usage Examples

from plover.registry import registry
from plover.machine.base import STATE_RUNNING, STATE_ERROR

# Get available machines
machines = registry.list_plugins('machine')
for machine in machines:
    print(f"Available machine: {machine.name}")

# Get specific machine
keyboard_plugin = registry.get_plugin('machine', 'Keyboard')
KeyboardMachine = keyboard_plugin.obj

# Create machine instance
machine = KeyboardMachine()

# Set up callbacks
def on_stroke(stroke_keys):
    print(f"Stroke received: {stroke_keys}")

def on_state_change(state):
    if state == STATE_RUNNING:
        print("Machine connected and ready")
    elif state == STATE_ERROR:
        print("Machine connection error")

machine.add_stroke_callback(on_stroke)
machine.add_state_callback(on_state_change)

# Configure keymap
keymap = {
    'q': 'S-',
    'w': 'T-', 
    'e': 'K-',
    'r': 'P-',
    't': 'W-',
    'y': 'H-',
    'u': 'R-',
    'i': 'A-',
    'o': 'O-',
    'p': '*',
    # ... more key mappings
}
machine.set_keymap(keymap)

# Start machine
try:
    machine.start_capture()
    print("Machine started successfully")
except ConnectionError as e:
    print(f"Failed to start machine: {e}")

# Later, stop machine
machine.stop_capture()

# Get machine information
keys = machine.get_keys()
actions = machine.get_actions()
options = machine.get_option_info()

print(f"Machine keys: {keys}")
print(f"Machine actions: {actions}")
print(f"Configuration options: {options}")

Machine Configuration Options

Keyboard Machine Options

  • layout: Keyboard layout ('QWERTY', 'Dvorak', 'Colemak')
  • arpeggiate: Enable arpeggiate mode for chording

Serial Machine Options

  • port: Serial port device path or name
  • baudrate: Communication speed (9600, 19200, 38400, etc.)
  • bytesize: Data bits (7, 8)
  • parity: Parity setting ('N', 'E', 'O')
  • stopbits: Stop bits (1, 2)
  • timeout: Read timeout in seconds
  • xonxoff: Software flow control
  • rtscts: Hardware flow control

USB Machine Options

  • vendor_id: USB vendor ID for device identification
  • product_id: USB product ID for device identification
  • interface: USB interface number

Developing Custom Machines

Basic Machine Implementation

from plover.machine.base import StenotypeBase

class CustomMachine(StenotypeBase):
    KEYS_LAYOUT = 'STENO_KEYS'
    ACTIONS = ('action1', 'action2')
    
    def __init__(self):
        super().__init__()
        self._stroke_callbacks = []
        self._state_callbacks = []
    
    def start_capture(self):
        # Initialize hardware connection
        # Start background processing
        self._notify_state('connected')
    
    def stop_capture(self):
        # Stop hardware communication
        self._notify_state('stopped')
    
    def _notify_stroke(self, keys):
        for callback in self._stroke_callbacks:
            callback(keys)
    
    def _notify_state(self, state):
        for callback in self._state_callbacks:
            callback(state)
    
    @classmethod  
    def get_option_info(cls):
        return {
            'port': {
                'type': 'choice',
                'choices': ['COM1', 'COM2', '/dev/ttyUSB0'],
                'default': 'COM1'
            }
        }

Threaded Machine Implementation

from plover.machine.base import ThreadedStenotypeBase
import threading
import time

class ThreadedCustomMachine(ThreadedStenotypeBase):
    def __init__(self):
        super().__init__()
        self._running = False
    
    def start_capture(self):
        self._running = True
        self.start()  # Start thread
    
    def stop_capture(self):
        self._running = False
        self.join()   # Wait for thread
    
    def run(self):
        """Background thread for stroke processing."""
        while self._running:
            # Read from hardware
            # Process strokes
            # Notify callbacks
            time.sleep(0.01)

Serial Machine Implementation

from plover.machine.base import SerialStenotypeBase
import serial

class SerialCustomMachine(SerialStenotypeBase):
    SERIAL_PARAMS = {
        'baudrate': 19200,
        'timeout': 1.0
    }
    
    def __init__(self):
        super().__init__()
        self._serial = None
    
    def start_capture(self):
        port = self._settings.get('port', 'COM1')
        self._serial = serial.Serial(port, **self.SERIAL_PARAMS)
        super().start_capture()
    
    def stop_capture(self):
        super().stop_capture()
        if self._serial:
            self._serial.close()
    
    def run(self):
        while self._running and self._serial:
            data = self._serial.read(10)
            if data:
                stroke = self._parse_stroke(data)
                if stroke:
                    self._notify_stroke(stroke)

Types

from typing import Dict, List, Tuple, Optional, Callable, Any, Union
from threading import Thread

StrokeKeys = List[str]
StrokeCallback = Callable[[StrokeKeys], None]
StateCallback = Callable[[str], None]

MachineState = str
MachineKeymap = Dict[str, str]
MachineOptions = Dict[str, Any]
MachineActions = Tuple[str, ...]

OptionInfo = Dict[str, Dict[str, Any]]
SerialParams = Dict[str, Union[int, float, str, bool]]

CallbackList = List[Callable]

Install with Tessl CLI

npx tessl i tessl/pypi-plover

docs

configuration.md

dictionaries.md

engine.md

extensions.md

index.md

machines.md

registry.md

steno-data.md

tile.json