Lockbox Python API
Python 3.11+ synchronous controls for the Lockbox server. Install from this
directory with python -m pip install . (or python -m pip install -e ".[dev]"
for development).
from lockbox import Server, SlopePreference
with Server("http://lockboxcontrol.internal:5106") as server:
box = server.get_lockbox(1) # Choose an ID from server.list_lockboxes().
box.on_coarse_resonance_found(lambda: print("Coarse resonance found"))
box.on_fine_resonance_found(lambda: print("Fine resonance found"))
box.on_lockbox_freeze_ready(lambda: print("Ready to freeze"))
box.set_setpoint(0.0)
box.set_low_high_threshold(-100, 100, SlopePreference.POSITIVE)
box.lock()
input("Press Enter to close the client connections... ")
Server owns one HTTP client and two SignalR connections: /Hubs/Status for
events and /Hubs/Run for setpoint and threshold commands. It uses the server's
JSON hub protocol. Entering the context connects both hubs; exiting closes all
connections without changing the hardware. There are no reconnects or retries.
Server(url, timeout=10.0) sets HTTP timeouts and the time allowed for hub
handshake readiness and command completion, not for physical device operations.
Supply the server root URL, including any hosting path prefix, without /api.
list_lockboxes() and get_lockbox(id) reuse objects by ID, preserving callbacks
while refreshing their name, IP and MAC metadata. Objects are registered for
status routing automatically. connect_to_status_hub(box) also registers manually
created objects; repeating registration of the same object is harmless.
Commands
All command methods return None after HTTP success or Run hub invocation
completion. They do not wait for a status or physical completion. Failed HTTP
responses raise Exception(response.text); hub errors use the server-provided
error message. Transport errors propagate.
| Method | Argument convention |
|---|---|
set_gain(gain) |
Input gain, at least 1 |
set_demodulation_cutoff(frequency) |
Hz |
set_demodulation_phase(phase) |
Radians |
set_modulation_amplitude(amplitude) |
Integer DAC codes, 0–524287 |
set_demodulation_amplitude(amplitude) |
Server amplitude scale, 0–100 |
set_setpoint(setpoint) |
Native device setpoint units |
set_pid(p, i, d) |
Native device PID coefficients |
set_threshold(threshold) |
Integer reflection threshold |
set_low_high_threshold(low, high, slope_preference=SlopePreference.NONE) |
Integer error-signal limits; enum values NONE, NEGATIVE, POSITIVE |
set_fine_output_code(code), set_coarse_output_code(code) |
Signed DAC codes, -524287–524287 |
lock(), unlock() |
Normal lock; unlock without starting a scan |
freeze(), unfreeze() |
Both toggle freeze through the same endpoint |
reload_configuration() |
Apply the server's saved configuration |
Setters change runtime values, not saved configuration. Validation is left to
the server, except conversion of the slope enum. Slope preference is forwarded
as the server enum value (0, 1, or 2). The current firmware ThresholdsEndpoint
updates limits but does not read the slope query parameter; actual slope behavior
depends on firmware support. This package does not change firmware.
The freeze endpoint may translate a firmware rejection (including not-ready 409) into a server 500. The client reports the response it receives.
Callbacks
Register callbacks after entering the context. Each registration stores one zero-argument function; registering again replaces it.
| Registration | Incoming Arduino status |
|---|---|
on_coarse_resonance_found(callback) |
CoarseResonanceFound |
on_fine_resonance_found(callback) |
FineResonanceFound |
on_lock_lost(callback) |
Unlocked |
on_lockbox_frozen(callback) |
FrozenEnabled |
on_lockbox_unfrozen(callback) |
FrozenDisabled |
on_lockbox_freeze_ready(callback) |
DriftEstimateReady |
on_lock_acquired and on_lockbox_recentering raise NotImplementedError
because dedicated statuses do not exist. HTTP results never trigger callbacks.
Unregistered devices and unrelated statuses are ignored.
Functions run directly on the status receiver thread, without arguments or custom exception recovery. They may issue commands using the separate HTTP/Run connections. While a callback runs, it delays further status delivery: do not wait inside it for another callback. There is no replay, ordering contract, or experiment scheduler.
Saved configuration
Fetch an independent snapshot, edit its supported fields, and explicitly save it:
from lockbox import Server, PidControllerType, SlopePreference
with Server("http://lockboxcontrol.internal:5106") as server:
box = server.get_lockbox(1)
config = box.get_configuration()
config.pid.proportional_gain = 2.0
config.pid.controller_type = PidControllerType.FILTERED_PID
config.demodulation.set_point = 0.1
config.lock_thresholds.out_of_lock_threshold = 80
config.lock_thresholds.ms_without_lock = 250
config.lock_thresholds.delock_smoothing_constant = 0.25
config.coarse_scan.scan_min = -2000
config.coarse_scan.scan_max = 2000
config.fine_scan.scan_time_ms = 1500
if config.pdh is not None:
config.pdh.low_limit = -100.5
config.pdh.high_limit = 100.25
config.pdh.slope_preference = SlopePreference.POSITIVE
box.save_configuration(config)
# Apply only when wanted:
box.reload_configuration()
get_configuration() reads GET /api/LockboxConfigurations/arduino/{id} on
every call. It returns a LockboxConfiguration with typed, snake_case sections:
pid, demodulation, lock_thresholds, pdh, coarse_scan, fine_scan,
coarse_refinement, fine_refinement, and top-level modulation_amplitude.
An absent optional PDH section is None. Runtime setters do not modify snapshots.
PidControllerType has SIMPLE_PI and FILTERED_PID; slope settings reuse
SlopePreference.NONE, NEGATIVE, and POSITIVE.
All lock-threshold and coarse/fine scan settings are editable. IDs, device
identity, section references, notch_filters, and
filter_selections are read-only. Filter selections expose notch_filter_id,
enabled, and the associated read-only notch_filter definition when present.
Snapshots update existing records only; a missing configuration raises the normal
HTTP error. They cannot create records or replace section identities.
save_configuration(config) returns None after these eight sequential writes
(seven when PDH is absent):
| Saved settings | Endpoint |
|---|---|
| PID coefficients, controller type, derivative cutoff | PUT /api/PidConfigurations/{pidId}/persist |
| Demodulation cutoff, phase, amplitude, setpoint | PUT /api/DemodulationConfigurations/{id}/persist |
| Modulation amplitude | PUT /api/PidConfigurations/{pidId}/modulation |
| All lock-threshold settings | PUT /api/LockThresholdSettings/{id} |
| PDH low/high limits and slope, when present | PUT /api/PdhSettings/{id} |
| Coarse scan limits and time | PUT /api/ScanSettings/{coarseId} |
| Fine scan limits and time | PUT /api/ScanSettings/{fineId} |
| Coarse and fine refinement | PUT /api/PidConfigurations/{pidId}/refinement |
Saving does not apply hardware settings or modify filters. It sends all supported values, including unchanged ones. Fetch again before editing if another client may have changed the saved configuration; there is no conflict detection.
The snapshot's device/server identity, required linked records, and payloads are checked before any writes, including both required scan records. Nonfinite or unserializable payloads are rejected before writing. PDH limits accept decimal values and are transmitted without integer conversion. Range and ordering validation is performed by the server. PDH is skipped only when both its section and linked ID are absent; a linked ID with a missing section is an error.
Saving is not atomic. A failed response stops subsequent requests and raises
Exception(response.text); earlier successful writes remain saved. There is no
rollback or retry. Other server-side validation errors can therefore leave a
partially saved configuration. The ordinary PID update endpoint is not used
because it also applies changes to hardware.
This version requires the server's resource-style PUT endpoints for
LockThresholdSettings, PdhSettings, and ScanSettings. There is no fallback
to the old integer-only threshold-saving endpoint. Notch-filter editing remains
excluded; definitions, selections, and enabled flags are preserved unchanged.
Runtime setters still use their existing interfaces; fractional PDH support here
applies to saved configuration.
Development
Run python -m pytest and python -m ruff check src tests from this directory.
Tests use mock HTTP responses and hub connections; they do not operate hardware.
Metadata
Release files for bouwmeester-lockbox-api 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bouwmeester_lockbox_api-0.1.1.tar.gz | 29.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bouwmeester_lockbox_api-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.0 kB
Release files / bouwmeester_lockbox_api-0.1.1.tar.gz
| Download URL | bouwmeester_lockbox_api-0.1.1.tar.gz |
|---|---|
| Size | 29.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
600566acf31d368c56f325a497047a6373a342c9407b9c394fd0cb6fb7230b43
|
|
BLAKE2b-256 checksum How to use checksums |
f7d9c714176b5099c27214efc6c3ad287b8d06b7c29e1a0713e15b8066b626a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 7, 2026.
Transparency logRelease files / bouwmeester_lockbox_api-0.1.1-py3-none-any.whl
| Download URL | bouwmeester_lockbox_api-0.1.1-py3-none-any.whl |
|---|---|
| Size | 25.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
506806c00312ed2b05eef91cbac00e685dde8d251319f71241ee35ea09b4e68d
|
|
BLAKE2b-256 checksum How to use checksums |
c8fd4333d1622bffe0cfa59e22122b94906370391902f6db1b3d078cf9a321dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 7, 2026.
Transparency log