Commit graph

119 commits

Author SHA1 Message Date
dlawler489
d5fa67d9e7 Merge MakerWorld anti-robot handling
All checks were successful
build-and-push / docker (push) Successful in 55s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 15:48:36 +10:00
dlawler489
08d9bdabb1 Note the anti-robot check in the README
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 15:45:09 +10:00
dlawler489
ef2093b73f Say when MakerWorld is waiting for you to prove you're not a robot
The challenge was detected but treated as an ordinary download failure, so
the message didn't say that only you can clear it. Now it stops the run,
is remembered centrally, and watches skip downloads while it stands —
metadata still imports, and models stay partial for "Get files". The
Watched page shows a banner with a link to MakerWorld and an "I've solved
it" button; a successful download clears it too. Detection also covers a
challenge page returned where JSON was expected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 15:44:54 +10:00
dlawler489
bbbaf8fa7d Merge chamber camera view
All checks were successful
build-and-push / docker (push) Successful in 16s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 15:08:01 +10:00
dlawler489
a91c7b4bf7 Show the chamber camera on the printer card
A Camera button on each card opens the printer's chamber view, served
through Printhive so the access code stays on the server. One connection
per printer however many people are watching, dropped as soon as the last
viewer closes it; the latest frame goes to a new viewer straight away.
Nothing is recorded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 15:07:22 +10:00
dlawler489
a8fd112257 Merge chamber camera connection and test
All checks were successful
build-and-push / docker (push) Successful in 17s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 15:04:32 +10:00
dlawler489
a442d31dc7 Add a chamber camera connection and a test for it
P1 and A1 series printers serve JPEG frames on port 6000 over TLS, behind
the same access code. This adds the connection, a frame reader, and
POST /api/printers/:id/camera/test, which waits for one picture and says
what arrived — enough to find out whether the camera answers before any
viewer is built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 11:47:09 +10:00
dlawler489
f7f7b21d36 Merge active filament display
All checks were successful
build-and-push / docker (push) Successful in 14s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 00:31:53 +10:00
dlawler489
1aca9a6c01 Show which filament is feeding each printer, with a tube from spool to toolhead
The live connection reads tray_now (unit × 4 + slot, 254 external, 255
none) and works out the unit by id or, failing that, by position — so the
A2L's AMS lite numbered 16 is handled. Cards say 'Using' (or 'Loaded' when
not printing) with the filament's name and slot, mark the slot in the list,
and draw a tube in its colour from that spool to the toolhead, with a
moving flow while printing. The external spool is drawn when it's in use.
Debug reports include tray_now and tray_tar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 00:27:57 +10:00
dlawler489
859554e345 Merge page and model addresses
All checks were successful
build-and-push / docker (push) Successful in 15s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 00:21:26 +10:00
dlawler489
f3e0a645a2 Keep the open model in the address too
Opening a model adds /model/<id> to the address (#collection/12/model/34),
so a refresh or bookmark reopens it and Back closes it. Moving from one
model to the next doesn't add Back steps, and closing takes back the step
opening added. A model that's gone is dropped from the address.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 00:21:03 +10:00
dlawler489
2aff27cbdd Keep the page you're on in the address
Each page has an address (#filament, #printers, #collection/12), so a refresh
or bookmark returns to it and Back/Forward move between pages. The search
and filter bar is hidden on the Filament page, where it did nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 00:19:13 +10:00
dlawler489
ff2b0bc11f Merge live status fixes and untagged spool naming
All checks were successful
build-and-push / docker (push) Successful in 14s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 23:55:16 +10:00
dlawler489
73176cc41d Call untagged spools by their material and reported colour, not a Bambu colour name
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 23:55:16 +10:00
dlawler489
88988237ae Don't let a busy printer's reports hold back spool sync; hide placeholder chamber temps
A printer mid-print reports every second, and each report restarted the
five-second wait, so a printing printer's spools never synced. Only a change
in what's loaded restarts it now. Printers without a chamber sensor report
5 °C, which is no longer shown.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 23:50:54 +10:00
dlawler489
4daf15d390 Merge live printer status
All checks were successful
build-and-push / docker (push) Successful in 16s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 23:46:50 +10:00
dlawler489
13b5e550a3 Show live printer status over a standing read-only connection
Each printer now has one MQTT connection that stays open, merging the
partial reports printers send while printing. Cards poll every 3 seconds
(paused while the tab is hidden) and show state, file, progress with time
left and finish time, layer, nozzle/bed/chamber temperatures, speed
setting, and print error and HMS codes. A spool change syncs the Filament
page and the card's drawing within seconds; a full report is requested
every 10 minutes as a backstop, replacing the 10-minute probe loop.

"Test & sync" uses the open connection rather than making a second one
(?debug=1 still makes a short separate one). Connections are reconciled
when printers are added, edited or removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:30:13 +10:00
dlawler489
c7d4194243 Merge loaded filament on printer cards
All checks were successful
build-and-push / docker (push) Successful in 14s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:23:22 +10:00
dlawler489
c2bae07e54 Show each printer's loaded filament on its card
Each sync keeps what the printer reported, slot by slot (material, colour,
empty, tagged — nothing that identifies a spool). The card's drawing colours
in the first AMS unit's spools, dashes empty slots, and lists every slot and
the external holder underneath with its colour name. "Test & sync" redraws
the card with what it just read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:21:03 +10:00
dlawler489
99e0f3d245 Merge AMS unit labels
All checks were successful
build-and-push / docker (push) Successful in 16s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:18:56 +10:00
dlawler489
fdb50d45e5 Label AMS units by their range, so the A2L's AMS lite isn't 'AMS 17'
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:14:03 +10:00
dlawler489
697ac0ccc8 Merge the Printers page and spool RFID tags
All checks were successful
build-and-push / docker (push) Successful in 16s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:03:17 +10:00
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
dlawler489
1c19ac565e Merge automatic spools from printer reports
All checks were successful
build-and-push / docker (push) Successful in 14s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:41:31 +10:00
dlawler489
48f564c648 Add spools automatically from what the printer has loaded
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>
2026-09-17 20:39:30 +10:00
dlawler489
99d6bdf06c Merge spool RFID identity and the raw-report diagnostic
All checks were successful
build-and-push / docker (push) Successful in 15s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:31:50 +10:00
dlawler489
88251ccd8a Keep a spool's RFID identity, and add a raw-report diagnostic
The tray reader kept material, colour, profile and amount but dropped the
spool's RFID fields, so the printer test couldn't have shown a filament's
serial even if the printer sent one. It now keeps tray_uuid (the spool),
tag_uid (the tag) and tray_id_name where the firmware provides it, and turns
the all-zero values sent for untagged spools into "none" rather than a fake id.

The printer test also takes ?debug=1, which returns one loaded tray exactly as
the printer sent it, to see which fields a given printer and firmware provide
before designing around them. It's filament data; no credentials are in it.

Also notes that an AMS lite's "remain" is full-or-empty rather than a
measurement, which the next change needs to account for per printer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:26:47 +10:00
dlawler489
7c893e50bd Merge read-only Bambu Lab printer connection
All checks were successful
build-and-push / docker (push) Successful in 31s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:16:36 +10:00
dlawler489
93130967af Connect to a Bambu Lab printer, read-only
Settings → Printer takes the printer's address, serial number and access code
(the code stored as a secret, like the other tokens). "Save & test connection"
connects to the printer's own MQTT broker on port 8883, subscribes to its
reports, asks once for a full report, and listens for a few seconds — then
says what it heard: its state, and the filament in each AMS slot or on the
external spool holder, with how much is left where the printer knows.

Nothing sent to the printer changes what it is doing; the only thing published
is the request for a full report. If the printer is in cloud mode and treats
even that as a write, the test says it received nothing rather than failing
silently. The access code never leaves the server.

Adds mqtt as a runtime dependency — server.js now loads it at startup, so the
lockfile has to travel with this change.

Not verified against a real printer yet: the printer's ports answer on the
network, but no connection has been made with its credentials.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:15:15 +10:00
dlawler489
122f9d53ce Merge any-colour slot assignment order
All checks were successful
build-and-push / docker (push) Successful in 15s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:40:37 +10:00
dlawler489
1581f767fa Fill "any colour" slots last, not first
Found on the live library: setting slot 2 of a four-colour bookmark to "any
colour" made it take the one Jade White spool, leaving slot 4 — which asks for
#FFFFFF — with nothing, and the verdict stuck at "needs 3 colours" when it
should have improved to 2.

The assignment sorts candidate pairs by colour distance and scored a colourless
slot 0, a perfect match, so it always claimed a spool ahead of a slot that
needed that exact colour. Colourless slots now sort last and take whatever is
left over.

Fixed in both the server and the browser copy, which mirror each other, and
pinned by a test built from the reported case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:27:58 +10:00
dlawler489
e8b68e1149 Merge editable filament slots, with an any-colour option
All checks were successful
build-and-push / docker (push) Successful in 16s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:21:04 +10:00
dlawler489
8f3e63585c Let a slot ask for any colour
Some prints don't care about the shade, and some change with the order. Each
slot in the editor now has an "any colour" box: ticking it greys out the swatch
and saves the slot without a colour, which the matcher fills from any spool of
that material. A slot added by hand starts this way, since adding one usually
means you care about the material rather than the shade.

An unparseable colour already became "any colour" rather than being refused;
that stays, and is now covered by tests along with the shapes the checkbox
actually sends — null, empty string, and no colour key at all.

Worth knowing when reading the printable count: ticking "any colour" makes a
model easier to satisfy, so the count now partly reflects your decisions rather
than only what the project files ask for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:19:47 +10:00
dlawler489
eaf1bd93d5 Let the required colours be edited on the model page
The slot list comes from the project file, which is sometimes wrong and
sometimes not what you intend to print. "Edit slots" turns it into fields:
material and colour per slot, with slots you can add or drop. Saved slots are
marked as yours and shown with a "your list" badge, and "Use the project's
list" throws the override away and re-reads the 3MF.

Edits have to outlive the background reader, which is the part that would fail
silently: the reader only picks up models with no status, and reread() now
skips 'edited' rather than nulling everything. Verified on a real model — the
edit survived a reread plus a full worker pass, and the revert restored the
project's own four slots exactly.

Input cleaning moved out of the route into lib/filaments.js so it can be
tested: a slot with no material is dropped, and a colour that isn't a proper
hex becomes "any colour of that material" rather than being rejected.

Not verified: none of this has been seen rendered — the local preview browser
has no session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:15:48 +10:00
dlawler489
c81dc68c0b Merge colour-naming fixes
All checks were successful
build-and-push / docker (push) Successful in 14s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:12:25 +10:00
dlawler489
08c6dcc432 Name a colour after the everyday filament, not the first match
Seventeen PLA filaments are #000000 and eleven are #FFFFFF, so slot 1 of a
bookmark came out as "Aero Black" — true, but not what anyone calls it. Naming
now prefers Basic, then Matte, before specialist types.

Also puts a space between the "none" badge and the nearest-spool hint, which
were running together as "nonenearest: Jade White".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:10:18 +10:00
dlawler489
d6ad49d9cb Merge colour-accurate filament matching and the Bambu filament picker
All checks were successful
build-and-push / docker (push) Successful in 16s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:06:40 +10:00
dlawler489
2635a1e9f0 Pick spools from the maker's filament list, and name slot colours
Adding a spool no longer means typing a hex code: choose "PLA Basic — Jade
White (10100)" and the material, colour name, swatch and filament code fill
themselves. Slot colours on the model page are named where they match, so a
slot reads "#586579 · Basic Blue Gray" instead of bare hex.

The filament code is stored per spool. It isn't decoration — the AMS reports
filament IDs, so this is what a later read-only printer connection will match a
loaded tray against.

db/bambu-filaments.json holds Bambu Lab's 325 filaments (material, type, colour
name, code, colours), included by kind permission of 3dfilamentprofiles.com,
whose terms otherwise reserve the data. The permission and source are recorded
in the file itself as well as the README, so nobody reuses it by accident.

Naming uses a much tighter threshold than matching (12 against 50): identifying
a colour and substituting for one are different questions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:01:09 +10:00
dlawler489
7b018aefdb Match slots by colour, not just material
Counting slots fixed one half of the problem; this fixes the other. A slot is
now filled by a spool of the same material whose colour is close to what the
slot asks for, one spool per slot, best matches assigned first. So four PLA
slots in black, blue-grey, grey and white are not satisfied by four spools of
white — only the white slot is.

Closeness is redmean distance with a tolerance of 50, which treats genuinely
near shades as the same colour while keeping #000000 and #161616 (about 66
apart) distinct. Slot colours arrive as #RRGGBB and #RRGGBBAA — the alpha
channel is parsed off rather than making the colour unreadable.

The model page now lists every slot: its colour, which spool fills it, and the
nearest thing on the shelf when nothing does.

Two things this fixes in code I wrote an hour ago: renderDetailPrintable still
called a function the rewrite removed, so opening any model would have thrown;
and the README claimed colours weren't matched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:47:18 +10:00
dlawler489
8814e79669 Count slots, not materials: four colours need four spools
Reported from the library: a four-colour AMS bookmark showed as "printable now"
against a single spool of Jade White. The matching collapsed the project's four
slots into the set of materials it used — {PLA} — and then asked only whether a
PLA spool existed. A sixteen-colour model would have passed the same way.

Ignoring colour is right: printing a bookmark in red instead of white still
prints. Ignoring slot count is not: four slots need four spools loaded. Matching
now counts slots per material and reports the shortfall — "needs 3 more PLA",
and on the model page "Needs 4 × PLA. You have 1 of 4 PLA."

The tests carried the same mistake, asserting that one spool satisfied a
multi-slot project, so they are rewritten around the reported case.

Also documents the feature in the README, which had no entry for it at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:41:40 +10:00
dlawler489
ecdcafc967 Merge printable-now badges, section and filter
All checks were successful
build-and-push / docker (push) Successful in 16s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:54:09 +10:00
dlawler489
5f708e24f5 Show what's printable with the filament you have
Three placements: a pill beside the sellable badge on each card, a "Printable
now" section between Source and License on the model page, and a toolbar filter
for "printable now" / "missing filament".

Only the 101 models whose slicer project names its filaments can answer this,
so the rest say so plainly rather than being reported as unprintable. Colour is
not matched — a different colour still prints.

Also fixes a bug from the filament page itself: 'filament' was missing from
SPECIAL_SCOPES, so any data refresh while that page was open (after an AI run,
say) decided the collection had vanished and dropped you back to All Models.

Caveat: the client's printability() mirrors lib/filaments.js by hand. The server
side has tests; this copy cannot easily get them, because app.js touches DOM
globals at load — so the two can drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:46:54 +10:00
dlawler489
5b559326e3 Merge filament inventory and per-model filament reading
All checks were successful
build-and-push / docker (push) Successful in 32s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:32:52 +10:00
dlawler489
ba3bb415bb Track the filament you have, and what each model asks for
A Bambu Studio or OrcaSlicer 3MF names the filaments its plates use, but
reading that means shelling out to Python, so it can't be done per page load.
A background pass — in the same loop as thumbnails and fingerprints — reads it
once per model into models.filaments, which turns "what can I print now" into
a comparison of two lists.

Adds a Filament page for the spools on hand: material, colour, brand and grams
left, edited in place. Matching is on material only; a project's exact colours
would make almost nothing printable, and a different colour still prints.

Measured on the dev library: 10 models read with no errors, the two Bambu
projects resolving to four PLA colours each, and no plate images written
because the reader skips --images and queues behind anything interactive.

Not done yet: nothing in the library view uses this — no "printable now" badge
or filter — and the Filament page itself has not been seen rendered, because
the local preview browser has no session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:18:54 +10:00
dlawler489
c0c5e3300a Add the dev server config the preview tool launches
All checks were successful
build-and-push / docker (push) Successful in 32s
Names the project's dev command and port (npm start, 3100) so the in-app
browser preview can start Printhive without being told how each time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 20:34:57 +10:00
dlawler489
341f8c5df1 Merge per-watch file downloading
All checks were successful
build-and-push / docker (push) Successful in 24s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 19:18:55 +10:00
dlawler489
32299da05e Let a watch download files as models arrive
Checks have imported metadata only since MakerWorld refused bulk downloads.
Today's measurement changed that picture: it refuses bursts a few seconds
apart, but 67 models downloaded in a row about 20 seconds apart without one
refusal. So downloading during a check is viable — at the right pace.

Per watch, off by default: "Files: on/off" on the row, a checkbox when adding
one. With it on, a check spaces downloads 20 seconds apart instead of 3, and
stops after two refusals in a row, coming back shortly rather than collecting
refusals for the whole batch. A model whose files were refused still lands as
'partial', so "Get files" can finish it later.

Auto-imports now get the same AI treatment a manual run does — tags, a clearer
name where the site's is boilerplate, and collections to review — rather than
tags alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 19:18:55 +10:00
dlawler489
8fbcc6a2b8 Merge the corrected download-limit note
All checks were successful
build-and-push / docker (push) Successful in 23s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 19:11:50 +10:00
dlawler489
8adac1018f Correct what the README says about MakerWorld's download limit
It claimed an account may only download "a couple in a row". Measured today:
67 models downloaded in a row, spaced about 20 seconds apart, with no refusal
at all. The refusals happen when downloads are fired a few seconds apart, as a
watch check used to do — so it is a rate limit on bursts, not an allowance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 18:53:32 +10:00
dlawler489
b2078690bd Merge deadlines for downloads
All checks were successful
build-and-push / docker (push) Successful in 39s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 17:52:12 +10:00