Prefer BLE device object resolution and bump to 0.1.12
Some checks failed
Deploy to GitHub Pages / build (push) Has been cancelled
Deploy to GitHub Pages / deploy (push) Has been cancelled

This commit is contained in:
paul2212
2026-03-07 15:07:53 +01:00
parent 822dbd35b2
commit 6b6d57bd77
6 changed files with 49 additions and 6 deletions

View File

@@ -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. 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 ## [0.1.11] - 2026-03-07
### Fixed ### Fixed

View File

@@ -90,6 +90,23 @@ async def find_printer() -> str:
raise PrinterNotFound("No Fichero/D11s printer found. Is it turned on?") 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 --- # --- Status ---
@@ -406,7 +423,7 @@ async def connect(
) from last_exc ) from last_exc
raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.") raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.")
else: else:
addr = address or await find_printer() target = await resolve_ble_target(address)
def _is_retryable_ble_error(exc: Exception) -> bool: def _is_retryable_ble_error(exc: Exception) -> bool:
msg = str(exc).lower() msg = str(exc).lower()
return any(token in msg for token in ("timeout", "timed out", "br-connection-timeout")) 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 last_exc: Exception | None = None
for attempt in range(1, BLE_CONNECT_RETRIES + 1): for attempt in range(1, BLE_CONNECT_RETRIES + 1):
try: try:
async with BleakClient(addr) as client: async with BleakClient(target) as client:
pc = PrinterClient(client) pc = PrinterClient(client)
await pc.start() await pc.start()
yield pc yield pc

View File

@@ -1,5 +1,9 @@
# Changelog # 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 ## 0.1.11
- Fixed unhandled BLE connect timeout (`asyncio.TimeoutError`) that previously caused HTTP 500 responses. - Fixed unhandled BLE connect timeout (`asyncio.TimeoutError`) that previously caused HTTP 500 responses.

View File

@@ -1,5 +1,5 @@
name: "Fichero Printer" name: "Fichero Printer"
version: "0.1.11" version: "0.1.12"
slug: "fichero_printer" slug: "fichero_printer"
description: "REST API for the Fichero D11s (AiYin) thermal label printer over Bluetooth" description: "REST API for the Fichero D11s (AiYin) thermal label printer over Bluetooth"
url: "https://git.leuschner.dev/Tobias/Fichero" url: "https://git.leuschner.dev/Tobias/Fichero"

View File

@@ -90,6 +90,23 @@ async def find_printer() -> str:
raise PrinterNotFound("No Fichero/D11s printer found. Is it turned on?") 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 --- # --- Status ---
@@ -406,7 +423,7 @@ async def connect(
) from last_exc ) from last_exc
raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.") raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.")
else: else:
addr = address or await find_printer() target = await resolve_ble_target(address)
def _is_retryable_ble_error(exc: Exception) -> bool: def _is_retryable_ble_error(exc: Exception) -> bool:
msg = str(exc).lower() msg = str(exc).lower()
return any(token in msg for token in ("timeout", "timed out", "br-connection-timeout")) 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 last_exc: Exception | None = None
for attempt in range(1, BLE_CONNECT_RETRIES + 1): for attempt in range(1, BLE_CONNECT_RETRIES + 1):
try: try:
async with BleakClient(addr) as client: async with BleakClient(target) as client:
pc = PrinterClient(client) pc = PrinterClient(client)
await pc.start() await pc.start()
yield pc yield pc

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "fichero-printer" name = "fichero-printer"
version = "0.1.11" version = "0.1.12"
description = "Fichero D11s thermal label printer - BLE CLI tool" description = "Fichero D11s thermal label printer - BLE CLI tool"
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = [ dependencies = [