Browse Source

Fix Bambu Lab spool detection in Spoolman sync and AMS cards

  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)
maziggy 4 tháng trước cách đây
mục cha
commit
61c6473eca

+ 6 - 4
backend/app/api/routes/spoolman.py

@@ -213,7 +213,7 @@ async def sync_printer_ams(
             location = client.convert_ams_slot_to_location(ams_id, tray.tray_id)
 
             # Skip non-Bambu Lab spools (SpoolEase/third-party) - track as skipped
-            if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid):
+            if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid, tray.tray_info_idx):
                 skipped.append(
                     SkippedSpool(
                         location=location,
@@ -230,7 +230,8 @@ async def sync_printer_ams(
                 if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000"
                 else tray.tag_uid
             )
-            current_tray_uuids.add(spool_tag.upper())
+            if spool_tag:
+                current_tray_uuids.add(spool_tag.upper())
 
             try:
                 sync_result = await client.sync_ams_tray(tray, printer.name)
@@ -342,7 +343,7 @@ async def sync_all_printers(db: AsyncSession = Depends(get_db)):
                 location = f"{printer.name} - {client.convert_ams_slot_to_location(ams_id, tray.tray_id)}"
 
                 # Skip non-Bambu Lab spools (SpoolEase/third-party) - track as skipped
-                if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid):
+                if not client.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid, tray.tray_info_idx):
                     all_skipped.append(
                         SkippedSpool(
                             location=location,
@@ -359,7 +360,8 @@ async def sync_all_printers(db: AsyncSession = Depends(get_db)):
                     if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000"
                     else tray.tag_uid
                 )
-                printer_tray_uuids[printer.name].add(spool_tag.upper())
+                if spool_tag:
+                    printer_tray_uuids[printer.name].add(spool_tag.upper())
 
                 try:
                     sync_result = await client.sync_ams_tray(tray, printer.name)

+ 37 - 13
backend/app/services/spoolman.py

@@ -49,6 +49,7 @@ class AMSTray:
     remain: int  # Remaining percentage (0-100)
     tag_uid: str  # RFID tag UID
     tray_uuid: str  # Spool UUID
+    tray_info_idx: str  # Bambu filament preset ID like "GFA00"
     tray_weight: int  # Spool weight in grams (usually 1000)
 
 
@@ -521,6 +522,9 @@ class SpoolmanClient:
         if tray_uuid in ("", "00000000000000000000000000000000"):
             tray_uuid = ""
 
+        # Get tray_info_idx (Bambu filament preset ID like "GFA00")
+        tray_info_idx = tray_data.get("tray_info_idx", "") or ""
+
         # Get remaining percentage, ensure non-negative
         remain = max(0, int(tray_data.get("remain", 0)))
 
@@ -533,6 +537,7 @@ class SpoolmanClient:
             remain=remain,
             tag_uid=tag_uid,
             tray_uuid=tray_uuid,
+            tray_info_idx=tray_info_idx.strip(),
             tray_weight=int(tray_data.get("tray_weight", 1000)),
         )
 
@@ -552,25 +557,35 @@ class SpoolmanClient:
         ams_letter = chr(ord("A") + ams_id)
         return f"AMS {ams_letter}{tray_id + 1}"
 
-    def is_bambu_lab_spool(self, tray_uuid: str, tag_uid: str = "") -> bool:
+    def is_bambu_lab_spool(self, tray_uuid: str, tag_uid: str = "", tray_info_idx: str = "") -> bool:
         """Check if a tray has a valid Bambu Lab spool.
 
-        Bambu Lab spools have a tray_uuid (32-character hex string) and/or
-        a tag_uid (16-character hex string). The tray_uuid is preferred as
-        it's consistent across printer models, but tag_uid is accepted as
-        a fallback since some spools may have RFID read issues.
+        Bambu Lab spools can be identified by:
+        1. tray_uuid: 32-character hex string (preferred, consistent across printers)
+        2. tag_uid: 16-character hex string (RFID tag, varies between readers)
+        3. tray_info_idx: Bambu filament preset ID like "GFA00" (most reliable)
 
-        Non-Bambu Lab spools (SpoolEase, third-party) won't have valid
-        tray_uuid or tag_uid.
+        Non-Bambu Lab spools (SpoolEase, third-party) won't have these identifiers.
 
         Args:
             tray_uuid: The tray UUID to check (32 hex chars)
             tag_uid: The RFID tag UID to check as fallback (16 hex chars)
+            tray_info_idx: Bambu filament preset ID like "GFA00", "GFB00"
 
         Returns:
             True if the spool has valid Bambu Lab identifiers, False otherwise.
         """
-        # First check tray_uuid (preferred - consistent across printer models)
+        # Check tray_info_idx first - Bambu filament preset IDs like "GFA00", "GFB00", etc.
+        # This is the most reliable indicator as it's set when the spool is recognized
+        if tray_info_idx:
+            idx = tray_info_idx.strip()
+            # Bambu Lab preset IDs start with "GF" followed by letter and digits
+            # e.g., GFA00, GFB00, GFL00, GFN00, GFG00, GFS00, GFU00
+            if idx and len(idx) >= 3 and idx.startswith("GF"):
+                logger.debug(f"Identified Bambu Lab spool via tray_info_idx: {idx}")
+                return True
+
+        # Check tray_uuid (preferred - consistent across printer models)
         if tray_uuid:
             uuid = tray_uuid.strip()
             if len(uuid) == 32 and uuid != "00000000000000000000000000000000":
