diff --git a/app.py b/app.py index 1eccdac..710b05a 100644 --- a/app.py +++ b/app.py @@ -3,14 +3,12 @@ import asyncio from functools import lru_cache from typing import Optional -from barcode import Code128 -from barcode.writer import ImageWriter from brother_ql.backends.helpers import send from brother_ql.conversion import convert from brother_ql.raster import BrotherQLRaster from fastapi import FastAPI, HTTPException from PIL import Image, ImageDraw, ImageFont -from pydantic import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field from pylibdmtx.pylibdmtx import encode TARGET_H = 106 @@ -29,39 +27,13 @@ class PrintRequest(BaseModel): product: str = Field(..., description="Product name") due_date: Optional[str] = Field(None, description="Due date from stock entry") - class Config: - # ignore any other fields Grocy might send - extra = "ignore" + model_config = ConfigDict(extra="ignore") def mm_for_px(px, dpi=DPI): return 25.4 * px / dpi -@lru_cache(maxsize=256) -def make_code128_exact( - grocycode: str, - xdim_px: int = 2, # narrower bars reduce overall width; ~0.25mm at 300dpi - dpi: int = DPI, -) -> Image.Image: - code = Code128(grocycode, writer=ImageWriter()) - pil = code.render( - writer_options={ - "write_text": False, - "dpi": dpi, - "module_height": mm_for_px(TARGET_H, dpi), # exact 106 px tall - "module_width": mm_for_px(xdim_px, dpi), # integer px/narrow bar - "quiet_zone": max(2.5, 10 * mm_for_px(xdim_px, dpi)), # generous quiet zone - } - ).convert( - "L" - ) # keep grayscale - # Height should already be 106 px; if writer adds 1px border, crop vertically. - if pil.height != TARGET_H: - pil = pil.resize((pil.width, TARGET_H), Image.NEAREST) - return pil - - @lru_cache(maxsize=256) def make_datamatrix_scaled(grocycode: str) -> Image.Image: dm = encode(grocycode.encode("utf-8")) @@ -148,7 +120,7 @@ def _ellipsize( def make_text_panel( - product: str, due_date: Optional[str], max_width: int = 360, padding: int = 4 + product: str, due_date: Optional[str], max_width: int = 480, padding: int = 4 ) -> Image.Image: """Render product (wrapped to 2 lines) + due_date in a fixed-height strip. @@ -294,19 +266,15 @@ def make_text_panel( return out -@app.post("/print", summary="Print Code128 + DataMatrix side-by-side") +@app.post("/print", summary="Print DataMatrix + text side-by-side") async def print_from_grocy(req: PrintRequest): try: loop = asyncio.get_running_loop() - code_img, dm_img = await asyncio.gather( - # use default x-dimension and DPI; only pass the payload - loop.run_in_executor(None, make_code128_exact, req.grocycode), - loop.run_in_executor(None, make_datamatrix_scaled, req.grocycode), - ) + dm_img = await loop.run_in_executor(None, make_datamatrix_scaled, req.grocycode) # Render a compact text panel; width-capped to keep label length reasonable text_panel = make_text_panel(req.product, req.due_date, max_width=360) # compose expects a list of images - label_img = compose_row([code_img, dm_img, text_panel], spacing=12) + label_img = compose_row([dm_img, text_panel], spacing=12) send_to_printer(label_img, req.printer_ip, req.model, req.label) except Exception: # optionally: log e with traceback and return a generic message @@ -315,5 +283,5 @@ async def print_from_grocy(req: PrintRequest): "status": "printed", "model": req.model, "label": req.label, - "layout": "code128 | datamatrix | text", + "layout": "datamatrix | text", }