فهرست منبع

fix: surface AMS dry_sf_reason and backfill filament (#971)

  Start Drying was publishing a valid MQTT command that the firmware
  silently refused. The per-AMS dry_sf_reason array was parsed but never
  consulted before publish, so users with the AMS 2 Pro external PSU
  unplugged (code 8) saw nothing happen. The empty filament field in our
  payload was also a refusal trigger on some firmwares.

  The /drying/start route now inspects dry_sf_reason and returns a 409
  with a specific message, and backfills filament from the first loaded
  tray (default PLA) so the printer can't reject the command for a
  missing field.
maziggy 1 ماه پیش
والد
کامیت
4a5b608a81
2فایلهای تغییر یافته به همراه44 افزوده شده و 0 حذف شده
  1. 1 0
      CHANGELOG.md
  2. 43 0
      backend/app/api/routes/printers.py

+ 1 - 0
CHANGELOG.md

@@ -19,6 +19,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Plate-Clear Confirmation Disabled by Default** — New installs ship with Settings → Workflow → "Require Plate-Clear Confirmation" off. Multiple new users reported queued prints appearing to not start because the prompt was waiting for acknowledgement; opt in from Workflow if you want the confirmation gate.
 
 ### Fixed
+- **AMS Drying Silently Does Nothing** ([#971](https://github.com/maziggy/bambuddy/issues/971)) — Clicking Start Drying on a supported printer (e.g. P1S with AMS 2 Pro) could publish the MQTT command successfully but leave the AMS idle with no UI feedback. Two issues: (1) the firmware rejects the command when `dry_sf_reason` reports a blocking state (most commonly code 8 — AMS 2 Pro external power adapter not plugged in — but also "AMS busy", "already drying", etc.), and Bambuddy parsed that array but never surfaced it to the user; (2) the payload sent `filament: ""`, which some firmwares treat as an invalid-field refusal. The `/drying/start` endpoint now inspects the live `dry_sf_reason` for the target AMS unit and returns a descriptive 409 (e.g. "Plug in the external AMS power adapter to start drying") instead of silently publishing, and backfills an empty `filament` from the first loaded tray's type (defaulting to `PLA`) so the printer never rejects the command for a missing field. Thanks to @MartinNYHC for reporting.
 - **Webhook Tokens Leaked into Logs When Debug Logging Enabled (Security)** — Turning on Settings → Support → Debug Logging elevated the `httpx` and `httpcore` loggers to DEBUG, which caused httpx to log the full URL of every outbound HTTP request. For Discord notifications and generic webhook notifications, the URL *is* the secret — the bearer token is embedded in the path — so any user who enabled debug logging (typically to capture logs for a bug report) was writing their Discord webhook token to `bambuddy.log` and then pasting it into GitHub issues or support bundles. `httpx`/`httpcore` are now pinned to `WARNING` regardless of the debug toggle; `paho.mqtt` still honours debug. If you enabled debug logging while notifications were sending, rotate any exposed Discord/webhook URLs — the token is in the path, so the whole URL must be regenerated in the provider's UI.
 - **Queue Item Stuck in "Printing" When Start Command is Dropped** ([#967](https://github.com/maziggy/bambuddy/issues/967)) — If the physical printer dropped or ignored the MQTT `project_file` start command (same half-broken-session shape as #887/#936), the queue item was permanently orphaned in the `printing` status at 100% because the scheduler optimistically flipped the DB row to `printing` right after the publish succeeded locally and had no watchdog to revert it. Recovery required manually editing the SQLite `print_queue` table. A new watchdog now captures the printer's pre-dispatch state and polls for up to 45 s after `start_print()` returns; if the printer never transitions, the item is reverted to `pending` so the scheduler picks it up again, and the MQTT session is force-reconnected so the retry lands without a printer reboot. Thanks to @stringham for reporting.
 - **Queued Prints Require Printer Reboot to Start** ([#936](https://github.com/maziggy/bambuddy/issues/936)) — On some printers, a queued print would be uploaded via FTP and the `project_file` MQTT command would be sent, but the printer never transitioned out of `FINISH`/`IDLE` and required a power cycle to unstick — after which it often started a previously cancelled print rather than the intended one. Root cause is a half-broken MQTT session (same shape as #887): the printer keeps publishing telemetry so Bambuddy reports it as connected, but our publishes on the command topic never reach the firmware. Existing recovery only triggered via the developer-mode probe path, which skips printers that already have a known `developer_mode` value. The print-dispatch verifier now treats an unacknowledged `project_file` (state unchanged after 15 s) as the same "commands not reaching printer" signal and forces a fresh MQTT session so the next dispatch can land without a printer reboot. The existing dev-mode probe path is refactored to share the same helper.

+ 43 - 0
backend/app/api/routes/printers.py

@@ -1493,6 +1493,49 @@ async def start_drying(
     if duration < 1 or duration > 24:
         raise HTTPException(400, "Duration must be 1-24 hours")
 
+    # Inspect the live AMS unit: surface blocking dry_sf_reasons (otherwise the
+    # firmware silently ignores the command — #971) and backfill an empty
+    # filament field from the first loaded tray so the printer doesn't reject
+    # the payload.
+    target_ams: dict | None = None
+    for unit in (live_state.raw_data.get("ams") if live_state else None) or []:
+        try:
+            if int(unit.get("id", -1)) == ams_id:
+                target_ams = unit
+                break
+        except (TypeError, ValueError):
+            continue
+
+    if target_ams is not None:
+        reason_messages = {
+            0: "Printer is busy",
+            1: "Insufficient power — too many AMS drying or external PSU required",
+            2: "AMS is busy",
+            3: "Filament is at the AMS outlet — retract it first",
+            4: "AMS is already starting a drying cycle",
+            5: "Not supported in 2D mode",
+            6: "AMS is already drying",
+            7: "AMS firmware is upgrading",
+            8: "Plug in the external AMS power adapter to start drying",
+        }
+        for code in target_ams.get("dry_sf_reason") or []:
+            try:
+                code_int = int(code)
+            except (TypeError, ValueError):
+                continue
+            if code_int in reason_messages:
+                raise HTTPException(409, reason_messages[code_int])
+
+        if not filament:
+            for tray in target_ams.get("tray") or []:
+                tray_type = tray.get("tray_type")
+                if tray_type:
+                    filament = str(tray_type)
+                    break
+
+    if not filament:
+        filament = "PLA"
+
     success = printer_manager.send_drying_command(
         printer_id, ams_id, temp, duration, mode=1, filament=filament, rotate_tray=rotate_tray
     )