Bundled Plugs
The plugs that ship with OpenHTF — UserInput, ADB and Fastboot over USB (openhtf.plugs.usb), Cambrionix USB hubs, SerialCollectionPlug for serial logging, DeviceWrappingPlug — with install extras, configuration keys and usage.
Last updated · Verified with OpenHTF 1.6.1
OpenHTF is not an instrument library, but a handful of plugs ship in openhtf.plugs. They cover the operator, Android devices over USB, USB hub control and serial port capture. Anything else — SCPI instruments, PLCs, your own DUT protocol — you write yourself, following the instrument guides.
| Plug | Module | Extra | Purpose |
|---|---|---|---|
UserInput | openhtf.plugs.user_input | — | Operator prompts (console and Operator UI) |
AdbDevice | openhtf.plugs.usb.adb_device | usb-plugs | Android Debug Bridge over USB, no adb binary |
FastbootDevice | openhtf.plugs.usb.fastboot_device | usb-plugs | Fastboot protocol over USB |
LibUsbHandle | openhtf.plugs.usb.local_usb | usb-plugs | Raw USB bulk endpoints via libusb1 |
EtherSync | openhtf.plugs.cambrionix | — | Cambrionix USB hub: map port → device serial, open a handle |
SerialCollectionPlug | openhtf.plugs.generic.serial_collection | serial-collection-plug | Background capture of a serial port to a file |
DeviceWrappingPlug | openhtf.plugs.device_wrapping | — | Base class that proxies attribute access to a wrapped device object |
UserInput
The one plug every production test uses. Covered on the Plugs page (prompt, text_input, timeout_s, image_url) and on Device Under Test (prompt_for_test_start).
USB plugs: ADB and Fastboot
pip install "openhtf[usb-plugs]" pulls in libusb1 and M2Crypto. The plugs speak the ADB and Fastboot wire protocols directly over libusb — no platform adb install, no daemon fighting your test for the device.
from openhtf.core import base_plugs
from openhtf.plugs.usb import adb_device, local_usb
class AndroidDut(base_plugs.BasePlug):
def __init__(self):
# Open the first ADB-interface USB device; filter by serial_number= or port_path= in a fixture.
handle = local_usb.LibUsbHandle.open(
interface_class=adb_device.CLASS,
interface_subclass=adb_device.SUBCLASS,
interface_protocol=adb_device.PROTOCOL,
)
self.adb = adb_device.AdbDevice.connect(handle)
def shell(self, command: str, timeout_ms: int = 5000) -> str:
return self.adb.command(command, timeout_ms=timeout_ms)
def tearDown(self):
self.adb.close()@htf.plug(dut=AndroidDut)
@htf.measures(htf.Measurement("battery_level").in_range(20, 100))
def read_battery(test, dut):
out = dut.shell("dumpsys battery | grep level")
test.measurements.battery_level = int(out.split(":")[1])AdbDevicecommand(cmd, raw=False, timeout_ms=None), async_command(...), push(local, remote), pull(remote, local=None), install(apk_path), list(path), reboot(destination=''), remount(), root(), get_serial(), get_system_type(), close(). Authentication uses an RSA key via M2CryptoSigner when the device requires it.
FastbootDeviceconnect(usb_handle), get_boot_config(name), set_boot_config(name, value), lock(), close(); flashing helpers live in fastboot_protocol.
LibUsbHandleopen(**filters) / iter_open(**filters) by serial_number, port_path, interface class/subclass/protocol; read(length, timeout_ms), write(data, timeout_ms), port_path, is_closed(), close().
Cambrionix hubs
openhtf.plugs.cambrionix.EtherSync(mac_address) talks to a Cambrionix EtherSync-capable hub and gives get_usb_serial(port_num) and open_usb_handle(port_num), so a fixture can address "the DUT on port 3" instead of a serial number that changes with every unit.
SerialCollectionPlug
Streams everything a serial port emits — a DUT's boot log, a modem trace — to a file while other phases run.
import openhtf as htf
from openhtf.plugs.generic import serial_collection
from openhtf.util import configuration
CONF = configuration.CONF
CONF.load(serial_collection_port="/dev/ttyUSB0", serial_collection_baud=115200)
@htf.plug(uart=serial_collection.SerialCollectionPlug)
def boot_dut(test, uart):
uart.start_collection("/tmp/boot.log")
power_cycle()
wait_for_prompt()
uart.stop_collection()
test.attach_from_file("/tmp/boot.log", name="boot_log")serial_collection_portconfig key/dev/ttyACM0.serial_collection_baudconfig key115200.start_collection(dest) / stop_collection() / is_collecting()The constructor uses @CONF.inject_positional_args, so the two keys must be declared/loaded before the plug is instantiated. Importing the module without pyserial installed raises with an explicit hint to install the extra.
DeviceWrappingPlug
A base class for plugs that wrap an existing driver object: set self._device and attribute access falls through to it, with logging of each call. Useful to expose a vendor SDK object as a plug without writing pass-through methods.
from openhtf.plugs import device_wrapping
class ScopePlug(device_wrapping.DeviceWrappingPlug):
def __init__(self):
super().__init__(vendor_sdk.Oscilloscope("192.168.1.20")) # becomes self._device
# In a phase: scope.set_timebase(1e-3) is forwarded to the SDK objectWriting your own
Everything else is a subclass of BasePlug with __init__, methods and tearDown(). The instrument guides show the three most common transports:
JSON format
Field-by-field reference for the JSON file OpenHTF's OutputToJSON writes — top-level record, metadata, phases, measurements, validators, units, attachments, subtests, branches, checkpoints, diagnoses, log records — with a complete annotated sample.
Multi-station
Run OpenHTF on several test stations and see them from one place — the built-in dashboard_server, multicast station discovery, station_server history, configuration per station, and where the built-in tooling stops.