Commit Graph
1014 Commits
Author SHA1 Message Date
Andreas f976335122 feat(pvforecast): local pvlib provider with measurement calibration
Add PVForecastAkkudoktorLocal, which runs the modelling chain inside EOS on
raw Open-Meteo irradiance instead of calling a forecast service: solar
position, horizon shading, plane transposition, incidence-angle modifier,
cell temperature, PVWatts DC and inverter AC.

It needs no API key and serves up to 16 days at 15-minute resolution from a
single hourly request, which is what keeps `optimization.tail_horizon_hours`
fed - services wrapping Open-Meteo cut the horizon much shorter. Several
Open-Meteo models can be listed in `weather_models` and are averaged per
variable at no extra request cost.

With `calibration_enabled` the provider fits itself against
`measurement.pv_production_emr_keys` over the past `calibration_days`: a
global scale factor plus optional per-solar-azimuth factors, each weighted by
modelled energy, shrunk toward the global factor by `calibration_prior_kwh`
and clamped to `[calibration_min_factor, calibration_max_factor]`. The
comparison runs on past intervals, where Open-Meteo serves analysed rather
than forecast weather, so it corrects the error of the PV model and not that
of the weather forecast.

Calibration is a scale factor on the output and never touches `userhorizon`,
`surface_tilt`, `surface_azimuth` or `peakpower`. The docs say so, and say
why a short window and a plant fault inside it are the two ways to end up
with a misleading factor.

Also add `Measurement.pv_production_total_kwh()` alongside the existing load
total, and `scripts/pvforecast_backtest.py`, which scores configuration
variants against the stored meter readings without waiting for new forecasts
to come true.
2026-09-06 18:21:20 +02:00
Andreas c0c9a1f669 feat(optimization): report the knee of the terminal value curve
Everything up to the knee is backed by residual load, everything beyond
it only by an export at the median feed-in tariff - the two halves have
very different confidence. The breakpoint list alone does not say where
the split is, so consumers had to guess it from the segment lengths.
2026-09-04 11:41:23 +02:00
Andreas 3074018bed fix(optimization): say why the terminal value fell back to the fixed scalar
A run whose price forecast is all zeros produces no priced residual load,
so AUTO cannot derive a curve and quietly credits the request scalar
instead. The reported mode was then "FIXED" - indistinguishable from a
run actually configured that way.

The solution now carries a reason, and the fallback is logged as a
warning instead of passing unnoticed.
2026-09-04 11:23:35 +02:00
Andreas 1c10ab83ad feat(optimization): concave terminal value for the energy left in the battery
The energy still stored when the horizon ends keeps its worth: it replaces
grid imports that are paid for afterwards. Crediting that with a single
price per kWh cannot describe it, because the value is not linear in the
amount stored. The first kWh replaces the most expensive hour that PV
cannot cover, the next one the second most expensive, and once every such
hour is served, further energy replaces nothing.

A scalar has to pick one slope for all of it. High enough for the first kWh
means hoarding a full battery; low enough for the last kWh means running it
empty by the end of the horizon - which is exactly what the previous default
of 0 EUR/kWh did.

terminal_value_mode = AUTO (the new default) builds the curve instead. There
is no forecast beyond the horizon, so its trailing window stands in for the
day that follows: residual load max(load - PV, 0) per slot, priced at its
import price, sorted and accumulated. LCOS is subtracted from every marginal
value so stored energy is not credited twice, and the tail beyond the
residual load is only credited when direct marketing allows an export. The
curve is built once per run; the search only interpolates on it.

The solution reports what a run used as terminal_value, curve included, so
the shape can be inspected instead of guessed. FIXED restores the previous
scalar behaviour.

In a 48 h scenario with two cheap slots at the end, AUTO keeps the battery
at 50 % and credits 3.85 EUR where FIXED with 0 EUR/kWh drains it to empty.
The stored optimization results move accordingly - the objective changed.
2026-09-04 10:37:48 +02:00
AndreasandClaude Opus 5 2a9543e710 fix(optimization): clear disabled AC charge in the reported plan
With grid charging switched off (inverter.max_ac_charge_power_w = 0) the
simulation zeroes the AC charge array before using it, but it did so by
rebinding a local name. The solution is read back from the original array,
so it still carried the optimizer's AC charge genes - genes the fitness
never evaluated, because the simulation ignored them, and which are
therefore arbitrary.

The effect was visible as a slot with ac_charge = 0.8 where the battery SoC
does not move. Harmless inside EOS, but a controller that follows the plan
would grid-charge the battery at a time nobody planned or paid for.

