Move _notified_hms_errors tracking update before the async notification
send to prevent concurrent status callbacks from both seeing the same
errors as new. This mirrors the existing milestone tracking pattern
which correctly updates before the await.
Function-scoped ftp_server meant 67 TLS server start/stop cycles.
Class-scoped reduces to 10. Adds per-test cleanup fixture to reset
failure injections and filesystem between tests. Isolates
test_disconnect_after_server_gone into its own class to prevent
close_all() from nuking other servers' asyncore sockets.
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.
Three fixes for the H2C nozzle rack reported by testers:
1. Empty slot position: rack nozzles were mapped by sequential array
index instead of by actual nozzle ID (16-21), so a missing slot
always appeared at position 6 instead of its real position. Now
maps each nozzle to its correct rack position via id offset.
2. Missing filament colors/materials: the H2C MQTT payload uses
different field names than H2D — color_m (not filament_colour),
fila_id (not filament_id), sn (not serial_number), tm (not
max_temp). Added fallback field name resolution in the backend
MQTT parser.
3. Medium card layout: the temperature row with nozzle rack overflowed
on medium-sized cards. Added flex-wrap and a 190px flex-basis on
the rack card so it wraps to a second line when space is tight.
AMS-HT global tray ID was calculated as ams_id * 4 (= 512 for unit 128)
but AMS-HT uses the raw ams_id directly since it has a single tray.
The backend misidentified 512 as an external spool, producing wrong
ams_mapping2. Fixed in 4 locations: frontend getGlobalTrayId(), backend
start_print() ams_mapping2 builder, print scheduler, and Spoolman tracking.
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
Timelapse: Replace mtime-based "most recent file" approach with a
snapshot-diff strategy — baseline existing MP4 filenames before waiting,
then detect the new file that appears after encoding. Fixes wrong
timelapse in LAN-only mode where printer clock is unsynchronized.
Falls back to print-name matching if no new file appears after retries.
Calibration: Skip archiving internal printer files under /usr/ (e.g.
/usr/etc/print/auto_cali_for_user.gcode) so calibration prints don't
appear in the archive.
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.
The remote_interface_ip setting only worked in proxy mode but was
completely ignored in server modes (immediate/review/print_queue).
Users with multiple NICs (LAN + Tailscale, Docker bridges) got wrong
auto-detected IP in SSDP broadcasts and TLS certificates.
Users can now choose their preferred slicer (Bambu Studio or OrcaSlicer)
in Settings → General. All "Open in Slicer" buttons across Archives,
3D Preview, and context menus use the correct protocol for the selected
slicer. OrcaSlicer uses orcaslicer://open?file=<URL> on all platforms;
Bambu Studio uses bambustudio:// (Windows) or bambustudioopen:// (macOS/
Linux). Default remains Bambu Studio for backward compatibility.
H2C was mapped to the "h2d" firmware API key, causing it to check
the H2D firmware page and offer H2D firmware for download. H2C has
its own separate firmware track (01.01.x.x vs H2D's 01.02.x.x).
Changed H2C to use its own "h2c" API key and added the reverse
mapping. Also added missing H2C (O1C) and H2S (O1S) entries to
the printer model ID and 3MF model name maps.
The nozzle_info MQTT array contains L/R nozzle heads (IDs 0, 1) and
rack slots (IDs 16-21). Backend was dumping all entries into nozzle_rack,
and frontend slice(0,6) grabbed L, R, plus only 4 rack slots — cutting
off the last position (e.g. the 0.6mm nozzle) and showing the mounted
nozzle as docked.
Backend: filter nozzle_rack to id >= 2 (rack-only), sort by ID.
Frontend: compact single-row layout with bottom accent bars for
mounted/docked status, wider rack card (flex-[2]), vertically
centered temp cards.
- 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.
This might fix (#58) getting uploads from the linux version of the
BambuStudio network plugin. Issue observer is connection resets with
larger uploads (somewhere >80k).
Large 3MF uploads intermittently failed with [Errno 104] Connection
reset by peer while the 16-byte verify_job always succeeded. The
_handle_data_connection callback returned immediately, letting the
asyncio task complete while cmd_STOR was still reading from the data
connection. The passive port listener also stayed open during transfers.
- Keep _handle_data_connection alive via _transfer_done event so the
asyncio task holds strong references throughout the transfer
- Close passive port listener after accepting the data connection
- Reject duplicate data connections with a warning log
- Add drain timeout (5s) to MQTT status pushes to prevent blocking
when the slicer is busy with FTP upload
- Improve error logging with bytes received and exception type
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.
H2D Pro firmware interprets use_ams as a nozzle index when sent as
integer (1 = deputy nozzle) instead of boolean. Bambu Studio sends
use_ams: true (boolean) while using integers for calibration fields.
Bambuddy was converting all boolean fields to integers for H2D series,
routing filament to the deputy nozzle instead of main, causing
extrusion motor overload at ~75% print completion.
Keep use_ams as boolean for all printers, matching Bambu Studio format.
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