Explorar o código

Fix Home Assistant smart plug auto-on for queued prints (Issue #200)

The print scheduler was hardcoded to use Tasmota service when powering
on printers for queued prints. Home Assistant plugs were not checked,
causing "Tasmota device not found" errors.

Changes:
- Make get_service_for_plug() public in smart_plug_manager
- Update print_scheduler to use smart_plug_manager.get_service_for_plug()
  instead of tasmota_service directly
- Fixes both _power_on_and_wait() and _power_off_if_needed() methods

Closes #200
maziggy hai 7 meses
pai
achega
428f54a68d

+ 4 - 0
CHANGELOG.md

@@ -157,6 +157,10 @@ All notable changes to Bambuddy will be documented in this file.
   - Tooltip shows full name on hover
   - Tooltip shows full name on hover
 - **K-Profiles Backup Status** - Fixed GitHub backup settings showing incorrect printer connection count (e.g., "1/2 connected" when both printers are connected); now fetches status from API instead of relying on WebSocket cache
 - **K-Profiles Backup Status** - Fixed GitHub backup settings showing incorrect printer connection count (e.g., "1/2 connected" when both printers are connected); now fetches status from API instead of relying on WebSocket cache
 - **GitHub Backup Timestamps** - Removed volatile timestamps from GitHub backup files so git diffs only show actual data changes
 - **GitHub Backup Timestamps** - Removed volatile timestamps from GitHub backup files so git diffs only show actual data changes
+- **Home Assistant Smart Plug Auto-On** - Fixed print scheduler only checking Tasmota plugs when powering on printers for queued prints (Issue #200):
+  - Home Assistant smart plugs now work with automatic printer power-on for scheduled queue jobs
+  - Previously only Tasmota plugs were checked, causing HA plugs to fail with "Tasmota device not found" error
+  - Both `_power_on_and_wait` and `_power_off_if_needed` now use the correct service based on plug type
 
 
 ### Maintenance
 ### Maintenance
 - Upgraded vitest from 2.x to 3.x to resolve npm audit security vulnerabilities in dev dependencies
 - Upgraded vitest from 2.x to 3.x to resolve npm audit security vulnerabilities in dev dependencies

+ 8 - 4
backend/app/services/print_scheduler.py

@@ -17,7 +17,7 @@ from backend.app.models.smart_plug import SmartPlug
 from backend.app.services.bambu_ftp import delete_file_async, get_ftp_retry_settings, upload_file_async, with_ftp_retry
 from backend.app.services.bambu_ftp import delete_file_async, get_ftp_retry_settings, upload_file_async, with_ftp_retry
 from backend.app.services.notification_service import notification_service
 from backend.app.services.notification_service import notification_service
 from backend.app.services.printer_manager import printer_manager
 from backend.app.services.printer_manager import printer_manager
-from backend.app.services.tasmota import tasmota_service
+from backend.app.services.smart_plug_manager import smart_plug_manager
 from backend.app.utils.printer_models import normalize_printer_model
 from backend.app.utils.printer_models import normalize_printer_model
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
@@ -348,15 +348,18 @@ class PrintScheduler:
 
 
         Returns True if printer connected successfully within timeout.
         Returns True if printer connected successfully within timeout.
         """
         """
+        # Get the appropriate service for the plug type (Tasmota or Home Assistant)
+        service = await smart_plug_manager.get_service_for_plug(plug, db)
+
         # Check current plug state
         # Check current plug state
-        status = await tasmota_service.get_status(plug)
+        status = await service.get_status(plug)
         if not status.get("reachable"):
         if not status.get("reachable"):
             logger.warning(f"Smart plug '{plug.name}' is not reachable")
             logger.warning(f"Smart plug '{plug.name}' is not reachable")
             return False
             return False
 
 
         # Turn on if not already on
         # Turn on if not already on
         if status.get("state") != "ON":
         if status.get("state") != "ON":
-            success = await tasmota_service.turn_on(plug)
+            success = await service.turn_on(plug)
             if not success:
             if not success:
                 logger.warning(f"Failed to turn on smart plug '{plug.name}'")
                 logger.warning(f"Failed to turn on smart plug '{plug.name}'")
                 return False
                 return False
@@ -425,7 +428,8 @@ class PrintScheduler:
             # Wait for cooldown (up to 10 minutes)
             # Wait for cooldown (up to 10 minutes)
             await printer_manager.wait_for_cooldown(item.printer_id, target_temp=50.0, timeout=600)
             await printer_manager.wait_for_cooldown(item.printer_id, target_temp=50.0, timeout=600)
             logger.info(f"Auto-off: Powering off printer {item.printer_id}")
             logger.info(f"Auto-off: Powering off printer {item.printer_id}")
-            await tasmota_service.turn_off(plug)
+            service = await smart_plug_manager.get_service_for_plug(plug, db)
+            await service.turn_off(plug)
 
 
     async def _get_job_name(self, db: AsyncSession, item: PrintQueueItem) -> str:
     async def _get_job_name(self, db: AsyncSession, item: PrintQueueItem) -> str:
         """Get a human-readable name for a queue item."""
         """Get a human-readable name for a queue item."""

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

@@ -27,7 +27,7 @@ class SmartPlugManager:
         self._scheduler_task: asyncio.Task | None = None
         self._scheduler_task: asyncio.Task | None = None
         self._last_schedule_check: dict[int, str] = {}  # plug_id -> "HH:MM" last executed
         self._last_schedule_check: dict[int, str] = {}  # plug_id -> "HH:MM" last executed
 
 
-    async def _get_service_for_plug(self, plug: "SmartPlug", db: AsyncSession | None = None):
+    async def get_service_for_plug(self, plug: "SmartPlug", db: AsyncSession | None = None):
         """Get the appropriate service for the plug type.
         """Get the appropriate service for the plug type.
 
 
         For HA plugs, configures the service with current settings from DB.
         For HA plugs, configures the service with current settings from DB.
@@ -110,7 +110,7 @@ class SmartPlugManager:
             plugs = result.scalars().all()
             plugs = result.scalars().all()
 
 
             for plug in plugs:
             for plug in plugs:
-                service = await self._get_service_for_plug(plug, db)
+                service = await self.get_service_for_plug(plug, db)
 
 
                 # Check if we should turn on
                 # Check if we should turn on
                 if plug.schedule_on_time == current_time:
                 if plug.schedule_on_time == current_time:
@@ -166,7 +166,7 @@ class SmartPlugManager:
 
 
         # Turn on the plug
         # Turn on the plug
         logger.info(f"Print started on printer {printer_id}, turning on plug '{plug.name}'")
         logger.info(f"Print started on printer {printer_id}, turning on plug '{plug.name}'")
-        service = await self._get_service_for_plug(plug, db)
+        service = await self.get_service_for_plug(plug, db)
         success = await service.turn_on(plug)
         success = await service.turn_on(plug)
 
 
         if success:
         if success:
@@ -261,7 +261,7 @@ class SmartPlugManager:
                     self.name = f"plug_{plug_id}"
                     self.name = f"plug_{plug_id}"
 
 
             plug_info = PlugInfo()
             plug_info = PlugInfo()
-            service = await self._get_service_for_plug(plug_info)
+            service = await self.get_service_for_plug(plug_info)
             success = await service.turn_off(plug_info)
             success = await service.turn_off(plug_info)
             logger.info(f"Turned off plug {plug_id} after time delay")
             logger.info(f"Turned off plug {plug_id} after time delay")
 
 
@@ -353,7 +353,7 @@ class SmartPlugManager:
                                 self.name = f"plug_{plug_id}"
                                 self.name = f"plug_{plug_id}"
 
 
                         plug_info = PlugInfo()
                         plug_info = PlugInfo()
-                        service = await self._get_service_for_plug(plug_info)
+                        service = await self.get_service_for_plug(plug_info)
                         success = await service.turn_off(plug_info)
                         success = await service.turn_off(plug_info)
                         logger.info(
                         logger.info(
                             f"Turned off plug {plug_id} after nozzle temp dropped to "
                             f"Turned off plug {plug_id} after nozzle temp dropped to "
@@ -474,7 +474,7 @@ class SmartPlugManager:
                         # For time mode, just turn off immediately since delay already passed
                         # For time mode, just turn off immediately since delay already passed
                         logger.info(f"Time-based auto-off was pending, turning off plug '{plug.name}' now")
                         logger.info(f"Time-based auto-off was pending, turning off plug '{plug.name}' now")
 
 
-                        service = await self._get_service_for_plug(plug, db)
+                        service = await self.get_service_for_plug(plug, db)
                         success = await service.turn_off(plug)
                         success = await service.turn_off(plug)
                         if success:
                         if success:
                             await self._mark_auto_off_executed(plug.id)
                             await self._mark_auto_off_executed(plug.id)