diff --git a/config.yaml b/config.yaml index 7fdfccf..033658b 100644 --- a/config.yaml +++ b/config.yaml @@ -6,7 +6,7 @@ # the root directory (no add-on folder as usual). name: "Akkudoktor-EOS" -version: "0.2.0.dev2603130753300674" +version: "0.2.0.dev2603131189846912" slug: "eos" description: "Akkudoktor-EOS add-on" url: "https://github.com/Akkudoktor-EOS/EOS" diff --git a/docs/_generated/configems.md b/docs/_generated/configems.md index 3930576..5d6ab1e 100644 --- a/docs/_generated/configems.md +++ b/docs/_generated/configems.md @@ -8,7 +8,7 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | | interval | `EOS_EMS__INTERVAL` | `float` | `rw` | `300.0` | Intervall between EOS energy management runs [seconds]. | -| mode | `EOS_EMS__MODE` | `Optional[akkudoktoreos.core.emsettings.EnergyManagementMode]` | `rw` | `None` | Energy management mode [OPTIMIZATION | PREDICTION]. | +| mode | `EOS_EMS__MODE` | `` | `rw` | `required` | Energy management mode [DISABLED | OPTIMIZATION | PREDICTION]. | | startup_delay | `EOS_EMS__STARTUP_DELAY` | `float` | `rw` | `5` | Startup delay in seconds for EOS energy management runs. | ::: diff --git a/docs/_generated/openapi.md b/docs/_generated/openapi.md index e2d6f2f..94d59e0 100644 --- a/docs/_generated/openapi.md +++ b/docs/_generated/openapi.md @@ -1,6 +1,6 @@ # Akkudoktor-EOS -**Version**: `v0.2.0.dev2603130753300674` +**Version**: `v0.2.0.dev2603131189846912` **Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period. diff --git a/docs/akkudoktoreos/optimauto.md b/docs/akkudoktoreos/optimauto.md index 93b099b..b415640 100644 --- a/docs/akkudoktoreos/optimauto.md +++ b/docs/akkudoktoreos/optimauto.md @@ -107,10 +107,11 @@ If no interval is configured (`None`, `null`) there will be only one energy mana startup. ::: -The energy management can be run in two modes: +The energy management can be run in three modes: - **OPTIMIZATION**: A full optimization is done. This includes update of predictions. - **PREDICTION**: Only the predictions are updated. +- **DISABLED**: No energy management is run. **Example:** diff --git a/docs/akkudoktoreos/optimpost.md b/docs/akkudoktoreos/optimpost.md index 2652d3a..5451550 100644 --- a/docs/akkudoktoreos/optimpost.md +++ b/docs/akkudoktoreos/optimpost.md @@ -17,6 +17,12 @@ The `POST /optimize` endpoint interface does not regard configurations set for t passed to the request. You have to set the parameters even if given in the configuration. ::: +:::{admonition} Warning +:class: warning +To prevent automatic optimization from interfering with `POST /optimize` requests, set `ems.mode` +to `DISABLED` in the configuration. +::: + ## Input Payload ### Sample Request @@ -86,6 +92,12 @@ passed to the request. You have to set the parameters even if given in the confi "initial_soc_percentage": 54, "min_soc_percentage": 0 }, + "dishwasher": { + "device_id": "dishwasher1", + "consumption_wh": 2000, + "duration_h": 3, + "time_windows": null + }, "temperature_forecast": [ 18.3, 17.8, 16.9, 16.2, 15.6, 15.1, 14.6, 14.2, 14.3, 14.8, 15.7, 16.7, 17.4, 18.0, 18.6, 19.2, 19.1, 18.7, 18.5, 17.7, 16.2, 14.6, 13.6, 13.0, 12.6, 12.2, diff --git a/openapi.json b/openapi.json index c83cd87..cbee65e 100644 --- a/openapi.json +++ b/openapi.json @@ -8,7 +8,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "version": "v0.2.0.dev2603130753300674" + "version": "v0.2.0.dev2603131189846912" }, "paths": { "/v1/admin/cache/clear": { @@ -3833,15 +3833,8 @@ ] }, "mode": { - "anyOf": [ - { - "$ref": "#/components/schemas/EnergyManagementMode" - }, - { - "type": "null" - } - ], - "description": "Energy management mode [OPTIMIZATION | PREDICTION].", + "$ref": "#/components/schemas/EnergyManagementMode", + "description": "Energy management mode [DISABLED | OPTIMIZATION | PREDICTION].", "examples": [ "OPTIMIZATION", "PREDICTION" @@ -3855,6 +3848,7 @@ "EnergyManagementMode": { "type": "string", "enum": [ + "DISABLED", "PREDICTION", "OPTIMIZATION" ], @@ -5410,7 +5404,7 @@ "examples": [ [ { - "duration": "2 hours", + "duration": "3 hours", "start_time": "10:00" } ] diff --git a/src/akkudoktoreos/config/config.py b/src/akkudoktoreos/config/config.py index d2f191f..ff311b8 100644 --- a/src/akkudoktoreos/config/config.py +++ b/src/akkudoktoreos/config/config.py @@ -23,7 +23,7 @@ from pydantic import Field, computed_field, field_validator # settings from akkudoktoreos.adapter.adapter import AdapterCommonSettings -from akkudoktoreos.config.configabc import SettingsBaseModel +from akkudoktoreos.config.configabc import SettingsBaseModel, is_home_assistant_addon from akkudoktoreos.config.configmigrate import migrate_config_data, migrate_config_file from akkudoktoreos.core.cachesettings import CacheCommonSettings from akkudoktoreos.core.coreabc import SingletonMixin @@ -69,14 +69,6 @@ def get_absolute_path( return None -def is_home_assistant_addon() -> bool: - """Detect Home Assistant add-on environment. - - Home Assistant sets this environment variable automatically. - """ - return "HASSIO_TOKEN" in os.environ or "SUPERVISOR_TOKEN" in os.environ - - def default_data_folder_path() -> Path: """Provide default data folder path. diff --git a/src/akkudoktoreos/config/configabc.py b/src/akkudoktoreos/config/configabc.py index 9f089be..a4af4e8 100644 --- a/src/akkudoktoreos/config/configabc.py +++ b/src/akkudoktoreos/config/configabc.py @@ -1,6 +1,7 @@ """Abstract and base classes for configuration.""" import calendar +import os from typing import Any, ClassVar, Iterator, Optional, Union import numpy as np @@ -18,6 +19,14 @@ from akkudoktoreos.utils.datetimeutil import ( ) +def is_home_assistant_addon() -> bool: + """Detect Home Assistant add-on environment. + + Home Assistant sets this environment variable automatically. + """ + return "HASSIO_TOKEN" in os.environ or "SUPERVISOR_TOKEN" in os.environ + + class SettingsBaseModel(PydanticBaseModel): """Base model class for all settings configurations.""" diff --git a/src/akkudoktoreos/core/ems.py b/src/akkudoktoreos/core/ems.py index 87df573..ba2dccb 100644 --- a/src/akkudoktoreos/core/ems.py +++ b/src/akkudoktoreos/core/ems.py @@ -154,8 +154,8 @@ class EnergyManagement( @classmethod def _run( cls, - start_datetime: Optional[DateTime] = None, - mode: Optional[EnergyManagementMode] = None, + start_datetime: DateTime, + mode: EnergyManagementMode, genetic_parameters: Optional[GeneticOptimizationParameters] = None, genetic_individuals: Optional[int] = None, genetic_seed: Optional[int] = None, @@ -170,14 +170,11 @@ class EnergyManagement( optimization depending on the selected mode or configuration. Args: - start_datetime (DateTime, optional): The starting timestamp - of the energy management run. Defaults to the current datetime - if not provided. - mode (EnergyManagementMode, optional): The management mode to use. Must be one of: + start_datetime (DateTime): The starting timestamp of the energy management run. + mode (EnergyManagementMode): The management mode to use. Must be one of: - "OPTIMIZATION": Runs the optimization process. - "PREDICTION": Updates the forecast without optimization. - - Defaults to the mode defined in the current configuration. + - "DISABLED": Does not run. genetic_parameters (GeneticOptimizationParameters, optional): The parameter set for the genetic algorithm. If not provided, it will be constructed based on the current configuration and predictions. @@ -196,8 +193,10 @@ class EnergyManagement( None """ # Ensure there is only one optimization/ energy management run at a time - if mode not in (None, "PREDICTION", "OPTIMIZATION"): + if not EnergyManagementMode.is_valid(mode): raise ValueError(f"Unknown energy management mode {mode}.") + if mode == EnergyManagementMode.DISABLED: + return logger.info("Starting energy management run.") @@ -220,9 +219,7 @@ class EnergyManagement( cls._stage = EnergyManagementStage.FORECAST_RETRIEVAL - if mode is None: - mode = cls.config.ems.mode - if mode is None or mode == "PREDICTION": + if mode == EnergyManagementMode.PREDICTION: # Update the predictions cls.prediction.update_data(force_enable=force_enable, force_update=force_update) logger.info("Energy management run done (predictions updated)") @@ -346,6 +343,10 @@ class EnergyManagement( async with self._run_lock: loop = get_running_loop() # Create a partial function with parameters "baked in" + if start_datetime is None: + start_datetime = to_datetime() + if mode is None: + mode = self.config.ems.mode func = partial( EnergyManagement._run, start_datetime=start_datetime, diff --git a/src/akkudoktoreos/core/emsettings.py b/src/akkudoktoreos/core/emsettings.py index 0ac4449..1b080d6 100644 --- a/src/akkudoktoreos/core/emsettings.py +++ b/src/akkudoktoreos/core/emsettings.py @@ -4,19 +4,47 @@ Kept in an extra module to avoid cyclic dependencies on package import. """ from enum import Enum -from typing import Optional +from typing import Optional, Union from pydantic import Field -from akkudoktoreos.config.configabc import SettingsBaseModel +from akkudoktoreos.config.configabc import SettingsBaseModel, is_home_assistant_addon class EnergyManagementMode(str, Enum): """Energy management mode.""" + DISABLED = "DISABLED" PREDICTION = "PREDICTION" OPTIMIZATION = "OPTIMIZATION" + @classmethod + def is_valid(cls, mode: Union[str, "EnergyManagementMode"]) -> bool: + """Check if value is a valid mode.""" + try: + cls(mode) + return True + except (ValueError, TypeError): + return False + + @classmethod + def from_value(cls, value: str) -> Optional["EnergyManagementMode"]: + """Safely convert string to enum, return None if invalid.""" + try: + return cls(value) + except ValueError: + return None + + +def ems_default_mode() -> EnergyManagementMode: + """Provide default EMS mode. + + Returns OPTIMIZATION when running under Home Assistant, else DISABLED. + """ + if is_home_assistant_addon(): + return EnergyManagementMode.OPTIMIZATION + return EnergyManagementMode.DISABLED + class EnergyManagementCommonSettings(SettingsBaseModel): """Energy Management Configuration.""" @@ -38,10 +66,10 @@ class EnergyManagementCommonSettings(SettingsBaseModel): }, ) - mode: Optional[EnergyManagementMode] = Field( - default=None, + mode: EnergyManagementMode = Field( + default_factory=ems_default_mode, json_schema_extra={ - "description": "Energy management mode [OPTIMIZATION | PREDICTION].", + "description": "Energy management mode [DISABLED | OPTIMIZATION | PREDICTION].", "examples": ["OPTIMIZATION", "PREDICTION"], }, ) diff --git a/src/akkudoktoreos/optimization/genetic/geneticdevices.py b/src/akkudoktoreos/optimization/genetic/geneticdevices.py index 3acd701..d998c9a 100644 --- a/src/akkudoktoreos/optimization/genetic/geneticdevices.py +++ b/src/akkudoktoreos/optimization/genetic/geneticdevices.py @@ -139,7 +139,7 @@ class HomeApplianceParameters(DeviceParameters): "description": "List of allowed time windows. Defaults to optimization general time window.", "examples": [ [ - {"start_time": "10:00", "duration": "2 hours"}, + {"start_time": "10:00", "duration": "3 hours"}, ], ], }, diff --git a/src/akkudoktoreos/server/dash/configuration.py b/src/akkudoktoreos/server/dash/configuration.py index 1729ace..255bf9b 100644 --- a/src/akkudoktoreos/server/dash/configuration.py +++ b/src/akkudoktoreos/server/dash/configuration.py @@ -727,9 +727,9 @@ def Configuration( # Home Assistant adapter optimization solution entities update_form_factory = make_config_update_list_form(eos_solution_entity_ids) elif config["name"].startswith("ems.mode"): - # Energy managemnt mode + # Energy management mode update_form_factory = make_config_update_value_form( - ["OPTIMIZATION", "PREDICTION", "None"] + ["OPTIMIZATION", "PREDICTION", "DISABLED"] ) elif config["name"].endswith("elecpricefixed.time_windows.windows"): update_form_factory = make_config_update_time_windows_windows_form(