|
|
@@ -5,6 +5,8 @@ All notable changes to Bambuddy will be documented in this file.
|
|
|
## [1.2.6b1] - Unreleased
|
|
|
|
|
|
### Added
|
|
|
+- **USB cameras are picked from a list** — With an external camera set to **USB Camera (V4L2)**, Settings > Camera now lists the video devices the Bambuddy server can see, by name and path (`HD Pro Webcam C920 (/dev/video0)`), instead of asking for the device path. A refresh button looks again after a camera is plugged in, and a saved camera that is unplugged stays selected, marked **not found**. When no device is visible, the list says how to pass a camera to the Docker container. **Enter path manually** still takes a typed path. Looking for devices no longer holds up the server while it runs.
|
|
|
+- **The MQTT relay publishes printer offline/online and low filament** — Three new topics for Home Assistant, Node-RED and other subscribers. `printers/{serial}/offline` is sent once a printer has been disconnected for a minute, and `printers/{serial}/online` when it comes back after that, so a short blip sends neither. `printers/{serial}/filament/low` is sent once when the spool in an AMS slot drops below its low-stock threshold, at the same moment as the Low Filament notification, and works even when no notification provider has that event switched on. Wiki updated with the payloads.
|
|
|
- **Notify! notification provider with iOS Live Activities and Lock Screen widgets (#2919, requested and contributed by @simplytoast1 in #3265)** — Notify! used to need the separate notify-bambuddy companion container. Bambuddy now talks to it directly: add a provider of type Notify! with the device ID and token.
|
|
|
- **Alerts:** the usual events, printer filter, quiet hours and daily digest apply as for every provider, with the camera photo attached. Each printer gets its own notification thread. **Time Sensitive problem alerts** sends failures, stops, printer errors and failure detection as time-sensitive notifications.
|
|
|
- **Live Activities:** with **Live Activities (iOS)** on, each running print shows a tile on the Lock Screen with a countdown to the end. A fault, a pause, running out of filament or a lost connection replaces the countdown with the problem. Progress style, extra values (progress, ETA, layer, temperatures), print stage, a privacy mode that hides the file name, symbol, colour and a button that opens Bambuddy can be set under **Live Activity appearance**. A dismissed tile stays dismissed for that print, and tiles survive a Bambuddy restart.
|
|
|
@@ -134,6 +136,11 @@ All notable changes to Bambuddy will be documented in this file.
|
|
|
- **Dutch (nl) is now a supported interface language (#2891, requested and contributed by @Igiegel)** — Adds `nl` as the fourteenth locale, listed as "Nederlands" in the language picker. The translation was contributed as a file on the issue and needed three corrections before it could be wired up, all of which the parity gate found. First, the nine `stats.timeframe.*` entries had their **keys** translated along with their values (`'today'` had become `'vandaag'`), which would have left the Statistics timeframe selector resolving nothing and rendering raw key names for every Dutch user — the values were kept and the keys restored. Second, the file was translated against an older `en.ts` and was 84 leaves short, missing the Filament Track Switch feed prompts, the AI-detection status strings, the no-3MF internal-history banner, the batch-order stranded-plate notices, the Avery starting-position field and the whole `locationHaSensors` section from #2824; those were translated and added. Rather than splice them in, `nl.ts` was regenerated from the `en.ts` skeleton with the contributor's strings carried over, so its structure, key order and section comments now match the reference locale exactly and a future diff against `en.ts` reads as content rather than as reordering. Third, 229 leaves were identical to English; each was checked individually and all were kept, because Dutch takes most technical UI vocabulary verbatim — printer, filament, status, nozzle, timelapse, dashboard — and Dutch slicer users use the English feature names (support, ironing, prime tower, gap fill) untranslated. Those 123 distinct values are now listed explicitly in a `NL_COGNATES` allow-list in `check-i18n-parity.mjs`, the same shape the other twelve locales use, so the exemption is an enumerated translator decision rather than a blanket skip. Parity green at 6264 leaves across all 14 locales.
|
|
|
|
|
|
### Changed
|
|
|
+- **Unused API routes and settings removed** — For anyone calling the API from scripts:
|
|
|
+ - **Removed routes:** `POST /printers/test` (it took the access code in the URL; adding a printer tests the connection itself), the two debug routes `POST /printers/{id}/debug/simulate-print-complete` and `GET /printers/{id}/runtime-debug`, and `POST /spoolbuddy/devices/{id}/update-status`, which SpoolBuddy stopped calling when updates moved to SSH.
|
|
|
+ - **Old virtual printer settings:** `GET /settings/virtual-printer` now returns only `tailscale_disabled`, `archive_name_source` and `status`, and `GET /settings` no longer lists `virtual_printer_enabled`, `virtual_printer_access_code` and `virtual_printer_mode` (`PUT /settings` ignores them). These settings stopped doing anything when each virtual printer got its own settings; use `/virtual-printers` instead.
|
|
|
+- **A grouping that ended in failures is marked failed** — A grouping whose jobs all failed, or failed and were cancelled, with nothing printed, stayed **active** for good, even through the start-up clean-up, and piled up next to real work. It is now marked **failed**, shown in red with a **Failed** filter on the Batches tab, and opens again if a job is added to it. Orders are unchanged: a failed run still leaves an order owing it.
|
|
|
+- **Deleting a custom maintenance type says it applies to every printer** — Maintenance types are shared, so deleting a custom one removes it, with its maintenance history, from every printer, including those of other teams when printer access is limited. The confirmation now says so.
|
|
|
- **The web interface downloads about 70% less on each new version (#3175, requested by @mason9468)** — The interface's script files were sent uncompressed and with no caching instructions, so a phone fetched the whole 11 MB again and the browser asked for it on every load.
|
|
|
- **Compressed:** scripts, styles and other text files under `/assets` now go out gzip-compressed when the browser accepts it. The main script drops from 11.0 MB to 3.0 MB.
|
|
|
- **Cached for good:** files whose name carries a content hash are now marked as never changing, so the browser loads them from its own cache until an update brings new ones. The page itself is still checked on every load, so updates are picked up as before.
|
|
|
@@ -161,6 +168,311 @@ All notable changes to Bambuddy will be documented in this file.
|
|
|
- **The frontend build no longer warns about `path` and `crypto` being externalized for the STEP previewer (#2976)** — `occt-import-js`, the Emscripten build behind STEP previews, requires both modules, but only inside its `ENVIRONMENT_IS_NODE` branches; in the browser it loads its `.wasm` from the URL the preview worker passes and draws randomness from `crypto.getRandomValues`. Vite still externalized both and printed two warnings on every build. `vite.config.ts` now drops exactly those two warnings for that one package through `build.rolldownOptions.onLog`, so an externalization anywhere else, or of any other module, still shows.
|
|
|
|
|
|
### Fixed
|
|
|
+- **Saving in the timelapse editor could fail on long videos, and ignored the music volume** — Save re-encoded the video while the browser waited, which for a long timelapse took minutes. A browser or reverse proxy that gave up showed an error while FFmpeg went on running on the server.
|
|
|
+ - **Runs in the background:** Save now starts the work on the server and returns at once. The button shows how far it has got, **Cancel** stops FFmpeg and keeps the original, and an editor closed and opened again shows the progress. While one edit of a timelapse runs, Save is unavailable for it. If the timelapse is removed or replaced while the edit runs, the edit is not saved over it.
|
|
|
+ - **Music volume:** the volume slider only changed the preview; the saved video always had the music at full volume. It is now mixed in at the volume set, and muted means silent.
|
|
|
+ - On a broken file, reading the video's details now gives up after a minute and each timeline frame after 30 seconds, instead of waiting forever.
|
|
|
+- **Firmware could be uploaded to a printer in the middle of a print** — The firmware update dialog offered **Upload Firmware** whatever the printer was doing, and the upload wrote to the SD card the print runs from. It is now only offered while the printer is idle (a finished or failed print counts), and the server refuses it otherwise. If a print starts while the firmware is still downloading, nothing is written to the card, and the queue holds its next print for a printer until its firmware upload is over.
|
|
|
+- **Reset Slot cleared an AMS slot on a single click, even mid-print** — It now asks first, and is unavailable while a print is running, paused or starting, since the print may be feeding from that slot.
|
|
|
+- **Some destructive buttons acted without asking** — **Clear all** on the shopping list, **Clear** in the Git backup history and removing a linked SSO account now ask for confirmation first. An account created through SSO, which has no password, can no longer remove its last SSO link, which was its only way to sign in.
|
|
|
+- **LDAP and SMTP "Test" checked the saved settings, not the form** — Changing a field and clicking **Test Connection** or **Test Email** tested the old values, so a test could pass on settings that weren't the ones about to be saved. Both now test what the form holds, before saving. LDAP's test also works while LDAP is still disabled, and in that case now uses a saved CA certificate path too. An empty password keeps the saved one, as when saving; with a changed server it has to be entered again.
|
|
|
+- **A user's email couldn't be removed** — Emptying the email field when editing a user saved without error but kept the old address. It is now removed. The edit is refused while advanced authentication is on, or while the user signs in with email codes (email two-factor authentication), since both need the address.
|
|
|
+- **Deleting a local backup failed silently** — When the server refused or failed the delete, no message was shown and the file stayed in the list. The error is now shown.
|
|
|
+- **Purging archives could delete by an age that wasn't previewed** — The "Purge old archives" dialog previews what it will delete a moment after you stop typing, but the delete button took the age currently in the field. Changing the age and clicking straight away deleted archives for the new age while the button still showed the old preview's count. The button now waits until the preview matches the field, and the purge uses the previewed age.
|
|
|
+- **Editing a transaction to "No cost center" kept the old cost center** — Finance showed "Transaction updated", but the entry stayed booked to its previous cost center.
|
|
|
+- **After deleting a printer, spools still showed as loaded in it** — The spool assignment list wasn't refreshed, so the inventory kept showing spools in the deleted printer's slots for up to 30 seconds.
|
|
|
+- **API keys of Operators were refused on queue, archive and library routes** — A key whose owner may act only on their own rows (the built-in Operators group) got `403 API key owner does not have 'queue:update_all' permission` when it listed, edited, cancelled or deleted even its owner's own queue jobs, and the same on archives and library files. Such a key now acts on its owner's rows and not on anyone else's. Keys of owners with the `_all` permissions, and keys without an owner, reach every row as before.
|
|
|
+- **Statistics: Today, This Week and the other ranges used UTC days** — Near midnight they showed the wrong day's prints for anyone outside UTC: in Berlin at 01:30, "Today" showed the previous day. A day now runs from midnight to midnight in your browser's time zone, for every date filter on the Statistics page, and the date pickers allow today. API callers can name the zone with the new `tz` parameter on the date-filtered archive and statistics endpoints; without it, a date is a day in Bambuddy's own `TZ` (UTC days before).
|
|
|
+- **A print that failed on its first run left the archive costing only the part it printed** — With the built-in inventory, the archive took the failed run's partial cost, so a reprint was estimated, reserved and billed from that fraction. The archive now keeps the cost of one full print (as in Spoolman mode); the failed run's own cost is still recorded in the Print Log.
|
|
|
+- **Spoolman: a very short print could end with its estimated cost instead of the real one** — The cost estimate made at print start could still be waiting on Spoolman when the print ended, and then wrote over the real cost. Completion now stops it first.
|
|
|
+- **After a 2FA or SSO login, LDAP and OIDC users were offered "Change Password"** — The login answered as if they were local users, until the page was reloaded.
|
|
|
+- **The same printer fault could be listed twice** — A fault the printer reported both as a print error and in its HMS list was not recognised as the same one.
|
|
|
+- **H2D: a refused manual filament load could show the wrong active tray later** — Its target slot was remembered until something cleared it, so a print loading the same slot number on another AMS much later was shown as that slot. It is now forgotten after two minutes.
|
|
|
+- **K-profiles: a printer refusing the re-applied profile after an RFID read was not in the logs** — Only the "applied" line was, which made wrong pressure-advance prints hard to diagnose.
|
|
|
+- **K-profiles: opening several pages at once could fetch the cloud filament names many times** — Each one fetched every custom preset from Bambu Cloud while holding a database connection; they now share one fetch.
|
|
|
+- **Library API: changing a file's folder could move it into or out of an external folder without moving the file** — The file then showed in a folder it was not in. That change is now refused with a pointer to the move endpoint, which moves the file itself.
|
|
|
+- **Slicing: a slow progress answer was reported as "Slicer sidecar unreachable"** — It now says the sidecar did not answer in time.
|
|
|
+- **Printer card: the cooling/heating mode switched on the printer showed the old mode** — The live updates did not carry it, so the card caught up only with its next refresh.
|
|
|
+- **Printer card: an H2C rack slot's material label disappeared** — The live updates replaced the rack without each slot's filament type, so the label fell back to the filament ID or went blank until the next refresh.
|
|
|
+- **Git backup: a print running when the backup was made came back as a failed print** — Restored as still printing, the archive was closed on the next printer reconnect as if the print had just failed, with a failure notification and a Print Log entry. It is now restored as cancelled ("No status update received"), the way Bambuddy closes a print it lost track of; an archive this instance still has keeps its own status on overwrite.
|
|
|
+- **Git backup: spools came back as never used** — When each spool was last used was not in the backup. It is now backed up and restored (an older backup leaves the value already there).
|
|
|
+- **File manager: a slow folder listing showed as "printer unavailable"** — Listing a large folder (such as the camera recordings) on a slow SD card can outlast the wait, and was reported like an unreachable printer; every refresh also started another listing while the last one still ran. It now says the listing is taking too long, and a refresh waits for the listing already running.
|
|
|
+- **Printer files from the evening could be dated a year back** — File times on the printer carry no year, and the year was taken from the host's local clock while printers report UTC. On a host behind UTC, files written in the evening were dated a year back, so their camera recordings were not matched to their prints.
|
|
|
+- **SpoolBuddy: writing a sticker could break a Bambu spool's AMS link in Spoolman mode** — A Bambu spool created from the AMS is linked by its RFID tray ID; it was listed as untagged on the Write Tag page, and a sticker written to it replaced that link in Spoolman, so the next AMS sync created a duplicate spool. It is no longer listed as untagged, and on a Spoolman that does not also keep the tray ID as a native tag (before 0.27) the write is refused with the reason.
|
|
|
+- **SpoolBuddy: Write Tag warned "color_name not set" for every Spoolman spool** — The warning looked at a Spoolman field that is never filled; Bambuddy keeps the colour name with the spool. It now warns only when the name is missing, and says the tag then carries the subtype instead.
|
|
|
+- **SpoolBuddy: an update reported success after installing its dependencies timed out** — The device was restarted while the install could still be running. A timeout now stops the update with an error (and the time allowed went from 2 to 5 minutes); a failed install still completes, and its output is now in the log.
|
|
|
+- **Virtual printer: a late slicer update could rewrite a queue job already being sent** — The slicer's print options can reach a virtual printer seconds after the job was queued. If the scheduler had already started sending the job, its AMS mapping and options were still rewritten, so the queue entry -- and the spool charged from it -- could differ from what the printer printed. Such a job is now left as it was sent.
|
|
|
+- **Backup restore: the virtual printers kept running during the restore** — The step meant to stop them did nothing, so their FTP, MQTT and discovery services stayed up while files were replaced. They are now stopped, and start again with the restart a restore asks for.
|
|
|
+- **API: the legacy virtual printer settings route answered 200 for settings it no longer applies** — `PUT /settings/virtual-printer` saved enabled, access code, mode, model, target printer and remote interface to settings nothing reads (each virtual printer keeps its own). It now refuses them with a pointer to `PUT /virtual-printers/{id}`; the archive name source and Tailscale switch still work there.
|
|
|
+- **Settings: removed "Save thumbnails" and "Notification Language", which did nothing** — Nothing read either: thumbnails were always stored, and notifications always used their template text as written. The API still accepts both fields.
|
|
|
+- **Restoring an old SQLite backup onto PostgreSQL could put some timestamps hours off** — Required timestamps the backup left empty were filled with the host's local time, while every other timestamp is stored in UTC, so on a host ahead of UTC those rows appeared hours in the future. They are now filled in UTC.
|
|
|
+- **Maintenance: a printer's time offline was counted as print time** — A printer that dropped off mid-print and came back printing hours later had the whole gap added to its runtime, so hours-based maintenance came due early. Time while Bambuddy itself was restarting is still counted, as before.
|
|
|
+- **Maintenance: duplicate maintenance items** — A printer's items are created when its maintenance is first shown, and two pages (or a page and the background check) could each create the same one; marking it performed could then update the copy not shown, and assigning that type again failed. Existing duplicates are merged on upgrade (the most recently performed one is kept, or the one the page showed when none was, with the history of all), and they can no longer be created.
|
|
|
+- **Maintenance API: "performed" answered in hours for an item set to days** — Its response used the maintenance type's interval type instead of the item's own setting, which the list already used.
|
|
|
+- **Notifications: the daily digest could be lost** — When the digest could not be sent (a network error, say), its events were deleted anyway. A digest time inside the provider's quiet hours was skipped, logged as sent, and its events deleted too. The digest now goes out at its time even during quiet hours, and a failed send keeps the events for the next day's digest (events more than two days old are dropped, so a provider that keeps failing does not pile them up). Lines from an earlier day show their date.
|
|
|
+- **Notifications: a later print could be attributed to the user of an earlier queued one** — When a queued print's archive was deleted while it was being sent to the printer, the record of who queued it was left behind, and an unrelated later print of the same name on that printer named and emailed that user.
|
|
|
+- **Notifications: "Print Stopped" after a printer came back online showed the time it was offline as the print's duration** — A print that ended while the printer was offline is closed when it reconnects; its real end time is unknown, so the notification now shows the sliced estimate instead of, say, 14 hours.
|
|
|
+- **Notification templates: the print outcome confirmation had no variables or preview in the editor** — The editor now lists its variables (including the confirmation link) and previews it, and seven other events (AMS humidity and temperature, bed cooled, the sensor alerts, auto-drying suspended, missing spool assignment) have their names.
|
|
|
+- **Configure Slot: a manual K value that could not be sent was reported as applied** — With no K-profile picked, a typed K value goes to the printer on its own command; when that send failed, the slot still read as configured. A warning now says the filament was set but the K value was not sent.
|
|
|
+- **A stray Stop on an idle printer could relabel a later failed print as cancelled** — Stop on a printer with no print loaded still marked it as stopped by the user. If the next print was first seen mid-way (after Bambuddy reconnected), that print's real failure was then reported as the user's cancel. The mark is now only set while a print is loaded (or the printer's state is unknown).
|
|
|
+- **Skip Objects and the printer connection diagnostic held a database connection through their printer I/O** — For a print started outside Bambuddy, the object list pulls the file over FTP, which on a printer mid-print can take a while; the diagnostic probes the printer and waits up to 10 seconds for a report. The connection is now released first.
|
|
|
+- **Camera light: the plate check could switch the light off under a live view** — With the camera light setting on, a live view opened while the print-start plate check had the chamber light on found it already lit, so it never took it, and the check then switched it off: the view went dark. The plate check now holds the light like every other camera use (and still lights the chamber whatever the camera light setting), so it goes off only once nothing uses the camera, after the usual short grace.
|
|
|
+- **External MJPEG cameras: timelapse frames, finish photos and Obico checks failed while someone watched** — A single-client camera (an ESP32 stream, for example) refused the second connection these opened while a live view was running. They now take the live view's frame, as they already did for the printer's own camera. A camera with a separate snapshot URL keeps using that URL.
|
|
|
+- **X1C live view could stop instead of reconnecting** — The printer ends each camera session about once a minute and Bambuddy redials; a background cleanup that ran at that moment could make the live view end instead.
|
|
|
+- **Camera test, diagnose, plate check and plate calibration held a database connection for the whole capture** — Up to 30 seconds each, which on a large farm could exhaust the connection pool. They now release it before capturing.
|
|
|
+- **API: `GET /queue?status=` with several statuses returned nothing** — The documented `?status=pending,printing` was matched as one status. Comma-separated statuses are now each matched.
|
|
|
+- **Saving an edited queue item showed "Queue item updated" twice** — The dialog also closed twice, a failed save showed two error toasts, and an edit that also added the job for further printers closed the dialog before those were added. It now reports and closes once, after everything is saved.
|
|
|
+- **A failed Unassign on the printer card said nothing** — Unassigning a spool from an AMS slot (built-in inventory or Spoolman) that the server refused showed no message, and the spool simply stayed assigned. The error is now shown.
|
|
|
+- **The OBS stream overlay ignored live updates** — It read the printer's status updates from the wrong field, so it moved only with its 2-second refresh instead of in real time.
|
|
|
+- **Uploading two files with the same name could show the wrong result per file** — When both were uploaded together, both rows showed the first file's result, so one that failed looked uploaded. Each row now shows its own file's result.
|
|
|
+- **The queue timeline and history rows showed plate 1 for every plate** — A queued plate of a multi-plate file appeared with the file's first plate as its picture, while the queue list showed the right plate. All three now show the queued plate.
|
|
|
+- **Inventory: removed the "Full" column** — It showed whether a spool had been added full, which nothing ever recorded, so every row read "-".
|
|
|
+- **Spoolman: the print dialog's cost estimate uses the Spoolman spool's price** — In Spoolman mode, the print dialog estimated each slot from the built-in inventory's assignments, which hold nothing in that mode or rows left from before the switch to Spoolman. The estimate (and the budget check that uses it) came out at the default price per kg or at a spool no longer in the slot, and the "g left" shown for each slot was wrong the same way. Both now come from the spool bound to the slot, priced the way the print is charged: the spool's own price, else its filament's, else the default.
|
|
|
+- **Spoolman: a print whose 3MF arrived late is charged from the 3MF** — A print that started before Bambuddy could fetch its 3MF (an "Untitled" print, a printer briefly unreachable) gets an empty archive that is filled in once the 3MF turns up. In Spoolman mode its filament was still charged from the AMS remaining-percentage change — in steps of 10 g on a 1 kg spool, nothing for a spool without a percentage, and nothing at all when no slot reported one at the start. The recovered 3MF's usage is now used, as the built-in inventory already did.
|
|
|
+- **Spoolman inventory: Cost/kg shows the price prints are charged** — A Spoolman spool priced only through its filament (the spool itself has no price, or a price of 0) showed "-" or "0.00" in the Inventory's Cost/kg column and no price on the SpoolBuddy spool card, although every print off it was charged that filament price. Both now show it, the column marked "(filament price)"; the spool's own price still wins, and editing the spool does not copy the filament's price onto it.
|
|
|
+- **Spoolman AMS syncs keep a spool assigned by hand, and say what they changed** — Three cases:
|
|
|
+ - **A spool assigned to an AMS slot by hand** while the automatic Spoolman sync was waiting for Spoolman could be replaced by the spool the sync had read before. The syncs now change only the slots whose spool they found different, and only while the slot still holds what they read.
|
|
|
+ - **The Sync buttons in Spoolman settings** changed slots without telling other open pages, and without updating the slot's filament preset, so the printer card could keep the previous spool's preset name. They now do both, as the automatic sync does.
|
|
|
+ - **The Sync buttons** gave a newly created Spoolman spool the remaining weight of a built-in inventory spool last assigned to that slot before the switch to Spoolman, when the printer reported no weight of its own. That leftover weight is no longer used, as in the automatic sync.
|
|
|
+- **Spoolman inventory: five requests refused or reported correctly** —
|
|
|
+ - **Deleting a storage location while Spoolman was unreachable** counted its spools from the built-in inventory, found none, and deleted a location Spoolman spools still used. The delete is now refused until Spoolman answers.
|
|
|
+ - **Printing labels for an archived Spoolman spool** failed the whole batch with "not found", as Spoolman leaves archived spools out of its plain spool list.
|
|
|
+ - **An archived Spoolman spool could be assigned to an AMS slot** (or put on one by a tag link) and then charged the next print's usage. It is now refused, as in the built-in inventory.
|
|
|
+ - **A slot assignment or tag link for an AMS number no printer has** failed with a server error, and the link had already written the tag to Spoolman. Such a request is now refused before anything is written.
|
|
|
+ - **Resetting the "Total Consumed" counters** reported success even when some or all of them failed. The result now says how many failed, as the other bulk actions do.
|
|
|
+- **A Git backup restore keeps "Reset Total Consumed"** — The backup did not include the point each spool's "Total Consumed" counter was reset at, so a restore brought every reset counter back to its full lifetime figure. It is now backed up and restored; a backup made before this keeps the reset points the spools already have.
|
|
|
+- **A finished print's filament is charged once** — Two cases in the built-in inventory:
|
|
|
+ - **An AMS update right as a print finished** could raise the spool's used weight from the AMS remain% while the print's exact usage was still being worked out, and that usage was then added on top: the print was charged twice. The AMS sync now waits until the print is charged, as it does while the print runs.
|
|
|
+ - **A print the printer reported finished without any file name** (one that failed while preparing, right after Bambuddy started) kept the AMS weight sync off until the next print started. It now turns the sync back on.
|
|
|
+- **Spool weights and slot assignments written at the same moment no longer undo each other** — Three cases:
|
|
|
+ - **Assigning or unassigning a spool by hand, or adding a slot's spool to the inventory,** while the printer reported an AMS change for the same slot (or on a double-click) could fail with a server error on PostgreSQL. These now wait for the AMS update, as AMS updates already wait for each other.
|
|
|
+ - **A weight set by hand or weighed on the SpoolBuddy scale** just as the AMS remain% sync ran could be overwritten by the AMS figure, with the weight still marked as locked.
|
|
|
+ - **The same weight set at the end of a print** was replaced by the weight from before it plus the print's usage. The usage is now added to the stored weight.
|
|
|
+- **Inventory views stay current, and spools follow their printers and slots** — Five cases:
|
|
|
+ - **Deleting a spool** left it shown in its AMS slot in other open tabs, and **a weight from the SpoolBuddy scale, the automatic AMS sync or "Sync Weights from AMS"** reached other open tabs and the printer cards only when they next refreshed. These changes are now announced like any other inventory change, and the printer cards also show the weight a finished print leaves straight away.
|
|
|
+ - **Deleting a printer** on SQLite (the default database) left its spools assigned to it, shown as loaded on a printer that no longer exists. Their slots are now freed, and assignments left behind by earlier deletes are cleared at the next start.
|
|
|
+ - **A spool assigned to an empty slot from SpoolBuddy** that the printer could not be told about when filament went in (for example while it was reconnecting) was marked done anyway and never configured. It is now tried again on the next AMS change.
|
|
|
+ - **A spool CSV import** stored a `last_used` time with a timezone offset as if it were UTC on SQLite; it is now converted.
|
|
|
+- **Filament matching, preheat temperatures and colour names follow the plate's own filaments** — Three cases:
|
|
|
+ - **An "Any model" job with a filament override** waited forever when the printers held an equivalent filament under another name (PA-CF loaded for a PA12-CF override). The type check already counted it as the same filament, but the colour check that picks the printer did not.
|
|
|
+ - **Preheating a plate of a multi-filament project** heated the bed for the hottest filament of the whole project: a PLA plate in a project that also holds PETG was preheated, and kept warm between jobs, at the PETG temperature. Preheat and keep-warm now heat the bed for the filaments of the plate the job prints.
|
|
|
+ - **Colour names on the queue page** came from the plain filament type, so a colour named differently in a product line (the same black is "Charcoal" in PLA Matte) showed the generic name. The queue page, and the colour name the print dialog saves with a filament override, now name it within the filament's product line, as the print dialog already showed it. Jobs queued before keep the name they were saved with.
|
|
|
+- **Stopping and cancelling leave nothing running or reserved** — Four cases:
|
|
|
+ - **Cancelling a pipeline run** set its queued jobs to cancelled, but a job already preheating went on heating the printer (up to its soak time), and its share of the cost centre's budget stayed reserved. Both now happen as when the job is cancelled from the queue.
|
|
|
+ - **Stopping a job while its printer is offline** left its share of the cost centre's budget reserved until Bambuddy restarted; it is now released at once.
|
|
|
+ - **A printer slow to answer the end-of-print file cleanup** (up to about nine minutes) held up the end of the print so long that the stranded-job check closed the job first, and its auto power-off and its charge were skipped. The cleanup now gives up after 45 seconds and leaves the file on the card.
|
|
|
+ - **A print start the printer refused** is now logged with the printer's reason, and the job goes back to the queue for a retry at once. Bambuddy used to read the refusal as a print it accepted and waited three minutes for it to start.
|
|
|
+- **Queue jobs added through the API reach the printers they should** — Three cases in the API and webhooks:
|
|
|
+ - **A model in lower case** (`"target_model": "x1c"`) was refused with "No active printers for model", although the scheduler matches models without regard to case. Adding (one job, alternatives, or library files), editing and pipelines now match it the same way and store the printers' own spelling (`X1C`); two alternatives for one model in different case are refused as the same printers. A pipeline aimed at a model class in lower case found no printers.
|
|
|
+ - **A reprint through the webhook** (`/webhook/queue/add`) of a print sent through the virtual printer ignored the AMS slots the slicer had picked, so the scheduler matched slots by colour and type again. It now reuses them on the printer they were picked on, as a reprint from the UI does.
|
|
|
+ - **A multi-plate file queued without a plate** (`/library/files/add-to-queue`, `POST /queue/`) prints its default plate, but the scheduler required every plate's filament, so a printer with that plate's filament loaded was never chosen, or the job was held for another plate's filament. The plate that prints is now stored on the job.
|
|
|
+- **Queue edits made while the queue is being worked through are kept** — The scheduler works on the queue as it read it at the start of each round, and a round can take minutes while a smart plug powers a printer on. A job edited in the meantime lost the edit: an AMS mapping set by hand was overwritten, a job moved to another printer (or an "Any model" job pinned to one) could still be sent to the printer the round had picked, and a new plate could be mapped from the old plate's filaments. A job edited during a round now waits for the next one, which reads the edit. A bulk edit likewise leaves a job that started dispatching while the edit was checked, instead of changing its printer or cost centre under the upload. While waiting for a printer to power on, the scheduler no longer keeps a database transaction open.
|
|
|
+- **A print's filament and cost are charged with its own plate and AMS mapping** — Bambuddy remembers per archive which plate and AMS slots a dispatch uses, for the usage and cost charged when the print ends. Three cases used the wrong ones:
|
|
|
+ - **A whole file queued after one plate of it** was sent but never started (refused, or the printer stayed idle) was charged as that plate, with its slots.
|
|
|
+ - **A plate that failed to send** wiped the plate number and slots of another plate of the same archive still printing, whose end then charged the whole file.
|
|
|
+ - **A reprint dispatched without a slot mapping** could be charged through the previous run's slots, when that run's printer had reported its own mapping.
|
|
|
+- **A timelapse goes to the print it belongs to** — Several cases gave an archive the wrong video, or none:
|
|
|
+ - **The next plate or a reprint started meanwhile:** the printer's timelapse is looked for for some minutes after a print ends. When the same archive started printing again in that time, the previous print's video went to the new print, and the new print's own video was then never imported. The search now stops once the archive is printing again, and nothing is attached or deleted from the printer.
|
|
|
+ - **Scan for Timelapse on an older archive** (from before the video list was recorded at print start) could pick a video another archive already has, for instance by a short name like "1" that every video's timestamp contains. Those are now left out of the automatic match, and are still offered for picking by hand.
|
|
|
+ - **Both the external camera's and the printer's timelapse on:** the archive keeps the external camera's video, as before, but the printer's video it replaces is now deleted instead of staying in the archive folder unused. A camera video stitched for a print whose archive is already printing again (the next plate, a reprint) is no longer attached to the new print.
|
|
|
+- **Archives pages show a timelapse as soon as it changes** — Only the automatic scan told other open pages; a video picked or uploaded by hand, the external camera's, the conversion to MP4 and deleting one showed only after a reload.
|
|
|
+- **An archive cost set through the API, or by Recalculate costs, reaches the statistics** — Setting an archive's cost (`PATCH /api/v1/archives/{id}`) or **Recalculate costs** changed the archive only, while statistics, project totals and batch sums read the cost from the print log, so they kept the old figure. A completed print that had the archive's cost now gets the new one too; a print priced from its own spools, and a failed or cancelled one, keep theirs. **Recalculate costs** still recalculates a cost that was set by hand, as before. A negative cost, or one over 1,000,000, is refused.
|
|
|
+- **A request with `Infinity` or `NaN` in a field that fails validation answers 422** — Such a request ended in a server error, because the error response repeats the value sent and JSON can't hold it.
|
|
|
+- **Editing an archive while it is being reprinted leaves the previous print alone** — Its outcome, failure reason, verdict and filament weight went onto the previous print's log entry, rewriting that print's result in the print log and failure statistics. The edit now changes the archive only.
|
|
|
+- **Archives made without their 3MF show their colours and AMS-HT filament** — When a print's 3MF couldn't be downloaded at start, the archive took its filament from the printer's AMS report. Colours were stored as the printer reports them (`FF8800FF`), so their swatches on the Archives page stayed blank and the colour filter listed them apart from the same colour of other archives; they are now stored as `#FF8800`, like an archive with its 3MF, and existing archives and their print log entries are repaired on startup. Filament the print took from an AMS-HT was not found, so a print using only an AMS-HT got no filament.
|
|
|
+- **An archive whose 3MF arrives later is priced from its spools** — Such an archive started with the default filament price, having no 3MF to estimate from at print start, and kept it when the 3MF was found during the print. It is now estimated from its spools then, as at print start; a 3MF found only as the print ends leaves the pricing to the print's end, as before.
|
|
|
+- **An archive shows the nozzle temperature of the filament its plate prints** — The temperature was taken from the project's first filament, so a plate printing only PETG showed the PLA temperature of filament 1. It is now that of the plate's first used filament.
|
|
|
+- **A print stopped while its printer was offline counts in the print log, and its real end replaces the stop** — When **Stop** couldn't reach the printer, the queue item and archive were closed as cancelled, but no print log entry was written, so the run was missing from the print log and failure statistics, and the Archives page kept showing it as printing until reloaded. The stop now writes the cancelled run and updates the Archives page. Should the printer report the print's real end on reconnect, that outcome replaces the cancelled entry, and the archive no longer shows **Completed** with the reason "Cancelled by user".
|
|
|
+ - **One entry per run:** a printer reporting the same end twice (after a reconnect) logged the run twice, doubling it in the statistics. The repeat is now left out.
|
|
|
+ - **A reprint's reason:** an archive whose earlier run failed kept that failure reason when a reprint completed. Each run's end now sets the archive's reason, or clears it.
|
|
|
+- **A print Bambuddy didn't start is matched to its own archive** — At its end, such a print was matched to an archive whose name merely contained its name, so finishing "Clip" could complete (and bill) a "Cable Clip" still marked as printing; `_` in a name also matched any character. It now needs the same name, allowing for the printer writing spaces as underscores, cutting long names short, or reporting the file name for a print started on its screen.
|
|
|
+- **Print start picks the print's own 3MF from the printer's folders** — When the 3MF couldn't be downloaded directly, Bambuddy searched the printer's folders and took the first file containing the print's name, so "Cube" could be archived with "Cube_large.3mf". A file named for the print is now taken, in whichever folder, and of several files that only contain the name, none.
|
|
|
+- **The A1, A1 Mini, P1 and A2L built-in calibration is not archived as a print** — These printers report their calibration run as a bare `auto_cali_for_user_param.gcode`, not under the system path other models use, so Bambuddy archived each run as a print (without a 3MF) and announced it. It is now recognised as the printer's own job. The printer kill switch still stops it on these models, since nothing tells it apart from a file given that name: switch the kill switch off to calibrate them.
|
|
|
+- **A notification channel limited to one printer gets that printer's events only** — A job added for **Any <model>** or not yet assigned, and any job waiting (for filament, a plate, a printer), were sent without a printer, so no printer filter applied: every channel limited to a printer received all of them, other printers' included. A job that waits on a printer now names it, and reaches that printer's channels; one with no printer yet goes to channels set to all printers, and a channel limited to one printer hears of it once it is assigned to or started there. A power alert from a plug linked to no printer likewise goes to channels set to all printers. Events about the whole setup (the queue finishing, stock, app messages) still reach every channel.
|
|
|
+- **Queue drying pre-dries in blocking mode** — With **Wait for drying to complete** on, a new drying cycle was refused on any printer with a pending job, scheduled ones included, so queue drying never dried anything ahead of a scheduled print. Only a job that could start now keeps a new cycle off; a printer whose job is scheduled for later is dried ahead of it, and the job still waits if the cycle has not finished by then.
|
|
|
+- **An "Any model" job waiting for a drying printer says so** — It read "Busy: X1C-01", as if that printer were printing; it now reads "Busy: X1C-01 (drying)", as a job for that printer does.
|
|
|
+- **An H2D shows the right active slot after a filament change** — An H2D reports its active slot as a bare number and names the AMS in each nozzle's own data, often in the same message. That message's AMS part was read first, against the previous message's nozzle data, so after a swap the card (and usage tracking) could keep pointing at the old slot until the printer reported its slot again. The nozzle data is now read first, and nozzle data from an earlier message no longer overrules the slot the printer just reported.
|
|
|
+- **Home Assistant "Run when printer turns off" scripts run when Bambuddy switches the printer off** — Linked scripts ran only when someone switched the printer's plug by hand. Auto-off after a print or drying (also when resumed after a restart), the plug's schedule and AI failure detection turned the printer off without them, and the schedule turned it on without its "on" scripts. They now run on all of these. "On" scripts keep running at every print start as before. For these automatic switches only a plug that powers the printer counts, the scripts run once when two plugs feed the printer, and a script whose **Enabled** toggle is off does not run (by hand either). Failure detection no longer tries to switch off a script.
|
|
|
+- **A plate check that could not run says so** — When the camera gave no image, the analysis failed or there was no calibration to compare against, the check before a print reported an empty plate, logged "Plate is empty" and told nobody. The print still goes ahead, but users who can control printers now get a warning with the reason, the log records it, and **Check Plate** shows it instead of "Plate appears empty".
|
|
|
+- **The archive recovery download gives a large 3MF time** — The retry that fetches a print's 3MF when the first download failed gave up after a flat 90 seconds, so a large file on a slow SD card never made it. Once the printer reports the file's size, the deadline follows it (up to 15 minutes), as the first download's already did.
|
|
|
+- **A broken MakerWorld download is not kept** — Only the HTTP status was checked, so a truncated file or an error page served as a success was stored as the model's 3MF. It is now refused unless it is a readable 3MF.
|
|
|
+- **An upload the printer never confirmed is checked** — When the printer's "transfer complete" reply does not arrive (an H2D can take over 30 seconds), the upload still counts, but its size is now read back on a new connection and a mismatch is logged as an error.
|
|
|
+- **The queue bulk edit says when it changed nothing** — The result was a neutral, English-only message even when no job was updated. It is now translated, and shown as an error when nothing was updated and as a warning when some jobs could not be changed.
|
|
|
+- **CallMeBot gets the phone number as written** — The phone number and API key went into the request unencoded, so "+49 151 ..." arrived without its "+".
|
|
|
+- **A plate printed only from the external spool starts without the AMS on multi-plate files too** — The queue already sends such a plate with the AMS off (#3087), but when another plate of the same file used an AMS slot, the print command turned the AMS back on and the printer refused the job, also for a job whose AMS was already switched off. The plate's own answer now stands.
|
|
|
+- **A Silk spool no longer takes a slot's Matte preset** — A spool without a slicer preset reuses the filament preset already in the slot when the material matches, to keep a calibrated preset, and so does **Configure Slot** for a preset without a filament id. That now also requires the finish to match when both name one (Basic, Matte, Silk, HF, ...); otherwise the slot gets the generic preset for the material and finish, such as Generic PLA Silk for a Silk+ spool. The generic fallback picks the finish's own generic in Spoolman mode too (Generic PLA Silk, PLA High Speed, PETG HF).
|
|
|
+- **A virtual printer for any printer of a model keeps the AMS on** — It stored the slicer's AMS on/off with the job. The slicer was looking at the virtual printer, which has no AMS, so that choice did not describe the printer the job went to. Only a virtual printer with a target printer keeps it now.
|
|
|
+- **The K-profile filament names from Bambu Cloud read a preset's id from either place** — The name lookup read a cloud preset's filament id only from the response envelope, while slicing also looked inside the preset itself; a preset with the id only there showed its raw id.
|
|
|
+- **A K-profile chosen on purpose is no longer undone when the AMS reports a change** — When a spool with a stored K-profile sat in a slot, an AMS report that changed the slot (its remaining amount, for example) set it back to the stored profile, so one picked in Bambu Studio or on the printer's screen did not last. That re-apply is gone: the existing lost-profile check puts the stored profile back when the slot has lost its selection (the default K after a reset or power cycle) while the printer is idle, and leaves a deliberately chosen profile, a chosen **Default** and the other nozzle's calibration alone.
|
|
|
+- **K-profile commands no longer change the shown nozzle size** — The printer's answer to saving, deleting or selecting a K-profile names the nozzle size of that profile, and Bambuddy took it as the installed nozzle. Only the printer's status updates the nozzle now.
|
|
|
+- **A K-profile save says when the printer did not confirm it** — A save, batch save or delete the printer did not acknowledge was reported as done. It now says the profile was sent but not confirmed (older firmware never answers), and an import or bulk delete says how many of its writes went unconfirmed. An edit on a single-nozzle printer, which removes the profile and adds it again, reads the printer's table back and fails when the edited profile is not there.
|
|
|
+- **Stopping drying says the command was sent** — The toast said "Drying stopped" as soon as the command went out; it now says "Stop command sent".
|
|
|
+- **A skip the printer refuses or never applies is logged** — Skipped objects were marked skipped the moment the command was sent. A refusal from the printer now takes them back off the skipped list and is logged with the printer's reason, and a skip the printer has not listed after a minute is logged as unconfirmed.
|
|
|
+- **The printer card shows a nozzle change, firmware update or developer-mode switch at once** — These were left out of the live updates and only reached the card with the 30-second refresh or a page reload.
|
|
|
+- **The print slot labels on AMS slots show up** — While a print runs, each AMS slot the print uses is meant to carry a small "P1 / P2" label, so a job sent to a printer with the wrong filament stands out. The labels read the print's mapping from the printer's own status, which almost never carries it, so they rarely appeared. Bambuddy already records the mapping from the print command, and the labels now use it. A print started from the printer's screen after a job that never started (refused, or cancelled before printing) no longer inherits that job's mapping, which usage tracking read as well.
|
|
|
+- **Drying no longer offers spool rotation on an AMS with filament in the feed tube** — **Rotate spool during drying** was disabled only for the tray state that means "loaded" on some firmware, so on others it stayed available and the printer refused the drying start. The slot the printer reports as loaded, and on two-nozzle printers the slot each nozzle is fed from, now count too.
|
|
|
+- **The run-out hint names the slot on two-nozzle printers with several AMS** — When a print paused for filament, the hint often said to check the printer screen, because the slot number alone does not say which AMS it is on. The AMS wired to the active nozzle now settles it; with the Filament Track Switch, where an AMS can feed either nozzle, the hint still asks.
|
|
|
+- **A regular AMS is never shown as an AMS-HT** — An AMS was taken for an AMS-HT when it reported a single tray. The unit number (128-135 for AMS-HT) now decides, on the printer card and its slot labels ("HT-B" became "AMS-B"), in the AMS Filament Backup view, print mapping, the slicer's loaded-spool list and the queue. A regular AMS that reports only some of its trays shows each one in its own slot rather than in list order.
|
|
|
+- **A print stopped by the printer kill switch is archived as cancelled** — The printer reports a stop as failed, and only stops made from Bambuddy were turned into cancelled, so every print the kill switch stopped went into the failure statistics as a failed print with no reason. It is now cancelled, with the reason **Stopped by the printer kill switch**, which the archive editor also offers.
|
|
|
+- **A queue job stopped while its printer was offline is counted as a user cancel** — Its archive stored an English sentence as the failure reason, which showed untranslated and counted as a cause of its own in the failure statistics. It now stores **User cancelled**, like every other stop; existing archives are converted on startup.
|
|
|
+- **Saving the SpoolBuddy backend config says it needs a restart** — The device writes a new backend URL or API token to its config file but keeps using the old ones until its daemon restarts, while the settings page only said "Config queued." It now says a restart applies it and offers **Restart Daemon**, which waits until the device has taken the config so the restart cannot replace it.
|
|
|
+- **A re-sliced archive reads as not printed and shows a cost** — Re-slicing an archive created a new archive marked "completed", so it appeared under **Printed** and was counted as a finished print although nothing had printed. It is now "archived" (shown as **Not printed**), like any file archived without printing, and it shows an estimated filament cost like other archives with grams.
|
|
|
+- **A reprint of a single-plate archive no longer leaves the old timelapse behind** — A timelapse attached by hand, or before Bambuddy recorded plates, was always kept as an unlinked file on a reprint, since on a multi-plate archive it can be another plate's only copy. On an archive with one plate there is no other plate, and it is now deleted with the run it belonged to.
|
|
|
+- **A GitHub backup restore checks the numbers it restores** — Grams, costs, energy, print times and spool-usage weights were copied from the backup as they were, so a damaged or hand-edited backup could put text, negative or absurd values into the totals. Anything that is not a valid number (grams at most 100000, as the archive editor allows) is left empty, and the restore summary says so. A quantity of 0 (a ruined plate) is kept instead of becoming 1.
|
|
|
+- **A print resumed after a restart keeps its plate height** — Each way Bambuddy picks up a running print (new archive, expected print, restart resume) remembered it under a different set of names. When the printer later reported a name that path had left out, the finished print was still found but lost its binding, and with it the height the plate restore uses after a restart. Every path now remembers the same names.
|
|
|
+- **An "Any model" job gets a fresh nozzle mapping on a nozzle-rack printer** — A nozzle mapping stored on a model-based job (through the API or an order's plate spec) was sent unchanged to whichever H2C picked it up, instead of the rack positions of that printer. It is now worked out for the printer that takes the job. On printers without a rack, such as the H2D, where the mapping means left and right nozzle, it is kept.
|
|
|
+- **A used weight entered for a new spool is kept** — Adding a spool (one, several with **Add copies**, or from a CSV) left its weight unlocked even when a used weight was entered, so the automatic AMS sync could overwrite a part-used spool's weight on its first pass. A used weight entered at creation now locks the spool, as editing the weight already did. A negative used weight is refused; more than the label weight is still accepted, since a mis-labelled spool really can give more.
|
|
|
+- **Spoolman's "last used" means a print used the spool** — Every AMS sync stamped *last used* on every loaded Spoolman spool, as did clearing a removed spool's location and weighing on the SpoolBuddy scale. Only print usage sets it now (through Spoolman's own usage endpoint), as in built-in inventory.
|
|
|
+- **The AMS slot K-profile picker accounts for the nozzle's flow** — On printers that report a high-flow or standard nozzle (H2-series), the picker offered and pre-selected K-profiles measured on the other kind, which are not applied when the slot is configured. They now sit under **Other**, and every profile shows its flow ([HF] / [S]), as in the spool form.
|
|
|
+- **Printer temperature history retention can be set, and no retention setting can wipe history** — How long the nozzle, bed and chamber temperature history is kept had no setting in the interface; it now sits next to the AMS history under **Settings → Filament → History Retention**. Both settings take at least one day: 0 or less was accepted through the API and made the daily cleanup delete all history, and a value stored that way is now read as one day.
|
|
|
+- **Bug reports no longer keep the reporter's email** — Every report sent from Bambuddy was also stored locally with the email it was sent with, and nothing ever read or removed it. The email still goes with the report; the local record keeps only the outcome, and emails stored before are cleared at startup.
|
|
|
+- **Unused API pieces removed and the rest documented** — The firmware upload no longer broadcasts a WebSocket progress message nothing listened to (the page polls the upload status, as before); the old route that deleted every plate-detection calibration of a printer at once is gone (references are deleted one at a time from the plate-detection dialog); `/spoolbuddy/calibration` now opens the SpoolBuddy settings page, where calibration lives. The wiki now documents `POST /archives/upload-source` for slicer post-processing scripts and `PUT /local-presets/{id}`.
|
|
|
+- **Load and Unload work on an AMS-HT** — Clicking **Load** on an AMS-HT slot was refused with "tray_id must be 0..15 …": the printer card numbered the slot like a regular AMS (unit × 4 + slot, 512 for the first AMS-HT), and the printer's commands would have turned the AMS-HT into a unit it does not have. **Unload** while the AMS-HT was the loaded tray was addressed to that same non-existent unit. Both now address the AMS-HT by its own unit number, as Bambu Studio does, and **Unload** from an AMS-HT slot's menu is aimed at that slot. Loading from the A2L's AMS-Lite now sends the target Bambu Studio sends as well.
|
|
|
+- **A second Bambuddy on the same machine no longer cuts the camera every minute** — Bambuddy's once-a-minute cleanup kills camera ffmpeg processes left behind by a crash or restart, and recognised them by their command alone. Another Bambuddy on the same host (a second install, a test instance) runs the very same command, so each one's cleanup killed the other's live camera once a minute and the view reconnected. Only an ffmpeg started by this Bambuddy, or left orphaned by an earlier run, is cleaned up now.
|
|
|
+- **A restart with a camera view open no longer logs errors** — A camera stream never ends by itself, so a restart closes it after 5 seconds. Each stream closed that way was logged as an ERROR with a full traceback ("Exception in ASGI application ... timeout graceful shutdown exceeded"), which looked like a crash in the log and in support bundles. It is now one INFO line per stream, and the "Cancel N running task(s)" line before them is INFO too; real errors are logged as before.
|
|
|
+- **Archive export includes the whole end day, and an invalid print speed is refused** — `GET /api/v1/archives/export?date_to=2026-10-15` read the date as midnight and left out everything printed that day; a date on its own now ends with that day, as in every other date filter, and a date and time with a UTC offset is converted to UTC. The Archives page was not affected, because it never sends dates. `POST /api/v1/printers/{id}/print-speed` with a mode other than 1-4 answered 500 "Failed to set print speed"; it now answers 422.
|
|
|
+- **AMS drying now applies to "Any <model>" queue jobs** — With **Wait for drying to complete** on, a job for "Any X1C" could still start on an X1C whose AMS was drying, and its drying was never stopped for the print; queue auto-drying also never dried anything for such a job. Now a model job passes over a drying printer for another of the model, and waits only while every one of them is drying. The printer it lands on stops drying like it would for a job pinned to it, and a scheduled model job has queue auto-drying dry every printer it could run on.
|
|
|
+- **Every way of adding a queue job now works like the print dialog** — Jobs added from File Manager's **Add to queue**, the webhook, a virtual printer, a pipeline run or an order's **Dispatch remaining** sent no "Job added" notification and no MQTT `queue/job_added` event; now every path sends them. Several jobs added at once are one notification per printer, such as "3 jobs (bracket, hinge, lid)", and MQTT gets one event per job. File Manager, the webhook and pipeline runs ignored the default print options (timelapse, bed levelling and the rest) and now use them. The copies an order dispatches lost their cost centre, so they were billed to none and never checked against its budget; they now keep it, and with billing on, dispatching is refused when you may not print with that cost centre or its budget cannot cover every copy. A `scheduled_time` sent to the API or webhook with a UTC offset, such as `10:00+02:00`, was stored as 10:00 UTC and the job started two hours late; the offset is now converted.
|
|
|
+- **Long prints keep their layer timelapse** — Stitching a layer timelapse was stopped after 5 minutes and its frames were then deleted, so a print with more than a few hundred layers on a Raspberry Pi lost its timelapse for good. Stitching now gets half a second per layer, from 5 minutes up to an hour. If ffmpeg fails anyway, or Bambuddy stops while it runs, the frames are kept for about 24 hours with an ffmpeg command to make the video by hand; when ffmpeg fails, the log names the folder. Without ffmpeg the frames are still deleted.
|
|
|
+- **An in-app update that hangs no longer blocks further updates** — A stalled `git fetch`, `pip install` or `npm install` left the update showing "downloading" or "installing" forever, and every new attempt was refused with "Update already in progress" until Bambuddy was restarted. Each step now has a time limit (5 minutes for git, 15 for pip and npm); a git or pip step that runs out of time ends the update with an error, and checking for updates again lets you retry. When pip or `git reset` is the one that ran out of time the new code may already be in place, so run the update again before restarting. A frontend build that runs out of time keeps the files shipped with the release.
|
|
|
+- **The plate picker downloads a printer's 3MF once** — Opening a 3MF in the printer's file browser downloaded the whole file again for the plate list and for every plate thumbnail, all at once: a ten-plate 60 MB file meant eleven full downloads, slow on any printer and slower still on an A1. The plate list, plate thumbnails and G-code preview now share one download and keep it for a minute (the 3D view still fetches its own copy).
|
|
|
+- **Printer file downloads tell a missing file from a failed one, and big files get the time they need** — Downloading a file from the printer, and the file browser's previews (G-code, plates, plate thumbnails), treated every failure as "File not found", including a slow transfer of a file just listed. Bambuddy now answers "not found" only when the printer says the file is gone, and otherwise that the download failed or took too long; the download's message and the timelapse messages show which. A file download was also held to 10 minutes whatever its size, so a 250 MB camera recording on a slow link always failed, and the previews to 5 minutes; both may now take as long as the file's size calls for, up to the 30-minute limit. A timelapse that could not be downloaded is reported the same way instead of as a server error.
|
|
|
+- **A timed-out printer download no longer runs on beside its retry** — When a timelapse download ran out of time, it kept going in the background while the next attempt opened a second connection to the printer. The transfer is now stopped first, and a download that already had the time its size allows is not retried.
|
|
|
+- **Large STL files no longer freeze Bambuddy while their thumbnail is made** — Uploading an STL, extracting a ZIP of STLs, generating missing thumbnails and scanning an external folder rendered each thumbnail in a way that held up everything else, so a high-poly model froze the web interface and printer updates for seconds or longer. Thumbnails are now made in the background, one at a time.
|
|
|
+- **A frozen external RTSP camera is reconnected** — When an external RTSP camera stops sending, `ffmpeg` can keep repeating the last picture, so the live view and `/camera/status` kept showing a picture that had stopped changing, and so did anything reusing the live frame (timelapse frames, finish photos, Obico). Like the printer's own camera, an external RTSP camera that sends the same picture for 20 seconds is now reconnected. A camera that is simply slow to change (a slow source, or a smart codec on a static night scene) is restarted at most once, after which Bambuddy waits three times the longest gap it has seen, up to 5 minutes; a picture that really does not change is re-checked every 5 minutes.
|
|
|
+- **A USB camera can be watched from several places at once** — Every viewer of an external camera opened its own connection to it. A USB camera can only be read by one program at a time, so opening the Camera page while a Cam Wall tile (or another browser) was already showing it gave a blank picture. External cameras now share one connection per printer, like the printer's own camera, which also means one connection per MJPEG or snapshot camera and one `ffmpeg` per RTSP or USB camera instead of one per viewer. As with the printer's own camera, the viewer who opens the connection sets its frame rate: with a Cam Wall tile already showing the camera, the Camera page gets the tile's 8 frames per second until everyone has closed it.
|
|
|
+- **Camera snapshots and the camera test no longer fail while someone watches the live view** — A USB camera can be read by one program at a time, so while a viewer was watching, every snapshot Home Assistant or the camera wall fetched from an external camera failed, and the external camera's **Test** button failed too. Snapshots now return the live view's frame, and the Test button reports the camera working from that frame. While the live view is still starting, a snapshot asks the caller to try again a moment later instead of taking the camera from the viewer (the camera wall and the camera page now retry once on their own); this now applies to the printer's own camera as well. An external camera with its own snapshot URL is still fetched from that URL.
|
|
|
+- **USB camera photos are no longer killed mid-capture** — A cleanup job that runs every minute stops leftover camera processes, and it did not know about the one-shot USB capture behind finish photos, timelapse frames and snapshots, so it sometimes killed one in progress. It also stopped any other program's `ffmpeg` reading a USB camera on the same machine; it now only stops Bambuddy's own.
|
|
|
+- **The camera Test button shows the resolution properly** — It read "Camera connected640x480"; it now reads "Camera connected (640x480)".
|
|
|
+- **Admins can remove a locked-out user's 2FA from Edit user** — The wiki described this, but Edit user had no control for it, so a user who lost their authenticator stayed locked out unless an admin called the API by hand. **Settings → Users → Edit user** now has **Disable 2FA**, which asks for the admin's own password (admins who sign in through LDAP or SSO usually have none and are not asked) and says whether the user had any 2FA to remove.
|
|
|
+- **The AMS drying button knows a printer cannot dry from the first page load** — After a page load and every 30-second refresh, the printer card didn't know the drying status or why drying wasn't possible, so a printer that cannot dry showed an enabled Start button until the next live update (the printer still refused the start). The page load and the live updates now describe AMS units and external spools in exactly the same way. A slot the firmware reports as only its number now also counts as empty from the first load, and the AMS temperature no longer switches between "24°C" and "24.0°C".
|
|
|
+- **The filament override picker keeps both external spools of a dual-nozzle printer without an AMS apart** — On an H2D or X2D feeding only from its external spools, identical spools on the left and right merged into one entry that was offered for both nozzles. The picker now uses the same dual-nozzle check as the print scheduler, so each external spool is offered only for the nozzle it feeds.
|
|
|
+- **A2L: filament used from the AMS Lite is charged to the right spool** — The printer names an AMS Lite tray by its local slot 1-4, and Bambuddy kept that number when it recorded which trays a print used, so the AMS Lite spool was never charged. With a regular AMS beside the AMS Lite, the usage went to that AMS's slot of the same number instead. A virtual printer with **Save AMS mapping** on stored the same local number for queued prints and their archives; with a regular AMS present, such a print was sent to that AMS instead of the AMS Lite.
|
|
|
+- **Proxy virtual printers no longer restart when another virtual printer is saved** — Creating, editing or deleting any virtual printer restarted every running proxy-mode virtual printer, dropping the slicer's connection and any upload in progress. They now restart only when their own settings change. Changing a printer's IP now restarts the proxy virtual printers aimed at it on the new address; before, they kept forwarding to the old one until a virtual printer was saved. Renaming a virtual printer now restarts it, in every mode, so the slicer sees the new name; before, a running virtual printer kept advertising its old name until Bambuddy restarted.
|
|
|
+- **Prints queued through a virtual printer keep the slicer's nozzle choices** —
|
|
|
+ - **Nozzle offset calibration** from the slicer, or the default from Settings → Workflow, is used. It always ran on "Auto".
|
|
|
+ - **H2C rack nozzles** picked in the slicer are kept only by a virtual printer bound to one printer. A virtual printer for any H2C sent those positions to whichever H2C took the job, whatever that printer's rack held; the dispatch now picks them from that printer's rack.
|
|
|
+ - **A job the queue moves to another printer**, or releases back to the pool, no longer takes the old printer's nozzle positions along.
|
|
|
+ - **AMS on or off** from the slicer is used when the slicer's options arrive in time; it was used only when they arrived late.
|
|
|
+ - **A slicer AMS mapping that arrives late** is no longer put on a plate of a multi-plate send that it cannot reach; that plate gets a computed mapping, as when the options arrive in time.
|
|
|
+- **Settings that were not saved or not used** —
|
|
|
+ - **Max copies per run** (Workflow) and the Docker compose directory used in the update instructions are saved. They changed on screen and were never sent, so a 100-copy pipeline run stayed refused at the old limit of 50.
|
|
|
+ - **Default printer** (General) is now selected in the print dialog when it opens, unless the file was sliced for another model or the job goes to a model rather than a printer. The setting was saved and never used.
|
|
|
+ - **Auto-archive prints** (General) now switches archiving off for every printer, for prints started outside Bambuddy. While it is on, each printer's own switch decides. Before, only the printer's switch was read. A printer's own calibration runs no longer send a "print started" notification while archiving is off.
|
|
|
+ - **Create Archive Entry When Printing** (File Manager) is removed. It was saved and never used: printing from the File Manager always creates an archive entry.
|
|
|
+- **A spool Spoolman creates from the AMS is the size of the roll** — a 250 g refill matched to an existing 1000 g filament was created as a 1000 g spool with 250 g left, showing as 75% used.
|
|
|
+- **An unregistered SpoolBuddy that is still running registers again on its next heartbeat**, as the documentation says. It stayed missing until the daemon or the Pi restarted, and the refused heartbeats filled its send buffer, which then held back everything queued behind them.
|
|
|
+- **SpoolBuddy installer, full mode, installed under /home** — the Bambuddy service it writes now starts. The service hid /home from itself and failed with 203/EXEC, as the main installer's did before #1685.
|
|
|
+- **Spoolman costs** —
|
|
|
+ - A failed or stopped print in Spoolman mode is logged in Print History with what its Spoolman spools cost for the filament it used, as with the built-in inventory. It was logged with no cost.
|
|
|
+ - The estimated cost of a queued library file is priced from the Spoolman spools in the trays it maps to. Every slot was priced at the default filament cost.
|
|
|
+- **Spoolman usage** —
|
|
|
+ - A failed or stopped single-filament print that switched to the AMS backup spool charges each Spoolman spool its share, as a completed print and the built-in inventory do. All of it went to the spool that ran out.
|
|
|
+ - Two copies of the same archive printing at once on two printers are both charged to Spoolman. The second print's start failed on the shared archive and that print was never deducted.
|
|
|
+ - A filament slot the print does not feed is no longer charged to the external spool, or to whichever AMS tray sits at that position, as with the built-in inventory.
|
|
|
+- **Spoolman slots** —
|
|
|
+ - Assigning or linking a Spoolman spool to an AMS slot is now checked against what the printer reports back, as with the built-in inventory. A printer that ignored the new filament showed no "not confirmed" notice, and later usage went to the wrong spool.
|
|
|
+ - Configure AMS Slot opens with the slicer preset set on the Spoolman spool when no per-printer-model preset applies, and the slot's K profile is matched under it. Before, the dialog opened with no preset and the K profile was matched against the tray's own filament.
|
|
|
+- **Spool slots** —
|
|
|
+ - Archiving a spool, one or in bulk, unassigns it from its AMS slots, with the built-in inventory and with Spoolman, and slots still held by spools archived earlier are released on update. The slot kept showing the archived spool and charged it the next print's usage. A Bambu Lab spool's tag still in the AMS after archiving is not given to another spool or turned into a new one. With Spoolman, a spool feeding a running print keeps its slot until the print's usage is reported.
|
|
|
+ - Deleting a Spoolman spool removes its slot assignments too, and a spool archived or deleted in Spoolman itself releases its slots after the next print on that printer. The slot kept pointing at a spool Spoolman no longer had.
|
|
|
+ - Printer cards and slot lists update right away when a Spoolman spool is archived, deleted or unassigned.
|
|
|
+ - **Sync** and **Sync all** in Settings → Spoolman no longer unlink a slot that reads empty while its printer is printing (a runout), or one the AMS still reports as holding a spool.
|
|
|
+ - The spool list in the Assign dialog updates right after a Spoolman assignment, so the spool just assigned is no longer offered for a second slot.
|
|
|
+ - **Restore** on an archived spool inside an expanded group row of the inventory table works. It did nothing.
|
|
|
+- **Spool weights from the AMS** —
|
|
|
+ - **Sync Weights from AMS** and **Sync Spoolman Weights from AMS** leave the spools of a printer that is printing alone, and say how many. Synced halfway through a print, that print's usage was counted twice at completion.
|
|
|
+ - A spool weighed on the SpoolBuddy scale is locked like one whose weight was set by hand, so the automatic AMS remain% sync no longer overwrites the measured weight.
|
|
|
+ - **Sync Weights from AMS** now also updates locked spools and releases their lock. Nothing else could release it, so a spool whose weight was once set by hand was skipped by the sync for good.
|
|
|
+- **SpoolBuddy** —
|
|
|
+ - The Write Tag page no longer stays on "Writing tag..." when the tag was written but could not be linked to the spool. It shows why: Spoolman not set up, linking in Spoolman failed, or the spool no longer exists.
|
|
|
+ - A scanned tag that cannot be looked up because Spoolman is unavailable, or because the lookup failed, now says so in the kiosk status bar for a few seconds. Before, nothing happened.
|
|
|
+ - With Spoolman switched on and its URL refused by Bambuddy's address check, tag lookups, tag writes and scale weights no longer fall back to the built-in inventory, where they found or changed a different spool. They report Spoolman as unavailable, and Write Tag says so. Switched on without a URL still means the built-in inventory, as everywhere else.
|
|
|
+ - Removing a SpoolBuddy device updates the device list right away.
|
|
|
+- **Archive status in Edit Archive** — the list offers Completed, Failed, Aborted and Cancelled. Choosing "Cancelled" used to save "aborted", which the statistics count as a failure, and a cancelled archive opened showing "Completed". An archive can no longer be set to "Printing" by hand, which had it closed again at the next reconnect as a missed print, with notifications and an extra Print Log entry. The archive card labels an aborted print "aborted" and now also marks a cancelled one, which looked like a normal completed print. Moving an aborted print to Cancelled keeps its recorded reason; marking a cancelled print Completed clears it.
|
|
|
+- **Archive costs** —
|
|
|
+ - **Recalculate Costs** and **Rescan** price one print of the archive. An archive printed three times was given the cost of all three. Usage recorded without a price is priced at the default filament cost instead of counting as free. Print History and the statistics still count every print with its own cost.
|
|
|
+ - Editing an archive's filament weight now reprices it, on the card and on the print it was copied to.
|
|
|
+ - A spool without a price is charged at the default filament cost when the print completes. With the default never saved, completion priced it at nothing while the Settings page showed 25 per kg, and Recalculate later used 25.
|
|
|
+- **A print of a file whose last print was never closed gets its own archive** — an archive left on "printing" because its end was missed used to take over the next print of the same file for two hours, so that print got no history entry, timelapse or cost estimate of its own. A different job id from the printer now marks it as a new print, except when it is Bambuddy's own reprint of that archive.
|
|
|
+- **Filament of a print that spans a Bambuddy restart is no longer counted twice** — when the restart found no saved print session, or one of another print, the AMS remaining-% sync raised the spool's used weight during the print, and completion then added the print's full usage on top.
|
|
|
+- **A print whose dispatch failed no longer leaves its cost centre behind** — the next print of the same archive without a cost centre of its own was charged to it, instead of the user or the archive's cost centre.
|
|
|
+- **Which plate of a multi-plate file printed** —
|
|
|
+ - Print History logs the filament and cost of the plate that printed. A reprint of another plate of the same file used to log the first plate's estimate.
|
|
|
+ - Filament usage after a Bambuddy restart mid-print is charged for the plate the queue job printed, not for the plate of the archive's first print.
|
|
|
+ - A print started outside Bambuddy records its plate on the archive when the printer reports it, so reprints, usage and Print History use that plate instead of the first one or the whole file.
|
|
|
+ - The archive's thumbnail and details describe the plate Bambuddy sent, also when the printer reports only the file name or names another plate.
|
|
|
+- **Reprints and their archives** —
|
|
|
+ - A reprint that completes no longer keeps showing why the previous run of the same archive failed (for example a clogged nozzle).
|
|
|
+ - A queued job whose archive was deleted before it started is now archived anew when it prints, on a printer that archives its prints. Before, the print ran with no archive, no start notification and no usage tracking.
|
|
|
+ - A timelapse deleted while it was being converted to MP4, or replaced by a reprint's in the meantime, no longer comes back when the conversion finishes.
|
|
|
+- **Print dialog fixes** —
|
|
|
+ - **Print Anyway** in a print to several printers now only waves through the printers whose filament warning it answered. Before, it went onto every selected printer, so one that was never checked also skipped the scheduler's filament checks.
|
|
|
+ - A job with printer alternatives whose plate dropdown was left untouched showed **Plate 1** but was queued with no plate, so it asked for every plate's filaments and the whole file's print time and could find no printer.
|
|
|
+ - On H2D and H2C, the nozzle shown for each filament now comes from the chosen plate, not from all plates of the file, which could match trays on the wrong nozzle.
|
|
|
+ - A sliced 3MF named without `.gcode` (a plate exported from Bambu Studio) can now be dropped on a printer card or picked in **Upload for Print**. An unsliced 3MF is refused with a message saying so.
|
|
|
+- **Orders and groupings show as finished however their last job ended** — An order or grouping was only re-checked when a print completed, an item was deleted or one was edited. A grouping whose last job was cancelled or stopped, or whose jobs were cancelled because their library file was deleted, an order whose last run was closed by the stuck-print cleanup, and one whose last open job failed before it reached the printer or was deleted, stayed **active** until the next restart. They are now re-checked on every way a job ends.
|
|
|
+- **A job cancelled while it is being sent stays cancelled** — Cancelling a queued job during its upload (the edit dialog even asks you to cancel a job that is being sent) was overwritten when the upload then failed: the job turned into a failure, a failure notification went out, and the next job that needs the previous print to have succeeded was skipped. A failure is now only recorded for a job that is still waiting or printing, and the upload toast of a cancelled job closes instead of spinning. Likewise, a job stopped just as its printer turned busy is no longer put back in the queue, and a stopped job whose nozzle rack pick no longer fit no longer leaves its file on the printer.
|
|
|
+- **"Only if the previous print succeeded" looks at the right printer** — For a job queued for any printer of a model, the check ran before a printer was chosen, against jobs with no printer, so it nearly always let the job go, even onto a printer whose last print had failed; when it did trigger, the job was skipped for good. It now checks the printer it is about to use, passes over one whose last print failed and takes another of the model, and waits if every free one failed last. The queue then shows a **Resume after failure** banner for that model, which clears the gate on its printers, and the "job waiting" notification also goes out when a job that waited for a busy printer comes to wait on such a failure. On PostgreSQL, a job cancelled without an end time (cancelling an order, a pipeline or a kept order run) also counted as the latest print and hid a real failure before it; such cancels now record their end time, and the check no longer ranks a missing one first.
|
|
|
+- **A queued job moved to another printer no longer keeps the old printer's settings** — A job's AMS tray mapping, **Print Anyway** and nozzle choice are worked out for one printer. Moving jobs to another printer (bulk edit, or editing one job) kept them, so a job could print from the slot that held the right material on the old printer but another colour on the new one, and a **Print Anyway** given for the old printer skipped the new printer's filament checks. **Dispatch remaining** of an order also pinned every copy of a job queued for any printer of a model to the printer its latest run happened to land on, with that printer's settings, so the other printers of that model stayed idle. These settings now stay with the old printer and are worked out again for the new one; plate, AMS use, filament overrides and print options are kept. Bulk edit leaves jobs with printer alternatives alone, as editing one does, and a job it moves to a printer no longer also waits for a model.
|
|
|
+- **Pages work for users without the settings permission** — Users in a group without **settings:read** could not read the install settings, so pages that need a few of them fell back to defaults. With billing on, the print dialog hid the cost centre picker and the print was then refused for lacking one. The print dialog also lost its print option and stagger defaults, currency and filament preference. The queue showed the wrong order with shortest job first on, the File Manager lost **Slice**, filament warnings an administrator had turned off still appeared, adding an unknown spool from its tag put it in the built-in inventory even in Spoolman mode, and the theme, currency and forecast lead time fell back to defaults. These settings now reach every signed-in user, without the credentials **settings:read** also shows; G-code snippets are only reported as set or not.
|
|
|
+- **SpoolBuddy writes the Spoolman spool to the tag in Spoolman mode** — Built-in spools are kept after switching to Spoolman. When one of them had the same id as the Spoolman spool being written, SpoolBuddy wrote the built-in spool's material, colour and weight to the tag and linked the tag to that hidden spool, while reporting success. The Spoolman spool never got the tag, so scanning it later found nothing. Writing a tag now uses only the inventory in use.
|
|
|
+- **Budget held by prints that ended is released** — Before a print without a user (authentication off, or its user deleted while it printed) was charged to its cost centre, it kept its budget reservation for good, so the cost centre showed that much less budget left. At the next start, reservations of prints that have finished or were removed from the queue are released. Only the reserved amount is freed; charges already booked don't change.
|
|
|
+- **A pipeline run remembers how each copy ended** — A copy a slicer pipeline sent to the queue only showed its outcome while its queue entry existed. Once the entry was deleted (from the queue page, with its archive, or with its user) the run could no longer tell a printed copy from one that never ran: it stayed **dispatching** forever, and **Retry failed** could not tell what to retry. Each copy now keeps its final state (completed, failed or cancelled) when its queue entry ends, and removing a copy that hadn't printed yet counts as cancelled. Copies whose entry was already gone before this update show as **unknown**: never retried, and counted as finished, so the run completes.
|
|
|
+- **Deleting a spool keeps its usage history, and a new spool no longer takes over a deleted one's** — On PostgreSQL, deleting a spool deleted its usage history, so **Recalculate costs** replaced the real spool cost of its prints with an estimate. On SQLite the history stayed, but the next spool created could get the deleted spool's number and then showed its prints and consumption as its own. The usage history now stays on both databases without a spool, the prints keep their spool cost, and spool numbers are never given out again on SQLite. A Git backup restores such history as well. Supplier and material statistics leave out usage of deleted spools, as there is no spool left to count it for.
|
|
|
+- **Archives a spool was used for can be deleted on PostgreSQL** — With PostgreSQL, deleting an archive for good (deleting it with **purge from statistics**, the automatic archive purge with that option, or deleting a printer together with its archives) failed with a server error when a spool from the inventory had been used for that print. The spool's usage record now stays and only loses its link to the deleted archive, on both databases; on SQLite, usage records still pointing at an archive deleted earlier are cleared of that link at the next start. **Recalculate costs** no longer charges usage records without an archive link to archives by their print name: that added the spool cost of a deleted print, of a print made with archiving off, or of every earlier run to each archive with the same name. Archives without linked usage get the estimate from the filament price instead, as they already did when no such record existed (usage recorded before 0.2.1, or with archiving off).
|
|
|
+- **An id of 0 no longer slips past the checks when queueing, batching or creating a project** — Adding to the queue with an archive id of `0` next to a library file, creating a batch with an archive or library file id of `0`, and creating a project with parent `0` skipped the check that the archive, file or project exists and saved `0` as its id: a server error on PostgreSQL, a link to nothing on SQLite. `0` now means "none" there, as it already did when editing a project. Creating a batch also checks that its archive or library file exists, and refuses an unknown one.
|
|
|
+- **Archiving a virtual-printer upload into a project that does not exist is refused up front** — The project chosen when archiving a pending upload was saved without checking it. On PostgreSQL an unknown project failed with a server error after the file had already been archived; on SQLite the archive was saved with a broken project link. The project is now checked first and an unknown one refused with "Project not found", before anything is archived.
|
|
|
+- **A project's bill of materials no longer shows archives from printers outside the user's groups** — A BOM line can be linked to an archive through the API, and the BOM answered with that archive's name whichever printer it came from. A member of a group limited to some printers could read the name of any archive by linking a line to it, and everyone reading the project saw the names of archives from printers they have no access to. A link to an archive that does not exist was saved too (a server error on PostgreSQL). A BOM line now links only to an archive the user can see, and shows a linked archive only to users who can see it.
|
|
|
+- **Adding archives or queue jobs to a project keeps to the user's own printers and prints** — Adding archives to a project (**Assign to project**), removing them, and adding queue jobs took any archive or job by its id. A member of a group limited to some printers could link another team's archives and jobs to a project or unlink them, and the reply's count told them which hidden archives exist; any project editor could also relink other users' prints. These now change only the archives and jobs the user could edit directly: on their printers, and only their own unless they may edit everyone's (Operators: their own). Others are skipped like missing ones, and **Assign to project** now reports how many were actually added. An API key with project access still needs no archive or queue access for this, but changes only the archives and jobs of its printers that its owner could change.
|
|
|
+- **Editing an archive can no longer move it to a printer outside the user's groups** — A member of a group limited to some printers could move an archive of one of their printers to any other printer through the archive edit, into the history of a team they have no access to, although uploading an archive to such a printer was already refused. Any edit could also link an archive to a printer or project that does not exist (a server error on PostgreSQL, a broken link on SQLite). Such an edit is now refused with "Printer not found" or "Project not found". Moving an archive between the user's own printers, and removing its printer or project, work as before.
|
|
|
+- **A smart plug or virtual printer can no longer be linked to printer 0** — Adding or editing a smart plug and adding a virtual printer skipped the "Printer not found" check when the printer id was `0`. On PostgreSQL the save then failed with a server error; on SQLite the plug or virtual printer was saved linked to a printer that does not exist. Such a request is now refused with "Printer not found", as for any other unknown printer.
|
|
|
+- **No more firmware-check errors in the log while the internet is down** — Without an internet connection, every firmware check of every printer logged a full error for each Bambu Lab page it tried (measured: 12 errors and 9 warnings for three printers checked three times). The outage is now logged once per Bambu Lab site as a warning, the checks keep retrying quietly, and a line in the log says when the site is reachable again.
|
|
|
+- **Bambu Cloud being unreachable is reported as such** — When Bambu Cloud failed (no internet, a blocked or failing connection, an error or an error page such as a Cloudflare challenge), the cloud preset, device and firmware views answered with a server error (500), and pasting an access token answered as if the token were wrong (401). Signing in or entering the email code without a connection to Bambu Cloud also answered 401, as if the password or code were wrong. These now answer 502 (Bambu Cloud unreachable) with the cause, as the preset lookups already did; a pasted token counts as invalid only when Bambu Cloud itself refuses it. An expired session is answered as such (401, sign in again) instead of with a server error.
|
|
|
+- **Editing a smart plug can no longer save a plug that cannot be read back** — Adding a plug checks its values: a Tasmota plug needs an IPv4 address, a Home Assistant plug an entity of the right kind, an MQTT plug a topic, a REST plug an ON or OFF URL, and the name must not be empty. Editing one did not, so clearing such a field, switching the type without filling in the new one, or typing a hostname as a Tasmota address saved a plug that the smart plug list, the plug itself and the edit all failed on with a server error until it was fixed in the database. Such an edit is now refused with the same message as when adding a plug, and the plug stays as it was. Switching a plug to Tasmota on a printer that already has a Tasmota plug is refused as well (adding one already was); before, it gave the printer a second one, and adding a plug to that printer then failed with a server error.
|
|
|
+- **Creating or importing a project gives it a priority and status the project editor accepts** — Importing a project from a file without a priority gave it the priority `0`: on PostgreSQL the import failed with a server error, on SQLite the project got a priority the project editor itself refuses. Creating a project through the API stored any priority, and importing one any status, the same way (a value longer than 20 characters failed with a server error on PostgreSQL). An imported project now keeps a valid priority (low, normal, high or urgent) and status (active, completed or archived) and gets **normal** and **active** otherwise, from a file and from the JSON import alike. Creating a project with an unknown priority is refused with "Invalid priority", as editing one already was. Such projects could not be edited at all (every save, even of the name alone, was refused as an invalid priority or status); existing ones get **normal** and **active** at the next start. Restoring a backup from such an older SQLite install onto PostgreSQL no longer fails on them either: they get the same values while restoring.
|
|
|
+- **The "Password Reset" email template is used again** — Since password resets send a link instead of a new password, the reset email (self-service and admin) was always the built-in one: editing the "Password Reset" template under Notifications changed nothing, and its default text still promised a new password. The reset email is now built from that template, and Bambuddy adds the reset link at the end of it (and as a button); the template itself cannot place or contain the link. `{password}` and `{login_url}` are gone from this template. The default text is updated on upgrade unless you edited it; if you did, reword it for a link instead of a new password.
|
|
|
+- **A cost centre is charged for a print whose user was deleted while it ran** — When a user was deleted during their print, the print was never charged: the cost centre's ledger missed its cost, and its budget reservation stayed active for good, holding back that much of the budget. The print is now charged to its cost centre, also to the deleted user's own private one, which their personal prints go through, and its reservation is settled. A queue print is charged under the user who queued it, never under the owner of the archive it reprints. Ledger entries without a user now read "No user" instead of "Deleted user": with login turned off, prints on a cost centre are now charged too and never had a user. Deleting a user together with their items now also releases the budget their queued prints still held; a print of theirs still running is kept until it ends (without a user) and billed to its cost centre like any other. Only a print with no cost centre at all (started while billing was off) has nobody left to charge.
|
|
|
+- **Deleting a printer no longer deletes an archive of a print starting at that moment (PostgreSQL)** — If a queue or reprint print started on the printer while it was being deleted, the delete could take the print's archive with it, even with "keep archives" and even when the archive belonged to another printer or a virtual printer queue. The delete now locks the printer while it runs, so such a print start waits and is turned away, and its archive stays as it was; this is logged as a print start for a deleted printer, not as an error, and sends no start notification. An archive that could not be written no longer leaves its copied 3MF behind in the archive folder.
|
|
|
+- **A printer deleted while its print was starting no longer gets an archive or tracking** — When a print starts, Bambuddy downloads its 3MF from the printer, which can take minutes. A printer deleted during that download still got an archive afterwards: on SQLite one that showed "printing" forever, since the deleted printer would never report the end, along with a layer timelapse and tracking for it; on PostgreSQL the archive was refused and an error logged. Bambuddy now checks that the printer still exists once the download is done, and again once the archive is written; a queue or reprint print starting just as its printer is deleted is no longer attached to that printer either. Archives the delete was told to keep stay as they are. A queue print dispatched to a printer deleted during its upload no longer records its owner for that printer either.
|
|
|
+- **Closing one camera view no longer cuts off the others** — With an external RTSP or USB camera, closing one view of a printer (a Cam Wall tile turning to snapshot, the Camera page, the camera window's refresh) stopped that printer's camera stream for every other open view too, which then froze until it reconnected. Each view now names itself on its stream and its stop: the stop ends that view's own stream (at once, as before, so a USB camera is released when its view closes) and any stream that has stopped sending pictures, and leaves the other views' streams running.
|
|
|
+- **Closing or minimizing a camera view now ends its stream** — The camera window on the Printers page left its stream open when minimized (one more open connection per minimize and expand), and both it and the Camera page closed only the first picture they had shown: after a reconnect or a restart, closing them left the current stream running. An open stream keeps the printer's camera connection busy, keeps the chamber light on when "Turn on chamber light for camera" is on for that printer, and made the "stop camera" request on closing do nothing. Every picture a camera view drops now ends its stream. A reconnect also no longer counts one extra failed attempt toward the reconnect limit.
|
|
|
+- **Cam Wall tiles no longer freeze on their last frame** — When the server ended a live stream (the camera dropped, or a brief database hiccup during the stream's 30-second access check), browsers report nothing, so a Cam Wall tile kept showing its last frame until the page was reloaded. Live tiles now reconnect their stream every minute while the page is visible, so a camera that comes back shows a picture again within a minute. If the camera stays down, the tile shows "No signal" (a printer's built-in camera keeps its last picture until Bambuddy stops retrying it). A tile that leaves live mode (scrolled away, paused) now closes its stream too, instead of leaving the connection open. The streaming overlay, which already renewed its camera every minute, now closes the old connection first: a browser source left running for hours piled up one open camera connection per minute. A single failed access check no longer ends a stream either: it is checked again after 5 seconds, and only a second failure in a row ends it.
|
|
|
+- **SQLite no longer gives a deleted user's, API key's, printer's or archive's number to a new one** — SQLite reuses the highest freed id, so whatever still referred to a deleted user, API key, printer, print archive or camera token could end up referring to the next one created. Earlier fixes cleaned up after each such case; these tables now never reuse an id at all. Existing SQLite databases are converted once at start (the rows, columns and indexes are kept), and ids still referred to (a deleted printer's print history, for example) are skipped. PostgreSQL never reused ids itself; a SQLite backup restored on PostgreSQL (including one restored before this release), or a PostgreSQL backup restored on SQLite, now keeps those ids unused too.
|
|
|
+- **Taking away live updates or camera access through a group now ends them** — Only creating a dashboard's live connection or a camera link checked the permission for it. When an admin removed that permission from a group, removed a user from the group that granted it, or turned off an API key's status access, a dashboard already open kept receiving live updates and could reconnect for up to an hour, and an open camera view kept running, its link opened new views and snapshots, and a Cam Wall or Home Assistant camera token kept working until it expired. Those connections and views are now closed and the links refused once the permission is gone; the dashboard falls back to refreshing on its own as it does for users without live updates.
|
|
|
+- **An open camera view stops once the viewer may no longer watch** — A live camera stream checked its link once, when it started, and then ran for as long as the tab stayed open. After a password change or reset, removing someone's two-factor authentication, deactivating a user, a backup restore, disabling or expiring an API key, revoking a Cam Wall or overlay token, a group change that takes the printer away, or turning authentication on, a view already open kept showing live video. Streams now check every 30 seconds that whoever opened them may still watch that printer, and end if not. A view that is still allowed keeps running, including past the hour its link is valid for.
|
|
|
+- **A tab whose live connection was closed by the server now recovers instead of freezing** — When the server closes the live-update connection because of sign-in (your session ended, authentication was just turned on, or a temporary check failed), the page used to stop updating and stay on screen until reloaded. It now re-checks your sign-in: a tab whose session ended goes to the login page, and one that is still signed in reconnects. While its connection keeps being refused or can't be made it waits longer between attempts, up to five minutes when the server closes an accepted connection again and up to 30 seconds otherwise, instead of retrying every three seconds.
|
|
|
+- **Signing a user out everywhere now also closes their open dashboards** — A dashboard's live connection is checked only when it connects. Changing your password, a password reset link, an admin removing someone's two-factor authentication, deactivating a user and restoring a backup all make earlier sign-ins invalid, but a dashboard already open on the account, possibly someone else's, kept receiving live updates until it disconnected, and the link it connected with stayed valid for an hour to connect again. Those live dashboard connections are now closed and their links removed; your own tab reconnects after signing in again. Turning authentication on likewise closes the dashboards opened while it was off, which kept every printer, and disabling an API key, or one expiring, closes the dashboards it opened.
|
|
|
+- **A deleted API key's open connections and tokens no longer pass to the next key** — SQLite gives a deleted key's id to the next key created. Live dashboards opened with the key stayed connected, and the camera and WebSocket links it created stayed valid for up to an hour; both were tied to the key by id and took the new key's printers. Deleting a key, or a user with keys, now closes those connections and removes the links, and links of keys deleted before are removed at start.
|
|
|
+- **A printer added after a delete no longer takes over the deleted printer's print, notifications, camera and plate references** — SQLite gives a deleted printer's id to the next printer created, and Bambuddy kept much of this under that id until the next restart.
|
|
|
+ - **Prints and billing:** the new printer took over the owner of a print still running when the old one was deleted. Its prints were treated as started from Bambuddy, so the kill switch let them run, and its first finished print was credited to that user. A job sent to the old printer that hadn't started yet could do the same for a print with the same file name. A new printer that was already printing when added could also take over the old print's filament tracking, charging its filament to the deleted printer's spools.
|
|
|
+ - **Gates and notifications:** the plate-clear prompt could block the queue on the new printer. Pending "bed cooled" and offline notifications fired under its name, and the first print's milestone notifications could be skipped.
|
|
|
+ - **First connect:** the steps that run when a printer connects, such as reading its K-profiles, were skipped.
|
|
|
+ - **Camera and detection:** a viewer could get the old printer's live picture for a few seconds, a layer timelapse kept recording from the old camera, and Obico failure detection showed the old verdict.
|
|
|
+ - **Plate detection:** the old printer's reference pictures of its empty plate stayed, so the new printer showed as calibrated, showed those pictures and compared its plate against them. They are now removed with the printer, and when a printer is added under an id that still has some from an earlier delete.
|
|
|
+- **SQLite: deleting archives, library files, queue jobs, batches or projects no longer leaves references to them behind** — SQLite doesn't apply the database's "when this row is deleted, clear or delete what points at it" rules, and it gives the highest freed id to the next row created. So a queue job could end up printing a different archive or library file than the one it was made for, a new project could show a deleted project's files and folders, and archives kept the id of a deleted library file (a new file then inherited them in project progress). Every delete of these now applies the rules the way PostgreSQL does, and every start repairs what earlier deletes left behind: queue jobs of a deleted archive are removed, as deleting an archive already does, while those of a library file that is gone are cancelled or kept as finished, as a library delete does. Deleting a user now also clears their id in the print log, so those runs show under "no user". Deleting a printer together with its archives, and the archive purge, now leave an archive alone while one of its queue jobs is printing (the printer delete answers that it has to wait), as deleting a single archive already did. Archives stored before archive search existed are now added to the search index once; until then, editing such an archive could fail with "database disk image is malformed". PostgreSQL installs aren't affected, apart from the printing check.
|
|
|
+- **API: `null` for a field that can't be empty no longer causes a server error** — On most PATCH/PUT routes, `null` for a field leaves it unchanged. About a dozen routes (printers, smart plugs, queue items and bulk edits, archives, filaments, spools, notification providers, GitHub backup, Home Assistant sensors, maintenance, external links) wrote it to the database instead and failed with a server error when the field can't be empty, such as a name or a switch. They now leave it unchanged too, and the rest of the request is applied. `null` for a field that may be empty, such as a printer's location, still clears it. The web interface never sent such values; this affects API clients and scripts.
|
|
|
+- **Webhook routes answer a database problem with 503, not 500** — The `/webhook/...` routes checked API keys with their own copy of the check, which missed earlier fixes. A database error while checking the key, a key too long to hash or a damaged stored key came back as a 500. They now use the same check as every other route: 503 when the key can't be checked right now, 401 when it isn't valid. An expired key now reads "Invalid API key" there too, like everywhere else.
|
|
|
+- **The MQTT relay publishes what the wiki says it does** — Three topics were missing or wrong. `smart_plugs/energy` was never sent: with the relay connected, each enabled plug's power, today's energy and total energy now go out once a minute (without the relay no plug is read for this, and an MQTT plug that stopped reporting is left out). `smart_plugs/on` and `/off` only came from switching a plug by hand; auto-on, auto-off, schedules, the queue powering a printer on and failure detection switching a plug off now publish them too. `maintenance/alert` always said 0 for the current value and the threshold; it now has the hours (or days) since the item was last done, its interval and a `unit` field.
|
|
|
+- **PostgreSQL databases from older versions get the links a new install has** — A database created before v0.2.4.1 (or before v1.2.5.2 for the last one) was missing three links: from a print log entry to its archive and to the user who started it, and from an archive to the library file it was printed from. Deleting an archive, a user or a library file left their ID behind instead of clearing it, so such entries didn't show up under "no user", for example. The links are added on the next start, and IDs of things already deleted are cleared. A print log entry without an archive also stays that way now: every start used to attach it to the newest archive with the same name on the same printer, which could be someone else's. 51 columns, mostly settings flags, also accepted an empty value there or had a default only those databases had. They now match a new install, and an empty value gets the column's default, with two exceptions so nothing gains access: an empty API key permission becomes "off", and an OIDC provider without an email claim, which so far linked no accounts, gets auto-linking turned off instead of starting to link by email. Apart from that, SQLite installs are unchanged.
|
|
|
+- **Spoolman: colour, material and cost from the spool weren't applied on older PostgreSQL installs** — On a PostgreSQL database created before v0.2.4.8, the start snapshot of the AMS levels was stored as text instead of JSON and read back as text. Reporting usage to Spoolman then failed after charging the spools from the 3MF, with "Spoolman usage reporting failed" in the log. Prints without a 3MF charged nothing, and no archive got the colour, material and cost of its Spoolman spools. The column is converted on the next start. Two number columns also only had single precision on PostgreSQL databases older than them and are widened, keeping the values as entered: a spool's "Reset usage" baseline (databases created before v0.2.4.2) and a REST smart plug's energy total multiplier (before v1.2.5). SQLite installs aren't affected.
|
|
|
+- **Every install now gets the dependency versions that were tested** — Bambuddy's Python dependencies only had minimum versions, so each Docker build, install and update took whatever was newest that day: a fresh install could run library versions nobody had tested Bambuddy with (SQLAlchemy 2.1 arrived that way). They are now pinned in `requirements.lock`, which Docker, the installers, the update scripts and the tests all use, and newer versions are taken on deliberately, after testing. Nothing changes for an install today: the pins are the versions a fresh install gets now. A native install moves to the pinned versions with the update after the one that brings this change, since an update still runs the previous version of the update script.
|
|
|
+- **Native installs need Python 3.11 or newer** — Since v0.2.4.8 Bambuddy hasn't started on Python 3.10, while the installer, README and wiki still said 3.10 was enough. The requirement now says 3.11. The installer uses the system's `python3` when it is new enough and otherwise looks for a newer Python next to it: on Ubuntu 22.04, whose `python3` is 3.10, it installs and uses the `python3.11` package. An environment left from an older Python is rebuilt. The update scripts check the install's Python first and, if it is too old, stop before changing anything and link to the steps for moving it to 3.11. Docker and the Windows installer ship Python 3.13 and are not affected. Wiki updated.
|
|
|
+- **The webhook endpoints listed in Settings didn't exist** — **Settings → API Keys → Webhook Endpoints** listed endpoints for the status of all printers, pausing and resuming that Bambuddy doesn't have, and two others under the wrong path. It now lists the real ones: add to the queue, start the next queued job, stop, cancel, printer status and queue status. Wiki updated.
|
|
|
+- **A valid API key could be refused as invalid during a brief database problem** — When checking an API key hit a database error (a busy database, or saving the key's last-used time while it was locked), the request was answered "Invalid API key", so integrations such as Home Assistant or scripts were told their key was wrong. It now answers "Authentication service temporarily unavailable" (503), which a client can retry, and the error is logged. A wrong or expired key is refused as before.
|
|
|
+- **Errors were reported as "Authentication service temporarily unavailable" with login switched off** — Without authentication, any unexpected error in Bambuddy answered 503 "Authentication service temporarily unavailable", and the log blamed the authentication check, which sent troubleshooting the wrong way. Such errors are now reported as the internal error they are (500), and the error with its details now lands in `bambuddy.log` (and so in support packages), whether login is on or off. The protection that refuses requests when Bambuddy can't tell whether login is on stays as it was.
|
|
|
+ - **With login on, a brief database hiccup no longer signs you out:** while checking a sign-in, a database error answered "Invalid token", and the web interface took that as final, signed the user out and forgot the sign-in. It now answers "Authentication service temporarily unavailable", which the interface retries, and the error is logged.
|
|
|
+- **A print started outside Bambuddy could be reported to the owner of the previous queue job** — On a printer with **Auto-archive completed prints** switched off (in the printer's settings), a print Bambuddy didn't start (for example one started on the printer itself) has no archive. When it finished, failed or was stopped, the notification looked for any queue job this printer had finished in the last five minutes and treated the print as that job: its owner got the "your print is done" email for a print that wasn't theirs. It now uses only the queue job the print itself belongs to, so such a print isn't sent to anyone's personal email.
|
|
|
+ - **A queue job whose archive couldn't be found** at the end, when it finishes or fails, is still reported to its owner, and its duration (`{duration}`) is how long it actually ran instead of "Unknown". A job stopped from the queue page in that situation no longer sends its owner the "stopped" email.
|
|
|
+- **Smart plug power alerts were never sent** — A plug's **Power Alerts** (alert if the power is above or below a value) never reached any notification provider, and each time one was due, that request for the plug's status on the printer card failed. They also were only checked while someone had a printer page open.
|
|
|
+ - **Now checked in the background,** about once a minute, whether or not Bambuddy is open.
|
|
|
+ - **One alert per crossing:** you're told when the power leaves the range, and again only after it was back inside it, so a printer idling below a low value is reported once rather than every five minutes. The low value only counts while the plug reports itself switched on, so turning a printer off raises no alert (a plug that can't report its switch state gets no low alerts). Alerts go out as the **Printer Error** event ("Printer Error: Power High" / "Power Low"). Wiki updated.
|
|
|
+- **A project's timeline stayed empty while one of its jobs was queued or printing** — The **Timeline** on a project's page failed to load as soon as the project had a pending or printing job in the queue, so it showed no events at all, not even the finished prints. It loads again, and queued and running jobs show under their print name (or file name; a job for any of several printer models under its first file).
|
|
|
+- **Stopping or cancelling a print through the API failed with an error** — `POST /api/v1/webhook/printer/{id}/cancel` answered with an error and left the print running. `POST /api/v1/webhook/printer/{id}/stop` stopped the print but still answered with an error. This affected Home Assistant automations and scripts that use an API key; the Stop button in Bambuddy was not affected.
|
|
|
+ - **Now both stop the print and answer normally.** If the printer drops off before the command can be sent, they answer "Printer not connected" (503).
|
|
|
+ - **A print stopped this way now counts as stopped, not failed,** as with the Stop button: the "print stopped" notification goes out instead of "print failed", and the print is no longer mislabelled with a printer error such as a layer shift.
|
|
|
+ - **`…/stop` now stops a paused print too,** as `…/cancel` and the Stop button on the printer card do; it used to answer that no print was in progress.
|
|
|
+ - **"Stop Printing" in the HMS error dialog** now also counts as stopped rather than failed.
|
|
|
- **Starting the next plate of a Send All deleted the previous plate's timelapse (#3275, reported by @dovmesiz)** — All plates sent together with **Send All** share one archive. When a print starts on an archive that already has a timelapse, Bambuddy clears it so the new run gets its own video and completion photo. It also deleted the old video file, so starting plate 2 deleted plate 1's. With **delete from printer after archiving** on, that was the only copy. The video is now deleted only when the same plate prints again. Another plate's video, or one attached by hand or before this release, stays in the archive folder and is removed with the archive. The card still shows the latest plate's video. An external-camera timelapse no longer overwrites a kept video of the same name.
|
|
|
- **A queued print still running 2 hours after it was sent lost what Bambuddy stored for it at dispatch:** its plate, AMS mapping and cost centre, which the completion uses (for example for the per-plate totals in the notification). A clean-up of unused print registrations removed them mid-print. They are now kept until the print completes.
|
|
|
- **A PP spool without a slicer preset was sent to the AMS slot with no filament ID (#3273, reported by @sgiffhorn)** — When a spool has no preset of its own, Bambuddy fills in Bambu's generic preset for its material. The table used for spool assignment had no entry for PP, PE, PCTG, PLA Silk or PLA High Speed, so those slots got an empty filament ID, while **Configure AMS Slot** used a separate, longer table. The backend now has one table with all five, matching the one the frontend already used. A slot already set to Generic PLA Silk or PLA High Speed still keeps it when a PLA spool without a preset is assigned, as before.
|