Reproduced live during the #1133 rollout: the SpoolBuddy display kept
serving the pre-fix picker for hours after every cache-clear,
chromium-restart, and pkill attempt because a chain of stale state
across HTTP cache + Service Worker + persistent profile prevented
fresh code from reaching the running tab.
Three independent changes — any one of them sufficient on a clean
profile, but all three needed to escape an already-corrupted one:
(1) backend/app/main.py — index.html now served with
Cache-Control: no-cache, must-revalidate on both / and the SPA
catch-all. Vite emits content-hashed JS/CSS bundle filenames so the
assets themselves are safe to cache forever, but the HTML wrapping
them is the only file that knows which hash is current. Without
explicit cache directives Chromium falls back to heuristic caching
(typically 10% of time since Last-Modified) and on long-running
kiosks happily serves stale HTML across browser restarts. That stale
HTML references an old bundle hash which is also still in disk
cache, so the kiosk runs pre-deploy JS forever without ever knowing
why.
(2) frontend/public/sw.js — CACHE_NAME bumped from bambuddy-v25 to
bambuddy-v26 so any client that fetches the new sw.js drops its old
CacheStorage. The SW does network-first for HTML/JS/CSS but
intercepts and falls back to cache, and cache-control on HTTP
responses doesn't reach into the SW's own cache layer.
(3) spoolbuddy/install/install.sh — generated kiosk launcher now uses
--user-data-dir=/tmp/spoolbuddy-kiosk-userdata with a pre-launch
rm -rf, so every kiosk restart starts from a clean slate (no HTTP
cache, no SW registration, no IndexedDB). Trade-off is a slightly
slower first paint and zero offline support; neither matters for a
single-purpose kiosk facing a backend on the same LAN, and the
guarantee that next-deploy-just-works is worth far more.
4 new tests in test_static_html_cache_headers.py: index.html on /
and SPA catch-all paths emit Cache-Control: no-cache,
must-revalidate; API routes are unaffected (no leak of HTML cache
directive onto endpoints we want React Query to cache aggressively).
For existing kiosks already trapped by an old persistent profile,
operator runs once: rm -rf ~/.config/chromium && systemctl restart
getty@tty1.service. The new launcher then picks up automatically.
The SW used stale-while-revalidate for JS/CSS, serving old cached
bundles even after a new build. Changed to network-first (Vite already
content-hashes filenames), bumped cache version v24→v25, and added
Cache-Control: no-cache to the sw.js endpoint so browsers always fetch
the latest service worker.
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.
Camera streams on macOS Safari were failing with "FetchEvent.respondWith
received an error: Load failed" because the Service Worker was intercepting
MJPEG streaming responses. Safari has known issues handling continuous
streaming responses through Service Workers.
- Add exclusion for /camera/stream and /camera/snapshot URLs in SW fetch handler
- Bump cache version to v24 to force SW update on clients
The camera components already have robust error handling with reconnect logic,
so bypassing the SW for these endpoints is safe and improves performance.
New feature: Publish BamBuddy events to external MQTT brokers for integration
with Home Assistant, Node-RED, and other automation platforms.
Backend changes:
- New MQTTRelayService (backend/app/services/mqtt_relay.py) with paho-mqtt
- MQTT settings added to AppSettings schema (enabled, broker, port, auth, TLS)
- New /settings/mqtt/status endpoint for connection status
- Event hooks in main.py, print_queue.py, print_scheduler.py, maintenance.py, smart_plugs.py
- PrinterInfo class and get_printer() method added to PrinterManager
- MQTT reconfiguration added to backup/restore flow
- Printer status throttled to 1 update/sec to avoid flooding
Frontend changes:
- New "Network" tab in Settings (between Filament and API Keys)
- FTP Retry settings moved from General to Network tab
- MQTT configuration card with broker, port, TLS toggle, auth fields
- Port auto-populates when TLS toggled (1883 ↔ 8883)
- Connection status indicator (green/red dot) in tab and card header
- Real-time status polling when on Network tab
Published topics:
- bambuddy/status - Online/offline
- bambuddy/printers/{serial}/status - Printer state (throttled)
- bambuddy/printers/{serial}/print/* - Print lifecycle
- bambuddy/printers/{serial}/ams/changed - AMS changes
- bambuddy/queue/* - Queue events
- bambuddy/maintenance/* - Maintenance alerts
- bambuddy/smart_plugs/* - Plug state/energy
- bambuddy/archive/* - Archive events
Tests:
- Added MQTT settings and status endpoint tests
Closes#78
- Comprehensive hex-based color mapping (~200 Bambu colors from CSV)
- Fixed tooltips to use Bambu color lookup
- Widened modals (max-w-md → max-w-lg)
- Adjusted grid layout to fit long color names
- Service worker bumped to v21
Pending changes in bambuddy repo:
- CHANGELOG.md, README.md
- backend/app/api/routes/print_queue.py
- backend/tests/integration/test_print_queue_api.py
- frontend components (ReprintModal, AddToQueueModal, EditQueueItemModal)
- sw.js (v21)
- ReprintModal: Dropdown to override auto-matched AMS slots
- AddToQueueModal: Collapsible filament mapping section
- Backend: Store ams_mapping in print queue, apply when print starts
- Color names in dropdowns (decoded from Bambu codes or derived from hex)
- Blue ring indicator for manually selected vs auto-matched slots
- Status indicators: green (match), yellow (type only), orange (not found)
Closes#76
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add time format utilities to date.ts (applyTimeFormat, formatDateTime,
formatTimeOnly, TimeFormat type)
- PrintersPage: Pass timeFormat to formatETA for printer card ETA display
- ArchivesPage: Add timeFormat prop to ArchiveCard for archive timestamps
- AMSHistoryModal: Apply timeFormat to chart labels, axis ticks, and tooltip
- ProjectDetailPage: Use formatDateTime for timeline dates
- QueuePage: Pass timeFormat to formatRelativeTime for scheduled prints
- SystemInfoPage: Use formatDateTime for boot time display
- NotificationLogViewer: Apply timeFormat to notification timestamps
- Bump service worker cache version to v17
Closes#75
- Add size-aware rendering to Dashboard component, passing widget size
(1/4, 1/2, full) to component render functions
- PrintCalendar: Dynamic cell sizing using ResizeObserver to fill
available width, shows 3/6/12 months based on widget size
- SuccessRateWidget: Per-printer breakdown at larger sizes
- TimeAccuracyWidget: Per-printer breakdown at larger sizes
- FilamentTypesWidget: Grid layout at larger sizes
- FailureAnalysisWidget: More failure reasons at larger sizes
- Bump service worker cache version