Zero the array in place so the reported plan matches what was simulated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 18:56:42 +02:00
AndreasandClaude Opus 5 a6b20e6de2 docs: clarify EV deadline weighting and export rate behaviour
Two questions that came up while using the new fields: how binding the EV
charging deadline actually is (soft, but the default penalty outweighs the
energy cost by roughly forty to one, and the target is met tightly), and
why grid_export_rates can vanish from the saved config file (values equal
to the default are omitted; /v1/config shows what is effective) together
with when a partial export level pays off at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 18:36:48 +02:00
AndreasandClaude Opus 5 f24d9ea0eb feat(optimization): deadlines for consumers and EV, graded grid export
Three related scheduling improvements, all opt-in and behaviour-preserving
when the new fields are not set.

Flexible consumers get absolute time bounds next to the recurring
time_windows: earliest_start_datetime and deadline_datetime, where the
deadline requires the complete run to have *finished* before that moment
("clean dishes by 03:00 tonight"). When no start can meet it,
deadline_policy decides between BEST_EFFORT (run as early as possible, so
the delay rather than the cost is minimized) and STRICT (keep the
deadline; a ONCE consumer then fails the optimization). The solution
reports appliance_deadline_missed per device.

The EV charging target can be given the same kind of deadline, as an
absolute min_soc_deadline_datetime and/or a relative min_soc_max_duration_h
("full in 6 hours"), the earlier of the two winning. The ev_soc_miss
penalty is then evaluated at that slot instead of at the end of the
horizon, and the seeding heuristic only proposes charge slots before it.

Battery-to-grid export under direct marketing is no longer all-or-nothing:
grid_export_rates configures the selectable export levels as a factor of
the rated discharge power (default [0.25, 0.5, 0.75, 1.0]). Each rate is
its own optimizer state, with the full-power state keeping its previous
index so existing seeds and heuristics are unaffected. The chosen level
per slot is reported in battery_grid_export_factor and as the
GRID_SUPPORT_EXPORT operation factor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 17:53:33 +02:00
AndreasandClaude Opus 5 8926cc7ae0 feat(pvforecast): send plane horizon to Forecast.Solar
A plane's userhorizon uses the same convention as the Forecast.Solar
horizon query parameter (evenly distributed heights in degrees, starting
north, clockwise), so pass it through instead of dropping it. Without it
a shaded plane is forecast as if it had a free horizon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 17:53:22 +02:00
Andreas 57d2917c0c Fix seasonal SMARD retail price forecast 2026-08-01 13:05:37 +02:00
Andreas 69ef57d9c9 Add SMARD quarter-hour price provider 2026-08-01 12:21:06 +02:00
Andreas f7e2ac3619 Add adaptive genetic evolution 2026-07-16 15:14:13 +02:00
Andreas 4dfd4b275b Improve genetic optimizer convergence 2026-07-16 14:32:20 +02:00
Andreas 6465e22f07 fix(optimization): add battery self-consumption state 2026-07-16 12:59:02 +02:00
Andreas 5b8f7de113 Expand optimizer seeding and cache fitness 2026-07-16 10:35:52 +02:00
Andreas b0b437f1d7 Improve genetic optimizer seeding 2026-07-16 09:56:50 +02:00
Andreas d4056af0f6 Add resilient market feed-in tariff providers 2026-07-15 16:10:34 +02:00
AndreasandClaude Opus 4.8 67cf6f7d8a feat(optimization): schedule any number of flexible consumers
Replace the single hourly "dishwasher" home appliance with a list of
flexible consumers (home_appliances). Each consumer defines its load
either as an explicit power profile (energy-preservingly resampled onto
the optimization slot grid, incl. 15-min and non-integer interval ratios)
or the flat consumption_wh/duration_h fallback, and runs ONCE or DAILY
within its time windows and the optimization horizon.

- ConsumerScheduleMode + shared load-definition validation (XOR of
  profile/fallback, reject negative/NaN/inf, unique device_id)
- ApplianceGeneLayout: variable appliance gene block (index into
  allowed_start_slots), ONCE/DAILY calendar-day based, no snapping
- per-device output: result.home_appliance_energy_wh, appliance_starts
  (absolute local times), per-device solution columns and DDBC RUN/OFF
  instructions on state transitions only
- deprecate dishwasher/washingstart/Home_appliance_wh_per_hour with
  backward-compatible mapping and explicit conflict rejection
- max_home_appliances is now an upper bound only; no demo appliance and
  no on/off behaviour
