Every Bambu spool reports an RFID identity — a spool id and a tag id, both unique — so a printer's report can now keep the Filament page in step: a new tagged spool is created, named and coded from the bundled filament list; one already known is moved to its current slot; one you added by hand before the printer was connected is adopted rather than duplicated; and one the printer no longer reports is marked as taken out. It runs on "Save & test connection" and every ten minutes in the background, still only reading from the printer. Untagged spools are listed, not stored: without a tag, one spool of a colour is indistinguishable from the next, so guessing would mean duplicates. The planning is a pure function, tested against the cases that would cause duplicates. The database side was exercised separately against a real Postgres: three successive reports created, moved and unloaded spools as intended, and a deliberate duplicate was refused by the partial unique index. The amount left isn't taken from the printer yet: an AMS lite only reports full or empty, so it's left blank until printers can say which kind of AMS they have. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
76 lines
3.7 KiB
JavaScript
76 lines
3.7 KiB
JavaScript
const test = require('node:test');
|
||
const assert = require('node:assert/strict');
|
||
const printer = require('../lib/printer');
|
||
|
||
test('AMS trays are read into material, colour and how much is left', () => {
|
||
const trays = printer.describeAms([{ id: '0', tray: [
|
||
{ id: '0', tray_type: 'PLA', tray_color: 'FFFFFFFF', tray_info_idx: 'GFA00', tray_sub_brands: 'PLA Basic', remain: 82 },
|
||
{ id: '1', tray_type: '', tray_color: '00000000' }, // empty slot
|
||
{ id: '2', tray_type: 'PETG', tray_color: '000000FF', remain: -1 }, // not a Bambu spool
|
||
] }]);
|
||
assert.deepEqual(trays[0], {
|
||
unit: 0, slot: 0, empty: false, material: 'PLA', colour: '#FFFFFF',
|
||
profile: 'GFA00', brand: 'PLA Basic', remain: 82, uuid: null, tagUid: null, idName: null,
|
||
weight: null, type: 'Basic',
|
||
});
|
||
assert.equal(trays[1].empty, true);
|
||
assert.equal(trays[1].material, null);
|
||
assert.equal(trays[1].colour, null, 'an empty slot has no colour, whatever the printer says');
|
||
assert.equal(trays[2].remain, null, '-1 means the printer can’t tell');
|
||
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', () => {
|
||
assert.deepEqual(printer.describeAms(undefined), []);
|
||
assert.deepEqual(printer.describeAms([{ id: '0' }]), []);
|
||
});
|
||
|
||
test('the external spool holder is read the same way as a tray', () => {
|
||
const ext = printer.describeTray({ id: '254', tray_type: 'PLA', tray_color: 'C12E1FFF', remain: 40 });
|
||
assert.equal(ext.unit, null);
|
||
assert.equal(ext.material, 'PLA');
|
||
assert.equal(ext.colour, '#C12E1F');
|
||
assert.equal(ext.remain, 40);
|
||
});
|
||
|
||
test('connection failures are explained in plain words', () => {
|
||
assert.equal(printer.friendlyError(new Error('Connection refused: Not authorized')),
|
||
'The printer refused the access code.');
|
||
assert.equal(printer.friendlyError(new Error('connect ECONNREFUSED 192.0.2.10:8883')),
|
||
'Nothing answered on port 8883 at that address.');
|
||
assert.equal(printer.friendlyError(new Error('connect EHOSTUNREACH 192.0.2.10:8883')),
|
||
'Couldn’t reach the printer from the server.');
|
||
assert.equal(printer.friendlyError(null), 'The connection failed.');
|
||
});
|
||
|
||
test('the product line is what follows the material in the brand', () => {
|
||
assert.equal(printer.productLine('PLA Basic', 'PLA'), 'Basic');
|
||
assert.equal(printer.productLine('PLA Silk+', 'PLA'), 'Silk+');
|
||
assert.equal(printer.productLine('PETG HF', 'PETG'), 'HF');
|
||
assert.equal(printer.productLine('Generic', 'PLA'), 'Generic', 'a brand without the material is kept whole');
|
||
assert.equal(printer.productLine('', 'PLA'), null);
|
||
});
|
||
|
||
test('a tray reports its weight when new', () => {
|
||
const t = printer.describeTray({ id: '0', tray_type: 'PLA', tray_color: 'FFFFFFFF', tray_weight: '1000' });
|
||
assert.equal(t.weight, 1000);
|
||
assert.equal(printer.describeTray({ id: '0', tray_type: 'PLA', tray_weight: '0' }).weight, null);
|
||
});
|