@@ -624,16 +639,17 @@ class SpoolmanClient:
         """
         logger.debug(
             f"Processing {printer_name} AMS {tray.ams_id} tray {tray.tray_id}: "
-            f"type={tray.tray_type}, uuid={tray.tray_uuid[:16] if tray.tray_uuid else 'none'}, "
+            f"type={tray.tray_type}, idx={tray.tray_info_idx or 'none'}, "
+            f"uuid={tray.tray_uuid[:16] if tray.tray_uuid else 'none'}, "
             f"tag={tray.tag_uid[:8] if tray.tag_uid else 'none'}..."
         )
 
-        # Only sync trays with valid Bambu Lab identifiers (tray_uuid or tag_uid)
-        if not self.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid):
-            if tray.tray_uuid or tray.tag_uid:
+        # Only sync trays with valid Bambu Lab identifiers
+        if not self.is_bambu_lab_spool(tray.tray_uuid, tray.tag_uid, tray.tray_info_idx):
+            if tray.tray_uuid or tray.tag_uid or tray.tray_info_idx:
                 logger.info(
                     f"Skipping non-Bambu Lab spool: {printer_name} AMS {tray.ams_id} tray {tray.tray_id} "
-                    f"(tray_uuid={tray.tray_uuid}, tag_uid={tray.tag_uid})"
+                    f"(tray_info_idx={tray.tray_info_idx}, tray_uuid={tray.tray_uuid}, tag_uid={tray.tag_uid})"
                 )
             else:
                 logger.debug(f"Skipping tray without RFID tag: AMS {tray.ams_id} tray {tray.tray_id}")
@@ -644,6 +660,14 @@ class SpoolmanClient:
             tray.tray_uuid if tray.tray_uuid and tray.tray_uuid != "00000000000000000000000000000000" else tray.tag_uid
         )
 
+        # If no unique identifier available, we can't sync even if it's a Bambu Lab spool
+        if not spool_tag:
+            logger.warning(
+                f"Bambu Lab spool detected but no unique identifier for Spoolman: "
+                f"{printer_name} AMS {tray.ams_id} tray {tray.tray_id} (tray_info_idx={tray.tray_info_idx})"
+            )
+            return None
+
         # Calculate remaining weight
         remaining = self.calculate_remaining_weight(tray.remain, tray.tray_weight)
         location = f"{printer_name} - {self.convert_ams_slot_to_location(tray.ams_id, tray.tray_id)}"

+ 32 - 3
frontend/src/pages/PrintersPage.tsx

@@ -703,6 +703,35 @@ function getWifiStrength(rssi: number | null | undefined): { label: string; colo
   return { label: 'Very weak', color: 'text-red-400', bars: 1 };
 }
 
+/**
+ * Check if a tray contains a Bambu Lab spool.
+ * Uses same logic as backend: tray_info_idx (GF*), tray_uuid, or tag_uid.
+ */
+function isBambuLabSpool(tray: {
+  tray_uuid?: string | null;
+  tag_uid?: string | null;
+  tray_info_idx?: string | null;
+} | null | undefined): boolean {
+  if (!tray) return false;
+
+  // Check tray_info_idx first (most reliable - Bambu preset IDs start with "GF")
+  if (tray.tray_info_idx && tray.tray_info_idx.startsWith('GF')) {
+    return true;
+  }
+
+  // Check tray_uuid (32 hex chars, non-zero)
+  if (tray.tray_uuid && tray.tray_uuid !== '00000000000000000000000000000000') {
+    return true;
+  }
+
+  // Check tag_uid (16 hex chars, non-zero)
+  if (tray.tag_uid && tray.tag_uid !== '0000000000000000') {
+    return true;
+  }
+
+  return false;
+}
+
 function CoverImage({ url, printName }: { url: string | null; printName?: string }) {
   const [loaded, setLoaded] = useState(false);
   const [error, setError] = useState(false);
@@ -1921,7 +1950,7 @@ function PrinterCard({
 
                                 // Build filament data for hover card
                                 const filamentData = tray?.tray_type ? {
-                                  vendor: (tray.tray_uuid ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
+                                  vendor: (isBambuLabSpool(tray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
                                   profile: cloudInfo?.name || tray.tray_sub_brands || tray.tray_type,
                                   colorName: getBambuColorName(tray.tray_id_name) || hexToBasicColorName(tray.tray_color),
                                   colorHex: tray.tray_color || null,
@@ -2068,7 +2097,7 @@ function PrinterCard({
 
                         // Build filament data for hover card
                         const filamentData = tray?.tray_type ? {
-                          vendor: (tray.tray_uuid ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
+                          vendor: (isBambuLabSpool(tray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
                           profile: cloudInfo?.name || tray.tray_sub_brands || tray.tray_type,
                           colorName: getBambuColorName(tray.tray_id_name) || hexToBasicColorName(tray.tray_color),
                           colorHex: tray.tray_color || null,
@@ -2242,7 +2271,7 @@ function PrinterCard({
 
                         // Build filament data for hover card
                         const extFilamentData = {
-                          vendor: (extTray.tray_uuid ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
+                          vendor: (isBambuLabSpool(extTray) ? 'Bambu Lab' : 'Generic') as 'Bambu Lab' | 'Generic',
                           profile: extCloudInfo?.name || extTray.tray_sub_brands || extTray.tray_type || 'Unknown',
                           colorName: getBambuColorName(extTray.tray_id_name) || hexToBasicColorName(extTray.tray_color),
                           colorHex: extTray.tray_color || null,

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
static/assets/index-COShNpeg.js


+ 1 - 1
static/index.html

@@ -23,7 +23,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-D7518JU5.js"></script>
+    <script type="module" crossorigin src="/assets/index-COShNpeg.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-DAZKHkJ3.css">
   </head>
   <body>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác