Commit Graph
7 Commits
Author SHA1 Message Date
ChristinandAndreas 3098605b0f feat(optimization): support a 15-minute optimization interval
The genetic optimizer was hard-wired to an hourly grid and forced
optimization.interval to 3600 s. Generalize it to a configurable slot grid
of length prediction.hours * (3600 / interval), accepting 900 (15 min) in
addition to the default 3600 (1 hour) so the optimizer can schedule on a
quarter-hour grid for 15-minute dynamic electricity tariffs.

- genetic.py: slot_duration_h / slots_per_hour / total_slots helpers; all GA
  vectors sized by total_slots; simulate()/evaluate() indexed by start slot.
- geneticparams.py: allow {900, 3600}; scale the load power series to per-slot
  energy, mirroring the PV series.
- battery.py / inverter.py: scale power caps to per-slot energy caps via
  slot_duration_h; homeappliance.py carries the hook.
- geneticsolution.py: serialize solution and plan on the slot grid (interval
  freq, start-slot offset, second-based instruction instants).

The default 3600 s interval keeps the previous hourly behaviour; the genetic
regression suite is unchanged. Adds tests for the 15-minute slot grid.
2026-07-12 09:08:39 +02:00
Christin 32e3eb5c6e fix(prediction): drop module-level from urllib.parse import quote
tests/test_docstringrst.py scans every class/function member of each
module via inspect.getmembers() with no __module__ filter, so
`from urllib.parse import quote` pulled the stdlib quote() into the
pvnode and Solcast provider namespaces and its non-reST docstring failed
the docstring-compliance check.

Import `urllib.parse` as a module and call `urllib.parse.quote(...)`
instead: a module member is skipped by the isfunction/isclass scan, and
the fully-qualified call keeps mypy happy (the requests stubs lack quote,
which is why urllib.parse was chosen over requests.utils in the first place).

test_all_docstrings_rst_compliant now passes; isort/ruff/ruff-format/mypy
pre-commit hooks all green.
2026-07-07 06:27:01 +00:00
Christin d4dc9fa662 fix(prediction): green up CI for the new PV providers (mypy + provider sequence)
- Use urllib.parse.quote instead of requests.utils.quote in the pvnode and
  Solcast providers: the runtime re-export exists, but the requests type stubs
  do not declare it, so the pre-commit mypy hook failed with
  'Module has no attribute "quote"'.
- Mark the force_update keyword in the new provider tests with `# type: ignore`
  — it is consumed by the cache_in_file decorator at runtime; same call
  convention and ignore style as pvforecastakkudoktor.py.
- Add PVForecastPVNode, PVForecastForecastSolar and PVForecastSolcast to the
  expected provider sequence in tests/test_prediction.py (fixture + index
  assertions) — the two sequence tests failed because the new providers were
  registered in prediction.py but missing from the hardcoded expectations.
2026-07-04 09:23:57 +00:00
Christin 2f09263a4a docs(prediction): document pvnode, Forecast.Solar and Solcast providers
Add provider descriptions and configuration examples for the three new PV
forecast providers to the prediction guide, a CHANGELOG entry, and regenerate
the affected auto-generated config docs.
2026-06-28 03:25:27 +00:00
Christin cec9e35aa9 feat(prediction): add Solcast PV forecast provider
Add PVForecastSolcast for the Solcast rooftop-site API. The operator registers a
site in the Solcast web app and enters the API key + resource (site) id:
GET /rooftop_sites/{site_id}/forecasts. pv_estimate (kW) is converted to watts
and fed as pvforecast_ac_power; the timestamp is normalised to the period start
(period_end - period) so it aligns with the resample axis. period_end is UTC.

Completes the set of selectable cloud PV forecast providers
(Akkudoktor, VRM, Import, pvnode, Forecast.Solar, Solcast). Adds tests for the
kW->W conversion, period-start normalisation, ISO-8601 period parsing, the
request URL/auth and HTTP-error handling.
2026-06-28 03:06:55 +00:00
Christin a1e2100206 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.
2026-06-28 03:03:12 +00:00
Christin 314e45cfaa feat(prediction): add native pvnode.com PV forecast provider
Add PVForecastPVNode, a native 15-minute PV forecast provider for the
pvnode.com V2 API, giving operators another forecast source to choose from
alongside Akkudoktor, VRM and Import.

Two request modes, selected by configuration:
  * site_id set   -> GET /v2/forecast/{site_id}   (a saved, possibly calibrated
    site managed on pvnode.com — the operator enters site id + API key)
  * site_id empty -> POST /v2/forecast/inline      (geometry sent inline from the
    configured pvforecast.planes; no web-app setup required)

V2 response timestamps are site-local wall-clock accompanied by an IANA
timezone; they are resolved to absolute instants before EOS resamples them.
Nullable pv_power (e.g. at night) is treated as 0 W so the optimizer's linear
resampling does not interpolate phantom production across the night.

Registered in pvforecast.py (provider settings + id list) and prediction.py
(singleton, factory list, container union). Adds tests covering timezone
resolution, null handling, both request modes and HTTP-error propagation.
2026-06-28 02:52:47 +00:00