- docs, openapi.json, CHANGELOG and optimize_result_2* fixtures updated;
  new tests/test_homeappliance.py covers the mandatory test matrix

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 14:19:46 +02:00
Andreas c59bf1b486 docs: describe feed-in tariff forecast 2026-07-15 09:23:29 +02:00
Andreas bed1f0f275 feat(optimization): model battery LCOS and probabilistic bypass 2026-07-15 09:23:29 +02:00
AndreasandGitHub 8ddb7ce754 Update CHANGELOG with new features and fixes
Added new features and bug fixes related to PV forecasts and battery management.
2026-07-14 18:46:02 +02:00
Andreas a1b469fa38 perf: cache AC charge break-even prices 2026-07-14 17:54:15 +02:00
Andreas 92a8a093e8 feat: complete 15-minute optimization support 2026-07-14 17:00:07 +02:00
ChristinandAndreas 81a36cf355 feat(elecprice): serve native 15-minute Tibber prices for the quarter-hour grid
Request QUARTER_HOURLY exchange prices from Tibber and store them at their
native resolution instead of pre-averaging to hourly values. EOS resamples the
stored records onto the optimization grid on demand (key_to_array), so keeping
the native step size lets both the hourly (interval=3600) and the 15-minute
(interval=900) optimizer be fed the correct grid automatically.

- GraphQL: priceInfoRange resolution HOURLY -> QUARTER_HOURLY (last 960).
- _hourly_series -> _normalize_series: dedupe by timestamp (mean) + sort, no
  1h aggregation; add _resolution_seconds (median of timestamp diffs, fallback
  3600s).
- Resolution-agnostic ETS extrapolation: seasonal windows and history
  thresholds are scaled by slots_per_hour, needed forecast length and the
  prediction index step are computed in slots. Hourly behaviour is unchanged
  (slots_per_hour=1 -> 168/24 seasonal periods, hourly steps).
- Tests: replace the 1h-averaging test with resolution-preserving + dedup
  tests, assert QUARTER_HOURLY in the query, add a 15-min end-to-end test
  (native storage stays 15min, slot-based seasonal periods = 96, 15-min
  forecast index). Hourly backward-compat tests stay green unchanged.
2026-07-12 09:08:40 +02:00
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
AndreasandClaude Fable 5 7f2ac9098c feat: Direktvermarktung mit Batterie-Netzeinspeisung
Fügt einen Direktvermarktungs-Modus (feedintariff.direct_marketing_enabled)
hinzu, der den Börsenpreis als Einspeisevergütung nutzt und aktive
Batterie-Entladung ins Netz (battery_grid_export_allowed) sowie
DC-Charge-Bypass optimiert.

