The on_ams_change auto-sync callback set locations for new spools but
never called clear_location_for_removed_spools(), leaving stale locations
that caused double-booked slots. Also pass synced_spool_ids in the
single-printer sync route to match the sync-all endpoint behavior.
The clearPlateMutation.isSuccess state from React Query persisted after
the first successful plate clear. When the next print finished, the
stale isSuccess rendered the static confirmation instead of the clickable
button. Reset mutation state when printer leaves FINISH/FAILED.
Swipe down from the top of the SpoolBuddy display to open a quick-access
menu for toggling printer smart plugs and managing the device (restart
daemon, restart browser, reboot, shutdown). All destructive actions
require confirmation.
Backend: new POST /spoolbuddy/devices/{id}/system/command endpoint
queuing reboot/shutdown/restart_daemon/restart_browser commands.
Daemon: handles commands via subprocess (sudo reboot, systemctl restart).
Frontend: SpoolBuddyQuickMenu component, swipe-down gesture detection,
i18n keys for all 7 locales.
Swipe down from the top of the SpoolBuddy display to open a quick-access
menu for toggling printer smart plugs and managing the device (restart
daemon, restart browser, reboot, shutdown). All destructive actions
require confirmation.
Backend: new POST /spoolbuddy/devices/{id}/system/command endpoint
queuing reboot/shutdown/restart_daemon/restart_browser commands.
Daemon: handles commands via subprocess (sudo reboot, systemctl restart).
Frontend: SpoolBuddyQuickMenu component, swipe-down gesture detection,
i18n keys for all 7 locales.
Users can authenticate against an LDAP/AD server with configurable
server URL, bind DN, search base, and user filter. Supports StartTLS
and LDAPS — plaintext is not allowed. Both Active Directory (memberOf)
and POSIX groups (memberUid) are mapped to BamBuddy groups on each
login. Auto-provisioning creates local accounts on first LDAP login.
Local admin accounts remain as fallback when LDAP is unreachable.
Password management is disabled for LDAP users.
Per-model start/end G-code snippets configurable in Settings (Workflow
tab). Queue items get "Inject G-code" toggle — scheduler injects
snippets into a temp 3MF copy before FTP upload. Supports Farmloop,
SwapMod, AutoClear, Printflow 3D and similar bed-clearing systems.
Original files are never modified.
The "Clear Plate & Start Next" button on printer cards appeared
even when "Require plate-clear confirmation" was disabled in
Settings. The backend correctly auto-dispatched without waiting,
but the frontend widget always showed the prompt. Thread the
require_plate_clear setting through PrinterCard → PrinterQueueWidget
so the widget shows a passive queue link when the setting is off.
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).
The bottom message bar showed stale warnings from the previous printer
after switching via dropdown or swipe. Cached AMS data was shared across
all printers in a single ref, so switching to a printer whose status
hadn't loaded fell back to the wrong printer's data. The Layout also
unconditionally cleared alerts set by child pages. Fixed by keying the
AMS cache per printer ID and only clearing Layout-owned alerts.
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.
Select multiple printer cards and apply bulk actions (stop, pause,
resume, clear notifications, clear bed) from a floating toolbar.
Selection shortcuts: Select All, Select by State (printing/paused/
finished/idle/error/offline), Select by Location. Action buttons are
smart-enabled based on selected printers' current states. Confirmation
modals for destructive actions. The status summary bar now shows all
printer states.
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.
Queue page redesign: compact stats bar replaces summary cards, color-
coded left borders for status scanning, collapsible history with
condensed rows, and a new production schedule timeline view (#823)
showing estimated completion times grouped by hour with filter tabs
and day navigation. i18n keys added for all 7 languages.
Fix plate thumbnails returning 401 in print modal when auth is enabled
(missing stream token). Fix schedule calendar picker opening off-screen
(hidden input positioned with sr-only instead of near the date field).
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.
Frontend: replace expensive idle dashboard animations (3x animate-ping
with scale transforms, blur-2xl glow, continuous animate-pulse on
status dots) with static NFC rings and slow 5s color-cycling spool.
Chromium: add --disable-extensions, --disable-background-timer-throttling,
--memory-pressure-off, --disable-renderer-backgrounding, --disable-breakpad,
and --js-flags=--max-old-space-size=128. Install script: mask stripped
services (not just disable) to prevent socket/dbus reactivation; use
/etc/systemd/user/ global overrides for user services instead of
unreliable su-based systemctl --user. Remove chromium/upower from
strip_packages since kiosk reinstalls them immediately.
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).
Chromium: override Debian defaults via /etc/chromium.d/spoolbuddy-kiosk
to disable GPU rasterization, enable low-end device mode, and disable
smooth scrolling/background networking. The system default
--enable-gpu-rasterization conflicted with per-launch flags.
WebSocket: add React Query `select` to SpoolBuddyLayout and
SpoolBuddyDashboard printer status queries so only `connected` is
extracted. Temperature/fan/progress changes no longer trigger
re-renders on every MQTT tick (~10/sec idle).
Services: expand install script to strip pipewire, CUPS, rpcbind,
upower, polkit, accounts-daemon, xdg-desktop-portal, mpris-proxy.
Add user-level service masking for pipewire/portals.
Virtual keyboard now participates in flex layout instead of fixed
overlay, eliminating dead space between content and keyboard. Bottom
nav and status bar hide when keyboard is open. Number inputs (e.g.
Weight field on Write Tag) now accept virtual keyboard input.
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.
New page at /spoolbuddy/inventory with compact 2-column spool card grid,
search bar, material filter chips, sort options, and tap-to-open detail
sheet. Shows Spoolman iframe when Spoolman is enabled. Navigation item
added between Write and Settings in the bottom bar.
CSS rules scoped via data-spoolbuddy-kiosk force the modal panel to
90vh with flex column layout. Content area scrolls, header/footer
pinned. Uses height (not just max-height) so flex-basis auto works
correctly. No flash — CSS applies before paint.
Used a ref callback wrapper in SpoolBuddyAmsPage to directly apply
inline styles (max-height 90vh, flex column, scrollable content) to
the shared AssignSpoolModal DOM elements without modifying the
shared component.
Added data-spoolbuddy-kiosk attribute to the SpoolBuddy layout and
global CSS rules that cap modals at 90vh with scrollable content on
the small touchscreen, without modifying shared components.
When the virtual keyboard opened on the SpoolBuddy device tab, the
API token field and Save button were hidden behind the keyboard.
The keyboard now adds bottom scroll padding and scrolls the parent
card (not just the focused input) to the top of the visible area,
keeping all related fields accessible.
When the virtual keyboard opened on the SpoolBuddy device tab, the
Backend URL and API Token fields were hidden behind the keyboard
overlay. The keyboard now adds temporary bottom padding to the
scrollable container so inputs scroll above it, and removes the
padding on dismiss.
Non-passive touchmove listener with preventDefault() blocks Chromium's
compositor-level back/forward swipe gesture. Once horizontal movement
is detected, the gesture is locked and all further touchmove events
are prevented for that swipe. Removed touch-action: pan-y which was
insufficient on its own.
Swiping left/right on the SpoolBuddy touchscreen now cycles through
online printers instead of triggering browser back/forward navigation.
Uses touch-action: pan-y to suppress Chromium's native swipe history
gesture. Auto-navigates to dashboard when a new NFC tag is detected
on any other page, with edge detection so a persistent tag doesn't
block manual navigation. Also wakes the screen if blanked.
Swiping left/right on the SpoolBuddy touchscreen now cycles through
online printers instead of triggering browser navigation. Auto-navigates
to dashboard when a new NFC tag is detected on any other page, with
edge detection so a persistent tag doesn't block manual navigation.
When an NFC tag is first detected while the UI is on a non-dashboard
page, automatically navigate to the main dashboard. Uses edge detection
(previous ref) so a persistently present tag doesn't block navigation
to other pages. Also wakes the screen if the display was blanked.
When an NFC tag is detected while the UI is on a non-dashboard page
(Settings, AMS, Write Tag, Calibration), automatically navigate back
to the main dashboard to display the scanned spool. Also wakes the
screen if the display blank timeout had kicked in.
The AssignToAmsModal was missing all material/profile validation guards
that exist in the main AssignSpoolModal. Clicking an AMS slot now checks
the spool's material and slicer profile against the target slot's current
filament, showing a confirmation dialog on mismatch. Respects the global
disable_filament_warnings setting. Uses existing i18n keys and ConfirmModal.
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