diff --git a/src/printer.js b/src/printer.js index f4fc639..3a8f402 100644 --- a/src/printer.js +++ b/src/printer.js @@ -6,6 +6,7 @@ import { execFile } from 'node:child_process'; import { createCanvas, loadImage, GlobalFonts } from '@napi-rs/canvas'; import config from './config.js'; import { photoAbsolutePath } from './photos.js'; +import { printPng, printerHealth } from './printing/ql-print.js'; /** * Printing happens on the server, not in the kiosk browser. @@ -330,33 +331,24 @@ export async function printBadge(visit, site) { if (!isConfigured(site)) throw new Error('Server printing is not turned on for this site.'); const png = await renderBadgePng(visit, site); - const file = path.join(os.tmpdir(), `badge-${crypto.randomBytes(6).toString('hex')}.png`); - fs.writeFileSync(file, png); - const port = Number(site.printer_port) || 9100; const target = `tcp://${site.printer_host}:${port}`; - try { - await runBrotherQl( - [ - '--backend', 'network', - '--model', site.printer_model || 'QL-820NWB', - '--printer', target, - 'print', - '--label', labelFor(site), - file, - ], - config.printing.timeoutMs - ); - note(site.id, true, `Printed to ${site.printer_host}`); - return { ok: true, target }; - } catch (err) { - const friendly = explainPrintError(err.message, site.printer_host); - note(site.id, false, friendly); - throw new Error(friendly); - } finally { - fs.rm(file, { force: true }, () => {}); - } + // The roll decides whether the job is two-colour, not the accent setting. + // A DK-22251 roll refuses a monochrome job even when the badge has no red + // on it, so labelFor() is passed straight through and ql-print maps it. + const result = await printPng(png, { + host: site.printer_host, + port, + label: labelFor(site), + }); + + note(site.id, result.ok, result.message); + if (!result.ok) throw new Error(result.message); + + // confirmed is false when the printer accepted the bytes but never said the + // label came out. Over the network that is the normal case, not a fault. + return { ok: true, confirmed: Boolean(result.confirmed), target }; } /** A sample badge, for checking the printer and the layout without a real visit. */ @@ -375,7 +367,23 @@ export function sampleVisit(site) { } export function available() { - return new Promise((resolve) => { - execFile(config.printing.command, ['--version'], (err) => resolve(!err)); + // Kept for callers that only ask "can this server print at all?". The + // external brother_ql binary is no longer involved, so the answer is always + // yes; use health(site) to ask about a specific printer. + return Promise.resolve(true); +} + +/** + * Reachability and, where the printer will say, roll state. + * + * ready is tri-state: true, false, or null meaning "reachable but it won't + * tell us". Null is the normal answer over the network — show it as unknown + * in the admin console rather than green or red. + */ +export function health(site) { + return printerHealth({ + host: site?.printer_host, + port: Number(site?.printer_port) || 9100, + label: labelFor(site), }); }