Просмотр исходного кода

Fix H2C nozzle rack wrong empty slot, missing filament colors, and medium card layout (#300)

Three fixes for the H2C nozzle rack reported by testers:

1. Empty slot position: rack nozzles were mapped by sequential array
   index instead of by actual nozzle ID (16-21), so a missing slot
   always appeared at position 6 instead of its real position. Now
   maps each nozzle to its correct rack position via id offset.

2. Missing filament colors/materials: the H2C MQTT payload uses
   different field names than H2D — color_m (not filament_colour),
   fila_id (not filament_id), sn (not serial_number), tm (not
   max_temp). Added fallback field name resolution in the backend
   MQTT parser.

3. Medium card layout: the temperature row with nozzle rack overflowed
   on medium-sized cards. Added flex-wrap and a 190px flex-basis on
   the rack card so it wraps to a second line when space is tight.
maziggy 3 месяцев назад
Родитель
Сommit
7f22260c90
3 измененных файлов с 14 добавлено и 8 удалено
  1. 1 0
      CHANGELOG.md
  2. 8 4
      backend/app/services/bambu_mqtt.py
  3. 5 4
      frontend/src/pages/PrintersPage.tsx

+ 1 - 0
CHANGELOG.md

@@ -46,6 +46,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Support Bundle Shows 0 AMS Units** — The support info always reported `ams_unit_count: 0` because it expected `raw_data["ams"]` to be a nested dict (`{"ams": [...]}`) but the MQTT handler stores it as a flat list. Now handles both formats.
 - **Firmware Badge Shown for Models Without API Data** ([#311](https://github.com/maziggy/bambuddy/issues/311)) — Printers whose model has no firmware data in Bambu Lab's API (e.g. H2C on public beta firmware) showed a misleading green "up to date" badge. The badge is now hidden when the API returns no `latest_version`, since there is nothing to compare against.
 - **AMS-HT Mapping Fails for Left Nozzle on H2D Pro** ([#318](https://github.com/maziggy/bambuddy/issues/318)) — Printing with the left nozzle on dual-nozzle printers (H2D/H2D Pro) using AMS-HT failed with "Failed to get AMS mapping table." The global tray ID for AMS-HT units (ams_id >= 128) was calculated as `ams_id * 4 + tray_id` (= 512), but AMS-HT uses the raw `ams_id` (128) since it has a single tray. The backend then misidentified 512 as an external spool. Fixed in frontend tray ID calculation, backend `ams_mapping2` builder, print scheduler, and Spoolman tracking.
+- **H2C Nozzle Rack Shows Wrong Empty Slot and Missing Filament Colors** ([#300](https://github.com/maziggy/bambuddy/issues/300)) — Empty rack slots always appeared at position 6 instead of their actual position because nozzles were mapped by array index instead of by ID. Fixed by mapping each nozzle to its correct rack position (`id - 16`). Filament colors and materials were missing because the H2C uses different MQTT field names (`color_m`, `fila_id`, `sn`, `tm`) than the H2D (`filament_colour`, `filament_id`, `serial_number`, `max_temp`). Added fallback field name resolution. Also fixed nozzle rack layout breaking on medium card size by allowing the temperature row to wrap.
 
 ### Documentation
 - **Supported Printers Updated** — Updated README, website, and wiki to list all 12 supported Bambu Lab printer models: X1, X1C, X1E, P1P, P1S, P2S, A1, A1 Mini, H2D, H2D Pro, H2C, H2S. Removed outdated "Testers Needed" messaging and Tested/Needs Testing distinctions — all models are now uniformly listed as supported. Added H2C printer image to website. Added H2D Pro, H2C columns to wiki feature comparison tables and new P2 Series section.

+ 8 - 4
backend/app/services/bambu_mqtt.py

@@ -1761,10 +1761,14 @@ class BambuMQTTClient:
                                 "diameter": str(n.get("diameter", "")),
                                 "wear": n.get("wear"),
                                 "stat": n.get("stat"),
-                                "max_temp": n.get("max_temp", 0),
-                                "serial_number": str(n.get("serial_number", "")),
-                                "filament_color": str(n.get("filament_colour", "")),
-                                "filament_id": str(n.get("filament_id", "")),
+                                # H2C uses "tm", H2D uses "max_temp"
+                                "max_temp": n.get("max_temp") or n.get("tm", 0),
+                                # H2C uses "sn", H2D uses "serial_number"
+                                "serial_number": str(n.get("serial_number") or n.get("sn", "")),
+                                # H2C uses "color_m", H2D uses "filament_colour"
+                                "filament_color": str(n.get("filament_colour") or n.get("color_m", "")),
+                                # H2C uses "fila_id", H2D uses "filament_id"
+                                "filament_id": str(n.get("filament_id") or n.get("fila_id", "")),
                                 "filament_type": str(n.get("tray_type", "") or n.get("filament_type", "")),
                             }
                             for i, n in enumerate(nozzle_info)

+ 5 - 4
frontend/src/pages/PrintersPage.tsx

@@ -778,19 +778,20 @@ function DualNozzleHoverCard({ leftSlot, rightSlot, activeNozzle, children }: {
 function NozzleRackCard({ slots }: { slots: import('../api/client').NozzleRackSlot[] }) {
   const { t } = useTranslation();
   // Rack nozzles only (IDs >= 2) — excludes L/R hotend nozzles (IDs 0, 1)
+  // H2C rack IDs are 16-21 — map by actual ID so empty slots appear in the correct position
   const rackNozzles = slots.filter(s => s.id >= 2);
-  // Always show 6 rack positions — pad with empty placeholders for unoccupied slots
   const RACK_SIZE = 6;
+  const minRackId = rackNozzles.length > 0 ? Math.min(...rackNozzles.map(s => s.id)) : 16;
   const rackSlots: (import('../api/client').NozzleRackSlot)[] = Array.from(
     { length: RACK_SIZE },
-    (_, i) => rackNozzles[i] ?? {
+    (_, i) => rackNozzles.find(s => s.id === minRackId + i) ?? {
       id: -(i + 1), nozzle_type: '', nozzle_diameter: '', wear: null, stat: null,
       max_temp: 0, serial_number: '', filament_color: '', filament_id: '', filament_type: '',
     },
   );
 
   return (
-    <div className="text-center px-2.5 py-1.5 bg-bambu-dark rounded-lg flex-[2] flex flex-col justify-center">
+    <div className="text-center px-2.5 py-1.5 bg-bambu-dark rounded-lg flex-[2_1_190px] flex flex-col justify-center">
       <p className="text-[9px] text-bambu-gray mb-1">{t('printers.nozzleRack')}</p>
       <div className="flex gap-[3px] justify-center">
         {rackSlots.map((slot, i) => {
@@ -2387,7 +2388,7 @@ function PrinterCard({
               const rightNozzleSlot = status.nozzle_rack?.find(s => s.id === 1);
 
               return (
-                <div className="flex items-stretch gap-1.5">
+                <div className="flex items-stretch gap-1.5 flex-wrap">
                   {/* Nozzle temp - combined for dual nozzle */}
                   <div className="text-center px-2 py-1.5 bg-bambu-dark rounded-lg flex-1 flex flex-col justify-center items-center">
                     <HeaterThermometer className="w-3.5 h-3.5 mb-0.5" color="text-orange-400" isHeating={nozzleHeating} />