From ffe8c13a1ce601dd19598d6ef4c2024541436ce1 Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Mon, 6 Jul 2026 12:36:14 +0200 Subject: [PATCH] fix(web): setup wizard no longer forces printers to bind to a barrier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Cilën barrierë shërben kjo pajisje?" is load-bearing for readers and cameras (which barrier a scan opens + inherited direction) but nothing consumes it on a printer — print routing is role + failoverRank (printer-routing.ts). The wizard applied the requirement to every non-controller device, so adding a printer demanded a meaningless relay pick that got stored as dead config. Printers are now exempt: no requirement, the binding panel is hidden, the binding is not persisted (a stale pre-fix one drops off on next edit), and the device list shows the printer's ROLE instead of a bogus amber "unbound". Server never validated it — no API change. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V --- apps/web/src/SetupWizard.tsx | 25 +++++++++++++++++---- wiki/log.md | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/apps/web/src/SetupWizard.tsx b/apps/web/src/SetupWizard.tsx index 34e5ed0..199678c 100644 --- a/apps/web/src/SetupWizard.tsx +++ b/apps/web/src/SetupWizard.tsx @@ -463,6 +463,16 @@ function DeviceSummary({ assignment, controllers }: { assignment: Assignment; co ); } + // Printers don't bind to a barrier (routing is role + failoverRank) — show the + // role instead of a bogus "unbound" warning. + if (assignment.category === "printer") { + const role = typeof cfg.role === "string" ? cfg.role : null; + return role ? ( + + {t(role === "booth-receipt" ? "devices.role.booth" : "devices.role.lane")} + + ) : null; + } // Bound device: show controller + relay it points at, with inherited direction. const controllerId = typeof cfg.controllerId === "string" ? cfg.controllerId : null; const relay = typeof cfg.relay === "number" ? cfg.relay : null; @@ -686,7 +696,11 @@ function DeviceForm({ ...(i.role === "presence" && i.activeLow ? { activeLow: true } : {}), ...(i.role === "button" && i.cooldownSec ? { cooldownSec: i.cooldownSec } : {}), })); - } else if (controllerId && boundRelay !== "") { + } else if (!isPrinter && controllerId && boundRelay !== "") { + // Readers/cameras bind to a controller relay (which barrier a scan opens + + // inherited direction). Printers do NOT — routing is role+failoverRank only, + // so no binding is emitted (and a stale one saved before 2026-07-06 drops + // off on the next edit). out.controllerId = controllerId; out.relay = boundRelay; } @@ -759,7 +773,9 @@ function DeviceForm({ if (!selected) return; // Bound devices must point at a controller relay (binding is optional in the // model with a fallback, but the wizard guides the admin to bind explicitly). - if (!isController && (!controllerId || boundRelay === "")) { + // Printers are exempt: nothing consumes a printer's binding — their routing is + // role + failoverRank (see printer-routing.ts). + if (!isController && !isPrinter && (!controllerId || boundRelay === "")) { setSaveError("Pick the controller and relay this device sits at."); return; } @@ -959,8 +975,9 @@ function DeviceForm({ /> )} - {/* BOUND device: which controller + relay it sits at. */} - {!isController && ( + {/* BOUND device: which controller + relay it sits at. Not printers — + nothing consumes a printer binding (role+rank routes print jobs). */} + {!isController && !isPrinter && ( 0 — the look-closer signal), cash/card stacked revenue +bars, peak-occupancy KPI, richer CSV (cash/card/occupancy_end). Server: reports.ts aggregation + +Intl formatter cached per tz; db package re-exports lt/gt. 5 new tests (295 server green). + +## [2026-07-06] update | USB printer truncation fixed (NONBLOCK partial write) — ICS XP-K200L + +First on-hardware USB print test failed exactly as the transport bug predicts: text printed, +barcode + cut missing (both live in the dropped tail). sendRawUsb did ONE write() on an O_NONBLOCK +usblp fd and never checked bytesWritten — anything past the printer's ~8 KB USB buffer was +silently discarded; TCP was immune. Fixed with writeAllUsb (4 KB chunks, partial-write +continuation, EAGAIN retry, deadline with N/M diagnostic) + 4 fake-handle tests. Also verified on +hardware (10.0.10.11): the ICS XP-K200L serves NO /prn_stat.htm → on network use the cashino +driver (reachability-only), not rongta, or monitoring calls a working printer offline. Details on +[[printer-usb-transport]]. + +## [2026-07-06] update | Driver rename: "cashino" → "escpos" (generic ESC/POS printer) + +The ICS XP-K200L exposed that the reachability-only clone driver carried its first unit's vendor +name — "cashino" in the setup UI was misleading for every other clone. Renamed properly: +printer-cashino.ts → printer-generic.ts, id "cashino" → "escpos", label "Generic ESC/POS 80mm +printer (Cashino, ICS/Xprinter…)". Stored rows rewritten by migration 0023; the registry keeps a +PERMANENT cashino→escpos alias so restored pre-rename backups still resolve. Prose mentions of the +Cashino as hardware stay (it's a real printer). Driver guidance for the XP-K200L: escpos on both +transports (it serves no /prn_stat.htm — verified; rongta would false-flag it). See +[[printer-usb-transport]], [[printer-status-monitoring]]. + +## [2026-07-06] update | Setup wizard: printers no longer forced to bind to a barrier relay + +Operator hit the wizard's blanket "pick the controller and relay this device sits at" gate while +adding the ICS printer. The binding (controllerId+relay → which barrier a scan opens + inherited +direction) is load-bearing for READERS and CAMERAS only; nothing consumes it on a printer — +printer routing is role + failoverRank (printer-routing.ts). Wizard now skips the requirement, +hides the "Cilën barrierë shërben kjo pajisje?" panel, and stops persisting the binding for +printers (a stale pre-fix binding drops off on next edit); the device list shows the printer's +ROLE instead of a bogus amber "unbound". Server never required it (no validation change).