From 220717f174e93644bbcac2d5bdef94ac02ae95b9 Mon Sep 17 00:00:00 2001 From: maziggy Date: Fri, 13 Mar 2026 12:08:11 +0100 Subject: [PATCH] Fix inventory usage tracker missing external spool mapping (#677) When all higher-priority slot-to-tray mapping methods failed (MQTT mapping, print command mapping, queue mapping, color matching), the internal usage tracker fell back to slot_id - 1 which can never reach external spool IDs (254/255) or AMS-HT IDs (128+). Added position-based resolution using sorted available tray IDs, matching the Spoolman fix from PR #686. --- CHANGELOG.md | 1 + backend/app/services/usage_tracker.py | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2764da17..77db61651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ All notable changes to Bambuddy will be documented in this file. - **Debug Logging Banner Timer Shows Negative Time** — When enabling debug logging, the banner showed a negative duration (e.g. "-60m -59s") equal to the server's UTC offset. The `enabled_at` timestamp was stored using `datetime.now()` (local time, no timezone indicator), but the frontend interpreted it as UTC. Now stores and compares all debug logging timestamps in UTC. - **Non-Bambu Lab Spools Can't Link/Unlink to Spoolman** ([#653](https://github.com/maziggy/bambuddy/pull/653)) — The "Link to Spoolman" button was not shown for non-Bambu Lab spools (which lack RFID tag UIDs). Now generates a fallback tag from the printer ID, AMS ID, and tray ID for spools without RFID identifiers. Also added an "Unlink from Spoolman" button for non-Bambu spools that are already linked. Contributed by @shrunbr. - **Spoolman Location Not Updated on Link/Unlink** ([#669](https://github.com/maziggy/bambuddy/pull/669)) — Linking a spool to Spoolman did not set the spool's location field. Now sets the Spoolman location to the printer name, AMS name, and slot number (e.g. "P2S-1 - AMS-A 3") when linking, and clears it when unlinking. Contributed by @shrunbr. +- **Inventory Usage Tracker Missing External Spool Mapping** ([#677](https://github.com/maziggy/bambuddy/issues/677)) — When all higher-priority slot-to-tray mapping methods failed (MQTT mapping, print command mapping, queue mapping, color matching), the internal inventory usage tracker fell back to `slot_id - 1` which can never reach external spool IDs (254/255) or AMS-HT IDs (128+). Added position-based resolution using sorted available tray IDs from the printer's AMS state, matching the fix applied to Spoolman tracking in #686. Contributed by @shrunbr. ## [0.2.2b2] - 2026-03-06 diff --git a/backend/app/services/usage_tracker.py b/backend/app/services/usage_tracker.py index abe92ba7d..93ea6bc80 100644 --- a/backend/app/services/usage_tracker.py +++ b/backend/app/services/usage_tracker.py @@ -475,7 +475,8 @@ async def _track_from_3mf( 2. MQTT mapping field from printer state (universal, all print sources) 3. Queue item ams_mapping (for queue-initiated prints) 4. tray_now from printer state (for single-filament non-queue prints) - 5. Default mapping: slot_id - 1 = global_tray_id (last resort) + 5. Position-based default using sorted available tray IDs (handles external spools) + 6. Default mapping: slot_id - 1 = global_tray_id (last resort) """ from backend.app.core.config import settings as app_settings from backend.app.models.archive import PrintArchive @@ -804,12 +805,26 @@ async def _track_from_3mf( # Single-filament non-queue print: use actual tray from printer state global_tray_id = tray_now_override else: - # Queue mapping or default: slot_id - 1, overridden by ams_mapping - global_tray_id = slot_id - 1 + # Explicit mapping (print command, MQTT, queue, color match) + global_tray_id = None if slot_to_tray and slot_id <= len(slot_to_tray): mapped = slot_to_tray[slot_id - 1] if isinstance(mapped, int) and mapped >= 0: global_tray_id = mapped + # Position-based default: sort available tray IDs so external spools (254/255) + # naturally follow standard AMS trays, matching slicer slot numbering + if global_tray_id is None: + _state = printer_manager.get_status(printer_id) + _raw = getattr(_state, "raw_data", None) if _state else None + if _raw: + from backend.app.services.spoolman_tracking import build_ams_tray_lookup + + available_trays = sorted(build_ams_tray_lookup(_raw).keys()) + if slot_id <= len(available_trays): + global_tray_id = available_trays[slot_id - 1] + # Final fallback: slot_id - 1 (legacy, works for pure AMS without external spools) + if global_tray_id is None: + global_tray_id = slot_id - 1 if global_tray_id >= 254: # External spool: ams_id=255 (sentinel), tray_id=slot index (0 or 1)