"""Status image functions — loading, alpha compositing, and WS transmission.""" import json import logging from pathlib import Path from PIL import Image log = logging.getLogger(__name__) IMG_DIR = Path(__file__).parent / "assets" / "img" STATUS_IMG_SIZE = 120 MONOCHROME_THRESHOLD = 140 def load_status_image(path: Path) -> bytes: """Load a PNG, convert to 1-bit 120x120 monochrome bitmap (MSB-first, black=1). Transparent pixels are composited onto white so they don't render as black. """ img = Image.open(path) # Composite transparent pixels onto white background if img.mode in ("RGBA", "LA", "PA"): bg = Image.new("RGBA", img.size, (255, 255, 255, 255)) bg.paste(img, mask=img.split()[-1]) img = bg img = img.convert("L") # Resize to fit within 120x120, preserving aspect ratio img.thumbnail((STATUS_IMG_SIZE, STATUS_IMG_SIZE), Image.LANCZOS) # Paste centered onto white canvas canvas = Image.new("L", (STATUS_IMG_SIZE, STATUS_IMG_SIZE), 255) x_off = (STATUS_IMG_SIZE - img.width) // 2 y_off = (STATUS_IMG_SIZE - img.height) // 2 canvas.paste(img, (x_off, y_off)) # Threshold to 1-bit: black (< MONOCHROME_THRESHOLD) -> 1, white -> 0 bw = canvas.point(lambda p: 1 if p < MONOCHROME_THRESHOLD else 0, "1") raw = bw.tobytes() log.info("Status image loaded: %s -> %d bytes", path.name, len(raw)) return raw async def send_status_image(ws, img_bytes: bytes): """Send a status image over the WebSocket (text header + binary payload).""" header = json.dumps({"type": "status_image", "width": STATUS_IMG_SIZE, "height": STATUS_IMG_SIZE}) await ws.send(header) await ws.send(img_bytes) log.info("Sent status image (%d bytes)", len(img_bytes))