Skip to content

Recover from stuck GATT operations and require HA 2026.6 (bleak 3) - #25

Merged
Nailik merged 3 commits into
mainfrom
fix/stuck-gatt-recovery
Oct 17, 2026
Merged

Nailik merged 3 commits into
mainfrom
fix/stuck-gatt-recovery

Conversation

@Nailik

@Nailik Nailik commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Summary

Recovers from GATT operations that BlueZ never completes, makes reads and writes more tolerant of a busy device, and moves the minimum Home Assistant version to 2026.6 (bleak 3).

Stuck GATT recovery

  • Every GATT read and write gets a 10 s limit (GATT_TIMEOUT). One that does not finish raises BlancoUnitGattTimeoutError instead of hanging forever.
  • BlueZ keeps an operation that was pending when the link dropped, so every later one on that characteristic also hangs. After a GATT timeout on a local BlueZ adapter the device is removed from BlueZ (unpair + disconnect) and connected once more (establish_validated_connection). Through an ESPHome Bluetooth proxy, which does not keep stuck operations, the connection is only closed and opened again, so the proxy keeps its bond (decided by BleakClient.backend_id). This is used for setup, the config flow and the runtime client.
  • Stale connections to the device are closed before connecting. The connection still bonds (pair=True), which BlueZ needs for service discovery.

Read / write robustness

  • Read errors while the device is still connected are retried, up to MAX_READ_ERRORS, instead of ending the read loop on the first one. Writes are followed by a short delay so the device can prepare its response.
  • Writes the device rejects as busy are retried up to 3 times with a short backoff, but only for the ATT errors that mean "not right now": PREPARE_QUEUE_FULL, UNLIKELY_ERROR, INSUFFICIENT_RESOURCE, PROCEDURE_ALREADY_IN_PROGRESS. Permanent rejections and lost links still fail on the first attempt. This retry only applies on a local BlueZ adapter: bleak-esphome reports GATT errors from a proxy as a plain BleakError, without an ATT error code.
  • A first response packet shorter than 3 bytes no longer raises IndexError when the packet count is read.
  • An incomplete response now reports attempts, empty_reads, duplicate_reads and read_errors in its TimeoutError.

CI

  • The workflows run for pull requests into any branch, not only main, so pull requests stacked on this one are checked too.

Logging

  • PINs, new PINs and WiFi passwords are redacted from debug logs (request bodies, config flow data, service calls, reload).

Home Assistant 2026.6 / bleak 3 (breaking)

  • MIN_HA_VERSION and hacs.json move to 2026.6.0, the first release that ships bleak 3. The busy-write retry relies on bleak 3's BleakGATTProtocolError.
  • manifest.json no longer requires bleak>=0.21.1. HA's bluetooth integration provides it.
  • Tests run against the minimum: homeassistant==2026.6.0 with the matching pytest-homeassistant-custom-component==0.13.336 on Python 3.14. The usb integration's test-only requirements follow HA (serialx, aioesphomeapi).
  • Snapshots are regenerated for HA 2026.6. Only the registry representation changed (aliases is a list, new object_id_base). Entity ids, unique ids, names, states and units are unchanged.

README

  • Requirements updated, release badge link fixed, troubleshooting sections for stuck connections and collecting debug logs. The stuck connection section points at rebooting the Home Assistant host (BlueZ) or restarting the proxy first, since the stuck operation lives there, not in the Blanco Unit.

Breaking change

Requires Home Assistant 2026.6.0 or newer.

Test plan

🤖 Generated with Claude Code

Nailik and others added 3 commits October 17, 2026 21:51
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Only remove a device from BlueZ after a GATT timeout. An ESPHome proxy
  does not keep stuck operations, so it keeps its bond and is only
  disconnected and connected again.
- Point the stuck connection troubleshooting at the host (BlueZ) or the
  proxy instead of the Blanco Unit.
- Run the workflows for pull requests into any branch, so pull requests
  stacked on another one are checked too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Nailik
Nailik merged commit 44efd66 into main Oct 17, 2026
3 checks passed
@Nailik
Nailik deleted the fix/stuck-gatt-recovery branch October 17, 2026 21:12
github-actions Bot pushed a commit to alexhaller/blanco_unit that referenced this pull request Nov 9, 2026
## [2.0.2](v2.0.1...v2.0.2) (2026-11-09)

### Bug Fixes

* harden the BLE connection against leaks and stuck GATT operations ([ef3c1fa](ef3c1fa)), closes [Nailik#24](https://github.com/alexhaller/blanco_unit/issues/24) [Nailik#25](https://github.com/alexhaller/blanco_unit/issues/25) [Nailik#27](https://github.com/alexhaller/blanco_unit/issues/27)
* point the documentation and issue links at this repository ([d423f16](d423f16)), closes [hacs/default#13647](hacs/default#13647)
* stop reporting the filter and CO2 capacity as a battery ([5311f42](5311f42))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant