A plugin for FilaMan that deducts the filament a Bambu Lab print used from the matching FilaMan spools, by itself.
It attaches to every Bambu Lab printer FilaMan already knows, notices when a print starts, reads the 3MF the printer is working from, and books the consumed grams against the right spool when the print ends. Nothing has to be configured twice: the credentials and the AMS slot assignments come from the printer that is already set up in FilaMan.
- Watches every printer whose
driver_keyisbambulab, over its own MQTT connection, and followsgcode_statefrom start to finish - Reads the 3MF off the printer over FTPS at print start: consumption per filament, the plate preview, and the share of each filament used at every layer, streamed out of the plate gcode
- Maps slicer slots to spools through
ams_mappingand FilaMan's ownPrinterSlotAssignment, and where there is no assignment, by the RFID tag of the tray: the printer reports a uuid per tray, FilaMan keeps the same value on the spool asrfid_uid. Anything that still cannot be resolved stays open rather than guessed at - Books through FilaMan's own
SpoolService, so the movement appears in the spool log like any other, withsource = bambu_usage - Deducts an aborted print at the share it actually reached, taken from the layer curves rather than from the percentage of time, because a filament used only in the last third costs nothing when the print stops halfway
- Keeps a history with plate previews, cost per print, deviation from the slicer's estimate, search and filters, and correction by hand: reassign a spool (which moves the booking with it), correct an amount, delete an entry
- Warns while a print runs when the assigned spool does not hold enough for what is left to print
- Sits inside FilaMan's navigation while the page is open, since FilaMan 1.2.45 renders a plugin page in its own shell. That mechanism came out of this plugin as an upstream pull request, so every plugin gained it, not only this one
Version 0.9.0. It works, and it has booked a real print.
- A full print, booked. Detected, 3MF fetched over FTPS, slicer filament 4
resolved to AMS slot 1-3 and from there to spool 25, 34.09 g booked, the spool
went from 525.01 g to 490.92 g, and the movement appears in FilaMan's spool
log with
source = bambu_usage. - Resolving a spool by the RFID tag of its tray, 21 August 2026. A two colour print: the row whose spool carried its Bambu uuid resolved by itself, with nobody assigning anything, and the row next to it, whose spool carried no tag, stayed open rather than being guessed at. FilaMan's own slot assignments stayed empty throughout: we read, we do not write.
- Deducting an abort at the share it reached, same print. Stopped at 95.8 % of the layers, the blue filament was booked 2.09 g of its 2.45 g estimate, which is the 85 % its layer curve had reached, not the 96 % a count of layers would suggest. The other filament was finished before the stop and cost all of it. The spools show it: 488.46 g to 486.37 g.
Written down plainly, because it is the second thing worth knowing:
- Only one printer model has ever run it: an X1C. P1, A1 and H2D are untouched.
- No spool change mid print, as auto refill would cause. The code splits a filament row at the moment of the change and books both halves separately, and that path has only ever run in tests.
- No real fault. A print somebody stops is told from one that broke by whether the printer left an error code behind. That reading is an assumption; the code is stored with every print so the first real fault shows whether it stands the right way round.
- Not together with FilaMan's scale. FilaMan can assign a spool to a slot
itself within a time window after weighing it (
auto_assign_enabled, off by default), which would cover spools with no Bambu tag. Where FilaMan has an assignment ours yields to it, so the two should complement each other, but that combination has never been tried. - Local prints, started from the printer's display or from SD, are not built at all yet.
It reads FilaMan's rfid_uid, and that field holds whatever was put there. A
scale writes the tag it read, which for a third party spool is an NTAG the
printer never reports. So the tag route covers spools carrying their Bambu uuid,
and for the rest a spool is picked by hand once, or FilaMan's scale assigns it.
The test suite has 232 tests and runs on the standard library alone.
MQTT (print.gcode_state, ams_mapping, subtask_id)
-> print start recognised
FTPS (bambulabs_api)
-> 3MF: used_g per filament, plate preview, layer curves from the gcode
FilaMan (PrinterSlot, PrinterSlotAssignment)
-> which spool sits in which tray
print ends
-> SpoolService.record_consumption(), one booking per spool
The plugin keeps its own tables under a private MetaData, so Alembic never
touches them and they survive an update. It never writes into FilaMan's own
tables, with one exception that is the whole point: the spool booking, and that
goes through FilaMan's service rather than around it.
AMS overview, assigning a spool to a tray, reading RFID, auto matching. FilaMan's
Bambu Lab driver already does all of that, better than a reimplementation could.
This plugin only reads that state. It also does not talk to the Spoolman
compatibility API, and it does not write printer_slot_assignments, because the
driver owns that table and rewrites it from the printer's own reports.
- In FilaMan, go to Admin, Plugins
- Upload
bambu_usage-<version>.zipfrom the releases page, or build it yourself, see below - Bambu Usage Tracking appears in the navigation
No restart is needed for the page, FilaMan resolves plugin pages per request. The router is mounted at startup, so a first installation does need one restart before the endpoints answer.
- FilaMan 1.2.45 or newer. That release renders a plugin page inside the navigation shell, which this page relies on. On an older FilaMan the page still works, but stands on its own without the navigation around it
- At least one printer with
driver_key == "bambulab", which is where host, serial and access code come from - The printer reachable on the LAN over MQTT and FTPS. The Bambu cloud is not used
If FilaMan holds no slot assignment for a tray, the print still lands in the history: its rows are marked as open and a spool can be picked by hand, after which the booking runs as usual.
python3 tools/build_zip.py # -> dist/bambu_usage-<version>.zip
python3 tools/build_zip.py --check # validate only
python3 tools/build_zip.py --selftest # prove the validation bites
python3 tools/check_architecture.py # module boundaries hold
python3 -m unittest discover -s tests -t . # unit tests, stdlib onlyThe build mirrors FilaMan's own checks: extension allow list, size limits, no hidden files, required files, manifest schema. What passes here is accepted by FilaMan. Building the same version twice is refused, because a version number has to describe exactly one package.
English and German, following whatever is selected in FilaMan: the page reads
the same localStorage['lang'] the main application uses, so there is no
separate switch. Adding a language means adding one file under
bambu_usage/locales/, for example fr.json, using en.json as the template.
No HTML and no Python has to change, and missing keys fall back to English
rather than leaving a blank.
All three FilaMan themes are supported.
FilaMan's Bambu Lab driver reads only print.ams and print.vt_tray out of the
MQTT payload, which is the AMS slot state and nothing else. It discards
gcode_state, subtask and ams_mapping, so it never learns about print jobs
and never deducts anything.
For Spoolman, OpenSpoolMan solves exactly this. This plugin brings the idea to FilaMan, with three differences:
| OpenSpoolMan | this plugin | |
|---|---|---|
| Printers | exactly one, hard wired through environment variables | any number, taken from FilaMan's printer list |
| Configuration | eleven environment variables, no interface | a page in FilaMan, credentials reused from the existing printer |
| Runtime | its own container next to Spoolman | runs inside FilaMan |
| Document | Contents |
|---|---|
docs/01_Design.md |
design, sequence, failure cases, decisions |
docs/02_FilaMan_Plugin_API.md |
how FilaMan's plugin system works |
docs/03_Bambu_Data_Sources.md |
MQTT fields and the structure of the 3MF |
docs/04_Data_Model.md |
the plugin's tables and the FilaMan models it reads |
docs/05_Research_Sources.md |
evidence, by repository, file and location |
Pull requests are welcome. Please read CONTRIBUTING.md
first: it holds the conventions this repository is built on, including the
module boundaries that tools/check_architecture.py enforces.
MIT, see LICENSE. The consumption logic is ported from OpenSpoolMan
(MIT); ported parts and their origin are recorded in NOTICE. No code
is taken from the FilaMan plugin repositories, which carry no license file.


