Plugs
Discover how to use OpenHTF's plugin system to abstract hardware interactions and encapsulate reusable test logic with examples.
Last updated · Verified with OpenHTF 1.6.1
Manage external hardware and resources with reusable logic.
OpenHTF manages a plug's lifecycle: it instantiates the plug when the test starts, injects it into the phases that declare it, and calls tearDown when the test ends so the connection to the instrument is always released.
- Test start
- __init__()plug instantiated
- phase_test(test, multimeter)multimeter.measure_voltage()
- tearDown()resources released
- Test end
Hardware tests often require connections to external resources like instruments, UUTs, or other softwares. OpenHTF manages these connections through Plugs, reusable classes that handle resource initialization, control, and cleanup, separating test logic from resource management for easier maintenance and better logging.
Syntax
A plug is a standard Python class that inherits from BasePlug.
from openhtf import plugs
import random
class MultimeterPlug(plugs.BasePlug):
def __init__(self):
# Simulate connecting to the multimeter
self.connected = True
def tearDown(self):
# Automatically called by OpenHTF after the test to clean up
self.connected = False
def measure_voltage(self):
# Simulate voltage measurement
return random.uniform(0, 10)
def measure_current(self):
# Simulate current measurement
return random.uniform(0, 2)Usage
You can then use the plug decorator in your test phases to inject the plug.
import openhtf as htf
from multimeter_plug import MultimeterPlug
@htf.plug(multimeter=MultimeterPlug)
def phase_test(test, multimeter):
# Use the plug to measure voltage and current
voltage = multimeter.measure_voltage()
current = multimeter.measure_current()
def main():
test = htf.Test(phase_test)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Hardware you connect this way — a multimeter over SCPI, a DUT over a serial port, a PLC over Modbus — has a dedicated guide: PyVISA, pyserial, pymodbus. OpenHTF also ships ready-made plugs for ADB, Fastboot and serial capture.
OpenHTF manages the plug's lifecycle automatically:
-
Instantiation: At the start of the test, OpenHTF instantiates the plug by calling its
__init__()method. -
Logging: During the test, the plug can log important actions or events using
self.logger. -
Teardown: After the test concludes, OpenHTF automatically calls the
tearDown()method to clean up resources. This ensures reliable cleanup since Python's destructors__del__()aren't always called.
User Input Plug
You can use the UserInput plug to prompt the operator during the test. The text_input parameter controls the type of interaction - when missing, it defaults to False (button prompt).
messagestrText shown to the operator, in the console and in the Operator UI.
text_inputboolTrue renders a text field; False (default) renders an Okay button.
timeout_sfloat or NoneRaise PromptUnansweredError if the operator does not answer in time. Default: wait forever.
image_urlstr or NoneURL of an image to show above the prompt in the Operator UI — a photo of the connector to plug, the LED to check. Ignored on the console.
cli_colorstrANSI color code for the console prompt (e.g. colorama.Fore.CYAN).
import openhtf as htf
from openhtf.plugs.user_input import UserInput
@htf.measures(
htf.Measurement("led_color")
.with_validator(lambda color: color in ["Red", "Green", "Blue"])
)
@htf.plug(user_input=UserInput)
def prompt_operator_led_color(test, user_input):
# text_input defaults to False when not specified
led_color = user_input.prompt(
message="What is the LED color? (Red/Green/Blue)",
text_input=True # Explicitly set to True for text input
)
test.measurements.led_color = led_color
def main():
test = htf.Test(prompt_operator_led_color)
test.execute(lambda: "SN1234")
if __name__ == "__main__":
main()Text Input vs Button Prompts
The text_input parameter determines the prompt behavior:
text_input=True: Displays a text input field where the operator types a responsetext_input=False(default): Displays a button prompt where the operator clicks "Okay"
# Text input prompt - operator types response
response = user_input.prompt(
message="What is the LED color? (Red/Green/Blue)",
text_input=True
)# Button prompt - operator clicks "Okay" (text_input defaults to False)
user_input.prompt(
message="Click Okay when the LED turns on"
)What is the LED color? (Red/Green/Blue):
--> Red
======================= test: openhtf_test outcome: PASS ======================Advanced use cases
Single plug configuration
You can define and load configurations specific to a plug, similar to test configurations.
from openhtf import BasePlug
from openhtf.util.configuration import CONF
# Define `com_port` configuration
CONF.declare("com_port", default_value="COM1")
class MultimeterPlug(BasePlug):
# Simulate connecting to the multimeter
def __init__(self):
self.com_port = CONF.com_port
self.connected = True
# Simulate measuring the voltage
def measure_voltage(self):
return 3.3
# Simulate disconnecting from the multimeter
# This method is called automatically by OpenHTF at the end of the test
def tearDown(self):
self.connected = FalseMultiple plug configuration
You can use bind_init_args to create multiple plug instances with variable configurations. This ensures flexible plug setup while maintaining compatibility with OpenHTF's automatic plug lifecycle management.
from openhtf import BasePlug
class MultimeterPlug(BasePlug):
# Simulate connecting to the multimeter with port argument
def __init__(self, com_port: str):
self.com_port = com_port
self.connected = True
# Simulate measuring the voltage
def measure_voltage(self):
return 3.3
# Simulate disconnecting from the multimeter
# This method is called automatically by OpenHTF at the end of the test
def tearDown(self):
self.connected = Falseimport openhtf as htf
from openhtf.plugs import plug
from openhtf.util.configuration import CONF, bind_init_args
from multimeter_plug import MultimeterPlug
COM_PORT_1 = CONF.declare("com_port_1", default_value="COM1")
COM_PORT_2 = CONF.declare("com_port_2", default_value="COM2")
MultimeterPlug1 = bind_init_args(MultimeterPlug, COM_PORT_1)
MultimeterPlug2 = bind_init_args(MultimeterPlug, COM_PORT_2)
@plug(multimeter=MultimeterPlug1)
def test_voltage_1(test, multimeter):
multimeter.measure_voltage()
@plug(multimeter=MultimeterPlug2)
def test_voltage_2(test, multimeter):
multimeter.measure_voltage()
def main():
test = htf.Test(test_voltage_1, test_voltage_2)
test.execute(lambda: "PCB0001") # UUT S/N
if __name__ == "__main__":
main()