Some small improvements for auto poweroff feature

This commit is contained in:
Martin Ziegler
2025-11-29 10:27:26 +01:00
parent a3167562bb
commit eaeac8be93
3 changed files with 54 additions and 14 deletions
+19 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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"
)