or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mddictionaries.mdengine.mdextensions.mdindex.mdmachines.mdregistry.mdsteno-data.md

machines.mddocs/

0

# Machine Interface System

1

2

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.

3

4

## Capabilities

5

6

### Base Machine Interface

7

8

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

9

10

```python { .api }

11

class StenotypeBase:

12

"""Base class for stenotype machine interfaces."""

13

14

KEYS_LAYOUT: str = ''

15

"""String describing the physical key layout of the machine."""

16

17

ACTIONS: tuple = ()

18

"""Tuple of available machine-specific actions."""

19

20

KEYMAP_MACHINE_TYPE: str = None

21

"""Machine type identifier for keymap compatibility."""

22

23

def __init__(self):

24

"""

25

Initialize machine interface.

26

27

Sets up machine instance with default configuration,

28

ready for keymap assignment and capture initialization.

29

"""

30

31

def set_keymap(self, keymap: dict) -> None:

32

"""

33

Set key mapping configuration.

34

35

Args:

36

keymap: Dictionary mapping physical keys to steno keys

37

38

Configures how physical machine keys map to stenographic keys.

39

"""

40

41

def start_capture(self) -> None:

42

"""

43

Start capturing strokes from machine.

44

45

Begins communication with machine hardware and starts

46

processing input for stroke detection.

47

48

Raises:

49

ConnectionError: If unable to connect to machine

50

"""

51

52

def stop_capture(self) -> None:

53

"""

54

Stop capturing strokes from machine.

55

56

Ceases communication with machine hardware and stops

57

all input processing.

58

"""

59

60

def add_stroke_callback(self, callback) -> None:

61

"""

62

Add callback for stroke events.

63

64

Args:

65

callback: Function to call when stroke detected

66

67

Callback signature: callback(stroke_keys: list[str])

68

"""

69

70

def remove_stroke_callback(self, callback) -> None:

71

"""

72

Remove previously added stroke callback.

73

74

Args:

75

callback: Function to remove from callbacks

76

"""

77

78

def add_state_callback(self, callback) -> None:

79

"""

80

Add callback for machine state changes.

81

82

Args:

83

callback: Function to call when state changes

84

85

Callback signature: callback(state: str)

86

States: 'stopped', 'initializing', 'connected', 'disconnected'

87

"""

88

89

def remove_state_callback(self, callback) -> None:

90

"""

91

Remove previously added state callback.

92

93

Args:

94

callback: Function to remove from callbacks

95

"""

96

97

def set_suppression(self, enabled: bool) -> None:

98

"""

99

Set output suppression state.

100

101

Args:

102

enabled: True to suppress machine output, False to allow

103

104

Controls whether machine generates its own output

105

in addition to Plover's processing.

106

"""

107

108

def suppress_last_stroke(self, send_backspaces: bool) -> None:

109

"""

110

Suppress the last stroke's output.

111

112

Args:

113

send_backspaces: Whether to send backspaces to undo output

114

115

Undoes the effect of the most recent stroke.

116

"""

117

118

@classmethod

119

def get_actions(cls) -> tuple:

120

"""

121

Get available machine actions.

122

123

Returns:

124

Tuple of action strings available for this machine type

125

126

Actions vary by machine and may include special functions.

127

"""

128

129

@classmethod

130

def get_keys(cls) -> tuple:

131

"""

132

Get available machine keys.

133

134

Returns:

135

Tuple of key strings available on this machine

136

137

Keys correspond to physical keys that can be mapped.

138

"""

139

140

@classmethod

141

def get_option_info(cls) -> dict:

142

"""

143

Get machine-specific option information.

144

145

Returns:

146

Dictionary describing available configuration options

147

148

Provides metadata for GUI configuration interfaces.

149

"""

150

```

151

152

### Threaded Machine Base

153

154

Base class adding threading support for machines requiring background processing.

155

156

```python { .api }

157

class ThreadedStenotypeBase(StenotypeBase, threading.Thread):

158

"""Base class with threading support for background processing."""

159

160

def run(self) -> None:

161

"""

162

Main thread execution method.

163

164

Override this method to implement machine-specific

165

background processing loop.

166

"""

167

```

168

169

### Serial Machine Base

170

171

Specialized base class for machines using serial port communication.

172

173

