Browse Source

Fix spurious error notifications from print_error status codes

  Some firmware sends non-zero print_error values (e.g. 0x03000002 →
  0300_0002) during normal printing as status/phase indicators. The
  parser treated any non-zero value as a real error, triggering
  notifications. All known real errors have codes >= 0x4000 (fatal,
  warning, prompt ranges). Now skips print_error values where the
  low 16 bits are below 0x4000.

  Also fix K-profile greenlet error on auto-created RFID spools by
  eagerly setting k_profiles=[] after flush in create_spool_from_tray().
maziggy 2 months ago
parent
commit
e2ba768c2e
2 changed files with 30 additions and 23 deletions
  1. 1 0
      CHANGELOG.md
  2. 29 23
      backend/app/services/bambu_mqtt.py

+ 1 - 0
CHANGELOG.md

@@ -21,6 +21,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Inventory Scale Weight Check Column** — Added a "Weight Check" column (hidden by default) to the inventory table that compares each spool's last scale measurement against its calculated gross weight (net remaining + core weight). Spools within a ±50g tolerance show a green checkmark; mismatched spools show a yellow warning with the difference and a sync button that trusts the scale reading and resets weight tracking. The backend stores `last_scale_weight` and `last_weighed_at` on each spool whenever weight is synced via SpoolBuddy, and the column tooltip shows scale weight, calculated weight, and difference. Edge case: when scale weight is below core weight (empty spool or not on scale), the comparison treats it as a match since sync can't correct this.
 
 ### Fixed
+- **Spurious Error Notifications During Normal Printing (0300_0002)** — Some firmware versions send non-zero `print_error` values in MQTT during normal printing (e.g., `0x03000002` → short code `0300_0002`). The `print_error` parser treated any non-zero value as a real error, appending it to `hms_errors` and triggering notifications — even though the printer was printing fine. All known real HMS error codes have their low 16 bits >= `0x4000` (`0x4xxx` = fatal, `0x8xxx` = warning/pause, `0xCxxx` = prompt). Values below `0x4000` are status/phase indicators, not faults. Now skips `print_error` values where the error portion is below `0x4000`.
 - **K-Profile Apply Fails With Greenlet Error on Auto-Created Spools** — When a Bambu Lab spool was detected via RFID for the first time (auto-creating a new inventory entry), the K-profile application step logged `WARNING greenlet_spawn has not been called; can't call await_only() here`. The `create_spool_from_tray()` function flushed the new spool to the database but didn't eagerly load the `k_profiles` relationship. When `auto_assign_spool()` then iterated `spool.k_profiles` to find a matching K-profile, SQLAlchemy attempted a lazy load — which requires a synchronous DB call that's illegal inside an async context. The K-profile step was silently skipped (caught by `except Exception`), so spool assignment still worked but without K-profile selection. Now eagerly sets `k_profiles = []` on newly created spools since they can never have K-profiles yet.
 - **SpoolBuddy Link Tag Missing tag_type** — Linking an NFC tag to a spool via the SpoolBuddy dashboard's "Link to Spool" action only set `tag_uid` but left `tag_type` and `data_origin` empty, because it called the generic `updateSpool` API instead of the dedicated `linkTagToSpool` endpoint. The printer card's `LinkSpoolModal` already used `linkTagToSpool` correctly. Now uses `linkTagToSpool` with `tag_type: 'generic'` and `data_origin: 'nfc_link'`, which also handles conflict checks and archived tag recycling.
 - **SpoolBuddy AMS Page Missing Fill Levels for Non-BL Spools** — AMS slots with non-Bambu Lab spools assigned to inventory didn't show fill level bars on the SpoolBuddy AMS page, even though the main printer card displayed them correctly. The SpoolBuddy AMS page only used the MQTT `remain` field (which is -1/unknown for non-BL spools), while the printer card had a fallback chain: Spoolman → inventory → AMS remain. Now fetches inventory spool assignments and computes fill levels from `(label_weight - weight_used) / label_weight`, falling back to AMS remain when no inventory assignment exists.

+ 29 - 23
backend/app/services/bambu_mqtt.py

@@ -1875,32 +1875,38 @@ class BambuMQTTClient:
                 module = (print_error >> 16) & 0xFFFF  # High 16 bits (e.g., 0x0500)
                 error = print_error & 0xFFFF  # Low 16 bits (e.g., 0x8061)
 
-                # Store in a format that matches the community error database
-                # attr stores the full 32-bit value for reconstruction
-                # code stores the short format string for lookup
-                short_code = f"{module:04X}_{error:04X}"
+                # Values below 0x4000 are status/phase indicators, not real errors.
+                # All known HMS errors use 0x4xxx (fatal), 0x8xxx (warning), 0xCxxx (prompt).
+                # Some firmware sends low values like 0x0002 during normal printing.
+                if error < 0x4000:
+                    pass  # Skip — not a real error
+                else:
+                    # Store in a format that matches the community error database
+                    # attr stores the full 32-bit value for reconstruction
+                    # code stores the short format string for lookup
+                    short_code = f"{module:04X}_{error:04X}"
 
-                logger.debug(
-                    f"[{self.serial_number}] print_error: {print_error} (0x{print_error:08x}) -> short_code={short_code}"
-                )
+                    logger.debug(
+                        f"[{self.serial_number}] print_error: {print_error} (0x{print_error:08x}) -> short_code={short_code}"
+                    )
 
-                # Only add if not already in HMS errors (avoid duplicates)
-                existing_short_codes = set()
-                for e in self.state.hms_errors:
-                    # Extract short code from existing errors
-                    e_module = (e.attr >> 16) & 0xFFFF
-                    e_error = int(e.code.replace("0x", ""), 16) if e.code else 0
-                    existing_short_codes.add(f"{e_module:04X}_{e_error:04X}")
-
-                if short_code not in existing_short_codes:
-                    self.state.hms_errors.append(
-                        HMSError(
-                            code=f"0x{error:x}",
-                            attr=print_error,  # Store full value for display
-                            module=module >> 8,  # High byte of module (e.g., 0x05)
-                            severity=3,  # Warning level for print_error
+                    # Only add if not already in HMS errors (avoid duplicates)
+                    existing_short_codes = set()
+                    for e in self.state.hms_errors:
+                        # Extract short code from existing errors
+                        e_module = (e.attr >> 16) & 0xFFFF
+                        e_error = int(e.code.replace("0x", ""), 16) if e.code else 0
+                        existing_short_codes.add(f"{e_module:04X}_{e_error:04X}")
+
+                    if short_code not in existing_short_codes:
+                        self.state.hms_errors.append(
+                            HMSError(
+                                code=f"0x{error:x}",
+                                attr=print_error,  # Store full value for display
+                                module=module >> 8,  # High byte of module (e.g., 0x05)
+                                severity=3,  # Warning level for print_error
+                            )
                         )
-                    )
 
         # Parse SD card status
         if "sdcard" in data: