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

Some small improvements for auto poweroff feature

Martin Ziegler 9 месяцев назад
Родитель
Сommit
eaeac8be93

+ 19 - 7
backend/app/api/routes/archives.py

@@ -539,10 +539,17 @@ async def scan_timelapse(
     base_name = Path(archive.filename).stem
 
     # Scan timelapse directory on printer
-    try:
-        files = await list_files_async(printer.ip_address, printer.access_code, "/timelapse/video")
-    except Exception:
-        raise HTTPException(500, "Failed to connect to printer")
+    # Try both /timelapse and /timelapse/video (different printer models use different paths)
+    files = []
+    for timelapse_path in ["/timelapse", "/timelapse/video"]:
+        try:
+            files = await list_files_async(printer.ip_address, printer.access_code, timelapse_path)
+            if files:
+                break
+        except Exception:
+            continue
+    if not files:
+        raise HTTPException(500, "Failed to connect to printer or no timelapse directory found")
 
     # Look for matching timelapse
     matching_file = None
@@ -574,7 +581,12 @@ async def scan_timelapse(
                     # Timelapse is usually created at print end, so compare to completed_at or created_at
                     compare_time = archive.completed_at or archive.created_at
                     if compare_time:
-                        diff = abs(file_time - compare_time)
+                        # Bambu printers use China Standard Time (UTC+8) for filenames
+                        # Try matching with CST offset adjustment
+                        diff_direct = abs(file_time - compare_time)
+                        # Also try with 8-hour offset (CST to UTC-ish local times)
+                        diff_cst_adjusted = abs(file_time - timedelta(hours=8) - compare_time)
+                        diff = min(diff_direct, diff_cst_adjusted)
                         if diff < best_diff:
                             best_diff = diff
                             best_match = f
@@ -587,8 +599,8 @@ async def scan_timelapse(
     if not matching_file:
         return {"status": "not_found", "message": "No matching timelapse found on printer"}
 
-    # Download the timelapse
-    remote_path = f"/timelapse/video/{matching_file['name']}"
+    # Download the timelapse - use the full path from the file listing
+    remote_path = matching_file.get('path') or f"/timelapse/{matching_file['name']}"
     timelapse_data = await download_file_bytes_async(
         printer.ip_address, printer.access_code, remote_path
     )

+ 16 - 1
backend/app/services/bambu_mqtt.py

@@ -153,6 +153,11 @@ class BambuMQTTClient:
 
         # Temperature data
         temps = {}
+        # Log all temperature-related fields for debugging (only when we have temp data)
+        temp_fields = {k: v for k, v in data.items() if 'temp' in k.lower() or 'nozzle' in k.lower()}
+        if temp_fields and not hasattr(self, '_temp_fields_logged'):
+            logger.info(f"[{self.serial_number}] Temperature fields in MQTT data: {temp_fields}")
+            self._temp_fields_logged = True
         if "bed_temper" in data:
             temps["bed"] = float(data["bed_temper"])
         if "bed_target_temper" in data:
@@ -162,10 +167,20 @@ class BambuMQTTClient:
         if "nozzle_target_temper" in data:
             temps["nozzle_target"] = float(data["nozzle_target_temper"])
         # Second nozzle for dual-extruder printers (H2 series)
+        # Try multiple possible field names used by different firmware versions
         if "nozzle_temper_2" in data:
             temps["nozzle_2"] = float(data["nozzle_temper_2"])
+        elif "right_nozzle_temper" in data:
+            temps["nozzle_2"] = float(data["right_nozzle_temper"])
         if "nozzle_target_temper_2" in data:
             temps["nozzle_2_target"] = float(data["nozzle_target_temper_2"])
+        elif "right_nozzle_target_temper" in data:
+            temps["nozzle_2_target"] = float(data["right_nozzle_target_temper"])
+        # Also check for left nozzle as primary (some H2 models)
+        if "left_nozzle_temper" in data and "nozzle" not in temps:
+            temps["nozzle"] = float(data["left_nozzle_temper"])
+        if "left_nozzle_target_temper" in data and "nozzle_target" not in temps:
+            temps["nozzle_target"] = float(data["left_nozzle_target_temper"])
         if "chamber_temper" in data:
             temps["chamber"] = float(data["chamber_temper"])
         if temps:
@@ -232,7 +247,7 @@ class BambuMQTTClient:
                 "raw_data": data,
             })
 
-        # Detect print completion
+        # Detect print completion (FINISH = success, FAILED = error)
         if (
             self._previous_gcode_state == "RUNNING"
             and self.state.state in ("FINISH", "FAILED")

+ 19 - 6
backend/app/services/smart_plug_manager.py

@@ -70,7 +70,11 @@ class SmartPlugManager:
     async def on_print_complete(
         self, printer_id: int, status: str, db: AsyncSession
     ):
-        """Called when a print completes - schedule turn off if configured."""
+        """Called when a print completes - schedule turn off if configured.
+
+        Only triggers auto-off on successful completion (status='completed').
+        Failed prints keep the printer powered on for user investigation.
+        """
         plug = await self._get_plug_for_printer(printer_id, db)
 
         if not plug:
@@ -84,8 +88,17 @@ class SmartPlugManager:
             logger.debug(f"Smart plug '{plug.name}' auto_off is disabled")
             return
 
+        # Only auto-off on successful completion, not on failures
+        # This allows the user to investigate errors before power-off
+        if status != "completed":
+            logger.info(
+                f"Print on printer {printer_id} ended with status '{status}', "
+                f"skipping auto-off for plug '{plug.name}' to allow investigation"
+            )
+            return
+
         logger.info(
-            f"Print completed on printer {printer_id} (status: {status}), "
+            f"Print completed successfully on printer {printer_id}, "
             f"scheduling turn-off for plug '{plug.name}'"
         )
 
@@ -192,14 +205,14 @@ class SmartPlugManager:
                     max_nozzle_temp = nozzle_temp
                     if nozzle_2_temp is not None:
                         max_nozzle_temp = max(nozzle_temp, nozzle_2_temp)
-                        logger.debug(
-                            f"Checking temp for plug {plug_id}: nozzle1={nozzle_temp}°C, "
+                        logger.info(
+                            f"Temp check plug {plug_id}: nozzle1={nozzle_temp}°C, "
                             f"nozzle2={nozzle_2_temp}°C, max={max_nozzle_temp}°C, "
                             f"threshold={temp_threshold}°C"
                         )
                     else:
-                        logger.debug(
-                            f"Checking temp for plug {plug_id}: nozzle={nozzle_temp}°C, "
+                        logger.info(
+                            f"Temp check plug {plug_id}: nozzle={nozzle_temp}°C, "
                             f"threshold={temp_threshold}°C"
                         )