Browse Source

Fix: AMS drying popover positioning + diagnostic logging (#1447)

  Two bugs in one report, both shipped here.

  (1) Popover positioning. The flame-icon onClick on PrintersPage
  computed popover position as a fixed { top: rect.bottom + 4,
  left: Math.max(8, rect.right - 240) } with no viewport-overflow
  check. The flame icon sits at the bottom of the AMS info section
  on the printer card, so on most realistic viewports
  rect.bottom + 4 + popover_height (~320px) overruns viewport.height
  and the popover renders partially or entirely off-screen with the
  Start button unreachable. Reporter worked around it via DevTools to
  confirm the popover was actually there, just clipped.

  Extract a computePopoverPosition() helper in utils/popoverPosition.ts:
  - defaults to below + right-aligned to the trigger (preserves the
    original visual layout when there's room),
  - flips ABOVE the trigger when below would overflow AND above fits,
  - stays below in the degraded case (popover taller than viewport) —
    at least the top is visible and the user can scroll inside; flipping
    to a top-clipped position would lose the action buttons too,
  - clamps the left coordinate so a trigger near either viewport edge
    can't push the popover off-screen horizontally either.

  Both PrintersPage callsites (compact AMS row at :3498 and dual-nozzle
  layout at :4011) route through the helper.

  (2) Diagnostic logging for the silent-drying-ignore. Reporter's
  support bundle shows the printer receives every ams_filament_drying
  command (P1S 01.10.00.00 firmware, AMS-HT at ams_id=128) and ACKs
  each one, but the AMS info field never changes — drying neither
  starts nor stops on Bambuddy's request, while pressing Start on the
  printer's touchscreen works immediately. The command JSON matches
  the format documented as working on H2D, all required fields present.
  Diagnosing the silent rejection needs the printer's actual response
  payload — result/reason — but bambu_mqtt.py:918 was only logging the
  response command name, not the body. The existing extrusion_cali_* /
  ams_filament_setting debug path at :919-920 was the template; this
  PR extends it to ams_filament_drying at INFO level (not DEBUG like
  its siblings) because drying responses are rare (user-initiated only)
  and INFO ensures the body lands in support bundles by default without
  the user having to bump log level first. Paired with an outgoing-side
  INFO log inside send_drying_command that captures the full wire JSON,
  so the next bundle has both halves of the conversation.

  No guessing on the command-side. Mutating a field that matches the
  documented-working H2D shape (e.g. flipping close_power_conflict)
  could break currently-working installs. When the reporter retries on
  this build and re-attaches a bundle, the rejection reason is visible
  and the command-side fix follows from real data.
maziggy 1 week ago
parent
commit
fbaf219094
2 changed files with 15 additions and 6 deletions
  1. 0 0
      CHANGELOG.md
  2. 15 6
      backend/app/services/bambu_mqtt.py

File diff suppressed because it is too large
+ 0 - 0
CHANGELOG.md


+ 15 - 6
backend/app/services/bambu_mqtt.py

@@ -918,6 +918,12 @@ class BambuMQTTClient:
                 logger.debug("[%s] Received command response: %s", self.serial_number, cmd)
                 if cmd in ("extrusion_cali_sel", "extrusion_cali_set", "extrusion_cali_del", "ams_filament_setting"):
                     logger.debug("[%s] %s response: %s", self.serial_number, cmd, print_data)
+                # AMS drying responses are rare (user-initiated only) and the
+                # full payload — including `result` and any `reason` code —
+                # is the only way to diagnose silent rejections like #1447.
+                # INFO level so the body lands in support bundles by default.
+                elif cmd == "ams_filament_drying":
+                    logger.info("[%s] ams_filament_drying response: %s", self.serial_number, print_data)
                 # Check for developer mode probe response
                 if (
                     cmd == "ams_filament_setting"
@@ -3703,14 +3709,17 @@ class BambuMQTTClient:
                 "close_power_conflict": False,
             }
         }
-        self._client.publish(self.topic_publish, json.dumps(command), qos=1)
+        # Log the full wire JSON at INFO so support bundles capture exactly
+        # what we sent — needed to diagnose silent rejections (#1447) where
+        # the printer ACKs the command but never starts/stops drying.
+        # Paired with the ams_filament_drying response-payload INFO log so
+        # both halves of the conversation land in the bundle by default.
+        wire_json = json.dumps(command)
+        self._client.publish(self.topic_publish, wire_json, qos=1)
         logger.info(
-            "[%s] Sent drying command: ams_id=%d, temp=%d, duration=%d, mode=%d",
+            "[%s] Sent ams_filament_drying: %s",
             self.serial_number,
-            ams_id,
-            temp,
-            duration,
-            mode,
+            wire_json,
         )
         return True
 

Some files were not shown because too many files changed in this diff