mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
fix(hms): wrong-plate Ignore actually ignores + buttons read as buttons + ack-detection survives transient re-pause (#1869)
The HMS error modal had three compounding bugs that surfaced when a user forced a wrong-plate HMS (0500_8051) and tried to dispatch the per-fault actions. (1) IGNORE_RESUME did not ignore. Bambuddy redirected the action on state=PAUSE to a plain `resume` command, citing a #1830 verdict that BambuStudio's "err-bearing shape" was firmware-silently-rejected. BambuStudio source disagrees: DeviceErrorDialog.cpp:600 dispatches IGNORE_RESUME via command_hms_ignore, whose wire shape is {command:"ignore", err:"<decimal>", param:"reserve", job_id:...}. That's a distinct command from `resume` — the firmware suppresses the next re-check AND auto-resumes in one operation. Plain resume means "re-check normally", which is exactly why the wrong-plate detection re-fired 1-2 s after the user clicked Ignore. The #1830 "err-bearing shape rejected" test almost certainly sent the err as a hex shortcode; BambuStudio passes std::to_string(int m_error_code) i.e. the DECIMAL form, which is what the firmware matches against. (2) Action buttons read as inert badges. The button className used `hover:${buttonHoverColor}` — a template-literal interpolation Tailwind's JIT scanner can't see as a literal string, so the per-severity hover utility never reached the compiled CSS. Same bg/text color as the severity badge above and no border made it read as another label. No disabled state and no spinner during the 2.5 s ack wait left clicks sitting silently inert. (3) Ack-detection 502'd on legitimate ack. The route compared (gcode_state, hms_errors-len) before vs after publish; wrong-plate re-pause round-tripped both fields to their pre-publish values inside the 2.5 s window → false 502 even though the firmware fully ack'd. PROBLEM_SOLVED_RESUME working but IGNORE_RESUME 502'ing on the same fault was the same race resolving differently. Fixes: bambu_mqtt.py — new hms_ignore_command() publishes the BambuStudio shape; existing hms_ignore(persistent) renamed to hms_idle_ignore (unchanged shape, used by NO_REMINDER_NEXT_TIME per DeviceErrorDialog.cpp:588). Dispatch routes IGNORE_RESUME, IGNORE_NO_REMINDER_NEXT_TIME, and DONT_REMIND_NEXT_TIME to hms_ignore_command (BambuStudio routes all three to the same command_hms_ignore — the "don't remind" half is the firmware's job). NO_REMINDER_NEXT_TIME stays on hms_idle_ignore type=0. Hex → decimal err conversion at the helper layer with a defensive fallback. job_id=None → empty string (matches BambuStudio's std::string default). HMSErrorModal.tsx — getSeverityInfo loses the dead buttonHoverColor field. Action button uses static `bg-white/10 hover:bg-white/20 active:bg-white/30 text-white border border-white/20`, wires `disabled={!hasPermission||mutation.isPending}`, and renders `<Loader2/>` only on the button whose (action,print_error) matches mutation.variables. printers.py — ack-detection probes `client._last_message_time` (bumped on every MQTT push regardless of payload) rather than diffing state fields. The pushall that follows every command guarantees a fresh push lands inside the 2.5 s window on any healthy printer; only firmware-silent-drop leaves the timestamp untouched, which is the 502 path #1830 wanted.
This commit is contained in:
@@ -3824,14 +3824,17 @@ async def execute_hms_action(
|
||||
# command. publish() success is NOT the same as printer-ack: Bambu's
|
||||
# firmware silently rejects malformed HMS commands at QoS 1 (the broker
|
||||
# ACKs the publish, but the printer drops it). Verified end-to-end against
|
||||
# a live H2D — see #1830 §(3). We sample (gcode_state, hms_errors length)
|
||||
# because every accepted HMS action mutates at least one of them.
|
||||
# a live H2D — see #1830 §(3).
|
||||
#
|
||||
# PrinterState.state carries the MQTT `gcode_state` value verbatim (see
|
||||
# bambu_mqtt.py line 2144); the raw `print_error` int isn't preserved on
|
||||
# state, only the derived HMSError entries are.
|
||||
pre_gcode = client.state.state
|
||||
pre_hms_count = len(client.state.hms_errors)
|
||||
# We probe `_last_message_time` (bumped on every MQTT push) rather than a
|
||||
# (gcode_state, hms_errors-length) diff. The old diff missed the
|
||||
# wrong-plate IGNORE_RESUME case where the printer briefly resumes and
|
||||
# re-pauses with the same fault inside the 2.5s window: both fields
|
||||
# round-trip to their pre-publish values → false 502 even though the
|
||||
# firmware fully ack'd the resume. Every accepted command triggers a
|
||||
# pushall response within ~100-500ms, so a fresh inbound message after
|
||||
# the publish is the robust ack signal.
|
||||
pre_last_message = client._last_message_time
|
||||
|
||||
success = client.execute_hms_action(body.print_error, body.action, body.job_id)
|
||||
if not success:
|
||||
@@ -3845,12 +3848,12 @@ async def execute_hms_action(
|
||||
# coroutine is awaiting.
|
||||
await asyncio.sleep(HMS_ACTION_ACK_WAIT_SECONDS)
|
||||
|
||||
acked = client.state.state != pre_gcode or len(client.state.hms_errors) != pre_hms_count
|
||||
acked = client._last_message_time > pre_last_message
|
||||
if not acked:
|
||||
# Publish succeeded but the printer's state didn't move. Almost always
|
||||
# firmware-side silent rejection (err mismatch, command/state mismatch).
|
||||
# 502 makes it visible at the UI instead of the 200-but-broken loop
|
||||
# #1830 reported.
|
||||
# Publish succeeded but the printer sent nothing back. Almost always
|
||||
# firmware-side silent rejection (err mismatch, command/state mismatch)
|
||||
# or a dropped MQTT route. 502 makes it visible at the UI instead of
|
||||
# the 200-but-broken loop #1830 reported.
|
||||
raise HTTPException(502, "Printer did not acknowledge HMS action within 2.5s")
|
||||
|
||||
return {"success": True, "message": "HMS action executed"}
|
||||
|
||||
Reference in New Issue
Block a user