mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 11:12:35 +02:00
fix(ams): HT slot shows "Generic" after configuring custom preset (#1053)
After configuring an AMS-HT slot with a custom cloud preset, the slot card and Configure modal kept showing "Generic PLA" even though the printer and slicer had the correct preset. The `/slot-presets` response keyed HT entries at `ams_id * 4 + tray_id = 512`, but frontend lookups used `ams_id` directly (128 on PrintersPage via getGlobalTrayId, 64 on SpoolBuddy via a one-off formula). All three agreed for regular AMS, so the mismatch only surfaced on HT — the saved preset never reached the UI and the render fell through to `tray.tray_type`. Backend now keys via a helper that mirrors frontend `getGlobalTrayId`. SpoolBuddy's AMS page switches to the shared helper. Regression test covers regular, HT, and external slot keys.
This commit is contained in:
@@ -5,6 +5,7 @@ All notable changes to Bambuddy will be documented in this file.
|
||||
## [0.2.4b1] - Unreleased
|
||||
|
||||
### Fixed
|
||||
- **AMS-HT: Custom Filament Preset Reverts to "Generic" in UI After Configure** ([#1053](https://github.com/maziggy/bambuddy/issues/1053)) — After configuring an AMS-HT slot (HT-A/HT-B) with a custom Bambu Cloud preset (e.g. "Devil Design PLA Basic"), the slot card and Configure modal kept showing "Generic PLA" even though the `ams_filament_setting` command succeeded and BambuStudio / the printer's LCD both rendered the correct custom preset. Root cause: the `GET /api/v1/printers/{id}/slot-presets` endpoint keyed its response dict by `ams_id * 4 + tray_id`, which collapses cleanly to the same integer the frontend uses for regular AMS slots (0 through 15) but produces `128 * 4 + 0 = 512` for HT-A — a key nothing looks up. The frontend's PrintersPage HT render path calls `getGlobalTrayId(ams.id, …, false)` which returns the ams_id itself (`128` for HT-A), and SpoolBuddy's AMS page used a third, unrelated formula (`(amsId - 128) * 4 + trayId + 64 = 64`). All three agreed for regular AMS so the mismatch only surfaced on HT, where the saved preset name never reached the UI and the render fell through to `tray.tray_type` → rendered as "Generic PLA". Backend now keys the response via a `_slot_preset_key` helper that mirrors frontend `getGlobalTrayId` (HT → `ams_id`, regular/external → `ams_id * 4 + tray_id`), and SpoolBuddyAmsPage uses the shared `getGlobalTrayId` helper instead of its home-grown formula. Regression test covers the key scheme for regular, HT, and external slots. Thanks to @mrnoisytiger for the detailed reproduction.
|
||||
- **⚠️ Bed-Jog "Home Z" Could Crash the Bed Into the Toolhead** ([#1052](https://github.com/maziggy/bambuddy/issues/1052)) — **Critical safety fix.** On H2C (and by extension any Bambu printer where Z-home moves the bed UP toward an endstop — H2D, H2S, and X1 family all share this kinematics) the bed-jog modal's "Home Z" button sent a raw `G28 Z` over the `gcode_line` MQTT command. Bare `G28 Z` skips the toolhead-park step that a full `G28` runs first, so the bed raised without stopping at a safe height — in the reporter's case the toolhead happened to be parked on the purge chute and no damage was caused, but hitting the button with a toolhead anywhere else would have driven the bed into it at full Z speed. Root cause was the `/api/v1/printers/{id}/home-axes` endpoint's per-axis gcode mapping (`"z" → "G28 Z"`, `"xy" → "G28 X Y"`, `"all" → "G28"`). The endpoint now ignores the `axes` argument entirely and always sends a bare `G28`, which Bambu firmware expands into the safe multi-step sequence (park toolhead → home XY → home Z). The MQTT client helper `BambuClient.home_axes()` has the same change. The bed-jog modal is retitled "Auto Home" and its copy now says "parks the toolhead, then homes X, Y, and Z" so users aren't surprised when X/Y motion happens first. After a successful Auto Home click, the modal no longer re-prompts on the next jog in the same session — the "not homed" warning is gated on a session-scoped acknowledgement flag that was only being set by "Move anyway" and now also fires on successful Auto Home. Regression test covers all three axes arguments producing the same bare `G28`. Thanks to @mikefromdot for catching this with an undamaged retest.
|
||||
- **AMS: Configure / Assign Spool Hidden on Reset Slots, and Assign Spool Missing Matching-Material Inventory** ([#1047](https://github.com/maziggy/bambuddy/issues/1047)) — Two separate symptoms from the same report. (1) After resetting an AMS slot from the printer UI, the Bambuddy printer card showed "Empty Slot" with no Configure or Assign Spool actions on hover, while the same slot in SpoolBuddy's AMS page still let the user re-configure it. Root cause: commit `c9efa4b8` (#784) added a `tray?.state === 10` gate to the `EmptySlotHoverCard` actions, intended to show the buttons only when a spool was physically present but not loaded (state=10) and hide them on truly empty slots (state=9). In practice, firmware often reports `state=9` (or no `state` field at all) after a user-initiated reset — even when a spool is still physically in the slot — so the actions disappeared exactly when the user needed them. The gate is redundant anyway (`EmptySlotHoverCard` is only rendered when the slot has no `tray_type`, so it's definitionally empty from Bambuddy's perspective), and configuring an empty slot is a valid "tell the printer what will be loaded here" operation. The gate is now removed at both the standard-AMS and AMS-HT render paths. (2) After configuring a slot with a Generic profile (e.g. "Devil Design PLA Basic Red"), the Assign Spool modal didn't list the matching inventory spool unless the user enabled the "Show all spools" toggle. Root cause: the filter at `AssignSpoolModal.tsx:144` required `normalizeValue(spool.slicer_filament_name) === normalizeValue(trayInfo.profile)` — manually-added inventory spools typically don't have `slicer_filament_name` populated, so they failed the exact-profile check even when the material matched. The filter now prefers an exact slicer-profile match when both sides advertise one, and falls back to partial material match in either direction (so e.g. a spool with `material="PLA"` is selectable for a slot reporting `"PLA Basic"`) when profile info is missing. (3) Once the matching spool was assignable, a "profile mismatch" confirmation dialog still warned on every assignment because Bambu Studio / OrcaSlicer slicer-profile names carry a printer/nozzle/variant qualifier after `@` (e.g. `"Devil Design PLA Basic @Bambu Lab H2D 0.4 nozzle (Custom)"`) while the tray stores only the bare base name (`"Devil Design PLA Basic"`), and `checkProfileMatch` compared the full strings. Both the filter and the mismatch check now strip the `@…` qualifier before comparing, so identical base profiles are treated as a match. Regression test covers a spool with no slicer profile being surfaced for a slot whose profile + material are both set. Thanks to @TravisWilder for the report.
|
||||
- **Skip Objects: Enlarged Preview Image Fails to Load on Auth-Enabled Instances** ([#1046](https://github.com/maziggy/bambuddy/issues/1046)) — Clicking the mini print-pr
|
||||
|
||||
@@ -1725,6 +1725,15 @@ async def start_calibration(
|
||||
# ============================================================================
|
||||
|
||||
|
||||
def _slot_preset_key(ams_id: int, tray_id: int) -> int:
|
||||
# Mirrors frontend getGlobalTrayId (amsHelpers.ts): AMS-HT (128-135) is keyed
|
||||
# by ams_id since each unit has a single slot and shares its global ID with
|
||||
# the unit itself. Regular AMS and external (255) use ams_id*4+tray_id.
|
||||
if 128 <= ams_id <= 135:
|
||||
return ams_id
|
||||
return ams_id * 4 + tray_id
|
||||
|
||||
|
||||
@router.get("/{printer_id}/slot-presets")
|
||||
async def get_slot_presets(
|
||||
printer_id: int,
|
||||
@@ -1736,7 +1745,7 @@ async def get_slot_presets(
|
||||
mappings = result.scalars().all()
|
||||
|
||||
return {
|
||||
mapping.ams_id * 4 + mapping.tray_id: {
|
||||
_slot_preset_key(mapping.ams_id, mapping.tray_id): {
|
||||
"ams_id": mapping.ams_id,
|
||||
"tray_id": mapping.tray_id,
|
||||
"preset_id": mapping.preset_id,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Unit tests for slot-preset key derivation.
|
||||
|
||||
Regression coverage for #1053: the backend's get_slot_presets response
|
||||
must use the same keying scheme as the frontend's getGlobalTrayId
|
||||
(amsHelpers.ts) so that AMS-HT mappings round-trip correctly.
|
||||
"""
|
||||
|
||||
from backend.app.api.routes.printers import _slot_preset_key
|
||||
|
||||
|
||||
def test_regular_ams_uses_global_tray_id():
|
||||
assert _slot_preset_key(0, 0) == 0
|
||||
assert _slot_preset_key(0, 3) == 3
|
||||
assert _slot_preset_key(1, 1) == 5
|
||||
assert _slot_preset_key(2, 2) == 10
|
||||
assert _slot_preset_key(3, 3) == 15
|
||||
|
||||
|
||||
def test_ams_ht_keyed_by_ams_id():
|
||||
# AMS-HT is single-slot and shares its global tray id with the unit id;
|
||||
# frontend getGlobalTrayId(amsId, 0, false) returns amsId for 128-135.
|
||||
assert _slot_preset_key(128, 0) == 128
|
||||
assert _slot_preset_key(129, 0) == 129
|
||||
assert _slot_preset_key(135, 0) == 135
|
||||
|
||||
|
||||
def test_external_spool_uses_multiplied_id():
|
||||
# External (ams_id=255) matches PrintersPage lookup: 255 * 4 + tray_id.
|
||||
assert _slot_preset_key(255, 0) == 1020
|
||||
assert _slot_preset_key(255, 1) == 1021
|
||||
@@ -291,7 +291,7 @@ export function SpoolBuddyAmsPage() {
|
||||
}, [effectiveTrayNow]);
|
||||
|
||||
const handleAmsSlotClick = useCallback((amsId: number, trayId: number, tray: AMSTray | null) => {
|
||||
const globalTrayId = amsId >= 128 ? (amsId - 128) * 4 + trayId + 64 : amsId * 4 + trayId;
|
||||
const globalTrayId = getGlobalTrayId(amsId, trayId, false);
|
||||
const slotPreset = slotPresets?.[globalTrayId];
|
||||
const mappedExtruderId = amsExtruderMap[String(amsId)];
|
||||
const normalizedId = amsId >= 128 ? amsId - 128 : amsId;
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -26,7 +26,7 @@
|
||||
|
||||
<!-- Splash screens for iOS -->
|
||||
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
|
||||
<script type="module" crossorigin src="/assets/index-DFkuTMRb.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DZZn3lqr.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CkAOuJaW.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user