Skip to main content

usb-helper

PyPI PyPI Downloads

A cross-platform Python framework for USB device monitoring, bulk transfer, and SCSI-over-Bulk communication. Built on pyusb/libusb.

Includes a CLI tool for quick device diagnostics, plug/unplug monitoring (JSONL output), and TOML-based device profile management.

Installation

% pip install usb-helper

Platform prerequisites

Platform Requirement
macOS brew install libusb
Linux sudo apt install libusb-1.0-0-dev
Windows Install WinUSB driver via Zadig

Verify your setup:

% usb-helper --check

CLI Usage

List connected USB devices

% usb-helper

Found 3 device(s):

  usb:1-3  1234:abcd  "TestDev"  [Bulk mode]  (mode=bulk)
  usb:1-5  5678:1001
  usb:2-1  9abc:def0
% usb-helper --json
{"status": true, "action": "scan", "meta": {...}, "data": [{"device_id": "usb:1-3", "vid": "1234", "pid": "abcd", ...}]}

Filter devices

% usb-helper --vid 1234                     # single vendor ID
% usb-helper --vid 1234 --vid 5678          # multiple vendor IDs
% usb-helper --vid 1234 --pid abcd          # vendor + product ID
% usb-helper --name "Test*"                 # product name glob pattern

Multiple --vid, --pid, and --name values are cross-producted into match rules. For example, --vid 1234 --vid 5678 --pid abcd creates two rules: 1234:abcd and 5678:abcd.

Monitor plug/unplug events

% usb-helper --listen

Outputs JSONL (one JSON object per line) — designed for piping into AI agents or automation scripts:

{"status": true, "action": "init", "meta": {...}, "data": [{"device_id": "usb:1-3", "vid": "1234", "pid": "abcd", "name": "TestDev", ...}]}
{"status": true, "action": "plug", "meta": {...}, "data": [{"device_id": "usb:2-5", "vid": "1234", "pid": "abcd", ...}]}
{"status": true, "action": "unplug", "meta": {...}, "data": [{"device_id": "usb:2-5", "vid": "1234", "pid": "abcd", ...}]}
{"status": true, "action": "stop", "meta": {...}, "data": []}

Events: init (startup device list), plug, unplug, error, stop (Ctrl+C).

Combine with filters:

% usb-helper --listen --vid 1234 --interval 1000
% usb-helper --listen --profile sample

Error output

When libusb or pyusb is missing, all modes (including --listen) emit a structured error:

{"status": false, "action": "error", "meta": {...}, "error": -1, "errorMessage": "No libusb backend found. Install libusb: macOS: brew install libusb | Linux: ..."}

Device Profiles

Profiles are TOML files that define named sets of device match rules. Instead of typing --vid and --pid every time, save your device definitions once and reference them by name.

Profile search directories (first match wins)

  1. ./usb-helper.d/ — current working directory (project-level)
  2. ~/.config/usb-helper/ — user config (shared across projects)

Create a profile

% mkdir -p ~/.config/usb-helper
% cat > ~/.config/usb-helper/sample.toml << 'EOF'
description = "My USB devices"

[[rules]]
vid = "1234"
pid = "abcd"
label = "Bulk mode"
[rules.metadata]
mode = "bulk"

[[rules]]
vid = "1234"
pid = "0002"
label = "Storage mode"
[rules.metadata]
mode = "storage"
EOF

Each [[rules]] entry supports these optional fields:

Field Description Example
vid Vendor ID (hex string) "1234"
pid Product ID (hex string) "abcd"
label Human-readable rule name "Bulk mode"
name Product name glob pattern "Test*"
serial Serial number glob pattern "SN-*"
metadata Arbitrary key-value pairs {mode = "bulk"}

Use a profile

% usb-helper --profile sample
% usb-helper --profile sample --listen
% usb-helper --profile sample --json

Or specify a TOML file directly:

% usb-helper --config ./my-devices.toml

Profile rules and CLI flags (--vid, --pid, --name) are merged — you can add extra filters on top of a profile.

List available profiles

% usb-helper profiles

Config directories (search order):
  ./usb-helper.d
  /Users/you/.config/usb-helper

Available profiles (1):

  sample
    My USB devices
    6 rule(s) — /Users/you/.config/usb-helper/sample.toml
% usb-helper profiles --json
{"status": true, "action": "profiles", "meta": {...}, "data": {"config_dirs": [...], "profiles": [{"name": "sample", ...}]}}

Project-level profiles

Place TOML files in usb-helper.d/ in your project root. These take priority over user-level profiles with the same name:

my-project/
  usb-helper.d/
    my-devices.toml      ← project-specific config
  src/
    main.py

Python API

Device monitoring

from usb_helper import USBMonitor, DeviceMatchRule

rules = [
    DeviceMatchRule(vid=0x1234, pid=0xABCD, label="Bulk mode"),
    DeviceMatchRule(vid=0x1234, pid=0x0002, label="Storage mode"),
]

monitor = USBMonitor(match_rules=rules, poll_interval_ms=500)

