Note
This is a daily beta build (2026-04-19). 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.
Fixed
- Printer Card Controls Row Overflows in Chrome — At Medium card size on a wide viewport, the printer-card controls row (fan badges, airduct mode, print speed, bed jog, then Stop / Pause on the right) visibly overlapped in Chrome while rendering fine in Firefox and Safari. The controls-row layout had a
max-[550px]:flex-wraprule on the left badge group that only fires below 550 viewport pixels, so on a wide viewport with a narrow card the left group never wrapped — and since its badges don't truncate, Chrome painted the overflowing speed/bed-jog badges on top of the right-pinned Stop/Pause buttons. German locales made it obvious ("Pausieren" is 9 characters). The left group now uses unconditionalflex-wrap, so when badges don't all fit on one line they wrap inside the left cell instead of colliding with the right cell; the parent row also wrapsgap-yso Stop/Pause drops to a new line in the worst case. Pre-existing (commit4ff3e2a6, Feb 2026), surfaced while testing #939. - MQTT Smart Plug Subscription Lost After Every Restart (#1010) — Users integrating a Shelly (or any other) plug through an external MQTT broker (e.g. ioBroker, Zigbee2MQTT, Home Assistant's MQTT broker) saw the plug's power / state / energy readings go dark after every Bambuddy restart, and the only fix was to open Settings → Smart Plugs, rename the topic to a dummy value, save, rename it back and save again. Root cause: the startup restore path in
main.py(~line 4120) still used the legacy single-topic model (mqtt_topicplus*_pathkwargs), while the Settings UI save path had been upgraded to the newer per-type model (mqtt_power_topic/mqtt_energy_topic/mqtt_state_topiceach with their own paths, multipliers andmqtt_state_on_value). Plugs configured entirely with the new per-type fields got skipped at startup because theif plug.mqtt_topic:guard short-circuited — which is exactly what a Shelly-via-ioBroker setup looks like, since those publish power and state on separate topics. The "rename, save, rename back" workaround triggered the update endpoint, which was using the correct per-type code and re-established the subscription. Fix: extracted the topic-resolution +service.subscribe()call into a singlesubscribe_plug_to_mqtt(service, plug)helper inbackend/app/services/mqtt_smart_plug.pythat preserves legacy fallback, and routed the startup restore, create, and update routes all through it so future schema changes can't cause the three paths to drift again. Regression tests cover: per-type topics restored without a legacy topic set, legacy single-topic backward compat, per-type multipliers overriding legacy, per-type winning when both are set, the empty-config skip case, and topic-list de-duplication. Thanks to saint-hh for the clear repro steps. - Large 3MF Uploads Archived as Corrupted ZIPs (#1032) — On bare-metal Raspberry Pi installs (armv7l / Python 3.11 / Bookworm), 3MF files larger than a few MB arrived complete via the virtual-printer FTP server but the copy into
data/archives/ended up not being a valid ZIP. The archive row was still written, the printer card looked fine, and the problem only surfaced later when opening the archive in the UI, whereGET /archives/{id}/platesloggedFailed to parse plates from archive N: File is not a zip fileand the thumbnail / plate / filament panels came up blank. Two things conspired:shutil.copy2takes the Linuxsendfile()fast path on Python ≥ 3.8, and a partial-return from that syscall silently truncated the destination for the upload sizes users hit; andThreeMFParser.parse()had a bareexcept: passaround itszipfile.ZipFileopen, so the archive pipeline kept going with empty metadata and left the bad file on disk. The copy is now an explicit chunked read/write withfsync()— no sendfile involved — with a post-conditionzipfile.is_zipfile()check that refuses to create the archive row (and cleans up the archive directory) when the source was a valid ZIP and the destination isn't, logging both sizes atERROR. The parser's silent catch now logs atWARNINGso corrupted 3MFs are visible in support bundles instead of disappearing into empty metadata. Regression tests cover small / multi-chunk copies, ZIP roundtrips, the post-copyis_zipfilesentinel on a truncated file, and the new parser WARNING. Thanks to saint-hh for the detailed diagnosis. - Thumbnails Blank Until Reload After Sign-In — On auth-enabled instances, signing out and back in left the File Manager (and occasionally the Archives page) full of broken thumbnails until the page was manually reloaded. Thumbnail URLs are gated by a short-lived camera-stream token that
<img>tags can't send viaAuthorizationheaders, so the token is appended as?token=…at render time. Two race conditions conspired to break this: (1) the token query was keyed only on['camera-stream-token']and fired while the user was still on the login page, 401'd, and stayed cached — after sign-in nothing invalidated it; (2) when the token did eventually arrive, the global variable holding it was not reactive, so any File Manager / Archives page that had already rendered kept serving image URLs with no token. The token query now includes the user id in its key and is gated on!!user, so a new login always triggers a fresh fetch; and when the token transitions from null to a value,useStreamTokenSyncwalks the DOM once and updatessrcon every already-rendered<img>/<video>pointing at/api/v1/without the current token, reloading them in place. - P2S Firmware Check Shows Stale "Latest" Version (#1030) — On P2S (and X2D) the Firmware Info modal reported
01.01.01.00as the newest available release even though01.02.00.00had shipped on the Bambu Lab wiki weeks earlier, so the "update available" badge never appeared. Two silent regex mismatches in the wiki scraper caused_fetch_all_versions_from_wiki()to return an empty list: (1) the section-heading anchor parser required a dash between the version bytes and the release date (id="h-01020000-20260409"), but P2S and X2D publish anchors without the dash (id="h-0102000020260409"); (2) the text-based fallback only accepted ASCII parens around the date, while P2S, X2D, A1 and A1-mini headings render dates in full-width(YYYYMMDD)(U+FF08/U+FF09). When both paths failed, the code silently fell back to the Bambu Lab download page, which still lagged at01.01.01.00. The anchor regex now accepts an optional dash and the fallback accepts both paren styles; added regression tests for the no-dash anchor and full-width paren shapes. Thanks to Minebuddy for reporting. - Library File Print-Usage Tracking (#1008) —
LibraryFile.print_countandlast_printed_atare now updated on every successful queued print completion. Previously both fields were defined on the model and displayed in the File Manager, but nothing ever wrote to them — every file in every library showed as never printed. Now counts increment cumulatively andlast_printed_atstamps the completion timestamp (UTC). Failed, cancelled and user-aborted prints are intentionally excluded, so the fields represent "successful usage" rather than "attempted usage." This unblocks sorting the File Manager by last-printed date and is a prerequisite for the scheduled-purge feature requested in #1008. Thanks to cadtoolbox for the report.
Improved
- File Manager: Collapse Folders by Default (#996) — Added a Collapse toggle next to Wrap in the File Manager sidebar header. When enabled, the folder tree opens with only top-level folders visible on every page load; disabling it restores the previous fully-expanded default. Toggling the preference also immediately re-collapses/re-expands the current tree — no reload required. Persisted to localStorage under
library-collapse-folders, matching the existinglibrary-*preference pattern. Thanks to AshieTashi for the request.
Changed
- Docker runtime image on Debian Trixie — The production Docker image now builds on
python:3.13-slim-trixieinstead of the Bookworm-basedpython:3.13-slim. Picks up ffmpeg 5 → 7 (HEVC/AV1 improvements for camera capture), OpenSSL 3.0 → 3.3, and two more years of APT package freshness. Frontend-builder stays on Bookworm until the Node.js image team publishes Trixie variants — users never see that stage.