Small toolkit for automating a serial-attached Linux shell
Project description
sdev
Small toolkit for automating a serial-attached Linux shell.
Installation
pip install -e .
CLI
# Run a command
sdev -p "ls /proc/meminfo" -d /dev/ttyUSB0 -b 115200
# Stream output incrementally
sdev -p "tail -f /var/log/syslog" --stream
# Stream with server-side regex filter
sdev -p "tail -f /var/log/syslog" --stream --grep "ERROR"
# Stream with complete-line output only
sdev -p "dmesg" --stream --line-mode
# Parse output with regex
sdev -p "cat /proc/meminfo" --parse "Mem.*"
# Wait for a specific output marker instead of shell prompt
sdev -p "./mnn_perf -m model.mnn" --end-flag "Frame rate:"
# Clear stray processes before running a command
sdev -p "uptime" --doctor
# Save defaults so you can omit -d and -b
sdev set-default /dev/ttyUSB0 115200
sdev -p "ls /proc/meminfo"
# Send Ctrl+C to interrupt a running command (without -p)
sdev --interrupt -d /dev/ttyUSB0 -b 115200
# Detect serial boards on this system
sdev --probe
sdev --probe --probe-baud 9600 --probe-baud 38400
# Custom prompt patterns for non-standard shells
sdev -p "ls" --prompt "[root@board]# " --prompt "admin@box> "
CLI options
| Flag | Description |
|---|---|
-p, --command |
Command to execute |
-d, --device |
Serial device path |
-b, --baud |
Baud rate |
-t, --timeout |
Timeout in seconds (default: 300) |
--stream |
Incremental output instead of buffered |
--grep REGEX |
Filter --stream lines by regex |
--line-mode |
Only yield complete lines in --stream |
--parse REGEX |
Show only matching lines |
--end-flag STR |
Stop when this string appears in output |
--doctor |
Clear foreground processes before command |
--prompt PATTERN |
Custom shell prompt pattern (repeatable) |
--interrupt |
Send Ctrl+C and wait for prompt |
--probe |
Detect serial boards and print info |
--probe-baud BAUD |
Baud rates to try during --probe (repeatable) |
set-default |
Persist device/baud as defaults |
Design Goals
- Stability: strict 5-minute timeout on all blocking operations
- Simplicity: small surface area, obvious API
- Predictability: prompt detection to determine command completion
- Streaming: incremental output for long-running commands
- Parsing: structured output with optional regex filtering
Python API
import sdev
# Session-based (recommended)
with sdev.SerialSession("/dev/ttyUSB0", 115200) as session:
result = session.cli("ls /proc/meminfo")
print(result.output)
# Custom prompt detection for non-standard shells
session = sdev.SerialSession("/dev/ttyUSB0", 115200, prompts=[b"[root@board]# "])
session.connect()
# Streaming for long-running commands
for chunk in session.stream("tail -f /var/log/syslog"):
print(chunk, end="")
# Streaming with line mode — only yields complete lines
for line in session.stream("tail -f /var/log/syslog", line_mode=True):
process(line)
# Streaming with server-side filter
for chunk in session.stream("tail -f /var/log/syslog", filter_fn=lambda t: t.upper()):
print(chunk, end="")
# Parsing with regex filtering
parsed = session.parse("cat /proc/meminfo", pattern=r"Mem.*")
print(parsed.matched)
# Wait for a specific output marker instead of shell prompt
# Useful for benchmarks that print results then keep running
result = session.cli("./mnn_perf -m model.mnn", end_flag="Frame rate:")
# Interrupt a running command (sends Ctrl+C and waits for prompt)
session.interrupt(timeout=5)
# Clear stray foreground processes and get a clean prompt
session.doctor()
# Wait until no data arrives for N seconds (boot completion)
session.wait_for_silence(timeout=1.5)
# Recover from device reboot without creating a new session
session.reconnect()
# Monitor CPU/memory during long operations
usage = sdev.resource_usage()
print(f"RSS: {usage['memory_mb']} MB, CPU: {usage['cpu_percent']}%")
# Detect serial boards and get OS/arch info
for device in sdev.probe():
print(f"{device['device']} @ {device['baud']}: {device['info']['os_name']}")
# Send raw bytes over serial (control sequences, custom protocols)
sdev.connect("/dev/ttyUSB0", 115200)
n = sdev.write(b"reboot\n")
print(f"Wrote {n} bytes")
# Clear stray foreground processes on the default connection
sdev.doctor()
# Wait for boot completion (no serial data for N seconds)
sdev.wait_for_silence(timeout=2.0)
Thread safety
Each SerialSession has an internal threading.Lock. Only one cli()
or stream() call can run at a time per session. Concurrent callers
will raise RuntimeError after 10s if the lock is held. interrupt()
does not acquire the lock — it remains the emergency escape hatch.
Module-level convenience API
sdev.connect("/dev/ttyUSB0", 115200)
result = sdev.cli("ls /proc/meminfo")
sdev.disconnect()
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sdev-1.0.0.tar.gz.
File metadata
- Download URL: sdev-1.0.0.tar.gz
- Upload date:
- Size: 37.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a4ed7aa7e89fbc1deafdbc44ea51d6c9d052fb85a86847ae0781a553a2e8589
|
|
| MD5 |
c2859c9fc88c27a0b15a69172e3fb768
|
|
| BLAKE2b-256 |
ee4e1f4134b5c003202d90b16c4ac8ef8935c8207def857577aeb94697ae5020
|
File details
Details for the file sdev-1.0.0-py3-none-any.whl.
File metadata
- Download URL: sdev-1.0.0-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4868d555efa89ba242154205c8fe9917860f63d7e46202773fc70b237d16260b
|
|
| MD5 |
6dad867438dcf0475a24ac7fc4445c9e
|
|
| BLAKE2b-256 |
7e28663106b4d5e9fdfd75bee9dff9c226b2e154b71725361655a5dcde93b51f
|