```python { .api }

174

class SerialStenotypeBase(ThreadedStenotypeBase):

175

"""Base class for serial port stenotype machines."""

176

177

SERIAL_PARAMS: dict = {

178

'baudrate': 9600,

179

'bytesize': 8,

180

'parity': 'N',

181

'stopbits': 1,

182

'timeout': 2.0,

183

'xonxoff': False,

184

'rtscts': False

185

}

186

"""Default serial port parameters."""

187

```

188

189

## Machine State Constants

190

191

Standard machine state identifiers used throughout the system.

192

193

```python { .api }

194

STATE_STOPPED: str = 'stopped'

195

"""Machine is not running or connected."""

196

197

STATE_INITIALIZING: str = 'initializing'

198

"""Machine is starting up or connecting."""

199

200

STATE_RUNNING: str = 'connected'

201

"""Machine is connected and receiving strokes."""

202

203

STATE_ERROR: str = 'disconnected'

204

"""Machine encountered error or lost connection."""

205

```

206

207

## Available Machine Types

208

209

### Keyboard Machine

210

Uses computer keyboard as stenotype input device.

211

212

**Plugin Name**: `Keyboard`

213

**Connection**: Direct keyboard input capture

214

**Configuration**: Keyboard layout selection, key mapping

215

**Use Case**: Practice, accessibility, no dedicated hardware

216

217

### TX Bolt Protocol Machines

218

Supports machines using the TX Bolt communication protocol.

219

220

**Plugin Name**: `TX Bolt`

221

**Connection**: Serial port communication

222

**Protocol**: TX Bolt binary format

223

**Machines**: Many commercial stenotype machines

224

225

### Gemini PR Protocol Machines

226

Supports machines using the Gemini PR communication protocol.

227

228

**Plugin Name**: `Gemini PR`

229

**Connection**: Serial or USB communication

230

**Protocol**: Gemini PR packet format

231

**Machines**: Neutrino Group machines, some others

232

233

### ProCAT Machines

234

Support for ProCAT stenotype machines.

235

236

**Plugin Name**: `ProCAT`

237

**Connection**: Serial port communication

238

**Protocol**: ProCAT-specific format

239

**Machines**: ProCAT stenotype models

240

241

### Stentura Machines

242

Support for Stentura stenotype machines.

243

244

**Plugin Name**: `Stentura`

245

**Connection**: Serial port communication

246

**Protocol**: Stentura-specific format

247

**Machines**: Stentura stenotype models

248

249

### Passport Machines

250

Support for Passport stenotype machines.

251

252

**Plugin Name**: `Passport`

253

**Connection**: Serial port communication

254

**Protocol**: Passport-specific format

255

**Machines**: Passport stenotype models

256

257

## Usage Examples

258

259

```python

260

from plover.registry import registry

261

from plover.machine.base import STATE_RUNNING, STATE_ERROR

262

263

# Get available machines

264

machines = registry.list_plugins('machine')

265

for machine in machines:

266

print(f"Available machine: {machine.name}")

267

268

# Get specific machine

269

keyboard_plugin = registry.get_plugin('machine', 'Keyboard')

270

KeyboardMachine = keyboard_plugin.obj

271

272

# Create machine instance

273

machine = KeyboardMachine()

274

275

# Set up callbacks

276

def on_stroke(stroke_keys):

277

print(f"Stroke received: {stroke_keys}")

278

279

def on_state_change(state):

280

if state == STATE_RUNNING:

281

print("Machine connected and ready")

282

elif state == STATE_ERROR:

283

print("Machine connection error")

284

285

machine.add_stroke_callback(on_stroke)

286

machine.add_state_callback(on_state_change)

287

288

# Configure keymap

289

keymap = {

290

'q': 'S-',

291

'w': 'T-',

292

'e': 'K-',

293

'r': 'P-',

294

't': 'W-',

295

'y': 'H-',

296

'u': 'R-',

297

'i': 'A-',

298

'o': 'O-',

299

'p': '*',

300

# ... more key mappings

301

}

302

machine.set_keymap(keymap)

303

304

# Start machine

305

try:

306

machine.start_capture()

307

print("Machine started successfully")

308

except ConnectionError as e:

309

print(f"Failed to start machine: {e}")

310

311

# Later, stop machine

312

machine.stop_capture()

313

314

# Get machine information

315

keys = machine.get_keys()

316

actions = machine.get_actions()

317

options = machine.get_option_info()

318

319

print(f"Machine keys: {keys}")

320

print(f"Machine actions: {actions}")

321

print(f"Configuration options: {options}")

322

```

