SMTP settings endpoints (GET/POST /auth/smtp, POST /auth/smtp/test)
used Depends(get_current_active_user) which always requires a logged-in
user. Replaced with RequirePermissionIfAuthEnabled to match the pattern
used by all other settings endpoints — accessible when auth is disabled,
permission-gated when auth is enabled.
The Bambu Cloud API returns 400 for many filament IDs (e.g. GFB01,
GFU99, GFL99), causing nozzle rack hover cards to fall back to
abbreviated tray_type values ("ASA", "TPU", "PLA") instead of full
names.
Added a built-in lookup table of 86 known Bambu filament codes as a
Phase 4 fallback in get_filament_info. Resolution order is now:
cache → cloud API → local profiles → built-in table → empty fallback.
GFB01 → "Bambu ASA", GFU99 → "Generic TPU", GFL99 → "Generic PLA",
etc. Also benefits AMS tray tooltips for unresolvable filament IDs.
1. H2C printer card was showing the H2D image — added dedicated
h2c.png and updated getPrinterImage() mapping.
2. Nozzle rack hover card showed raw filament IDs (e.g. "GFU99")
instead of human-readable names. Now resolves names via 3-tier
fallback: Bambu Cloud → local slicer profiles → raw ID.
- Frontend: nozzle rack filament_id values included in cloud
lookup query; NozzleSlotHoverCard displays resolved name.
- Backend: get_filament_info endpoint refactored from cloud-only
to cache → cloud → local profiles. Matches local presets by
setting_id in the imported OrcaSlicer JSON blob.
raw_data["ams"] is stored as a list by the MQTT handler, but the
support info code only checked for a nested dict format. AMS unit
and tray counts were always 0.
- Show all 6 nozzles including mounted: backend includes all nozzle_info
entries (removes id >= 2 filter), frontend filters to non-empty
- Translate nozzle type codes to full names: HS→Hardened Steel, 00/01/05
mapped to Stainless Steel/Hardened Steel/Tungsten Carbide
- Add flow type to hover card: HH→High Flow, HS→Standard
- Show filament material type in hover card (PLA, PETG etc.) from MQTT
tray_type/filament_type field
- Add filament_type to NozzleRackSlot schema (backend + frontend)
- Add translations (en, de, ja, it): nozzleTungstenCarbide, nozzleFlow,
nozzleHighFlow, nozzleStandardFlow
The cover image cache was keyed by subtask_name and never invalidated
between prints. When a user printed the same project name twice with a
different bed layout, the cached thumbnail from the first print was
returned instead of fetching the new one from the printer.
Clear the cover cache for the printer on every print start so the next
cover request downloads a fresh 3MF and extracts the current thumbnail.
- Fixed Edit User modal to populate email field
- Added Reset Password button to Edit User modal (advanced auth only)
- Added "Welcome Email" template for user creation
- Added "Password Reset" email template
- Removed scrollbar from Settings menu tabs
Co-authored-by: cadtoolbox <12723486+cadtoolbox@users.noreply.github.com>
Users who use OrcaSlicer without Bambu Cloud can now import slicer
presets directly into Bambuddy. Supports .orca_filament, .bbscfg,
.bbsflmt, .zip, and .json exports with automatic inheritance resolution
via OrcaSlicer's GitHub base profiles (cached with 7-day TTL).
Capture 4 additional fields from MQTT nozzle data (max_temp, serial_number,
filament_color, filament_id) and surface them through REST/WebSocket APIs.
Add per-slot hover popover to the NozzleRackCard showing all metrics, filament
color backgrounds on slots, and i18n labels in all 4 locales.
The H2C printer has a tool-changer with a 6-nozzle rack, but
device.nozzle.info entries beyond index 1 were being dropped due to a
hardcoded 2-nozzle limit. This adds full nozzle rack storage, API
exposure, and a frontend card showing all dock positions.
The Add Printer dialog previously required users to manually enter their
network subnet for scanning (defaulting to 192.168.1.0/24). Now the
backend detects available network interfaces and returns their subnets
via the /discovery/info endpoint. The frontend auto-selects the first
detected subnet and shows a dropdown when multiple subnets are available,
falling back to a text input if none are detected.
AMS Lite units (A1 series) have no weight sensor and always report 0%
fill level. When a spool is linked to Spoolman with weight data, use
Spoolman's remaining weight as a fallback. External spools also show
fill level from Spoolman data instead of always showing unknown.
Backend: Enrich GET /spoolman/spools/linked response with
remaining_weight and filament_weight alongside spool ID.
Frontend: Add getSpoolmanFillLevel() helper. Update regular AMS, HT
AMS, and external spool fill computations to use Spoolman fallback
when AMS reports 0%. Show "(Spoolman)" indicator in hover card when
fill data comes from Spoolman.
Implements retry mechanism to handle intermittent network errors when
fetching spools cache for AMS sync operations.
Changes:
- Add retry logic to get_spools() with 3 attempts and 500ms delay
- Configure httpx client with connection pool limits to prevent stale
connection reuse (max_keepalive_connections=5, keepalive_expiry=30s)
- Recreate client on connection errors (ReadError, RemoteProtocolError)
- Abort sync operations if cache fetch fails after all retries
- Update on_ams_change, sync_single_printer, and sync_all_printers to
handle cache fetch failures gracefully
This addresses ReadError(ClosedResourceError()) failures that occurred
intermittently when Spoolman closed idle connections or connection
pooling reused stale connections.
Testing:
- Added 4 new unit tests for retry behavior
- All 1018 tests passing
- Add cached_spools parameter to find_spool_by_tag, find_spools_by_location_prefix, sync_ams_tray, and clear_location_for_removed_spools
- Fetch spools once before loops in on_ams_change, sync_single_printer, and sync_all_printers endpoints
- Cache newly created spools during sync to avoid duplicate API calls
- Add 5 unit tests for caching functionality (all passing)
- Reduce redundant API calls when syncing multiple AMS trays
- Improve sync performance for users with large spool databases
- Maintain backward compatibility with optional cached_spools parameters
- Add HA_URL and HA_TOKEN environment variables for automatic HA
integration configuration in HA add-on deployments
- Environment variables always override database settings with
non-negotiable precedence; database values preserved for fallback
- Auto-enable integration when both env vars are set; partial config
(one env var) uses database enable state without auto-enabling
- Add centralized get_homeassistant_settings() function following
Spoolman pattern; replace direct database queries across codebase
- Add ha_url_from_env, ha_token_from_env, ha_env_managed fields to
AppSettings schema to inform frontend about configuration source
- UI shows read-only fields with lock icons and "(Environment Managed)"
labels when env-controlled; toggle shows auto-enable badge
- Add comprehensive test coverage: 9 integration + 8 unit tests
Closes#283
homeassistant_service.get_energy() was called without first configuring
the service with the HA URL and token, so it returned None for all
Home Assistant smart plugs. Added configure() call before the plug
loop, matching the pattern used in main.py and smart_plugs.py.
The CodeQL cleanup in "Housekeeping" (2b11efd) bulk-narrowed except
clauses across 50+ files, breaking FTP uploads on ALL printer models.
ftplib.error_perm (550 errors) is not a subclass of ftplib.error_reply,
so diagnose_storage() CWD failures escaped the handler and prevented
STOR from ever executing — causing 100% upload failure and HTTP 500s
on /api/v1/archives/{id}/reprint and /api/v1/library/files/{id}/print.
FTP fixes:
- Remove diagnose_storage() from upload hot path
- Change all except (OSError, ftplib.error_reply) to
except (OSError, ftplib.Error) across bambu_ftp.py
Exception handling reverts (9 files):
- Revert narrowed except clauses back to except Exception in route
handlers and service code where broad catches are intentional
defensive programming (archive parsing, HTTP clients, 3MF/ZIP
processing, Home Assistant, firmware checks)
- Keep narrow exceptions only where safe (single-op blocks like
int(), file.unlink(), socket.close())
- Remove unused XMLParseError imports from archive.py, threemf_tools.py
Version system:
- Add 4-segment version support (e.g. 0.1.8.1) for patch releases
- Bump version to 0.1.8.1
Closes#287
The CodeQL cleanup in "Housekeeping" (2b11efd) bulk-narrowed except
clauses across 50+ files, breaking FTP uploads on ALL printer models.
ftplib.error_perm (550 errors) is not a subclass of ftplib.error_reply,
so diagnose_storage() CWD failures escaped the handler and prevented
STOR from ever executing — causing 100% upload failure and HTTP 500s
on /api/v1/archives/{id}/reprint and /api/v1/library/files/{id}/print.
FTP fixes:
- Remove diagnose_storage() from upload hot path
- Change all except (OSError, ftplib.error_reply) to
except (OSError, ftplib.Error) across bambu_ftp.py
Exception handling reverts (9 files):
- Revert narrowed except clauses back to except Exception in route
handlers and service code where broad catches are intentional
defensive programming (archive parsing, HTTP clients, 3MF/ZIP
processing, Home Assistant, firmware checks)
- Keep narrow exceptions only where safe (single-op blocks like
int(), file.unlink(), socket.close())
- Remove unused XMLParseError imports from archive.py, threemf_tools.py
Closes#287
Several FTP operations (file browser, timelapse scan, storage info,
cover download, skip objects, etc.) were missing the printer_model
parameter. Without it, A1/A1 Mini and PS1 printers can't use the prot_p/prot_c
auto-detection and fallback logic, causing FTP failures on these models
when the mode cache isn't already populated.
Simplify always-true authEnabled ternary and localSettings truthiness
checks in SettingsPage.tsx. Remove commented-out auth re-setup guard
and its dead _existing_setting/_user_count queries from auth.py.
Add clarifying comments to firmware_check.py api_key logs (model
identifier, not a secret).
Remove vestigial _debug_logging_enabled and _debug_logging_enabled_at
globals from support.py (written but never read; DB is queried directly).
Simplify hue classification in PrintersPage.tsx and colors.ts by removing
always-true h < 345 checks and dead 'Unknown' fallbacks. Narrow
getWifiStrength param type to remove always-false null guard.
- Remove 28 unused imports across 22 test files
- Prefix 4 unused local variables with _ in app code
(archives, bambu_mqtt, main) and remove 1 dead store
- Consolidate import/import-from in test_plate_detection.py
- Fix unreachable statement in test_archive_service.py
- Simplify redundant comparison in timelapse_processor.py
Resolves ~50 CodeQL py/unused-import, py/unused-local-variable,
py/import-and-import-from, py/unreachable-statement, and
py/redundant-comparison findings.
These modules were already imported at the top of each file.
Removes re-imports of re, json, zipfile, and logging from
inside functions in archive.py, library.py, main.py,
printers.py, support.py, and test_library_api.py.
Resolves all 30 CodeQL py/repeated-import findings.
- Replace number inputs with select dropdowns for retry attempts,
retry delay, and connection timeout to avoid auto-save race conditions
- Move connection timeout inside the FTP retry toggle section
- Add ftp_timeout to backend settings schema and integer parsing list
so the value actually persists (was silently dropped before)
Closes#275
- Fix defusedxml import style in print_queue.py to be recognized by Bandit
(use `import defusedxml.ElementTree as ET` not `from defusedxml import`)
- Update Trivy scanner version from 0.65.0 to 0.69.1
Security scan (Bandit) identified vulnerable XML parsing in 3MF file
processing. The standard xml.etree.ElementTree is vulnerable to XXE
(XML External Entity) attacks.
Changes:
- Add defusedxml>=0.7.0 to requirements.txt
- Replace all xml.etree.ElementTree imports with defusedxml.ElementTree
in production code (6 files)
Affected files:
- backend/app/services/archive.py
- backend/app/services/print_scheduler.py
- backend/app/api/routes/print_queue.py
- backend/app/api/routes/library.py
- backend/app/api/routes/printers.py
- backend/app/api/routes/archives.py
Test files intentionally left unchanged (test XML is trusted).
Implement accurate per-filament usage tracking for Spoolman integration,
similar to OpenSpoolman v0.3.0. This replaces the previous single-spool
reporting with multi-material aware tracking.
Features:
- Parse G-code from 3MF files at print start to build per-layer,
per-filament cumulative extrusion maps
- Store tracking data in new `active_print_spoolman` database table
(survives server restarts for long prints)
- Report accurate partial usage when prints fail/cancel based on
actual layer progress and G-code data
- Add "Disable AMS Weight Sync" setting to prevent AMS percentage-based
weight estimates from overwriting Spoolman's granular tracking
- Add "Report Partial Usage for Failed Prints" toggle (only shown when
weight sync is disabled)
- Use Spoolman's filament density instead of defaults for mm-to-grams
conversion
- Prefer tray_uuid over tag_uid for spool identification