From 16886bfa2159f83680a0640476ba727f3f45891d Mon Sep 17 00:00:00 2001 From: paul2212 Date: Mon, 16 Mar 2026 10:16:12 +0100 Subject: [PATCH] add files --- README.md | 12 ++++++++++++ fichero/printer.py | 32 +++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f8ff60e..a06ab02 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,18 @@ asyncio.run(main()) The package exports `PrinterClient`, `connect`, `PrinterError`, `PrinterNotFound`, `PrinterTimeout`, `PrinterNotReady`, and `PrinterStatus`. +## Troubleshooting + +### Classic Bluetooth: [Errno 12] Out of memory + +If you encounter `[Errno 12] Out of memory` failures on Classic Bluetooth connections, it typically implies a stale state in the BlueZ stack or the printer's radio. As of v0.1.17, the library automatically falls back to a BLE connection when this specific error occurs. + +If you wish to resolve the underlying Classic Bluetooth issue, these steps can help: + +- **Power cycle the printer**: This clears the printer's radio state and is often the only fix if the device is rejecting RFCOMM. +- **Verify Pairing**: Classic Bluetooth (RFCOMM) requires the device to be paired and trusted in the OS. You can use the "Pair Device" or "Unpair Device" buttons in the Home Assistant add-on's web UI, or run `bluetoothctl pair ` and `bluetoothctl trust ` (or `bluetoothctl remove `) on the host. Pairing is not required for BLE. +- **Restart Bluetooth**: `systemctl restart bluetooth` on the host can clear stuck socket handles. + ## TODO - [ ] Emoji support in text labels. The default Pillow font has no emoji glyphs, so they render as squares. Needs two-pass rendering: split text into emoji/non-emoji segments, render emoji with Apple Color Emoji (macOS) or Noto Color Emoji (Linux) using `embedded_color=True`, then composite onto the label. diff --git a/fichero/printer.py b/fichero/printer.py index 7d44601..3ae4f18 100644 --- a/fichero/printer.py +++ b/fichero/printer.py @@ -8,6 +8,7 @@ Device class: AiYinNormalDevice (LuckPrinter SDK) import asyncio import sys +import errno from collections.abc import AsyncGenerator from contextlib import asynccontextmanager @@ -427,14 +428,31 @@ async def connect( yield pc return except (PrinterError, PrinterTimeout) as exc: + # On Linux, a stale BlueZ device state can cause RFCOMM connect() + # to fail with [Errno 12] Out of memory. This is a known quirk. + # We treat this specific error as a signal to fall back to BLE. + if isinstance(exc.__cause__, OSError) and exc.__cause__.errno == errno.ENOMEM: + print( + "Classic Bluetooth connection failed with [Errno 12] Out of memory. " + "Falling back to BLE connection." + ) + classic = False # Modify flag to trigger BLE path below + last_exc = exc + break last_exc = exc - if last_exc is not None: - raise PrinterError( - f"Classic Bluetooth connection failed for '{address}'. " - f"Tried channels: {channels}. Last error: {last_exc}" - ) from last_exc - raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.") - else: + + # If the 'classic' flag is still true, it means the loop completed without + # hitting the ENOMEM fallback case, so all classic attempts failed. + if classic: + if last_exc is not None: + raise PrinterError( + f"Classic Bluetooth connection failed for '{address}'. " + f"Tried channels: {channels}. Last error: {last_exc}" + ) from last_exc + raise PrinterError(f"Classic Bluetooth connection failed for '{address}'.") + + # If classic=False initially, or if it was set to False for the ENOMEM fallback: + if not classic: target = await resolve_ble_target(address) def _is_retryable_ble_error(exc: Exception) -> bool: msg = str(exc).lower()