Merge spool RFID identity and the raw-report diagnostic
All checks were successful
build-and-push / docker (push) Successful in 15s
All checks were successful
build-and-push / docker (push) Successful in 15s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
99d6bdf06c
3 changed files with 48 additions and 6 deletions
|
|
@ -9,8 +9,12 @@ const mqtt = require('mqtt');
|
||||||
|
|
||||||
const PORT = 8883;
|
const PORT = 8883;
|
||||||
|
|
||||||
|
// An id the printer sends as all zeros means no RFID tag was read.
|
||||||
|
const realId = (v) => (v && !/^0+$/.test(String(v)) ? String(v) : null);
|
||||||
|
|
||||||
// One tray, as the printer describes it. `remain` is a percentage; the printer
|
// One tray, as the printer describes it. `remain` is a percentage; the printer
|
||||||
// sends -1 when it can't tell, which is mostly the case for non-Bambu spools.
|
// sends -1 when it can't tell. An AMS lite only knows full or empty, so its
|
||||||
|
// figure isn't a measurement — callers decide whether to trust it.
|
||||||
function describeTray(tray, unit = null) {
|
function describeTray(tray, unit = null) {
|
||||||
const loaded = Boolean(tray && tray.tray_type);
|
const loaded = Boolean(tray && tray.tray_type);
|
||||||
return {
|
return {
|
||||||
|
|
@ -22,6 +26,10 @@ function describeTray(tray, unit = null) {
|
||||||
profile: loaded ? tray.tray_info_idx || null : null, // Bambu's filament profile, e.g. GFA00
|
profile: loaded ? tray.tray_info_idx || null : null, // Bambu's filament profile, e.g. GFA00
|
||||||
brand: loaded ? tray.tray_sub_brands || null : null,
|
brand: loaded ? tray.tray_sub_brands || null : null,
|
||||||
remain: loaded && Number(tray.remain) >= 0 ? Number(tray.remain) : null,
|
remain: loaded && Number(tray.remain) >= 0 ? Number(tray.remain) : null,
|
||||||
|
// A Bambu spool's RFID tag: tray_uuid names the spool, tag_uid the tag.
|
||||||
|
uuid: loaded ? realId(tray.tray_uuid) : null,
|
||||||
|
tagUid: loaded ? realId(tray.tag_uid) : null,
|
||||||
|
idName: loaded ? tray.tray_id_name || null : null, // e.g. "A00-W1", on firmware that sends it
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,10 +53,12 @@ function friendlyError(err) {
|
||||||
|
|
||||||
// Connects, asks for a full report, and gathers what arrives for a few seconds.
|
// Connects, asks for a full report, and gathers what arrives for a few seconds.
|
||||||
// Resolves with what was seen — never with the access code.
|
// Resolves with what was seen — never with the access code.
|
||||||
function probe({ host, serial, accessCode, waitMs = 8000 }) {
|
// With `debug`, also returns one loaded tray exactly as the printer sent it, to
|
||||||
|
// see which fields a given printer and firmware actually provide.
|
||||||
|
function probe({ host, serial, accessCode, waitMs = 8000, debug = false }) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const started = Date.now();
|
const started = Date.now();
|
||||||
const seen = { connected: false, reports: 0, printer: null, ams: null, external: null, error: null };
|
const seen = { connected: false, reports: 0, printer: null, ams: null, external: null, error: null, raw: null };
|
||||||
let done = false;
|
let done = false;
|
||||||
let timer = null;
|
let timer = null;
|
||||||
const client = mqtt.connect(`mqtts://${host}:${PORT}`, {
|
const client = mqtt.connect(`mqtts://${host}:${PORT}`, {
|
||||||
|
|
@ -85,7 +95,18 @@ function probe({ host, serial, accessCode, waitMs = 8000 }) {
|
||||||
if (!p) return;
|
if (!p) return;
|
||||||
if (p.gcode_state) seen.printer = { ...(seen.printer || {}), state: p.gcode_state };
|
if (p.gcode_state) seen.printer = { ...(seen.printer || {}), state: p.gcode_state };
|
||||||
if (p.mc_percent != null) seen.printer = { ...(seen.printer || {}), progress: Number(p.mc_percent) };
|
if (p.mc_percent != null) seen.printer = { ...(seen.printer || {}), progress: Number(p.mc_percent) };
|
||||||
if (p.ams && Array.isArray(p.ams.ams)) seen.ams = describeAms(p.ams.ams);
|
if (p.ams && Array.isArray(p.ams.ams)) {
|
||||||
|
seen.ams = describeAms(p.ams.ams);
|
||||||
|
if (debug) {
|
||||||
|
const unit = p.ams.ams[0] || {};
|
||||||
|
seen.raw = {
|
||||||
|
amsKeys: Object.keys(p.ams),
|
||||||
|
unitKeys: Object.keys(unit),
|
||||||
|
unitInfo: { info: unit.info, humidity: unit.humidity, temp: unit.temp },
|
||||||
|
tray: (unit.tray || []).find((t) => t && t.tray_type) || null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
if (p.vt_tray) seen.external = describeTray(p.vt_tray, null);
|
if (p.vt_tray) seen.external = describeTray(p.vt_tray, null);
|
||||||
});
|
});
|
||||||
client.on('error', (err) => {
|
client.on('error', (err) => {
|
||||||
|
|
|
||||||
|
|
@ -337,7 +337,10 @@ app.post('/api/printer/test', async (req, res, next) => {
|
||||||
const accessCode = (await resolveSetting('printer_access_code')).value;
|
const accessCode = (await resolveSetting('printer_access_code')).value;
|
||||||
const missing = [!host && 'address', !serial && 'serial number', !accessCode && 'access code'].filter(Boolean);
|
const missing = [!host && 'address', !serial && 'serial number', !accessCode && 'access code'].filter(Boolean);
|
||||||
if (missing.length) return res.status(400).json({ error: `Fill in the printer’s ${missing.join(', ')} first.` });
|
if (missing.length) return res.status(400).json({ error: `Fill in the printer’s ${missing.join(', ')} first.` });
|
||||||
res.json(await printer.probe({ host: host.trim(), serial: serial.trim(), accessCode: accessCode.trim() }));
|
res.json(await printer.probe({
|
||||||
|
host: host.trim(), serial: serial.trim(), accessCode: accessCode.trim(),
|
||||||
|
debug: req.query.debug === '1', // raw fields for one tray — filament data, no credentials
|
||||||
|
}));
|
||||||
} catch (e) { next(e); }
|
} catch (e) { next(e); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ test('AMS trays are read into material, colour and how much is left', () => {
|
||||||
] }]);
|
] }]);
|
||||||
assert.deepEqual(trays[0], {
|
assert.deepEqual(trays[0], {
|
||||||
unit: 0, slot: 0, empty: false, material: 'PLA', colour: '#FFFFFF',
|
unit: 0, slot: 0, empty: false, material: 'PLA', colour: '#FFFFFF',
|
||||||
profile: 'GFA00', brand: 'PLA Basic', remain: 82,
|
profile: 'GFA00', brand: 'PLA Basic', remain: 82, uuid: null, tagUid: null, idName: null,
|
||||||
});
|
});
|
||||||
assert.equal(trays[1].empty, true);
|
assert.equal(trays[1].empty, true);
|
||||||
assert.equal(trays[1].material, null);
|
assert.equal(trays[1].material, null);
|
||||||
|
|
@ -19,6 +19,24 @@ test('AMS trays are read into material, colour and how much is left', () => {
|
||||||
assert.equal(trays[2].colour, '#000000', 'the alpha byte is dropped');
|
assert.equal(trays[2].colour, '#000000', 'the alpha byte is dropped');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a Bambu spool’s RFID identity is kept, and an unread tag is not invented', () => {
|
||||||
|
const [tagged] = printer.describeAms([{ id: '0', tray: [{
|
||||||
|
id: '0', tray_type: 'PLA', tray_color: 'C12E1FFF',
|
||||||
|
tray_uuid: 'A1B2C3D4E5F60718293A4B5C6D7E8F90', tag_uid: '04AB12CD34EF5680', tray_id_name: 'A00-R0',
|
||||||
|
}] }]);
|
||||||
|
assert.equal(tagged.uuid, 'A1B2C3D4E5F60718293A4B5C6D7E8F90');
|
||||||
|
assert.equal(tagged.tagUid, '04AB12CD34EF5680');
|
||||||
|
assert.equal(tagged.idName, 'A00-R0');
|
||||||
|
|
||||||
|
const [plain] = printer.describeAms([{ id: '0', tray: [{
|
||||||
|
id: '0', tray_type: 'PLA', tray_color: 'FFFFFFFF',
|
||||||
|
tray_uuid: '00000000000000000000000000000000', tag_uid: '0000000000000000',
|
||||||
|
}] }]);
|
||||||
|
assert.equal(plain.uuid, null, 'all zeros means no tag was read');
|
||||||
|
assert.equal(plain.tagUid, null);
|
||||||
|
assert.equal(plain.idName, null);
|
||||||
|
});
|
||||||
|
|
||||||
test('a printer with no AMS reports nothing rather than failing', () => {
|
test('a printer with no AMS reports nothing rather than failing', () => {
|
||||||
assert.deepEqual(printer.describeAms(undefined), []);
|
assert.deepEqual(printer.describeAms(undefined), []);
|
||||||
assert.deepEqual(printer.describeAms([{ id: '0' }]), []);
|
assert.deepEqual(printer.describeAms([{ id: '0' }]), []);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue