Browse Source

Fix AMS-HT mapping for left nozzle on H2D Pro (#318)

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.
maziggy 3 months ago
parent
commit
c5fff9bc15

+ 1 - 0
CHANGELOG.md

@@ -45,6 +45,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **GitHub Backup Description Misleading** — The "App Settings" backup card said "excludes sensitive data" but the complete database is pushed. Updated description to "complete database."
 - **Support Bundle Shows 0 AMS Units** — The support info always reported `ams_unit_count: 0` because it expected `raw_data["ams"]` to be a nested dict (`{"ams": [...]}`) but the MQTT handler stores it as a flat list. Now handles both formats.
 - **Firmware Badge Shown for Models Without API Data** ([#311](https://github.com/maziggy/bambuddy/issues/311)) — Printers whose model has no firmware data in Bambu Lab's API (e.g. H2C on public beta firmware) showed a misleading green "up to date" badge. The badge is now hidden when the API returns no `latest_version`, since there is nothing to compare against.
+- **AMS-HT Mapping Fails for Left Nozzle on H2D Pro** ([#318](https://github.com/maziggy/bambuddy/issues/318)) — Printing with the left nozzle on dual-nozzle printers (H2D/H2D Pro) using AMS-HT failed with "Failed to get AMS mapping table." The global tray ID for AMS-HT units (ams_id >= 128) was calculated as `ams_id * 4 + tray_id` (= 512), but AMS-HT uses the raw `ams_id` (128) since it has a single tray. The backend then misidentified 512 as an external spool. Fixed in frontend tray ID calculation, backend `ams_mapping2` builder, print scheduler, and Spoolman tracking.
 
 ### Documentation
 - **Supported Printers Updated** — Updated README, website, and wiki to list all 12 supported Bambu Lab printer models: X1, X1C, X1E, P1P, P1S, P2S, A1, A1 Mini, H2D, H2D Pro, H2C, H2S. Removed outdated "Testers Needed" messaging and Tested/Needs Testing distinctions — all models are now uniformly listed as supported. Added H2C printer image to website. Added H2D Pro, H2C columns to wiki feature comparison tables and new P2 Series section.

+ 3 - 0
backend/app/services/bambu_mqtt.py

@@ -2076,6 +2076,9 @@ class BambuMQTTClient:
                         # For ams_mapping2, slot_id is 0 (main) or 1 (deputy), not the tray_id
                         external_slot = 0 if tray_id == 254 else 1
                         ams_mapping2.append({"ams_id": 255, "slot_id": external_slot})
+                    elif tray_id >= 128:
+                        # AMS-HT: global tray ID IS the ams_id (single tray per unit)
+                        ams_mapping2.append({"ams_id": tray_id, "slot_id": 0})
                     else:
                         # Regular AMS tray: Global tray ID = (ams_id * 4) + slot_id
                         ams_id = tray_id // 4

+ 2 - 1
backend/app/services/print_scheduler.py

@@ -511,7 +511,8 @@ class PrintScheduler:
                     # Normalize color: remove alpha, add hash
                     color = self._normalize_color(tray_color)
                     # Calculate global tray ID
-                    global_tray_id = ams_id * 4 + tray_id
+                    # AMS-HT units have IDs starting at 128 with a single tray
+                    global_tray_id = ams_id if ams_id >= 128 else ams_id * 4 + tray_id
 
                     filaments.append(
                         {

+ 2 - 1
backend/app/services/spoolman_tracking.py

@@ -58,7 +58,8 @@ def build_ams_tray_lookup(raw_data: dict) -> dict[int, dict]:
         ams_id = ams_unit.get("id", 0)
         for tray in ams_unit.get("tray", []):
             tray_id = tray.get("id", 0)
-            global_tray_id = ams_id * 4 + tray_id
+            # AMS-HT units have IDs starting at 128 with a single tray
+            global_tray_id = ams_id if ams_id >= 128 else ams_id * 4 + tray_id
             lookup[global_tray_id] = {
                 "tray_uuid": tray.get("tray_uuid", ""),
                 "tag_uid": tray.get("tag_uid", ""),

+ 1 - 1
backend/tests/unit/test_scheduler_ams_mapping.py

@@ -129,7 +129,7 @@ class TestBuildLoadedFilaments:
         result = scheduler._build_loaded_filaments(MockStatus())
         assert len(result) == 1
         assert result[0]["is_ht"] is True
-        assert result[0]["global_tray_id"] == 512  # 128 * 4 + 0
+        assert result[0]["global_tray_id"] == 128  # AMS-HT uses ams_id directly
 
     def test_build_loaded_filaments_with_external(self, scheduler):
         """Should include external spool."""

+ 1 - 1
frontend/src/__tests__/hooks/useFilamentMapping.test.ts

@@ -134,7 +134,7 @@ describe('buildLoadedFilaments', () => {
     const result = buildLoadedFilaments(status);
 
     expect(result[0].isHt).toBe(true);
-    expect(result[0].globalTrayId).toBe(512);  // 128 * 4 + 0
+    expect(result[0].globalTrayId).toBe(128);  // AMS-HT uses ams_id directly
   });
 });
 

+ 4 - 2
frontend/src/utils/amsHelpers.ts

@@ -78,10 +78,10 @@ export function formatSlotLabel(
 /**
  * Calculate global tray ID for MQTT command.
  * Used in the ams_mapping array sent to the printer.
- * @param amsId - AMS unit ID
+ * @param amsId - AMS unit ID (0-3 for regular AMS, 128+ for AMS-HT)
  * @param trayId - Tray/slot ID within the AMS unit
  * @param isExternal - Whether this is the external spool holder
- * @returns Global tray ID (0-15 for AMS, 254 for external)
+ * @returns Global tray ID (0-15 for AMS, 128+ for AMS-HT, 254 for external)
  */
 export function getGlobalTrayId(
   amsId: number,
@@ -89,6 +89,8 @@ export function getGlobalTrayId(
   isExternal: boolean
 ): number {
   if (isExternal) return 254;
+  // AMS-HT units have IDs starting at 128 with a single tray — use ID directly
+  if (amsId >= 128) return amsId;
   return amsId * 4 + trayId;
 }