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
MultimeterPlug
idle
self.logger

    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.

    multimeter_plug.py
    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.

    main.py
    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:

    1. Instantiation: At the start of the test, OpenHTF instantiates the plug by calling its __init__() method.

    2. Logging: During the test, the plug can log important actions or events using self.logger.

    3. 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).

    messagestr

    Text shown to the operator, in the console and in the Operator UI.

    text_inputbool

    True renders a text field; False (default) renders an Okay button.

    timeout_sfloat or None

    Raise PromptUnansweredError if the operator does not answer in time. Default: wait forever.

    image_urlstr or None

    URL 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_colorstr

    ANSI color code for the console prompt (e.g. colorama.Fore.CYAN).

    main.py
    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 response
    • text_input=False (default): Displays a button prompt where the operator clicks "Okay"
    Text Input Example
    # Text input prompt - operator types response
    response = user_input.prompt(
        message="What is the LED color? (Red/Green/Blue)",
        text_input=True
    )
    Button Prompt Example
    # Button prompt - operator clicks "Okay" (text_input defaults to False)
    user_input.prompt(
        message="Click Okay when the LED turns on"
    )
    Terminal
    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.

    multimeter_plug.py
    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 = False

    Multiple 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.

    multimeter_plug.py
    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 = False
    main.py
    import 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()

    On this page

    First-pass yield
    0%4.1
    Track with TofuPilot