"""Print on the LX-D02 thermal printer from the PC. Usage: python lxd02.py "text" [more lines...] python lxd02.py picture.jpg [more pictures...] (stretched to the tape width) Set LXD02_MAC=AA:BB:CC:DD:EE:FF to skip the Bluetooth search. """ import asyncio import os import sys from pathlib import Path from bleak import BleakClient, BleakScanner from PIL import Image, ImageOps sys.path.insert(0, str(Path(__file__).resolve().parent / "custom_components" / "lxd02")) from printer import WIDTH, print_payloads, render, to_payloads # noqa: E402 PICTURES = {".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp", ".tif", ".tiff"} def picture(path): """Any picture -> 1-bit image WIDTH wide. Landscape ones are turned to run along the tape, so they come out bigger.""" img = ImageOps.exif_transpose(Image.open(path)).convert("RGBA") white = Image.new("RGBA", img.size, "white") img = Image.alpha_composite(white, img).convert("L") # transparent -> white, not black if img.width > img.height: img = img.rotate(90, expand=True) img = img.resize((WIDTH, max(2, round(img.height * WIDTH / img.width))), Image.LANCZOS) return ImageOps.autocontrast(img, cutoff=1).convert("1") # dithering for halftones async def send(img, mac=None): mac = mac or os.environ.get("LXD02_MAC") if not mac: device = await BleakScanner.find_device_by_name("LX-D02", timeout=20) if device is None: raise RuntimeError("LX-D02 not found: is it on and not connected to a phone?") mac = device.address for attempt in range(3): # Windows often fails the first connect to a sleeping printer try: client = BleakClient(mac, timeout=20) await client.connect() break except Exception: if attempt == 2: raise await asyncio.sleep(2) try: await print_payloads(client, mac, to_payloads(img)) finally: await client.disconnect() def main(args): if args and all(Path(a).suffix.lower() in PICTURES and Path(a).is_file() for a in args): for a in args: print("printing", a) asyncio.run(send(picture(a))) else: asyncio.run(send(render("\n".join(args) or "Hello, LX-D02!\nПривет с ПК"))) print("ok") if __name__ == "__main__": try: main(sys.argv[1:]) except Exception as err: # started from "Send to": keep the window open to show why print("ERROR:", err) if sys.stdin and sys.stdin.isatty(): input("Press Enter to close") sys.exit(1)