mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 00:45:22 +00:00
Update manual documentation for nested config.
* Add config_file_path, config_folder_path back to general (ConfigCommonSettings). Overwrite in docs generation.
This commit is contained in:
parent
af5e4a753a
commit
1658b491d2
@ -15,6 +15,8 @@ General configuration to set directories of cache and output files.
|
||||
| data_cache_subpath | `EOS_GENERAL__DATA_CACHE_SUBPATH` | `Optional[pathlib.Path]` | `rw` | `cache` | Sub-path for the EOS cache data directory. |
|
||||
| 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. |
|
||||
| config_file_path | | `Optional[pathlib.Path]` | `ro` | `N/A` | Path to EOS configuration file. |
|
||||
:::
|
||||
|
||||
### Example Input
|
||||
@ -42,7 +44,9 @@ General configuration to set directories of cache and output files.
|
||||
"data_output_subpath": "output",
|
||||
"data_cache_subpath": "cache",
|
||||
"data_output_path": null,
|
||||
"data_cache_path": null
|
||||
"data_cache_path": null,
|
||||
"config_folder_path": "/home/user/.config/net.akkudoktoreos.net",
|
||||
"config_file_path": "/home/user/.config/net.akkudoktoreos.net/EOS.config.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -192,11 +192,11 @@ Returns:
|
||||
Fastapi Config Put
|
||||
|
||||
```
|
||||
Write the provided settings into the current settings.
|
||||
Update the current config with the provided settings.
|
||||
|
||||
The existing settings are completely overwritten. Note that for any setting
|
||||
value that is None, the configuration will fall back to values from other sources such as
|
||||
environment variables, the EOS configuration file, or default values.
|
||||
Note that for any setting value that is None or unset, the configuration will fall back to
|
||||
values from other sources such as environment variables, the EOS configuration file, or default
|
||||
values.
|
||||
|
||||
Args:
|
||||
settings (SettingsEOS): The settings to write into the current settings.
|
||||
|
@ -7,10 +7,9 @@ management.
|
||||
|
||||
## Storing Configuration
|
||||
|
||||
EOS stores configuration data in a **key-value store**, where a `configuration key` refers to the
|
||||
unique identifier used to store and retrieve specific configuration data. Note that the key-value
|
||||
store is memory-based, meaning all stored data will be lost upon restarting the EOS REST server if
|
||||
not saved to the `EOS configuration file`.
|
||||
EOS stores configuration data in a `nested structure`. Note that configuration changes inside EOS
|
||||
are updated in memory, meaning all changes will be lost upon restarting the EOS REST server if not
|
||||
saved to the `EOS configuration file`.
|
||||
|
||||
Some `configuration keys` are read-only and cannot be altered. These keys are either set up by other
|
||||
means, such as environment variables, or determined from other information.
|
||||
@ -25,32 +24,33 @@ Use endpoint `PUT /v1/config/file` to save the current configuration to the
|
||||
|
||||
### Load Configuration File
|
||||
|
||||
Use endpoint `POST /v1/config/update` to update the configuration from the `EOS configuration file`.
|
||||
Use endpoint `POST /v1/config/reset` to reset the configuration to the values in the
|
||||
`EOS configuration file`.
|
||||
|
||||
## Configuration Sources and Priorities
|
||||
|
||||
The configuration sources and their priorities are as follows:
|
||||
|
||||
1. **Settings**: Provided during runtime by the REST interface
|
||||
1. **Runtime Config Updates**: Provided during runtime by the REST interface
|
||||
2. **Environment Variables**: Defined at startup of the REST server and during runtime
|
||||
3. **EOS Configuration File**: Read at startup of the REST server and on request
|
||||
4. **Default Values**
|
||||
|
||||
### Settings
|
||||
### Runtime Config Updates
|
||||
|
||||
Settings are sets of configuration data that take precedence over all other configuration data from
|
||||
different sources. Note that settings are not persistent. To make the current configuration with the
|
||||
current settings persistent, save the configuration to the `EOS configuration file`.
|
||||
The EOS configuration can be updated at runtime. Note that those updates are not persistet
|
||||
automatically. However it is possible to save the configuration to the `EOS configuration file`.
|
||||
|
||||
Use the following endpoints to change the current configuration settings:
|
||||
Use the following endpoints to change the current runtime configuration:
|
||||
|
||||
- `PUT /v1/config`: Replaces the entire configuration settings.
|
||||
- `PUT /v1/config/value`: Sets a specific configuration option.
|
||||
- `PUT /v1/config`: Update the entire or parts of the configuration.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
All `configuration keys` can be set by environment variables with the same name. EOS recognizes the
|
||||
following special environment variables:
|
||||
All `configuration keys` can be set by environment variables prefixed with `EOS_` and separated by
|
||||
`__` for nested structures. Environment variables are case insensitive.
|
||||
|
||||
EOS recognizes the following special environment variables (case sensitive):
|
||||
|
||||
- `EOS_CONFIG_DIR`: The directory to search for an EOS configuration file.
|
||||
- `EOS_DIR`: The directory used by EOS for data, which will also be searched for an EOS
|
||||
@ -65,7 +65,7 @@ If you do not have a configuration file, it will be automatically created on the
|
||||
the REST server in a system-dependent location.
|
||||
|
||||
To determine the location of the configuration file used by EOS, ask the REST server. The endpoint
|
||||
`GET /v1/config` provides the `config_file_path` configuration key.
|
||||
`GET /v1/config` provides the `general.config_file_path` configuration key.
|
||||
|
||||
EOS searches for the configuration file in the following order:
|
||||
|
||||
@ -74,9 +74,15 @@ EOS searches for the configuration file in the following order:
|
||||
3. A platform-specific default directory for EOS
|
||||
4. The current working directory
|
||||
|
||||
The first available configuration file found in these directories is loaded. If no configuration
|
||||
file is found, a default configuration file is created in the platform-specific default directory,
|
||||
and default settings are loaded into it.
|
||||
The first configuration file available in these directories is loaded. If no configuration file is
|
||||
found, a default configuration file is created, and the default settings are written to it. The
|
||||
location of the created configuration file follows the same order in which EOS searches for
|
||||
configuration files, and it depends on whether the relevant environment variables are set.
|
||||
|
||||
Use the following endpoints to interact with the configuration file:
|
||||
|
||||
- `PUT /v1/config/file`: Save the current configuration to the configuration file.
|
||||
- `PUT /v1/config/reset`: Reload the configuration file, all unsaved runtime configuration is reset.
|
||||
|
||||
### Default Values
|
||||
|
||||
|
@ -19,10 +19,14 @@ data is lost on re-start of the EOS REST server.
|
||||
## Prediction Providers
|
||||
|
||||
Most predictions can be sourced from various providers. The specific provider to use is configured
|
||||
in the EOS configuration. For example:
|
||||
in the EOS configuration and can be set by prediction type. For example:
|
||||
|
||||
```python
|
||||
provider = "ClearOutside"
|
||||
{
|
||||
"weather": {
|
||||
"provider": "ClearOutside"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Some providers offer multiple prediction keys. For instance, a weather provider might provide data
|
||||
@ -107,26 +111,28 @@ Prediction keys:
|
||||
|
||||
Configuration options:
|
||||
|
||||
- `provider`: Electricity price provider id of provider to be used.
|
||||
- `elecprice`: Electricity price configuration.
|
||||
|
||||
- `Akkudoktor`: Retrieves from Akkudoktor.net.
|
||||
- `Import`: Imports from a file or JSON string.
|
||||
- `provider`: Electricity price provider id of provider to be used.
|
||||
|
||||
- `charges_kwh`: Electricity price charges (€/kWh).
|
||||
- `import_file_path`: Path to the file to import electricity price forecast data from.
|
||||
- `import_json`: JSON string, dictionary of electricity price forecast value lists.
|
||||
- `ElecPriceAkkudoktor`: Retrieves from Akkudoktor.net.
|
||||
- `ElecPriceImport`: Imports from a file or JSON string.
|
||||
|
||||
### Akkudoktor Provider
|
||||
- `charges_kwh`: Electricity price charges (€/kWh).
|
||||
- `provider_settings.import_file_path`: Path to the file to import electricity price forecast data from.
|
||||
- `provider_settings.import_json`: JSON string, dictionary of electricity price forecast value lists.
|
||||
|
||||
The `Akkudoktor` provider retrieves electricity prices directly from **Akkudoktor.net**,
|
||||
### ElecPriceAkkudoktor Provider
|
||||
|
||||
The `ElecPriceAkkudoktor` provider retrieves electricity prices directly from **Akkudoktor.net**,
|
||||
which supplies price data for the next 24 hours. For periods beyond 24 hours, the provider generates
|
||||
prices by extrapolating historical price data combined with the most recent actual prices obtained
|
||||
from Akkudoktor.net. Electricity price charges given in the `charges_kwh` configuration
|
||||
option are added.
|
||||
|
||||
### Import Provider
|
||||
### ElecPriceImport Provider
|
||||
|
||||
The `Import` provider is designed to import electricity prices from a file or a JSON
|
||||
The `ElecPriceImport` provider is designed to import electricity prices from a file or a JSON
|
||||
string. An external entity should update the file or JSON string whenever new prediction data
|
||||
becomes available.
|
||||
|
||||
@ -148,14 +154,16 @@ Prediction keys:
|
||||
|
||||
Configuration options:
|
||||
|
||||
- `provider`: Load provider id of provider to be used.
|
||||
- `load`: Load configuration.
|
||||
|
||||
- `LoadAkkudoktor`: Retrieves from local database.
|
||||
- `LoadImport`: Imports from a file or JSON string.
|
||||
- `provider`: Load provider id of provider to be used.
|
||||
|
||||
- `loadakkudoktor_year_energy`: Yearly energy consumption (kWh).
|
||||
- `loadimport_file_path`: Path to the file to import load forecast data from.
|
||||
- `loadimport_json`: JSON string, dictionary of load forecast value lists.
|
||||
- `LoadAkkudoktor`: Retrieves from local database.
|
||||
- `LoadImport`: Imports from a file or JSON string.
|
||||
|
||||
- `provider_settings.loadakkudoktor_year_energy`: Yearly energy consumption (kWh).
|
||||
- `provider_settings.loadimport_file_path`: Path to the file to import load forecast data from.
|
||||
- `provider_settings.loadimport_json`: JSON string, dictionary of load forecast value lists.
|
||||
|
||||
### LoadAkkudoktor Provider
|
||||
|
||||
@ -183,44 +191,49 @@ or `loadimport_json` configuration option.
|
||||
|
||||
Prediction keys:
|
||||
|
||||
- `ac_power`: Total DC power (W).
|
||||
- `dc_power`: Total AC power (W).
|
||||
- `pvforecast_ac_power`: Total DC power (W).
|
||||
- `pvforecast_dc_power`: Total AC power (W).
|
||||
|
||||
Configuration options:
|
||||
|
||||
- `provider`: PVForecast provider id of provider to be used.
|
||||
- `prediction`: General prediction configuration.
|
||||
|
||||
- `PVForecastAkkudoktor`: Retrieves from Akkudoktor.net.
|
||||
- `PVForecastImport`: Imports from a file or JSON string.
|
||||
- `latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)"
|
||||
- `longitude`: Longitude in decimal degrees, within -180 to 180 (°)
|
||||
|
||||
- `latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)"
|
||||
- `longitude`: Longitude in decimal degrees, within -180 to 180 (°)
|
||||
- `pvforecast<0..5>_surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking.
|
||||
- `pvforecast<0..5>_surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).
|
||||
- `pvforecast<0..5>_userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.
|
||||
- `pvforecast<0..5>_peakpower`: Nominal power of PV system in kW.
|
||||
- `pvforecast<0..5>_pvtechchoice`: PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.
|
||||
- `pvforecast<0..5>_mountingplace`: Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.
|
||||
- `pvforecast<0..5>_loss`: Sum of PV system losses in percent
|
||||
- `pvforecast<0..5>_trackingtype`: Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.
|
||||
- `pvforecast<0..5>_optimal_surface_tilt`: Calculate the optimum tilt angle. Ignored for two-axis tracking.
|
||||
- `pvforecast<0..5>_optimalangles`: Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.
|
||||
- `pvforecast<0..5>_albedo`: Proportion of the light hitting the ground that it reflects back.
|
||||
- `pvforecast<0..5>_module_model`: Model of the PV modules of this plane.
|
||||
- `pvforecast<0..5>_inverter_model`: Model of the inverter of this plane.
|
||||
- `pvforecast<0..5>_inverter_paco`: AC power rating of the inverter. [W]
|
||||
- `pvforecast<0..5>_modules_per_string`: Number of the PV modules of the strings of this plane.
|
||||
- `pvforecast<0..5>_strings_per_inverter`: Number of the strings of the inverter of this plane.
|
||||
- `import_file_path`: Path to the file to import PV forecast data from.
|
||||
- `import_json`: JSON string, dictionary of PV forecast value lists.
|
||||
- `pvforecast`: PV forecast configuration.
|
||||
|
||||
- `provider`: PVForecast provider id of provider to be used.
|
||||
|
||||
- `PVForecastAkkudoktor`: Retrieves from Akkudoktor.net.
|
||||
- `PVForecastImport`: Imports from a file or JSON string.
|
||||
|
||||
- `planes[].surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking.
|
||||
- `planes[].surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).
|
||||
- `planes[].userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.
|
||||
- `planes[].peakpower`: Nominal power of PV system in kW.
|
||||
- `planes[].pvtechchoice`: PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.
|
||||
- `planes[].mountingplace`: Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated.
|
||||
- `planes[].loss`: Sum of PV system losses in percent
|
||||
- `planes[].trackingtype`: Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south.
|
||||
- `planes[].optimal_surface_tilt`: Calculate the optimum tilt angle. Ignored for two-axis tracking.
|
||||
- `planes[].optimalangles`: Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.
|
||||
- `planes[].albedo`: Proportion of the light hitting the ground that it reflects back.
|
||||
- `planes[].module_model`: Model of the PV modules of this plane.
|
||||
- `planes[].inverter_model`: Model of the inverter of this plane.
|
||||
- `planes[].inverter_paco`: AC power rating of the inverter. [W]
|
||||
- `planes[].modules_per_string`: Number of the PV modules of the strings of this plane.
|
||||
- `planes[].strings_per_inverter`: Number of the strings of the inverter of this plane.
|
||||
- `provider_settings.import_file_path`: Path to the file to import PV forecast data from.
|
||||
- `provider_settings.import_json`: JSON string, dictionary of PV forecast value lists.
|
||||
|
||||
------
|
||||
|
||||
Some of the configuration options directly follow the [PVGIS](https://joint-research-centre.ec.europa.eu/photovoltaic-geographical-information-system-pvgis/getting-started-pvgis/pvgis-user-manual_en) nomenclature.
|
||||
Some of the planes configuration options directly follow the [PVGIS](https://joint-research-centre.ec.europa.eu/photovoltaic-geographical-information-system-pvgis/getting-started-pvgis/pvgis-user-manual_en) nomenclature.
|
||||
|
||||
Detailed definitions taken from **PVGIS**:
|
||||
|
||||
- `pvforecast<0..5>_pvtechchoice`
|
||||
- `pvtechchoice`
|
||||
|
||||
The performance of PV modules depends on the temperature and on the solar irradiance, but the exact dependence varies between different types of PV modules. At the moment we can estimate the losses due to temperature and irradiance effects for the following types of modules: crystalline silicon cells; thin film modules made from CIS or CIGS and thin film modules made from Cadmium Telluride (CdTe).
|
||||
|
||||
@ -228,19 +241,19 @@ For other technologies (especially various amorphous technologies), this correct
|
||||
|
||||
PV power output also depends on the spectrum of the solar radiation. PVGIS can calculate how the variations of the spectrum of sunlight affects the overall energy production from a PV system. At the moment this calculation can be done for crystalline silicon and CdTe modules. Note that this calculation is not yet available when using the NSRDB solar radiation database.
|
||||
|
||||
- `pvforecast<0..5>_peakpower`
|
||||
- `peakpower`
|
||||
|
||||
This is the power that the manufacturer declares that the PV array can produce under standard test conditions (STC), which are a constant 1000W of solar irradiation per square meter in the plane of the array, at an array temperature of 25°C. The peak power should be entered in kilowatt-peak (kWp). If you do not know the declared peak power of your modules but instead know the area of the modules and the declared conversion efficiency (in percent), you can calculate the peak power as power = area * efficiency / 100.
|
||||
|
||||
Bifacial modules: PVGIS doesn't make specific calculations for bifacial modules at present. Users who wish to explore the possible benefits of this technology can input the power value for Bifacial Nameplate Irradiance. This can also be can also be estimated from the front side peak power P_STC value and the bifaciality factor, φ (if reported in the module data sheet) as: P_BNPI = P_STC * (1 + φ * 0.135). NB this bifacial approach is not appropriate for BAPV or BIPV installations or for modules mounting on a N-S axis i.e. facing E-W.
|
||||
|
||||
- `pvforecast<0..5>_loss`
|
||||
- `loss`
|
||||
|
||||
The estimated system losses are all the losses in the system, which cause the power actually delivered to the electricity grid to be lower than the power produced by the PV modules. There are several causes for this loss, such as losses in cables, power inverters, dirt (sometimes snow) on the modules and so on. Over the years the modules also tend to lose a bit of their power, so the average yearly output over the lifetime of the system will be a few percent lower than the output in the first years.
|
||||
|
||||
We have given a default value of 14% for the overall losses. If you have a good idea that your value will be different (maybe due to a really high-efficiency inverter) you may reduce this value a little.
|
||||
|
||||
- `pvforecast<0..5>_mountingplace`
|
||||
- `mountingplace`
|
||||
|
||||
For fixed (non-tracking) systems, the way the modules are mounted will have an influence on the temperature of the module, which in turn affects the efficiency. Experiments have shown that if the movement of air behind the modules is restricted, the modules can get considerably hotter (up to 15°C at 1000W/m2 of sunlight).
|
||||
|
||||
@ -248,7 +261,7 @@ In PVGIS there are two possibilities: free-standing, meaning that the modules ar
|
||||
|
||||
Some types of mounting are in between these two extremes, for instance if the modules are mounted on a roof with curved roof tiles, allowing air to move behind the modules. In such cases, the performance will be somewhere between the results of the two calculations that are possible here.
|
||||
|
||||
- `pvforecast<0..5>_userhorizon`
|
||||
- `userhorizon`
|
||||
|
||||
Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. In the user horizon
|
||||
data each number represents the horizon height in degrees in a certain compass direction around the
|
||||
@ -260,15 +273,15 @@ degrees west of north.
|
||||
|
||||
------
|
||||
|
||||
Most of the configuration options are in line with the [PVLib](https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/iotools/pvgis.html) definition for PVGIS data.
|
||||
Most of the planes configuration options are in line with the [PVLib](https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/iotools/pvgis.html) definition for PVGIS data.
|
||||
|
||||
Detailed definitions from **PVLib** for PVGIS data.
|
||||
|
||||
- `pvforecast<0..5>_surface_tilt`:
|
||||
- `surface_tilt`:
|
||||
|
||||
Tilt angle from horizontal plane.
|
||||
|
||||
- `pvforecast<0..5>_surface_azimuth`
|
||||
- `surface_azimuth`
|
||||
|
||||
Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180,
|
||||
west=270). This is offset 180 degrees from the convention used by PVGIS.
|
||||
@ -280,47 +293,60 @@ west=270). This is offset 180 degrees from the convention used by PVGIS.
|
||||
The `PVForecastAkkudoktor` provider retrieves the PV power forecast data directly from
|
||||
**Akkudoktor.net**.
|
||||
|
||||
The following general configuration options of the PV system must be set:
|
||||
The following prediction configuration options of the PV system must be set:
|
||||
|
||||
- `latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)"
|
||||
- `longitude`: Longitude in decimal degrees, within -180 to 180 (°)
|
||||
- `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 (°)
|
||||
|
||||
For each plane `<0..5>` 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:
|
||||
|
||||
- `pvforecast<0..5>_surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking.
|
||||
- `pvforecast<0..5>_surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).
|
||||
- `pvforecast<0..5>_userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.
|
||||
- `pvforecast<0..5>_inverter_paco`: AC power rating of the inverter. [W]
|
||||
- `pvforecast<0..5>_peakpower`: Nominal power of PV system in kW.
|
||||
- `pvforecast.planes[].surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking.
|
||||
- `pvforecast.planes[].surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270).
|
||||
- `pvforecast.planes[].userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.
|
||||
- `pvforecast.planes[].inverter_paco`: AC power rating of the inverter. [W]
|
||||
- `pvforecast.planes[].peakpower`: Nominal power of PV system in kW.
|
||||
|
||||
Example:
|
||||
|
||||
```Python
|
||||
{
|
||||
"latitude": 50.1234,
|
||||
"longitude": 9.7654,
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
"pvforecast0_peakpower": 5.0,
|
||||
"pvforecast0_surface_azimuth": -10,
|
||||
"pvforecast0_surface_tilt": 7,
|
||||
"pvforecast0_userhorizon": [20, 27, 22, 20],
|
||||
"pvforecast0_inverter_paco": 10000,
|
||||
"pvforecast1_peakpower": 4.8,
|
||||
"pvforecast1_surface_azimuth": -90,
|
||||
"pvforecast1_surface_tilt": 7,
|
||||
"pvforecast1_userhorizon": [30, 30, 30, 50],
|
||||
"pvforecast1_inverter_paco": 10000,
|
||||
"pvforecast2_peakpower": 1.4,
|
||||
"pvforecast2_surface_azimuth": -40,
|
||||
"pvforecast2_surface_tilt": 60,
|
||||
"pvforecast2_userhorizon": [60, 30, 0, 30],
|
||||
"pvforecast2_inverter_paco": 2000,
|
||||
"pvforecast3_peakpower": 1.6,
|
||||
"pvforecast3_surface_azimuth": 5,
|
||||
"pvforecast3_surface_tilt": 45,
|
||||
"pvforecast3_userhorizon": [45, 25, 30, 60],
|
||||
"pvforecast3_inverter_paco": 1400,
|
||||
"pvforecast4_peakpower": None,
|
||||
"prediction": {
|
||||
"latitude": 50.1234,
|
||||
"longitude": 9.7654,
|
||||
},
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
"planes": [
|
||||
{
|
||||
"peakpower": 5.0,
|
||||
"surface_azimuth": -10,
|
||||
"surface_tilt": 7,
|
||||
"userhorizon": [20, 27, 22, 20],
|
||||
"inverter_paco": 10000,
|
||||
},
|
||||
{
|
||||
"peakpower": 4.8,
|
||||
"surface_azimuth": -90,
|
||||
"surface_tilt": 7,
|
||||
"userhorizon": [30, 30, 30, 50],
|
||||
"inverter_paco": 10000,
|
||||
},
|
||||
{
|
||||
"peakpower": 1.4,
|
||||
"surface_azimuth": -40,
|
||||
"surface_tilt": 60,
|
||||
"userhorizon": [60, 30, 0, 30],
|
||||
"inverter_paco": 2000,
|
||||
},
|
||||
{
|
||||
"peakpower": 1.6,
|
||||
"surface_azimuth": 5,
|
||||
"surface_tilt": 45,
|
||||
"userhorizon": [45, 25, 30, 60],
|
||||
"inverter_paco": 1400,
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -332,8 +358,8 @@ becomes available.
|
||||
|
||||
The prediction keys for the PV forecast data are:
|
||||
|
||||
- `ac_power`: Total DC power (W).
|
||||
- `dc_power`: Total AC power (W).
|
||||
- `pvforecast_ac_power`: Total DC power (W).
|
||||
- `pvforecast_dc_power`: Total AC power (W).
|
||||
|
||||
The PV forecast data must be provided in one of the formats described in
|
||||
<project:#prediction-import-providers>. The data source must be given in the
|
||||
@ -368,14 +394,16 @@ Prediction keys:
|
||||
|
||||
Configuration options:
|
||||
|
||||
- `provider`: Load provider id of provider to be used.
|
||||
- `weather`: General weather configuration.
|
||||
|
||||
- `BrightSky`: Retrieves from https://api.brightsky.dev.
|
||||
- `ClearOutside`: Retrieves from https://clearoutside.com/forecast.
|
||||
- `LoadImport`: Imports from a file or JSON string.
|
||||
- `provider`: Load provider id of provider to be used.
|
||||
|
||||
- `import_file_path`: Path to the file to import weatherforecast data from.
|
||||
- `import_json`: JSON string, dictionary of weather forecast value lists.
|
||||
- `BrightSky`: Retrieves from https://api.brightsky.dev.
|
||||
- `ClearOutside`: Retrieves from https://clearoutside.com/forecast.
|
||||
- `LoadImport`: Imports from a file or JSON string.
|
||||
|
||||
- `provider_settings.import_file_path`: Path to the file to import weatherforecast data from.
|
||||
- `provider_settings.import_json`: JSON string, dictionary of weather forecast value lists.
|
||||
|
||||
### BrightSky Provider
|
||||
|
||||
|
36
openapi.json
36
openapi.json
@ -161,6 +161,34 @@
|
||||
"ConfigCommonSettings-Output": {
|
||||
"description": "Settings for common configuration.\n\nGeneral configuration to set directories of cache and output files.",
|
||||
"properties": {
|
||||
"config_file_path": {
|
||||
"anyOf": [
|
||||
{
|
||||
"format": "path",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Path to EOS configuration file.",
|
||||
"readOnly": true,
|
||||
"title": "Config File Path"
|
||||
},
|
||||
"config_folder_path": {
|
||||
"anyOf": [
|
||||
{
|
||||
"format": "path",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Path to EOS configuration directory.",
|
||||
"readOnly": true,
|
||||
"title": "Config Folder Path"
|
||||
},
|
||||
"data_cache_path": {
|
||||
"anyOf": [
|
||||
{
|
||||
@ -237,7 +265,9 @@
|
||||
},
|
||||
"required": [
|
||||
"data_output_path",
|
||||
"data_cache_path"
|
||||
"data_cache_path",
|
||||
"config_folder_path",
|
||||
"config_file_path"
|
||||
],
|
||||
"title": "ConfigCommonSettings",
|
||||
"type": "object"
|
||||
@ -257,6 +287,8 @@
|
||||
"general": {
|
||||
"$ref": "#/components/schemas/ConfigCommonSettings-Output",
|
||||
"default": {
|
||||
"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"
|
||||
}
|
||||
@ -3087,7 +3119,7 @@
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"description": "Write the provided settings into the current settings.\n\nThe existing settings are completely overwritten. Note that for any setting\nvalue that is None, the configuration will fall back to values from other sources such as\nenvironment variables, the EOS configuration file, or default values.\n\nArgs:\n settings (SettingsEOS): The settings to write into the current settings.\n\nReturns:\n configuration (ConfigEOS): The current configuration after the write.",
|
||||
"description": "Update the current config with the provided settings.\n\nNote that for any setting value that is None or unset, the configuration will fall back to\nvalues from other sources such as environment variables, the EOS configuration file, or default\nvalues.\n\nArgs:\n settings (SettingsEOS): The settings to write into the current settings.\n\nReturns:\n configuration (ConfigEOS): The current configuration after the write.",
|
||||
"operationId": "fastapi_config_put_v1_config_put",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
|
@ -5,20 +5,19 @@ import argparse
|
||||
import json
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from typing import Any, Union
|
||||
|
||||
from pydantic.fields import ComputedFieldInfo, FieldInfo
|
||||
from pydantic_core import PydanticUndefined
|
||||
|
||||
from akkudoktoreos.config.config import get_config
|
||||
from akkudoktoreos.config.config import ConfigCommonSettings, ConfigEOS, get_config
|
||||
from akkudoktoreos.core.logging import get_logger
|
||||
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
||||
from akkudoktoreos.utils.docs import get_model_structure_from_examples
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
config_eos = get_config()
|
||||
|
||||
|
||||
documented_types: set[PydanticBaseModel] = set()
|
||||
undocumented_types: dict[PydanticBaseModel, tuple[str, list[str]]] = dict()
|
||||
@ -238,12 +237,18 @@ def generate_config_table_md(
|
||||
return table
|
||||
|
||||
|
||||
def generate_config_md() -> str:
|
||||
def generate_config_md(config_eos: ConfigEOS) -> str:
|
||||
"""Generate configuration specification in Markdown with extra tables for prefixed values.
|
||||
|
||||
Returns:
|
||||
str: The Markdown representation of the configuration spec.
|
||||
"""
|
||||
# Fix file path for general settings to not show local/test file path
|
||||
ConfigCommonSettings._config_file_path = Path(
|
||||
"/home/user/.config/net.akkudoktoreos.net/EOS.config.json"
|
||||
)
|
||||
ConfigCommonSettings._config_folder_path = config_eos.general.config_file_path.parent
|
||||
|
||||
markdown = "# Configuration Table\n\n"
|
||||
|
||||
# Generate tables for each top level config
|
||||
@ -271,9 +276,10 @@ def main():
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
config_eos = get_config()
|
||||
|
||||
try:
|
||||
config_md = generate_config_md()
|
||||
config_md = generate_config_md(config_eos)
|
||||
if args.output_file:
|
||||
# Write to file
|
||||
with open(args.output_file, "w", encoding="utf8") as f:
|
||||
|
@ -37,6 +37,11 @@ def generate_openapi() -> dict:
|
||||
routes=app.routes,
|
||||
)
|
||||
|
||||
# Fix file path for general settings to not show local/test file path
|
||||
general = openapi_spec["components"]["schemas"]["ConfigEOS"]["properties"]["general"]["default"]
|
||||
general["config_file_path"] = "/home/user/.config/net.akkudoktoreos.net/EOS.config.json"
|
||||
general["config_folder_path"] = "/home/user/.config/net.akkudoktoreos.net"
|
||||
|
||||
return openapi_spec
|
||||
|
||||
|
||||
|
@ -39,27 +39,36 @@ def prepare_optimization_real_parameters() -> OptimizationParameters:
|
||||
# PV Forecast
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
"pvforecast0_peakpower": 5.0,
|
||||
"pvforecast0_surface_azimuth": -10,
|
||||
"pvforecast0_surface_tilt": 7,
|
||||
"pvforecast0_userhorizon": [20, 27, 22, 20],
|
||||
"pvforecast0_inverter_paco": 10000,
|
||||
"pvforecast1_peakpower": 4.8,
|
||||
"pvforecast1_surface_azimuth": -90,
|
||||
"pvforecast1_surface_tilt": 7,
|
||||
"pvforecast1_userhorizon": [30, 30, 30, 50],
|
||||
"pvforecast1_inverter_paco": 10000,
|
||||
"pvforecast2_peakpower": 1.4,
|
||||
"pvforecast2_surface_azimuth": -40,
|
||||
"pvforecast2_surface_tilt": 60,
|
||||
"pvforecast2_userhorizon": [60, 30, 0, 30],
|
||||
"pvforecast2_inverter_paco": 2000,
|
||||
"pvforecast3_peakpower": 1.6,
|
||||
"pvforecast3_surface_azimuth": 5,
|
||||
"pvforecast3_surface_tilt": 45,
|
||||
"pvforecast3_userhorizon": [45, 25, 30, 60],
|
||||
"pvforecast3_inverter_paco": 1400,
|
||||
"pvforecast4_peakpower": None,
|
||||
"planes": [
|
||||
{
|
||||
"peakpower": 5.0,
|
||||
"surface_azimuth": -10,
|
||||
"surface_tilt": 7,
|
||||
"userhorizon": [20, 27, 22, 20],
|
||||
"inverter_paco": 10000,
|
||||
},
|
||||
{
|
||||
"peakpower": 4.8,
|
||||
"surface_azimuth": -90,
|
||||
"surface_tilt": 7,
|
||||
"userhorizon": [30, 30, 30, 50],
|
||||
"inverter_paco": 10000,
|
||||
},
|
||||
{
|
||||
"peakpower": 1.4,
|
||||
"surface_azimuth": -40,
|
||||
"surface_tilt": 60,
|
||||
"userhorizon": [60, 30, 0, 30],
|
||||
"inverter_paco": 2000,
|
||||
},
|
||||
{
|
||||
"peakpower": 1.6,
|
||||
"surface_azimuth": 5,
|
||||
"surface_tilt": 45,
|
||||
"userhorizon": [45, 25, 30, 60],
|
||||
"inverter_paco": 1400,
|
||||
},
|
||||
],
|
||||
},
|
||||
# Weather Forecast
|
||||
"weather": {
|
||||
@ -67,7 +76,7 @@ def prepare_optimization_real_parameters() -> OptimizationParameters:
|
||||
},
|
||||
# Electricity Price Forecast
|
||||
"elecprice": {
|
||||
"provider": "Akkudoktor",
|
||||
"provider": "ElecPriceAkkudoktor",
|
||||
},
|
||||
# Load Forecast
|
||||
"load": {
|
||||
@ -298,7 +307,7 @@ def prepare_optimization_parameters() -> OptimizationParameters:
|
||||
"initial_soc_percentage": 15,
|
||||
"min_soc_percentage": 15,
|
||||
},
|
||||
"inverter": {"device_id": "iv1", "max_power_wh": 10000, "battery": "battery1"},
|
||||
"inverter": {"device_id": "iv1", "max_power_wh": 10000, "battery_id": "battery1"},
|
||||
"eauto": {
|
||||
"device_id": "ev1",
|
||||
"min_soc_percentage": 50,
|
||||
|
@ -68,6 +68,9 @@ class ConfigCommonSettings(SettingsBaseModel):
|
||||
General configuration to set directories of cache and output files.
|
||||
"""
|
||||
|
||||
_config_folder_path: ClassVar[Optional[Path]] = None
|
||||
_config_file_path: ClassVar[Optional[Path]] = None
|
||||
|
||||
data_folder_path: Optional[Path] = Field(
|
||||
default=None, description="Path to EOS data directory.", examples=[None, "/home/eos/data"]
|
||||
)
|
||||
@ -87,13 +90,24 @@ class ConfigCommonSettings(SettingsBaseModel):
|
||||
"""Compute data_output_path based on data_folder_path."""
|
||||
return get_absolute_path(self.data_folder_path, self.data_output_subpath)
|
||||
|
||||
# Computed fields
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def data_cache_path(self) -> Optional[Path]:
|
||||
"""Compute data_cache_path based on data_folder_path."""
|
||||
return get_absolute_path(self.data_folder_path, self.data_cache_subpath)
|
||||
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def config_folder_path(self) -> Optional[Path]:
|
||||
"""Path to EOS configuration directory."""
|
||||
return self._config_folder_path
|
||||
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def config_file_path(self) -> Optional[Path]:
|
||||
"""Path to EOS configuration file."""
|
||||
return self._config_file_path
|
||||
|
||||
|
||||
class SettingsEOS(BaseSettings):
|
||||
"""Settings for all EOS.
|
||||
@ -191,9 +205,6 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
ENCODING: ClassVar[str] = "UTF-8"
|
||||
CONFIG_FILE_NAME: ClassVar[str] = "EOS.config.json"
|
||||
|
||||
_config_folder_path: ClassVar[Optional[Path]] = None
|
||||
_config_file_path: ClassVar[Optional[Path]] = None
|
||||
|
||||
@classmethod
|
||||
def settings_customise_sources(
|
||||
cls,
|
||||
@ -224,7 +235,8 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
2. If the configuration file does not exist, creates the directory (if needed) and attempts to copy a
|
||||
default configuration file to the location. If the copy fails, uses the default configuration file directly.
|
||||
3. Creates a `JsonConfigSettingsSource` for both the configuration file and the default configuration file.
|
||||
4. Updates class attributes `_config_folder_path` and `_config_file_path` to reflect the determined paths.
|
||||
4. Updates class attributes `ConfigCommonSettings._config_folder_path` and
|
||||
`ConfigCommonSettings._config_file_path` to reflect the determined paths.
|
||||
5. Returns a tuple containing all provided and newly created settings sources in the desired order.
|
||||
|
||||
Notes:
|
||||
@ -246,8 +258,8 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
default_settings = JsonConfigSettingsSource(
|
||||
settings_cls, json_file=cls.config_default_file_path
|
||||
)
|
||||
cls._config_folder_path = config_dir
|
||||
cls._config_file_path = config_file
|
||||
ConfigCommonSettings._config_folder_path = config_dir
|
||||
ConfigCommonSettings._config_file_path = config_file
|
||||
|
||||
return (
|
||||
init_settings,
|
||||
@ -258,16 +270,6 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
default_settings,
|
||||
)
|
||||
|
||||
@property
|
||||
def config_folder_path(self) -> Optional[Path]:
|
||||
"""Path to EOS configuration directory."""
|
||||
return self._config_folder_path
|
||||
|
||||
@property
|
||||
def config_file_path(self) -> Optional[Path]:
|
||||
"""Path to EOS configuration file."""
|
||||
return self._config_file_path
|
||||
|
||||
@classmethod
|
||||
@classproperty
|
||||
def config_default_file_path(cls) -> Path:
|
||||
@ -341,13 +343,15 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
self._setup()
|
||||
|
||||
def _create_initial_config_file(self) -> None:
|
||||
if self.config_file_path and not self.config_file_path.exists():
|
||||
self.config_file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if self.general.config_file_path and not self.general.config_file_path.exists():
|
||||
self.general.config_file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
with open(self.config_file_path, "w") as f:
|
||||
with open(self.general.config_file_path, "w") as f:
|
||||
f.write(self.model_dump_json(indent=4))
|
||||
except Exception as e:
|
||||
logger.error(f"Could not write configuration file '{self.config_file_path}': {e}")
|
||||
logger.error(
|
||||
f"Could not write configuration file '{self.general.config_file_path}': {e}"
|
||||
)
|
||||
|
||||
def _update_data_folder_path(self) -> None:
|
||||
"""Updates path to the data directory."""
|
||||
@ -412,9 +416,9 @@ class ConfigEOS(SingletonMixin, SettingsEOSDefaults):
|
||||
Raises:
|
||||
ValueError: If the configuration file path is not specified or can not be written to.
|
||||
"""
|
||||
if not self.config_file_path:
|
||||
if not self.general.config_file_path:
|
||||
raise ValueError("Configuration file path unknown.")
|
||||
with self.config_file_path.open("w", encoding=self.ENCODING) as f_out:
|
||||
with self.general.config_file_path.open("w", encoding=self.ENCODING) as f_out:
|
||||
json_str = super().model_dump_json()
|
||||
f_out.write(json_str)
|
||||
|
||||
|
@ -283,11 +283,11 @@ def fastapi_config_get() -> ConfigEOS:
|
||||
|
||||
@app.put("/v1/config", tags=["config"])
|
||||
def fastapi_config_put(settings: SettingsEOS) -> ConfigEOS:
|
||||
"""Write the provided settings into the current settings.
|
||||
"""Update the current config with the provided settings.
|
||||
|
||||
The existing settings are completely overwritten. Note that for any setting
|
||||
value that is None, the configuration will fall back to values from other sources such as
|
||||
environment variables, the EOS configuration file, or default values.
|
||||
Note that for any setting value that is None or unset, the configuration will fall back to
|
||||
values from other sources such as environment variables, the EOS configuration file, or default
|
||||
values.
|
||||
|
||||
Args:
|
||||
settings (SettingsEOS): The settings to write into the current settings.
|
||||
|
@ -145,7 +145,7 @@ def config_eos(
|
||||
assert not config_file_cwd.exists()
|
||||
config_eos = get_config()
|
||||
config_eos.reset_settings()
|
||||
assert config_file == config_eos.config_file_path
|
||||
assert config_file == config_eos.general.config_file_path
|
||||
assert config_file.exists()
|
||||
assert not config_file_cwd.exists()
|
||||
assert config_default_dirs[-1] / "data" == config_eos.general.data_folder_path
|
||||
|
@ -57,7 +57,7 @@ def test_computed_paths(config_eos):
|
||||
|
||||
def test_singleton_behavior(config_eos, config_default_dirs):
|
||||
"""Test that ConfigEOS behaves as a singleton."""
|
||||
initial_cfg_file = config_eos.config_file_path
|
||||
initial_cfg_file = config_eos.general.config_file_path
|
||||
with patch(
|
||||
"akkudoktoreos.config.config.user_config_dir", return_value=str(config_default_dirs[0])
|
||||
):
|
||||
@ -65,7 +65,7 @@ def test_singleton_behavior(config_eos, config_default_dirs):
|
||||
instance2 = ConfigEOS()
|
||||
assert instance1 is config_eos
|
||||
assert instance1 is instance2
|
||||
assert instance1.config_file_path == initial_cfg_file
|
||||
assert instance1.general.config_file_path == initial_cfg_file
|
||||
|
||||
|
||||
def test_default_config_path(config_eos, config_default_dirs):
|
||||
@ -86,13 +86,13 @@ def test_config_file_priority(config_default_dirs):
|
||||
config_file = Path(config_default_dir_cwd) / ConfigEOS.CONFIG_FILE_NAME
|
||||
config_file.write_text("{}")
|
||||
config_eos = get_config()
|
||||
assert config_eos.config_file_path == config_file
|
||||
assert config_eos.general.config_file_path == config_file
|
||||
|
||||
config_file = Path(config_default_dir_user) / ConfigEOS.CONFIG_FILE_NAME
|
||||
config_file.parent.mkdir()
|
||||
config_file.write_text("{}")
|
||||
config_eos.update()
|
||||
assert config_eos.config_file_path == config_file
|
||||
assert config_eos.general.config_file_path == config_file
|
||||
|
||||
|
||||
@patch("akkudoktoreos.config.config.user_config_dir")
|
||||
|
@ -86,7 +86,7 @@ def test_config_md_current(config_eos):
|
||||
sys.path.insert(0, str(root_dir))
|
||||
from scripts import generate_config_md
|
||||
|
||||
config_md = generate_config_md.generate_config_md()
|
||||
config_md = generate_config_md.generate_config_md(config_eos)
|
||||
|
||||
with open(new_config_md_path, "w", encoding="utf8") as f_new:
|
||||
f_new.write(config_md)
|
||||
|
@ -163,10 +163,14 @@ sample_config_data = {
|
||||
},
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
"pvforecast0_peakpower": 5.0,
|
||||
"pvforecast0_surface_azimuth": 180,
|
||||
"pvforecast0_surface_tilt": 30,
|
||||
"pvforecast0_inverter_paco": 10000,
|
||||
"planes": [
|
||||
{
|
||||
"peakpower": 5.0,
|
||||
"surface_azimuth": 180,
|
||||
"surface_tilt": 30,
|
||||
"inverter_paco": 10000,
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user