From a23c33e29384b0903fd49432a7282ebe5bbab456 Mon Sep 17 00:00:00 2001 From: paul2212 Date: Mon, 16 Mar 2026 19:01:14 +0100 Subject: [PATCH] 0.1.30 --- fichero_printer/CHANGELOG.md | 9 ++++ fichero_printer/fichero/api.py | 83 ++++++++++++++++++++++++++++-- fichero_printer/fichero/printer.py | 10 ++-- 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/fichero_printer/CHANGELOG.md b/fichero_printer/CHANGELOG.md index 9e18f48..a52b579 100644 --- a/fichero_printer/CHANGELOG.md +++ b/fichero_printer/CHANGELOG.md @@ -4,6 +4,15 @@ 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.30] - 2026-03-16 + +### Fixed +- **BLE Connection**: Restored fallback to raw address string when BLE scan fails to find the specific device. This fixes connectivity for devices that are reachable but not advertising (e.g. during rapid reconnection or BlueZ cache issues), resolving "BLE device not found during scan" errors. + +### Added +- **Web UI**: Restored support for the modern, responsive web interface. If the build artifacts are present in `fichero/web`, they will be served by default. +- **Web UI**: Added a `?legacy=true` query parameter to the root URL to force the simple server-side rendered UI, which includes the new debug scan tool. ## [0.1.29] - 2026-03-16 ### Fixed diff --git a/fichero_printer/fichero/api.py b/fichero_printer/fichero/api.py index 55018e9..fbedf20 100644 --- a/fichero_printer/fichero/api.py +++ b/fichero_printer/fichero/api.py @@ -27,8 +27,10 @@ from typing import Annotated from fastapi import FastAPI, File, Form, HTTPException, UploadFile from fastapi.middleware.cors import CORSMiddleware from fastapi.openapi.docs import get_swagger_ui_html -from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.responses import HTMLResponse +from fastapi.staticfiles import StaticFiles from PIL import Image +from bleak import BleakScanner from fichero.cli import DOTS_PER_MM, do_print from fichero.imaging import text_to_image @@ -75,7 +77,7 @@ async def lifespan(app: FastAPI): # noqa: ARG001 app = FastAPI( title="Fichero Printer API", description="REST API for the Fichero D11s (AiYin) thermal label printer.", - version = "0.1.29", + version = "0.1.30", lifespan=lifespan, docs_url=None, redoc_url=None, @@ -88,6 +90,13 @@ app.add_middleware( allow_headers=["*"], ) +# Serve static files for the modern web UI (if built and present in 'web' dir) +_WEB_ROOT = Path(__file__).parent / "web" +if _WEB_ROOT.exists(): + # Typical SPA assets folder + if (_WEB_ROOT / "assets").exists(): + app.mount("/assets", StaticFiles(directory=_WEB_ROOT / "assets"), name="assets") + def _address(address: str | None) -> str | None: """Return the effective BLE address (request value overrides env default).""" @@ -105,21 +114,67 @@ def _ui_html() -> str: return "

Error: index.html not found

" # Simple substitution for initial values - return ( + template = ( template.replace("{default_address}", default_address) .replace("{ble_selected}", " selected" if default_transport == "ble" else "") .replace("{classic_selected}", " selected" if default_transport == "classic" else "") .replace("{default_channel}", str(_DEFAULT_CHANNEL)) ) + # Inject debug scan section and script + scan_html = """ +
+

Debug Scan

+

Scans for all nearby BLE devices to help with debugging connection issues.

+ +

+        
+ """ + scan_script = r''' + + ''' + # Inject before the closing tag + if "" in template: + parts = template.split("", 1) + template = parts[0] + scan_html + scan_script + "" + parts[1] + else: + # Fallback if no body tag + template += scan_html + scan_script + + return template + # --------------------------------------------------------------------------- # Endpoints # --------------------------------------------------------------------------- @app.get("/", include_in_schema=False, response_class=HTMLResponse) -async def root(): +async def root(legacy: bool = False): """Serve a compact printer UI for Home Assistant.""" + # Prefer the modern SPA if available, unless ?legacy=true is used + if not legacy and (_WEB_ROOT / "index.html").exists(): + return HTMLResponse((_WEB_ROOT / "index.html").read_text(encoding="utf-8")) return HTMLResponse(_ui_html()) @@ -190,6 +245,26 @@ async def get_info( return info +@app.get( + "/scan", + summary="Scan for BLE devices", + response_description="List of discovered BLE devices", +) +async def scan_devices(): + """Scan for nearby BLE devices for 10 seconds for debugging.""" + try: + devices = await BleakScanner.discover(timeout=10.0) + return [ + {"address": d.address, "name": d.name or "N/A", "rssi": d.rssi} + for d in devices + ] + except Exception as exc: + # This provides more debug info to the user if scanning fails + raise HTTPException( + status_code=500, detail=f"An error occurred during BLE scanning: {exc}" + ) + + @app.post( "/pair", summary="Pair and trust a Bluetooth device", diff --git a/fichero_printer/fichero/printer.py b/fichero_printer/fichero/printer.py index 8a4c69e..0bee4fb 100644 --- a/fichero_printer/fichero/printer.py +++ b/fichero_printer/fichero/printer.py @@ -101,17 +101,13 @@ async def resolve_ble_target(address: str | None = None): device = await BleakScanner.find_device_by_address(address, timeout=8.0) if device is not None: return device - # Fallback to active scan/match before giving up; do not fall back to - # raw address because BlueZ may then attempt BR/EDR and fail with - # br-connection-not-supported. + # Fallback to active scan/match before giving up. devices = await BleakScanner.discover(timeout=8) for d in devices: if d.address and d.address.lower() == address.lower(): return d - raise PrinterNotFound( - f"BLE device {address} not found during scan. " - "Ensure printer is on, awake, and in range." - ) + print(f" Warning: BLE device {address} not found in scan. Falling back to direct address connection.") + return 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):