fix: authentication extension packet ordering and Bleak 3.x compatibility#25
Open
eman wants to merge 12 commits into
Open
fix: authentication extension packet ordering and Bleak 3.x compatibility#25eman wants to merge 12 commits into
eman wants to merge 12 commits into
Conversation
MkDocs does not recognise 'version' as a valid top-level config key. Running with --strict aborted the build with: WARNING - Config value 'version': Unrecognised configuration name: version Aborted with 1 configuration warnings in 'strict' mode! Removing the key fixes the GitHub Pages workflow.
- Add _make_bleak_client() helper in client.py to handle the deprecated
adapter= kwarg: on Bleak 3+ / Linux uses bluez={'adapter': ...};
on other platforms the adapter concept is BlueZ-only and is omitted;
falls back to adapter= for Bleak < 3
- Add connection checks in TelemetryService.read_once() before the
flow/pressure and temperature queries so a mid-sequence pump
disconnect returns the partial telemetry already collected instead
of raising BleakError (reported in issue #24)
- Document Bleak 3.x support in pyproject.toml dependency comment
…hen EXTEND_2) The extension packets were being sent in a TaskGroup (parallel), with EXTEND_2 submitted first despite the protocol spec requiring EXTEND_1 first. On strict firmware (BLE V06.00.01, issue #24) this caused the session to not be fully established, resulting in the pump disconnecting ~500ms after the first data query. Changes: - Send EXTEND_1 before EXTEND_2 (matches 02_authentication.md packet trace which is the empirical source of truth) - Use sequential awaits with a 50ms gap between packets instead of concurrent TaskGroup tasks - matches the documented reference implementation timing - Fix the pseudocode reference comment at the bottom of the module which had the order reversed
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses a BLE firmware-specific authentication regression (issue #24) by enforcing correct extension packet sequencing/timing, adds compatibility for Bleak 3.x’s adapter= deprecation, and makes telemetry reads more resilient to mid-sequence disconnects.
Changes:
- Send authentication extension packets sequentially (EXTEND_1 → 50ms → EXTEND_2) instead of concurrently/out-of-order.
- Add
_make_bleak_client()to select BleakClient kwargs compatible with Bleak 2.x vs 3.x (adapter=vsbluez={...}on Linux). - Return partial telemetry from
TelemetryService.read_once()when a disconnect is detected mid-read.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| src/alpha_hwr/core/authentication.py | Enforces correct extension packet ordering/timing; updates embedded pseudocode accordingly. |
| src/alpha_hwr/client.py | Introduces Bleak client factory for Bleak 3.x adapter= deprecation handling. |
| src/alpha_hwr/services/telemetry.py | Adds connection-state guards to return partial telemetry instead of raising after disconnects. |
| pyproject.toml | Notes Bleak 3.x support in dependency comment. |
Comments suppressed due to low confidence (1)
src/alpha_hwr/services/telemetry.py:284
- New behavior: if the pump disconnects before the temperature query, read_once() returns the partial telemetry rather than attempting the query. Please add a unit test covering this path (transport.is_connected() -> False) to ensure the early-return behavior is preserved.
# 3. Query Temperatures (always poll)
if not self.transport.is_connected():
logger.debug("Pump disconnected before temperature query")
return self._telemetry
- _make_bleak_client: wrap _pkg_version() in try/except and fall back to inspecting BleakClient.__init__ signature when importlib.metadata is unavailable (editable/vendored installs) - send_extension_packets: add delay parameter (default 0.05s) so fast_mode=True correctly skips the intra-stage sleep; update authenticate() to pass delay=0 in fast_mode - authenticate() docstring: fix stage-3 description from 'packets in parallel' to sequential order, and clarify fast_mode skips all delays - Add tests/unit/core/test_authentication.py: verify EXTEND_1 before EXTEND_2, sleep between packets, no sleep in fast_mode - Add tests/unit/core/test_make_bleak_client.py: verify Bleak 2/3 and Linux/non-Linux branching, PackageNotFoundError signature fallback - Add tests/unit/core/test_telemetry_disconnect_guard.py: verify read_once() returns partial telemetry without raising when transport disconnects after motor-state or flow/pressure query
- Change SetpointInfo.control_mode field type from int to ControlMode | int and add a field_validator that coerces known raw int values to the ControlMode enum. Users can now call .name on the result for recognised modes; unknown values fall back to plain int. - Pass self._current_mode (already coerced) to SetpointInfo in get_mode() instead of the raw local int. - Fix double Pa->m conversion: control.py already converted CONSTANT_PRESSURE and PROPORTIONAL_PRESSURE from Pascals to metres before storing in SetpointInfo, but get_display_value() and get_limits_display() then divided by 9806.65 again for all pressure modes, producing values ~10,000x too small. Extended the service-side conversion to cover all pressure modes for consistency, and removed the redundant division from both model methods. - Add scripts/verify_hardware.py: hardware regression-test script that connects via BLE, reads device info (firmware version), telemetry, and control mode, then prints a PASS/FAIL summary. Verified on device running BLE firmware V06.00.01. Verified on hardware: firmware 2811431V06.00.01.00001, control mode TEMPERATURE_RANGE_CONTROL reads correctly at 36.00 degC.
Drop support for bleak <3.x. The _make_bleak_client() version-detection
helper is replaced with a direct two-line conditional at the call site:
on Linux with an adapter specified, use bluez={'adapter': ...}; otherwise
use BleakClient(address) with no extra kwargs. Remove the inspect, and
importlib.metadata imports that were only needed for the shim.
Update pyproject.toml minimum bleak requirement from >=0.19.0 to >=3.0.0.
Remove test_make_bleak_client.py (tested the now-deleted helper).
Also remove 'strict firmware' language from send_extension_packets()
docstring. The sequential ordering with a 50ms gap is simply the correct
protocol behaviour, not a workaround for a particular firmware variant.
Ruff format / mypy: - Fix mypy call-overload errors in _coerce_control_mode: replace int(v: object) calls with an isinstance guard, eliminating the need for type: ignore comments entirely. - Reflow long lines in models.py and verify_hardware.py so ruff format --check passes. PR review threads: - Patch asyncio.sleep in test_telemetry_disconnect_guard.py so the five unit tests run in ~0ms instead of incurring real 50ms waits. Also shorten one assertion message that exceeded the 80-char limit. - Handle PackageNotFoundError from importlib.metadata.version() in verify_hardware.py (_pkg_ver helper falls back to 'unknown'). - Remove stale reference to _make_bleak_client in script docstring. - Add CHANGELOG.md Unreleased section documenting all fixes from this branch, including the breaking bleak>=3.0.0 minimum requirement.
- Add assert guards to narrow service attributes from `| None` to their concrete types after context manager entry - Replace hasattr guard with isinstance(ControlMode) for proper type narrowing of control_mode.name access
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the bugs surfaced by issue #24 (pump disconnecting after authentication on BLE firmware V06.00.01).
Fix 1: Extension packets sent in wrong order and in parallel
send_extension_packets()usedasyncio.TaskGroupto fire both packets concurrently, with EXTEND_2 submitted before EXTEND_1. The packet trace spec requires EXTEND_1 → EXTEND_2 with a 50ms gap. Sending them in parallel causes premature disconnection. Fixed by sending sequentially withawaitand a 50ms sleep.Fix 2: Bump minimum Bleak requirement to 3.0
Bleak 3.0 removed the
adapter=keyword. Rather than maintain a version-detection shim, the minimum requirement is nowbleak>=3.0.0. On Linux, an adapter name is passed viabluez={"adapter": ...}; on other platforms it is ignored. The_make_bleak_client()helper and its tests are removed.Fix 3: Disconnection guard in
read_once()Added
transport.is_connected()checks before the flow/pressure and temperature queries. If the pump disconnects mid-sequence the method now returns partial telemetry instead of raising aBleakError.Fix 4:
SetpointInfo.control_modetype (int→ControlMode | int)The field was typed as bare
int, causingAttributeErrorwhen callers accessed.name. Added afield_validatorthat coerces known values to theControlModeIntEnum; unknown values remain asint. The service now passes the already-coercedself._current_modeto the constructor.Fix 5: Double Pa→m conversion for pressure setpoints
ControlService.get_mode()converted pressure setpoints from Pascals to metres before storing inSetpointInfo, butget_display_value()andget_limits_display()then divided by 9806.65 again, producing values ~10,000× too small. Extended the service-side conversion to all pressure modes and removed the redundant division from both model methods.Changes
core/authentication.pyclient.pyservices/telemetry.pyread_once()(fix 3)models.pySetpointInfo.control_modetype + double-conversion fix (fixes 4, 5)services/control.pypyproject.tomlbleak>=3.0.0scripts/verify_hardware.pyHardware verification
Tested on a physical ALPHA HWR running BLE firmware
2811431V06.00.01.00001(the firmware reported in #24) using Bleak 3.0.2 and Python 3.14.5:control_mode.nameresolves toTEMPERATURE_RANGE_CONTROLget_display_value()returns36.00 °Cflow_m3handhead_mare populatedCloses #24