diff --git a/CHANGELOG.md b/CHANGELOG.md index 185019c..e29a1f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project are documented in this file. The format is based on Keep a Changelog and this project uses Semantic Versioning. +## [0.1.12] - 2026-03-07 + +### Fixed +- BLE target resolution now prefers discovered Bleak device objects (instead of raw address strings), improving BlueZ LE connection handling on hosts that previously returned `br-connection-not-supported`. + ## [0.1.11] - 2026-03-07 ### Fixed diff --git a/fichero/printer.py b/fichero/printer.py index 821b492..a9fa089 100644 --- a/fichero/printer.py +++ b/fichero/printer.py @@ -90,6 +90,23 @@ async def find_printer() -> str: raise PrinterNotFound("No Fichero/D11s printer found. Is it turned on?") +async def resolve_ble_target(address: str | None = None): + """Resolve a BLE target as Bleak device object when possible. + + Passing a discovered device object to BleakClient helps BlueZ keep the + correct LE context for dual-mode environments. + """ + if address: + device = await BleakScanner.find_device_by_address(address, timeout=8.0) + return device or address + devices = await BleakScanner.discover(timeout=8) + for d in devices: + if d.name and any(d.name.startswith(p) for p in PRINTER_NAME_PREFIXES): + print(f" Found {d.name} at {d.address}") + return d + raise PrinterNotFound("No Fichero/D11s printer found. Is it turned on?") + + # --- Status --- @@ -406,7 +423,7 @@ async def connect( ) from last_exc raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.") else: - addr = address or await find_printer() + target = await resolve_ble_target(address) def _is_retryable_ble_error(exc: Exception) -> bool: msg = str(exc).lower() return any(token in msg for token in ("timeout", "timed out", "br-connection-timeout")) @@ -414,7 +431,7 @@ async def connect( last_exc: Exception | None = None for attempt in range(1, BLE_CONNECT_RETRIES + 1): try: - async with BleakClient(addr) as client: + async with BleakClient(target) as client: pc = PrinterClient(client) await pc.start() yield pc diff --git a/fichero_printer/CHANGELOG.md b/fichero_printer/CHANGELOG.md index c2c856b..874fed6 100644 --- a/fichero_printer/CHANGELOG.md +++ b/fichero_printer/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.12 + +- Improved BLE connection target resolution by preferring discovered BLE device objects over raw MAC strings to avoid BlueZ `br-connection-not-supported` on some hosts. + ## 0.1.11 - Fixed unhandled BLE connect timeout (`asyncio.TimeoutError`) that previously caused HTTP 500 responses. diff --git a/fichero_printer/config.yaml b/fichero_printer/config.yaml index 5286365..8189e2b 100644 --- a/fichero_printer/config.yaml +++ b/fichero_printer/config.yaml @@ -1,5 +1,5 @@ name: "Fichero Printer" -version: "0.1.11" +version: "0.1.12" slug: "fichero_printer" description: "REST API for the Fichero D11s (AiYin) thermal label printer over Bluetooth" url: "https://git.leuschner.dev/Tobias/Fichero" diff --git a/fichero_printer/fichero/printer.py b/fichero_printer/fichero/printer.py index 821b492..a9fa089 100644 --- a/fichero_printer/fichero/printer.py +++ b/fichero_printer/fichero/printer.py @@ -90,6 +90,23 @@ async def find_printer() -> str: raise PrinterNotFound("No Fichero/D11s printer found. Is it turned on?") +async def resolve_ble_target(address: str | None = None): + """Resolve a BLE target as Bleak device object when possible. + + Passing a discovered device object to BleakClient helps BlueZ keep the + correct LE context for dual-mode environments. + """ + if address: + device = await BleakScanner.find_device_by_address(address, timeout=8.0) + return device or address + devices = await BleakScanner.discover(timeout=8) + for d in devices: + if d.name and any(d.name.startswith(p) for p in PRINTER_NAME_PREFIXES): + print(f" Found {d.name} at {d.address}") + return d + raise PrinterNotFound("No Fichero/D11s printer found. Is it turned on?") + + # --- Status --- @@ -406,7 +423,7 @@ async def connect( ) from last_exc raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.") else: - addr = address or await find_printer() + target = await resolve_ble_target(address) def _is_retryable_ble_error(exc: Exception) -> bool: msg = str(exc).lower() return any(token in msg for token in ("timeout", "timed out", "br-connection-timeout")) @@ -414,7 +431,7 @@ async def connect( last_exc: Exception | None = None for attempt in range(1, BLE_CONNECT_RETRIES + 1): try: - async with BleakClient(addr) as client: + async with BleakClient(target) as client: pc = PrinterClient(client) await pc.start() yield pc diff --git a/pyproject.toml b/pyproject.toml index 804cde7..8ee907c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fichero-printer" -version = "0.1.11" +version = "0.1.12" description = "Fichero D11s thermal label printer - BLE CLI tool" requires-python = ">=3.10" dependencies = [