Forked from drndos/openspoolman This fork targets reliable operation on a Bambu Lab X1C in LAN-only / Developer Mode without any cloud dependency.
The original openspoolman relies on ams_mapping metadata from the project_file MQTT command to assign filaments to trays. This only works for cloud-initiated prints β LAN and local prints don't carry this metadata.
This fork replaces the static mapping approach with a live MQTT signal observer (live_tray_resolver.py) that watches AMS state transitions in real time:
- Detects filament swap announcements via
extruder.starchanges - Confirms tray bindings at SETTLED state (
ams_status & 0x300 == 0x300ANDtray_pre == tray_now) - Works for all job sources: LAN, LOCAL (sdcard), LOCAL (repeat-print / "Erneut drucken"), CLOUD
- Guards against spurious SETTLED events during bed leveling and purging
The original code collected filaments from all plates in slice_info.config, leading to phantom filament entries in print history for plates not being printed.
This fork:
- Reads
plate_idxfrom MQTT (push_status) to identify the active plate - Passes
plate_idxthrough togetMetaDataFrom3mf()which now filters filaments per plate - Reads
filament_sequence.jsonfrom the 3MF archive as the authoritative swap sequence source (filament IDs, not AMS slot indices) - Fixes incorrect startup tray binding when the printer switches filament immediately at job start (e.g. Plate 2 starting with a different filament than what was loaded from Plate 1)
When a print is started via the printer's "Print again" button, the MQTT URL is file:///userdata/project_file.gcode.3mf β a generic placeholder that cannot be fetched via FTP. This fork:
- Detects this case via
"project_file.gcode" in url - Falls back to the cached 3MF from the last LAN/LOCAL print (
data/cache/<printer_id>.3mf) - Other LOCAL jobs (
file:///sdcard/...) with real filenames continue to be fetched via FTP as normal
On resume after a restart, the original code lost print_id β all DB writes after recovery went nowhere. This fork stores print_id in the checkpoint metadata and restores it on resume.
Previously PrintMonitor.apply_filaments() inserted filament rows at PREPARE time using all filaments from all plates. Now:
PrintMonitoronly callsinsert_print()andset_tracking()β pure job managementFilamentUsageTracker.start_print()receives the plate-filtered filament dict and calls_apply_filaments()internallyprint_historyis a pure DB interface with no logic
| Bug | Fix |
|---|---|
Crash on repeat-print: zipfile.ZipFile(None) |
_load_model() guards against model_path=None; download() sets download_done = bool(metadata) instead of unconditional True |
get_metadata() crash when metadata is None |
if self.metadata is None: self.metadata = {} guard added |
NameError: name 'reason' in download() exception handler |
Removed undefined variable |
| All filaments from all plates inserted into print history | Per-plate filtering via plate_idx from MQTT |
| Wrong filamentβtray binding when Plate 2 starts with a filament not in Plate 2 | Startup SETTLED skipped when star already points to a different tray |
print_id = None after checkpoint recovery |
print_id now stored in and restored from checkpoint |
| M620 slot indices used as filament indices in resolver (off-by-one for multi-plate) | filament_sequence.json used as swap sequence source (filament IDs directly) |
The following were made obsolete by the LiveTrayResolver and have been removed:
PrintContext.get_ams_usage()andmetadata["use_ams"]PrintContext.get_mapping()(ams_mappingpassthrough)FilamentUsageTracker.apply_ams_mapping()(was already a no-op)FilamentUsageTracker._retrieve_model()(replaced by_load_model())import tempfile,from urllib.parse import urlparse,download3mfFromCloud/FTP/LocalFilesystemimports in trackerexisting.pop("ams_mapping", None)in checkpoint
Reliable filament tracking on a Bambu Lab X1C in LAN-only / Developer Mode. Cloud prints are not a priority. The focus is:
- Correct spool-to-print-job assignment across all job initiation modes
- Accurate per-layer filament consumption tracking
- Robust handling of multi-plate jobs
- No dependency on Bambu cloud services
- v0.3.0 - 23.12.2025 β more accurate filament accounting and layer tracking, higher-fidelity print history, and better Bambu Lab / AMS integration
- v0.2.0 - 07.12.2025 β Adds material-aware tray/spool mismatch detection, tray color cues, print reassign/pagination, spool material filters, and SpoolMan URL handling with refreshed responsive layouts.
- v0.1.9 - 25.05.2025 β Ships post-print spool assignment, multi-platform Docker images, customizable spool sorting, timezone config, and compatibility/uI polish.
- v0.1.8 - 20.04.2025 β Starts importing each filament's SpoolMan
filament_idfor accurate matching (requires thefilament_idcustom field). - v0.1.7 - 17.04.2025 β Introduces print cost tracking, printer header info, SPA gating improvements, and fixes for drawer colors/local prints.
- 0.1.6 - 09.04.2025 β Published container images (main service + Helm chart) and packaged artifacts for easier deployments.
Overview over the trays and the assigned spools and spool information
Desktop screenshots (expand to view)
Overview over the trays and the assigned spools and spool information
Assign a spool to a tray with quick filters.
Track every print with filament usage, used spools and costs.
Shows informations about the spool and allows to assign it to a tray.
Assign and refresh NFC tags so you can scan them with you mobile and get directly to the spool info.
Change or remove the spool assignment after a print Useful when the wrong spool was assigned or the print was canceled.
Mobile screenshots (expand to view)
- Android Phone with Chrome web browser or iPhone (manual process much more complicated if using NFC Tags)
- Server to run OpenSpoolMan with https (optional when not using NFC Tags) that is reachable from your Phone and can reach both SpoolMan and Bambu Lab printer on the network
- Bambu Lab printer in LAN-only / Developer Mode β https://eu.store.bambulab.com/collections/3d-printer
- SpoolMan installed β https://github.com/Donkie/Spoolman
- NFC Tags (optional) β https://eu.store.bambulab.com/en-sk/collections/nfc/products/nfc-tag-with-adhesive
Python / venv deployment (see Environment configuration below)
- Clone the repository:
git clone https://github.com/diegoeloren/openspoolman.git cd openspoolman - Create and activate a virtual environment, then install the dependencies:
python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt - Configure the environment variables (see below).
- Run the server with:
OpenSpoolMan listens on port
python wsgi.py
8001by default so it does not clash with SpoolMan on the same host.
Docker deployment (see Environment configuration below)
- Make sure
dockeranddocker composeare installed. - Configure the environment variables (see below).
- Copy
docker-compose.yamlto your deployment directory and adjust any host volumes or ports as needed. - Build and start the containers:
docker compose up -d
Kubernetes (Helm) deployment (see Environment configuration below)
- Use the bundled Helm chart under
./helm/openspoolman:helm dependency update helm/openspoolman
- Create a
values.yaml(or usehelm/openspoolman/values.yaml) that overrides the sameconfig.enventries and configures an ingress with TLS for your cluster. - Install or upgrade the release:
helm upgrade --install openspoolman helm/openspoolman -f values.yaml --namespace openspoolman --create-namespace
- Verify the pods and ingress:
kubectl get pods -n openspoolman kubectl describe ingress -n openspoolman
Rename config.env.template to config.env or set environment properties:
OPENSPOOLMAN_BASE_URLβ the HTTPS URL where OpenSpoolMan will be available on your network (no trailing slash, required for NFC writes).PRINTER_IDβ find it in the printer settings under Setting β Device β Printer SN.PRINTER_ACCESS_CODEβ find it in Setting β LAN Only Mode β Access Code (the LAN Only Mode toggle may stay off).PRINTER_IPβ found in Setting β LAN Only Mode β IP Address.SPOOLMAN_BASE_URLβ the URL of your SpoolMan installation without trailing slash.AUTO_SPENDβ set toTrueto enable filament tracking (required for any tracking to work).TRACK_LAYER_USAGEβ set toTrueto enable per-layer tracking and consumption whileAUTO_SPENDis alsoTrue. IfAUTO_SPENDisFalse, all filament tracking remains disabled regardless ofTRACK_LAYER_USAGE.DISABLE_MISMATCH_WARNINGβ set toTrueto hide mismatch warnings in the UI (mismatches are still detected and logged tologs/filament_mismatch.json).CLEAR_ASSIGNMENT_WHEN_EMPTYβ set toTrueif you want OpenSpoolMan to clear any SpoolMan assignment and reset the AMS tray whenever the printer reports no spool in that slot.COLOR_DISTANCE_TOLERANCEβ integer (default40), perceptual ΞE threshold for tray/spool color mismatch warnings.
By default, the app reads data/3d_printer_logs.db for print history; override it through OPENSPOOLMAN_PRINT_HISTORY_DB.
Run SpoolMan and add these extra fields:
- Filaments
typeβ Choice:AERO,CF,GF,FR,Basic,HF,Translucent,Aero,Dynamic,Galaxy,Glow,Impact,Lite,Marble,Matte,Metal,Silk,Silk+,Sparkle,Tough,Tough+,Wood,Support for ABS,Support for PA PET,Support for PLA,Support for PLA-PETG,G,W,85A,90A,95A,95A HF,for AMSnozzle_temperatureβ Integer Range, Β°C, 190β230filament_idβ Text
- Spools
tagβ Textactive_trayβ Text
Add your Manufacturers, Filaments and Spools to SpoolMan (consider 'Import from External' for faster workflow).
The filament id lives in C:\Users\USERNAME\AppData\Roaming\BambuStudio\user\USERID\filament\base (same for each printer/nozzle).
SpoolMan can print QR-code stickers for every spool; follow the SpoolMan label guide to generate them. Before printing, update the base URL in SpoolMan's settings to point at OpenSpoolMan so every sticker redirects to OpenSpoolMan instead of SpoolMan.
- The spool's
materialmust match the AMS tray'stray_type(main type). - For Bambu filaments, the AMS reports a sub-brand; this must match the spool's sub-brand. You can model this either as:
material= full Bambu material (e.g.,PLA Wood) and leavetypeempty, ormaterial= base (e.g.,PLA) andtype= the add-on (e.g.,Wood).
- You can wrap optional notes in parentheses inside
material(e.g.,PLA CF (recycled)); anything in parentheses is ignored during matching. - If matching still fails, temporarily hide the UI warning via
DISABLE_MISMATCH_WARNING=true(mismatches are still logged tologs/filament_mismatch.json).
- For non-Bambu filament, select it in SpoolMan, click 'Write,' and tap an NFC tag near your phone (allow NFC).
- Attach the NFC tag to the filament.
- Load the filament into AMS, then bring the phone near the NFC tag so it opens OpenSpoolMan.
- Assign the AMS slot you used in the UI.
- Click 'Fill' on a tray and select the desired spool.
- Done.
Once the server is running, open https://<host>:8443 if you used the built-in adhoc SSL mode, or http://<host>:8001 when the service listens on the default port 8001. For Docker deployments, you can also use docker compose port openspoolman 8001 to see the mapped host port.
- If you change the
OPENSPOOLMAN_BASE_URL, you will need to reconfigure all NFC tags. - Cloud prints are not tested or supported in this fork.
- Filament remaining in AMS (AMS Lite only β no full AMS tested yet)
- Checkpoint resume: spoolβtray bindings are re-learned live after restart; usage between crash and first SETTLED event is buffered and applied once the tray is confirmed
- Multi-AMS setups: architecture supports it (tray IDs 0β3 = AMS 1, 4β7 = AMS 2), but not yet tested
- LOCAL jobs from printer internal storage (
file:///userdata/paths other thanproject_file.gcode) β MQTT recordings needed to extend detection - Video showcase

