bouwmeester-lockbox-api 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: bouwmeester-lockbox-api
3
+ Version: 0.1.0
4
+ Summary: Python client for the Bouwmeester Lab Lockbox REST API
5
+ License-Expression: AGPL-3.0-only
6
+ Project-URL: Homepage, https://github.com/Bouwmeester-Lab/Lockbox-PythonApi
7
+ Project-URL: Repository, https://github.com/Bouwmeester-Lab/Lockbox-PythonApi
8
+ Project-URL: Issues, https://github.com/Bouwmeester-Lab/Lockbox-PythonApi/issues
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: httpx>=0.27
13
+ Requires-Dist: signalrcore==1.0.2
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
16
+ Requires-Dist: ruff; extra == "dev"
17
+ Dynamic: license-file
18
+
19
+ # Lockbox Python API
20
+
21
+ Python 3.11+ synchronous controls for the Lockbox server. Install from this
22
+ directory with `python -m pip install .` (or `python -m pip install -e ".[dev]"`
23
+ for development).
24
+
25
+ ```python
26
+ from lockbox import Server, SlopePreference
27
+
28
+ with Server("http://lockboxcontrol.internal:5106") as server:
29
+ box = server.get_lockbox(1) # Choose an ID from server.list_lockboxes().
30
+ box.on_coarse_resonance_found(lambda: print("Coarse resonance found"))
31
+ box.on_fine_resonance_found(lambda: print("Fine resonance found"))
32
+ box.on_lockbox_freeze_ready(lambda: print("Ready to freeze"))
33
+
34
+ box.set_setpoint(0.0)
35
+ box.set_low_high_threshold(-100, 100, SlopePreference.POSITIVE)
36
+ box.lock()
37
+ input("Press Enter to close the client connections... ")
38
+ ```
39
+
40
+ `Server` owns one HTTP client and two SignalR connections: `/Hubs/Status` for
41
+ events and `/Hubs/Run` for setpoint and threshold commands. It uses the server's
42
+ JSON hub protocol. Entering the context connects both hubs; exiting closes all
43
+ connections without changing the hardware. There are no reconnects or retries.
44
+ `Server(url, timeout=10.0)` sets HTTP timeouts and the time allowed for hub
45
+ handshake readiness and command completion, not for physical device operations.
46
+ Supply the server root URL, including any hosting path prefix, without `/api`.
47
+
48
+ `list_lockboxes()` and `get_lockbox(id)` reuse objects by ID, preserving callbacks
49
+ while refreshing their name, IP and MAC metadata. Objects are registered for
50
+ status routing automatically. `connect_to_status_hub(box)` also registers manually
51
+ created objects; repeating registration of the same object is harmless.
52
+
53
+ ## Commands
54
+
55
+ All command methods return `None` after HTTP success or Run hub invocation
56
+ completion. They do not wait for a status or physical completion. Failed HTTP
57
+ responses raise `Exception(response.text)`; hub errors use the server-provided
58
+ error message. Transport errors propagate.
59
+
60
+ | Method | Argument convention |
61
+ | --- | --- |
62
+ | `set_gain(gain)` | Input gain, at least 1 |
63
+ | `set_demodulation_cutoff(frequency)` | Hz |
64
+ | `set_demodulation_phase(phase)` | Radians |
65
+ | `set_modulation_amplitude(amplitude)` | Integer DAC codes, 0–524287 |
66
+ | `set_demodulation_amplitude(amplitude)` | Server amplitude scale, 0–100 |
67
+ | `set_setpoint(setpoint)` | Native device setpoint units |
68
+ | `set_pid(p, i, d)` | Native device PID coefficients |
69
+ | `set_threshold(threshold)` | Integer reflection threshold |
70
+ | `set_low_high_threshold(low, high, slope_preference=SlopePreference.NONE)` | Integer error-signal limits; enum values `NONE`, `NEGATIVE`, `POSITIVE` |
71
+ | `set_fine_output_code(code)`, `set_coarse_output_code(code)` | Signed DAC codes, -524287–524287 |
72
+ | `lock()`, `unlock()` | Normal lock; unlock without starting a scan |
73
+ | `freeze()`, `unfreeze()` | Both toggle freeze through the same endpoint |
74
+ | `reload_configuration()` | Apply the server's saved configuration |
75
+
76
+ Setters change runtime values, not saved configuration. Validation is left to
77
+ the server, except conversion of the slope enum. Slope preference is forwarded
78
+ as the server enum value (0, 1, or 2). The current firmware `ThresholdsEndpoint`
79
+ updates limits but does not read the slope query parameter; actual slope behavior
80
+ depends on firmware support. This package does not change firmware.
81
+
82
+ The freeze endpoint may translate a firmware rejection (including not-ready 409)
83
+ into a server 500. The client reports the response it receives.
84
+
85
+ ## Callbacks
86
+
87
+ Register callbacks after entering the context. Each registration stores one
88
+ zero-argument function; registering again replaces it.
89
+
90
+ | Registration | Incoming Arduino status |
91
+ | --- | --- |
92
+ | `on_coarse_resonance_found(callback)` | `CoarseResonanceFound` |
93
+ | `on_fine_resonance_found(callback)` | `FineResonanceFound` |
94
+ | `on_lock_lost(callback)` | `Unlocked` |
95
+ | `on_lockbox_frozen(callback)` | `FrozenEnabled` |
96
+ | `on_lockbox_unfrozen(callback)` | `FrozenDisabled` |
97
+ | `on_lockbox_freeze_ready(callback)` | `DriftEstimateReady` |
98
+
99
+ `on_lock_acquired` and `on_lockbox_recentering` raise `NotImplementedError`
100
+ because dedicated statuses do not exist. HTTP results never trigger callbacks.
101
+ Unregistered devices and unrelated statuses are ignored.
102
+
103
+ Functions run directly on the status receiver thread, without arguments or custom
104
+ exception recovery. They may issue commands using the separate HTTP/Run connections.
105
+ While a callback runs, it delays further status delivery: do not wait inside it
106
+ for another callback. There is no replay, ordering contract, or experiment scheduler.
107
+
108
+ ## Saved configuration
109
+
110
+ Fetch an independent snapshot, edit its supported fields, and explicitly save it:
111
+
112
+ ```python
113
+ from lockbox import Server, PidControllerType, SlopePreference
114
+
115
+ with Server("http://lockboxcontrol.internal:5106") as server:
116
+ box = server.get_lockbox(1)
117
+ config = box.get_configuration()
118
+ config.pid.proportional_gain = 2.0
119
+ config.pid.controller_type = PidControllerType.FILTERED_PID
120
+ config.demodulation.set_point = 0.1
121
+ config.lock_thresholds.out_of_lock_threshold = 80
122
+ config.lock_thresholds.ms_without_lock = 250
123
+ config.lock_thresholds.delock_smoothing_constant = 0.25
124
+ config.coarse_scan.scan_min = -2000
125
+ config.coarse_scan.scan_max = 2000
126
+ config.fine_scan.scan_time_ms = 1500
127
+ if config.pdh is not None:
128
+ config.pdh.low_limit = -100.5
129
+ config.pdh.high_limit = 100.25
130
+ config.pdh.slope_preference = SlopePreference.POSITIVE
131
+ box.save_configuration(config)
132
+
133
+ # Apply only when wanted:
134
+ box.reload_configuration()
135
+ ```
136
+
137
+ `get_configuration()` reads `GET /api/LockboxConfigurations/arduino/{id}` on
138
+ every call. It returns a `LockboxConfiguration` with typed, snake_case sections:
139
+ `pid`, `demodulation`, `lock_thresholds`, `pdh`, `coarse_scan`, `fine_scan`,
140
+ `coarse_refinement`, `fine_refinement`, and top-level `modulation_amplitude`.
141
+ An absent optional PDH section is `None`. Runtime setters do not modify snapshots.
142
+ `PidControllerType` has `SIMPLE_PI` and `FILTERED_PID`; slope settings reuse
143
+ `SlopePreference.NONE`, `NEGATIVE`, and `POSITIVE`.
144
+
145
+ All lock-threshold and coarse/fine scan settings are editable. IDs, device
146
+ identity, section references, `notch_filters`, and
147
+ `filter_selections` are read-only. Filter selections expose `notch_filter_id`,
148
+ `enabled`, and the associated read-only `notch_filter` definition when present.
149
+ Snapshots update existing records only; a missing configuration raises the normal
150
+ HTTP error. They cannot create records or replace section identities.
151
+
152
+ `save_configuration(config)` returns `None` after these eight sequential writes
153
+ (seven when PDH is absent):
154
+
155
+ | Saved settings | Endpoint |
156
+ | --- | --- |
157
+ | PID coefficients, controller type, derivative cutoff | `PUT /api/PidConfigurations/{pidId}/persist` |
158
+ | Demodulation cutoff, phase, amplitude, setpoint | `PUT /api/DemodulationConfigurations/{id}/persist` |
159
+ | Modulation amplitude | `PUT /api/PidConfigurations/{pidId}/modulation` |
160
+ | All lock-threshold settings | `PUT /api/LockThresholdSettings/{id}` |
161
+ | PDH low/high limits and slope, when present | `PUT /api/PdhSettings/{id}` |
162
+ | Coarse scan limits and time | `PUT /api/ScanSettings/{coarseId}` |
163
+ | Fine scan limits and time | `PUT /api/ScanSettings/{fineId}` |
164
+ | Coarse and fine refinement | `PUT /api/PidConfigurations/{pidId}/refinement` |
165
+
166
+ Saving does not apply hardware settings or modify filters. It sends all supported
167
+ values, including unchanged ones. Fetch again before editing if another client
168
+ may have changed the saved configuration; there is no conflict detection.
169
+
170
+ The snapshot's device/server identity, required linked records, and payloads are
171
+ checked before any writes, including both required scan records. Nonfinite or
172
+ unserializable payloads are rejected before writing. PDH limits accept decimal
173
+ values and are transmitted without integer conversion. Range and ordering
174
+ validation is performed by the server. PDH is skipped only when both its section
175
+ and linked ID are absent; a linked ID with a missing section is an error.
176
+
177
+ **Saving is not atomic.** A failed response stops subsequent requests and raises
178
+ `Exception(response.text)`; earlier successful writes remain saved. There is no
179
+ rollback or retry. Other server-side validation errors can therefore leave a
180
+ partially saved configuration. The ordinary PID update endpoint is not used
181
+ because it also applies changes to hardware.
182
+
183
+ This version requires the server's resource-style PUT endpoints for
184
+ `LockThresholdSettings`, `PdhSettings`, and `ScanSettings`. There is no fallback
185
+ to the old integer-only threshold-saving endpoint. Notch-filter editing remains
186
+ excluded; definitions, selections, and enabled flags are preserved unchanged.
187
+ Runtime setters still use their existing interfaces; fractional PDH support here
188
+ applies to saved configuration.
189
+
190
+ ## Development
191
+
192
+ Run `python -m pytest` and `python -m ruff check src tests` from this directory.
193
+ Tests use mock HTTP responses and hub connections; they do not operate hardware.
@@ -0,0 +1,9 @@
1
+ bouwmeester_lockbox_api-0.1.0.dist-info/licenses/LICENSE,sha256=ILBn-G3jdarm2w8oOrLmXeJNU3czuJvVhDLBASWdhM8,34522
2
+ lockbox/__init__.py,sha256=N2Ko0lVQCj_seVOSsQ_80fmkq0tgHdHG58ArP21QY9A,682
3
+ lockbox/_hub.py,sha256=qMJIfFwxFj-nVSGVvmo_b_ZUy1uqUb4AwwNemR9-bSo,2963
4
+ lockbox/client.py,sha256=PlzHQB-GjriMgjw82XIByEsMulMs4pXAoJ7ho-c_6uc,9490
5
+ lockbox/configuration.py,sha256=HYc8lM-AH-s5FxsDGqkCfTTRNHKg3oTGaMLmr3PYFdg,9953
6
+ bouwmeester_lockbox_api-0.1.0.dist-info/METADATA,sha256=avb6Mz5gXOf1CVxWiuNL7S5oOQxA3vYjBkoLCLJ5xxE,9660
7
+ bouwmeester_lockbox_api-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
8
+ bouwmeester_lockbox_api-0.1.0.dist-info/top_level.txt,sha256=_iFppj6wKh_GTFFFdDcJbbJ0dIoQWhI6Ng2r6Tr11As,8
9
+ bouwmeester_lockbox_api-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+