323

324

## Machine Configuration Options

325

326

### Keyboard Machine Options

327

- `layout`: Keyboard layout ('QWERTY', 'Dvorak', 'Colemak')

328

- `arpeggiate`: Enable arpeggiate mode for chording

329

330

### Serial Machine Options

331

- `port`: Serial port device path or name

332

- `baudrate`: Communication speed (9600, 19200, 38400, etc.)

333

- `bytesize`: Data bits (7, 8)

334

- `parity`: Parity setting ('N', 'E', 'O')

335

- `stopbits`: Stop bits (1, 2)

336

- `timeout`: Read timeout in seconds

337

- `xonxoff`: Software flow control

338

- `rtscts`: Hardware flow control

339

340

### USB Machine Options

341

- `vendor_id`: USB vendor ID for device identification

342

- `product_id`: USB product ID for device identification

343

- `interface`: USB interface number

344

345

## Developing Custom Machines

346

347

### Basic Machine Implementation

348

349

```python

350

from plover.machine.base import StenotypeBase

351

352

class CustomMachine(StenotypeBase):

353

KEYS_LAYOUT = 'STENO_KEYS'

354

ACTIONS = ('action1', 'action2')

355

356

def __init__(self):

357

super().__init__()

358

self._stroke_callbacks = []

359

self._state_callbacks = []

360

361

def start_capture(self):

362

# Initialize hardware connection

363

# Start background processing

364

self._notify_state('connected')

365

366

def stop_capture(self):

367

# Stop hardware communication

368

self._notify_state('stopped')

369

370

def _notify_stroke(self, keys):

371

for callback in self._stroke_callbacks:

372

callback(keys)

373

374

def _notify_state(self, state):

375

for callback in self._state_callbacks:

376

callback(state)

377

378

@classmethod

379

def get_option_info(cls):

380

return {

381

'port': {

382

'type': 'choice',

383

'choices': ['COM1', 'COM2', '/dev/ttyUSB0'],

384

'default': 'COM1'

385

}

386

}

387

```

388

389

### Threaded Machine Implementation

390

391

```python

392

from plover.machine.base import ThreadedStenotypeBase

393

import threading

394

import time

395

396

class ThreadedCustomMachine(ThreadedStenotypeBase):

397

def __init__(self):

398

super().__init__()

399

self._running = False

400

401

def start_capture(self):

402

self._running = True

403

self.start() # Start thread

404

405

def stop_capture(self):

406

self._running = False

407

self.join() # Wait for thread

408

409

def run(self):

410

"""Background thread for stroke processing."""

411

while self._running:

412

# Read from hardware

413

# Process strokes

414

# Notify callbacks

415

time.sleep(0.01)

416

```

417

418

### Serial Machine Implementation

419

420

```python

421

from plover.machine.base import SerialStenotypeBase

422

import serial

423

424

class SerialCustomMachine(SerialStenotypeBase):

425

SERIAL_PARAMS = {

426

'baudrate': 19200,

427

'timeout': 1.0

428

}

429

430

def __init__(self):

431

super().__init__()

432

self._serial = None

433

434

def start_capture(self):

435

port = self._settings.get('port', 'COM1')

436

self._serial = serial.Serial(port, **self.SERIAL_PARAMS)

437

super().start_capture()

438

439

def stop_capture(self):

440

super().stop_capture()

441

if self._serial:

442

self._serial.close()

443

444

def run(self):

445

while self._running and self._serial:

446

data = self._serial.read(10)

447

if data:

448

stroke = self._parse_stroke(data)

449

if stroke:

450

self._notify_stroke(stroke)

451

```

452

453

## Types

454

455

```python { .api }

456

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

457

from threading import Thread

458

459

StrokeKeys = List[str]

460

StrokeCallback = Callable[[StrokeKeys], None]

461

StateCallback = Callable[[str], None]

462

463

MachineState = str

464

MachineKeymap = Dict[str, str]

465

MachineOptions = Dict[str, Any]

466

MachineActions = Tuple[str, ...]

467

468

OptionInfo = Dict[str, Dict[str, Any]]

469

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

470

471

CallbackList = List[Callable]

472

```