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