Files
HomeAssistantVS/custom_components/intelligent_heating_pilot/infrastructure/adapters/scheduler_commander.py
T
2026-07-08 10:43:39 -04:00

140 lines
4.7 KiB
Python

"""Home Assistant scheduler commander adapter.
This adapter implements ISchedulerCommander by calling Home Assistant
scheduler services to trigger heating actions.
"""
from __future__ import annotations
import logging
from datetime import datetime
from homeassistant.core import HomeAssistant
from ...domain.interfaces.scheduler_commander_interface import ISchedulerCommander
from .utils import get_entity_name
_LOGGER = logging.getLogger(__name__)
# Scheduler service configuration
SCHEDULER_DOMAIN = "scheduler"
SERVICE_RUN_ACTION = "run_action"
class HASchedulerCommander(ISchedulerCommander):
"""Home Assistant implementation of scheduler commander.
Executes scheduler commands using the scheduler-component's run_action service.
See: https://github.com/nielsfaber/scheduler-component/#schedulerrun_action
This adapter contains NO business logic - it only translates domain
requests into Home Assistant service calls.
"""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the scheduler commander adapter.
Args:
hass: Home Assistant instance
scheduler_entity_id: The scheduler entity ID to control
"""
self._hass = hass
async def run_action(self, target_time: datetime, scheduler_entity_id: str) -> None:
"""Trigger a scheduler action for a specific timeslot.
This will start heating in the mode configured in the scheduler
for the timeslot at the given time.
Args:
target_time: The time of the scheduler timeslot to trigger
scheduler_entity_id: Scheduler entity ID to control
Raises:
ValueError: If scheduler entity is not configured
"""
if not scheduler_entity_id:
_LOGGER.error("Cannot run action: no scheduler entity configured")
raise ValueError("Scheduler entity ID not configured")
# Format time as HH:MM for scheduler service
trigger_time_str = target_time.strftime("%H:%M")
device_name = get_entity_name(self._hass, scheduler_entity_id)
_LOGGER.info("[%s] Triggering scheduler action at time %s", device_name, trigger_time_str)
try:
await self._hass.services.async_call(
SCHEDULER_DOMAIN,
SERVICE_RUN_ACTION,
{
"entity_id": scheduler_entity_id,
"time": trigger_time_str,
"skip_conditions": False, # Respect scheduler conditions
},
blocking=True,
)
_LOGGER.debug("Scheduler action triggered successfully")
except Exception as err:
_LOGGER.error(
"Failed to trigger scheduler action for %s: %s",
scheduler_entity_id,
err,
exc_info=True,
)
raise
async def cancel_action(self, scheduler_entity_id: str) -> None:
"""Cancel current scheduler action and return to current timeslot.
This is used to stop overshoot by reverting to the mode configured
for the current time (now()).
Note: The scheduler-component doesn't have a direct "cancel" service.
This implementation triggers the action for "now" which effectively
reverts to the current scheduled state.
Args:
scheduler_entity_id: Scheduler entity ID to control
Raises:
ValueError: If scheduler entity is not configured
"""
if not scheduler_entity_id:
_LOGGER.error("Cannot cancel action: no scheduler entity configured")
raise ValueError("Scheduler entity ID not configured")
# Get current time
from homeassistant.util import dt as dt_util
now = dt_util.now()
current_time_str = now.strftime("%H:%M")
device_name = get_entity_name(self._hass, scheduler_entity_id)
_LOGGER.info(
"[%s] Canceling scheduler action by reverting to current time %s",
device_name,
current_time_str,
)
try:
await self._hass.services.async_call(
SCHEDULER_DOMAIN,
SERVICE_RUN_ACTION,
{
"entity_id": scheduler_entity_id,
"time": current_time_str,
"skip_conditions": False,
},
blocking=True,
)
_LOGGER.debug("Scheduler action canceled successfully")
except Exception as err:
_LOGGER.error(
"Failed to cancel scheduler action for %s: %s",
scheduler_entity_id,
err,
exc_info=True,
)
raise