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.
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.
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.
- Round scale weight to integer before sending to backend (Pydantic
rejects non-whole floats for int fields), move modal close to finally
block, add error toast with actual API message
- Fix null-field crash in SpoolInfoCard prop construction: pick one
source object instead of per-field ?? fallbacks that crash when
displayedSpool has null subtype/brand/rgba and matchedSpool is null
- Add React ErrorBoundary to App so crashes show error instead of
black screen
- Remove --max-old-space-size=128 and --enable-low-end-device-mode
from kiosk Chromium flags (crashed renderer/display)
- Append kiosk flags to Pi GPU defaults instead of resetting them
- Add wlr-randr keep-alive and screenBlankTimeout=0 to prevent
display blanking on labwc 0.9.x
- Fix tests: add ToastProvider to Dashboard test wrapper, update
StatusBar tests for removed animate-pulse class
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.
Catalog-style spool grid with colored spool circles (matching AMS page
style), material/subtype labels, color dots, fill level bars, remaining
weight percentages, and AMS location badges. Touch-friendly search bar
and inline filter pills (All, In AMS, per-material). Full-screen detail
view with slicer filament, PA K-profiles, weight breakdown, temperature,
cost, tag ID, AMS assignment, and notes — updates live from query data.
Shows Spoolman iframe when Spoolman is enabled. Navigation item added
between Write and Settings in the bottom bar.
Catalog-style spool grid with colored spool icons, material/subtype
labels, color dots, remaining weight percentages, and AMS location
badges. Search bar with inline filter pills (All, In AMS, per-material).
Detail bottom sheet on tap. Shows Spoolman iframe when enabled.
Navigation item added between Write and Settings in the bottom bar.
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.
Narrowed CSS selector to only target max-w-2xl modals (Assign Spool)
not max-w-sm ones (slot action picker). Removed inner max-h-96 on
the spool grid to eliminate nested scrollbars.
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.
useEffect in SpoolBuddyAmsPage applies inline styles to the modal
panel after render (requestAnimationFrame + setTimeout) to cap it
at 90vh with scrollable content. Targets the DOM directly to avoid
CSS specificity issues with Tailwind v4.
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.
Added scoped CSS in SpoolBuddyAmsPage that constrains the shared
AssignSpoolModal to 90vh with scrollable content and pinned
header/footer, without modifying the shared component.
Added scoped CSS in SpoolBuddyAmsPage that constrains the shared
AssignSpoolModal to 90vh with scrollable content, without modifying
the shared component used by the main Bambuddy frontend.
The NFC Diag and Scale Diag buttons on the Write Tag page's right
card were misplaced — diagnostics are already available on the
Settings page. Removed the buttons and all associated dead code.
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.
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.
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.
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.
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.
Assigning or unassigning a spool now broadcasts a spool_assignment_changed
event to all connected WebSocket clients. The frontend handles this event
by invalidating the spool-assignments and slotPresets caches, so other
open tabs update automatically without a page reload.
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.
When Spoolman is enabled, the sidebar filament item embedded
Spoolman at its root URL. Now navigates to <url>/spool so users
land directly on the spool management page.