Bluetooth-Devices
bleak-smlight
Blog
Docs
Changelog
Blog
Docs
Changelog
Overview
Branches
Benchmarks
Runs
Performance History
Latest Results
chore(pre-commit.ci): pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.13 → v0.16.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.13...v0.16.2) - [github.com/pre-commit/mirrors-mypy: v2.1.0 → v2.3.0](https://github.com/pre-commit/mirrors-mypy/compare/v2.1.0...v2.3.0)
pre-commit-ci-update-config
5 days ago
chore(pre-commit.ci): pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.13 → v0.16.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.13...v0.16.1) - [github.com/pre-commit/mirrors-mypy: v2.1.0 → v2.3.0](https://github.com/pre-commit/mirrors-mypy/compare/v2.1.0...v2.3.0)
pre-commit-ci-update-config
12 days ago
chore(ci): bump the github-actions group with 9 updates Bumps the github-actions group with 9 updates: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `6` | `7` | | [actions/setup-python](https://github.com/actions/setup-python) | `6` | `7` | | [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `8.1.0` | `9.0.0` | | [actions/cache](https://github.com/actions/cache) | `4` | `6` | | [codecov/codecov-action](https://github.com/codecov/codecov-action) | `6` | `7` | | [CodSpeedHQ/action](https://github.com/codspeedhq/action) | `4` | `5` | | [python-semantic-release/python-semantic-release](https://github.com/python-semantic-release/python-semantic-release) | `10.5.3` | `10.6.1` | | [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) | `1.14.0` | `1.14.2` | | [tiangolo/issue-manager](https://github.com/tiangolo/issue-manager) | `0.6.0` | `0.8.1` | Updates `actions/checkout` from 6 to 7 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) Updates `actions/setup-python` from 6 to 7 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v6...v7) Updates `astral-sh/setup-uv` from 8.1.0 to 9.0.0 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/v8.1.0...v9.0.0) Updates `actions/cache` from 4 to 6 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v6) Updates `codecov/codecov-action` from 6 to 7 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v6...v7) Updates `CodSpeedHQ/action` from 4 to 5 - [Release notes](https://github.com/codspeedhq/action/releases) - [Changelog](https://github.com/CodSpeedHQ/action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codspeedhq/action/compare/v4...v5) Updates `python-semantic-release/python-semantic-release` from 10.5.3 to 10.6.1 - [Release notes](https://github.com/python-semantic-release/python-semantic-release/releases) - [Changelog](https://github.com/python-semantic-release/python-semantic-release/blob/master/CHANGELOG.rst) - [Commits](https://github.com/python-semantic-release/python-semantic-release/compare/v10.5.3...v10.6.1) Updates `pypa/gh-action-pypi-publish` from 1.14.0 to 1.14.2 - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.14.0...v1.14.2) Updates `tiangolo/issue-manager` from 0.6.0 to 0.8.1 - [Release notes](https://github.com/tiangolo/issue-manager/releases) - [Changelog](https://github.com/tiangolo/issue-manager/blob/master/release-notes.md) - [Commits](https://github.com/tiangolo/issue-manager/compare/0.6.0...0.8.1) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-python dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: astral-sh/setup-uv dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/cache dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: codecov/codecov-action dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: CodSpeedHQ/action dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: python-semantic-release/python-semantic-release dependency-version: 10.6.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.14.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: tiangolo/issue-manager dependency-version: 0.8.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com>
dependabot/github_actions/github-actions-7b5d59c011
15 days ago
docs: document the proxy's unauthenticated trust boundary Add a Security considerations section to architecture.md and a root SECURITY.md covering the finding in #24: the SLZB proxy protocol has no authentication, so anything on the proxy's network segment can forge the handshake or inject advertisements, and habluetooth's best-RSSI resolution lets an injected advert override a real device's data. The wire-protocol fix belongs in pysmlight; this documents the trust boundary for users of this library.
bluetoothbot:koan/document-proxy-trust-boundary
17 days ago
docs: correct the claim that the proxy client reconnects The "Connection lifecycle and retries" section said `BleProxyClient` "owns its own connect/retry loop" and "retries with exponential backoff if the device is unreachable", which reads as reconnection being handled. It is not: `_connect_loop` breaks out of the retry loop on the first ACK and is never re-entered. Only `_ping_loop` survives, and it never checks that its pings are answered, so `_connected_evt` stays latched for the lifetime of the client. Verified empirically against pysmlight 0.5.0 by pointing a BleProxyClient at a local UDP server that ACKs pings, then killing it: connect_task is the same object as at start() : True connect_task.done() : True connect_task cancelled / exception : False / None _connected_evt still set after proxy death : True transport still not None : True ping_loop still running (pinging into the void): True Bringing the server back on the same port showed pings resuming (3 within 5s, so advertisements recover on their own) but zero SET_SCAN_MODE packets re-sent. The practical consequence is a silent divergence: after a device reboot the firmware is back at its default scan mode while the scanner still reports the mode that was pinned. AUTO escapes it only because `async_request_active_window` re-sends both the window request and the expiry restore on every cycle. Documents the boundary and the idempotent re-pin workaround. The real fix belongs upstream in pysmlight — clearing `_connected_evt` when pings go unanswered and resuming the connect loop — which would also let this library's handshake gate mean "reachable now" instead of "answered once".
bluetoothbot:koan/correct-reconnect-claims
17 days ago
docs: document the read path for non-connectable proxy advertisements The usage example, the example script, the README and the architecture page all told users to read proxied advertisements through bleak-shaped APIs. None of them work for this library. Verified against habluetooth 6.5.0 by feeding a synthetic advertisement through a registered SMLIGHTScanner and reading every candidate path: bleak.BleakScanner.discover() -> local adapter only HaBleakScannerWrapper.discover() -> {} HaBleakScannerWrapper.discovered_devices -> [] HaBleakScannerWrapper.detection_callback -> never fires HaBleakScannerWrapper.advertisement_data() -> never yields HaBleakScannerWrapper.find_device_by_address/name -> works get_manager().async_discovered_service_info(False) -> works BluetoothManager._discover_service_info override -> works (streaming) The scanner is registered non-connectable, so advertisements only ever enter habluetooth's all-scanners history. Every bleak-shaped read path reads the connectable history instead ("Bleak callbacks must get a connectable device", manager.py). They return empty rather than raising, which reads as a broken proxy. Both examples now subclass BluetoothManager to override _discover_service_info for the streaming case and poll async_discovered_service_info(False) for the snapshot case. Overriding it also silences habluetooth's startup warning that the bare BluetoothManager does not implement it. Also adds the missing async_setup() line to the advanced connect_scanner snippet, which raised RuntimeError from get_manager() as written. Two tests lock the connectable-gating so the docs cannot drift: if a future habluetooth widens its bleak surface to non-connectable scanners, the suite fails and the docs get revisited. Closes #21
bluetoothbot:koan/docs-proxy-read-path
17 days ago
docs: cut shipped justification prose from the scanner `scanner.py` had grown to 44% docstrings and comments, most of it rationale aimed at a reviewer rather than at a future maintainer, and some of it already stale on arrival: - `_proxy_ready`'s docstring enumerated the pysmlight versions its private read is valid for (0.5.0-0.5.3) — wrong the moment 0.5.4 ships — and described what `test_scanner_habluetooth.py` does to catch a rename. That test already documents itself; a shipped docstring is the wrong place to explain the test suite. - The stand-down rationale for a mode push meeting an open window was stated three times: in `async_set_scanning_mode`'s docstring, at the `_window_end` guard, and again in `_async_push_mode_when_connected`. - `async_request_active_window` listed its `False` conditions across two paragraphs and left a third (the zero-millisecond clamp) only in an inline comment. Each fact now appears once, at the point it is enforced: docstrings state the contract a caller can rely on, comments explain why the code is shaped the way it is. Net -35 lines, prose share 44% -> 38%. One fact is added rather than removed. `async_set_scanning_mode` reports the new mode before the radio has been told, while `async_request_active_window` explicitly refuses to; the asymmetry is deliberate (a deferred push still lands, an abandoned window does not) but was undocumented and reads like an inconsistency. No behaviour change.
bluetoothbot:koan/extend-in-flight-active-window
17 days ago
test: drive the handshake gate through a real ACK datagram The readiness gate the whole scan-mode stack depends on reads a private pysmlight attribute — BleProxyClient exposes no public connected signal — but every test stood in for the handshake by setting that attribute directly, on a MagicMock in most cases. So a rename or removal in pysmlight would keep CI green and surface in a consumer instead: AttributeError out of async_request_active_window (which the scheduler catches and logs every tick, silently killing active windows) and out of SMLIGHTConnectionManager.start(), which fails the manager outright. Route the integration module's handshake through pysmlight's own BleProxyProtocol decoder with a genuine two-byte ACK datagram, and add a test that asserts the gate from both sides of it. A rename now fails on the dependency bump, which is where a human is already looking. Verified _connected_evt is byte-identical across every published 0.5.x (0.5.0/0.5.1/0.5.2/0.5.3) and recorded that in the docstring, along with the fact that it latches: _connect_loop breaks on the first ACK and only stop() clears it, so it means "answered at least once", not "reachable now". Also refuse windows on the clamped timeout rather than on duration. The firmware field is whole milliseconds, so any 0 < duration < 0.001 truncated to a 0 ms window and spent the arm/restore packet pair the zero-check exists to avoid.
bluetoothbot:koan/extend-in-flight-active-window
17 days ago
Latest Branches
CodSpeed Performance Gauge
×2.4
chore(pre-commit.ci): pre-commit autoupdate
#10
5 days ago
18eff4b
pre-commit-ci-update-config
CodSpeed Performance Gauge
N/A
chore(ci): bump the github-actions group with 9 updates
#26
15 days ago
6bcb692
dependabot/github_actions/github-actions-7b5d59c011
CodSpeed Performance Gauge
×2.4
docs: document the proxy's unauthenticated trust boundary
#25
17 days ago
9d8de95
bluetoothbot:koan/document-proxy-trust-boundary
© 2026 CodSpeed Technology
Home
Terms
Privacy
Docs