From 813ce19219f0c0ea05c247ca3c049816a849b4b6 Mon Sep 17 00:00:00 2001 From: Martin Ziegler Date: Sat, 29 Nov 2025 11:44:32 +0100 Subject: [PATCH] Added HMS error handling to printer card --- backend/app/services/bambu_mqtt.py | 4 + frontend/src/components/HMSErrorModal.tsx | 125 +++ frontend/src/pages/PrintersPage.tsx | 24 +- .../{index-B0NJFRfx.js => index-8ml33qQA.js} | 770 +++++++++--------- static/assets/index-BKGWUT04.css | 1 + static/assets/index-BKPvAiVm.css | 1 - static/index.html | 4 +- 7 files changed, 533 insertions(+), 396 deletions(-) create mode 100644 frontend/src/components/HMSErrorModal.tsx rename static/assets/{index-B0NJFRfx.js => index-8ml33qQA.js} (68%) create mode 100644 static/assets/index-BKGWUT04.css delete mode 100644 static/assets/index-BKPvAiVm.css diff --git a/backend/app/services/bambu_mqtt.py b/backend/app/services/bambu_mqtt.py index 7980b8854..ce21ebf07 100644 --- a/backend/app/services/bambu_mqtt.py +++ b/backend/app/services/bambu_mqtt.py @@ -236,6 +236,10 @@ class BambuMQTTClient: and self._previous_gcode_file is not None ) + if is_new_print or is_file_change: + # Clear any old HMS errors when a new print starts + self.state.hms_errors = [] + if (is_new_print or is_file_change) and self.on_print_start: logger.info( f"[{self.serial_number}] PRINT START detected - file: {current_file}, " diff --git a/frontend/src/components/HMSErrorModal.tsx b/frontend/src/components/HMSErrorModal.tsx new file mode 100644 index 000000000..35d315159 --- /dev/null +++ b/frontend/src/components/HMSErrorModal.tsx @@ -0,0 +1,125 @@ +import { X, AlertTriangle, AlertCircle, Info, ExternalLink } from 'lucide-react'; +import type { HMSError } from '../api/client'; + +interface HMSErrorModalProps { + printerName: string; + errors: HMSError[]; + onClose: () => void; +} + +// HMS error code descriptions (common ones) +const HMS_DESCRIPTIONS: Record = { + '0x20054': 'The heatbed temperature is abnormal. The sensor may be disconnected or damaged.', + '0x50005': 'Motor driver overheated. Let the printer cool down.', + '0x50006': 'Motor driver communication error.', + '0x70001': 'AMS communication error.', + '0x70002': 'AMS filament runout.', + '0x70003': 'AMS filament not detected.', + '0xC0003': 'First layer inspection failed.', + '0xC0004': 'Nozzle clog detected.', + '0xC8000': 'Foreign object detected on print bed.', + '0x50000': 'Motor X axis lost steps.', + '0x50001': 'Motor Y axis lost steps.', + '0x50002': 'Motor Z axis lost steps.', +}; + +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', Icon: AlertTriangle }; + case 2: + 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', Icon: AlertCircle }; + case 4: + default: + return { label: 'Info', color: 'text-blue-400', bgColor: 'bg-blue-500/20', Icon: Info }; + } +} + +function getHMSWikiUrl(code: string): string { + // Convert hex code to format used by Bambu Lab wiki + // Example: 0x20054 -> HMS_0200_0005_0004 + const codeNum = parseInt(code.replace('0x', ''), 16); + const part1 = ((codeNum >> 24) & 0xFF).toString(16).padStart(2, '0').toUpperCase(); + const part2 = ((codeNum >> 16) & 0xFF).toString(16).padStart(2, '0').toUpperCase(); + const part3 = ((codeNum >> 8) & 0xFF).toString(16).padStart(2, '0').toUpperCase(); + const part4 = (codeNum & 0xFF).toString(16).padStart(2, '0').toUpperCase(); + return `https://wiki.bambulab.com/en/x1/troubleshooting/hmscode/HMS_${part1}${part2}_${part3}${part4}`; +} + +export function HMSErrorModal({ printerName, errors, onClose }: HMSErrorModalProps) { + return ( +
+
+ {/* Header */} +
+
+ +

HMS Errors - {printerName}

+
+ +
+ + {/* Content */} +
+ {errors.length === 0 ? ( +
+ +

No HMS errors

+
+ ) : ( +
+ {errors.map((error, index) => { + const { label, color, bgColor, Icon } = getSeverityInfo(error.severity); + const description = HMS_DESCRIPTIONS[error.code] || 'Unknown error. Click the link below for details.'; + const wikiUrl = getHMSWikiUrl(error.code); + + return ( +
+
+ +
+
+ {error.code} + + {label} + +
+

{description}

+ + + View on Bambu Lab Wiki + +
+
+
+ ); + })} +
+ )} +
+ + {/* Footer */} +
+

+ HMS (Health Management System) monitors printer health. Clear errors on the printer to dismiss them here. +

+
+
+
+ ); +} diff --git a/frontend/src/pages/PrintersPage.tsx b/frontend/src/pages/PrintersPage.tsx index 218db81ca..a0f642132 100644 --- a/frontend/src/pages/PrintersPage.tsx +++ b/frontend/src/pages/PrintersPage.tsx @@ -24,6 +24,7 @@ import { Button } from '../components/Button'; import { ConfirmModal } from '../components/ConfirmModal'; import { FileManagerModal } from '../components/FileManagerModal'; import { MQTTDebugModal } from '../components/MQTTDebugModal'; +import { HMSErrorModal } from '../components/HMSErrorModal'; function formatTime(seconds: number): string { const hours = Math.floor(seconds / 3600); @@ -88,6 +89,7 @@ function PrinterCard({ printer, hideIfDisconnected }: { printer: Printer; hideIf const [showMQTTDebug, setShowMQTTDebug] = useState(false); const [showPowerOnConfirm, setShowPowerOnConfirm] = useState(false); const [showPowerOffConfirm, setShowPowerOffConfirm] = useState(false); + const [showHMSModal, setShowHMSModal] = useState(false); const { data: status } = useQuery({ queryKey: ['printerStatus', printer.id], @@ -173,25 +175,22 @@ function PrinterCard({ printer, hideIfDisconnected }: { printer: Printer; hideIf {/* HMS Status Indicator */} {status?.connected && ( - setShowHMSModal(true)} + className={`flex items-center gap-1 px-2 py-1 rounded-full text-xs cursor-pointer hover:opacity-80 transition-opacity ${ status.hms_errors && status.hms_errors.length > 0 ? status.hms_errors.some(e => e.severity <= 2) ? 'bg-red-500/20 text-red-400' : 'bg-orange-500/20 text-orange-400' : 'bg-bambu-green/20 text-bambu-green' }`} - title={ - status.hms_errors && status.hms_errors.length > 0 - ? `${status.hms_errors.length} HMS error(s)` - : 'No HMS errors' - } + title="Click to view HMS errors" > {status.hms_errors && status.hms_errors.length > 0 ? status.hms_errors.length : 'OK'} - + )}