github maziggy/bambuddy v0.2.4b3-daily.20260506
Daily Beta Build v0.2.4b3-daily.20260506

pre-release5 hours ago

Note

This is a daily beta build (2026-05-06). It contains the latest fixes and improvements but may have undiscovered issues.

Docker users: Update by pulling the new image:

docker pull ghcr.io/maziggy/bambuddy:daily

or

docker pull maziggy/bambuddy:daily


**Tip:** Use [Watchtower](https://containrrr.dev/watchtower/) to automatically update when new daily builds are pushed.

Added

  • Slicer Bundle (.bbscfg) import — pick presets from a stored bundle instead of resolving cloud/local/standard PresetRefs every slice — Closes the long tail of preset-resolution corner cases (cloud presets behind login, "from User" sentinel handling, the # -prefix clone trick, dangling inherits on renamed parents, etc.) by letting users upload a BambuStudio "Printer Preset Bundle" (.bbscfg) once per printer and pick from it for every subsequent slice. Service layer (backend/app/services/slicer_api.py): BundleSummary / BundleNotFoundError types, import_bundle / list_bundles / get_bundle / delete_bundle methods, slice_with_bundle which posts /slice with bundle id + per-category preset names instead of the JSON triplet. Routes (/api/v1/slicer/bundles, all gated on Permission.LIBRARY_UPLOAD): POST / GET / GET :id / DELETE :id. All routes proxy via _resolve_slicer_api_url so they follow the user's preferred_slicer setting (bambu_studio vs orcaslicer). Status-code mapping treats sidecar 4xx as 400, BundleNotFoundError as 404, sidecar unreachable as 503, and sidecar 5xx as 502. Preview-slice (backend/app/services/slice_preview.py::get_preview_filaments) picks up optional bundle_id + printer_name + process_name + filament_names params and routes through slice_with_bundle when set; the cache key picks up a bundle-context fingerprint so different bundle picks on the same file occupy distinct entries — gram numbers in the preview now match what the real print will produce instead of being derived from the file's embedded process settings (which can drift from the triplet the actual slice would use). The library.py and archives.py /filament-requirements routes forward the new params. Dispatch (SliceRequest.bundle: SliceBundleSpec): when set, _run_slicer_with_fallback skips resolve_preset_ref and calls slice_with_bundle; the validator skips the preset-required check so bundle-only requests validate. 3MF + bundle CLI 5xx still falls back to the embedded-settings slice path (used_embedded_settings=True surfaces in the response), and sidecar 404 (unknown bundle / preset name) maps to 400. Frontend SliceModal Bundle tier: new "Slicer bundle" picker at the top of the modal, rendered only when at least one bundle is imported (GET /slicer/bundles non-empty). Selecting a bundle replaces cloud / local / standard preset dropdowns with bundle-scoped pickers (process + per-slot filament names from the bundle) — printer is implicit (each .bbscfg has exactly one). "None" leaves the modal on the original preset-triplet path. Submit routes through SliceRequest.bundle so the backend skips PresetRef resolution and asks the sidecar to materialise the JSON triplet from the stored bundle by name. Frontend types: SliceBundleSpec + bundle?: SliceBundleSpec on SliceRequest; getLibraryFileFilamentRequirements / getArchiveFilamentRequirements accept an optional 4th-arg bundle context object. The orca-slicer-api fork's bundle endpoints (shipped on bambuddy/bundle-import) are the server side of this — see the slicer-api sidecar docker-compose for the matching versions.

Fixed

  • Filament usage double-counted when AMS auto-falls-back to a same-material spool (#957) — When one spool ran out mid-print and the AMS transparently switched to a sibling slot loaded with the same material, the usage tracker credited the originally-mapped spool with the full 3MF estimate AND added the fallback spool's remain%-delta on top — so a 78 g print could show as 78 g + 60 g = 138 g consumed across the two spools, leaving the empty spool's recorded weight beyond its label weight (the symptom the original report flagged on a 1209 g spool reading "1188.30 g used" while the new spool only got a 30 g credit). Two interacting bugs: (1) the tray-change recorder in bambu_mqtt.py gated on state in ("RUNNING", "PAUSE") literal strings, and P2S firmware briefly transitions out of RUNNING during the AMS swap, so the switch was never appended to tray_change_log; (2) the usage-tracker splitting branch in usage_tracker.py was gated on not slot_to_tray, so even when the tray-change log was populated the splitting code only ran for prints where the slicer's mapping had not been captured — i.e. never on the actual fallback case. Fix: the bambu_mqtt.py gate now keys on the print-lifecycle flags (_was_running and not _completion_triggered) so any tray change between print start and completion is captured regardless of the momentary gcode_state string. The usage_tracker.py gate is split so tray_change_log evidence with > 1 entries always takes over from slot_to_tray, treating the per-segment per-layer gcode usage as the source of truth when the printer actually fed from multiple trays. Path 2 (AMS remain%-delta fallback) then naturally skips both trays because they're already in handled_trays after splitting, eliminating the double-credit. Tests: new test_tray_change_recorded_during_intermediate_state and test_tray_change_not_recorded_after_completion in test_bambu_mqtt.py exercising the new gate; new test_tray_switch_overrides_print_cmd_mapping in test_usage_tracker.py pinning that with ams_mapping=[0] set and tray_change_log=[(0,0),(1,30)] the splitter produces two segments summing to the 3MF estimate (no double-count) and adds both (0,0) and (0,1) to handled_trays.
  • 3D Preview returned {"detail":"Not Found"} in Docker installs (#1218) — The embedded GCode viewer's static assets (gcode_viewer/) were not copied into the production Docker image, so clicking "3D Preview" on any archive loaded an iframe at /gcode-viewer/?archive=<id> that returned a bare FastAPI 404 — Firefox / Chrome rendered the JSON response inside the iframe area while the outer Bambuddy layout looked normal, masking the failure unless the user actually inspected the iframe. The Vite production build doesn't stage gcode_viewer/ into static/ either (the dev server serves it via a configureServer middleware that's dev-only), and the only integration test for the route accepted 404 as a valid outcome ("assert response.status_code in (200, 404)") so CI never caught the missing files. Affected every Docker build since the embedded viewer landed in 0.2.4b1 (commit 3adce435, 2026-04-22). Fix: Dockerfile now copies the gcode_viewer/ directory alongside the React build output. Defence in depth: backend/app/main.py logs an ERROR at startup when _gcode_viewer_dir / "index.html" is missing so future packaging gaps surface in docker logs and the support bundle instead of as silent runtime 404s. Test guard: backend/tests/integration/test_gcode_viewer.py adds test_gcode_viewer_index_served_when_assets_present which skips when the directory is intentionally absent (unit-test environments) but asserts 200 OK + a non-empty HTML body when the assets do exist on disk — so a future broken COPY fails CI loudly rather than continuing to ship a broken image.
  • Slice button no longer enabled before the preview slice resolves — Until the preview slice (or embedded-metadata read for already-sliced 3MFs) returned the per-plate filament list, the SliceModal rendered a synthetic single-slot fallback so the auto-pick had something to bind against. That made the Slice button enabled the moment the modal opened, even before the slicer had told us which AMS slots the plate actually consumes — clicking would dispatch against opaque defaults and the real-life print would either pick the wrong filament or fail with a slot-mismatch error after the fact. Adds filamentReqsQuery.isSuccess to the isReady chain so the button stays disabled while the preview slice is in flight (or before the backend's /filament-requirements call settles for sliced files) and flips to enabled the moment the real slot list lands and auto-pick fills it.
  • Docker data-volume ownership normalised at startup via gosu entrypoint (#1211) — Two long-standing failure modes have been biting Docker users repeatedly: (1) Docker named volumes are created by the daemon as root:root, and the previous chmod 777 /app/data Dockerfile workaround only covered the named-volume root — so subdirs Bambuddy creates at runtime (virtual_printer/uploads, virtual_printer/certs, etc.) inherited wrong ownership when the container ran as 1000:1000. (2) The shipped docker-compose.yml ships ./virtual_printer:/app/data/virtual_printer uncommented, and dockerd creates a missing bind-mount source on the host as root before the container starts — leaving the host directory unwritable by uid 1000 inside the container even though the named volume above it had the chmod-777 workaround. Symptom either way: [Errno 13] Permission denied: '/app/data/virtual_printer/uploads', no virtual printer ever starts, "VP doesn't work" support reports follow. Replaces the chmod-777 hack with a proper entrypoint: deploy/docker-entrypoint.sh runs as root, chowns /app/data and /app/logs (and /app/data/virtual_printer when bind-mounted) to PUID:PGID, then drops to that uid via gosu before exec'ing the app. The chown is gated behind a top-level ownership check so subsequent restarts skip the recursive traversal — no multi-second startup penalty on multi-GB archive directories. A sentinel .bambuddy file in each data path prevents Docker from re-syncing image directory metadata on every mount (otherwise empty volumes have their ownership reverted from the image on each restart, defeating the idempotency). When the container is started with an explicit user: directive or --user flag the entrypoint detects it isn't root and falls through to direct exec — preserving compatibility for users who pin a specific uid. Compose template changes: removes user: "${PUID:-1000}:${PGID:-1000}" (the entrypoint owns privilege drop now), adds PUID / PGID env vars with the same defaults, and comments out the ./virtual_printer:/app/data/virtual_printer bind mount by default with explicit "only needed if you also run a native install of Bambuddy on the same host and want both to share the VP CA cert" guidance. The entrypoint chowns the host-side dir through the bind mount the first time it sees wrong ownership, so existing uncommented installs continue to work and #1211 specifically gets fixed.

Don't miss a new bambuddy release

NewReleases is sending notifications on new releases.