REST/Webhook smart plugs can now fetch power and energy data from
individual URLs instead of requiring all values in a single status
response. Each value falls back to the shared Status URL when no
separate URL is set, preserving backward compatibility. Added power
and energy multipliers for unit conversion (e.g. 0.001 for Wh→kWh).
GitHub backup can now optionally include spool inventory (with usage
history) and print archive metadata as JSON. Both toggles are off by
default. No binary files (gcode/3MF) are included.
When multiple AMS spools match the same type/color criteria, an optional
setting now prefers the spool with the lowest remaining filament. This
helps consume partial spools before starting new ones. Sorting applies
to all matching paths: queue scheduler, print modal, and multi-printer
mapping. Unknown remain values (-1) sort to end.
Admins can now filter the Statistics page by user via a new
stats:filter_by_user permission. A user dropdown appears in the stats
header showing all users plus "No User (System)" for prints without
attribution. The filter applies to all stats widgets, failure analysis,
and CSV/Excel exports. Backend validates the permission on all 4 stats
endpoints, returning 403 if the filter is used without authorization.
Stagger option now available when printing directly to multiple printers,
not just in queue mode. Prints are automatically queued with staggered
start times using group size/interval from Settings. New "Require
plate-clear confirmation" setting lets farm users disable per-printer
plate confirmations so queued prints start automatically on finished
printers.
Also fixes settings API type parsing for require_plate_clear (boolean),
stagger_group_size and stagger_interval_minutes (integer) — without this,
saved values returned as strings would cause the settings toggle to
always show enabled and trigger a permanent save loop.
The archives page only fetched the 50 most recent prints due to a
hardcoded API limit default. Added client-side pagination with
configurable page sizes (25/50/100/200/All) and bumped the fetch
limit to return all archives. Page size is persisted to localStorage.
When adding a print to the queue for multiple printers, users can now
enable "Stagger printer starts" to avoid power spikes from simultaneous
bed heating. Configurable group size and interval — first group starts
immediately (ASAP) or at scheduled time, subsequent groups get offset
scheduled_time values. No backend queue/scheduler changes — leverages
existing scheduled_time field.
Also adds a dedicated Queue tab in Settings (stagger defaults + auto-
drying moved from Filament tab), i18n keys for all 7 locales, frontend
and backend tests.
Camera streams, snapshots, thumbnails, timelapse videos, photos, QR
codes, and cover images served via <img>/<video> tags were previously
unauthenticated because browser media elements cannot send Authorization
headers. When auth is enabled, these endpoints are now protected by a
reusable stream token (?token=xxx) obtained from POST
/printers/camera/stream-token (requires CAMERA_VIEW permission).
Auto-off now has a "Keep Enabled" toggle that keeps it active between
prints instead of disabling after each use (one-shot). Useful for HA
accessories like BentoBox filters that should always power off after
prints. Default behavior (one-shot) is unchanged.
The daemon now collects CPU temp, core count, load average, memory/disk
usage, OS info, and system uptime every heartbeat using stdlib-only reads
from /proc and /sys. Stats are sent as a JSON blob in the heartbeat
payload, stored in a new system_stats TEXT column, and displayed in a
new "System" tab in SpoolBuddy Settings with color-coded usage bars.
Some printers (e.g. H2D) only send {id, state} in incremental MQTT
tray updates — no tray_type, tray_color, or other fields. When
filament is unloaded (state changes from 11 to 10), the old tray
data persisted indefinitely because the merge logic only updates
fields present in the incoming message.
Backend:
- Detect tray state != 11 without tray_type in incremental updates
and clear stale tray data (bambu_mqtt.py)
- Expose tray `state` field via REST API and WebSocket broadcasts
(printer.py schema, printers.py route, printer_manager.py)
- Include AMS tray content in WebSocket dedup key so load/unload
transitions trigger broadcasts (main.py)
Frontend:
- Add `state` to AMSTray interface (client.ts)
- Show configure/assign buttons for state=10 (spool present, not
loaded) but hide for state=9 (truly empty) on AMS/HT slots
- Hide fill level bar on empty slots
Tests:
- 5 new tests covering state-based clearing, preservation, reload,
and idempotency
Host directories (NAS, USB, network shares) can now be mounted
into the File Manager without copying files. Files are indexed
into the database on scan but read directly from their original
location. Supports read-only mode, hidden file filtering, and
automatic thumbnail extraction for 3MF/STL/gcode.
- POST /library/folders/external — create with path validation
- POST /library/folders/{id}/scan — discover/sync files
- Block uploads, moves, and deletes for read-only external folders
- Never delete actual files from external paths (DB-only removal)
- Purple folder icon + info bar with rescan button in UI
- i18n for all 7 languages
- 19 backend + 11 frontend tests
The daemon's self-update mechanism (git fetch/reset on its own code) was
fragile: .git permission errors, self-modifying code mid-run, hardcoded
main branch. Bambuddy now SSHes into the SpoolBuddy Pi and drives the
update remotely — matching its own branch, with step-by-step progress
via WebSocket. Install script updated with SSH access, sudoers entry,
and --ssh-pubkey flag for pairing.
The SpoolBuddy daemon update endpoint fetched GitHub releases and compared
them against device.firmware_version, which always showed "up to date"
because the comparison source was wrong. Now compares directly against
APP_VERSION from the running backend, so a daemon at 0.2.3b1 correctly
sees 0.2.3 as an available update.
SpoolBuddy devices can now be updated from Settings → Updates without
SSH access. The daemon picks up an "update" command via its existing
heartbeat, runs git fetch/reset + pip install, reports progress back
to the backend, then exits for systemd to restart with the new code.
Backend: update_status/update_message fields, trigger + status endpoints
Daemon: _perform_update() handler, report_update_status() API method
Frontend: "Apply Update" button with live progress in UpdatesTab
Replace fixed 30-second debug log collection with an interactive
3-step flow: start logging, reproduce the issue, stop & submit.
Users now control timing instead of racing a countdown.
Backend: split _collect_debug_logs() into POST /start-logging and
POST /stop-logging endpoints; add debug_logs field to submit request.
Frontend: 3-step progress indicator with elapsed timer, pulsing
active state, and 5-minute auto-stop. Updated all 7 locale files.
Add a "Rotate spool during drying" checkbox to the manual drying popover
for AMS 2 Pro and AMS-HT units. The firmware-level rotate_tray field was
already sent (hardcoded to false) — this makes it user-configurable.
The checkbox defaults to unchecked and resets each time the popover opens.
Firmware silently disables rotation if filament is currently loaded.
Add a speed control badge to the printer monitoring card controls row
that lets users switch between Silent (50%), Standard (100%), Sport
(124%), and Ludicrous (166%) presets during active prints. The badge
displays a gauge icon with the current speed percentage, always visible
but disabled when idle. Includes backend endpoint, optimistic UI
updates, i18n for all 7 locales, and full test coverage.
Per-printer camera rotation (0°/90°/180°/270°) for cameras mounted
in portrait or upside-down. CSS rotation for live views, Pillow
rotation for notification snapshots. Setting visible in external
camera config when enabled.
Ambient drying: automatically dry filament on idle printers when
humidity exceeds threshold, regardless of queue state. Separate toggle
from queue auto-drying — both can run simultaneously. Uses the same
presets, humidity threshold, and power constraint detection.
Fix: block mode (wait for drying) previously skipped the humidity
auto-stop check for already-drying printers, causing drying to
continue indefinitely. Now only prevents starting new drying.
Queue auto-drying: scheduler automatically starts drying on idle printers
with scheduled queue prints when AMS humidity exceeds the configured
threshold. Uses conservative parameters (lowest temp, longest duration)
for mixed filaments. Drying stops when humidity drops below threshold
(30-minute minimum prevents oscillation), when scheduled items are
removed, or when the feature is disabled. Optional "block queue" mode
delays the next print until drying completes.
Configurable presets: temperature and duration per filament type,
editable in Settings → Print Queue, used by both manual drying popover
and queue auto-drying. Separate presets for AMS 2 Pro (n3f) and AMS-HT
(n3s) reflecting different heating capabilities.
PSU detection: drying button disabled with tooltip when dry_sf_reason
indicates insufficient power. Parses drying status bits and dry_sf_reason
from AMS info hex string via MQTT.
Backend: print_scheduler.py (+316 lines), bambu_mqtt.py, printer_manager,
schemas, settings route. Frontend: PrintersPage drying presets prop,
SettingsPage drying config UI, i18n (7 locales). Tests: 27 new tests in
test_scheduler_auto_drying.py covering conservative params, presets,
state sync, stop logic, minimum drying time, and auto-stop regressions.
Notify users when the first layer finishes printing so they can check
adhesion remotely. Triggers once per print when layer 2 begins
(layer_num >= 2, capped at <= 5 to handle reconnects). Includes a
camera snapshot attachment. Adds the on_first_layer_complete toggle
to all notification providers, with backend/frontend/i18n support
across all 7 locales.
Start, monitor, and stop drying sessions for AMS 2 Pro (n3f) and
AMS-HT (n3s) directly from the Printers page. Flame icon in AMS card
header opens a popover with filament-based temperature/duration presets
from BambuStudio. Live countdown status bar shows time remaining.
Backend: cache module_type from MQTT get_version, expose dry_time and
module_type in both WebSocket and REST paths, add supports_drying()
with firmware gating, add server-side guard on start_drying endpoint.
Frontend: drying button, popover with filament select + temp/duration
sliders, status bar, start/stop mutations. i18n for all 7 locales.
Supported: X1/X1C (fw 01.09+), P1P/P1S (fw 01.08+), H2D (fw 01.02.30+),
H2D Pro, X1E. Not supported: P2S, A1, A1 Mini, H2S, H2C.
home_flag bit 18 is set on all printers regardless of hardware,
causing the ethernet badge to appear on WiFi-only models (A1, P1P,
etc.). The previous fix removed the feature entirely, so ethernet
printers lost their badge too.
Now only trusts bit 18 on models with an ethernet port (X1C, X1E,
P1S, P2S, H2D, H2D Pro, H2C, H2S). WiFi-only models always show
the WiFi signal badge.
* Enhance link spool functionality with additional printer and AMS details
* Refactor linkSpool function to accept detailed context object for improved spooling integration
* Enhance LinkSpoolModal to include amsName in props and linkSpool mutation for improved functionality
* Add amsName prop to PrinterCard for enhanced spool linking functionality
* Updates LinkSpoolModal test to work with recent spool location update changes
* Adds clear_location to unlink testing
* Refactor LinkSpoolRequest to remove ams_name and update location generation logic for improved clarity
* Remove amsName from LinkSpoolModal and PrinterCard for cleaner API integration
* Remove amsName from LinkSpoolModal test props and update linkSpool mock response for improved clarity
* Expanded link logic to accept a generic spool tag (spool_tag, tray_uuid, or tag_uid) and validate 16/32-char hex values, including rejection of all-zero tags. Added a new unlink endpoint that clears Spoolman extra.tag for a given spool ID.
* Updated the link validation expectation to match the new 16-or-32-hex rule. Added an integration test for the new unlink endpoint to verify extra.tag is cleared and success is returned.
* Updated the link API call to send spool_tag instead of tray_uuid. Added a new unlinkSpool API helper that calls the new backend unlink route.
* Switched modal data source from inventory spools to Spoolman unlinked spools so the list matches the backend response you shared. Updated the link action to call Spoolman link directly and use trayUuid or tagUid as the slot tag.
* Refactored Spoolman card behavior so actions render correctly across linked/unlinked cases, and added support for an Unlink from Spoolman button under Open in Spoolman. Final UI rule now hides Unlink for Bambu Lab filament and shows it only for non-Bambu linked spools.
* Added fallback slot-tag generation so untagged slots can still be linked/unlinked reliably and linked spool lookups work consistently across AMS, AMS-HT, and external trays. Wired a new unlink mutation and passed per-slot unlink callbacks into the hover card, with query invalidation for linked/unlinked spool caches after unlink.
* Added/updated the noTrayUuid message to reflect that either tray UUID or tag UID is required when no slot tag is available. This keeps error text aligned with the new tag fallback and modal behavior.
* Added noTrayUuid message in the other languages. The translation was done using Google Translate.
* Updated LinkSpoolModal tests to use Spoolman API instead of inventory API, with new UnlinkedSpool data structure and Select Spool UI text.
* Adds hashSerialToHex32, uses new function in getFallbackSpoolTag and updates all uses. This ensures we're generating a fallback spool tag using the printers serial number rather than the database ID for consistency.
* Removes 'import json' from unlink_spool and moves to the top of the file.
* Removes hasUnlinkedSpools and related references since it's no longer used
* Removes noTrayUuid from translations and references in components.
* Adds confirmation dialog before fully unlinking spool from Spoolman
* Adds unlinkConfrimTitle and unlinkConfirmMessage. All languages other than English were done using Google Translate
* Fixes unlink toast to use dedicated unlinkSuccess and unlinkFailed keys.
* Changes tag priority on spoolTag, fixes type error by adding non-null assertion to spoolTag
* Switches around tag_uid and tray_uuid on const trayTag
---------
Co-authored-by: MartinNYHC <mz@v8w.de>
Sends persistent notifications to the HA dashboard using the existing
HA connection from Settings. Zero config — just select "Home Assistant"
as provider type. Users can forward notifications to mobile via HA
automations.
home_flag bit 18 was incorrectly interpreted as "wired/ethernet
connection", causing the ethernet badge to always show — even on
printers without an ethernet port (e.g. A1, P1S). This hid the
WiFi signal indicator entirely.
Removed the wired_network field and ethernet badge UI. The WiFi
signal badge now shows correctly whenever the printer reports
signal strength.
* Added "Force Color Match" feature to filament override
* Update settings.py
* Update settings.py
* Label updates for warnings and checkbox case
* Initial plan
* Add noMatchingMaterial i18n translations to all 7 locale files
Co-authored-by: cadtoolbox <12723486+cadtoolbox@users.noreply.github.com>
* Use {{material}} interpolation in noMatchingMaterial; populate from filament_overrides with getColorName
Co-authored-by: cadtoolbox <12723486+cadtoolbox@users.noreply.github.com>
* Updates for PR#625 comments
* Added "Force Color Match" feature to filament override
* Update settings.py
* Update settings.py
* Label updates for warnings and checkbox case
* Initial plan
* Add noMatchingMaterial i18n translations to all 7 locale files
Co-authored-by: cadtoolbox <12723486+cadtoolbox@users.noreply.github.com>
* Use {{material}} interpolation in noMatchingMaterial; populate from filament_overrides with getColorName
Co-authored-by: cadtoolbox <12723486+cadtoolbox@users.noreply.github.com>
* Updates to PrintersPage and QueuePage
* Update QueuePage.tsx
* Rebase to 0.2.2b3
* Fix compile errors with FilamentOverride
* Updates for PR#625 comments
https://github.com/maziggy/bambuddy/pull/625#pullrequestreview-3908738261
* Fix force color match default, loadedFilamentTypes bug, remove dead translations
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cadtoolbox <12723486+cadtoolbox@users.noreply.github.com>
Co-authored-by: MartinNYHC <mz@v8w.de>
Virtual printers in Queue mode now have an "Auto-dispatch" setting.
When enabled (default), prints start automatically — preserving current
behavior. When disabled, prints are added with manual_start so they
wait for manual dispatch from the queue UI.
Checkbox selection + "Delete Selected" button in Settings > Filament
for both Spool Catalog and Color Catalog. Adds POST bulk-delete
endpoints and translations for all 7 locales.
visibility into AMS spool assignments on printer cards without exposing
the full Inventory page (#635). The list_assignments endpoint now
requires this new permission instead of inventory:read. All default
groups (Administrators, Operators, Viewers) include it for backward
compatibility.
Closes#634
All filament mapping dropdowns (single-printer, multi-printer, and
"Print to Any" model-based) showed only the base material type (e.g.
"PLA") without distinguishing subtypes like "PLA Basic" vs "PLA Matte",
making entries with different subtypes but same color look identical.
Backend: Add tray_sub_brands to available-filaments response and include
it in the dedup key so different subtypes of the same color appear as
separate entries.
Frontend: Show tray_sub_brands in FilamentMapping, FilamentOverride, and
PrinterSelector dropdowns, falling back to base type when unset. Add
traySubBrands field to LoadedFilament interface.
* AMS Labels addition to PrintersPage
* Added database reinitialization for schema migrations on database restore
* Bug fixes and AMS Label persistence updates
* Update database.py to resolve PR conflicts
* PR Comment Resolution
* Resolve conflicts in database.py for PR#570
* Updates to address PR#570 additional comments
* Optimize visibility handling for popup component
* Improve serial key handling in printers.py
Refactor serial key assignment to handle empty AMS serial gracefully.
* Implement error handling in serial number mapping
Add error handling for serial number mapping.
* Add migration to drop old ams_labels table
---------
Co-authored-by: MartinNYHC <mz@v8w.de>
Floating bug report button submits issues via bambuddy.cool relay (no GitHub
token needed locally). Collects 30s debug logs with printer push_all, sanitizes
all sensitive data, uploads logs as files to GitHub. Screenshot upload/paste/drag
with JPEG compression. Translated into all 7 languages. Includes 21 tests.
Parse home_flag bit 18 (0x00040000) from MQTT to detect ethernet
connections. When set, the printer card shows a green "Ethernet"
badge with a cable icon instead of WiFi signal strength in dBm.
The printer info modal also displays "Ethernet" instead of WiFi
signal details.
* feat(queue): show spool grams left in filament slot mapping
* Bumped version
* Add SpoolBuddy AMS slot config, external slots, and dashboard redesign
- AMS page: external spool slots (Ext/Ext-L/Ext-R), click-to-configure
modal on all slots, temperature/humidity threshold-colored indicators,
nozzle L/R badges for dual-nozzle printers, compact AMS-HT layout
- Dashboard: two-column layout with device status + printers list (left)
and current spool card (right), state-colored scale/NFC icons, dashed
border card styling
- Daemon: suppress redundant scale reports (±2g threshold + stability
state change detection) to prevent weight display bouncing
- TopBar: auto-select online printers only, SpoolBuddy logo
* Fix SpoolBuddy daemon crash when read_tag module is missing
NFCReader.__init__ imported read_tag and instantiated PN5180() outside
the try/except block, so a missing module crashed the entire daemon.
Moved the import inside the existing try/except so the daemon gracefully
skips NFC polling — matching the scale reader's existing behavior.
* Fix SpoolBuddy daemon failing to import hardware drivers
The daemon imports read_tag and scale_diag as bare modules, but they
live in spoolbuddy/scripts/ which isn't on sys.path when systemd runs
the daemon. Added scripts/ to sys.path at startup, resolved relative
to the module file. Also moved the read_tag import inside NFCReader's
try/except (was crashing the daemon instead of skipping gracefully)
and demoted hardware-not-available messages from ERROR to INFO.
* Increase scale moving average window to reduce weight bouncing
5 samples at 100ms (500ms window) wasn't enough to smooth NAU7802 ADC
noise — the averaged value still varied by >2g between 1s report
intervals, and the stability state kept flipping, triggering a report
every cycle. Increased to 20 samples (2s window) so noise is smoothed
before reaching the reporting layer.
* Remove stability flipping as scale report trigger
When ADC noise kept the spread hovering around the 2g stability
threshold, the stable flag toggled every cycle, forcing a report with
a slightly different weight each time. Now only actual weight changes
of >=2g trigger reports. The stable flag is still included in each
report for consumers that need it.
* Fix formatting of option elements in FilamentMapping
* Make low stock threshold editable
* Add new filter for low spools
* Update bug report template to require additional fields
* Added toast for invalid imputs with locales, updated inputb field restrictions
* Minor Spoolbuddy frontend improvements
* Updated test_backend.sh
* Updated Spoolbuddy install script to strip down Raspbian
* Updated Spoolbuddy install script
* Add API key auth support to /auth/me for SpoolBuddy kiosk
When Bambuddy auth is enabled, the SpoolBuddy kiosk gets redirected to
the login page because ProtectedRoute requires a user from GET /auth/me,
which only handled JWT tokens. The kiosk daemon already has an API key
but couldn't use it to satisfy the frontend auth check.
- Backend: /auth/me now accepts API keys (Bearer bb_xxx or X-API-Key)
and returns a synthetic admin UserResponse with all permissions
- Frontend: AuthContext reads ?token= from URL on first load, stores in
localStorage, and strips from URL (prevents history/referrer leakage)
- Install script: kiosk URL now includes ?token=${API_KEY}
- Tests: 3 new integration tests (Bearer API key, X-API-Key header,
invalid key rejection)
* SpoolBuddy touch-friendly UI overhaul for 1024x600 kiosk display
Enlarge all interactive elements across 9 SpoolBuddy components to meet
44px minimum tap targets on the RPi touchscreen. Increase nav icons
(20→24px), labels (10→12px), bar heights, section headers, printer
buttons, spool visualizations, fill bars, and status indicators.
Compact the dashboard stats bar and remove the printers card. Add
fullScreen prop to ConfigureAmsSlotModal with two-column layout
(filament list left, K-profile + color right) to eliminate scrolling.
* Minor changes, CSS fixes
* Refactor usageFilter state to remove 'lowstock' option for clarity
* Move var saving to API, add test coverage
* fix: threshold validation and cleanup
* Change test input from '150' to '0'
---------
Co-authored-by: tridev <c.tripod@gmx.ch>
Co-authored-by: MartinNYHC <mz@v8w.de>