From c5fff9bc1545072eb1847fbd5e2bfb446adbf713 Mon Sep 17 00:00:00 2001 From: maziggy Date: Tue, 10 Feb 2026 13:54:21 +0100 Subject: [PATCH] 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. --- CHANGELOG.md | 1 + backend/app/services/bambu_mqtt.py | 3 +++ backend/app/services/print_scheduler.py | 3 ++- backend/app/services/spoolman_tracking.py | 3 ++- backend/tests/unit/test_scheduler_ams_mapping.py | 2 +- frontend/src/__tests__/hooks/useFilamentMapping.test.ts | 2 +- frontend/src/utils/amsHelpers.ts | 6 ++++-- 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1b8b75b3..2a46006ef 100644 --- a/CHANGELOG.md +++ b/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. diff --git a/backend/app/services/bambu_mqtt.py b/backend/app/services/bambu_mqtt.py index 8a9eedfa9..0ad16d1d5 100644 --- a/backend/app/services/bambu_mqtt.py +++ b/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 diff --git a/backend/app/services/print_scheduler.py b/backend/app/services/print_scheduler.py index 4156251e2..cc838a5f4 100644 --- a/backend/app/services/print_scheduler.py +++ b/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( { diff --git a/backend/app/services/spoolman_tracking.py b/backend/app/services/spoolman_tracking.py index 4eb027df5..b02d24a1a 100644 --- a/backend/app/services/spoolman_tracking.py +++ b/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", ""), diff --git a/backend/tests/unit/test_scheduler_ams_mapping.py b/backend/tests/unit/test_scheduler_ams_mapping.py index 919c658f9..04b7b3d7a 100644 --- a/backend/tests/unit/test_scheduler_ams_mapping.py +++ b/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.""" diff --git a/frontend/src/__tests__/hooks/useFilamentMapping.test.ts b/frontend/src/__tests__/hooks/useFilamentMapping.test.ts index 39a5f7e3b..06c2a847a 100644 --- a/frontend/src/__tests__/hooks/useFilamentMapping.test.ts +++ b/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 }); }); diff --git a/frontend/src/utils/amsHelpers.ts b/frontend/src/utils/amsHelpers.ts index c65a1dbf1..5e4dbe946 100644 --- a/frontend/src/utils/amsHelpers.ts +++ b/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; }