mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-08-25 17:56:37 +00:00
feat: add pvlib pv forecast provider (#1214)
Add a PV forecast provider that calculates the forecast using a PVLib system model and weather forecast from the EOS weather forecast provider. Additional module and inverter models can be easily added as the database is build from PVLib and SAM databases and a bundled csv file. The module model and inververt model names are provided by new endpoints to be used in configuration. The provider is based on the fantastic work of EMHASS. See https://github.com/davidusb-geek/emhass/blob/master/src/emhass/forecast.py A short description of the provider is added to the documentation. Besides the new features there are the fixes and improvements: * feat: improve EOSdash config page * fix: kex_to_series for start_datetime Make key_to_series always start the series at start_datetime. * fix: default provider for GENETIC and GENETIC0 optimization To make the default less dependent on internet servers (with API changes and availability issues) the default for PVForecast is set to PVForecastPVLib and for ElecPrice to ElecPriceFixed. The default weather provider is changed to OpenMeteo. * fix: EOSdash display resampled prediction values Make EOSdash display resampled prediction values where resampling fits to the prediction value type. Use bar width that fits to 15 minutes value samples. * chore: add a UI hints system to EOSdash The UI hints system eases the definition of forms for configuration items. There are also forms for items in maps and lists. These forms allow to add and delete items to/ from maps and lists. The forms ensure that all required fields of newly added items are filled. * chore: Create an enum for valid optimization algorithms * chore. Make config also provide the available energy management modes. Used for configuration hints. * chore: Randomize default device id in configuration Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
|
||||
import bz2
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pickle
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -14,6 +17,7 @@ from pathlib import Path
|
||||
from typing import Generator, Optional, Union
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
import pandas as pd
|
||||
import pendulum
|
||||
import psutil
|
||||
import pytest
|
||||
@@ -176,6 +180,74 @@ def cfg_non_existent(request):
|
||||
# ------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def cec_databases_data() -> tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""Load CEC test databases once per test session."""
|
||||
DIR_TESTDATA = Path(__file__).parent / "testdata" / "pvforecastpvlib"
|
||||
FILE_TESTDATA_CEC_INVERTERS_PBZ2 = DIR_TESTDATA / "cec_inverters.pbz2"
|
||||
FILE_TESTDATA_CEC_MODULES_PBZ2 = DIR_TESTDATA / "cec_modules.pbz2"
|
||||
|
||||
with bz2.BZ2File(FILE_TESTDATA_CEC_MODULES_PBZ2, "rb") as f:
|
||||
modules: pd.DataFrame = pickle.load(f)
|
||||
with bz2.BZ2File(FILE_TESTDATA_CEC_INVERTERS_PBZ2, "rb") as f:
|
||||
inverters: pd.DataFrame = pickle.load(f)
|
||||
|
||||
return modules, inverters
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def cec_databases(monkeypatch, cec_databases_data) -> tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""Short-circuit CEC database access for every test (per-test patch, session-cached data).
|
||||
|
||||
Config requests the database in PVForecastPVLibCommonSettings by a computed_field.
|
||||
|
||||
To undo this fixture in a specific class do:
|
||||
@pytest.fixture(autouse=True)
|
||||
def cec_databases(self):
|
||||
yield None
|
||||
"""
|
||||
modules, inverters = cec_databases_data
|
||||
|
||||
def fake_update_cec_database() -> None:
|
||||
#print(f"_update_cec_database faked")
|
||||
pass
|
||||
|
||||
def fake_load_cec_database(path: Path) -> pd.DataFrame:
|
||||
#print(f"_loadcec_database faked")
|
||||
if "inverter" in path.name:
|
||||
return inverters
|
||||
if "module" in path.name:
|
||||
return modules
|
||||
raise ValueError(f"Unexpected CEC database path in test: {path}")
|
||||
|
||||
def fake_cec_inverters() -> pd.DataFrame:
|
||||
#print(f"_cec_inverters faked")
|
||||
return inverters
|
||||
|
||||
def fake_cec_modules() -> pd.DataFrame:
|
||||
#print(f"_cec_modules faked")
|
||||
return modules
|
||||
|
||||
monkeypatch.setattr(
|
||||
"akkudoktoreos.prediction.pvforecastpvlib._update_cec_database",
|
||||
fake_update_cec_database,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"akkudoktoreos.prediction.pvforecastpvlib._load_cec_database",
|
||||
fake_load_cec_database,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"akkudoktoreos.prediction.pvforecastpvlib._cec_inverters",
|
||||
fake_cec_inverters,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"akkudoktoreos.prediction.pvforecastpvlib._cec_modules",
|
||||
fake_cec_modules,
|
||||
)
|
||||
|
||||
return modules, inverters
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_default_dirs(tmpdir):
|
||||
"""Fixture that provides a list of directories to be used as config dir."""
|
||||
@@ -234,6 +306,7 @@ def user_data_dir(config_default_dirs):
|
||||
|
||||
@pytest.fixture
|
||||
def config_eos_factory(
|
||||
cec_databases,
|
||||
disable_debug_logging,
|
||||
user_config_dir,
|
||||
user_data_dir,
|
||||
|
||||
Reference in New Issue
Block a user