feat(prediction): add Forecast.Solar PV forecast provider

Add PVForecastForecastSolar, a PV forecast provider for the free Forecast.Solar
API (https://forecast.solar), giving operators a no-account forecast source in
addition to Akkudoktor, VRM, Import and pvnode. An optional API key raises the
rate limit.

result.watts is the instantaneous AC power per timestamp, fed directly as
pvforecast_ac_power. Plants with several roof planes issue one request per plane
(Forecast.Solar is single-plane) and the powers are summed per timestamp.
Forecast.Solar azimuth (-180=N..0=S..90=W) is converted from EOS surface_azimuth
(north=0..south=180); local wall-clock timestamps are resolved via the response
timezone before resampling.

Registered in pvforecast.py and prediction.py. Adds tests for timezone
resolution, azimuth conversion, multi-plane summation and HTTP-error handling.
This commit is contained in:
Christin
2026-06-28 03:03:12 +00:00
parent 314e45cfaa
commit a1e2100206
4 changed files with 308 additions and 1 deletions

View File

@@ -45,6 +45,7 @@ from akkudoktoreos.prediction.loadimport import LoadImport
from akkudoktoreos.prediction.loadvrm import LoadVrm
from akkudoktoreos.prediction.predictionabc import PredictionContainer
from akkudoktoreos.prediction.pvforecastakkudoktor import PVForecastAkkudoktor
from akkudoktoreos.prediction.pvforecastforecastsolar import PVForecastForecastSolar
from akkudoktoreos.prediction.pvforecastimport import PVForecastImport
from akkudoktoreos.prediction.pvforecastpvnode import PVForecastPVNode
from akkudoktoreos.prediction.pvforecastvrm import PVForecastVrm
@@ -86,6 +87,7 @@ loadforecast_import = LoadImport()
pvforecast_akkudoktor = PVForecastAkkudoktor()
pvforecast_vrm = PVForecastVrm()
pvforecast_pvnode = PVForecastPVNode()
pvforecast_forecastsolar = PVForecastForecastSolar()
pvforecast_import = PVForecastImport()
weather_brightsky = WeatherBrightSky()
weather_clearoutside = WeatherClearOutside()
@@ -108,6 +110,7 @@ def prediction_providers() -> list[
PVForecastAkkudoktor,
PVForecastVrm,
PVForecastPVNode,
PVForecastForecastSolar,
PVForecastImport,
WeatherBrightSky,
WeatherClearOutside,
@@ -133,6 +136,7 @@ def prediction_providers() -> list[
pvforecast_akkudoktor, \
pvforecast_vrm, \
pvforecast_pvnode, \
pvforecast_forecastsolar, \
pvforecast_import, \
weather_brightsky, \
weather_clearoutside, \
@@ -154,6 +158,7 @@ def prediction_providers() -> list[
pvforecast_akkudoktor,
pvforecast_vrm,
pvforecast_pvnode,
pvforecast_forecastsolar,
pvforecast_import,
weather_brightsky,
weather_clearoutside,
@@ -180,6 +185,7 @@ class Prediction(PredictionContainer):
PVForecastAkkudoktor,
PVForecastVrm,
PVForecastPVNode,
PVForecastForecastSolar,
PVForecastImport,
WeatherBrightSky,
WeatherClearOutside,