- 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.
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
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
When exporting individual plates from a multi-plate 3MF in Bambu Studio,
all uploaded archives showed plate 1's name and thumbnail regardless of
which plate was actually exported.
Root cause: The 3MF parser used an incorrect XPath lookup (plate_idx
attribute) and didn't extract the plate index from slice_info.config
metadata.
Changes:
- Extract plate index from <metadata key="index" value="N"/> in
slice_info.config
- Remove incorrect plate[@plate_idx='N'] XPath lookup that doesn't
work for single-plate exports
- Set self.plate_number from extracted index so _extract_thumbnail()
uses the correct plate thumbnail (e.g., plate_5.png instead of
plate_1.png)
- Append " - Plate N" to print_name when plate index > 1 to
distinguish multi-plate exports
- Add 7 unit tests for plate index extraction and print_name
enhancement
- 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
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
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
Fix Applied:
1. Added supports_chamber_temp() helper function that returns True only for X1/X1C/X1E, P2S, and H2 series
2. Modified printer_state_to_dict() to filter out chamber, chamber_target, chamber_heating from temperatures for unsupported models
3. Added model caching in PrinterManager so we can look up the model without a database query
4. Updated all callers (main.py, websocket.py) to pass the model
The chamber temperature widget will now simply not appear for P1S/P1P/A1/A1Mini printers since the temperatures.chamber field won't be sent to the frontend.
- 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)
- 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
- Correct SSDP model codes: C11=P1P, C12=P1S, N7=P2S, C13=X1E
- Fix serial prefixes based on actual Bambu serial format
- Add confirmation modal for pending upload discard
- Sort model dropdown alphabetically, remove internal codes
- Add "Setup Required" warning with link to wiki documentation
- Update wiki with certificate installation and platform setup guides
Features:
- Configurable printer model for virtual printer emulation
- Supports X1 series (X1C, X1, X1E), P series (P1S, P1P, P2S),
A1 series (A1, A1 Mini), and H2 series (H2D, H2C, H2S)
- Dropdown in Settings > Virtual Printer to select model
- Model affects SSDP discovery and slicer compatibility
- Model change restarts virtual printer services automatically
Backend:
- Added VIRTUAL_PRINTER_MODELS mapping in manager.py
- Added virtual_printer_model setting in database
- New GET /api/v1/settings/virtual-printer/models endpoint
- Updated PUT /api/v1/settings/virtual-printer to accept model
Frontend:
- Added model dropdown to VirtualPrinterSettings component
- Status display shows selected model name
- Model change disabled while virtual printer is running
Tests:
- Added 3 unit tests for model configuration
- Updated frontend test mocks for getModels API
Features:
- Resizable printer cards (S/M/L/XL) with toolbar controls on Printers page
- Queue Only mode for staging prints without automatic scheduling
- "Queue Only" option in add/edit queue modals
- Purple "Staged" badge and Play button to release to queue
- manual_start field in database with migration
Fixes:
- Improved camera stream stuck detection with automatic reconnection
Tests:
- Added 16 integration tests for print queue API endpoints
- New APIBrowser component with full OpenAPI schema integration
- Fetches and parses /openapi.json automatically
- Groups endpoints by API tags (printers, archives, settings, etc.)
- Expandable endpoint sections with color-coded method badges
- Path parameter, query parameter, and JSON body editors
- Auto-populates request body with schema examples
- Live API request execution with response display
- Response shows status code, timing, and formatted JSON
- Copy response button with clipboard fallback
- Search to filter endpoints across all categories
- Expand All / Collapse All buttons
- Link to Swagger UI (/docs)
- Two-column layout for API Keys tab
- Left: API key management + webhook documentation
- Right: API Browser with dedicated test key input
- Parameter validation
- Shows warning for missing required parameters
- Validates before sending requests to avoid 422 errors
- UX improvements
- "Use in API Browser" button on newly created keys
- Responsive layout (stacked on mobile, side-by-side on xl+)
- Skip Objects Modal:
- Object ID markers overlaid on preview image (grid layout)
- Large prominent ID badges to match printer display
- Info banner explaining to match IDs with printer screen
- Layer warning when skip unavailable (layer <= 1)
- Red badge on button showing skipped count
- Preview image now shown when paused (not just running)
- Close with ESC key or click outside modal
- Full light/dark theme support
- Backend:
- Extract object positions from 3MF (include_positions=True)
- API returns x/y coordinates for objects (when available)
- Parse s_obj from printer MQTT for skipped state persistence
- Cover URL available in PAUSE/PAUSED states
- Tests:
- Added test for objects with position data
Implement comprehensive theme customization with independent settings for
dark and light modes:
- Style layer: Classic (clean shadows), Glow (accent-colored glow effects),
Vibrant (dramatic deep shadows)
- Background layer: Neutral, Warm, Cool (light mode); plus OLED, Slate,
Forest (dark mode only)
- Accent colors: Green, Teal, Blue, Orange, Purple, Red
All combinations work independently (e.g., Glow + Forest + Teal). Settings
sync across devices via database and show toast confirmations on change.
Backend:
- Add 6 new settings fields (dark_style, dark_background, dark_accent,
light_style, light_background, light_accent)
- Add integration test for theme settings API
Frontend:
- Refactor index.css with 3-layer CSS variable system
- Update ThemeContext for dual-mode theme management
- Add Appearance section to Settings page with 6 dropdowns
- Update components for new ThemeContext API
- Virtual printer appears in Bambu Studio/Orca Slicer via SSDP discovery
- Secure TLS/MQTT communication with auto-generated certificates
- Queue mode (pending uploads) or auto-start mode
- Configurable access code for authentication
- Docker support with network_mode: host and certificate persistence
- Fix backup/restore for virtual printer settings (auto-save no longer overwrites)
- Features:
- Tasmota device discovery: Auto-detect network and scan for Tasmota devices
in Add Smart Plug modal. Supports devices with/without authentication.
- Switchbar: Quick access widget in sidebar footer for controlling smart plugs
from anywhere in the app. Shows real-time status and power consumption.
- Detect Docker environment automatically via /.dockerenv and cgroup
- Show subnet input field in Add Printer dialog when running in Docker
- Scan IP range for Bambu printer ports (8883 MQTT, 990 FTPS)
- Query SSDP to get printer name/serial/model from discovered IPs
- Map raw SSDP model codes to friendly names (BL-P001→X1C, O1D→H2D)
1. "Unknown Status (255)" bug
File: backend/app/services/printer_manager.py:291-293
Problem: A1/P1 printers send stg_cur=255 when idle (unlike X1 which uses -1). The code treated 255 as a valid stage number and returned "Unknown stage (255)".
Fix: Changed the condition from if state.stg_cur >= 0: to if 0 <= state.stg_cur < 255: so that 255 is now treated as "no calibration stage" (same as -1).
2. AMS humidity/temp displaced on mobile
File: frontend/src/pages/PrintersPage.tsx:1164-1212
Problem: The AMS row used a rigid horizontal flex layout that didn't adapt to narrow mobile screens, causing humidity/temperature indicators to be displaced.
Fix:
- Added flex-wrap to allow wrapping on very narrow screens
- Added ml-auto to humidity/temp section to keep it right-aligned
- Added max-w-[60px] sm:max-w-none to truncate long filament names on mobile
- Changed gap from fixed gap-3 to responsive gap-2 sm:gap-3
Tests Added
4 new unit tests for get_derived_status_name:
- test_stg_cur_255_returns_none - Verifies A1/P1 idle state
- test_stg_cur_negative_one_returns_none_when_idle - Verifies X1 idle state
- test_valid_stage_returns_name - Verifies valid stages work
- test_stg_cur_zero_returns_printing - Verifies printing state
All 382 tests pass.
Test Summary:
- Build tests: 3 passed (image build, backend imports, static files)
- Backend unit tests: 378 passed (9 docker tests excluded)
- Frontend unit tests: 137 passed
- Integration tests: 9 passed (health, API endpoints, persistence, WebSocket)
Changes made to fix the Docker test suite:
1. Added curl to the production Dockerfile for integration tests
2. Removed deprecated version attribute from docker-compose.test.yml
3. Added --pull flag to all build commands to ensure fresh images
4. Added explicit build step before starting integration container
5. Fixed WebSocket test to accept 200 as a valid response
6. Excluded docker-marked tests from backend unit test runs (-m "not docker")
- H2D sends timelapse status in ipcam.timelapse field, not xcam.timelapse
- Added parsing for timelapse from ipcam data
- Added test to prevent regression
2. Systemd service updated - /etc/systemd/system/bambuddy.service
- Now runs as claude user instead of root
- Added ExecStartPre commands with + prefix (runs as root) to:
- Create logs and archive directories if missing
- Fix ownership to claude:claude before service starts
The service restarted successfully with the permission fixes running at startup. On the next print with timelapse enabled, both the snapshot and timelapse should be captured automatically.
1. bambu_mqtt.py: Added hms_errors to the completion callback data, which includes the HMS error codes when a print fails
2. archive.py: Updated update_archive_status() to accept an optional failure_reason parameter
3. main.py: Added auto-detection of failure reasons:
- status == "aborted" → failure_reason = "User cancelled"
- status == "failed" with HMS errors → maps module codes to reasons:
- Module 0x07 (Filament) → "Filament runout"
- Module 0x0C (Motion Controller) → "Layer shift"
- Module 0x05 (Nozzle) → "Clogged nozzle"
Frontend changes:
1. ArchivesPage.tsx:
- Badge now shows "cancelled" for aborted prints, "failed" for failed prints
- "Failed Prints" collection and "Hide Failed" filter now include both failed and aborted statuses
2. EditArchiveModal.tsx: Failure reason field now shows for both failed and aborted prints
New tests added:
- test_hms_errors_included_in_failed_completion_callback
- test_aborted_status_when_cancelled
The HMS error module mapping is basic and can be expanded as you observe more failure types. The actual Bambu HMS error codes are documented in their wiki - we can add
more mappings as needed.
Problem: xcam data was parsed BEFORE _was_running was set to True, so timelapse detection failed when both arrived in the same message.
Fix: Added timelapse detection at two critical points:
# Lines 1375-1381: When entering RUNNING state
if self.state.state == "RUNNING" and current_file:
if not self._was_running:
if self.state.timelapse:
self._timelapse_during_print = True
logger.info(f"[{self.serial_number}] Timelapse detected when entering RUNNING state")
# Lines 1391-1399: When print start detected
if self.state.timelapse:
self._timelapse_during_print = True
logger.info(f"[{self.serial_number}] Timelapse detected at print start")
2. Callback Error Handling (main.py + printer_manager.py)
Problem: The on_print_complete callback was hanging silently - no logs after "Found archive".
Fixes:
- Added try/except wrapper around "Update archive status" section (line 749-767)
- Added [CALLBACK] log messages at start and end of callback
- Added exception handler to _schedule_async in printer_manager.py to log any exceptions from async callbacks
3. New Integration Tests (test_bambu_mqtt.py)
Added TestRealisticMessageFlow class with 5 new tests that simulate real MQTT message sequences:
1. test_timelapse_detected_at_print_start_in_same_message - The critical race condition test
2. test_timelapse_not_detected_when_disabled
3. test_timelapse_detected_when_enabled_after_print_start
4. test_print_complete_includes_timelapse_flag_full_flow - Full lifecycle test
5. test_print_failed_includes_timelapse_flag
These tests process messages through _process_message() with proper {"print": {...}} wrapping, matching real MQTT flow.
Files Modified:
- backend/app/services/bambu_mqtt.py - Timelapse race condition fix
- backend/app/main.py - Error handling + logging for on_print_complete
- backend/app/services/printer_manager.py - Exception capture in _schedule_async
- backend/tests/unit/services/test_bambu_mqtt.py - New integration tests
Next Steps
You should restart the application to apply these changes. The next print will show:
- [CALLBACK] on_print_complete started for printer X
- [ARCHIVE] Updating archive X status...
- [ENERGY], [PHOTO], [AUTO-OFF] logs (if those features trigger)
- [CALLBACK] on_print_complete finished for printer X, archive X
Expanded the useWebSocket.test.ts from minimal coverage to 20 comprehensive tests covering:
WebSocket Mock Tests (6 tests)
- Creates WebSocket with correct URL
- Starts in CONNECTING state
- Transitions to OPEN state
- Can receive messages
- Can close connection
- Tracks all instances
Hook Connection Tests (2 tests)
- Connects to WebSocket on mount
- Reports connected state when WebSocket opens
Message Handling Tests (9 tests)
- Updates printer status in query cache on printer_status message
- Preserves wifi_signal when new value is null
- Invalidates archives on print_complete message
- Invalidates archives on archive_created message
- Invalidates archives on archive_updated message (new handler for timelapse auto-assignment)
- Ignores pong messages without error
- Handles malformed JSON gracefully
- Handles unknown message types gracefully
sendMessage Tests (2 tests)
- Sends JSON message when connected
- Does not send when disconnected
Reconnection Tests (2 tests)
- Reconnects after connection closes
- Cleans up on unmount
Key Fixes
- Fixed MSW (Mock Service Worker) conflict by:
a. Adding addEventListener/removeEventListener to MockWebSocket class
b. Updating MSW setup to bypass WebSocket requests
c. Properly managing WebSocket mock lifecycle in each test
Test Results:
- Frontend: 137 tests passed (9 test files)
- Backend: 346 tests passed
Backend:
- pytest configuration with async support and coverage
- Unit tests for notification service (23 tests)
- Unit tests for smart plug manager (12 tests)
- Unit tests for archive service (16 tests)
- Integration tests for API endpoints
- Fix: notifications now send immediately (digest is summary only)
Frontend:
- Vitest configuration with jsdom and coverage
- MSW for API mocking
- Component tests for Toggle, Button, Card, ConfirmModal (77 tests)
- Test utilities with custom render wrapper
CI/CD:
- GitHub Actions workflow for automated testing
- Backend lint, unit tests, integration tests
- Frontend lint, type-check, unit tests, build