# One-time scan
for identity, rule in monitor.scan_once():
    print(f"Found: {identity} (rule: {rule.label})")

# Continuous monitoring
monitor.on_device_event = lambda event: print(f"[{event.event_type.value}] {event.device}")
monitor.run_forever()  # Ctrl+C to stop

Bulk transfer

from usb_helper import BulkDevice, DeviceIdentity

identity = DeviceIdentity(vid=0x1234, pid=0xABCD, bus=1, address=3)

with BulkDevice(identity, frame_size=65536) as device:
    # Write (auto-splits into frames)
    result = device.bulk_write(data)

    # Read
    result = device.bulk_read(512, timeout_ms=5000)
    if result.ok:
        print(f"Received {len(result.data)} bytes")

SCSI-over-Bulk

from usb_helper import SCSIDevice, DeviceIdentity

identity = DeviceIdentity(vid=0x1234, pid=0x0002, bus=1, address=5)

with SCSIDevice(identity) as device:
    # Send SCSI command (CBW → data → CSW)
    cdb = bytes([0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
    result = device.send_command(cdb=cdb, data_in_length=2, timeout_ms=5000)
    if result.ok:
        print(f"Response: {result.data.hex()}")

Load profiles programmatically

from usb_helper import load_profile, load_profile_by_name, list_profiles

# By name (searches config dirs)
profile = load_profile_by_name("sample")

# By path
profile = load_profile("./my-devices.toml")

# List all
for p in list_profiles():
    print(f"{p.name}: {p.description} ({p.rule_count} rules)")

# Use profile rules with USBMonitor
monitor = USBMonitor(match_rules=profile.rules)

Runtime metadata

from usb_helper import get_meta

meta = get_meta()
# {"usb_helper": "0.1.0", "python": "3.12.3", "platform": "...", "os": "Darwin", "arch": "arm64", "pyusb": "1.2.1", "libusb": "usb.backend.libusb1"}

Running Tests

Tests are fully mock-based — no real USB hardware needed.

% git clone https://github.com/changyy/py-usb-helper.git
% cd py-usb-helper
% pip install -e ".[dev]"
% python -m pytest tests/ -v

Current test suite: 102 tests covering types, CBW/CSW protocol, BulkDevice, SCSIDevice, USBMonitor, config loader, CLI, and meta.

To run with coverage:

% python -m pytest tests/ --cov=usb_helper --cov-report=term-missing

Project Structure

py-usb-helper/
  src/usb_helper/
    __init__.py         Package exports
    types.py            DeviceIdentity, DeviceMatchRule, DeviceEvent, TransferResult
    _cbw.py             CBW/CSW binary protocol (SCSI command blocks)
    device.py           USBDevice abstract base class
    bulk_device.py      BulkDevice — pyusb wrapper with frame-based writes
    scsi_device.py      SCSIDevice — SCSI-over-Bulk with CBW→data→CSW
    monitor.py          USBMonitor — polling-based attach/detach detection
    config.py           TOML profile loader
    cli.py              CLI entry point (usb-helper command)
  tests/
    test_types.py       DeviceIdentity, DeviceMatchRule matching
    test_cbw.py         CBW build/CSW parse, tag wrapping
    test_bulk_device.py BulkDevice open/close/read/write (22 tests)
    test_scsi_device.py SCSIDevice command/error/retry (8 tests)
    test_monitor.py     USBMonitor scan/attach/detach (11 tests)
    test_config.py      TOML loader, profile search, sample validation (18 tests)
    test_cli.py         CLI argument parsing, rule building, meta (23 tests)
  examples/
    sample.toml         Sample device profile example
  pyproject.toml
  README.md

Requirements

  • Python 3.10+
  • libusb (platform-specific, see installation)
  • pyusb >= 1.2.1

License

MIT © Yuan-Yi Chang

Metadata

Release files for usb-helper 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for usb-helper 1.1.0
File Size Uploaded Provenance
usb_helper-1.1.0.tar.gz 47.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for usb-helper 1.1.0
File Interpreter ABI Platform
usb_helper-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 84.9 kB

Release files / usb_helper-1.1.0.tar.gz

Download URL usb_helper-1.1.0.tar.gz
Size 47.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1e5e6f67f2104bff8b3fd561d91651662eb066c7c2c58f2b555b87ece1dea2b2
BLAKE2b-256 checksum
How to use checksums
0b9536c4a4219c18ea7f97d57b47338a284f8f377115dd3626fa629eeb0662f8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 30, 2026.

Transparency log

Release files / usb_helper-1.1.0-py3-none-any.whl

Download URL usb_helper-1.1.0-py3-none-any.whl
Size 37.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
34e1258f27f940361bcccd7afe2888adcc6dcac755c4d311daafc4ddbb982957
BLAKE2b-256 checksum
How to use checksums
48dac782a27f226bbe9d16e765d4ebd8a0f72a03007cd12d60af5178eef1263c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 30, 2026.

Transparency log

Security for usb-helper 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for usb_helper-1.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 30, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page