Initil after Upgrade

This commit is contained in:
2026-06-15 10:53:52 -04:00
parent 2fe9bf0dd6
commit 887feaa50a
143 changed files with 2288 additions and 881 deletions
@@ -10,8 +10,8 @@ import gzip
import logging
import os
from homeassistant.const import STATE_OFF
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, State, callback
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, STATE_OFF
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, State, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_point_in_time
from homeassistant.helpers.typing import ConfigType
@@ -64,6 +64,7 @@ class PlaybookStrategy(PowerCalculationStrategyInterface):
self._update_callback: Callable[[Decimal], None] = lambda power: None
self._start_time: datetime = dt.utcnow()
self._cancel_timer: CALLBACK_TYPE | None = None
self._cancel_stop_listener: CALLBACK_TYPE | None = None
self._config = config
self._repeat: bool = bool(config.get(CONF_REPEAT))
self._autostart: str | None = config.get(CONF_AUTOSTART)
@@ -116,6 +117,9 @@ class PlaybookStrategy(PowerCalculationStrategyInterface):
if self._cancel_timer is not None:
self._cancel_timer()
self._cancel_timer = None
if self._cancel_stop_listener is not None:
self._cancel_stop_listener()
self._cancel_stop_listener = None
def get_active_playbook(self) -> Playbook | None:
"""Get running playbook"""
@@ -124,41 +128,79 @@ class PlaybookStrategy(PowerCalculationStrategyInterface):
@callback
def _execute_playbook_entry(self) -> None:
"""Execute one step of the playbook"""
if self._cancel_timer is not None:
self._cancel_timer()
self._cancel_timer = None
self._cancel_pending_timer()
if not self._active_playbook: # pragma: no cover
_LOGGER.error("Could not execute next playbook entry. No active playbook")
return
queue = self._active_playbook.queue
if len(queue) == 0:
if self._repeat:
_LOGGER.debug("Playbook %s repeating", self._active_playbook.key)
self._start_time = dt.utcnow()
queue.reset()
self._execute_playbook_entry()
return
_LOGGER.debug("Playbook %s completed", self._active_playbook.key)
self._active_playbook = None
playbook = self._active_playbook
if self._complete_or_repeat_playbook(playbook):
return
entry = queue.dequeue()
entry = playbook.queue.dequeue()
self._schedule_playbook_entry_update(playbook, entry)
@callback
def _cancel_pending_timer(self) -> None:
if self._cancel_timer is not None:
self._cancel_timer()
self._cancel_timer = None
@callback
def _complete_or_repeat_playbook(self, playbook: Playbook) -> bool:
queue = playbook.queue
if len(queue) != 0:
return False
if self._repeat:
_LOGGER.debug("Playbook %s repeating", playbook.key)
self._start_time = dt.utcnow()
queue.reset()
self._execute_playbook_entry()
return True
_LOGGER.debug("Playbook %s completed", playbook.key)
self._active_playbook = None
return True
@callback
def _schedule_playbook_entry_update(self, playbook: Playbook, entry: PlaybookEntry) -> None:
"""Schedule the next playbook power update."""
@callback
def _update_power(date_time: datetime) -> None:
active_playbook = self._active_playbook
if active_playbook is None: # pragma: no cover
return
self._power = entry.power
_LOGGER.debug("playbook %s: Update power %.2f", self._active_playbook.key, self._power) # type: ignore
_LOGGER.debug("playbook %s: Update power %.2f", active_playbook.key, self._power)
self._update_callback(self._power)
# Schedule next update
self._execute_playbook_entry()
@callback
def _cancel_pending_updates_on_stop(_: datetime) -> None:
self._cancel_pending_timer()
if self._cancel_stop_listener is not None:
self._cancel_stop_listener()
self._cancel_stop_listener = None
self._active_playbook = None
if self._cancel_stop_listener is not None:
self._cancel_stop_listener()
self._cancel_stop_listener = self._hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP,
_cancel_pending_updates_on_stop,
)
# Schedule update in the future
self._cancel_timer = async_track_point_in_time(
self._hass,
_update_power,
HassJob(
_update_power,
name=f"powercalc playbook {playbook.key}",
cancel_on_shutdown=True,
),
self._start_time + timedelta(seconds=entry.time),
)
@@ -167,20 +209,20 @@ class PlaybookStrategy(PowerCalculationStrategyInterface):
if playbook_id in self._loaded_playbooks:
return self._loaded_playbooks[playbook_id]
playbooks: dict[str, str] = self._config.get(CONF_PLAYBOOKS) # type: ignore
playbooks: dict[str, str] = dict(self._config.get(CONF_PLAYBOOKS) or {})
if playbook_id not in playbooks:
raise StrategyConfigurationError(
f"Playbook with id {playbook_id} not defined in playbooks config",
)
file_path = os.path.join(self._playbook_directory, playbooks[playbook_id])
if not (os.path.exists(file_path) or os.path.exists(f"{file_path}.gz")):
raise StrategyConfigurationError(
f"Playbook file '{file_path}' does not exist",
)
def _load_playbook_entries() -> list[PlaybookEntry]:
"""Load playbook entries from a CSV file, with support for gzipped files"""
if not (os.path.exists(file_path) or os.path.exists(f"{file_path}.gz")):
raise StrategyConfigurationError(
f"Playbook file '{file_path}' does not exist",
)
actual_path = file_path if os.path.exists(file_path) else f"{file_path}.gz"
open_func = gzip.open if actual_path.endswith(".gz") else open
with open_func(actual_path, mode="rt") as csv_file: