From 428f54a68d76cb8b0bb903f12f87291d918d3a3e Mon Sep 17 00:00:00 2001 From: maziggy Date: Sat, 31 Jan 2026 12:59:45 +0100 Subject: [PATCH] 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 --- CHANGELOG.md | 4 ++++ backend/app/services/print_scheduler.py | 12 ++++++++---- backend/app/services/smart_plug_manager.py | 12 ++++++------ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c38fffb10..505809b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -157,6 +157,10 @@ All notable changes to Bambuddy will be documented in this file. - 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 - **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 - Upgraded vitest from 2.x to 3.x to resolve npm audit security vulnerabilities in dev dependencies diff --git a/backend/app/services/print_scheduler.py b/backend/app/services/print_scheduler.py index 81ab1e520..d96d09883 100644 --- a/backend/app/services/print_scheduler.py +++ b/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.notification_service import notification_service 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 logger = logging.getLogger(__name__) @@ -348,15 +348,18 @@ class PrintScheduler: 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 - status = await tasmota_service.get_status(plug) + status = await service.get_status(plug) if not status.get("reachable"): logger.warning(f"Smart plug '{plug.name}' is not reachable") return False # Turn on if not already on if status.get("state") != "ON": - success = await tasmota_service.turn_on(plug) + success = await service.turn_on(plug) if not success: logger.warning(f"Failed to turn on smart plug '{plug.name}'") return False @@ -425,7 +428,8 @@ class PrintScheduler: # Wait for cooldown (up to 10 minutes) 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}") - 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: """Get a human-readable name for a queue item.""" diff --git a/backend/app/services/smart_plug_manager.py b/backend/app/services/smart_plug_manager.py index 39366d7da..34fcd2c5c 100644 --- a/backend/app/services/smart_plug_manager.py +++ b/backend/app/services/smart_plug_manager.py @@ -27,7 +27,7 @@ class SmartPlugManager: self._scheduler_task: asyncio.Task | None = None 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. For HA plugs, configures the service with current settings from DB. @@ -110,7 +110,7 @@ class SmartPlugManager: plugs = result.scalars().all() 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 if plug.schedule_on_time == current_time: @@ -166,7 +166,7 @@ class SmartPlugManager: # Turn on the plug 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) if success: @@ -261,7 +261,7 @@ class SmartPlugManager: self.name = f"plug_{plug_id}" 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) logger.info(f"Turned off plug {plug_id} after time delay") @@ -353,7 +353,7 @@ class SmartPlugManager: self.name = f"plug_{plug_id}" 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) logger.info( 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 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) if success: await self._mark_auto_off_executed(plug.id)