From 3bb0e02aed7bb6ab96224aa377fa054abf0bfd0a Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Mon, 20 Jul 2026 14:37:03 +0200 Subject: [PATCH] fix: provider settings in top level field of configuration (#1161) Configuration for some providers was given in the sub-field `provider_settings` combining the settings of several providers. Pydantic does not understand this very well and the configuration became cumbersome, especially in EOSdash. All provider settings from the `provider-settings` sub-field are now lifted to the top level field of the configuration. The changes are automatically migrated in the configuration. Signed-off-by: Bobby Noelte --- docs/_generated/configelecprice.md | 2 +- docs/_generated/configexample.md | 47 ++- docs/_generated/configfeedintariff.md | 70 ++--- docs/_generated/configload.md | 30 +- docs/_generated/configpvforecast.md | 249 ++++++++-------- docs/_generated/configweather.md | 51 +--- docs/_generated/openapi.md | 2 +- docs/akkudoktoreos/prediction.md | 99 ++++--- openapi.json | 269 +++++------------- src/akkudoktoreos/config/configmigrate.py | 96 +++++-- .../optimization/genetic/geneticparams.py | 6 +- src/akkudoktoreos/prediction/elecprice.py | 2 +- src/akkudoktoreos/prediction/feedintariff.py | 33 +-- .../prediction/feedintarifffixed.py | 4 +- .../prediction/feedintariffimport.py | 10 +- src/akkudoktoreos/prediction/load.py | 10 +- src/akkudoktoreos/prediction/loadvrm.py | 12 +- src/akkudoktoreos/prediction/pvforecast.py | 60 ++-- .../prediction/pvforecastforecastsolar.py | 2 +- .../prediction/pvforecastimport.py | 10 +- .../prediction/pvforecastpvnode.py | 8 +- .../prediction/pvforecastsolcast.py | 2 +- src/akkudoktoreos/prediction/pvforecastvrm.py | 12 +- src/akkudoktoreos/prediction/weather.py | 23 +- src/akkudoktoreos/prediction/weatherimport.py | 10 +- src/akkudoktoreos/server/dash/prediction.py | 2 +- src/akkudoktoreos/server/eos.py | 6 +- tests/test_feedintarifffixed.py | 12 +- tests/test_loadvrm.py | 6 +- tests/test_pvforecastforecastsolar.py | 2 +- tests/test_pvforecastimport.py | 22 +- tests/test_pvforecastpvnode.py | 12 +- tests/test_pvforecastsolcast.py | 4 +- tests/test_pvforecastvrm.py | 11 +- tests/test_weatherimport.py | 22 +- .../docs/_generated/configelecprice.md | 2 +- .../testdata/docs/_generated/configexample.md | 47 ++- .../docs/_generated/configfeedintariff.md | 70 ++--- tests/testdata/docs/_generated/configload.md | 30 +- .../docs/_generated/configpvforecast.md | 249 ++++++++-------- .../testdata/docs/_generated/configweather.md | 51 +--- tests/testdata/eos_config_stripped.json | 7 +- tests/testdata/eos_config_unstripped.json | 27 +- uv.lock | 158 +++++----- 44 files changed, 798 insertions(+), 1061 deletions(-) diff --git a/docs/_generated/configelecprice.md b/docs/_generated/configelecprice.md index cb6d466c..92219cdf 100644 --- a/docs/_generated/configelecprice.md +++ b/docs/_generated/configelecprice.md @@ -9,7 +9,7 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | charges_kwh | `EOS_ELECPRICE__CHARGES_KWH` | `Optional[float]` | `rw` | `None` | Electricity price charges [amount/kWh]. Will be added to variable market price. | | elecpricefixed | `EOS_ELECPRICE__ELECPRICEFIXED` | `ElecPriceFixedCommonSettings` | `rw` | `required` | Fixed electricity price provider settings. | -| elecpriceimport | `EOS_ELECPRICE__ELECPRICEIMPORT` | `ElecPriceImportCommonSettings` | `rw` | `required` | Import provider settings. | +| elecpriceimport | `EOS_ELECPRICE__ELECPRICEIMPORT` | `ElecPriceImportCommonSettings` | `rw` | `required` | Electricity price import provider settings. | | energycharts | `EOS_ELECPRICE__ENERGYCHARTS` | `ElecPriceEnergyChartsCommonSettings` | `rw` | `required` | Energy Charts provider settings. | | provider | `EOS_ELECPRICE__PROVIDER` | `Optional[str]` | `rw` | `None` | Electricity price provider id of provider to be used. | | providers | | `list[str]` | `ro` | `N/A` | Available electricity price provider ids. | diff --git a/docs/_generated/configexample.md b/docs/_generated/configexample.md index debdee9f..904891db 100644 --- a/docs/_generated/configexample.md +++ b/docs/_generated/configexample.md @@ -123,9 +123,12 @@ }, "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": null + }, + "feedintariffimport": { + "import_file_path": null, + "import_json": null } }, "general": { @@ -142,13 +145,13 @@ "loadakkudoktor": { "loadakkudoktor_year_energy_kwh": null }, - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 - }, "loadimport": { "import_file_path": null, "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 } }, "logging": { @@ -189,12 +192,25 @@ }, "pvforecast": { "provider": "PVForecastAkkudoktor", - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, + "pvnode": { + "api_key": "", + "site_id": null, + "forecast_days": 2 + }, + "forecastsolar": { + "api_key": null + }, + "solcast": { + "api_key": "", + "site_id": "" }, "planes": [ { @@ -258,8 +274,9 @@ "utils": {}, "weather": { "provider": "WeatherImport", - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": null } } } diff --git a/docs/_generated/configfeedintariff.md b/docs/_generated/configfeedintariff.md index dd855ef1..217b41ac 100644 --- a/docs/_generated/configfeedintariff.md +++ b/docs/_generated/configfeedintariff.md @@ -7,8 +7,9 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | +| feedintarifffixed | `EOS_FEEDINTARIFF__FEEDINTARIFFFIXED` | `FeedInTariffFixedCommonSettings` | `rw` | `required` | Fixed feed in tariff provider settings. | +| feedintariffimport | `EOS_FEEDINTARIFF__FEEDINTARIFFIMPORT` | `FeedInTariffImportCommonSettings` | `rw` | `required` | Feed in tarif import provider settings. | | provider | `EOS_FEEDINTARIFF__PROVIDER` | `Optional[str]` | `rw` | `None` | Feed in tariff provider id of provider to be used. | -| provider_settings | `EOS_FEEDINTARIFF__PROVIDER_SETTINGS` | `FeedInTariffCommonProviderSettings` | `rw` | `required` | Provider settings | | providers | | `list[str]` | `ro` | `N/A` | Available feed in tariff provider ids. | ::: @@ -22,9 +23,12 @@ { "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": null + }, + "feedintariffimport": { + "import_file_path": null, + "import_json": null } } } @@ -40,9 +44,12 @@ { "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": null + }, + "feedintariffimport": { + "import_file_path": null, + "import_json": null }, "providers": [ "FeedInTariffFixed", @@ -56,7 +63,7 @@ ### Common settings for feed in tariff data import from file or JSON string -:::{table} feedintariff::provider_settings::FeedInTariffImport +:::{table} feedintariff::feedintariffimport :widths: 10 10 5 5 30 :align: left @@ -75,11 +82,9 @@ ```json { "feedintariff": { - "provider_settings": { - "FeedInTariffImport": { - "import_file_path": null, - "import_json": "{\"fead_in_tariff_wh\": [0.000078, 0.000078, 0.000023]}" - } + "feedintariffimport": { + "import_file_path": null, + "import_json": "{\"fead_in_tariff_wh\": [0.000078, 0.000078, 0.000023]}" } } } @@ -89,7 +94,7 @@ ### Common settings for elecprice fixed price -:::{table} feedintariff::provider_settings::FeedInTariffFixed +:::{table} feedintariff::feedintarifffixed :widths: 10 10 5 5 30 :align: left @@ -107,41 +112,8 @@ ```json { "feedintariff": { - "provider_settings": { - "FeedInTariffFixed": { - "feed_in_tariff_kwh": 0.078 - } - } - } - } -``` - - -### Feed In Tariff Prediction Provider Configuration - - -:::{table} feedintariff::provider_settings -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| FeedInTariffFixed | `Optional[akkudoktoreos.prediction.feedintarifffixed.FeedInTariffFixedCommonSettings]` | `rw` | `None` | FeedInTariffFixed settings | -| FeedInTariffImport | `Optional[akkudoktoreos.prediction.feedintariffimport.FeedInTariffImportCommonSettings]` | `rw` | `None` | FeedInTariffImport settings | -::: - - - -**Example Input/Output** - - - -```json - { - "feedintariff": { - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": 0.078 } } } diff --git a/docs/_generated/configload.md b/docs/_generated/configload.md index 95055dad..feec1705 100644 --- a/docs/_generated/configload.md +++ b/docs/_generated/configload.md @@ -9,9 +9,9 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | loadakkudoktor | `EOS_LOAD__LOADAKKUDOKTOR` | `LoadAkkudoktorCommonSettings` | `rw` | `required` | LoadAkkudoktor provider settings. | | loadimport | `EOS_LOAD__LOADIMPORT` | `LoadImportCommonSettings` | `rw` | `required` | LoadImport provider settings. | -| loadvrm | `EOS_LOAD__LOADVRM` | `LoadVrmCommonSettings` | `rw` | `required` | LoadVrm provider settings. | | provider | `EOS_LOAD__PROVIDER` | `Optional[str]` | `rw` | `None` | Load provider id of provider to be used. | | providers | | `list[str]` | `ro` | `N/A` | Available load provider ids. | +| vrm | `EOS_LOAD__VRM` | `LoadVrmCommonSettings` | `rw` | `required` | Victron Remote Management (VRM) provider settings. | ::: @@ -27,13 +27,13 @@ "loadakkudoktor": { "loadakkudoktor_year_energy_kwh": null }, - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 - }, "loadimport": { "import_file_path": null, "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 } } } @@ -52,14 +52,14 @@ "loadakkudoktor": { "loadakkudoktor_year_energy_kwh": null }, - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 - }, "loadimport": { "import_file_path": null, "import_json": null }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, "providers": [ "LoadAkkudoktor", "LoadAkkudoktorAdjusted", @@ -74,14 +74,14 @@ ### Common settings for load forecast VRM API -:::{table} load::loadvrm +:::{table} load::vrm :widths: 10 10 5 5 30 :align: left | Name | Type | Read-Only | Default | Description | | ---- | ---- | --------- | ------- | ----------- | -| load_vrm_idsite | `int` | `rw` | `12345` | VRM-Installation-ID | -| load_vrm_token | `str` | `rw` | `your-token` | Token for Connecting VRM API | +| site_id | `int` | `rw` | `12345` | VRM-Installation-ID | +| token | `str` | `rw` | `your-token` | Access token for connecting to the Victron Remote Management (VRM) API | ::: @@ -93,9 +93,9 @@ ```json { "load": { - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 + "vrm": { + "token": "your-token", + "site_id": 12345 } } } diff --git a/docs/_generated/configpvforecast.md b/docs/_generated/configpvforecast.md index a5776e00..ce9cf64c 100644 --- a/docs/_generated/configpvforecast.md +++ b/docs/_generated/configpvforecast.md @@ -7,6 +7,7 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | +| forecastsolar | `EOS_PVFORECAST__FORECASTSOLAR` | `PVForecastForecastSolarCommonSettings` | `rw` | `required` | ForecastSolar provider settings | | max_planes | `EOS_PVFORECAST__MAX_PLANES` | `Optional[int]` | `rw` | `0` | Maximum number of planes that can be set | | planes | `EOS_PVFORECAST__PLANES` | `Optional[list[akkudoktoreos.prediction.pvforecast.PVForecastPlaneSetting]]` | `rw` | `None` | Plane configuration. | | planes_azimuth | | `List[float]` | `ro` | `N/A` | Compute a list of the azimuths per active planes. | @@ -15,8 +16,11 @@ | planes_tilt | | `List[float]` | `ro` | `N/A` | Compute a list of the tilts per active planes. | | planes_userhorizon | | `Any` | `ro` | `N/A` | Compute a list of the user horizon per active planes. | | provider | `EOS_PVFORECAST__PROVIDER` | `Optional[str]` | `rw` | `None` | PVForecast provider id of provider to be used. | -| provider_settings | `EOS_PVFORECAST__PROVIDER_SETTINGS` | `PVForecastCommonProviderSettings` | `rw` | `required` | Provider settings | | providers | | `list[str]` | `ro` | `N/A` | Available PVForecast provider ids. | +| pvforecastimport | `EOS_PVFORECAST__PVFORECASTIMPORT` | `PVForecastImportCommonSettings` | `rw` | `required` | PV forecast import provider settings | +| pvnode | `EOS_PVFORECAST__PVNODE` | `PVForecastPVNodeCommonSettings` | `rw` | `required` | PVNode provider settings | +| solcast | `EOS_PVFORECAST__SOLCAST` | `PVForecastSolcastCommonSettings` | `rw` | `required` | Solcast provider settings | +| vrm | `EOS_PVFORECAST__VRM` | `PVForecastVrmCommonSettings` | `rw` | `required` | Victron Remote Management (VRM) provider settings | ::: @@ -29,12 +33,25 @@ { "pvforecast": { "provider": "PVForecastAkkudoktor", - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, + "pvnode": { + "api_key": "", + "site_id": null, + "forecast_days": 2 + }, + "forecastsolar": { + "api_key": null + }, + "solcast": { + "api_key": "", + "site_id": "" }, "planes": [ { @@ -97,12 +114,25 @@ { "pvforecast": { "provider": "PVForecastAkkudoktor", - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, + "pvnode": { + "api_key": "", + "site_id": null, + "forecast_days": 2 + }, + "forecastsolar": { + "api_key": null + }, + "solcast": { + "api_key": "", + "site_id": "" }, "planes": [ { @@ -192,10 +222,41 @@ ``` +### Common settings for PV forecast VRM API + + +:::{table} pvforecast::vrm +:widths: 10 10 5 5 30 +:align: left + +| Name | Type | Read-Only | Default | Description | +| ---- | ---- | --------- | ------- | ----------- | +| site_id | `int` | `rw` | `12345` | VRM-Installation-ID | +| token | `str` | `rw` | `your-token` | Access token for connecting to the Victron Remote Management (VRM) API | +::: + + + +**Example Input/Output** + + + +```json + { + "pvforecast": { + "vrm": { + "token": "your-token", + "site_id": 12345 + } + } + } +``` + + ### Common settings for the Solcast PV forecast provider -:::{table} pvforecast::provider_settings::PVForecastSolcast +:::{table} pvforecast::solcast :widths: 10 10 5 5 30 :align: left @@ -214,42 +275,9 @@ ```json { "pvforecast": { - "provider_settings": { - "PVForecastSolcast": { - "api_key": "your-solcast-key", - "site_id": "abcd-1234-efgh-5678" - } - } - } - } -``` - - -### Common settings for the Forecast.Solar PV forecast provider - - -:::{table} pvforecast::provider_settings::PVForecastForecastSolar -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| api_key | `Optional[str]` | `rw` | `None` | Forecast.Solar API key. Optional — the public endpoint works without a key (lower rate limit). | -::: - - - -**Example Input/Output** - - - -```json - { - "pvforecast": { - "provider_settings": { - "PVForecastForecastSolar": { - "api_key": null - } + "solcast": { + "api_key": "your-solcast-key", + "site_id": "abcd-1234-efgh-5678" } } } @@ -259,7 +287,7 @@ ### Common settings for the pvnode.com PV forecast provider -:::{table} pvforecast::provider_settings::PVForecastPVNode +:::{table} pvforecast::pvnode :widths: 10 10 5 5 30 :align: left @@ -279,45 +307,10 @@ ```json { "pvforecast": { - "provider_settings": { - "PVForecastPVNode": { - "api_key": "pvn_live_xxxxxxxxxxxxxxxx", - "site_id": "abcd-1234", - "forecast_days": 2 - } - } - } - } -``` - - -### Common settings for PV forecast VRM API - - -:::{table} pvforecast::provider_settings::PVForecastVrm -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| pvforecast_vrm_idsite | `int` | `rw` | `12345` | VRM-Installation-ID | -| pvforecast_vrm_token | `str` | `rw` | `your-token` | Token for Connecting VRM API | -::: - - - -**Example Input/Output** - - - -```json - { - "pvforecast": { - "provider_settings": { - "PVForecastVrm": { - "pvforecast_vrm_token": "your-token", - "pvforecast_vrm_idsite": 12345 - } + "pvnode": { + "api_key": "pvn_live_xxxxxxxxxxxxxxxx", + "site_id": "abcd-1234", + "forecast_days": 2 } } } @@ -327,7 +320,7 @@ ### Common settings for pvforecast data import from file or JSON string -:::{table} pvforecast::provider_settings::PVForecastImport +:::{table} pvforecast::pvforecastimport :widths: 10 10 5 5 30 :align: left @@ -346,48 +339,9 @@ ```json { "pvforecast": { - "provider_settings": { - "PVForecastImport": { - "import_file_path": null, - "import_json": "{\"pvforecast_ac_power\": [0, 8.05, 352.91]}" - } - } - } - } -``` - - -### PV Forecast Provider Configuration - - -:::{table} pvforecast::provider_settings -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| PVForecastForecastSolar | `Optional[akkudoktoreos.prediction.pvforecastforecastsolar.PVForecastForecastSolarCommonSettings]` | `rw` | `None` | PVForecastForecastSolar settings | -| PVForecastImport | `Optional[akkudoktoreos.prediction.pvforecastimport.PVForecastImportCommonSettings]` | `rw` | `None` | PVForecastImport settings | -| PVForecastPVNode | `Optional[akkudoktoreos.prediction.pvforecastpvnode.PVForecastPVNodeCommonSettings]` | `rw` | `None` | PVForecastPVNode settings | -| PVForecastSolcast | `Optional[akkudoktoreos.prediction.pvforecastsolcast.PVForecastSolcastCommonSettings]` | `rw` | `None` | PVForecastSolcast settings | -| PVForecastVrm | `Optional[akkudoktoreos.prediction.pvforecastvrm.PVForecastVrmCommonSettings]` | `rw` | `None` | PVForecastVrm settings | -::: - - - -**Example Input/Output** - - - -```json - { - "pvforecast": { - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": "{\"pvforecast_ac_power\": [0, 8.05, 352.91]}" } } } @@ -458,3 +412,32 @@ } ``` + +### Common settings for the Forecast.Solar PV forecast provider + + +:::{table} pvforecast::forecastsolar +:widths: 10 10 5 5 30 +:align: left + +| Name | Type | Read-Only | Default | Description | +| ---- | ---- | --------- | ------- | ----------- | +| api_key | `Optional[str]` | `rw` | `None` | Forecast.Solar API key. Optional — the public endpoint works without a key (lower rate limit). | +::: + + + +**Example Input/Output** + + + +```json + { + "pvforecast": { + "forecastsolar": { + "api_key": null + } + } + } +``` + diff --git a/docs/_generated/configweather.md b/docs/_generated/configweather.md index dee9ad63..024a267c 100644 --- a/docs/_generated/configweather.md +++ b/docs/_generated/configweather.md @@ -8,8 +8,8 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | | provider | `EOS_WEATHER__PROVIDER` | `Optional[str]` | `rw` | `None` | Weather provider id of provider to be used. | -| provider_settings | `EOS_WEATHER__PROVIDER_SETTINGS` | `WeatherCommonProviderSettings` | `rw` | `required` | Provider settings | | providers | | `list[str]` | `ro` | `N/A` | Available weather provider ids. | +| weatherimport | `EOS_WEATHER__WEATHERIMPORT` | `WeatherImportCommonSettings` | `rw` | `required` | Weather import provider settings | ::: @@ -22,8 +22,9 @@ { "weather": { "provider": "WeatherImport", - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": null } } } @@ -39,8 +40,9 @@ { "weather": { "provider": "WeatherImport", - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": null }, "providers": [ "BrightSky", @@ -56,7 +58,7 @@ ### Common settings for weather data import from file or JSON string -:::{table} weather::provider_settings::WeatherImport +:::{table} weather::weatherimport :widths: 10 10 5 5 30 :align: left @@ -75,40 +77,9 @@ ```json { "weather": { - "provider_settings": { - "WeatherImport": { - "import_file_path": null, - "import_json": "{\"weather_temp_air\": [18.3, 17.8, 16.9]}" - } - } - } - } -``` - - -### Weather Forecast Provider Configuration - - -:::{table} weather::provider_settings -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| WeatherImport | `Optional[akkudoktoreos.prediction.weatherimport.WeatherImportCommonSettings]` | `rw` | `None` | WeatherImport settings | -::: - - - -**Example Input/Output** - - - -```json - { - "weather": { - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": "{\"weather_temp_air\": [18.3, 17.8, 16.9]}" } } } diff --git a/docs/_generated/openapi.md b/docs/_generated/openapi.md index cff6369a..3566a274 100644 --- a/docs/_generated/openapi.md +++ b/docs/_generated/openapi.md @@ -1,6 +1,6 @@ # Akkudoktor-EOS -**Version**: `v0.3.0.dev2607201124501528` +**Version**: `v0.3.0.dev2607201194575252` **Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period. diff --git a/docs/akkudoktoreos/prediction.md b/docs/akkudoktoreos/prediction.md index d1a0e7cb..d1f3251e 100644 --- a/docs/akkudoktoreos/prediction.md +++ b/docs/akkudoktoreos/prediction.md @@ -220,9 +220,9 @@ Configuration options: - `FeedInTariffFixed`: Provides fixed feed in tariff values. - `FeedInTariffImport`: Imports from a file or JSON string or by endpoint data provision. - - `provider_settings.feed_in_tariff_kwh`: Fixed feed in tariff (€/kWh). - - `provider_settings.import_file_path`: Path to the file to import feed in tariff forecast data from. - - `provider_settings.import_json`: JSON string, dictionary of feed in tariff value lists. + - `feedintarifffixed.feed_in_tariff_kwh`: Fixed feed in tariff (€/kWh). + - `feedintariffimport.import_file_path`: Path to the file to import feed in tariff forecast data from. + - `feedintariffimport.import_json`: JSON string, dictionary of feed in tariff value lists. ### FeedInTariffImport Provider @@ -264,14 +264,14 @@ Configuration options: - `provider`: Load provider id of provider to be used. - `LoadAkkudoktor`: Retrieves from local database. - - `LoadVrm`: Retrieves data from the VRM API by Victron Energy. + - `LoadVrm`: Retrieves data from the Victron Remeote Management (VRM) API by Victron Energy. - `LoadImport`: Imports from a file or JSON string or by endpoint data provision. - - `provider_settings.LoadAkkudoktor.loadakkudoktor_year_energy_kwh`: Yearly energy consumption (kWh). - - `provider_settings.LoadVRM.load_vrm_token`: API token. - - `provider_settings.LoadVRM.load_vrm_idsite`: load_vrm_idsite. - - `provider_settings.LoadImport.loadimport_file_path`: Path to the file to import load forecast data from. - - `provider_settings.LoadImport.loadimport_json`: JSON string, dictionary of load forecast value lists. + - `loadakkudoktor.loadakkudoktor_year_energy_kwh`: Yearly energy consumption (kWh). + - `vrm.load_vrm_token`: API token. + - `vrm.load_vrm_idsite`: load_vrm_idsite. + - `loadimport.loadimport_file_path`: Path to the file to import load forecast data from. + - `loadimport.loadimport_json`: JSON string, dictionary of load forecast value lists. ### LoadAkkudoktor Provider @@ -312,12 +312,10 @@ Preferences. This token must be stored in the EOS configuration along with the V { "load": { "provider": "LoadVrm", - "provider_settings": { - "LoadVRM": { - "load_vrm_token": "dummy-token", - "load_vrm_idsite": 12345 - } - } + "vrm": { + "token": "dummy-token", + "site_id": 12345 + } } } ``` @@ -371,13 +369,25 @@ Configuration options: - `provider`: PVForecast provider id of provider to be used. - - `PVForecastAkkudoktor`: Retrieves from Akkudoktor.net. - - `PVForecastVrm`: Retrieves data from the VRM API by Victron Energy. + - `PVForecastAkkudoktor`: Retrieves forcast from Akkudoktor.net. + - `PVForecastVrm`: Retrieves forecast from the Victron Remote Management (VRM) API. - `PVForecastPVNode`: Retrieves native 15-minute forecasts from the pvnode.com API. - `PVForecastForecastSolar`: Retrieves forecasts from the free Forecast.Solar API. - `PVForecastSolcast`: Retrieves forecasts from the Solcast rooftop-site API. - `PVForecastImport`: Imports from a file or JSON string or by endpoint data provision. + - `vrm.token`: Victron Remote Management (VRM) access token. + - `vrm.site_id`: Victron Remote Management (VRM) installation ID. + - `pvnode.site_id`: pvnode.com saved-site id. Leave empty for inline mode. + - `pvnode.api_key`: pvnode.com API key. + - `pvnode.site_id`: pvnode.com saved-site id. Leave empty for inline mode. + - `pvnode.forecast_days`: Forecast horizon in days (1-7). + - `forecastsolar.api_key`: Forecast.Solar API key (optional). + - `solcast.api_key`: Solcast API key. + - `solcast.site_id`: Solcast rooftop resource (site) id. + - `pvforcastimport.import_file_path`: Path to the file to import PV forecast data from. + - `pvforcastimport.import_json`: JSON string, dictionary of PV forecast value lists. + - `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). @@ -402,14 +412,6 @@ Configuration options: - `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. - - `provider_settings.PVForecastPVNode.api_key`: pvnode.com API key. - - `provider_settings.PVForecastPVNode.site_id`: pvnode.com saved-site id. Leave empty for inline mode. - - `provider_settings.PVForecastPVNode.forecast_days`: Forecast horizon in days (1-7). - - `provider_settings.PVForecastForecastSolar.api_key`: Forecast.Solar API key (optional). - - `provider_settings.PVForecastSolcast.api_key`: Solcast API key. - - `provider_settings.PVForecastSolcast.site_id`: Solcast rooftop resource (site) id. --- @@ -575,18 +577,19 @@ Example: ### PVForecastVrm Provider -The `PVForecastVrm` provider retrieves pv power forecast data from the VRM API by Victron Energy. -To receive forecasts, the system data must be configured under Dynamic ESS in the VRM portal. -To query the forecasts, an API token is required, which can also be created in the VRM portal under Preferences. -This token must be stored in the EOS configuration along with the VRM-Installations-ID. +The `PVForecastVrm` provider retrieves pv power forecast data from the Victron Remote Management +(VRM) API by Victron Energy. To receive forecasts, the system data must be configured under Dynamic +ESS in the VRM portal. To query the forecasts, an API token is required, which can also be created +in the VRM portal under Preferences. This token must be stored in the EOS configuration along with +the VRM-Installations-ID (site_id). ```python { "pvforecast": { "provider": "PVForecastVrm", - "provider_settings": { - "pvforecast_vrm_token": "dummy-token", - "pvforecast_vrm_idsite": 12345 + "vrm": { + "token": "dummy-token", + "site_id": 12345 } } ``` @@ -624,12 +627,10 @@ site id empty to send the configured `planes` geometry inline. { "pvforecast": { "provider": "PVForecastPVNode", - "provider_settings": { - "PVForecastPVNode": { - "api_key": "your-pvnode-key", - "site_id": "your-site-id", - "forecast_days": 2 - } + "pvnode": { + "api_key": "your-pvnode-key", + "site_id": "your-site-id", + "forecast_days": 2 } } } @@ -651,10 +652,8 @@ the system geometry from the configured `planes` (one request per plane, summed { "pvforecast": { "provider": "PVForecastForecastSolar", - "provider_settings": { - "PVForecastForecastSolar": { - "api_key": null - } + "forecastsolar": { + "api_key": null } } } @@ -676,11 +675,9 @@ tier limits the number of API calls per day. { "pvforecast": { "provider": "PVForecastSolcast", - "provider_settings": { - "PVForecastSolcast": { - "api_key": "your-solcast-key", - "site_id": "your-resource-id" - } + "solcast": { + "api_key": "your-solcast-key", + "site_id": "your-resource-id" } } } @@ -727,10 +724,10 @@ Configuration options: - `BrightSky`: Retrieves from [BrightSky](https://api.brightsky.dev). - `ClearOutside`: Retrieves from [ClearOutside](https://clearoutside.com/forecast). - `OpenMeteo`: Retrieves from [OpenMeteo](https://api.open-meteo.com/v1/forecast). - - `LoadImport`: Imports from a file or JSON string or by endpoint data provision. + - `WeatherImport`: Imports from a file or JSON string or by endpoint data provision. - - `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. + - `weatherimport.import_file_path`: Path to the file to import weatherforecast data from. + - `weatherimport.import_json`: JSON string, dictionary of weather forecast value lists. ### BrightSky Provider @@ -837,7 +834,7 @@ The prediction keys for the weather forecast data are: - `weather_wind_direction`: Wind Direction (°) - `weather_wind_speed`: Wind Speed (kmph) -The PV forecast data must be provided in one of the formats described in +The weather forecast data must be provided in one of the formats described in . The data source can be given in the `import_file_path` or `import_json` configuration option. diff --git a/openapi.json b/openapi.json index c07d1ccb..0da3f1a5 100644 --- a/openapi.json +++ b/openapi.json @@ -8,7 +8,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "version": "v0.3.0.dev2607201124501528" + "version": "v0.3.0.dev2607201194575252" }, "paths": { "/v1/admin/cache/clear": { @@ -3490,7 +3490,7 @@ }, "elecpriceimport": { "$ref": "#/components/schemas/ElecPriceImportCommonSettings", - "description": "Import provider settings." + "description": "Electricity price import provider settings." }, "energycharts": { "$ref": "#/components/schemas/ElecPriceEnergyChartsCommonSettings", @@ -3561,7 +3561,7 @@ }, "elecpriceimport": { "$ref": "#/components/schemas/ElecPriceImportCommonSettings", - "description": "Import provider settings." + "description": "Electricity price import provider settings." }, "energycharts": { "$ref": "#/components/schemas/ElecPriceEnergyChartsCommonSettings", @@ -4372,41 +4372,6 @@ "title": "FRBCTimerStatus", "description": "Current status of an FRBC Timer.\n\nIndicates when the Timer will be finished." }, - "FeedInTariffCommonProviderSettings": { - "properties": { - "FeedInTariffFixed": { - "anyOf": [ - { - "$ref": "#/components/schemas/FeedInTariffFixedCommonSettings" - }, - { - "type": "null" - } - ], - "description": "FeedInTariffFixed settings", - "examples": [ - null - ] - }, - "FeedInTariffImport": { - "anyOf": [ - { - "$ref": "#/components/schemas/FeedInTariffImportCommonSettings" - }, - { - "type": "null" - } - ], - "description": "FeedInTariffImport settings", - "examples": [ - null - ] - } - }, - "type": "object", - "title": "FeedInTariffCommonProviderSettings", - "description": "Feed In Tariff Prediction Provider Configuration." - }, "FeedInTariffCommonSettings-Input": { "properties": { "provider": { @@ -4425,12 +4390,13 @@ "FeedInTarifImport" ] }, - "provider_settings": { - "$ref": "#/components/schemas/FeedInTariffCommonProviderSettings", - "description": "Provider settings", - "examples": [ - {} - ] + "feedintarifffixed": { + "$ref": "#/components/schemas/FeedInTariffFixedCommonSettings", + "description": "Fixed feed in tariff provider settings." + }, + "feedintariffimport": { + "$ref": "#/components/schemas/FeedInTariffImportCommonSettings", + "description": "Feed in tarif import provider settings." } }, "type": "object", @@ -4455,12 +4421,13 @@ "FeedInTarifImport" ] }, - "provider_settings": { - "$ref": "#/components/schemas/FeedInTariffCommonProviderSettings", - "description": "Provider settings", - "examples": [ - {} - ] + "feedintarifffixed": { + "$ref": "#/components/schemas/FeedInTariffFixedCommonSettings", + "description": "Fixed feed in tariff provider settings." + }, + "feedintariffimport": { + "$ref": "#/components/schemas/FeedInTariffImportCommonSettings", + "description": "Feed in tarif import provider settings." }, "providers": { "items": { @@ -6424,13 +6391,13 @@ "$ref": "#/components/schemas/LoadAkkudoktorCommonSettings", "description": "LoadAkkudoktor provider settings." }, - "loadvrm": { - "$ref": "#/components/schemas/LoadVrmCommonSettings", - "description": "LoadVrm provider settings." - }, "loadimport": { "$ref": "#/components/schemas/LoadImportCommonSettings", "description": "LoadImport provider settings." + }, + "vrm": { + "$ref": "#/components/schemas/LoadVrmCommonSettings", + "description": "Victron Remote Management (VRM) provider settings." } }, "type": "object", @@ -6458,14 +6425,14 @@ "$ref": "#/components/schemas/LoadAkkudoktorCommonSettings", "description": "LoadAkkudoktor provider settings." }, - "loadvrm": { - "$ref": "#/components/schemas/LoadVrmCommonSettings", - "description": "LoadVrm provider settings." - }, "loadimport": { "$ref": "#/components/schemas/LoadImportCommonSettings", "description": "LoadImport provider settings." }, + "vrm": { + "$ref": "#/components/schemas/LoadVrmCommonSettings", + "description": "Victron Remote Management (VRM) provider settings." + }, "providers": { "items": { "type": "string" @@ -6527,18 +6494,18 @@ }, "LoadVrmCommonSettings": { "properties": { - "load_vrm_token": { + "token": { "type": "string", - "title": "Load Vrm Token", - "description": "Token for Connecting VRM API", + "title": "Token", + "description": "Access token for connecting to the Victron Remote Management (VRM) API", "default": "your-token", "examples": [ "your-token" ] }, - "load_vrm_idsite": { + "site_id": { "type": "integer", - "title": "Load Vrm Idsite", + "title": "Site Id", "description": "VRM-Installation-ID", "default": 12345, "examples": [ @@ -7657,83 +7624,6 @@ "title": "PPBCStartInterruptionInstruction", "description": "Represents an instruction to interrupt execution of a running power sequence.\n\nThis model defines a control instruction that interrupts the execution of an\nactive power sequence. It enables dynamic control over sequence execution,\nallowing temporary suspension of a sequence in response to changing system conditions\nor requirements, particularly for sequences marked as interruptible." }, - "PVForecastCommonProviderSettings": { - "properties": { - "PVForecastImport": { - "anyOf": [ - { - "$ref": "#/components/schemas/PVForecastImportCommonSettings" - }, - { - "type": "null" - } - ], - "description": "PVForecastImport settings", - "examples": [ - null - ] - }, - "PVForecastVrm": { - "anyOf": [ - { - "$ref": "#/components/schemas/PVForecastVrmCommonSettings" - }, - { - "type": "null" - } - ], - "description": "PVForecastVrm settings", - "examples": [ - null - ] - }, - "PVForecastPVNode": { - "anyOf": [ - { - "$ref": "#/components/schemas/PVForecastPVNodeCommonSettings" - }, - { - "type": "null" - } - ], - "description": "PVForecastPVNode settings", - "examples": [ - null - ] - }, - "PVForecastForecastSolar": { - "anyOf": [ - { - "$ref": "#/components/schemas/PVForecastForecastSolarCommonSettings" - }, - { - "type": "null" - } - ], - "description": "PVForecastForecastSolar settings", - "examples": [ - null - ] - }, - "PVForecastSolcast": { - "anyOf": [ - { - "$ref": "#/components/schemas/PVForecastSolcastCommonSettings" - }, - { - "type": "null" - } - ], - "description": "PVForecastSolcast settings", - "examples": [ - null - ] - } - }, - "type": "object", - "title": "PVForecastCommonProviderSettings", - "description": "PV Forecast Provider Configuration." - }, "PVForecastCommonSettings-Input": { "properties": { "provider": { @@ -7751,12 +7641,25 @@ "PVForecastAkkudoktor" ] }, - "provider_settings": { - "$ref": "#/components/schemas/PVForecastCommonProviderSettings", - "description": "Provider settings", - "examples": [ - {} - ] + "pvforecastimport": { + "$ref": "#/components/schemas/PVForecastImportCommonSettings", + "description": "PV forecast import provider settings" + }, + "vrm": { + "$ref": "#/components/schemas/PVForecastVrmCommonSettings", + "description": "Victron Remote Management (VRM) provider settings" + }, + "pvnode": { + "$ref": "#/components/schemas/PVForecastPVNodeCommonSettings", + "description": "PVNode provider settings" + }, + "forecastsolar": { + "$ref": "#/components/schemas/PVForecastForecastSolarCommonSettings", + "description": "ForecastSolar provider settings" + }, + "solcast": { + "$ref": "#/components/schemas/PVForecastSolcastCommonSettings", + "description": "Solcast provider settings" }, "planes": { "anyOf": [ @@ -7855,12 +7758,25 @@ "PVForecastAkkudoktor" ] }, - "provider_settings": { - "$ref": "#/components/schemas/PVForecastCommonProviderSettings", - "description": "Provider settings", - "examples": [ - {} - ] + "pvforecastimport": { + "$ref": "#/components/schemas/PVForecastImportCommonSettings", + "description": "PV forecast import provider settings" + }, + "vrm": { + "$ref": "#/components/schemas/PVForecastVrmCommonSettings", + "description": "Victron Remote Management (VRM) provider settings" + }, + "pvnode": { + "$ref": "#/components/schemas/PVForecastPVNodeCommonSettings", + "description": "PVNode provider settings" + }, + "forecastsolar": { + "$ref": "#/components/schemas/PVForecastForecastSolarCommonSettings", + "description": "ForecastSolar provider settings" + }, + "solcast": { + "$ref": "#/components/schemas/PVForecastSolcastCommonSettings", + "description": "Solcast provider settings" }, "planes": { "anyOf": [ @@ -8402,18 +8318,18 @@ }, "PVForecastVrmCommonSettings": { "properties": { - "pvforecast_vrm_token": { + "token": { "type": "string", - "title": "Pvforecast Vrm Token", - "description": "Token for Connecting VRM API", + "title": "Token", + "description": "Access token for connecting to the Victron Remote Management (VRM) API", "default": "your-token", "examples": [ "your-token" ] }, - "pvforecast_vrm_idsite": { + "site_id": { "type": "integer", - "title": "Pvforecast Vrm Idsite", + "title": "Site Id", "description": "VRM-Installation-ID", "default": 12345, "examples": [ @@ -9523,27 +9439,6 @@ "title": "ValueTimeWindowSequence", "description": "Sequence of value time windows.\n\nThis model specializes `TimeWindowSequence` to ensure that all\ncontained windows are instances of `ValueTimeWindow`.\nIt provides the full set of sequence operations (containment checks,\navailability, start time calculations) for value windows." }, - "WeatherCommonProviderSettings": { - "properties": { - "WeatherImport": { - "anyOf": [ - { - "$ref": "#/components/schemas/WeatherImportCommonSettings" - }, - { - "type": "null" - } - ], - "description": "WeatherImport settings", - "examples": [ - null - ] - } - }, - "type": "object", - "title": "WeatherCommonProviderSettings", - "description": "Weather Forecast Provider Configuration." - }, "WeatherCommonSettings-Input": { "properties": { "provider": { @@ -9561,12 +9456,9 @@ "WeatherImport" ] }, - "provider_settings": { - "$ref": "#/components/schemas/WeatherCommonProviderSettings", - "description": "Provider settings", - "examples": [ - {} - ] + "weatherimport": { + "$ref": "#/components/schemas/WeatherImportCommonSettings", + "description": "Weather import provider settings" } }, "type": "object", @@ -9590,12 +9482,9 @@ "WeatherImport" ] }, - "provider_settings": { - "$ref": "#/components/schemas/WeatherCommonProviderSettings", - "description": "Provider settings", - "examples": [ - {} - ] + "weatherimport": { + "$ref": "#/components/schemas/WeatherImportCommonSettings", + "description": "Weather import provider settings" }, "providers": { "items": { diff --git a/src/akkudoktoreos/config/configmigrate.py b/src/akkudoktoreos/config/configmigrate.py index f74b853d..f993ecf0 100644 --- a/src/akkudoktoreos/config/configmigrate.py +++ b/src/akkudoktoreos/config/configmigrate.py @@ -48,49 +48,105 @@ MIGRATION_MAP: Dict[ None, # drop ], ] = { - # 0.2.0.dev -> 0.2.0.dev + # adapter + # ======= + # - HomeAssistant "adapter/homeassistant/optimization_solution_entity_ids": ( "adapter/homeassistant/solution_entity_ids", lambda v: v if isinstance(v, list) else None, ), - "general/data_folder_path": ("general/data_folder_path", _KEEP_DEFAULT), - # 0.2.0 -> 0.2.0+dev + # - NodeRed + # devices + # ======= + # - batteries + "devices/batteries/0/initial_soc_percentage": None, + # - electric_vehicles + "devices/electric_vehicles/0/initial_soc_percentage": None, + # elecprice + # ========= + # - ElecPriceAkkudoktor + # - ElecPriceEnergyCharts + # - ElecPriceFixed + # - ElecPriceImport "elecprice/provider_settings/ElecPriceImport/import_file_path": "elecprice/elecpriceimport/import_file_path", "elecprice/provider_settings/ElecPriceImport/import_json": "elecprice/elecpriceimport/import_json", - "load/provider_settings/LoadAkkudoktor/loadakkudoktor_year_energy_kwh": "load/loadakkudoktor/loadakkudoktor_year_energy_kwh", - "load/provider_settings/LoadVrm/load_vrm_idsite": "load/loadvrm/load_vrm_idsite", - "load/provider_settings/LoadVrm/load_vrm_token": "load/loadvrm/load_vrm_token", - "load/provider_settings/LoadImport/import_file_path": "load/loadimport/import_file_path", - "load/provider_settings/LoadImport/import_json": "load/loadimport/import_json", - # 0.1.0 -> 0.2.0+dev - "devices/batteries/0/initial_soc_percentage": None, - "devices/electric_vehicles/0/initial_soc_percentage": None, "elecprice/provider_settings/import_file_path": "elecprice/elecpriceimport/import_file_path", "elecprice/provider_settings/import_json": "elecprice/elecpriceimport/import_json", + # feedintariff + # ============ + # - FeedInTariffFixed + "feedintariff/provider_settings/FeedInTariffFixed/feed_in_tariff_kwh": "feedintariff/feedintarifffixed/feed_in_tariff_kwh", + # - FeedInTariffImport + "feedintariff/provider_settings/FeedInTariffImport/import_file_path": "feedintariff/feedintariffimport/import_file_path", + "feedintariff/provider_settings/FeedInTariffImport/import_json": "feedintariff/feedintariffimport/import_json", + # general + # ======= + "general/data_folder_path": ("general/data_folder_path", _KEEP_DEFAULT), + # load + # ==== + # - LoadAkkudoktor + "load/provider_settings/LoadAkkudoktor/loadakkudoktor_year_energy_kwh": "load/loadakkudoktor/loadakkudoktor_year_energy_kwh", + "load/provider_settings/loadakkudoktor_year_energy": "load/loadakkudoktor/loadakkudoktor_year_energy_kwh", + # - LoadVrm + "load/loadvrm/load_vrm_idsite": "load/vrm/site_id", + "load/loadvrm/load_vrm_token": "load/vrm/token", + "load/provider_settings/load_vrm_idsite": "load/vrm/site_id", + "load/provider_settings/load_vrm_token": "load/vrm/token", + "load/provider_settings/LoadVrm/load_vrm_idsite": "load/vrm/site_id", + "load/provider_settings/LoadVrm/load_vrm_token": "load/vrm/token", + # - LoadImport + "load/provider_settings/LoadImport/import_file_path": "load/loadimport/import_file_path", + "load/provider_settings/LoadImport/import_json": "load/loadimport/import_json", "load/provider_settings/import_file_path": "load/loadimport/import_file_path", "load/provider_settings/import_json": "load/loadimport/import_json", - "load/provider_settings/loadakkudoktor_year_energy": "load/loadakkudoktor/loadakkudoktor_year_energy_kwh", - "load/provider_settings/load_vrm_idsite": "load/loadvrm/load_vrm_idsite", - "load/provider_settings/load_vrm_token": "load/loadvrm/load_vrm_token", + # logging + # ======= "logging/level": "logging/console_level", "logging/root_level": None, + # measurement + # =========== "measurement/load0_name": "measurement/load_emr_keys/0", "measurement/load1_name": "measurement/load_emr_keys/1", "measurement/load2_name": "measurement/load_emr_keys/2", "measurement/load3_name": "measurement/load_emr_keys/3", "measurement/load4_name": "measurement/load_emr_keys/4", + # optimization + # ============ "optimization/ev_available_charge_rates_percent": ( "devices/electric_vehicles/0/charge_rates", lambda v: [x / 100 for x in v], ), "optimization/hours": "optimization/horizon_hours", "optimization/penalty": ("optimization/genetic/penalties/ev_soc_miss", lambda v: float(v)), - "pvforecast/provider_settings/import_file_path": "pvforecast/provider_settings/PVForecastImport/import_file_path", - "pvforecast/provider_settings/import_json": "pvforecast/provider_settings/PVForecastImport/import_json", - "pvforecast/provider_settings/load_vrm_idsite": "pvforecast/provider_settings/PVForecastVrm/load_vrm_idsite", - "pvforecast/provider_settings/load_vrm_token": "pvforecast/provider_settings/PVForecastVrm/load_vrm_token", - "weather/provider_settings/import_file_path": "weather/provider_settings/WeatherImport/import_file_path", - "weather/provider_settings/import_json": "weather/provider_settings/WeatherImport/import_json", + # pvforecast + # ========== + # - PVForecastAkkudoktor + # - PVForecastVrm + "pvforecast/provider_settings/PVForecastVrm/pvforecast_vrm_token": "pvforecast/vrm/token", + "pvforecast/provider_settings/PVForecastVrm/pvforecast_vrm_idsite": "pvforecast/vrm/site_id", + "pvforecast/provider_settings/load_vrm_idsite": "pvforecast/vrm/site_id", + "pvforecast/provider_settings/load_vrm_token": "pvforecast/vrm/token", + # - PVForecastPVNode + "pvforecast/provider_settings/PVForecastPVNode/api_key": "pvforecast/pvnode/api_key", + "pvforecast/provider_settings/PVForecastPVNode/site_id": "pvforecast/pvnode/site_id", + "pvforecast/provider_settings/PVForecastPVNode/forecast_days": "pvforecast/pvnode/forecast_days", + # - PVForecastForecastSolar + "pvforecast/provider_settings/PVForecastForecastSolar/api_key": "pvforecast/forecastsolar/api_key", + # - PVForecastSolcast + "pvforecast/provider_settings/PVForecastSolcast/api_key": "pvforecast/solcast/api_key", + "pvforecast/provider_settings/PVForecastSolcast/site_id": "pvforecast/solcast/site_id", + # - PVForecastImport + "pvforecast/provider_settings/PVForecastImport/import_file_path": "pvforecast/pvforecastimport/import_file_path", + "pvforecast/provider_settings/PVForecastImport/import_json": "pvforecast/pvforecastimport/import_json", + "pvforecast/provider_settings/import_file_path": "pvforecast/pvforecastimport/import_file_path", + "pvforecast/provider_settings/import_json": "pvforecast/pvforecastimport/import_json", + # weather + # ======= + # - WeatherImport + "weather/provider_settings/WeatherImport/import_file_path": "weather/weatherimport/import_file_path", + "weather/provider_settings/WeatherImport/import_json": "weather/weatherimport/import_json", + "weather/provider_settings/import_file_path": "weather/weatherimport/import_file_path", + "weather/provider_settings/import_json": "weather/weatherimport/import_json", } # ----------------------------- diff --git a/src/akkudoktoreos/optimization/genetic/geneticparams.py b/src/akkudoktoreos/optimization/genetic/geneticparams.py index b717f6c0..5802b294 100644 --- a/src/akkudoktoreos/optimization/genetic/geneticparams.py +++ b/src/akkudoktoreos/optimization/genetic/geneticparams.py @@ -392,10 +392,8 @@ class GeneticOptimizationParameters( { "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": { - "feed_in_tariff_kwh": 0.078, - }, + "feedintarifffixed": { + "feed_in_tariff_kwh": 0.078, }, }, } diff --git a/src/akkudoktoreos/prediction/elecprice.py b/src/akkudoktoreos/prediction/elecprice.py index c9dfdc6a..84ece408 100644 --- a/src/akkudoktoreos/prediction/elecprice.py +++ b/src/akkudoktoreos/prediction/elecprice.py @@ -70,7 +70,7 @@ class ElecPriceCommonSettings(SettingsBaseModel): elecpriceimport: ElecPriceImportCommonSettings = Field( default_factory=ElecPriceImportCommonSettings, - json_schema_extra={"description": "Import provider settings."}, + json_schema_extra={"description": "Electricity price import provider settings."}, ) energycharts: ElecPriceEnergyChartsCommonSettings = Field( diff --git a/src/akkudoktoreos/prediction/feedintariff.py b/src/akkudoktoreos/prediction/feedintariff.py index 095970e8..7e1ade4e 100644 --- a/src/akkudoktoreos/prediction/feedintariff.py +++ b/src/akkudoktoreos/prediction/feedintariff.py @@ -25,19 +25,6 @@ def feedintariff_provider_ids() -> list[str]: ] -class FeedInTariffCommonProviderSettings(SettingsBaseModel): - """Feed In Tariff Prediction Provider Configuration.""" - - FeedInTariffFixed: Optional[FeedInTariffFixedCommonSettings] = Field( - default=None, - json_schema_extra={"description": "FeedInTariffFixed settings", "examples": [None]}, - ) - FeedInTariffImport: Optional[FeedInTariffImportCommonSettings] = Field( - default=None, - json_schema_extra={"description": "FeedInTariffImport settings", "examples": [None]}, - ) - - class FeedInTariffCommonSettings(SettingsBaseModel): """Feed In Tariff Prediction Configuration.""" @@ -49,18 +36,14 @@ class FeedInTariffCommonSettings(SettingsBaseModel): }, ) - provider_settings: FeedInTariffCommonProviderSettings = Field( - default_factory=FeedInTariffCommonProviderSettings, - json_schema_extra={ - "description": "Provider settings", - "examples": [ - # Example 1: Empty/default settings (all providers None) - { - "FeedInTariffFixed": None, - "FeedInTariffImport": None, - }, - ], - }, + feedintarifffixed: FeedInTariffFixedCommonSettings = Field( + default_factory=FeedInTariffFixedCommonSettings, + json_schema_extra={"description": "Fixed feed in tariff provider settings."}, + ) + + feedintariffimport: FeedInTariffImportCommonSettings = Field( + default_factory=FeedInTariffImportCommonSettings, + json_schema_extra={"description": "Feed in tarif import provider settings."}, ) @computed_field # type: ignore[prop-decorator] diff --git a/src/akkudoktoreos/prediction/feedintarifffixed.py b/src/akkudoktoreos/prediction/feedintarifffixed.py index 45566803..536bee1c 100644 --- a/src/akkudoktoreos/prediction/feedintarifffixed.py +++ b/src/akkudoktoreos/prediction/feedintarifffixed.py @@ -37,9 +37,7 @@ class FeedInTariffFixed(FeedInTariffProvider): async def _update_data(self, force_update: Optional[bool] = False) -> None: error_msg = "Feed in tariff not provided" try: - feed_in_tariff = ( - self.config.feedintariff.provider_settings.FeedInTariffFixed.feed_in_tariff_kwh - ) + feed_in_tariff = self.config.feedintariff.feedintarifffixed.feed_in_tariff_kwh except Exception: logger.exception(error_msg) raise ValueError(error_msg) diff --git a/src/akkudoktoreos/prediction/feedintariffimport.py b/src/akkudoktoreos/prediction/feedintariffimport.py index 10e51ca4..16733f75 100644 --- a/src/akkudoktoreos/prediction/feedintariffimport.py +++ b/src/akkudoktoreos/prediction/feedintariffimport.py @@ -67,16 +67,16 @@ class FeedInTariffImport(FeedInTariffProvider, PredictionImportProvider): async def _update_data(self, force_update: Optional[bool] = False) -> None: # Both _sequence_lock and _record_lock are already held by the caller. # Use internal sync methods only — never await public async counterparts. - if self.config.feedintariff.provider_settings.FeedInTariffImport is None: + if self.config.feedintariff.feedintariffimport is None: logger.debug(f"{self.provider_id()} data update without provider settings.") return - if self.config.feedintariff.provider_settings.FeedInTariffImport.import_file_path: + if self.config.feedintariff.feedintariffimport.import_file_path: await self._import_from_file( - self.config.provider_settings.FeedInTariffImport.import_file_path, + self.config.feedintariffimport.import_file_path, key_prefix="feedintariff", ) - if self.config.feedintariff.provider_settings.FeedInTariffImport.import_json: + if self.config.feedintariff.feedintariffimport.import_json: await self._import_from_json( - self.config.feedintariff.provider_settings.FeedInTariffImport.import_json, + self.config.feedintariff.feedintariffimport.import_json, key_prefix="feedintariff", ) diff --git a/src/akkudoktoreos/prediction/load.py b/src/akkudoktoreos/prediction/load.py index 6fe5b2bc..42295265 100644 --- a/src/akkudoktoreos/prediction/load.py +++ b/src/akkudoktoreos/prediction/load.py @@ -44,16 +44,16 @@ class LoadCommonSettings(SettingsBaseModel): json_schema_extra={"description": "LoadAkkudoktor provider settings."}, ) - loadvrm: LoadVrmCommonSettings = Field( - default_factory=LoadVrmCommonSettings, - json_schema_extra={"description": "LoadVrm provider settings."}, - ) - loadimport: LoadImportCommonSettings = Field( default_factory=LoadImportCommonSettings, json_schema_extra={"description": "LoadImport provider settings."}, ) + vrm: LoadVrmCommonSettings = Field( + default_factory=LoadVrmCommonSettings, + json_schema_extra={"description": "Victron Remote Management (VRM) provider settings."}, + ) + @computed_field # type: ignore[prop-decorator] @property def providers(self) -> list[str]: diff --git a/src/akkudoktoreos/prediction/loadvrm.py b/src/akkudoktoreos/prediction/loadvrm.py index 9e0ee598..f2983a23 100644 --- a/src/akkudoktoreos/prediction/loadvrm.py +++ b/src/akkudoktoreos/prediction/loadvrm.py @@ -26,14 +26,14 @@ class VrmForecastResponse(PydanticBaseModel): class LoadVrmCommonSettings(SettingsBaseModel): """Common settings for load forecast VRM API.""" - load_vrm_token: str = Field( + token: str = Field( default="your-token", json_schema_extra={ - "description": "Token for Connecting VRM API", + "description": "Access token for connecting to the Victron Remote Management (VRM) API", "examples": ["your-token"], }, ) - load_vrm_idsite: int = Field( + site_id: int = Field( default=12345, json_schema_extra={"description": "VRM-Installation-ID", "examples": [12345]} ) @@ -62,9 +62,9 @@ class LoadVrm(LoadProvider): def _request_forecast(self, start_ts: int, end_ts: int) -> VrmForecastResponse: """Fetch forecast data from Victron VRM API.""" base_url = "https://vrmapi.victronenergy.com/v2/installations" - vrm_settings = self.config.load.loadvrm - installation_id = vrm_settings.load_vrm_idsite - api_token = vrm_settings.load_vrm_token + vrm_settings = self.config.load.vrm + installation_id = vrm_settings.site_id + api_token = vrm_settings.token url = f"{base_url}/{installation_id}/stats?type=forecast&start={start_ts}&end={end_ts}&interval=hours" headers = {"X-Authorization": f"Token {api_token}", "Content-Type": "application/json"} diff --git a/src/akkudoktoreos/prediction/pvforecast.py b/src/akkudoktoreos/prediction/pvforecast.py index 6816f9a8..0f752959 100644 --- a/src/akkudoktoreos/prediction/pvforecast.py +++ b/src/akkudoktoreos/prediction/pvforecast.py @@ -180,31 +180,6 @@ class PVForecastPlaneSetting(SettingsBaseModel): return pvtechchoice -class PVForecastCommonProviderSettings(SettingsBaseModel): - """PV Forecast Provider Configuration.""" - - PVForecastImport: Optional[PVForecastImportCommonSettings] = Field( - default=None, - json_schema_extra={"description": "PVForecastImport settings", "examples": [None]}, - ) - PVForecastVrm: Optional[PVForecastVrmCommonSettings] = Field( - default=None, - json_schema_extra={"description": "PVForecastVrm settings", "examples": [None]}, - ) - PVForecastPVNode: Optional[PVForecastPVNodeCommonSettings] = Field( - default=None, - json_schema_extra={"description": "PVForecastPVNode settings", "examples": [None]}, - ) - PVForecastForecastSolar: Optional[PVForecastForecastSolarCommonSettings] = Field( - default=None, - json_schema_extra={"description": "PVForecastForecastSolar settings", "examples": [None]}, - ) - PVForecastSolcast: Optional[PVForecastSolcastCommonSettings] = Field( - default=None, - json_schema_extra={"description": "PVForecastSolcast settings", "examples": [None]}, - ) - - class PVForecastCommonSettings(SettingsBaseModel): """PV Forecast Configuration.""" @@ -221,18 +196,29 @@ class PVForecastCommonSettings(SettingsBaseModel): }, ) - provider_settings: PVForecastCommonProviderSettings = Field( - default_factory=PVForecastCommonProviderSettings, - json_schema_extra={ - "description": "Provider settings", - "examples": [ - # Example 1: Empty/default settings (all providers None) - { - "PVForecastImport": None, - "PVForecastVrm": None, - }, - ], - }, + pvforecastimport: PVForecastImportCommonSettings = Field( + default_factory=PVForecastImportCommonSettings, + json_schema_extra={"description": "PV forecast import provider settings"}, + ) + + vrm: PVForecastVrmCommonSettings = Field( + default_factory=PVForecastVrmCommonSettings, + json_schema_extra={"description": "Victron Remote Management (VRM) provider settings"}, + ) + + pvnode: PVForecastPVNodeCommonSettings = Field( + default_factory=PVForecastPVNodeCommonSettings, + json_schema_extra={"description": "PVNode provider settings"}, + ) + + forecastsolar: PVForecastForecastSolarCommonSettings = Field( + default_factory=PVForecastForecastSolarCommonSettings, + json_schema_extra={"description": "ForecastSolar provider settings"}, + ) + + solcast: PVForecastSolcastCommonSettings = Field( + default_factory=PVForecastSolcastCommonSettings, + json_schema_extra={"description": "Solcast provider settings"}, ) planes: Optional[list[PVForecastPlaneSetting]] = Field( diff --git a/src/akkudoktoreos/prediction/pvforecastforecastsolar.py b/src/akkudoktoreos/prediction/pvforecastforecastsolar.py index f8f9ca30..4ef56a85 100644 --- a/src/akkudoktoreos/prediction/pvforecastforecastsolar.py +++ b/src/akkudoktoreos/prediction/pvforecastforecastsolar.py @@ -62,7 +62,7 @@ class PVForecastForecastSolar(PVForecastProvider): @property def _api_key(self) -> Optional[str]: - settings = self.config.pvforecast.provider_settings.PVForecastForecastSolar + settings = self.config.pvforecast.forecastsolar return settings.api_key if settings is not None else None def _to_utc_datetime(self, local_ts: Any, iana_tz: Optional[str]) -> Any: diff --git a/src/akkudoktoreos/prediction/pvforecastimport.py b/src/akkudoktoreos/prediction/pvforecastimport.py index 70b46675..ee8cd0ee 100644 --- a/src/akkudoktoreos/prediction/pvforecastimport.py +++ b/src/akkudoktoreos/prediction/pvforecastimport.py @@ -67,16 +67,16 @@ class PVForecastImport(PVForecastProvider, PredictionImportProvider): async def _update_data(self, force_update: Optional[bool] = False) -> None: # Both _sequence_lock and _record_lock are already held by the caller. # Use internal sync methods only — never await public async counterparts. - if self.config.pvforecast.provider_settings.PVForecastImport is None: + if self.config.pvforecast.pvforecastimport is None: logger.debug(f"{self.provider_id()} data update without provider settings.") return - if self.config.pvforecast.provider_settings.PVForecastImport.import_file_path is not None: + if self.config.pvforecast.pvforecastimport.import_file_path is not None: await self._import_from_file( - self.config.pvforecast.provider_settings.PVForecastImport.import_file_path, + self.config.pvforecast.pvforecastimport.import_file_path, key_prefix="pvforecast", ) - if self.config.pvforecast.provider_settings.PVForecastImport.import_json is not None: + if self.config.pvforecast.pvforecastimport.import_json is not None: await self._import_from_json( - self.config.pvforecast.provider_settings.PVForecastImport.import_json, + self.config.pvforecast.pvforecastimport.import_json, key_prefix="pvforecast", ) diff --git a/src/akkudoktoreos/prediction/pvforecastpvnode.py b/src/akkudoktoreos/prediction/pvforecastpvnode.py index 611f2baa..a9cd19ac 100644 --- a/src/akkudoktoreos/prediction/pvforecastpvnode.py +++ b/src/akkudoktoreos/prediction/pvforecastpvnode.py @@ -16,7 +16,7 @@ null is treated as 0 W so the optimizer's linear resampling does not interpolate phantom production across the night. Notes: - - Requires ``pvforecast.provider_settings.PVForecastPVNode.api_key`` (Bearer auth). + - Requires ``pvforecast.pvnode.api_key`` (Bearer auth). - API: https://api.pvnode.com/v2 (15-minute resolution). """ @@ -81,7 +81,7 @@ class PVForecastPVNode(PVForecastProvider): @property def _settings(self) -> PVForecastPVNodeCommonSettings: - settings = self.config.pvforecast.provider_settings.PVForecastPVNode + settings = self.config.pvforecast.pvnode if settings is None: settings = PVForecastPVNodeCommonSettings() return settings @@ -158,7 +158,7 @@ class PVForecastPVNode(PVForecastProvider): settings = self._settings api_key = settings.api_key if not api_key: - raise ValueError("PVForecastPVNode requires pvforecast...PVForecastPVNode.api_key") + raise ValueError("PVForecastPVNode requires pvforecast.pvnode.api_key") headers = {"Authorization": f"Bearer {api_key}", "Accept": "application/json"} params = {"forecast_days": str(settings.forecast_days)} @@ -189,7 +189,7 @@ class PVForecastPVNode(PVForecastProvider): if latitude is None or longitude is None: raise ValueError( "PVForecastPVNode inline mode needs general.latitude/longitude " - "(or set pvforecast...PVForecastPVNode.site_id)" + "(or set pvforecast.pvnode.site_id)" ) planes = self.config.pvforecast.planes or [] strings = [] diff --git a/src/akkudoktoreos/prediction/pvforecastsolcast.py b/src/akkudoktoreos/prediction/pvforecastsolcast.py index 7318aad8..3436ea56 100644 --- a/src/akkudoktoreos/prediction/pvforecastsolcast.py +++ b/src/akkudoktoreos/prediction/pvforecastsolcast.py @@ -63,7 +63,7 @@ class PVForecastSolcast(PVForecastProvider): @property def _settings(self) -> PVForecastSolcastCommonSettings: - settings = self.config.pvforecast.provider_settings.PVForecastSolcast + settings = self.config.pvforecast.solcast if settings is None: settings = PVForecastSolcastCommonSettings() return settings diff --git a/src/akkudoktoreos/prediction/pvforecastvrm.py b/src/akkudoktoreos/prediction/pvforecastvrm.py index 6943b5db..810d30b7 100644 --- a/src/akkudoktoreos/prediction/pvforecastvrm.py +++ b/src/akkudoktoreos/prediction/pvforecastvrm.py @@ -1,4 +1,4 @@ -"""Retrieves pvforecast data from VRM API.""" +"""Retrieves pvforecast data from Victron Remote Management (VRM) API.""" from typing import Any, Optional, Union @@ -26,14 +26,14 @@ class VrmForecastResponse(PydanticBaseModel): class PVForecastVrmCommonSettings(SettingsBaseModel): """Common settings for PV forecast VRM API.""" - pvforecast_vrm_token: str = Field( + token: str = Field( default="your-token", json_schema_extra={ - "description": "Token for Connecting VRM API", + "description": "Access token for connecting to the Victron Remote Management (VRM) API", "examples": ["your-token"], }, ) - pvforecast_vrm_idsite: int = Field( + site_id: int = Field( default=12345, json_schema_extra={"description": "VRM-Installation-ID", "examples": [12345]} ) @@ -63,8 +63,8 @@ class PVForecastVrm(PVForecastProvider): def _request_forecast(self, start_ts: int, end_ts: int) -> VrmForecastResponse: """Fetch forecast data from Victron VRM API.""" source = "https://vrmapi.victronenergy.com/v2/installations" - id_site = self.config.pvforecast.provider_settings.PVForecastVrm.pvforecast_vrm_idsite - api_token = self.config.pvforecast.provider_settings.PVForecastVrm.pvforecast_vrm_token + id_site = self.config.pvforecast.vrm.site_id + api_token = self.config.pvforecast.vrm.token headers = {"X-Authorization": f"Token {api_token}", "Content-Type": "application/json"} url = f"{source}/{id_site}/stats?type=forecast&start={start_ts}&end={end_ts}&interval=hours" logger.debug(f"Requesting VRM forecast: {url}") diff --git a/src/akkudoktoreos/prediction/weather.py b/src/akkudoktoreos/prediction/weather.py index 65b8d839..c90d63d0 100644 --- a/src/akkudoktoreos/prediction/weather.py +++ b/src/akkudoktoreos/prediction/weather.py @@ -26,15 +26,6 @@ def weather_provider_ids() -> list[str]: ] -class WeatherCommonProviderSettings(SettingsBaseModel): - """Weather Forecast Provider Configuration.""" - - WeatherImport: Optional[WeatherImportCommonSettings] = Field( - default=None, - json_schema_extra={"description": "WeatherImport settings", "examples": [None]}, - ) - - class WeatherCommonSettings(SettingsBaseModel): """Weather Forecast Configuration.""" @@ -46,17 +37,9 @@ class WeatherCommonSettings(SettingsBaseModel): }, ) - provider_settings: WeatherCommonProviderSettings = Field( - default_factory=WeatherCommonProviderSettings, - json_schema_extra={ - "description": "Provider settings", - "examples": [ - # Example 1: Empty/default settings (all providers None) - { - "WeatherImport": None, - }, - ], - }, + weatherimport: WeatherImportCommonSettings = Field( + default_factory=WeatherImportCommonSettings, + json_schema_extra={"description": "Weather import provider settings"}, ) @computed_field # type: ignore[prop-decorator] diff --git a/src/akkudoktoreos/prediction/weatherimport.py b/src/akkudoktoreos/prediction/weatherimport.py index 5d0d6de3..ef8a84b5 100644 --- a/src/akkudoktoreos/prediction/weatherimport.py +++ b/src/akkudoktoreos/prediction/weatherimport.py @@ -67,16 +67,16 @@ class WeatherImport(WeatherProvider, PredictionImportProvider): async def _update_data(self, force_update: Optional[bool] = False) -> None: # Both _sequence_lock and _record_lock are already held by the caller. # Use internal sync methods only — never await public async counterparts. - if self.config.weather.provider_settings.WeatherImport is None: + if self.config.weather.weatherimport is None: logger.debug(f"{self.provider_id()} data update without provider settings.") return - if self.config.weather.provider_settings.WeatherImport.import_file_path: + if self.config.weather.weatherimport.import_file_path: await self._import_from_file( - self.config.weather.provider_settings.WeatherImport.import_file_path, + self.config.weather.weatherimport.import_file_path, key_prefix="weather", ) - if self.config.weather.provider_settings.WeatherImport.import_json: + if self.config.weather.weatherimport.import_json: await self._import_from_json( - self.config.weather.provider_settings.WeatherImport.import_json, + self.config.weather.weatherimport.import_json, key_prefix="weather", ) diff --git a/src/akkudoktoreos/server/dash/prediction.py b/src/akkudoktoreos/server/dash/prediction.py index cfc5dcc4..15e208ff 100644 --- a/src/akkudoktoreos/server/dash/prediction.py +++ b/src/akkudoktoreos/server/dash/prediction.py @@ -177,7 +177,7 @@ def LoadForecast(predictions: pd.DataFrame, config: dict, date_time_tz: str, dar "date_time", "loadforecast_power_w", source=source, - legend_label="Load forcast value (adjusted by measurement)", + legend_label="Load forecast value (adjusted by measurement)", color="red", ) plot.line( diff --git a/src/akkudoktoreos/server/eos.py b/src/akkudoktoreos/server/eos.py index 0c97edd8..c51d96d2 100755 --- a/src/akkudoktoreos/server/eos.py +++ b/src/akkudoktoreos/server/eos.py @@ -1314,7 +1314,7 @@ async def fastapi_gesamtlast(request: GesamtlastRequest) -> list[float]: detail=f"Can not update predictions: {e}", ) - # Get the forcast starting at start of day + # Get the forecast starting at start of day start_datetime = to_datetime().start_of("day") end_datetime = start_datetime.add(days=2) try: @@ -1374,7 +1374,7 @@ async def fastapi_gesamtlast_simple(year_energy: float) -> list[float]: detail=f"Can not update predictions: {e}", ) - # Get the forcast starting at start of day + # Get the forecast starting at start of day start_datetime = to_datetime().start_of("day") end_datetime = start_datetime.add(days=2) try: @@ -1430,7 +1430,7 @@ async def fastapi_pvforecast() -> ForecastResponse: detail=f"Can not update predictions: {e}", ) - # Get the forcast starting at start of day + # Get the forecast starting at start of day start_datetime = to_datetime().start_of("day") end_datetime = start_datetime.add(days=2) try: diff --git a/tests/test_feedintarifffixed.py b/tests/test_feedintarifffixed.py index ca7b6e4f..69975e00 100644 --- a/tests/test_feedintarifffixed.py +++ b/tests/test_feedintarifffixed.py @@ -16,10 +16,8 @@ def provider(config_eos): settings = { "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": { - "feed_in_tariff_kwh": 0.078, - }, + "feedintarifffixed": { + "feed_in_tariff_kwh": 0.078, }, } } @@ -46,10 +44,8 @@ def test_invalid_provider(provider, config_eos): settings = { "feedintariff": { "provider": "", - "provider_settings": { - "FeedInTariffFixed": { - "feed_in_tariff_kwh": 0.078, - }, + "feedintarifffixed": { + "feed_in_tariff_kwh": 0.078, }, } } diff --git a/tests/test_loadvrm.py b/tests/test_loadvrm.py index 062b148a..eb0635fa 100644 --- a/tests/test_loadvrm.py +++ b/tests/test_loadvrm.py @@ -19,9 +19,9 @@ def load_vrm_instance(config_eos): settings = { "load": { "provider": "LoadVrm", - "loadvrm": { - "load_vrm_token": "dummy-token", - "load_vrm_idsite": 12345, + "vrm": { + "token": "dummy-token", + "site_id": 12345, }, } } diff --git a/tests/test_pvforecastforecastsolar.py b/tests/test_pvforecastforecastsolar.py index b6a16a33..2cb055e9 100644 --- a/tests/test_pvforecastforecastsolar.py +++ b/tests/test_pvforecastforecastsolar.py @@ -12,10 +12,10 @@ def _config(config_eos, planes=None, api_key=None): "general": {"latitude": 52.5, "longitude": 13.4}, "pvforecast": { "provider": "PVForecastForecastSolar", + "forecastsolar": {"api_key": api_key}, "planes": planes if planes is not None else [{"surface_tilt": 30.0, "surface_azimuth": 180.0, "peakpower": 5.0}], - "provider_settings": {"PVForecastForecastSolar": {"api_key": api_key}}, }, } config_eos.merge_settings_from_dict(settings) diff --git a/tests/test_pvforecastimport.py b/tests/test_pvforecastimport.py index b2ac4c20..a45c4450 100644 --- a/tests/test_pvforecastimport.py +++ b/tests/test_pvforecastimport.py @@ -19,11 +19,9 @@ def provider(sample_import_1_json, config_eos): settings = { "pvforecast": { "provider": "PVForecastImport", - "provider_settings": { - "PVForecastImport": { - "import_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON), - "import_json": json.dumps(sample_import_1_json), - }, + "pvforecastimport": { + "import_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON), + "import_json": json.dumps(sample_import_1_json), }, } } @@ -57,10 +55,8 @@ def test_invalid_provider(provider, config_eos): settings = { "pvforecast": { "provider": "", - "provider_settings": { - "PVForecastImport": { - "import_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON), - }, + "pvforecastimport": { + "import_file_path": str(FILE_TESTDATA_PVFORECASTIMPORT_1_JSON), }, } } @@ -92,11 +88,11 @@ async def test_import(provider, sample_import_1_json, start_datetime, from_file, ems_eos = get_ems() ems_eos.set_start_datetime(to_datetime(start_datetime, in_timezone="Europe/Berlin")) if from_file: - config_eos.pvforecast.provider_settings.PVForecastImport.import_json = None - assert config_eos.pvforecast.provider_settings.PVForecastImport.import_json is None + config_eos.pvforecast.pvforecastimport.import_json = None + assert config_eos.pvforecast.pvforecastimport.import_json is None else: - config_eos.pvforecast.provider_settings.PVForecastImport.import_file_path = None - assert config_eos.pvforecast.provider_settings.PVForecastImport.import_file_path is None + config_eos.pvforecast.pvforecastimport.import_file_path = None + assert config_eos.pvforecast.pvforecastimport.import_file_path is None await provider.delete_by_datetime(start_datetime=None, end_datetime=None) # Call the method diff --git a/tests/test_pvforecastpvnode.py b/tests/test_pvforecastpvnode.py index 397c1033..a9bd081b 100644 --- a/tests/test_pvforecastpvnode.py +++ b/tests/test_pvforecastpvnode.py @@ -13,12 +13,10 @@ def pvforecast_instance(config_eos): "general": {"latitude": 52.5, "longitude": 13.4}, "pvforecast": { "provider": "PVForecastPVNode", - "provider_settings": { - "PVForecastPVNode": { - "api_key": "dummy-key", - "site_id": "test-site-123", - "forecast_days": 2, - }, + "pvnode": { + "api_key": "dummy-key", + "site_id": "test-site-123", + "forecast_days": 2, }, }, } @@ -122,8 +120,8 @@ def test_request_forecast_inline_post_when_no_site(config_eos): "general": {"latitude": 52.5, "longitude": 13.4}, "pvforecast": { "provider": "PVForecastPVNode", + "pvnode": {"api_key": "k", "site_id": None}, "planes": [{"surface_tilt": 30.0, "surface_azimuth": 180.0, "peakpower": 5.0}], - "provider_settings": {"PVForecastPVNode": {"api_key": "k", "site_id": None}}, }, } ) diff --git a/tests/test_pvforecastsolcast.py b/tests/test_pvforecastsolcast.py index ddc5d008..8fa589c9 100644 --- a/tests/test_pvforecastsolcast.py +++ b/tests/test_pvforecastsolcast.py @@ -12,9 +12,7 @@ def pvforecast_instance(config_eos): settings = { "pvforecast": { "provider": "PVForecastSolcast", - "provider_settings": { - "PVForecastSolcast": {"api_key": "dummy-key", "site_id": "site-abc"}, - }, + "solcast": {"api_key": "dummy-key", "site_id": "site-abc"}, }, } config_eos.merge_settings_from_dict(settings) diff --git a/tests/test_pvforecastvrm.py b/tests/test_pvforecastvrm.py index 00bb1e8a..b42cbbd4 100644 --- a/tests/test_pvforecastvrm.py +++ b/tests/test_pvforecastvrm.py @@ -17,13 +17,10 @@ def pvforecast_instance(config_eos): # Settings for PVForecastVrm settings = { "pvforecast": { - "provider": "PVForecastVrm", - "provider_settings": { - "PVForecastVrm": { - "pvforecast_vrm_token": "dummy-token", - "pvforecast_vrm_idsite": 12345, - }, - } + "vrm": { + "token": "dummy-token", + "site_id": 12345, + }, } } config_eos.merge_settings_from_dict(settings) diff --git a/tests/test_weatherimport.py b/tests/test_weatherimport.py index 8c3990d3..3f85efa9 100644 --- a/tests/test_weatherimport.py +++ b/tests/test_weatherimport.py @@ -19,11 +19,9 @@ def provider(sample_import_1_json, config_eos): settings = { "weather": { "provider": "WeatherImport", - "provider_settings": { - "WeatherImport": { - "import_file_path": str(FILE_TESTDATA_WEATHERIMPORT_1_JSON), - "import_json": json.dumps(sample_import_1_json), - }, + "weatherimport": { + "import_file_path": str(FILE_TESTDATA_WEATHERIMPORT_1_JSON), + "import_json": json.dumps(sample_import_1_json), }, } } @@ -57,10 +55,8 @@ def test_invalid_provider(provider, config_eos, monkeypatch): settings = { "weather": { "provider": "", - "provider_settings": { - "WeatherImport": { - "import_file_path": str(FILE_TESTDATA_WEATHERIMPORT_1_JSON), - }, + "weatherimport": { + "import_file_path": str(FILE_TESTDATA_WEATHERIMPORT_1_JSON), }, } } @@ -93,11 +89,11 @@ async def test_import(provider, sample_import_1_json, start_datetime, from_file, ems_eos = get_ems() ems_eos.set_start_datetime(to_datetime(start_datetime, in_timezone="Europe/Berlin")) if from_file: - config_eos.weather.provider_settings.WeatherImport.import_json = None - assert config_eos.weather.provider_settings.WeatherImport.import_json is None + config_eos.weather.weatherimport.import_json = None + assert config_eos.weather.weatherimport.import_json is None else: - config_eos.weather.provider_settings.WeatherImport.import_file_path = None - assert config_eos.weather.provider_settings.WeatherImport.import_file_path is None + config_eos.weather.weatherimport.import_file_path = None + assert config_eos.weather.weatherimport.import_file_path is None await provider.delete_by_datetime(start_datetime=None, end_datetime=None) # Call the method diff --git a/tests/testdata/docs/_generated/configelecprice.md b/tests/testdata/docs/_generated/configelecprice.md index f2c3f544..0d00431f 100644 --- a/tests/testdata/docs/_generated/configelecprice.md +++ b/tests/testdata/docs/_generated/configelecprice.md @@ -9,7 +9,7 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | charges_kwh | `EOS_ELECPRICE__CHARGES_KWH` | `Optional[float]` | `rw` | `None` | Electricity price charges [amount/kWh]. Will be added to variable market price. | | elecpricefixed | `EOS_ELECPRICE__ELECPRICEFIXED` | `ElecPriceFixedCommonSettings` | `rw` | `required` | Fixed electricity price provider settings. | -| elecpriceimport | `EOS_ELECPRICE__ELECPRICEIMPORT` | `ElecPriceImportCommonSettings` | `rw` | `required` | Import provider settings. | +| elecpriceimport | `EOS_ELECPRICE__ELECPRICEIMPORT` | `ElecPriceImportCommonSettings` | `rw` | `required` | Electricity price import provider settings. | | energycharts | `EOS_ELECPRICE__ENERGYCHARTS` | `ElecPriceEnergyChartsCommonSettings` | `rw` | `required` | Energy Charts provider settings. | | provider | `EOS_ELECPRICE__PROVIDER` | `Optional[str]` | `rw` | `None` | Electricity price provider id of provider to be used. | | providers | | `list[str]` | `ro` | `N/A` | Available electricity price provider ids. | diff --git a/tests/testdata/docs/_generated/configexample.md b/tests/testdata/docs/_generated/configexample.md index 31f6b665..c9557d6a 100644 --- a/tests/testdata/docs/_generated/configexample.md +++ b/tests/testdata/docs/_generated/configexample.md @@ -119,9 +119,12 @@ }, "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": null + }, + "feedintariffimport": { + "import_file_path": null, + "import_json": null } }, "general": { @@ -138,13 +141,13 @@ "loadakkudoktor": { "loadakkudoktor_year_energy_kwh": null }, - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 - }, "loadimport": { "import_file_path": null, "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 } }, "logging": { @@ -185,12 +188,25 @@ }, "pvforecast": { "provider": "PVForecastAkkudoktor", - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, + "pvnode": { + "api_key": "", + "site_id": null, + "forecast_days": 2 + }, + "forecastsolar": { + "api_key": null + }, + "solcast": { + "api_key": "", + "site_id": "" }, "planes": [ { @@ -254,8 +270,9 @@ "utils": {}, "weather": { "provider": "WeatherImport", - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": null } } } diff --git a/tests/testdata/docs/_generated/configfeedintariff.md b/tests/testdata/docs/_generated/configfeedintariff.md index dd855ef1..217b41ac 100644 --- a/tests/testdata/docs/_generated/configfeedintariff.md +++ b/tests/testdata/docs/_generated/configfeedintariff.md @@ -7,8 +7,9 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | +| feedintarifffixed | `EOS_FEEDINTARIFF__FEEDINTARIFFFIXED` | `FeedInTariffFixedCommonSettings` | `rw` | `required` | Fixed feed in tariff provider settings. | +| feedintariffimport | `EOS_FEEDINTARIFF__FEEDINTARIFFIMPORT` | `FeedInTariffImportCommonSettings` | `rw` | `required` | Feed in tarif import provider settings. | | provider | `EOS_FEEDINTARIFF__PROVIDER` | `Optional[str]` | `rw` | `None` | Feed in tariff provider id of provider to be used. | -| provider_settings | `EOS_FEEDINTARIFF__PROVIDER_SETTINGS` | `FeedInTariffCommonProviderSettings` | `rw` | `required` | Provider settings | | providers | | `list[str]` | `ro` | `N/A` | Available feed in tariff provider ids. | ::: @@ -22,9 +23,12 @@ { "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": null + }, + "feedintariffimport": { + "import_file_path": null, + "import_json": null } } } @@ -40,9 +44,12 @@ { "feedintariff": { "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": null + }, + "feedintariffimport": { + "import_file_path": null, + "import_json": null }, "providers": [ "FeedInTariffFixed", @@ -56,7 +63,7 @@ ### Common settings for feed in tariff data import from file or JSON string -:::{table} feedintariff::provider_settings::FeedInTariffImport +:::{table} feedintariff::feedintariffimport :widths: 10 10 5 5 30 :align: left @@ -75,11 +82,9 @@ ```json { "feedintariff": { - "provider_settings": { - "FeedInTariffImport": { - "import_file_path": null, - "import_json": "{\"fead_in_tariff_wh\": [0.000078, 0.000078, 0.000023]}" - } + "feedintariffimport": { + "import_file_path": null, + "import_json": "{\"fead_in_tariff_wh\": [0.000078, 0.000078, 0.000023]}" } } } @@ -89,7 +94,7 @@ ### Common settings for elecprice fixed price -:::{table} feedintariff::provider_settings::FeedInTariffFixed +:::{table} feedintariff::feedintarifffixed :widths: 10 10 5 5 30 :align: left @@ -107,41 +112,8 @@ ```json { "feedintariff": { - "provider_settings": { - "FeedInTariffFixed": { - "feed_in_tariff_kwh": 0.078 - } - } - } - } -``` - - -### Feed In Tariff Prediction Provider Configuration - - -:::{table} feedintariff::provider_settings -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| FeedInTariffFixed | `Optional[akkudoktoreos.prediction.feedintarifffixed.FeedInTariffFixedCommonSettings]` | `rw` | `None` | FeedInTariffFixed settings | -| FeedInTariffImport | `Optional[akkudoktoreos.prediction.feedintariffimport.FeedInTariffImportCommonSettings]` | `rw` | `None` | FeedInTariffImport settings | -::: - - - -**Example Input/Output** - - - -```json - { - "feedintariff": { - "provider_settings": { - "FeedInTariffFixed": null, - "FeedInTariffImport": null + "feedintarifffixed": { + "feed_in_tariff_kwh": 0.078 } } } diff --git a/tests/testdata/docs/_generated/configload.md b/tests/testdata/docs/_generated/configload.md index 95055dad..feec1705 100644 --- a/tests/testdata/docs/_generated/configload.md +++ b/tests/testdata/docs/_generated/configload.md @@ -9,9 +9,9 @@ | ---- | -------------------- | ---- | --------- | ------- | ----------- | | loadakkudoktor | `EOS_LOAD__LOADAKKUDOKTOR` | `LoadAkkudoktorCommonSettings` | `rw` | `required` | LoadAkkudoktor provider settings. | | loadimport | `EOS_LOAD__LOADIMPORT` | `LoadImportCommonSettings` | `rw` | `required` | LoadImport provider settings. | -| loadvrm | `EOS_LOAD__LOADVRM` | `LoadVrmCommonSettings` | `rw` | `required` | LoadVrm provider settings. | | provider | `EOS_LOAD__PROVIDER` | `Optional[str]` | `rw` | `None` | Load provider id of provider to be used. | | providers | | `list[str]` | `ro` | `N/A` | Available load provider ids. | +| vrm | `EOS_LOAD__VRM` | `LoadVrmCommonSettings` | `rw` | `required` | Victron Remote Management (VRM) provider settings. | ::: @@ -27,13 +27,13 @@ "loadakkudoktor": { "loadakkudoktor_year_energy_kwh": null }, - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 - }, "loadimport": { "import_file_path": null, "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 } } } @@ -52,14 +52,14 @@ "loadakkudoktor": { "loadakkudoktor_year_energy_kwh": null }, - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 - }, "loadimport": { "import_file_path": null, "import_json": null }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, "providers": [ "LoadAkkudoktor", "LoadAkkudoktorAdjusted", @@ -74,14 +74,14 @@ ### Common settings for load forecast VRM API -:::{table} load::loadvrm +:::{table} load::vrm :widths: 10 10 5 5 30 :align: left | Name | Type | Read-Only | Default | Description | | ---- | ---- | --------- | ------- | ----------- | -| load_vrm_idsite | `int` | `rw` | `12345` | VRM-Installation-ID | -| load_vrm_token | `str` | `rw` | `your-token` | Token for Connecting VRM API | +| site_id | `int` | `rw` | `12345` | VRM-Installation-ID | +| token | `str` | `rw` | `your-token` | Access token for connecting to the Victron Remote Management (VRM) API | ::: @@ -93,9 +93,9 @@ ```json { "load": { - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 + "vrm": { + "token": "your-token", + "site_id": 12345 } } } diff --git a/tests/testdata/docs/_generated/configpvforecast.md b/tests/testdata/docs/_generated/configpvforecast.md index a5776e00..ce9cf64c 100644 --- a/tests/testdata/docs/_generated/configpvforecast.md +++ b/tests/testdata/docs/_generated/configpvforecast.md @@ -7,6 +7,7 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | +| forecastsolar | `EOS_PVFORECAST__FORECASTSOLAR` | `PVForecastForecastSolarCommonSettings` | `rw` | `required` | ForecastSolar provider settings | | max_planes | `EOS_PVFORECAST__MAX_PLANES` | `Optional[int]` | `rw` | `0` | Maximum number of planes that can be set | | planes | `EOS_PVFORECAST__PLANES` | `Optional[list[akkudoktoreos.prediction.pvforecast.PVForecastPlaneSetting]]` | `rw` | `None` | Plane configuration. | | planes_azimuth | | `List[float]` | `ro` | `N/A` | Compute a list of the azimuths per active planes. | @@ -15,8 +16,11 @@ | planes_tilt | | `List[float]` | `ro` | `N/A` | Compute a list of the tilts per active planes. | | planes_userhorizon | | `Any` | `ro` | `N/A` | Compute a list of the user horizon per active planes. | | provider | `EOS_PVFORECAST__PROVIDER` | `Optional[str]` | `rw` | `None` | PVForecast provider id of provider to be used. | -| provider_settings | `EOS_PVFORECAST__PROVIDER_SETTINGS` | `PVForecastCommonProviderSettings` | `rw` | `required` | Provider settings | | providers | | `list[str]` | `ro` | `N/A` | Available PVForecast provider ids. | +| pvforecastimport | `EOS_PVFORECAST__PVFORECASTIMPORT` | `PVForecastImportCommonSettings` | `rw` | `required` | PV forecast import provider settings | +| pvnode | `EOS_PVFORECAST__PVNODE` | `PVForecastPVNodeCommonSettings` | `rw` | `required` | PVNode provider settings | +| solcast | `EOS_PVFORECAST__SOLCAST` | `PVForecastSolcastCommonSettings` | `rw` | `required` | Solcast provider settings | +| vrm | `EOS_PVFORECAST__VRM` | `PVForecastVrmCommonSettings` | `rw` | `required` | Victron Remote Management (VRM) provider settings | ::: @@ -29,12 +33,25 @@ { "pvforecast": { "provider": "PVForecastAkkudoktor", - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, + "pvnode": { + "api_key": "", + "site_id": null, + "forecast_days": 2 + }, + "forecastsolar": { + "api_key": null + }, + "solcast": { + "api_key": "", + "site_id": "" }, "planes": [ { @@ -97,12 +114,25 @@ { "pvforecast": { "provider": "PVForecastAkkudoktor", - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": null + }, + "vrm": { + "token": "your-token", + "site_id": 12345 + }, + "pvnode": { + "api_key": "", + "site_id": null, + "forecast_days": 2 + }, + "forecastsolar": { + "api_key": null + }, + "solcast": { + "api_key": "", + "site_id": "" }, "planes": [ { @@ -192,10 +222,41 @@ ``` +### Common settings for PV forecast VRM API + + +:::{table} pvforecast::vrm +:widths: 10 10 5 5 30 +:align: left + +| Name | Type | Read-Only | Default | Description | +| ---- | ---- | --------- | ------- | ----------- | +| site_id | `int` | `rw` | `12345` | VRM-Installation-ID | +| token | `str` | `rw` | `your-token` | Access token for connecting to the Victron Remote Management (VRM) API | +::: + + + +**Example Input/Output** + + + +```json + { + "pvforecast": { + "vrm": { + "token": "your-token", + "site_id": 12345 + } + } + } +``` + + ### Common settings for the Solcast PV forecast provider -:::{table} pvforecast::provider_settings::PVForecastSolcast +:::{table} pvforecast::solcast :widths: 10 10 5 5 30 :align: left @@ -214,42 +275,9 @@ ```json { "pvforecast": { - "provider_settings": { - "PVForecastSolcast": { - "api_key": "your-solcast-key", - "site_id": "abcd-1234-efgh-5678" - } - } - } - } -``` - - -### Common settings for the Forecast.Solar PV forecast provider - - -:::{table} pvforecast::provider_settings::PVForecastForecastSolar -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| api_key | `Optional[str]` | `rw` | `None` | Forecast.Solar API key. Optional — the public endpoint works without a key (lower rate limit). | -::: - - - -**Example Input/Output** - - - -```json - { - "pvforecast": { - "provider_settings": { - "PVForecastForecastSolar": { - "api_key": null - } + "solcast": { + "api_key": "your-solcast-key", + "site_id": "abcd-1234-efgh-5678" } } } @@ -259,7 +287,7 @@ ### Common settings for the pvnode.com PV forecast provider -:::{table} pvforecast::provider_settings::PVForecastPVNode +:::{table} pvforecast::pvnode :widths: 10 10 5 5 30 :align: left @@ -279,45 +307,10 @@ ```json { "pvforecast": { - "provider_settings": { - "PVForecastPVNode": { - "api_key": "pvn_live_xxxxxxxxxxxxxxxx", - "site_id": "abcd-1234", - "forecast_days": 2 - } - } - } - } -``` - - -### Common settings for PV forecast VRM API - - -:::{table} pvforecast::provider_settings::PVForecastVrm -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| pvforecast_vrm_idsite | `int` | `rw` | `12345` | VRM-Installation-ID | -| pvforecast_vrm_token | `str` | `rw` | `your-token` | Token for Connecting VRM API | -::: - - - -**Example Input/Output** - - - -```json - { - "pvforecast": { - "provider_settings": { - "PVForecastVrm": { - "pvforecast_vrm_token": "your-token", - "pvforecast_vrm_idsite": 12345 - } + "pvnode": { + "api_key": "pvn_live_xxxxxxxxxxxxxxxx", + "site_id": "abcd-1234", + "forecast_days": 2 } } } @@ -327,7 +320,7 @@ ### Common settings for pvforecast data import from file or JSON string -:::{table} pvforecast::provider_settings::PVForecastImport +:::{table} pvforecast::pvforecastimport :widths: 10 10 5 5 30 :align: left @@ -346,48 +339,9 @@ ```json { "pvforecast": { - "provider_settings": { - "PVForecastImport": { - "import_file_path": null, - "import_json": "{\"pvforecast_ac_power\": [0, 8.05, 352.91]}" - } - } - } - } -``` - - -### PV Forecast Provider Configuration - - -:::{table} pvforecast::provider_settings -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| PVForecastForecastSolar | `Optional[akkudoktoreos.prediction.pvforecastforecastsolar.PVForecastForecastSolarCommonSettings]` | `rw` | `None` | PVForecastForecastSolar settings | -| PVForecastImport | `Optional[akkudoktoreos.prediction.pvforecastimport.PVForecastImportCommonSettings]` | `rw` | `None` | PVForecastImport settings | -| PVForecastPVNode | `Optional[akkudoktoreos.prediction.pvforecastpvnode.PVForecastPVNodeCommonSettings]` | `rw` | `None` | PVForecastPVNode settings | -| PVForecastSolcast | `Optional[akkudoktoreos.prediction.pvforecastsolcast.PVForecastSolcastCommonSettings]` | `rw` | `None` | PVForecastSolcast settings | -| PVForecastVrm | `Optional[akkudoktoreos.prediction.pvforecastvrm.PVForecastVrmCommonSettings]` | `rw` | `None` | PVForecastVrm settings | -::: - - - -**Example Input/Output** - - - -```json - { - "pvforecast": { - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null, - "PVForecastPVNode": null, - "PVForecastForecastSolar": null, - "PVForecastSolcast": null + "pvforecastimport": { + "import_file_path": null, + "import_json": "{\"pvforecast_ac_power\": [0, 8.05, 352.91]}" } } } @@ -458,3 +412,32 @@ } ``` + +### Common settings for the Forecast.Solar PV forecast provider + + +:::{table} pvforecast::forecastsolar +:widths: 10 10 5 5 30 +:align: left + +| Name | Type | Read-Only | Default | Description | +| ---- | ---- | --------- | ------- | ----------- | +| api_key | `Optional[str]` | `rw` | `None` | Forecast.Solar API key. Optional — the public endpoint works without a key (lower rate limit). | +::: + + + +**Example Input/Output** + + + +```json + { + "pvforecast": { + "forecastsolar": { + "api_key": null + } + } + } +``` + diff --git a/tests/testdata/docs/_generated/configweather.md b/tests/testdata/docs/_generated/configweather.md index dee9ad63..024a267c 100644 --- a/tests/testdata/docs/_generated/configweather.md +++ b/tests/testdata/docs/_generated/configweather.md @@ -8,8 +8,8 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | | provider | `EOS_WEATHER__PROVIDER` | `Optional[str]` | `rw` | `None` | Weather provider id of provider to be used. | -| provider_settings | `EOS_WEATHER__PROVIDER_SETTINGS` | `WeatherCommonProviderSettings` | `rw` | `required` | Provider settings | | providers | | `list[str]` | `ro` | `N/A` | Available weather provider ids. | +| weatherimport | `EOS_WEATHER__WEATHERIMPORT` | `WeatherImportCommonSettings` | `rw` | `required` | Weather import provider settings | ::: @@ -22,8 +22,9 @@ { "weather": { "provider": "WeatherImport", - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": null } } } @@ -39,8 +40,9 @@ { "weather": { "provider": "WeatherImport", - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": null }, "providers": [ "BrightSky", @@ -56,7 +58,7 @@ ### Common settings for weather data import from file or JSON string -:::{table} weather::provider_settings::WeatherImport +:::{table} weather::weatherimport :widths: 10 10 5 5 30 :align: left @@ -75,40 +77,9 @@ ```json { "weather": { - "provider_settings": { - "WeatherImport": { - "import_file_path": null, - "import_json": "{\"weather_temp_air\": [18.3, 17.8, 16.9]}" - } - } - } - } -``` - - -### Weather Forecast Provider Configuration - - -:::{table} weather::provider_settings -:widths: 10 10 5 5 30 -:align: left - -| Name | Type | Read-Only | Default | Description | -| ---- | ---- | --------- | ------- | ----------- | -| WeatherImport | `Optional[akkudoktoreos.prediction.weatherimport.WeatherImportCommonSettings]` | `rw` | `None` | WeatherImport settings | -::: - - - -**Example Input/Output** - - - -```json - { - "weather": { - "provider_settings": { - "WeatherImport": null + "weatherimport": { + "import_file_path": null, + "import_json": "{\"weather_temp_air\": [18.3, 17.8, 16.9]}" } } } diff --git a/tests/testdata/eos_config_stripped.json b/tests/testdata/eos_config_stripped.json index 71a66881..a931f291 100644 --- a/tests/testdata/eos_config_stripped.json +++ b/tests/testdata/eos_config_stripped.json @@ -57,12 +57,7 @@ "provider": "ElecPriceAkkudoktor" }, "feedintariff": { - "provider": "FeedInTariffFixed", - "provider_settings": { - "FeedInTariffFixed": { - "feed_in_tariff_kwh": 0.078 - } - } + "provider": "FeedInTariffFixed" }, "load": { "provider": "LoadAkkudoktorAdjusted", diff --git a/tests/testdata/eos_config_unstripped.json b/tests/testdata/eos_config_unstripped.json index c08cbb32..6e0b73da 100644 --- a/tests/testdata/eos_config_unstripped.json +++ b/tests/testdata/eos_config_unstripped.json @@ -1,14 +1,7 @@ { "general": { - "version": "0.2.0.dev2603031877440961", - "data_folder_path": "/home/bobby/.local/share/net.akkudoktor.eos", - "data_output_subpath": "output", - "latitude": 52.52, - "longitude": 13.405, - "timezone": "Europe/Berlin", - "data_output_path": "/home/bobby/.local/share/net.akkudoktor.eos/output", - "config_folder_path": "/home/bobby/.config/net.akkudoktor.eos", - "config_file_path": "/home/bobby/.config/net.akkudoktor.eos/EOS.config.json" + "version": "__ANY__", + "data_output_subpath": "output" }, "cache": { "subpath": "cache", @@ -34,8 +27,7 @@ }, "logging": { "console_level": null, - "file_level": null, - "file_path": "/home/bobby/.local/share/net.akkudoktor.eos/output/eos.log" + "file_level": null }, "devices": { "batteries": [ @@ -302,9 +294,9 @@ "loadakkudoktor": { "loadakkudoktor_year_energy_kwh": 3000 }, - "loadvrm": { - "load_vrm_token": "your-token", - "load_vrm_idsite": 12345 + "vrm": { + "token": "your-token", + "site_id": 12345 }, "loadimport": { "import_file_path": null, @@ -319,10 +311,6 @@ }, "pvforecast": { "provider": "PVForecastAkkudoktor", - "provider_settings": { - "PVForecastImport": null, - "PVForecastVrm": null - }, "planes": [ { "surface_tilt": 7, @@ -476,9 +464,6 @@ }, "weather": { "provider": "BrightSky", - "provider_settings": { - "WeatherImport": null - }, "providers": [ "BrightSky", "ClearOutside", diff --git a/uv.lock b/uv.lock index bd8cc840..345d6c7e 100644 --- a/uv.lock +++ b/uv.lock @@ -84,7 +84,7 @@ requires-dist = [ { name = "cachebox", specifier = "==6.1.2" }, { name = "commitizen", marker = "extra == 'dev'", specifier = "==4.16.5" }, { name = "contourpy", specifier = "==1.3.3" }, - { name = "coverage", marker = "extra == 'dev'", specifier = "==7.15.1" }, + { name = "coverage", marker = "extra == 'dev'", specifier = "==7.15.2" }, { name = "deap", specifier = "==1.4.4" }, { name = "deprecated", marker = "extra == 'dev'", specifier = "==1.3.1" }, { name = "docutils", marker = "extra == 'dev'", specifier = "==0.21.2" }, @@ -791,86 +791,86 @@ wheels = [ [[package]] name = "coverage" -version = "7.15.1" +version = "7.15.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/83/6051c2a2feab48ae5bd27c84ef047191d2d4a3172f689e38eaa48ed17db1/coverage-7.15.1.tar.gz", hash = "sha256:165e9949eaf222ef1f018635d0d7f368a23bfe0212af558534c40d8c04686d67", size = 927640, upload-time = "2026-07-12T20:58:19.908Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d0/55fe630f4cf94e3fcba868240fad8c8cdd1f764e2a932f8926347e6ec4cd/coverage-7.15.2.tar.gz", hash = "sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d", size = 927741, upload-time = "2026-07-15T18:56:19.558Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/67/ee/5095e07a0986930174fc153fd0bb115f7f2724f5344cc672e016d4f23a4e/coverage-7.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6506330b4a8dcf53b95bd84d8d0e817107cdb3fc1438e835029cdf0bc6612eb0", size = 221320, upload-time = "2026-07-12T20:56:16.036Z" }, - { url = "https://files.pythonhosted.org/packages/43/7a/7e2598ce98433a214020a61c0755d5961f9f26df0c630dc73e06b86d3416/coverage-7.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8a51a8ec382c39d939cba0ab07ae949077ae4e842343bd4eed22d432358cea9", size = 221823, upload-time = "2026-07-12T20:56:17.54Z" }, - { url = "https://files.pythonhosted.org/packages/8b/4f/67dac6a69139b490a239d91b6d5ef127982ffb89159769241656ab879ee5/coverage-7.15.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:18ea20e3922d7f8ca9e0ef1084408d08c4ad62d5e531cb9c1f6896a99297ebea", size = 252242, upload-time = "2026-07-12T20:56:18.868Z" }, - { url = "https://files.pythonhosted.org/packages/05/37/5e439725a96de592309725550c8df639c359c209dcb4b152ed46032d4c0d/coverage-7.15.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aab9902a64b8390e3b56e539fddae1d79a267807fe5cb0c18d7d2f544ce867e2", size = 254153, upload-time = "2026-07-12T20:56:20.118Z" }, - { url = "https://files.pythonhosted.org/packages/0f/1c/671ba9e15f9651a99962fb588a1fe4fb267cae4bb85f9bb4ac4413c6f7ef/coverage-7.15.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cc316264317b07a9e90d7f2b4188a15e36e9b54e651081b791b0515fa612a29", size = 256261, upload-time = "2026-07-12T20:56:21.682Z" }, - { url = "https://files.pythonhosted.org/packages/d6/3c/88305322b4d015b4536b7174b8f9b87f850f01af9d89008cdbf984b65ff2/coverage-7.15.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d4e47e7eea81a8ccf060a07627654151d929da62c7b715738387c200905cec89", size = 258222, upload-time = "2026-07-12T20:56:22.962Z" }, - { url = "https://files.pythonhosted.org/packages/8d/20/d02e42333a57f266ded61ed4fb3821da1f2dc143734aaa3dcc9c117204c5/coverage-7.15.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c6829d9a3b55ad2b73ef5fda8302e5be03683789e88b1a079dcf4a773229c21d", size = 252368, upload-time = "2026-07-12T20:56:24.475Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d0/94ebc010926a191d83e83b1f1236f8bd0d14d0eb98b5c324aa4be2589a8e/coverage-7.15.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:764e045811f9c8cda436641f3f088283351d331a519b5807f19041cd0a68da1c", size = 253953, upload-time = "2026-07-12T20:56:26.036Z" }, - { url = "https://files.pythonhosted.org/packages/2e/a6/2c8553191da0c2da2c43ff294fb9bab57a5b0fae03560304a82a8b5addc3/coverage-7.15.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:09b3b088aa24489c4082bcc35fcc8224281ab94a653dfb6d3f0c8165b0d628ab", size = 252016, upload-time = "2026-07-12T20:56:27.374Z" }, - { url = "https://files.pythonhosted.org/packages/17/99/20f47986d8360fa1a31d9858dd2ba0be009d44ecfa8d02120b1eb93c028b/coverage-7.15.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7911b02f57053adf8164ae63edb1c26574d24dfccabadc5268cf69310a69a358", size = 255783, upload-time = "2026-07-12T20:56:28.921Z" }, - { url = "https://files.pythonhosted.org/packages/17/36/fb61983b5259f78fa5e62ee74e91fe4de4c556fcbc9a3b9c9021c2f8b57b/coverage-7.15.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:664e279ed40599b8ed16f4db18d92a7e212c73129672bec8f5d96d4da48d2404", size = 251735, upload-time = "2026-07-12T20:56:30.465Z" }, - { url = "https://files.pythonhosted.org/packages/6c/2e/d109d8d2d6c726ba2e78125297073918a2a91464f0ea777e5398fa3cf75c/coverage-7.15.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34fe7cf79d5f1f87f2e8ce7dc1c32950841f50e10d0120f263856acfad66de34", size = 252645, upload-time = "2026-07-12T20:56:32.076Z" }, - { url = "https://files.pythonhosted.org/packages/bc/a0/5b3219cbb46135e14673427f2bf5890c4da4e6f655243692f3448aa3f42c/coverage-7.15.1-cp311-cp311-win32.whl", hash = "sha256:5e2d2536d2f57a354aa382ed303ac0e2e5c9522a508c05b998d26181b94163a7", size = 223414, upload-time = "2026-07-12T20:56:33.432Z" }, - { url = "https://files.pythonhosted.org/packages/5b/80/24e13ade0b6df8090e2492f9c55044bf1423f7ef28c76fc1af8c827a61cb/coverage-7.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:c337da8fca7ea93ab43f3868cfcde6cf6dad32c3906b273cfbad5d7390bc423b", size = 223888, upload-time = "2026-07-12T20:56:34.784Z" }, - { url = "https://files.pythonhosted.org/packages/2d/9e/34659930ae380491af48e2f5f5f9a2e99cd9fb7de3d30f27be5ac5425137/coverage-7.15.1-cp311-cp311-win_arm64.whl", hash = "sha256:db3403fdb7a94d5eb73e099befad8104d2a7d110a0f0d99df0de61c5d1fa756c", size = 223435, upload-time = "2026-07-12T20:56:36.302Z" }, - { url = "https://files.pythonhosted.org/packages/d9/76/32c1826309beaf4604c54accef108fdd611e5e5e93f2f5192f050cd5f6bd/coverage-7.15.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d9476292594309db922cc841dd13b303b3c388f4c25d279884f7e2341c681f80", size = 221497, upload-time = "2026-07-12T20:56:37.628Z" }, - { url = "https://files.pythonhosted.org/packages/db/5c/b88ce0d68fa550c7f3b58617fbf363bce64df5bf8295a01b627e4696e022/coverage-7.15.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0c579056b0de461b3a62318b63d0b6ce90aed7f8158d3f00da094df82f29d189", size = 221854, upload-time = "2026-07-12T20:56:39.033Z" }, - { url = "https://files.pythonhosted.org/packages/0e/fe/8509fd2a66fc4e0a829f76a0f0b1dc3cc163368352435b5f243168658077/coverage-7.15.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:23214bdbe226f2b0e9c66a7d6a1d59d4a88045dcf86e702cf0fe0d0935e3d615", size = 253359, upload-time = "2026-07-12T20:56:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/e5/81/c7009ed7ea9765adb2b9d095054d748266fae5f07ac6c5f925f33715fcde/coverage-7.15.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df164be93b46b4825cc39339440a05edc54c4d1d865ba4a60fd43d151a2a1cd3", size = 256096, upload-time = "2026-07-12T20:56:42.115Z" }, - { url = "https://files.pythonhosted.org/packages/21/52/dc8ee03968a5ba86e2da5aa48ddc9e3747bd65d63825fdce2d96acb9c5ff/coverage-7.15.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a524fca1a6f08927d9dc2d4c873cfb7bd7202c247f08b14bdc02424071b8b304", size = 257211, upload-time = "2026-07-12T20:56:43.513Z" }, - { url = "https://files.pythonhosted.org/packages/b8/27/95d7623908da8937deb53d48efcdbf423907a47540e63c62fa21372c652b/coverage-7.15.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d70f3542cd38de85a9e257dcb1ac4c1ab4b6d7d2c2a645809207556628755d1c", size = 259473, upload-time = "2026-07-12T20:56:44.974Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3b/730d761928de97d585465680b568ae69622fb40716babadeabffe75cb51b/coverage-7.15.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d78aa537237212c4313aabe5e964b66acc86350ed19ebc56a3e202df33b6077b", size = 253759, upload-time = "2026-07-12T20:56:46.615Z" }, - { url = "https://files.pythonhosted.org/packages/f1/fc/6b9277acff1f9484b6c12857af5774689d1a6a95e13265f7405329d2f5da/coverage-7.15.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a318112bb4f79d9d04766196d5a3388caa825908a6a9b052aa87de3d9aea7c61", size = 255131, upload-time = "2026-07-12T20:56:48.073Z" }, - { url = "https://files.pythonhosted.org/packages/3d/f2/c704f86129594ba34e25a64695d2068c71d51c2b98907184d716c94f4aec/coverage-7.15.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e55d24cada901963eed5bc89fa562aa033f0d84b9d3de4ecf363737c13aed11e", size = 253275, upload-time = "2026-07-12T20:56:49.538Z" }, - { url = "https://files.pythonhosted.org/packages/f6/29/80fee8af47de4a6dce71ccf2938491f444687a756af258a56d8469b8f1b0/coverage-7.15.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3c78f0cea7275342cf2adc2ad5fdd0aafa106ad91e66d573568f2fcf62c41df5", size = 257345, upload-time = "2026-07-12T20:56:51.038Z" }, - { url = "https://files.pythonhosted.org/packages/20/21/a1e7d7ed1b48a8adf8fd5154d9e83fcc5ad8e6ff20ae00e44865057dce8d/coverage-7.15.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:86bd37eabe39977216f630a7fc1b698e7f5e81a191c7186013245c6c3d313f9d", size = 252844, upload-time = "2026-07-12T20:56:52.535Z" }, - { url = "https://files.pythonhosted.org/packages/a7/8c/a4bc26e6ee207d412f3678f04d74be1550e83140563ca0e4997510579712/coverage-7.15.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c6db15c217693bdc3ca0b84de1ba9afafe1c14c26a8a29d77f4ed0de2b6132e2", size = 254716, upload-time = "2026-07-12T20:56:53.968Z" }, - { url = "https://files.pythonhosted.org/packages/11/9d/8ad0266ecfada6353cf6627a1a02294cf55a907521b6ee0bd7b770cfd659/coverage-7.15.1-cp312-cp312-win32.whl", hash = "sha256:359f3fbe09a51500c51966596ee4ee4070b356552c70b3b2420eb200d68e0f76", size = 223554, upload-time = "2026-07-12T20:56:55.583Z" }, - { url = "https://files.pythonhosted.org/packages/81/6d/24224929e06c6e05a93f738bc5f9e8e6ab658f8f1d9b823e7b85430e28b8/coverage-7.15.1-cp312-cp312-win_amd64.whl", hash = "sha256:fa75dc099c126e941a9c0baa8ebd2cbc78bd778687534fe410baf754f6d9e374", size = 224087, upload-time = "2026-07-12T20:56:57.041Z" }, - { url = "https://files.pythonhosted.org/packages/35/23/f81441dd01de88e53c97842e706907b307d9078918c3f4998b11e9ac7250/coverage-7.15.1-cp312-cp312-win_arm64.whl", hash = "sha256:26f89cf6d0634375f454fa71057945ad18edb0f1607a90fecf22c57dc3dc289a", size = 223472, upload-time = "2026-07-12T20:56:58.594Z" }, - { url = "https://files.pythonhosted.org/packages/ca/1e/6fa289d7993a2a39f1b283ddb58c4bfec80f7800be654b8ba8a9f6a07c63/coverage-7.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:71ac4ca1658ca99160fd58cc6967110e989c34b04627f24ed6ec9f70fb24571a", size = 221519, upload-time = "2026-07-12T20:57:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/6f/e1/0db4902a0588234a70ab0218073c0b20fbc5c740aa35f91d360160a2ebc9/coverage-7.15.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:26a40cbf2b13bd94af53ee02a424cb3bb96a9edfac0d00834bd068512a62714b", size = 221895, upload-time = "2026-07-12T20:57:01.867Z" }, - { url = "https://files.pythonhosted.org/packages/b4/cb/3719783865092dac5e08df842730305ee9ab1973ae7ddb6fbdf27d401f30/coverage-7.15.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4c5a5eff4ad4f9f7088fd3fc7a66d98d06566ee294b3b053309fb0a3b45be1e", size = 252882, upload-time = "2026-07-12T20:57:03.459Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5e/caf3abbdbb22629626160ffc9c017eb995b7cb11c0be46b974834cef1792/coverage-7.15.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:962aa56c1c9b016d681265880eb6acc9966029d2c4c559319cc43a1abbb9b59a", size = 255479, upload-time = "2026-07-12T20:57:04.984Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f1/d60f375bfe095fef944f0f19427aefdbf9bdd5a9571c41a4bf6e2f5fdb81/coverage-7.15.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1678eb2dc57a8ce67601b029582ef6d41e9e6ca22692aaeccd4107e40f27386c", size = 256715, upload-time = "2026-07-12T20:57:06.446Z" }, - { url = "https://files.pythonhosted.org/packages/d7/17/8b0cbc90d02dc5adad4d9034c1824ec3fa567771b4c39d9c1e3f9b1431b8/coverage-7.15.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1174900a43f6f8c425fee10d7dbddc308adefcdc78aaced32357f5ab750a0e90", size = 258845, upload-time = "2026-07-12T20:57:08.092Z" }, - { url = "https://files.pythonhosted.org/packages/92/29/c5e69f5fb75c322e9a3e4ef64d02eebfc3d66efceccc8514ff80a3c13a56/coverage-7.15.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98847557a6859cadf693792ce89f440cb89692993f60dc6d3a7e35f3d340216f", size = 253098, upload-time = "2026-07-12T20:57:09.636Z" }, - { url = "https://files.pythonhosted.org/packages/64/57/21144252fdd0c01d707d48fbcea13a80b0b7c42ced3f299f885ab8978c3a/coverage-7.15.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8697b2edb57143546a24389efc11e1b000cd5800fc20d84f04edb601e4a7cfb8", size = 254844, upload-time = "2026-07-12T20:57:11.141Z" }, - { url = "https://files.pythonhosted.org/packages/59/2a/499a28a322b0ce6768328e6c5bb2e2ad00ac068a7c7adb2ecd8533c8c5d9/coverage-7.15.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6827ac0519be3fe91bf96b4060eb00d1d24f82649b29862cd75a3cfca248b02a", size = 252807, upload-time = "2026-07-12T20:57:12.678Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/928a95da5da8b60f2b00e1482c7787b3316188e6d2d227fb8e124ada43a1/coverage-7.15.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2de8ecbbc77c7e4d22572779920ed8979c69168675e96be3a548c996568c6c31", size = 256965, upload-time = "2026-07-12T20:57:14.326Z" }, - { url = "https://files.pythonhosted.org/packages/16/10/889adbc1b8c9f866ed51e18a98bcafc0259fb9d29b81f50a719407c64ea8/coverage-7.15.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2b25f0f0fa5260df9d7bb55d47c8bdc23fa3382c1a18f7c9cae122e6c320b1ad", size = 252628, upload-time = "2026-07-12T20:57:15.892Z" }, - { url = "https://files.pythonhosted.org/packages/1a/30/a5e1871e5d93416511f8e359d1ccebfe0cbb050a1bbf7dd20228533ec0cf/coverage-7.15.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a2effcbd93ae340a58db718fe4181d967f84d352c4cefeaab4ff82ce813901a", size = 254399, upload-time = "2026-07-12T20:57:17.703Z" }, - { url = "https://files.pythonhosted.org/packages/2d/26/c36fbffd549dadbdd1a75827528fb00a4c46aa3187b007b750b1e2cebbf2/coverage-7.15.1-cp313-cp313-win32.whl", hash = "sha256:895e65c96aef0cecea250f6e35e9a32f11375514e1a0cb5210e0fda128c04e8e", size = 223564, upload-time = "2026-07-12T20:57:19.253Z" }, - { url = "https://files.pythonhosted.org/packages/16/fc/becbb9d2c4206d242b9b1e1e8e24a42f7926c0200dd3c788b9fab4bb96d5/coverage-7.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6d0a28b63a0d75f9ed5118105d1154fc3aa40a8605a30d5d87e3d043ad90fe7", size = 224106, upload-time = "2026-07-12T20:57:21.108Z" }, - { url = "https://files.pythonhosted.org/packages/d3/30/1cfc641461369b6858799fca61c0a8b5edc490c519bf7c636ffa6bbf556f/coverage-7.15.1-cp313-cp313-win_arm64.whl", hash = "sha256:b4ee9818e8bae3544379ad2c09b851c4fb886aaa8860d57a1c1316ddcb16db49", size = 223497, upload-time = "2026-07-12T20:57:22.734Z" }, - { url = "https://files.pythonhosted.org/packages/f0/46/81961952e7aebfb38ad0ae4264e8954cc607a7af9e7ac111f9fa986595cc/coverage-7.15.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a886af95f59edf67d5770fd3564d53f4a8af93f25f8c1d60d27e00d7f5674ee8", size = 221560, upload-time = "2026-07-12T20:57:24.282Z" }, - { url = "https://files.pythonhosted.org/packages/13/d2/ee14d715889f216baf47301d9f469e08fff6995552aaf67e897b282865f6/coverage-7.15.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:985657ebd707941de90d488d1cbb5efac20bdf81f7b91eba771624ccda4d36f4", size = 221894, upload-time = "2026-07-12T20:57:25.87Z" }, - { url = "https://files.pythonhosted.org/packages/f3/38/f830bc6e6c2c5f23f43847125e6c650d378872f7eeba8d49f1d42193e8a9/coverage-7.15.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5bbe2a06e0a5e1404d9ffbdb49b819bbd6a3bb198ebea4c8dfe7ad9f1e1c2e81", size = 252938, upload-time = "2026-07-12T20:57:27.506Z" }, - { url = "https://files.pythonhosted.org/packages/e1/53/0d3dd963631259d794c898735d5436e68d6a8d40749c419a07ff7c171469/coverage-7.15.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bde0fe24083d0b7b3dbafa7a09f0796410af1afa2523f28f5f208d8340a4aaca", size = 255445, upload-time = "2026-07-12T20:57:29.234Z" }, - { url = "https://files.pythonhosted.org/packages/b1/fd/aabed228557565c958259251b89bab8c5669b31291fa63b3e2154ebb017a/coverage-7.15.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f89f7453d6d46db14cf233e2cd8edcd78de2b9c49d4f1dc109590b4e5dbfbb74", size = 256790, upload-time = "2026-07-12T20:57:30.826Z" }, - { url = "https://files.pythonhosted.org/packages/bc/aa/1cc888e5d3623e603c4e5399653cb25728bb2b40d7519188a3e293d24620/coverage-7.15.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc3656c9ecc27b36bd0907455b77f83c0069ca9ad4a66dec892b76c696eb6047", size = 259104, upload-time = "2026-07-12T20:57:32.63Z" }, - { url = "https://files.pythonhosted.org/packages/5f/61/fc16d5f5e53098dae41efa21e8ccc611a9b4fe922750dd03dc56db552182/coverage-7.15.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:24d8e85a2a45e44883b488c2659f51fa761dad5353fdb319b672a93facbd2ca9", size = 252956, upload-time = "2026-07-12T20:57:34.316Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f3/52384668c3de4519ca770bf1975a89e4d6eb5aa2faf0da0577a14008cba4/coverage-7.15.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68931b5fe746ed4fdaa8892989cab9e6c35781eeb3b0ab2ded893d561e1b3652", size = 254797, upload-time = "2026-07-12T20:57:35.947Z" }, - { url = "https://files.pythonhosted.org/packages/ce/68/54b807e7c1868178e902fd8360b5d4e559394462f97285c50edf1c4608db/coverage-7.15.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1ce6947e2a95534ecaa5a15e73c21e550514c980d80eda204d064d789a95f6a4", size = 252762, upload-time = "2026-07-12T20:57:37.856Z" }, - { url = "https://files.pythonhosted.org/packages/7b/48/dde8adf0338e3ace738757dccf1ce817e5fdcadfae77e1b48a77e5a3b265/coverage-7.15.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:841befdbc89b9c82435fc25b0f4f41858b6238693e45af758bec4cfc1968171c", size = 257037, upload-time = "2026-07-12T20:57:39.488Z" }, - { url = "https://files.pythonhosted.org/packages/07/f2/179dd88cf60a0aeeee16a970ffe250dccea8b80ed4beab4c5d3f6c41ad4b/coverage-7.15.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5d3de58b837375e7f4c0e1a088ccab5f655efb2fd7427b729df02c862a559633", size = 252577, upload-time = "2026-07-12T20:57:41.363Z" }, - { url = "https://files.pythonhosted.org/packages/2c/37/8a593d69ab521beb6a105a2017cac4ba94425ee0a8349e29c3c0b522d24f/coverage-7.15.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b1801963f9f44ae0c0f6d737bc7aeb2bbcde7d1fe7e3b43cddc1961af42d3b41", size = 254235, upload-time = "2026-07-12T20:57:43.025Z" }, - { url = "https://files.pythonhosted.org/packages/6d/34/bc9b3bced66f2cdad4bf5e57ae51c54ea226e8aaaebfc9370a9a11877bf3/coverage-7.15.1-cp314-cp314-win32.whl", hash = "sha256:8c7953c4128ef53b6ffb5f90d87c87d4ce26731df294760bb2314eb0e069e44b", size = 223771, upload-time = "2026-07-12T20:57:44.662Z" }, - { url = "https://files.pythonhosted.org/packages/4c/f3/4d20337bed61915d14349e62b88d5e4144d5a9872b64adbe90e9906db6db/coverage-7.15.1-cp314-cp314-win_amd64.whl", hash = "sha256:6f0bab60a582d415f0fb535ccff13ba334a47a1538f98913330a525d23bd535a", size = 224257, upload-time = "2026-07-12T20:57:46.412Z" }, - { url = "https://files.pythonhosted.org/packages/7b/df/bbfeae4948f3ded516f92b32f2d57952427fc5ecfc0924487bb6ee6a5f38/coverage-7.15.1-cp314-cp314-win_arm64.whl", hash = "sha256:0f410ee8f0ac4ec7db71bc0b7632a8b9994e1cad2755bd1566c17e6a162caa74", size = 223683, upload-time = "2026-07-12T20:57:48.106Z" }, - { url = "https://files.pythonhosted.org/packages/35/65/0b431856064e387d1f5cf474625e4a0465e907024d42f35de6af19ced0be/coverage-7.15.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc868bab88e049d41fcd41766810d790a8b960053be2a45e060f5ce0d31d258b", size = 222298, upload-time = "2026-07-12T20:57:49.882Z" }, - { url = "https://files.pythonhosted.org/packages/a6/96/50eac9bd49df8a3df5f3d38746d1bf332299dffb554486c94ebd55c9dc49/coverage-7.15.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:206d4ec6028f2773b40932d09f074539d6bcdd8f6b318d40cb04bdbd68ed0b49", size = 222561, upload-time = "2026-07-12T20:57:51.688Z" }, - { url = "https://files.pythonhosted.org/packages/e0/5b/6ba1c4a27e10b8816fd2622b98162c83d3bdf1185097360373611bf96364/coverage-7.15.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:620482ef1c9f4e61f962e159325fe77dea59d16e39d9c9470d069053b244d864", size = 263923, upload-time = "2026-07-12T20:57:53.392Z" }, - { url = "https://files.pythonhosted.org/packages/e4/59/fe03ade97a3ca2d890e98c572cf48a99fda9adba85757c34b823f41efe1e/coverage-7.15.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d385fc9b054e309ad3cecdc77b586d2af0c98aeec2fdb3773544586f366e817c", size = 266043, upload-time = "2026-07-12T20:57:55.095Z" }, - { url = "https://files.pythonhosted.org/packages/16/e0/55c4b1217a572a43e13b39e1eb78d0da29fb23679003bd0cdf22c50b1978/coverage-7.15.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1198bca9c0dd7c188aae1f185b0c0b5fc4f0a2b6909000858c29550320bdb07", size = 268465, upload-time = "2026-07-12T20:57:57.017Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e2/ee47944f76afc03909119b036fe9e0da8cbd274a5141287de79791a0fb6d/coverage-7.15.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d0297e6a070eadb49df7cddd0ab6f420b8b689dd8904c7dd815a323168fa57e", size = 269584, upload-time = "2026-07-12T20:57:58.958Z" }, - { url = "https://files.pythonhosted.org/packages/cb/8a/6b4d9779c7b2e21c3d12c3425e3261aa7411399319e27aa402dfec4db5d0/coverage-7.15.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916fcf2214f56960e409561b37fc32a160a42b6e85483d0652d7b70fa55d707e", size = 263019, upload-time = "2026-07-12T20:58:00.979Z" }, - { url = "https://files.pythonhosted.org/packages/c4/1e/db5c7fa0c8ba5ece390a1e1a3f30db71d440240a80589df28e66a7503c40/coverage-7.15.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f837bae572c7869ffaa502e604c87e182543012831cf87aae4586ad090ac6dcf", size = 265916, upload-time = "2026-07-12T20:58:03.005Z" }, - { url = "https://files.pythonhosted.org/packages/83/53/fe5176682b00709b13fab36addd26883139d0dea430816fea412e69255e2/coverage-7.15.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3ea65e3ee6c7c32349fd00559927a9e577bdd72386087eeed1c42b62dfce9b82", size = 263520, upload-time = "2026-07-12T20:58:04.994Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e7/16f15127be93fbc70c667df5ec5dce934fc76c9b0888d84969a5d5341e2c/coverage-7.15.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:345034976f46a1c54bd17f4e43eb30bb92cb7082fcddff03250cff136cc4eb82", size = 267254, upload-time = "2026-07-12T20:58:06.824Z" }, - { url = "https://files.pythonhosted.org/packages/cd/73/e5119111f6f065376395a525f7ce6e9174d83f3db6d217ea0211a61cca4d/coverage-7.15.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4f051a64eb8f8addb4661c2b41d6eea5b7ebc68ad4b2baea8d9bc54e1956e5f7", size = 262366, upload-time = "2026-07-12T20:58:08.555Z" }, - { url = "https://files.pythonhosted.org/packages/d7/9c/6d0a81182df18a73b081e7a8630f0e2a52b12dfd7898c6ab839551a454d2/coverage-7.15.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a7625770f7720b49bb30d194ad2f8d50fab3c5177874af3d2399676f95f9c594", size = 264680, upload-time = "2026-07-12T20:58:10.359Z" }, - { url = "https://files.pythonhosted.org/packages/d9/a2/bac0cbd4450638f1be2041a464b1766c8cc94abf705a2df6f1c8d4be870d/coverage-7.15.1-cp314-cp314t-win32.whl", hash = "sha256:81e503d130a472ad1bd38199ecd35116b40d92bcd31e27a2cacde035381f2070", size = 224077, upload-time = "2026-07-12T20:58:12.065Z" }, - { url = "https://files.pythonhosted.org/packages/f0/b2/d83c5403155172a43ba47c08641bad3f89822d8405102423a41339d2c857/coverage-7.15.1-cp314-cp314t-win_amd64.whl", hash = "sha256:724e878b213b302ad46e9f2fc872d386613f20ebfc492a211482d917ea76c14f", size = 224908, upload-time = "2026-07-12T20:58:13.956Z" }, - { url = "https://files.pythonhosted.org/packages/cc/41/442b74cad832cc77712080585455482e7cc4f4a9a13192f65731dcd18231/coverage-7.15.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ce2f05c14d077f406fefc4fa5e4f093ad0e0787549f6582535d6e28766f0361b", size = 224219, upload-time = "2026-07-12T20:58:16.315Z" }, - { url = "https://files.pythonhosted.org/packages/34/98/07a67cf1a26e795d617ed5c540c042b0ac87b72f810c30c07f076cf334f3/coverage-7.15.1-py3-none-any.whl", hash = "sha256:717d01e6e00bed56ad13306f19e0dd2f4f645ee8159d2c72c72301d6cfc7090c", size = 213284, upload-time = "2026-07-12T20:58:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/7d/3a/54536704f507d4573bf9161c4d0dd3dd59b6d85e48c664e901b6844d8e33/coverage-7.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f1ec6f304b156669cfde653b4e9a953f5de87e247ea02ac599bce0ab2744036", size = 221414, upload-time = "2026-07-15T18:53:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d9/8ba925d29743e3577b21e4d8c11a702b76bc93c41e7fdfd1177af63d4b8d/coverage-7.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d3361879d736f469f45723c11ea1a5bbdaf1f6928f0e632c940378b5aa9b660", size = 221913, upload-time = "2026-07-15T18:53:53.682Z" }, + { url = "https://files.pythonhosted.org/packages/09/54/a855f3aa0187f2b431ade4e4791b77b56282cfb5d201c83ec26a31b5b36a/coverage-7.15.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c6a98d698f9e2c8008d0370ec7fc452ebfcc530002ae2d0061170d768b992589", size = 252332, upload-time = "2026-07-15T18:53:55.467Z" }, + { url = "https://files.pythonhosted.org/packages/8e/d3/13ac97b4370640ba3452fc8559b06cc2f479ce3ba4a0b632a73e44c38a7d/coverage-7.15.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d50dd325e18ec25bfcc10cd7f99b04df1ab9ec76b0918c260e60817ad0643dee", size = 254243, upload-time = "2026-07-15T18:53:57.055Z" }, + { url = "https://files.pythonhosted.org/packages/88/83/5eca144942d8d0659d3f55176517f4a59cdc65eefd17146a0770935a3ebd/coverage-7.15.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67d7602480a47bdf5b675635403625553ebaa70d5a62a657c035149fd401cea0", size = 256352, upload-time = "2026-07-15T18:53:58.83Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ba/d3db2e01a50fc88cdb4c0f19542bcf6f61489e34dc9aa3538413e2459a38/coverage-7.15.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cee0f89f4767a6057c8fbf168f8135f18be651300496086bd873e3189fed0487", size = 258313, upload-time = "2026-07-15T18:54:00.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/b3/aba83416e9177df28e5186d856c19158c59fc0e7e814aaa61a4a2354ad1b/coverage-7.15.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a29ec5305a7335aacee2d799e3422e91e1c8a12474986e2b3b07e315c91be82f", size = 252449, upload-time = "2026-07-15T18:54:02.456Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a5/4b00ecac0194431ab451b0f6710f8e2517d04cef60f821b14dec4637d575/coverage-7.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48ccc6395958eda89093ecdc35644c86f23a8b23a7f4d44958812b721aad67c1", size = 254043, upload-time = "2026-07-15T18:54:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/75/b6/cfa209b4313ee7f1b34da47efcd789ea51c024ad35af390e00f5a3c10a2e/coverage-7.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:81f382c5a94b434ec1f6da607edb904c76d7212e618cd4d1bc9f97bed4120ef5", size = 252107, upload-time = "2026-07-15T18:54:06.745Z" }, + { url = "https://files.pythonhosted.org/packages/36/67/e8cac5a6954038c98d7fe7eb9802afe7ab3ecb637bb7cc00e69b4148b56d/coverage-7.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bbc808daf4f5cd567af8075ecc72d21c6dfef9a254709a621a84c217c935ebc0", size = 255873, upload-time = "2026-07-15T18:54:08.48Z" }, + { url = "https://files.pythonhosted.org/packages/2c/92/395cca9f330a86c3fe3471d73e2c102116c4c58fdc619dbbc125c6e93a54/coverage-7.15.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a4c46b247b5d4b78f613bd89fea926d32b25c6cc61a50bd1e99ba310348f3dad", size = 251826, upload-time = "2026-07-15T18:54:10.083Z" }, + { url = "https://files.pythonhosted.org/packages/51/60/3e91b20295439652424f426b7086ec5bf4fbe3f604c73eda22b986c4fd6b/coverage-7.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:094dd37f3ef7b2da8b068b583d1f4c40f91c65197e16c52a71962d5d537fc5db", size = 252735, upload-time = "2026-07-15T18:54:11.878Z" }, + { url = "https://files.pythonhosted.org/packages/a5/eb/8c07839005e5e3c6b3877d3a6e2a80ce766589f31dd2b6882b78d59a7b8c/coverage-7.15.2-cp311-cp311-win32.whl", hash = "sha256:a63b9e190711134d581c4d703df5df09851b1acf99792c7aacbbe9f41f0283c9", size = 223500, upload-time = "2026-07-15T18:54:13.525Z" }, + { url = "https://files.pythonhosted.org/packages/2e/98/59d83c257cd59f0fbaf9d9ddb26b744a576760dfd1ae16e516408894a02b/coverage-7.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:8bb9f4b4279187560796a4cdaca3b0a93dd97e48ee667df005f4ed9a97403688", size = 223973, upload-time = "2026-07-15T18:54:15.163Z" }, + { url = "https://files.pythonhosted.org/packages/ea/09/2d285c8bef5c4f695d120c1c96dc11715638aa8e134069f210bb6a62a9fe/coverage-7.15.2-cp311-cp311-win_arm64.whl", hash = "sha256:8c726b232659cbd2ae57ade46509eb068c9bd7a06df9fcbff6fe484870006934", size = 223519, upload-time = "2026-07-15T18:54:16.803Z" }, + { url = "https://files.pythonhosted.org/packages/6a/50/eb5bf42e531611a9f8d272556b1ed4de503f84a91413584094487cf69f8f/coverage-7.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1adac78e5abc7c5438f7a209c9ca69d06542f0bf481d728b6989ea80b813fdf9", size = 221587, upload-time = "2026-07-15T18:54:18.439Z" }, + { url = "https://files.pythonhosted.org/packages/06/d1/da99af464c335d4e023a6efcd7ec30f63b88a43c93745154ab74ffb31cea/coverage-7.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b868acc62aa5de3be7a9d05c2333bf8359ca987e43f9cb30ff8fbda6a024ab73", size = 221943, upload-time = "2026-07-15T18:54:20.062Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8a/13c42723d61ca447eafa18732e8141dd6a63f2732e1c7e1502c182dd88d7/coverage-7.15.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6f6966fc30e6f06ca8f98fb0ce51eda6b111b3ee8d066a8b1ec9e77fa06ab55d", size = 253450, upload-time = "2026-07-15T18:54:21.765Z" }, + { url = "https://files.pythonhosted.org/packages/d7/29/99021303f98fbdcb63504b4d07bea4cc025b9b2dd907c4f07c85d50a0dab/coverage-7.15.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:68af907f595ab01a78f794932ff3bdf929c316d3000810d38dbc247129e26f8b", size = 256187, upload-time = "2026-07-15T18:54:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a8/fd503715ed6ca9c5d742923aa5209257340b367a867b2ced0c7d4ba8a0b9/coverage-7.15.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afa29e2eff3d5729267e2cb2fd4ce9d61c952932fb2694e34ccb5d9540c6a296", size = 257301, upload-time = "2026-07-15T18:54:25.183Z" }, + { url = "https://files.pythonhosted.org/packages/da/40/3f4b8fb409810036ebc2857d36adc0498c6e957b5df0290c5036b2e143f1/coverage-7.15.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bbf44513ceb1589e31948e20eafbde9deaface90e1a1afa5f5f77b4423d17ce6", size = 259562, upload-time = "2026-07-15T18:54:27.204Z" }, + { url = "https://files.pythonhosted.org/packages/0b/8a/9bdffbef47db77cce3d6b02a28f7e919b19f0106c4b080c2c2246040f885/coverage-7.15.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9deddf09eecb717b7f980414b43d90a5b22ff3967d2949ab29cb0aa83d9e9098", size = 253841, upload-time = "2026-07-15T18:54:29.134Z" }, + { url = "https://files.pythonhosted.org/packages/1b/1e/9031efde019d31a06646261fce6dfc5c3c74e951e27a71e5c9a424563178/coverage-7.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae901f7e55ba405c84ee1cab3d3e962e4e871e4a2bcb9c90911adbd69b42ac5a", size = 255221, upload-time = "2026-07-15T18:54:31.142Z" }, + { url = "https://files.pythonhosted.org/packages/56/db/787acde872389fc84a9ef9d8cd1ccc658e391ab4cb5b28092a714426a394/coverage-7.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a0f47002c6eeb7c280228467a4cb0cc15ca2103a8421b986b2d3ec04a0f9bd8b", size = 253366, upload-time = "2026-07-15T18:54:32.886Z" }, + { url = "https://files.pythonhosted.org/packages/2f/9b/6f57bc4b93c842eef1695f8cdaf2318e35e7ba54f5ba80d84be213ab7858/coverage-7.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd7a5beb7af3e864a13b1f0fb26efd3695da43ef0daf71e586adfffaf34d5b2", size = 257434, upload-time = "2026-07-15T18:54:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/b3186a21b2acc83e451118978905c81c7072c3333707804db09a78c096a2/coverage-7.15.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:97a5c5457a9fb1d6c4e06cfb5dc835871fbfb6a6a51addc9e925bdeff5ef7440", size = 252935, upload-time = "2026-07-15T18:54:36.548Z" }, + { url = "https://files.pythonhosted.org/packages/20/c2/c9f3376b2e717ea69ed7a6e9a5fcab968fb0b290db6cf4bd9a1fc7541b75/coverage-7.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0901cfe6c13bcd2302da4f83e884555d2a22bda6e4c476f09ef204ba20ca536e", size = 254807, upload-time = "2026-07-15T18:54:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e1/dfc15401f4a8aaeb486e1ba3e9e3c40522a6e38bd0ecf0b3f29cb8082957/coverage-7.15.2-cp312-cp312-win32.whl", hash = "sha256:b171bdd71cb7ff792bf32e376173b0ace7e7963e7e57c58dfc42063a6a7174cd", size = 223641, upload-time = "2026-07-15T18:54:40.103Z" }, + { url = "https://files.pythonhosted.org/packages/91/40/81b6d809d320cd366ec5bdf8176575e897dcb8efe7fb4b489ef9e93e4d13/coverage-7.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:582edc45c2040543fef83341be23c43024a3ab3ae0c2d8bc498a06282905ad40", size = 224172, upload-time = "2026-07-15T18:54:41.882Z" }, + { url = "https://files.pythonhosted.org/packages/ef/28/9f14ec438149f7de557f45518f09b4a7917b795cc37083aa7db482693f8c/coverage-7.15.2-cp312-cp312-win_arm64.whl", hash = "sha256:a638db90c61cd219aeee65e83a24fdaa57269a741ae0cf773309208ac862cee3", size = 223556, upload-time = "2026-07-15T18:54:43.674Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d5/f8c838e6b7282976f7c918884b792df7a0c42c5bba5d99c60ad2d221d56d/coverage-7.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8", size = 221606, upload-time = "2026-07-15T18:54:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/bf/37/97c926376364f66298cc44893b89cdf17b8bc406376497c4061ae4b8a8ff/coverage-7.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1", size = 221982, upload-time = "2026-07-15T18:54:47.341Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/a36050a6e83c2135ee0776f452ca3948224befc6d7f26acecc082d0c106a/coverage-7.15.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578", size = 252972, upload-time = "2026-07-15T18:54:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/31/d3/06b5f1daf95f0f15ab05bd75f26ba5f3c8b33d0bb72f3aaa3cf41d1bad3a/coverage-7.15.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1", size = 255569, upload-time = "2026-07-15T18:54:51.098Z" }, + { url = "https://files.pythonhosted.org/packages/81/1c/9afb3f8de2b8d36960391c48559a2e3ff96594b58099f115921549ea8d0d/coverage-7.15.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6", size = 256806, upload-time = "2026-07-15T18:54:53.145Z" }, + { url = "https://files.pythonhosted.org/packages/64/d8/b989f96061a5e32d82fddd1b1b9ff48a7c8f8ae7606f0e80fd9de54b1e33/coverage-7.15.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7", size = 258936, upload-time = "2026-07-15T18:54:55.015Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fa/f99771f5110457c7b511c1935ca49ddf288218eaa84322e028b9334146ae/coverage-7.15.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d", size = 253178, upload-time = "2026-07-15T18:54:57.527Z" }, + { url = "https://files.pythonhosted.org/packages/f6/96/c098a6044d119c751ceede7be91035fa8310170ec24a6523aff72f0a5793/coverage-7.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026", size = 254934, upload-time = "2026-07-15T18:54:59.41Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a2/1457b3a7a50c8d77500103b97a046db863e2f59a1cf6d2f814595f349885/coverage-7.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa", size = 252898, upload-time = "2026-07-15T18:55:01.338Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0e/76958874c471ecfcdde0d2b2747bb2c61bdbf34a40636f4ce9db9923e643/coverage-7.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d", size = 257056, upload-time = "2026-07-15T18:55:03.243Z" }, + { url = "https://files.pythonhosted.org/packages/7c/7c/3d7c4e3bf58baa40327dc7edc2272b17cf02299366d52763db1b0ca1556a/coverage-7.15.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b", size = 252718, upload-time = "2026-07-15T18:55:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b8/1cecffed9ce14fb25be9ba42d37b6bb61485c9a3ddd43cd3dde36b6087d8/coverage-7.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188", size = 254490, upload-time = "2026-07-15T18:55:06.889Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2c/42984561bc7f4c045dca67516a0c50ee5ef8d84352dbeb5559dc86c4823e/coverage-7.15.2-cp313-cp313-win32.whl", hash = "sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050", size = 223647, upload-time = "2026-07-15T18:55:08.941Z" }, + { url = "https://files.pythonhosted.org/packages/41/9f/39c7c9245efc583beddf89a87683574e663ed93637f3afb6cd7b88405676/coverage-7.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c", size = 224190, upload-time = "2026-07-15T18:55:10.789Z" }, + { url = "https://files.pythonhosted.org/packages/c7/de/3a2883cf8a213659280ef4b403059e17a9acaeb7fc7fd4105e1226ff2e6d/coverage-7.15.2-cp313-cp313-win_arm64.whl", hash = "sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b", size = 223583, upload-time = "2026-07-15T18:55:12.678Z" }, + { url = "https://files.pythonhosted.org/packages/81/5f/aed265fd7a3551a394f36dfe41868aee709b7f95db4052205b4ad1563ac3/coverage-7.15.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:40f633c5c5fc783732f6312280122e859538fa24461235597c13d803ea9a108a", size = 221650, upload-time = "2026-07-15T18:55:14.527Z" }, + { url = "https://files.pythonhosted.org/packages/6b/2c/222ba12a545189017120f8eddfc1a0bd4616b47d5d4a8d99421edb2fe4c6/coverage-7.15.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:075560438765b7a2ef43bf7aa7758661b53d889df47f062a31bda6c1ade553a2", size = 221988, upload-time = "2026-07-15T18:55:16.674Z" }, + { url = "https://files.pythonhosted.org/packages/aa/38/304b5877ab46e6c290b4292cfcf3fe28245f0e5597cad7f6acc91fc7e0a4/coverage-7.15.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:25fd15dd40a0a2c51a500d664ca29053c09c3259d998407bf982b6e114696138", size = 253029, upload-time = "2026-07-15T18:55:18.856Z" }, + { url = "https://files.pythonhosted.org/packages/6c/58/821b533b8db9e44cf1d8a97bd525149ced40dde1d0093da02cb78e715244/coverage-7.15.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f", size = 255536, upload-time = "2026-07-15T18:55:21.027Z" }, + { url = "https://files.pythonhosted.org/packages/f1/f2/7aa06604c389d32ea7f0a6a988359a7eafc3cd3f8e7bc2e88cd2fdf0b877/coverage-7.15.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9854ca62c152874b2060772503535be2e8f53f70b8aaa7686b094888d872f984", size = 256881, upload-time = "2026-07-15T18:55:23.125Z" }, + { url = "https://files.pythonhosted.org/packages/a2/4f/1ef342339c7916d0096bc5888cc0f653882cc7bc8f897d5cb89143287c9b/coverage-7.15.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:913b6c56e110da40e035bbd168353bf7aaa2544a5eaccea5d98a4629aac156c7", size = 259196, upload-time = "2026-07-15T18:55:25.099Z" }, + { url = "https://files.pythonhosted.org/packages/fe/f4/7ed055d7a9c5ec13b161773a115a5ccc6b0081d568c31fad830806306cc7/coverage-7.15.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aaccad4129d735a8a4d526f26929894c9a4e8ef7034566f210b176749d6906e3", size = 253036, upload-time = "2026-07-15T18:55:27.018Z" }, + { url = "https://files.pythonhosted.org/packages/14/79/ea82cca18c242a3a38b6c017da39726aa62dcb64aa635abf79b92009975c/coverage-7.15.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a164b50081fc7357331c4024ef4d17b78ba325f8380d05f5a69599a7e05257ee", size = 254887, upload-time = "2026-07-15T18:55:29.084Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ba/a136db3c0d9562b00e10b72540dbf3a33cd3bc5b95060c9308e247494623/coverage-7.15.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:bfd341ccf78128e72c094bc70cc25b3ef309c33c7c2c66ba3ed4309549e02de1", size = 252852, upload-time = "2026-07-15T18:55:31.184Z" }, + { url = "https://files.pythonhosted.org/packages/17/17/ea334246b16b7d059953fad6fdefa11e33c68efbd3fe37b1098120a1fac2/coverage-7.15.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1473b3ba8e7ee0f076117b1a72c23f579a2b9e2bb742f48a8d86ea27ca93f91a", size = 257128, upload-time = "2026-07-15T18:55:33.163Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c3/074fb66d46d607855f710876b117cbda562c5ab08363528e78820449f937/coverage-7.15.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:17c432b5f73ad52ef46fb06019f6fa7c66ce381961cf0f7dfd1d3a4bd3a98145", size = 252668, upload-time = "2026-07-15T18:55:35.063Z" }, + { url = "https://files.pythonhosted.org/packages/e1/c1/f620850ada9b36435921c9a3a8057013422b1d964eb4bf37fe138724d192/coverage-7.15.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:77f0ef5011df53a4bd1b35211ab122287f8d9b8d7aa1c4553e5c2deb24b1d446", size = 254325, upload-time = "2026-07-15T18:55:37.125Z" }, + { url = "https://files.pythonhosted.org/packages/cc/31/a729ca3689404493af82ef8e6ff70bd88bdda8da89aeef6ca9b387aeb2b4/coverage-7.15.2-cp314-cp314-win32.whl", hash = "sha256:f653e5d7248c1191ec988a85c72edeab46c3ff44f90639a4ed4874ec0be90243", size = 223844, upload-time = "2026-07-15T18:55:39.078Z" }, + { url = "https://files.pythonhosted.org/packages/c6/83/5d809dc808fb1698c671f3e372259bb9158e64b7ea526fc6ab7de64de9fe/coverage-7.15.2-cp314-cp314-win_amd64.whl", hash = "sha256:9911f31aad8906abe337c271343485cf20df5e70df5d2f57f9f136e7b55f26bc", size = 224331, upload-time = "2026-07-15T18:55:41.346Z" }, + { url = "https://files.pythonhosted.org/packages/16/4e/35e488548e952795829e129995c4174df33bf432b591d1aa42c8d9e4e7ad/coverage-7.15.2-cp314-cp314-win_arm64.whl", hash = "sha256:e38def96ad59853824c97953fdcd2c320a84ba3ce99b417db78af8bb6c3db635", size = 223760, upload-time = "2026-07-15T18:55:43.518Z" }, + { url = "https://files.pythonhosted.org/packages/ed/49/dd2c86cd6374038f6e415fb5bfb86db5218553209c081384a020369dee79/coverage-7.15.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:835ec4e20b45f0a7f63ed78f94065aca00de033403df8377bfe8b9c6abc0a7be", size = 222384, upload-time = "2026-07-15T18:55:45.569Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/173ff17a1c0808e5a438f549f6f145d5ac7528f2791310b63523e3200ac7/coverage-7.15.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7466cc7ab6dc0db871d264bf99e8779f0917ee63d40730af0552f71535a6e072", size = 222647, upload-time = "2026-07-15T18:55:47.544Z" }, + { url = "https://files.pythonhosted.org/packages/84/f8/b8cba872162356fb44ac79c10309d987206a4461e32072fc29228dad7331/coverage-7.15.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e370c12133095ff18432de8c044962be85a5a96d90c6fcbce8e17e76236d2328", size = 264013, upload-time = "2026-07-15T18:55:49.768Z" }, + { url = "https://files.pythonhosted.org/packages/ee/67/a807a7586d0b8cae485308ddd55756f0806c92f8e0b411bacbf23c48edf3/coverage-7.15.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe41909c9515c3bfdb5f02c4d1f857dba322d9a9a1178069b91eea77889df63a", size = 266135, upload-time = "2026-07-15T18:55:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/ce/67/cd78771dc985f7e4ebdcc82b1a96d9a932af9e806f01f2f91a89f4c72e80/coverage-7.15.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aa28cfb6488e5453b5b762d65f73aa586380f6693a04d58078ce228a29b06c0", size = 268555, upload-time = "2026-07-15T18:55:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/18/3e/10134cf81275188c58568f324fc74aedff32c63ca4d5bbc513a91944a6f0/coverage-7.15.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcc0aae933921d03096f53b0b03eeb702129fd406dee59f08d2efacc68681fa5", size = 269674, upload-time = "2026-07-15T18:55:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/75/4a/771b77de446cba985dc414bbc5844bd21604da05dbc044286df8318a48a7/coverage-7.15.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c63387e21ab21f512c69c9756a8c7dadd322c7275edb064064433c9a09c3743", size = 263101, upload-time = "2026-07-15T18:55:58.107Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b5/70a7011da15f4071943361183aefa27847f3e3aec4fd335f1cb3d3a622b1/coverage-7.15.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e55510bc98ae943cece9e667a6c0fe94c6a92913720dea34243657a17993d0c", size = 266007, upload-time = "2026-07-15T18:56:00.468Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0d/f9547e804ce7ad49646ffeffac26699510efbe6c0f751b66fdc960c4e825/coverage-7.15.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2ff08701be2d1556fc78b326c80a3e8042da09352ecb3819105f8e386c8a3071", size = 263611, upload-time = "2026-07-15T18:56:02.615Z" }, + { url = "https://files.pythonhosted.org/packages/ac/59/f576a396659c0efd351f5c1544f67c3560e89c7761cabf7f65e412beeda5/coverage-7.15.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:38c9518b7103826c403a461544e3c2e77151e8676d06eaed85911a97e962584a", size = 267344, upload-time = "2026-07-15T18:56:04.622Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5d/c2e4fce3579c0cb635024293f1a32bbe26df101b3e3a69f22243d1352b6c/coverage-7.15.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:dee88b1ed88587abd8c0269a1fc1f4cc77f7750d1dfde2869e2a123af420e67d", size = 262456, upload-time = "2026-07-15T18:56:06.641Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/956287d69436b66094bc4b57ac2da71e43bfd2a5524e958900b9f582fcf8/coverage-7.15.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fbeeeecea279727f8ac16c8e1133ddfeee793e985c86ae343d6a5ce744eef8c", size = 264771, upload-time = "2026-07-15T18:56:08.795Z" }, + { url = "https://files.pythonhosted.org/packages/2c/5a/6f979530c2734c575de77cf58f5f28d51f7123a94b5030fd9156fe5f363c/coverage-7.15.2-cp314-cp314t-win32.whl", hash = "sha256:cb0fddaa6884be6aae36ced9544b5e90f7d5f03845a2853bf47a14953a4e8688", size = 224151, upload-time = "2026-07-15T18:56:10.856Z" }, + { url = "https://files.pythonhosted.org/packages/54/7e/27f6b2a74d484742f4017553e710b01e396b23d809df3e95ca0bb9a2824b/coverage-7.15.2-cp314-cp314t-win_amd64.whl", hash = "sha256:77f091ea3a9cc611cd29f433565476bc1936c084ac8eee00ea0e7e70c27e4199", size = 224981, upload-time = "2026-07-15T18:56:12.928Z" }, + { url = "https://files.pythonhosted.org/packages/b1/48/284863423aa474240f6842bd00d680da22f4e6ea2e466618ef7c9c9e69a9/coverage-7.15.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6fc448c377d6eeb00a47c673494bd9bae29280ca53987e1869e67ebedfe20658", size = 224294, upload-time = "2026-07-15T18:56:15.156Z" }, + { url = "https://files.pythonhosted.org/packages/ec/82/32e3bd191d498e64f6f911ad55d14006a0861e54869d2d32452326399e65/coverage-7.15.2-py3-none-any.whl", hash = "sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c", size = 213375, upload-time = "2026-07-15T18:56:17.305Z" }, ] [package.optional-dependencies]