fix(printer): bed-jog "Home Z" could crash bed into toolhead on H2C/H2D/H2S/X1 (#1052)

Critical safety fix. The bed-jog dialog's "Home Z" button sent a bare
  `G28 Z` over gcode_line. On Bambu printers where the Z endstop is at
  the top (bed moves UP into it — H2C, H2D, H2S, X1 family), `G28 Z`
  skips the toolhead-park step that a full `G28` runs first, so the bed
  rises at full speed with nothing getting out of the way. The reporter
  only escaped damage because the toolhead happened to be parked on the
  purge chute.

  The /printers/{id}/home-axes endpoint and BambuClient.home_axes() now
  always send bare `G28` regardless of the axes argument, triggering the
  firmware's safe multi-step routine (park toolhead → home XY → home Z).
  The axes argument is kept for API compat but ignored; invalid values
  still return 400.

  Frontend retitles the button "Auto Home" and updates the dialog copy
  in all 7 locales so users aren't surprised when X/Y motion happens
  before Z. Parameterized regression test asserts z/xy/all all produce
  bare G28.

(cherry picked from commit 7026a6de77)
This commit is contained in:
maziggy
2026-04-20 13:14:15 +02:00
parent 7d77440770
commit b9aa1ff5f4
13 changed files with 61 additions and 51 deletions
+22 -11
View File
@@ -2497,19 +2497,30 @@ async def bed_jog(
@router.post("/{printer_id}/home-axes")
async def home_axes(
printer_id: int,
axes: str = Query("z", description="Axes to home: 'z', 'xy', or 'all'"),
axes: str = Query(
"all",
description="Legacy; accepted values are 'z' | 'xy' | 'all'. Always runs the printer's full auto-home sequence — see below.",
),
_=RequirePermissionIfAuthEnabled(Permission.PRINTERS_CONTROL),
db: AsyncSession = Depends(get_db),
):
"""Home one or more axes via G28."""
"""Run the printer's full auto-home sequence via bare `G28`.
Bambu printers (H2C / H2D / H2S / X1 family) home the Z axis by moving
the BED UP toward an endstop at the top of travel. If the toolhead is
not already parked out of the way, a bare `G28 Z` will crash the bed
into the toolhead — #1052 reported exactly that on H2C: the bed rose
without stopping at a safe height because `G28 Z` skipped the
toolhead-park step that a full `G28` runs first.
The endpoint therefore ignores the `axes` argument and always sends a
bare `G28`, which the firmware expands into a safe multi-step sequence
(park toolhead → home XY → home Z). The argument is kept only for
backward-compat with existing clients; sending an invalid value still
returns 400 so typos surface instead of silently proceeding.
"""
axes = axes.lower()
if axes == "z":
gcode = "G28 Z"
elif axes == "xy":
gcode = "G28 X Y"
elif axes == "all":
gcode = "G28"
else:
if axes not in ("z", "xy", "all"):
raise HTTPException(400, "axes must be 'z', 'xy', or 'all'")
result = await db.execute(select(Printer).where(Printer.id == printer_id))
@@ -2521,10 +2532,10 @@ async def home_axes(
if not client:
raise HTTPException(400, "Printer not connected")
if not client.send_gcode(gcode):
if not client.send_gcode("G28"):
raise HTTPException(500, "Failed to send home command")
return {"success": True, "message": f"Home {axes} command sent"}
return {"success": True, "message": "Full auto-home sequence sent"}
@router.post("/{printer_id}/hms/clear")