- FeedInTariffEnergyCharts-Provider (Börsen-Einspeisetarif inkl. Prognose)
- Inverter: DC/AC-Wirkungsgrade und Batterie-Grid-Export in process_energy
- Genetik: Export-/DC-Charge-Zustände, Restwert-Bewertung des Akkus
- Solution-Result: neues Feld Feed_in_tariff (verwendeter Tarif je Stunde)
- Tests für neue Provider, Solution und Simulation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 09:01:33 +02:00
Andreas cc583600d8 fix: use stored history for Tibber price forecast 2026-07-09 11:20:46 +02:00
Andreas 15ba84b39c fix: add Tibber electricity price extrapolation 2026-07-09 10:38:21 +02:00
Andreas e381cbf542 feat: add Tibber price provider and PV forecast providers 2026-07-08 16:36:59 +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
AndreasandGitHub d66a096d02 Elecprice Interpolation wrong
deprecated /strompreis API
2026-07-06 16:31:22 +02:00
NormannandGitHub 2c89e3fee9 Merge branch 'main' into feat/pvnode-forecast-provider 2026-07-06 01:21:29 +02:00
NormannandGitHub cd2992c18d Merge branch 'main' into codex/fehler-im-ladealgorithmus-untersuchen 2026-07-06 01:20:57 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1031c21f68 build(deps): bump fastapi from 0.138.2 to 0.139.0 (#1128)
Bump Version / Bump Version Workflow (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Close stale pull requests/issues / Find Stale issues and PRs (push) Has been cancelled
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.138.2 to 0.139.0.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.138.2...0.139.0)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-version: 0.139.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 01:19:43 +02:00
NormannandGitHub 5a44dba663 Merge branch 'main' into feat/pvnode-forecast-provider 2026-07-05 15:03:42 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
36dcb269de build(deps-dev): bump coverage from 7.14.3 to 7.15.0 (#1129)
Bump Version / Bump Version Workflow (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
docker-build / platform-excludes (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
Close stale pull requests/issues / Find Stale issues and PRs (push) Has been cancelled
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.14.3 to 7.15.0.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.14.3...7.15.0)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.15.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-05 15:03:08 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5ca7b39539 build(deps): bump fastapi-cli from 0.0.27 to 0.0.28 (#1130)
Bumps [fastapi-cli](https://github.com/fastapi/fastapi-cli) from 0.0.27 to 0.0.28.
- [Release notes](https://github.com/fastapi/fastapi-cli/releases)
- [Changelog](https://github.com/fastapi/fastapi-cli/blob/main/release-notes.md)
- [Commits](https://github.com/fastapi/fastapi-cli/compare/0.0.27...0.0.28)

---
updated-dependencies:
- dependency-name: fastapi-cli
  dependency-version: 0.0.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-05 15:02:48 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7774b9c4a9 build(deps): bump uvicorn from 0.49.0 to 0.50.0 (#1131)
Bumps [uvicorn](https://github.com/Kludex/uvicorn) from 0.49.0 to 0.50.0.
- [Release notes](https://github.com/Kludex/uvicorn/releases)
- [Changelog](https://github.com/Kludex/uvicorn/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/uvicorn/compare/0.49.0...0.50.0)

---
updated-dependencies:
- dependency-name: uvicorn
  dependency-version: 0.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-05 15:02:31 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
155929f10b build(deps): bump pillow from 12.1.1 to 12.2.0 (#1132)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.1 to 12.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.1.1...12.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-05 15:02:00 +02:00
Andreas cd5cdf8b47 fix(genetic): limit EV charging to unmet min SoC 2026-07-05 12:56:08 +02: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
NormannandGitHub 6f4a26a045 Merge branch 'main' into feat/pvnode-forecast-provider 2026-06-30 09:00:04 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
631d428e89 build(deps): bump fastapi from 0.138.0 to 0.138.2 (#1125)
Bump Version / Bump Version Workflow (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Close stale pull requests/issues / Find Stale issues and PRs (push) Has been cancelled
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.138.0 to 0.138.2.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.138.0...0.138.2)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-version: 0.138.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 08:59:33 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f1157c0692 build(deps): bump python-fasthtml from 0.14.3 to 0.14.4 (#1126)
Bumps [python-fasthtml](https://github.com/AnswerDotAI/fasthtml) from 0.14.3 to 0.14.4.
- [Release notes](https://github.com/AnswerDotAI/fasthtml/releases)
- [Changelog](https://github.com/AnswerDotAI/fasthtml/blob/main/CHANGELOG.md)
- [Commits](https://github.com/AnswerDotAI/fasthtml/compare/0.14.3...0.14.4)

---
updated-dependencies:
- dependency-name: python-fasthtml
  dependency-version: 0.14.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 08:59:15 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
211a43a7de build(deps-dev): bump coverage from 7.14.2 to 7.14.3 (#1127)
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.14.2 to 7.14.3.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.14.2...7.14.3)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.14.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 08:58:45 +02: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
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
441a4639c3 build(deps): bump numpydantic from 1.9.1 to 1.10.0 (#1119)
Bump Version / Bump Version Workflow (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Close stale pull requests/issues / Find Stale issues and PRs (push) Has been cancelled
Bumps [numpydantic](https://github.com/p2p-ld/numpydantic) from 1.9.1 to 1.10.0.
- [Release notes](https://github.com/p2p-ld/numpydantic/releases)
- [Changelog](https://github.com/p2p-ld/numpydantic/blob/main/docs/changelog.md)
- [Commits](https://github.com/p2p-ld/numpydantic/compare/v1.9.1...v1.10.0)

---
updated-dependencies:
- dependency-name: numpydantic
  dependency-version: 1.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-23 09:41:30 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
41453f2781 build(deps): bump monsterui from 1.0.45 to 1.0.46 (#1120)
Bumps [monsterui](https://github.com/AnswerDotAI/MonsterUI) from 1.0.45 to 1.0.46.
- [Release notes](https://github.com/AnswerDotAI/MonsterUI/releases)
- [Changelog](https://github.com/AnswerDotAI/MonsterUI/blob/main/CHANGELOG.bak)
- [Commits](https://github.com/AnswerDotAI/MonsterUI/compare/1.0.45...1.0.46)

---
updated-dependencies:
- dependency-name: monsterui
  dependency-version: 1.0.46
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-23 09:41:10 +02:00