printhive/lib/printers.js
dlawler489 e98edaa9ed Add a Printers page for any number of printers, and show spools' RFID tags
Printers move out of Settings into their own table and page: each has an
optional nickname (the model is shown when blank), a model from the Bambu
lineup with a line drawing that follows the selection, an AMS type
suggested from the model, and its address, serial and access code. Access
codes are never returned to the browser. The printer set up in Settings is
migrated automatically and its settings rows removed.

Background sync covers every printer. Grams left are filled in only from an
AMS that measures (AMS, AMS 2 Pro, AMS HT), never from an AMS lite.

Spools now show their RFID tag ID, product code and filament code, and say
which printer they're loaded in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:00:43 +10:00

90 lines
3.3 KiB
JavaScript

// The printers you've added.
//
// Access codes are stored here but never handed back to the browser: every
// read returns `codeSet` instead, and saving with a blank code keeps the one
// already stored.
const db = require('./db');
// Which AMS types can say how much filament is left. An AMS lite only knows
// full or empty, so its reading is never used as a weight.
const AMS_TYPES = { none: false, ams_lite: false, ams: true, ams_2_pro: true, ams_ht: true };
const clean = (value, max) => {
const s = String(value ?? '').trim();
return s ? s.slice(0, max) : null;
};
function publicPrinter(row) {
if (!row) return null;
const { access_code: accessCode, ...rest } = row;
return { ...rest, codeSet: Boolean(accessCode) };
}
function fields(data) {
return {
nickname: clean(data.nickname, 60),
model: clean(data.model, 40),
host: clean(data.host, 255),
serial: clean(data.serial, 40),
ams_type: Object.prototype.hasOwnProperty.call(AMS_TYPES, data.ams_type) ? data.ams_type : null,
};
}
async function list() {
const { rows } = await db.query('SELECT * FROM printers ORDER BY id');
return rows.map(publicPrinter);
}
// Server-side only: includes the access code, for connecting.
async function listWithCodes() {
const { rows } = await db.query('SELECT * FROM printers ORDER BY id');
return rows;
}
async function get(id, { withCode = false } = {}) {
const { rows: [row] } = await db.query('SELECT * FROM printers WHERE id = $1', [id]);
if (!row) return null;
return withCode ? row : publicPrinter(row);
}
async function create(data) {
const f = fields(data);
const { rows: [row] } = await db.query(
`INSERT INTO printers (nickname, model, host, serial, access_code, ams_type)
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
[f.nickname, f.model, f.host, f.serial, clean(data.access_code, 64), f.ams_type]);
return publicPrinter(row);
}
// Only the fields sent are changed; a blank access code keeps the stored one.
async function update(id, data) {
const f = fields(data);
const has = (key) => Object.prototype.hasOwnProperty.call(data, key);
const { rows: [row] } = await db.query(
`UPDATE printers SET
nickname = CASE WHEN $2::boolean THEN $3 ELSE nickname END,
model = CASE WHEN $4::boolean THEN $5 ELSE model END,
host = CASE WHEN $6::boolean THEN $7 ELSE host END,
serial = CASE WHEN $8::boolean THEN $9 ELSE serial END,
ams_type = CASE WHEN $10::boolean THEN $11 ELSE ams_type END,
access_code = COALESCE($12, access_code),
date_modified = now()
WHERE id = $1 RETURNING *`,
[id, has('nickname'), f.nickname, has('model'), f.model, has('host'), f.host,
has('serial'), f.serial, has('ams_type'), f.ams_type, clean(data.access_code, 64)]);
return publicPrinter(row);
}
// Removing a printer also marks its spools as no longer loaded anywhere.
async function remove(id) {
const { rows: [row] } = await db.query('DELETE FROM printers WHERE id = $1 RETURNING serial', [id]);
if (!row) return false;
if (row.serial) {
await db.query('UPDATE filament_spools SET ams_slot = NULL WHERE printer_serial = $1', [row.serial]);
}
return true;
}
const measures = (amsType) => Boolean(AMS_TYPES[amsType]);
module.exports = { AMS_TYPES, list, listWithCodes, get, create, update, remove, measures, publicPrinter };