-
Notifications
You must be signed in to change notification settings - Fork 1
fix: authentication extension packet ordering and Bleak 3.x compatibility #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
eman
wants to merge
12
commits into
main
Choose a base branch
from
fix/bleak3-compat
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
8b4ba1c
fix: remove invalid top-level version key from mkdocs.yml
eman b4d245d
Merge branch 'main' of https://github.com/eman/alpha-hwr
eman 77d0efe
fix: Bleak 3.x compatibility and disconnection guard in read_once
eman ee72412
fix: send extension packets sequentially in correct order (EXTEND_1 t…
eman e6f59ea
fix: address PR review comments
eman ae20fdd
Merge branch 'main' into fix/bleak3-compat
2cf3d7b
fix: SetpointInfo.control_mode type and double Pa->m conversion
558a339
fix: require bleak>=3.0.0 and remove compat shim
2c154eb
fix: CI failures and address all PR review comments
17e3f94
fix: resolve basedpyright errors in verify_hardware.py
07d64ee
update changelog for release
eman bca92a3
docs: clarify pairing requirements
eman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| Hardware verification script for issue #24 regression testing. | ||
|
|
||
| Connects to a local ALPHA HWR pump via BLE, reads telemetry and device | ||
| information to verify the fix for: | ||
| - Sequential extension packet ordering (EXTEND_1 then EXTEND_2) | ||
| - Bleak 3.x adapter handling | ||
| - Disconnection guard in read_once (is_connected checks) | ||
|
|
||
| Usage: | ||
| # Auto-discover pump: | ||
| .venv/bin/python scripts/verify_hardware.py | ||
|
|
||
| # Specify address directly: | ||
| .venv/bin/python scripts/verify_hardware.py --address <BLE_ADDRESS> | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import asyncio | ||
| import logging | ||
| import sys | ||
| from importlib.metadata import PackageNotFoundError | ||
| from importlib.metadata import version as pkg_version | ||
|
|
||
| import typer | ||
|
|
||
| from alpha_hwr.client import AlphaHWRClient | ||
|
|
||
| app = typer.Typer(add_completion=False) | ||
|
|
||
|
|
||
| def _pkg_ver(name: str) -> str: | ||
| try: | ||
| return pkg_version(name) | ||
| except PackageNotFoundError: | ||
| return "unknown" | ||
|
|
||
|
|
||
| def _setup_logging(verbose: bool) -> None: | ||
| level = logging.DEBUG if verbose else logging.INFO | ||
| logging.basicConfig( | ||
| level=level, | ||
| format="%(asctime)s %(name)s %(levelname)s: %(message)s", | ||
| ) | ||
| if not verbose: | ||
| logging.getLogger("bleak").setLevel(logging.WARNING) | ||
|
|
||
|
|
||
| async def _run(address: str | None, verbose: bool) -> int: | ||
| _setup_logging(verbose) | ||
|
|
||
| print( | ||
| f"alpha-hwr {_pkg_ver('alpha-hwr')}" | ||
| f" | bleak {_pkg_ver('bleak')}" | ||
| f" | Python {sys.version.split()[0]}" | ||
| ) | ||
| print() | ||
|
|
||
| # ------------------------------------------------------------------ # | ||
| # Discovery # | ||
| # ------------------------------------------------------------------ # | ||
| if address is None: | ||
| print("Scanning for ALPHA HWR pumps (10 s)...") | ||
| devices = await AlphaHWRClient.discover(timeout=10.0) | ||
| if not devices: | ||
| print( | ||
| "ERROR: No ALPHA HWR pumps found." | ||
| " Is the device powered on and in range?" | ||
| ) | ||
| return 1 | ||
| print(f"Found {len(devices)} pump(s):") | ||
| for d in devices: | ||
| print(f" - {d.name or 'Unknown'} [{d.address}]") | ||
| address = devices[0].address | ||
| print(f"\nUsing first device: {address}") | ||
| else: | ||
| print(f"Using specified address: {address}") | ||
|
|
||
| print() | ||
|
|
||
| # ------------------------------------------------------------------ # | ||
| # Connection + verification # | ||
| # ------------------------------------------------------------------ # | ||
| passed: list[str] = [] | ||
| failed: list[str] = [] | ||
|
|
||
| try: | ||
| async with AlphaHWRClient(address) as client: | ||
| # Services are guaranteed non-None inside the context manager. | ||
| assert client.device_info is not None | ||
| assert client.telemetry is not None | ||
| assert client.control is not None | ||
|
|
||
| print("Connected and authenticated successfully.") | ||
| passed.append("Connection and authentication") | ||
|
|
||
| # -- Firmware / device info --------------------------------- # | ||
| print("\n--- Device Information ---") | ||
| try: | ||
| info = await client.device_info.read_info() | ||
| if info: | ||
| print(f" Product name : {info.product_name or 'N/A'}") | ||
| print(f" BLE firmware : {info.ble_version or 'N/A'}") | ||
| if info.ble_version: | ||
| passed.append(f"Firmware detected: {info.ble_version}") | ||
| else: | ||
| failed.append("BLE firmware version not returned") | ||
| else: | ||
| print(" (no device info returned)") | ||
| failed.append("read_info returned None") | ||
| except Exception as exc: | ||
| print(f" ERROR reading device info: {exc}") | ||
| failed.append(f"Device info error: {exc}") | ||
|
|
||
| # -- Telemetry ---------------------------------------------- # | ||
| print("\n--- Telemetry ---") | ||
| try: | ||
| telemetry = await client.telemetry.read_once() | ||
| if telemetry is not None: | ||
| print(f" Flow : {telemetry.flow_m3h} m3/h") | ||
| print(f" Head : {telemetry.head_m} m") | ||
| print(f" Power : {telemetry.power_w} W") | ||
| if telemetry.flow_m3h is not None: | ||
| passed.append("Telemetry flow_m3h populated") | ||
| else: | ||
| failed.append("flow_m3h is None (possible regression)") | ||
| if telemetry.head_m is not None: | ||
| passed.append("Telemetry head_m populated") | ||
| else: | ||
| failed.append("head_m is None (possible regression)") | ||
| else: | ||
| print(" Telemetry returned None") | ||
| failed.append("read_once returned None") | ||
| except Exception as exc: | ||
| print(f" ERROR reading telemetry: {exc}") | ||
| failed.append(f"Telemetry error: {exc}") | ||
|
|
||
| # -- Control mode ------------------------------------------- # | ||
| print("\n--- Control Mode ---") | ||
| try: | ||
| mode_info = await client.control.get_mode() | ||
| if mode_info: | ||
| from alpha_hwr.constants import ControlMode | ||
|
|
||
| if isinstance(mode_info.control_mode, ControlMode): | ||
| mode_name = mode_info.control_mode.name | ||
| else: | ||
| mode_name = f"unknown({mode_info.control_mode})" | ||
| print(f" Mode : {mode_name}") | ||
| value, unit = mode_info.get_display_value() | ||
| print(f" Setpoint: {value:.2f} {unit}") | ||
| passed.append("Control mode read") | ||
| else: | ||
| print(" (no control mode data)") | ||
| failed.append("get_mode returned None") | ||
| except Exception as exc: | ||
| print(f" ERROR reading control mode: {exc}") | ||
| failed.append(f"Control mode error: {exc}") | ||
|
|
||
| except Exception as exc: | ||
| print(f"\nFATAL: Could not connect/authenticate: {exc}") | ||
| failed.append(f"Connection failed: {exc}") | ||
| logging.exception("Connection error") | ||
|
|
||
| # ------------------------------------------------------------------ # | ||
| # Summary # | ||
| # ------------------------------------------------------------------ # | ||
| print("\n" + "=" * 50) | ||
| print("VERIFICATION SUMMARY") | ||
| print("=" * 50) | ||
| for item in passed: | ||
| print(f" PASS {item}") | ||
| for item in failed: | ||
| print(f" FAIL {item}") | ||
|
|
||
| regression_keywords = [ | ||
| "Service Discovery", | ||
| "BleakError", | ||
| "regression", | ||
| "None", | ||
| ] | ||
| regressions = [ | ||
| f for f in failed if any(k in f for k in regression_keywords) | ||
| ] | ||
| if regressions: | ||
| print("\nPotential regressions detected (see FAIL lines above).") | ||
| return 2 | ||
|
|
||
| if failed: | ||
| print("\nSome checks failed - review output above.") | ||
| return 1 | ||
|
|
||
| print("\nAll checks passed. No regressions detected.") | ||
| return 0 | ||
|
|
||
|
|
||
| @app.command() | ||
| def main( | ||
| address: str | None = typer.Option( | ||
| None, | ||
| "--address", | ||
| "-a", | ||
| help="BLE device address. Auto-discovers if omitted.", | ||
| ), | ||
| verbose: bool = typer.Option( | ||
| False, "--verbose", "-v", help="Enable debug logging." | ||
| ), | ||
| ) -> None: | ||
| """Verify hardware connectivity and firmware for issue #24 regression.""" | ||
| exit_code = asyncio.run(_run(address, verbose)) | ||
| raise SystemExit(exit_code) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| app() |
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.