mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 00:45:22 +00:00
Config: Move lat/long/timezone from prediction to general
This commit is contained in:
parent
1658b491d2
commit
c1dd31528b
@ -2,7 +2,22 @@
|
||||
|
||||
## Settings for common configuration
|
||||
|
||||
General configuration to set directories of cache and output files.
|
||||
General configuration to set directories of cache and output files and system location (latitude
|
||||
and longitude).
|
||||
Validators ensure each parameter is within a specified range. A computed property, `timezone`,
|
||||
determines the time zone based on latitude and longitude.
|
||||
|
||||
Attributes:
|
||||
latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.
|
||||
longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.
|
||||
|
||||
Properties:
|
||||
timezone (Optional[str]): Computed time zone string based on the specified latitude
|
||||
and longitude.
|
||||
|
||||
Validators:
|
||||
validate_latitude (float): Ensures `latitude` is within the range -90 to 90.
|
||||
validate_longitude (float): Ensures `longitude` is within the range -180 to 180.
|
||||
|
||||
:::{table} general
|
||||
:widths: 10 20 10 5 5 30
|
||||
@ -13,6 +28,9 @@ General configuration to set directories of cache and output files.
|
||||
| data_folder_path | `EOS_GENERAL__DATA_FOLDER_PATH` | `Optional[pathlib.Path]` | `rw` | `None` | Path to EOS data directory. |
|
||||
| data_output_subpath | `EOS_GENERAL__DATA_OUTPUT_SUBPATH` | `Optional[pathlib.Path]` | `rw` | `output` | Sub-path for the EOS output data directory. |
|
||||
| data_cache_subpath | `EOS_GENERAL__DATA_CACHE_SUBPATH` | `Optional[pathlib.Path]` | `rw` | `cache` | Sub-path for the EOS cache data directory. |
|
||||
| latitude | `EOS_GENERAL__LATITUDE` | `Optional[float]` | `rw` | `52.52` | Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°) |
|
||||
| longitude | `EOS_GENERAL__LONGITUDE` | `Optional[float]` | `rw` | `13.405` | Longitude in decimal degrees, within -180 to 180 (°) |
|
||||
| timezone | | `Optional[str]` | `ro` | `N/A` | Compute timezone based on latitude and longitude. |
|
||||
| data_output_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Compute data_output_path based on data_folder_path. |
|
||||
| data_cache_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Compute data_cache_path based on data_folder_path. |
|
||||
| config_folder_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Path to EOS configuration directory. |
|
||||
@ -28,7 +46,9 @@ General configuration to set directories of cache and output files.
|
||||
"general": {
|
||||
"data_folder_path": null,
|
||||
"data_output_subpath": "output",
|
||||
"data_cache_subpath": "cache"
|
||||
"data_cache_subpath": "cache",
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -43,6 +63,9 @@ General configuration to set directories of cache and output files.
|
||||
"data_folder_path": null,
|
||||
"data_output_subpath": "output",
|
||||
"data_cache_subpath": "cache",
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
"timezone": "Europe/Berlin",
|
||||
"data_output_path": null,
|
||||
"data_cache_path": null,
|
||||
"config_folder_path": "/home/user/.config/net.akkudoktoreos.net",
|
||||
@ -308,27 +331,18 @@ Attributes:
|
||||
## General Prediction Configuration
|
||||
|
||||
This class provides configuration for prediction settings, allowing users to specify
|
||||
parameters such as the forecast duration (in hours) and location (latitude and longitude).
|
||||
Validators ensure each parameter is within a specified range. A computed property, `timezone`,
|
||||
determines the time zone based on latitude and longitude.
|
||||
parameters such as the forecast duration (in hours).
|
||||
Validators ensure each parameter is within a specified range.
|
||||
|
||||
Attributes:
|
||||
hours (Optional[int]): Number of hours into the future for predictions.
|
||||
Must be non-negative.
|
||||
historic_hours (Optional[int]): Number of hours into the past for historical data.
|
||||
Must be non-negative.
|
||||
latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.
|
||||
longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.
|
||||
|
||||
Properties:
|
||||
timezone (Optional[str]): Computed time zone string based on the specified latitude
|
||||
and longitude.
|
||||
|
||||
Validators:
|
||||
validate_hours (int): Ensures `hours` is a non-negative integer.
|
||||
validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer.
|
||||
validate_latitude (float): Ensures `latitude` is within the range -90 to 90.
|
||||
validate_longitude (float): Ensures `longitude` is within the range -180 to 180.
|
||||
|
||||
:::{table} prediction
|
||||
:widths: 10 20 10 5 5 30
|
||||
@ -338,12 +352,9 @@ Validators:
|
||||
| ---- | -------------------- | ---- | --------- | ------- | ----------- |
|
||||
| hours | `EOS_PREDICTION__HOURS` | `Optional[int]` | `rw` | `48` | Number of hours into the future for predictions |
|
||||
| historic_hours | `EOS_PREDICTION__HISTORIC_HOURS` | `Optional[int]` | `rw` | `48` | Number of hours into the past for historical predictions data |
|
||||
| latitude | `EOS_PREDICTION__LATITUDE` | `Optional[float]` | `rw` | `52.52` | Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°) |
|
||||
| longitude | `EOS_PREDICTION__LONGITUDE` | `Optional[float]` | `rw` | `13.405` | Longitude in decimal degrees, within -180 to 180 (°) |
|
||||
| timezone | | `Optional[str]` | `ro` | `N/A` | Compute timezone based on latitude and longitude. |
|
||||
:::
|
||||
|
||||
### Example Input
|
||||
### Example Input/Output
|
||||
|
||||
```{eval-rst}
|
||||
.. code-block:: json
|
||||
@ -351,25 +362,7 @@ Validators:
|
||||
{
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 48,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example Output
|
||||
|
||||
```{eval-rst}
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 48,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
"timezone": "Europe/Berlin"
|
||||
"historic_hours": 48
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -295,8 +295,8 @@ The `PVForecastAkkudoktor` provider retrieves the PV power forecast data directl
|
||||
|
||||
The following prediction configuration options of the PV system must be set:
|
||||
|
||||
- `prediction.latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)"
|
||||
- `prediction.longitude`: Longitude in decimal degrees, within -180 to 180 (°)
|
||||
- `general.latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)"
|
||||
- `general.longitude`: Longitude in decimal degrees, within -180 to 180 (°)
|
||||
|
||||
For each plane of the PV system the following configuration options must be set:
|
||||
|
||||
@ -310,7 +310,7 @@ Example:
|
||||
|
||||
```Python
|
||||
{
|
||||
"prediction": {
|
||||
"general": {
|
||||
"latitude": 50.1234,
|
||||
"longitude": 9.7654,
|
||||
},
|
||||
|
207
openapi.json
207
openapi.json
@ -107,7 +107,7 @@
|
||||
"type": "object"
|
||||
},
|
||||
"ConfigCommonSettings-Input": {
|
||||
"description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files.",
|
||||
"description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude.\n\nValidators:\n validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
|
||||
"properties": {
|
||||
"data_cache_subpath": {
|
||||
"anyOf": [
|
||||
@ -153,13 +153,43 @@
|
||||
"default": "output",
|
||||
"description": "Sub-path for the EOS output data directory.",
|
||||
"title": "Data Output Subpath"
|
||||
},
|
||||
"latitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 90.0,
|
||||
"minimum": -90.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 52.52,
|
||||
"description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
|
||||
"title": "Latitude"
|
||||
},
|
||||
"longitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 180.0,
|
||||
"minimum": -180.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 13.405,
|
||||
"description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
|
||||
"title": "Longitude"
|
||||
}
|
||||
},
|
||||
"title": "ConfigCommonSettings",
|
||||
"type": "object"
|
||||
},
|
||||
"ConfigCommonSettings-Output": {
|
||||
"description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files.",
|
||||
"description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files and system location (latitude\nand longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude.\n\nValidators:\n validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
|
||||
"properties": {
|
||||
"config_file_path": {
|
||||
"anyOf": [
|
||||
@ -261,9 +291,53 @@
|
||||
"default": "output",
|
||||
"description": "Sub-path for the EOS output data directory.",
|
||||
"title": "Data Output Subpath"
|
||||
},
|
||||
"latitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 90.0,
|
||||
"minimum": -90.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 52.52,
|
||||
"description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
|
||||
"title": "Latitude"
|
||||
},
|
||||
"longitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 180.0,
|
||||
"minimum": -180.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 13.405,
|
||||
"description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
|
||||
"title": "Longitude"
|
||||
},
|
||||
"timezone": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Compute timezone based on latitude and longitude.",
|
||||
"readOnly": true,
|
||||
"title": "Timezone"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"timezone",
|
||||
"data_output_path",
|
||||
"data_cache_path",
|
||||
"config_folder_path",
|
||||
@ -290,7 +364,10 @@
|
||||
"config_file_path": "/home/user/.config/net.akkudoktoreos.net/EOS.config.json",
|
||||
"config_folder_path": "/home/user/.config/net.akkudoktoreos.net",
|
||||
"data_cache_subpath": "cache",
|
||||
"data_output_subpath": "output"
|
||||
"data_output_subpath": "output",
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
"timezone": "Europe/Berlin"
|
||||
}
|
||||
},
|
||||
"load": {
|
||||
@ -324,13 +401,10 @@
|
||||
}
|
||||
},
|
||||
"prediction": {
|
||||
"$ref": "#/components/schemas/PredictionCommonSettings-Output",
|
||||
"$ref": "#/components/schemas/PredictionCommonSettings",
|
||||
"default": {
|
||||
"historic_hours": 48,
|
||||
"hours": 48,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
"timezone": "Europe/Berlin"
|
||||
"hours": 48
|
||||
}
|
||||
},
|
||||
"pvforecast": {
|
||||
@ -1989,8 +2063,8 @@
|
||||
"title": "PVForecastPlaneSetting",
|
||||
"type": "object"
|
||||
},
|
||||
"PredictionCommonSettings-Input": {
|
||||
"description": "General Prediction Configuration.\n\nThis class provides configuration for prediction settings, allowing users to specify\nparameters such as the forecast duration (in hours) and location (latitude and longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n hours (Optional[int]): Number of hours into the future for predictions.\n Must be non-negative.\n historic_hours (Optional[int]): Number of hours into the past for historical data.\n Must be non-negative.\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude.\n\nValidators:\n validate_hours (int): Ensures `hours` is a non-negative integer.\n validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer.\n validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
|
||||
"PredictionCommonSettings": {
|
||||
"description": "General Prediction Configuration.\n\nThis class provides configuration for prediction settings, allowing users to specify\nparameters such as the forecast duration (in hours).\nValidators ensure each parameter is within a specified range.\n\nAttributes:\n hours (Optional[int]): Number of hours into the future for predictions.\n Must be non-negative.\n historic_hours (Optional[int]): Number of hours into the past for historical data.\n Must be non-negative.\n\nValidators:\n validate_hours (int): Ensures `hours` is a non-negative integer.\n validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer.",
|
||||
"properties": {
|
||||
"historic_hours": {
|
||||
"anyOf": [
|
||||
@ -2019,122 +2093,11 @@
|
||||
"default": 48,
|
||||
"description": "Number of hours into the future for predictions",
|
||||
"title": "Hours"
|
||||
},
|
||||
"latitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 90.0,
|
||||
"minimum": -90.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 52.52,
|
||||
"description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
|
||||
"title": "Latitude"
|
||||
},
|
||||
"longitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 180.0,
|
||||
"minimum": -180.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 13.405,
|
||||
"description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
|
||||
"title": "Longitude"
|
||||
}
|
||||
},
|
||||
"title": "PredictionCommonSettings",
|
||||
"type": "object"
|
||||
},
|
||||
"PredictionCommonSettings-Output": {
|
||||
"description": "General Prediction Configuration.\n\nThis class provides configuration for prediction settings, allowing users to specify\nparameters such as the forecast duration (in hours) and location (latitude and longitude).\nValidators ensure each parameter is within a specified range. A computed property, `timezone`,\ndetermines the time zone based on latitude and longitude.\n\nAttributes:\n hours (Optional[int]): Number of hours into the future for predictions.\n Must be non-negative.\n historic_hours (Optional[int]): Number of hours into the past for historical data.\n Must be non-negative.\n latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.\n longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.\n\nProperties:\n timezone (Optional[str]): Computed time zone string based on the specified latitude\n and longitude.\n\nValidators:\n validate_hours (int): Ensures `hours` is a non-negative integer.\n validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer.\n validate_latitude (float): Ensures `latitude` is within the range -90 to 90.\n validate_longitude (float): Ensures `longitude` is within the range -180 to 180.",
|
||||
"properties": {
|
||||
"historic_hours": {
|
||||
"anyOf": [
|
||||
{
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 48,
|
||||
"description": "Number of hours into the past for historical predictions data",
|
||||
"title": "Historic Hours"
|
||||
},
|
||||
"hours": {
|
||||
"anyOf": [
|
||||
{
|
||||
"minimum": 0.0,
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 48,
|
||||
"description": "Number of hours into the future for predictions",
|
||||
"title": "Hours"
|
||||
},
|
||||
"latitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 90.0,
|
||||
"minimum": -90.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 52.52,
|
||||
"description": "Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (\u00b0)",
|
||||
"title": "Latitude"
|
||||
},
|
||||
"longitude": {
|
||||
"anyOf": [
|
||||
{
|
||||
"maximum": 180.0,
|
||||
"minimum": -180.0,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": 13.405,
|
||||
"description": "Longitude in decimal degrees, within -180 to 180 (\u00b0)",
|
||||
"title": "Longitude"
|
||||
},
|
||||
"timezone": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Compute timezone based on latitude and longitude.",
|
||||
"readOnly": true,
|
||||
"title": "Timezone"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"timezone"
|
||||
],
|
||||
"title": "PredictionCommonSettings",
|
||||
"type": "object"
|
||||
},
|
||||
"PydanticDateTimeData": {
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
@ -2404,7 +2367,7 @@
|
||||
"prediction": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/PredictionCommonSettings-Input"
|
||||
"$ref": "#/components/schemas/PredictionCommonSettings"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
|
@ -30,11 +30,13 @@ def prepare_optimization_real_parameters() -> OptimizationParameters:
|
||||
"""
|
||||
# Make a config
|
||||
settings = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
# PV Forecast
|
||||
"pvforecast": {
|
||||
|
@ -16,11 +16,13 @@ prediction_eos = get_prediction()
|
||||
def config_pvforecast() -> dict:
|
||||
"""Configure settings for PV forecast."""
|
||||
settings = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
@ -62,11 +64,13 @@ def config_pvforecast() -> dict:
|
||||
def config_weather() -> dict:
|
||||
"""Configure settings for weather forecast."""
|
||||
settings = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"weather": dict(),
|
||||
}
|
||||
@ -76,11 +80,13 @@ def config_weather() -> dict:
|
||||
def config_elecprice() -> dict:
|
||||
"""Configure settings for electricity price forecast."""
|
||||
settings = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"elecprice": dict(),
|
||||
}
|
||||
@ -90,12 +96,14 @@ def config_elecprice() -> dict:
|
||||
def config_load() -> dict:
|
||||
"""Configure settings for load forecast."""
|
||||
settings = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
}
|
||||
},
|
||||
}
|
||||
return settings
|
||||
|
||||
|
@ -39,6 +39,7 @@ from akkudoktoreos.prediction.prediction import PredictionCommonSettings
|
||||
from akkudoktoreos.prediction.pvforecast import PVForecastCommonSettings
|
||||
from akkudoktoreos.prediction.weather import WeatherCommonSettings
|
||||
from akkudoktoreos.server.server import ServerCommonSettings
|
||||
from akkudoktoreos.utils.datetimeutil import to_timezone
|
||||
from akkudoktoreos.utils.utils import UtilsCommonSettings, classproperty
|
||||
|
||||
logger = get_logger(__name__)
|
||||
@ -65,7 +66,22 @@ def get_absolute_path(
|
||||
class ConfigCommonSettings(SettingsBaseModel):
|
||||
"""Settings for common configuration.
|
||||
|
||||
General configuration to set directories of cache and output files.
|
||||
General configuration to set directories of cache and output files and system location (latitude
|
||||
and longitude).
|
||||
Validators ensure each parameter is within a specified range. A computed property, `timezone`,
|
||||
determines the time zone based on latitude and longitude.
|
||||
|
||||
Attributes:
|
||||
latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.
|
||||
longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.
|
||||
|
||||
Properties:
|
||||
timezone (Optional[str]): Computed time zone string based on the specified latitude
|
||||
and longitude.
|
||||
|
||||
Validators:
|
||||
validate_latitude (float): Ensures `latitude` is within the range -90 to 90.
|
||||
validate_longitude (float): Ensures `longitude` is within the range -180 to 180.
|
||||
"""
|
||||
|
||||
_config_folder_path: ClassVar[Optional[Path]] = None
|
||||
@ -83,7 +99,28 @@ class ConfigCommonSettings(SettingsBaseModel):
|
||||
default="cache", description="Sub-path for the EOS cache data directory."
|
||||
)
|
||||
|
||||
latitude: Optional[float] = Field(
|
||||
default=52.52,
|
||||
ge=-90.0,
|
||||
le=90.0,
|
||||
description="Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)",
|
||||
)
|
||||
longitude: Optional[float] = Field(
|
||||
default=13.405,
|
||||
ge=-180.0,
|
||||
le=180.0,
|
||||
description="Longitude in decimal degrees, within -180 to 180 (°)",
|
||||
)
|
||||
|
||||
# Computed fields
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def timezone(self) -> Optional[str]:
|
||||
"""Compute timezone based on latitude and longitude."""
|
||||
if self.latitude and self.longitude:
|
||||
return to_timezone(location=(self.latitude, self.longitude), as_string=True)
|
||||
return None
|
||||
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def data_output_path(self) -> Optional[Path]:
|
||||
|
@ -108,13 +108,13 @@ class ElecPriceAkkudoktor(ElecPriceProvider):
|
||||
# Try to take data from 5 weeks back for prediction
|
||||
date = to_datetime(self.start_datetime - to_duration("35 days"), as_string="YYYY-MM-DD")
|
||||
last_date = to_datetime(self.end_datetime, as_string="YYYY-MM-DD")
|
||||
url = f"{source}/prices?start={date}&end={last_date}&tz={self.config.prediction.timezone}"
|
||||
url = f"{source}/prices?start={date}&end={last_date}&tz={self.config.general.timezone}"
|
||||
response = requests.get(url)
|
||||
logger.debug(f"Response from {url}: {response}")
|
||||
response.raise_for_status() # Raise an error for bad responses
|
||||
akkudoktor_data = self._validate_data(response.content)
|
||||
# We are working on fresh data (no cache), report update time
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.prediction.timezone)
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.general.timezone)
|
||||
return akkudoktor_data
|
||||
|
||||
def _cap_outliers(self, data: np.ndarray, sigma: int = 2) -> np.ndarray:
|
||||
@ -160,7 +160,7 @@ class ElecPriceAkkudoktor(ElecPriceProvider):
|
||||
series_data = pd.Series(dtype=float) # Initialize an empty series
|
||||
|
||||
for value in akkudoktor_data.values:
|
||||
orig_datetime = to_datetime(value.start, in_timezone=self.config.prediction.timezone)
|
||||
orig_datetime = to_datetime(value.start, in_timezone=self.config.general.timezone)
|
||||
if highest_orig_datetime is None or orig_datetime > highest_orig_datetime:
|
||||
highest_orig_datetime = orig_datetime
|
||||
|
||||
|
@ -129,4 +129,4 @@ class LoadAkkudoktor(LoadProvider):
|
||||
self.update_value(date, values)
|
||||
date += to_duration("1 hour")
|
||||
# We are working on fresh data (no cache), report update time
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.prediction.timezone)
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.general.timezone)
|
||||
|
@ -28,7 +28,7 @@ Attributes:
|
||||
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from pydantic import Field, computed_field
|
||||
from pydantic import Field
|
||||
|
||||
from akkudoktoreos.config.configabc import SettingsBaseModel
|
||||
from akkudoktoreos.prediction.elecpriceakkudoktor import ElecPriceAkkudoktor
|
||||
@ -41,34 +41,24 @@ from akkudoktoreos.prediction.pvforecastimport import PVForecastImport
|
||||
from akkudoktoreos.prediction.weatherbrightsky import WeatherBrightSky
|
||||
from akkudoktoreos.prediction.weatherclearoutside import WeatherClearOutside
|
||||
from akkudoktoreos.prediction.weatherimport import WeatherImport
|
||||
from akkudoktoreos.utils.datetimeutil import to_timezone
|
||||
|
||||
|
||||
class PredictionCommonSettings(SettingsBaseModel):
|
||||
"""General Prediction Configuration.
|
||||
|
||||
This class provides configuration for prediction settings, allowing users to specify
|
||||
parameters such as the forecast duration (in hours) and location (latitude and longitude).
|
||||
Validators ensure each parameter is within a specified range. A computed property, `timezone`,
|
||||
determines the time zone based on latitude and longitude.
|
||||
parameters such as the forecast duration (in hours).
|
||||
Validators ensure each parameter is within a specified range.
|
||||
|
||||
Attributes:
|
||||
hours (Optional[int]): Number of hours into the future for predictions.
|
||||
Must be non-negative.
|
||||
historic_hours (Optional[int]): Number of hours into the past for historical data.
|
||||
Must be non-negative.
|
||||
latitude (Optional[float]): Latitude in degrees, must be between -90 and 90.
|
||||
longitude (Optional[float]): Longitude in degrees, must be between -180 and 180.
|
||||
|
||||
Properties:
|
||||
timezone (Optional[str]): Computed time zone string based on the specified latitude
|
||||
and longitude.
|
||||
|
||||
Validators:
|
||||
validate_hours (int): Ensures `hours` is a non-negative integer.
|
||||
validate_historic_hours (int): Ensures `historic_hours` is a non-negative integer.
|
||||
validate_latitude (float): Ensures `latitude` is within the range -90 to 90.
|
||||
validate_longitude (float): Ensures `longitude` is within the range -180 to 180.
|
||||
"""
|
||||
|
||||
hours: Optional[int] = Field(
|
||||
@ -79,27 +69,6 @@ class PredictionCommonSettings(SettingsBaseModel):
|
||||
ge=0,
|
||||
description="Number of hours into the past for historical predictions data",
|
||||
)
|
||||
latitude: Optional[float] = Field(
|
||||
default=52.52,
|
||||
ge=-90.0,
|
||||
le=90.0,
|
||||
description="Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)",
|
||||
)
|
||||
longitude: Optional[float] = Field(
|
||||
default=13.405,
|
||||
ge=-180.0,
|
||||
le=180.0,
|
||||
description="Longitude in decimal degrees, within -180 to 180 (°)",
|
||||
)
|
||||
|
||||
# Computed fields
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def timezone(self) -> Optional[str]:
|
||||
"""Compute timezone based on latitude and longitude."""
|
||||
if self.latitude and self.longitude:
|
||||
return to_timezone(location=(self.latitude, self.longitude), as_string=True)
|
||||
return None
|
||||
|
||||
|
||||
class Prediction(PredictionContainer):
|
||||
|
@ -14,11 +14,13 @@ Classes:
|
||||
Example:
|
||||
# Set up the configuration with necessary fields for URL generation
|
||||
settings_data = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
@ -213,8 +215,8 @@ class PVForecastAkkudoktor(PVForecastProvider):
|
||||
"""Build akkudoktor.net API request URL."""
|
||||
base_url = "https://api.akkudoktor.net/forecast"
|
||||
query_params = [
|
||||
f"lat={self.config.prediction.latitude}",
|
||||
f"lon={self.config.prediction.longitude}",
|
||||
f"lat={self.config.general.latitude}",
|
||||
f"lon={self.config.general.longitude}",
|
||||
]
|
||||
|
||||
for i in range(len(self.config.pvforecast.planes)):
|
||||
@ -236,7 +238,7 @@ class PVForecastAkkudoktor(PVForecastProvider):
|
||||
"cellCoEff=-0.36",
|
||||
"inverterEfficiency=0.8",
|
||||
"albedo=0.25",
|
||||
f"timezone={self.config.prediction.timezone}",
|
||||
f"timezone={self.config.general.timezone}",
|
||||
"hourly=relativehumidity_2m%2Cwindspeed_10m",
|
||||
]
|
||||
)
|
||||
@ -265,7 +267,7 @@ class PVForecastAkkudoktor(PVForecastProvider):
|
||||
logger.debug(f"Response from {self._url()}: {response}")
|
||||
akkudoktor_data = self._validate_data(response.content)
|
||||
# We are working on fresh data (no cache), report update time
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.prediction.timezone)
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.general.timezone)
|
||||
return akkudoktor_data
|
||||
|
||||
def _update_data(self, force_update: Optional[bool] = False) -> None:
|
||||
@ -285,8 +287,8 @@ class PVForecastAkkudoktor(PVForecastProvider):
|
||||
akkudoktor_data = self._request_forecast(force_update=force_update) # type: ignore
|
||||
|
||||
# Timezone of the PV system
|
||||
if self.config.prediction.timezone != akkudoktor_data.meta.timezone:
|
||||
error_msg = f"Configured timezone '{self.config.prediction.timezone}' does not match Akkudoktor timezone '{akkudoktor_data.meta.timezone}'."
|
||||
if self.config.general.timezone != akkudoktor_data.meta.timezone:
|
||||
error_msg = f"Configured timezone '{self.config.general.timezone}' does not match Akkudoktor timezone '{akkudoktor_data.meta.timezone}'."
|
||||
logger.error(f"Akkudoktor schema change: {error_msg}")
|
||||
raise ValueError(error_msg)
|
||||
|
||||
@ -306,7 +308,7 @@ class PVForecastAkkudoktor(PVForecastProvider):
|
||||
# Iterate over forecast data points
|
||||
for forecast_values in zip(*akkudoktor_data.values):
|
||||
original_datetime = forecast_values[0].datetime
|
||||
dt = to_datetime(original_datetime, in_timezone=self.config.prediction.timezone)
|
||||
dt = to_datetime(original_datetime, in_timezone=self.config.general.timezone)
|
||||
|
||||
# Skip outdated forecast data
|
||||
if compare_datetimes(dt, self.start_datetime.start_of("day")).lt:
|
||||
@ -375,11 +377,13 @@ if __name__ == "__main__":
|
||||
"""
|
||||
# Set up the configuration with necessary fields for URL generation
|
||||
settings_data = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
|
@ -99,7 +99,7 @@ class WeatherBrightSky(WeatherProvider):
|
||||
date = to_datetime(self.start_datetime, as_string="YYYY-MM-DD")
|
||||
last_date = to_datetime(self.end_datetime, as_string="YYYY-MM-DD")
|
||||
response = requests.get(
|
||||
f"{source}/weather?lat={self.config.prediction.latitude}&lon={self.config.prediction.longitude}&date={date}&last_date={last_date}&tz={self.config.prediction.timezone}"
|
||||
f"{source}/weather?lat={self.config.general.latitude}&lon={self.config.general.longitude}&date={date}&last_date={last_date}&tz={self.config.general.timezone}"
|
||||
)
|
||||
response.raise_for_status() # Raise an error for bad responses
|
||||
logger.debug(f"Response from {source}: {response}")
|
||||
@ -109,7 +109,7 @@ class WeatherBrightSky(WeatherProvider):
|
||||
logger.error(error_msg)
|
||||
raise ValueError(error_msg)
|
||||
# We are working on fresh data (no cache), report update time
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.prediction.timezone)
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.general.timezone)
|
||||
return brightsky_data
|
||||
|
||||
def _description_to_series(self, description: str) -> pd.Series:
|
||||
@ -200,7 +200,7 @@ class WeatherBrightSky(WeatherProvider):
|
||||
description = "Total Clouds (% Sky Obscured)"
|
||||
cloud_cover = self._description_to_series(description)
|
||||
ghi, dni, dhi = self.estimate_irradiance_from_cloud_cover(
|
||||
self.config.prediction.latitude, self.config.prediction.longitude, cloud_cover
|
||||
self.config.general.latitude, self.config.general.longitude, cloud_cover
|
||||
)
|
||||
|
||||
description = "Global Horizontal Irradiance (W/m2)"
|
||||
|
@ -91,13 +91,13 @@ class WeatherClearOutside(WeatherProvider):
|
||||
response: Weather forecast request reponse from ClearOutside.
|
||||
"""
|
||||
source = "https://clearoutside.com/forecast"
|
||||
latitude = round(self.config.prediction.latitude, 2)
|
||||
longitude = round(self.config.prediction.longitude, 2)
|
||||
latitude = round(self.config.general.latitude, 2)
|
||||
longitude = round(self.config.general.longitude, 2)
|
||||
response = requests.get(f"{source}/{latitude}/{longitude}?desktop=true")
|
||||
response.raise_for_status() # Raise an error for bad responses
|
||||
logger.debug(f"Response from {source}: {response}")
|
||||
# We are working on fresh data (no cache), report update time
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.prediction.timezone)
|
||||
self.update_datetime = to_datetime(in_timezone=self.config.general.timezone)
|
||||
return response
|
||||
|
||||
def _update_data(self, force_update: Optional[bool] = None) -> None:
|
||||
@ -307,7 +307,7 @@ class WeatherClearOutside(WeatherProvider):
|
||||
data=clearout_data["Total Clouds (% Sky Obscured)"], index=clearout_data["DateTime"]
|
||||
)
|
||||
ghi, dni, dhi = self.estimate_irradiance_from_cloud_cover(
|
||||
self.config.prediction.latitude, self.config.prediction.longitude, cloud_cover
|
||||
self.config.general.latitude, self.config.general.longitude, cloud_cover
|
||||
)
|
||||
|
||||
# Add GHI, DNI, DHI to clearout data
|
||||
|
@ -34,7 +34,7 @@ class VisualizationReport(ConfigMixin):
|
||||
self.pdf_pages = PdfPages(filename, metadata={}) # Initialize PdfPages without metadata
|
||||
self.version = version # overwrite version as test for constant output of pdf for test
|
||||
self.current_time = to_datetime(
|
||||
as_string="YYYY-MM-DD HH:mm:ss", in_timezone=self.config.prediction.timezone
|
||||
as_string="YYYY-MM-DD HH:mm:ss", in_timezone=self.config.general.timezone
|
||||
)
|
||||
|
||||
def add_chart_to_group(self, chart_func: Callable[[], None]) -> None:
|
||||
@ -173,7 +173,7 @@ class VisualizationReport(ConfigMixin):
|
||||
plt.grid(True)
|
||||
|
||||
# Add vertical line for the current date if within the axis range
|
||||
current_time = pendulum.now(self.config.prediction.timezone)
|
||||
current_time = pendulum.now(self.config.general.timezone)
|
||||
if timestamps[0].subtract(hours=2) <= current_time <= timestamps[-1]:
|
||||
plt.axvline(current_time, color="r", linestyle="--", label="Now")
|
||||
plt.text(current_time, plt.ylim()[1], "Now", color="r", ha="center", va="bottom")
|
||||
@ -419,9 +419,7 @@ def prepare_visualize(
|
||||
start_hour: Optional[int] = 0,
|
||||
) -> None:
|
||||
report = VisualizationReport(filename)
|
||||
next_full_hour_date = (
|
||||
pendulum.now(report.config.prediction.timezone).start_of("hour").add(hours=1)
|
||||
)
|
||||
next_full_hour_date = pendulum.now(report.config.general.timezone).start_of("hour").add(hours=1)
|
||||
# Group 1:
|
||||
report.create_line_chart_date(
|
||||
next_full_hour_date, # start_date
|
||||
|
@ -3,8 +3,9 @@ from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from akkudoktoreos.config.config import ConfigEOS
|
||||
from akkudoktoreos.config.config import ConfigCommonSettings, ConfigEOS
|
||||
from akkudoktoreos.core.logging import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
@ -147,3 +148,67 @@ def test_config_copy(config_eos, monkeypatch):
|
||||
assert config_eos._get_config_file_path() == (temp_config_file_path, False)
|
||||
config_eos.update()
|
||||
assert temp_config_file_path.exists()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"latitude, longitude, expected_timezone",
|
||||
[
|
||||
(40.7128, -74.0060, "America/New_York"), # Valid latitude/longitude
|
||||
(None, None, None), # No location
|
||||
(51.5074, -0.1278, "Europe/London"), # Another valid location
|
||||
],
|
||||
)
|
||||
def test_config_common_settings_valid(latitude, longitude, expected_timezone):
|
||||
"""Test valid settings for ConfigCommonSettings."""
|
||||
general_settings = ConfigCommonSettings(
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
)
|
||||
assert general_settings.latitude == latitude
|
||||
assert general_settings.longitude == longitude
|
||||
assert general_settings.timezone == expected_timezone
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"field_name, invalid_value, expected_error",
|
||||
[
|
||||
("latitude", -91.0, "Input should be greater than or equal to -90"),
|
||||
("latitude", 91.0, "Input should be less than or equal to 90"),
|
||||
("longitude", -181.0, "Input should be greater than or equal to -180"),
|
||||
("longitude", 181.0, "Input should be less than or equal to 180"),
|
||||
],
|
||||
)
|
||||
def test_config_common_settings_invalid(field_name, invalid_value, expected_error):
|
||||
"""Test invalid settings for PredictionCommonSettings."""
|
||||
valid_data = {
|
||||
"latitude": 40.7128,
|
||||
"longitude": -74.0060,
|
||||
}
|
||||
assert ConfigCommonSettings(**valid_data) is not None
|
||||
valid_data[field_name] = invalid_value
|
||||
|
||||
with pytest.raises(ValidationError, match=expected_error):
|
||||
ConfigCommonSettings(**valid_data)
|
||||
|
||||
|
||||
def test_config_common_settings_no_location():
|
||||
"""Test that timezone is None when latitude and longitude are not provided."""
|
||||
settings = ConfigCommonSettings(latitude=None, longitude=None)
|
||||
assert settings.timezone is None
|
||||
|
||||
|
||||
def test_config_common_settings_with_location():
|
||||
"""Test that timezone is correctly computed when latitude and longitude are provided."""
|
||||
settings = ConfigCommonSettings(latitude=34.0522, longitude=-118.2437)
|
||||
assert settings.timezone == "America/Los_Angeles"
|
||||
|
||||
|
||||
def test_config_common_settings_timezone_none_when_coordinates_missing():
|
||||
"""Test that timezone is None when latitude or longitude is missing."""
|
||||
config_no_latitude = ConfigCommonSettings(latitude=None, longitude=-74.0060)
|
||||
config_no_longitude = ConfigCommonSettings(latitude=40.7128, longitude=None)
|
||||
config_no_coords = ConfigCommonSettings(latitude=None, longitude=None)
|
||||
|
||||
assert config_no_latitude.timezone is None
|
||||
assert config_no_longitude.timezone is None
|
||||
assert config_no_coords.timezone is None
|
||||
|
@ -39,49 +39,18 @@ def forecast_providers():
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hours, historic_hours, latitude, longitude, expected_timezone",
|
||||
[
|
||||
(48, 24, 40.7128, -74.0060, "America/New_York"), # Valid latitude/longitude
|
||||
(0, 0, None, None, None), # No location
|
||||
(100, 50, 51.5074, -0.1278, "Europe/London"), # Another valid location
|
||||
],
|
||||
)
|
||||
def test_prediction_common_settings_valid(
|
||||
hours, historic_hours, latitude, longitude, expected_timezone
|
||||
):
|
||||
"""Test valid settings for PredictionCommonSettings."""
|
||||
settings = PredictionCommonSettings(
|
||||
hours=hours,
|
||||
historic_hours=historic_hours,
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
)
|
||||
assert settings.hours == hours
|
||||
assert settings.historic_hours == historic_hours
|
||||
assert settings.latitude == latitude
|
||||
assert settings.longitude == longitude
|
||||
assert settings.timezone == expected_timezone
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"field_name, invalid_value, expected_error",
|
||||
[
|
||||
("hours", -1, "Input should be greater than or equal to 0"),
|
||||
("historic_hours", -5, "Input should be greater than or equal to 0"),
|
||||
("latitude", -91.0, "Input should be greater than or equal to -90"),
|
||||
("latitude", 91.0, "Input should be less than or equal to 90"),
|
||||
("longitude", -181.0, "Input should be greater than or equal to -180"),
|
||||
("longitude", 181.0, "Input should be less than or equal to 180"),
|
||||
],
|
||||
)
|
||||
def test_prediction_common_settings_invalid(field_name, invalid_value, expected_error):
|
||||
def test_prediction_common_settings_invalid(field_name, invalid_value, expected_error, config_eos):
|
||||
"""Test invalid settings for PredictionCommonSettings."""
|
||||
valid_data = {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 40.7128,
|
||||
"longitude": -74.0060,
|
||||
}
|
||||
assert PredictionCommonSettings(**valid_data) is not None
|
||||
valid_data[field_name] = invalid_value
|
||||
@ -90,31 +59,6 @@ def test_prediction_common_settings_invalid(field_name, invalid_value, expected_
|
||||
PredictionCommonSettings(**valid_data)
|
||||
|
||||
|
||||
def test_prediction_common_settings_no_location():
|
||||
"""Test that timezone is None when latitude and longitude are not provided."""
|
||||
settings = PredictionCommonSettings(hours=48, historic_hours=24, latitude=None, longitude=None)
|
||||
assert settings.timezone is None
|
||||
|
||||
|
||||
def test_prediction_common_settings_with_location():
|
||||
"""Test that timezone is correctly computed when latitude and longitude are provided."""
|
||||
settings = PredictionCommonSettings(
|
||||
hours=48, historic_hours=24, latitude=34.0522, longitude=-118.2437
|
||||
)
|
||||
assert settings.timezone == "America/Los_Angeles"
|
||||
|
||||
|
||||
def test_prediction_common_settings_timezone_none_when_coordinates_missing():
|
||||
"""Test that timezone is None when latitude or longitude is missing."""
|
||||
config_no_latitude = PredictionCommonSettings(latitude=None, longitude=-74.0060)
|
||||
config_no_longitude = PredictionCommonSettings(latitude=40.7128, longitude=None)
|
||||
config_no_coords = PredictionCommonSettings(latitude=None, longitude=None)
|
||||
|
||||
assert config_no_latitude.timezone is None
|
||||
assert config_no_longitude.timezone is None
|
||||
assert config_no_coords.timezone is None
|
||||
|
||||
|
||||
def test_initialization(prediction, forecast_providers):
|
||||
"""Test that Prediction is initialized with the correct providers in sequence."""
|
||||
assert isinstance(prediction, Prediction)
|
||||
|
@ -88,27 +88,27 @@ class TestPredictionBase:
|
||||
@pytest.fixture
|
||||
def base(self, monkeypatch):
|
||||
# Provide default values for configuration
|
||||
monkeypatch.setenv("EOS_PREDICTION__LATITUDE", "50.0")
|
||||
monkeypatch.setenv("EOS_PREDICTION__LONGITUDE", "10.0")
|
||||
monkeypatch.setenv("EOS_PREDICTION__HOURS", "10")
|
||||
derived = DerivedBase()
|
||||
derived.config.reset_settings()
|
||||
assert derived.config.prediction.hours == 10
|
||||
return derived
|
||||
|
||||
def test_config_value_from_env_variable(self, base, monkeypatch):
|
||||
# From Prediction Config
|
||||
monkeypatch.setenv("EOS_PREDICTION__LATITUDE", "2.5")
|
||||
monkeypatch.setenv("EOS_PREDICTION__HOURS", "2")
|
||||
base.config.reset_settings()
|
||||
assert base.config.prediction.latitude == 2.5
|
||||
assert base.config.prediction.hours == 2
|
||||
|
||||
def test_config_value_from_field_default(self, base, monkeypatch):
|
||||
assert base.config.prediction.model_fields["hours"].default == 48
|
||||
assert base.config.prediction.hours == 48
|
||||
monkeypatch.setenv("EOS_PREDICTION__HOURS", "128")
|
||||
assert base.config.prediction.model_fields["historic_hours"].default == 48
|
||||
assert base.config.prediction.historic_hours == 48
|
||||
monkeypatch.setenv("EOS_PREDICTION__HISTORIC_HOURS", "128")
|
||||
base.config.reset_settings()
|
||||
assert base.config.prediction.hours == 128
|
||||
monkeypatch.delenv("EOS_PREDICTION__HOURS")
|
||||
assert base.config.prediction.historic_hours == 128
|
||||
monkeypatch.delenv("EOS_PREDICTION__HISTORIC_HOURS")
|
||||
base.config.reset_settings()
|
||||
assert base.config.prediction.hours == 48
|
||||
assert base.config.prediction.historic_hours == 48
|
||||
|
||||
def test_get_config_value_key_error(self, base):
|
||||
with pytest.raises(AttributeError):
|
||||
@ -185,10 +185,6 @@ class TestPredictionProvider:
|
||||
# The following values are currently not set in EOS config, we can override
|
||||
monkeypatch.setenv("EOS_PREDICTION__HISTORIC_HOURS", "2")
|
||||
assert os.getenv("EOS_PREDICTION__HISTORIC_HOURS") == "2"
|
||||
monkeypatch.setenv("EOS_PREDICTION__LATITUDE", "37.7749")
|
||||
assert os.getenv("EOS_PREDICTION__LATITUDE") == "37.7749"
|
||||
monkeypatch.setenv("EOS_PREDICTION__LONGITUDE", "-122.4194")
|
||||
assert os.getenv("EOS_PREDICTION__LONGITUDE") == "-122.4194"
|
||||
provider.config.reset_settings()
|
||||
|
||||
ems_eos.set_start_datetime(sample_start_datetime)
|
||||
@ -196,8 +192,6 @@ class TestPredictionProvider:
|
||||
|
||||
assert provider.config.prediction.hours == config_eos.prediction.hours
|
||||
assert provider.config.prediction.historic_hours == 2
|
||||
assert provider.config.prediction.latitude == 37.7749
|
||||
assert provider.config.prediction.longitude == -122.4194
|
||||
assert provider.start_datetime == sample_start_datetime
|
||||
assert provider.end_datetime == sample_start_datetime + to_duration(
|
||||
f"{provider.config.prediction.hours} hours"
|
||||
|
@ -25,11 +25,13 @@ FILE_TESTDATA_PV_FORECAST_RESULT_1 = DIR_TESTDATA.joinpath("pv_forecast_result_1
|
||||
def sample_settings(config_eos):
|
||||
"""Fixture that adds settings data to the global config."""
|
||||
settings = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
@ -155,11 +157,13 @@ sample_value = AkkudoktorForecastValue(
|
||||
windspeed_10m=10.0,
|
||||
)
|
||||
sample_config_data = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
|
@ -67,8 +67,8 @@ def test_invalid_provider(provider, monkeypatch):
|
||||
|
||||
def test_invalid_coordinates(provider, monkeypatch):
|
||||
"""Test invalid coordinates raise ValueError."""
|
||||
monkeypatch.setenv("EOS_PREDICTION__LATITUDE", "1000")
|
||||
monkeypatch.setenv("EOS_PREDICTION__LONGITUDE", "1000")
|
||||
monkeypatch.setenv("EOS_GENERAL__LATITUDE", "1000")
|
||||
monkeypatch.setenv("EOS_GENERAL__LONGITUDE", "1000")
|
||||
with pytest.raises(
|
||||
ValueError, # match="Latitude '1000' and/ or longitude `1000` out of valid range."
|
||||
):
|
||||
|
@ -27,7 +27,7 @@ def provider(config_eos):
|
||||
"weather": {
|
||||
"provider": "ClearOutside",
|
||||
},
|
||||
"prediction": {
|
||||
"general": {
|
||||
"latitude": 50.0,
|
||||
"longitude": 10.0,
|
||||
},
|
||||
@ -87,7 +87,7 @@ def test_invalid_coordinates(provider, config_eos):
|
||||
"weather": {
|
||||
"provider": "ClearOutside",
|
||||
},
|
||||
"prediction": {
|
||||
"general": {
|
||||
"latitude": 1000.0,
|
||||
"longitude": 1000.0,
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user