Embedded camera viewers now stay open when navigating away from the
Printers page and back. Previously, users had to reopen each camera
view manually after navigation.
- Add rename functionality for files and folders:
- New RenameModal component with validation
- Backend endpoint supports filename updates (rejects path separators)
- Context menu "Rename" option in grid and list views
- Inline rename button in list view actions
- Add Print button to multi-selection toolbar:
- Appears when exactly one sliced file is selected
- Opens full PrintModal with plate selection and options
- Add to Queue button now uses Clock icon for clarity
- Improve mobile/PWA accessibility:
- Three-dot menu button always visible on mobile (md:opacity-0)
- Selection checkbox always visible on touch devices
- Better experience for PWA users on tablets and phones
Closes#94
- New useMultiPrinterFilamentMapping hook for parallel printer status fetching
- Per-printer custom slot configuration with "Custom mapping" checkbox
- Auto-configure using RFID data to match filaments by type/color
- Match status indicator (exact/partial/missing) under each printer
- Re-read button to refresh loaded filaments
- New setting: "Expand custom mapping by default" in Settings → Filament
- FilamentMapping component now accepts defaultExpanded prop
Frontend:
- PrinterSelector: Inline mapping editor for each selected printer
- PrintModal: Fetches settings, auto-expands mapping when setting enabled
- SettingsPage: Toggle for per_printer_mapping_expanded
Backend:
- Added per_printer_mapping_expanded to AppSettings schema
- Added boolean parsing in settings routes
- Added integration tests for new setting
Closes#104
that allows viewing and filtering application logs in real-time.
Features:
- Start/Stop live streaming with 2-second auto-refresh
- Filter by log level (DEBUG, INFO, WARNING, ERROR)
- Text search across messages and logger names
- Clear logs with one click
- Expandable multi-line log entries (stack traces, etc.)
- Auto-scroll to follow new entries
Closes#87
- Print from File Manager with plate selection, AMS mapping, and print options. Closes#94
- Add to queue without creating archive (reduces clutter)
- Queue displays library file name, thumbnail, and print time
Fixes:
Queue items from File Manager now reference library files directly instead of
creating archives upfront. Archives are created automatically when prints start.
Added ability to send prints or queue items to multiple printers at once:
- Checkbox selection for multiple printers in reprint and add-to-queue modes
- "Select all" / "Clear" buttons for quick printer selection
- Same filament slot mapping applies to all selected printers
- Progress indicator during multi-printer submission (e.g., "Sending 2/3...")
- Info message: "Slot mapping below applies to all N printers"
- Ideal for print farms with identical filament configurations
Implementation:
- Added multi-select props to PrinterSelector component
- Added computeAmsMapping() utility function for imperative AMS mapping
- Updated PrintModal to handle array of selected printers
- Created unified PrintModal.test.tsx replacing three separate test files
Closes#104
Users can now choose how camera streams open via Settings → General → Camera:
- "New Window" (default): Opens camera in a separate browser window
- "Embedded": Shows camera as a draggable, resizable overlay on the main screen
The embedded camera viewer features:
- Draggable header to reposition anywhere on screen
- Resizable via bottom-right corner handle
- Position and size persist to localStorage across sessions
- Minimize/maximize toggle
- Auto-reconnect with exponential backoff on stream errors
- Stall detection for frozen streams
Closes#105
Manually configure AMS slots for third-party or generic filaments:
1. Hover over an AMS slot on the printer card
2. Click the menu button (:material-dots-vertical:) that appears
3. Select **Configure Slot**
4. Choose a filament preset from your Bambu Studio cloud presets
5. Select a matching K profile (pressure advance calibration)
6. Optionally set a custom color using the color picker
7. Click **Configure Slot** to apply
**Color Picker Features:**
- Enter custom hex codes or color names (e.g., "brown", "FF8800")
- Live preview of selected color
- Expandable color picker in Configure AMS Slot modal:
- 8 basic colors shown by default
- 24 additional colors available via expand button
- Tests for ConfigureAmsSlotModal component
- Tests for AMS change callback
- Updated README with AMS slot configuration feature
- Wiki documentation for Configure AMS Slot feature
- Multi plate issue where plate names showed incorrect. #93
- Items from Queue end up as "source" files in archive. #107
- Added env variable support to change network port. #108
Docs -> https://wiki.bambuddy.cool/getting-started/docker/?h=port#custom-port
Remove unused imports (waitFor, screen, render, vi, userEvent) from
test files that were causing CI lint failures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
┌─────────────────────────┬───────────────────────────────────────────────────────────┐
│ File │ Fix │
├─────────────────────────┼───────────────────────────────────────────────────────────┤
│ CameraPage.tsx:75 │ Copy imgRef.current to a variable before cleanup │
├─────────────────────────┼───────────────────────────────────────────────────────────┤
│ useWebSocket.ts:121 │ Add processMessageQueue to useCallback dependencies │
├─────────────────────────┼───────────────────────────────────────────────────────────┤
│ SpoolmanSettings.tsx:89 │ Add eslint-disable comment (intentional debounce pattern) │
├─────────────────────────┼───────────────────────────────────────────────────────────┤
│ MQTTDebugModal.tsx:95 │ Wrap logs in useMemo to prevent recreating on each render │
├─────────────────────────┼───────────────────────────────────────────────────────────┤
│ Layout.tsx:166 │ Wrap navItemsMap and extLinksMap in useMemo │
├─────────────────────────┼───────────────────────────────────────────────────────────┤
│ KProfilesView.tsx:715 │ Wrap getProfileKey in useCallback │
├─────────────────────────┼───────────────────────────────────────────────────────────┤
│ GcodeViewer.tsx:171 │ Add eslint-disable comment (intentional behavior) │
└─────────────────────────┴───────────────────────────────────────────────────────────┘
Docker Integration Test Fix
The /api/v1/settings endpoint was returning 404 because:
1. The route was defined at /settings/ (with trailing slash)
2. Curl without -L doesn't follow redirects
3. The catch-all route was intercepting API paths
Fixes:
- Added routes for both with and without trailing slash in settings.py and notification_templates.py
- Updated catch-all in main.py to raise proper HTTPException for API routes
Test Results
- Frontend lint: 0 errors, 0 warnings
- TypeScript: No errors
- Backend unit tests: 264 passed
- Backend integration tests: 309 passed
When reprinting a multi-plate 3MF file exported with "All sliced file",
the system now asks which plate to print and only maps filaments for
that specific plate.
Backend changes:
- Add GET /archives/{id}/plates endpoint to list available plates
with thumbnails, print times, and per-plate filament requirements
- Add GET /archives/{id}/plate-thumbnail/{index} for plate thumbnails
- Update GET /archives/{id}/filament-requirements to accept plate_id
query parameter for filtering filaments by plate
- Add plate_id field to ReprintRequest schema
- Update POST /archives/{id}/reprint to use plate_id from request body
instead of auto-detecting (maintains backward compatibility)
Frontend changes:
- Add getArchivePlates API method
- Update getArchiveFilamentRequirements to accept optional plateId
- Update reprintArchive to accept plate_id in options
- Add plate selection UI to ReprintModal when multi-plate 3MF detected
- Show plate thumbnails, names, and filament counts in selection grid
- Require plate selection before printing multi-plate files
- Filter filament requirements to show only selected plate's filaments
Tests:
- Add unit tests for multi-plate slice_info.config parsing
- Add unit tests for plate detection from gcode files
- Add integration tests for new plate endpoints
- Add plug_type field to SmartPlug model ("tasmota" or "homeassistant")
- Add ha_entity_id field for Home Assistant entity reference
- Add global HA settings (ha_url, ha_token, ha_enabled) in Settings
- Create homeassistant_service.py with REST API calls for entity control
- Update smart_plug_manager to dispatch to correct service by plug_type
- Add HA entity discovery endpoint (/ha/entities)
- Add HA connection test endpoint (/ha/test-connection)
- Add Home Assistant tab in AddSmartPlugModal with entity dropdown
- Add HA settings section in Settings → Network tab
- Filter already-configured entities from dropdown
- Update backup/restore to include plug_type and ha_entity_id
- Add frontend tests for HA plug rendering
- Add backend integration tests for HA endpoints
- Update README and CHANGELOG
Closes#91
- Add f3d_path field to PrintArchive model and ArchiveResponse schema
- Add API endpoints for F3D upload/download/delete
- Add cyan badge on archive cards when F3D file is attached
- Add context menu options: Upload/Replace F3D, Download F3D, Remove F3D
- Include F3D files in backup/restore
- Add database migration for f3d_path column
- Add API tests for F3D endpoints
- Rebuild CHANGELOG from GitHub releases
Resolves#90
Fixes:
1. frontend/src/pages/SettingsPage.tsx - Added isSavingRef to prevent concurrent saves while a mutation is in progress
2. backend/app/services/mqtt_relay.py - Added 3-second timeout for MQTT connect to prevent blocking on unreachable brokers
Track individual parts/objects separately from print plates in projects.
Useful for multi-part builds like Voron where 25 plates produce 150 parts.
Backend:
- Add target_parts_count field to Project model
- Calculate parts_progress_percent and remaining_parts in stats
- Auto-detect quantity from 3MF printable objects when archiving
- Sum archive quantities for completed_count (parts)
- Use archive_count for plates progress
Frontend:
- Add "Target Parts" input in project create/edit modal
- Show separate progress bars for plates vs parts
- Stats footer displays both plates and parts count
- Header badge shows parts progress when target set
Scripts:
- Add update_archive_quantities.py to migrate existing archives
Tests:
- Add 5 integration tests for parts tracking
- Add 3 unit tests for 3MF object extraction
Closes#85
The spool detection only checked tray_uuid and tag_uid, but some Bambu Lab
spools have RFID read issues causing these to be missing while still having
valid tray_info_idx (Bambu filament preset ID like "GFA00").
Backend changes (backend/app/services/spoolman.py):
- Add tray_info_idx to AMSTray dataclass
- Parse tray_info_idx from raw MQTT data in parse_ams_tray()
- Check tray_info_idx first in is_bambu_lab_spool() - most reliable indicator
- Bambu Lab presets start with "GF" (GFA00, GFB00, GFL00, etc.)
- Fall back to tray_uuid then tag_uid for unique spool identification
- Add warning when Bambu spool detected but no unique ID for Spoolman
Backend changes (backend/app/api/routes/spoolman.py):
- Update /sync/{printer_id} and /sync-all to pass tray_info_idx
- Guard against empty spool_tag when adding to tracking set
Frontend changes (frontend/src/pages/PrintersPage.tsx):
- Add isBambuLabSpool() helper with same detection logic as backend
- Update all 4 vendor detection locations in AMS cards to use new helper
Detection priority (same on frontend and backend):
1. tray_info_idx starts with "GF" (Bambu preset ID)
2. tray_uuid is valid 32-char hex (non-zero)
3. tag_uid is valid 16-char hex (non-zero)
Backend:
- Count print job records instead of summing quantities for stats
- Include "archived" status as successful (completed + archived)
- Include all failure states (failed, aborted, cancelled, stopped)
- Add completed_count and failed_count to ProjectListResponse
Frontend ProjectsPage:
- Show completed_count instead of total_items in progress displays
- Progress bar now shows "X / Y completed" to match percentage
- Add failed count indicator (red) when failures exist
- Footer stats show completed (green checkmark) and failed (red) separately
Frontend ProjectDetailPage:
- Replace confusing "Total Items" card with "Print Jobs" card
- Show "X successful" as main value, "Y failed" below in red
- Show "Z in progress" in yellow for unfinished prints
- Progress bar text changed from "items" to "completed"
- Add hint prop to StatCard component for tooltips
This ensures stats are consistent between:
- Project list page and detail page
- Stats cards and archive list counts
- Progress percentages and displayed fractions
Fixes#85
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
- Moved BAMBU_HEX_COLORS mapping and getColorName()/hexToColorName() functions to a shared location
- Exported functions for use across components
- Updated modals to use shared colors:
- ReprintModal.tsx - removed ~100 lines of duplicate color code, now imports from utils/colors
- AddToQueueModal.tsx - same cleanup
- EditQueueItemModal.tsx - same cleanup
- Added AMS mapping to EditQueueItemModal:
- Added state for showFilamentMapping, isRefreshing, manualMappings
- Added queries for filament requirements and printer status
- Added loadedFilaments, filamentComparison, amsMapping memos
- Added collapsible filament mapping UI section (same as other modals)
- Initializes manualMappings from existing item.ams_mapping
- Submits ams_mapping when saving changes
- Backend changes:
- Added ams_mapping field to PrintQueueItemUpdate schema
- Updated update_queue_item route to serialize ams_mapping to JSON
- Frontend API client:
- Added ams_mapping to PrintQueueItemUpdate interface
- Changed "Print" to "Reprint" on archive card button (ArchivesPage.tsx:690)
- Bumped service worker to v22
- 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
- New BatchProjectModal component for assigning archives to projects
- "Project" button in selection toolbar (next to Tags)
- Select a project to assign all selected archives at once
- "Remove from project" option to clear assignments in bulk
- Proper query invalidation for Projects page and Project Detail page
Closes#70
- Add light toggle button next to camera button at bottom of card
- Custom ChamberLight SVG icon with on/off states (yellow fill + rays when on)
- Backend endpoint: POST /printers/{id}/chamber-light?on=true|false
- Optimistic UI update for instant feedback
- Works on all Bambu Lab printers (X1, P1, A1, H2D)
- H2D dual chamber lights controlled together
Closes#67
Multi-color prints store comma-separated values in filament_color (e.g.,
"#FFFFFF,#43403D") and filament_type (e.g., "PLA, PLA-S"). The project
cards were displaying these raw strings instead of splitting them into
individual badges.
- Split comma-separated colors into individual color swatches
- Split comma-separated materials into individual text badges
- Deduplicate both to avoid showing "PLA" multiple times
Problem: The backend stores timestamps in UTC, but when sending them to the frontend without timezone indicators, JavaScript interprets them inconsistently. Users saw UTC times displayed directly instead of converted to their local timezone (e.g., America/New_York).
Solution implemented:
1. Created /frontend/src/utils/date.ts with utilities:
- parseUTCDate(dateStr) - Parses backend timestamps as UTC by appending 'Z' to strings without timezone indicators
- formatDate(dateStr, options) - Formats UTC timestamps to local date/time
- formatDateOnly(dateStr, options) - Formats UTC timestamps to local date-only
2. Updated 15 frontend files to use the new utilities:
- ArchivesPage.tsx - Archive cards and list view dates
- PrintersPage.tsx - Last print date
- SettingsPage.tsx - API key last used date
- QueuePage.tsx - Scheduled print times
- ProjectDetailPage.tsx - Due dates and timeline
- ProfilesPage.tsx - Relative time display
- CalendarView.tsx - Calendar grouping by local date
- NotificationLogViewer.tsx - Log timestamps
- NotificationProviderCard.tsx - Last success date
- AMSHistoryModal.tsx - Chart data timestamps
- FilamentTrends.tsx - Usage data grouping
- PrinterQueueWidget.tsx - Queue item dates
Resolves FTP transfer failures on A1/A1 Mini printers caused by SSL
session reuse incompatibility. Also adds configurable FTP timeout
setting for slow WiFi connections.
Changes:
- Add SSL session reuse bypass for A1/A1 Mini printers (auto-detected)
- Add printer_model parameter to FTP functions for model detection
- Add configurable FTP timeout (10-120s, default 30s) in Settings
- Update all FTP callers: archiving, reprint, timelapse, firmware upload
Technical details:
- A1/A1 Mini printers don't support vsFTPd's SSL session reuse
- X1C/P1S continue to use session reuse as required by their FTP server
- ImplicitFTP_TLS now accepts skip_session_reuse flag
- BambuFTPClient auto-detects A1/A1 Mini and sets flag accordingly