Skip to main content
LagerBox is the entry point. It is cheap to construct and does no network traffic until you call something, so building one in a test helper costs nothing.

Constructing

connect() accepts a host name, an IP, a host:port, or a full URL. The scheme defaults to http and the port to 9000.

The builder

timeout() sets the budget for quick commands only. Long-running actions compute their own wider budgets regardless — a wait_for_level of 60 seconds is not cut short by a 10-second default.

Environment variables

Discovery

BoxCapabilities

status().capabilities says which endpoints the box serves.
A capability flag mirrors route registration, not the box’s ability to do the work. ble_command: true on a box whose container has no BlueZ still fails every BLE call with Error::Box and HTTP 502; wifi_command: true without nmcli installed does the same. Use these flags to decide whether an endpoint exists, not whether it will succeed.

usb_devices()

Enumerates the box’s USB bus from sysfs. It takes a few milliseconds, needs no exclusive access to anything, and is therefore safe to poll while waiting for a DUT to re-enumerate.
Requires box 0.33.0 or newer.
devnum changes every time a device re-enumerates. Match on serial, or on vid/pid, when checking that a device came back after a power cycle.

Locking the box

A shared bench needs a reservation, or two CI jobs will drive the same instruments at once.
Contention is loud. Locking a box someone else holds is Error::Box with HTTP 409 and Box is locked by <holder>; unlocking as a non-holder is HTTP 403 with the same message. Unlocking a box that is already free succeeds.
lock_guard() is blocking-only. The async client has every other lock method, but no RAII guard.

Safety limits

Per-net ceilings, enforced by the box’s hardware service rather than by your test — so they hold even when the test misbehaves. Requires box 0.35.0 or newer.
A refused setpoint arrives as Error::Box:
and a refused erase as HTTP 403:
A PUT replaces the whole record. Fields you leave as None are removed, not preserved. Setting only max_voltage on a net that already had a max_current ceiling drops the current ceiling. Read the current limits first and modify what you read.
Ceilings cap setpoints, not protection trips. With a 3.6 V ceiling, set_voltage(5.0) is refused — but set_ovp(12.0) is accepted and applied. A refused set_ocp on such a net is being refused by the instrument’s own hardware limit, not by the ceiling. Do not rely on a safety limit to bound an OVP or OCP setting.
There is deliberately no max_power: one setter call establishes either a voltage or a current, never both, so the box cannot evaluate a power ceiling honestly and refuses the key outright.

Notes

  • Handles borrow the client, so keep the LagerBox alive as long as any handle derived from it.
  • The box serializes access per physical instrument, so parallel tests cannot interleave I/O on one instrument. Tests sharing a net still see each other’s state changes.
  • nets() falls back to the older {"nets": [...]} response shape automatically, so it works against boxes that predate the bare-array form.