mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-18 11:56:01 +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:
@@ -874,17 +874,17 @@ const ERROR_DESCRIPTIONS: Record<string, string> = {
|
||||
'18FF_C00A': 'Please observe the nozzle of the right extruder. If the filament has been extruded, select \'Continue\'; if not, please push the filament forward slightly and then select \'Retry\'.',
|
||||
};
|
||||
|
||||
function getSeverityInfo(severity: number): { label: string; color: string; bgColor: string; buttonHoverColor: string; Icon: typeof AlertTriangle } {
|
||||
function getSeverityInfo(severity: number): { label: string; color: string; bgColor: string; Icon: typeof AlertTriangle } {
|
||||
switch (severity) {
|
||||
case 1:
|
||||
return { label: 'Fatal', color: 'text-red-500', bgColor: 'bg-red-500/20', buttonHoverColor: 'bg-red-500/10', Icon: AlertTriangle };
|
||||
return { label: 'Fatal', color: 'text-red-500', bgColor: 'bg-red-500/20', Icon: AlertTriangle };
|
||||
case 2:
|
||||
return { label: 'Serious', color: 'text-red-400', bgColor: 'bg-red-500/15', buttonHoverColor: 'bg-red-500/10', Icon: AlertTriangle };
|
||||
return { label: 'Serious', color: 'text-red-400', bgColor: 'bg-red-500/15', Icon: AlertTriangle };
|
||||
case 3:
|
||||
return { label: 'Warning', color: 'text-orange-400', bgColor: 'bg-orange-500/20', buttonHoverColor: 'bg-orange-500/10', Icon: AlertCircle };
|
||||
return { label: 'Warning', color: 'text-orange-400', bgColor: 'bg-orange-500/20', Icon: AlertCircle };
|
||||
case 4:
|
||||
default:
|
||||
return { label: 'Info', color: 'text-blue-400', bgColor: 'bg-blue-500/20', buttonHoverColor: 'bg-blue-500/10', Icon: Info };
|
||||
return { label: 'Info', color: 'text-blue-400', bgColor: 'bg-blue-500/20', Icon: Info };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1000,7 +1000,7 @@ export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPerm
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{knownErrors.map((error, index) => {
|
||||
const { label, color, bgColor, buttonHoverColor, Icon } = getSeverityInfo(error.severity);
|
||||
const { label, color, bgColor, Icon } = getSeverityInfo(error.severity);
|
||||
const codeNum = parseInt(error.code.replace('0x', ''), 16) || 0;
|
||||
const shortCode = getShortCode(error.attr, codeNum);
|
||||
const description = ERROR_DESCRIPTIONS[shortCode] ?? t('hmsErrors.unknownCode');
|
||||
@@ -1024,26 +1024,42 @@ export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPerm
|
||||
<p className="text-sm text-bambu-gray mb-2">{description}</p>
|
||||
{error.actions && error.actions.length > 0 && (
|
||||
<div className="flex flex-wrap gap-2 my-2">
|
||||
{error.actions.map((action) => (
|
||||
<button
|
||||
key={action}
|
||||
onClick={() => {
|
||||
// full_code is the firmware-matching key (16
|
||||
// chars for hms[]-array faults, 8 chars for
|
||||
// print_error). Fall back to the 8-char
|
||||
// shortCode for older backends that haven't
|
||||
// populated it. See #1830.
|
||||
activateActionMutation.mutate({
|
||||
action,
|
||||
print_error: error.full_code || shortCode.replace("_", ""),
|
||||
job_id: error.job_id ?? null,
|
||||
});
|
||||
}}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-lg ${bgColor} ${color} hover:${buttonHoverColor} transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0`}
|
||||
>
|
||||
{t(`hmsErrors.actions.${action}`, action)}
|
||||
</button>
|
||||
))}
|
||||
{error.actions.map((action) => {
|
||||
const pendingVars = activateActionMutation.variables;
|
||||
const isThisPending =
|
||||
activateActionMutation.isPending
|
||||
&& pendingVars?.action === action
|
||||
&& pendingVars?.print_error === (error.full_code || shortCode.replace('_', ''));
|
||||
return (
|
||||
<button
|
||||
key={action}
|
||||
onClick={() => {
|
||||
// full_code is the firmware-matching key (16
|
||||
// chars for hms[]-array faults, 8 chars for
|
||||
// print_error). Fall back to the 8-char
|
||||
// shortCode for older backends that haven't
|
||||
// populated it. See #1830.
|
||||
activateActionMutation.mutate({
|
||||
action,
|
||||
print_error: error.full_code || shortCode.replace('_', ''),
|
||||
job_id: error.job_id ?? null,
|
||||
});
|
||||
}}
|
||||
// Static hover/active classes — Tailwind's JIT
|
||||
// can't resolve `hover:${var}` template
|
||||
// literals, so the previous severity-tinted
|
||||
// hover never reached the compiled CSS and
|
||||
// the action buttons read as inert badges.
|
||||
// White-on-tint reads as a clear affordance
|
||||
// against any severity-coloured container.
|
||||
disabled={!hasPermission('printers:control') || activateActionMutation.isPending}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-lg bg-white/10 hover:bg-white/20 active:bg-white/30 text-white border border-white/20 hover:border-white/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0"
|
||||
>
|
||||
{isThisPending && <Loader2 className="w-4 h-4 animate-spin" />}
|
||||
{t(`hmsErrors.actions.${action}`, action)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<a
|
||||
|
||||
Reference in New Issue
Block a user