feat: deliver complete GENETIC optimization to main (#1330)

* feat: adapt configuration for multi optimization algorithms

Decouple configuration from optimization algorithm parameters. Add to_[algorithm]_param() methods
to the configuration that derive optimization algorithm specific parameters from the configuration.
Add x-scope tags to the configuration options that describe for which specific algorithms the
configuration option is for.

The whole device settings are restructured. There are now general settings for the device classes
with the afore mentioned to_[algorithm]_param() methods. The general device settings got their own
directory `devices/settings`. By this the parameter class also does not have to be a pydantic model
which can be used for future optimization/ simulations speed up.

Also the parameter class for a device is now part of the device module. This better decouples and
also is the natural place for parameters of a device.

Besides this feature there are also fixes and improvements:

* feat: extend home appliance time window settings and simulation

  Home appliance can now be configured for multiple runs with per-cycle allowed time windows. The
  number of remaining cycles to plan is determined at runtime by reading the
  ``cycles_completed_measurement_key`` from the measurement store.

* feat: specialiced CycleTimeWindowSequence for time window sequences

  Sequence of time windows associated to cycles.

  This model specializes ``ValueTimeWindowSequence`` so that the ``value``
  field of each ``ValueTimeWindow`` encodes the **cycle index** (0-based
  integer) the window belongs to.

  Typical use: an appliance that must run ``n`` times per day, each run
  constrained to a distinct time window.  Assign ``value=0`` to windows
  for the first cycle, ``value=1`` for the second, and so on.  Multiple
  windows may share the same cycle index (their allowed regions are unioned).
  Windows with ``value=None`` are silently ignored by all cycle-aware methods.

* fix: Make test_configmigrate also regard the _ANY_SENTENIEL in key values

* chore: Make devices configurations a map instead of a list

  This makes config paths stable regardless of declaration order and lets each device settings
  class build its own config path from ``self.device_id`` without needing an external index.
  Tests are adapted likewise.

  Devices configurations are automatically migrated from lists to maps.

* chore: rename levelized_cost_of_storage_kwh to levelized_cost_of_storage_amt kwh

  This better fits in the naming scheme and also makes clear the costs are money.

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>

* fix: runtime config update ignored by config file

Runtime settings were handed back to pydantic-settings as init settings,
which rank below the config file and the environment. Any key already
present in EOS.config.json or in the environment silently discarded the
update, so a bulk PUT /v1/config returned 200 without applying anything,
while the granular PUT /v1/config/{path} endpoint kept working.

Add a dedicated runtime settings source ranked directly below the command
line arguments and record granular updates there as well, so both
endpoints share one store that survives re-evaluation of the settings
sources. Environment variables keep precedence over the config file for
all keys that were not set at runtime.

Also repairs revert_settings() and update(), which passed their data
through the same init settings.

Closes #1303

* fix: env vars ignored on first config build

ConfigEOS.__init__ passed self as first positional argument to _setup,
which forwards it to pydantic_settings.BaseSettings.__init__. Its first
positional parameter is _case_sensitive, so the environment source
matched the upper case variable names against the lower case field names
and returned nothing. Environment settings only took effect after the
next configuration setup.

* docs: changelog for config priority fixes

* fix(config): preserve device identities and storage costs during migration

* fix(measurement): restore JSON records into the existing singleton

* fix(devices): preserve charge-rate typing and public import compatibility

* feat(measurement): integrate typed energy quality and capacity APIs

Port the locally backed-up measurement extensions to main async storage and PR #1256 device maps. Preserve runtime capacity estimates across #1305 bulk updates. Confirm JSON singleton restore defect on unchanged main and add regression. No production configuration or measurements included.

Co-authored-by: Andreas <drbacke@gmx.de>

* docs(measurement): describe household settings and consolidate regression coverage

* docs(measurement): regenerate configuration and API contracts

* test(measurement): isolate capacity database state between tests

* ruff format fix

* fix(measurement): restore JSON records into the existing singleton

* test(measurement): assert restored timestamps before timezone conversion

* test(measurement): assert restored timestamps before timezone conversion

* fix: preserve imported feed-in revenue during parameter preparation

Cancel GENETIC preparation when imported revenue cannot be read or contains invalid values, preserving the chosen provider instead of replacing it with demo tariffs. Keep valid positive, zero and negative amount/Wh series unchanged.

Adapt the revenue-preservation regressions from PRs #1224 and #1304 to the async main API, including real timestamped imports and simulation repricing. The feature-only direct-marketing override remains outside this main fix.

Co-authored-by: Christin <info@bikinibottom.capital>

Co-authored-by: Normann <github@koldrack.com>

* feat(devices): port slot-aware battery export and direct-use physics

Port scoped device changes from d2e2d58237. Keep PR #1256 parameter conversion structure and separate GENETIC0 devices. Validate physical flows and reprice changed simulation results.

Co-authored-by: Andreas <drbacke@gmx.de>

Co-authored-by: Christin <info@bikinibottom.capital>

* docs(measurement): align API version with refreshed prerequisites

* fix: return only completed optimization results per run

* feat(pvforecast): add calibrated local Akkudoktor backend

Port local PV modeling and outage calibration from feature commits f976335, 6dc58c3 and faed0fd by Andreas. Keep PVForecastAkkudoktor identity and remote default, adapt to async storage, and migrate legacy provider settings.

* fix(cache): distinguish callables in the shared EMS cache

Include the function object in cache keys so methods of one interpolator cannot reuse a probability as a power value. Cover both call orders, keyword arguments, cache hits and separate closures with identical qualified names.

* fix(devices): constrain the physics port and validate export levels

Defer inactive EV deadline fields to the optimizer port, reject nonfinite export rates, and document the hourly Optimize boundary. Verify converter IDs, rates and LCOS, separate GENETIC0 interpolation, physical boundary flows and independent GENETIC repricing.

* docs(pvforecast): regenerate local backend configuration schema

* docs(devices): regenerate slot-physics configuration and OpenAPI schemas

* test: type dynamic Optimize regression arguments

* style: wrap imported tariff test parameter import

* style(pvforecast): apply CI import formatting

* docs(pvforecast): refresh API version after CI formatting

* fix(config): satisfy typed device conversion and migration contracts

* docs(config): refresh validated configuration prerequisite schemas

* fix(measurement): enforce typed capacity and sample validation

* test(devices): align physics regressions with strict type checking

* style(measurement): normalize imports for CI

* docs(measurement): refresh typed measurement API schemas

* docs(devices): refresh API version after prerequisite merge

* test: make optimization dispatch timezones explicit

* docs(interpolator): use portable reStructuredText markup

* docs(devices): refresh API version after docstring compatibility fix

* feat: complete configuration-driven GENETIC optimization and reports (#1329)

* feat(devices): port slot-aware battery export and direct-use physics

Port scoped device changes from d2e2d58237. Keep PR #1256 parameter conversion structure and separate GENETIC0 devices. Validate physical flows and reprice changed simulation results.

Co-authored-by: Andreas <drbacke@gmx.de>

Co-authored-by: Christin <info@bikinibottom.capital>

* feat(optimization): port tested terminal and tail value primitives

Source d2e2d58237. 22 primitive tests pass; integration with the optimizer, forecast horizon and API is still pending.

Co-authored-by: Andreas <drbacke@gmx.de>

Co-authored-by: Christin <info@bikinibottom.capital>

* fix(devices): preserve charge-rate typing and public import compatibility

* feat(measurement): integrate typed energy quality and capacity APIs

Port the locally backed-up measurement extensions to main async storage and PR #1256 device maps. Preserve runtime capacity estimates across #1305 bulk updates. Confirm JSON singleton restore defect on unchanged main and add regression. No production configuration or measurements included.

Co-authored-by: Andreas <drbacke@gmx.de>

* test(integration): validate optimizer economics and document measurement settings

* docs(integration): record tested checkpoint and remaining consolidation work

* docs(development): define isolated PR packages and remaining porting gates

* docs(integration): refresh API version after measurement reconciliation

* docs(integration): record PR readiness verification results

* docs(development): record publication and verification of PR 1322

* test(measurement): assert restored timestamps before timezone conversion

* docs(development): record corrected PR head and CI progress

* docs(integration): refresh API version after prerequisite alignment

* docs(integration): define parallel packages and Optimize compatibility gates

* fix: preserve imported feed-in revenue during parameter preparation

Cancel GENETIC preparation when imported revenue cannot be read or contains invalid values, preserving the chosen provider instead of replacing it with demo tariffs. Keep valid positive, zero and negative amount/Wh series unchanged.

Adapt the revenue-preservation regressions from PRs #1224 and #1304 to the async main API, including real timestamped imports and simulation repricing. The feature-only direct-marketing override remains outside this main fix.

Co-authored-by: Christin <info@bikinibottom.capital>

Co-authored-by: Normann <github@koldrack.com>

* test(integration): verify tariff protection with mapped device physics

* fix: return only completed optimization results per run

* test(integration): verify algorithm aliases and mapped-device contracts

* fix(cache): distinguish callables in the shared EMS cache

Include the function object in cache keys so methods of one interpolator cannot reuse a probability as a power value. Cover both call orders, keyword arguments, cache hits and separate closures with identical qualified names.

* fix(devices): constrain the physics port and validate export levels

Defer inactive EV deadline fields to the optimizer port, reject nonfinite export rates, and document the hourly Optimize boundary. Verify converter IDs, rates and LCOS, separate GENETIC0 interpolation, physical boundary flows and independent GENETIC repricing.

* feat(pvforecast): add calibrated local Akkudoktor backend

Port local PV modeling and outage calibration from feature commits f976335, 6dc58c3 and faed0fd by Andreas. Keep PVForecastAkkudoktor identity and remote default, adapt to async storage, and migrate legacy provider settings.

* docs(integration): record combined compatibility checks and green JSON PR CI

* test: type dynamic Optimize regression arguments

* docs(integration): record Optimize fix PR publication

* docs(integration): record imported tariff protection PR

* style(pvforecast): apply CI import formatting

* style(integration): align combined regression imports

* test: make optimization dispatch timezones explicit

* docs(interpolator): use portable reStructuredText markup

* chore: validate combined integration with locked mypy

* docs: hand off six validated pull requests for manual review

* feat: report genetic interval and terminal value diagnostics

* feat(devices): reconcile flexible profiles and EV deadlines with cycle scheduling

Adapt the flexible consumer primitives from d2e2d582 while retaining the keyed settings and per-cycle scheduling introduced by #1256. Preserve slot battery physics and GENETIC0 flat-load conversion. Cover energy conservation, deadlines, window intersections, DST, completed cycles and EV converters.

* test: satisfy typed genetic PDF chart contracts

* feat(optimization): resolve quarter-hour GENETIC requests from configuration

* test(genetic): verify real device scheduling, measurement and export contracts

Register appliance completed-cycle measurement keys so the real store accepts both default and custom counters. Exercise complete low-budget optimizer runs, persisted measurements, generic solution output and instructions, including zero-power phases, EV departure boundaries, per-cycle windows and LCOS.

* fix: bound genetic report forecasts to executable horizon

* feat: complete native genetic scheduling and retained result contracts

* fix: retain missing raw samples when dropna is disabled

* fix: align local optimization slots and measurement instants

* docs: explain complete genetic rollout and PR dependencies

* feat: expose retained GENETIC report through the versioned API

* docs: regenerate complete genetic configuration and API schema

* docs: format consolidation and review handoff markdown

* test: align isolated EMS fixture with native genetic run options

* test(genetic): clean up singleton measurements after device integration tests

* test: freeze the clock without replacing timestamp conversion

* fix: preserve explicit warmstart timezones in runtime requests

* test(genetic): validate device schedules in UTC and Berlin

Use explicit Berlin origins for Berlin wall-clock windows, compare absolute deadline instants correctly, and run all real device optimizer scenarios under both UTC and Europe/Berlin. Compare exported starts in the run timezone instead of assuming the output timezone matches the host.

* fix: start automatic genetic runs in the site timezone

* Preserve aware GENETIC snapshot times across host timezones

* docs: specify site clock and rehearsed merge resolutions

* test: isolate invalid measurement records and refresh API version

* fix: render single-slot genetic tail diagnostics

* docs: refresh schema version after report fix

* fix: preserve configuration-only Optimize API contract

* docs: refresh configuration request schema

---------

Co-authored-by: Christin <info@bikinibottom.capital>
Co-authored-by: Normann <github@koldrack.com>

---------

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
Co-authored-by: Bobby Noelte <b0661n0e17e@gmail.com>
Co-authored-by: r0b2g1t <r0b2g1t@users.noreply.github.com>
Co-authored-by: Normann <github@koldrack.com>
Co-authored-by: Christin <info@bikinibottom.capital>
This commit is contained in:
Andreas
2026-09-17 20:14:24 +02:00
committed by GitHub
co-authored by Andreas Christin Normann Bobby Noelte r0b2g1t
parent 3c862543a1
commit 04f28997ea
64 changed files with 12540 additions and 1431 deletions
+30 -5
View File
@@ -63,7 +63,9 @@ config path from ``self.device_id`` without needing an external index.
0.5,
0.75,
1.0
]
],
"min_soc_deadline_datetime": null,
"min_soc_max_duration_h": null
}
},
"max_batteries": 1,
@@ -98,7 +100,9 @@ config path from ``self.device_id`` without needing an external index.
0.5,
0.75,
1.0
]
],
"min_soc_deadline_datetime": null,
"min_soc_max_duration_h": null
}
},
"max_electric_vehicles": 1,
@@ -112,7 +116,14 @@ config path from ``self.device_id`` without needing an external index.
"num_cycles": 1,
"cycle_time_windows": null,
"min_cycle_gap_h": 0,
"cycles_completed_measurement_key": null
"cycles_completed_measurement_key": null,
"load_profile_power_w": null,
"load_profile_interval_seconds": null,
"schedule_mode": "ONCE",
"time_windows": null,
"earliest_start_datetime": null,
"deadline_datetime": null,
"deadline_policy": "BEST_EFFORT"
}
},
"max_home_appliances": 3
@@ -161,6 +172,8 @@ config path from ``self.device_id`` without needing an external index.
0.75,
1.0
],
"min_soc_deadline_datetime": null,
"min_soc_max_duration_h": null,
"measurement_key_soc_factor": "bat0-soc-factor",
"measurement_key_power_l1_w": "bat0-power-l1-w",
"measurement_key_power_l2_w": "bat0-power-l2-w",
@@ -208,6 +221,8 @@ config path from ``self.device_id`` without needing an external index.
0.75,
1.0
],
"min_soc_deadline_datetime": null,
"min_soc_max_duration_h": null,
"measurement_key_soc_factor": "ev0-soc-factor",
"measurement_key_power_l1_w": "ev0-power-l1-w",
"measurement_key_power_l2_w": "ev0-power-l2-w",
@@ -234,8 +249,17 @@ config path from ``self.device_id`` without needing an external index.
"cycle_time_windows": null,
"min_cycle_gap_h": 0,
"cycles_completed_measurement_key": null,
"load_profile_power_w": null,
"load_profile_interval_seconds": null,
"schedule_mode": "ONCE",
"time_windows": null,
"earliest_start_datetime": null,
"deadline_datetime": null,
"deadline_policy": "BEST_EFFORT",
"effective_num_cycles": 1,
"measurement_keys": []
"measurement_keys": [
"dishwasher.cycles_completed"
]
}
},
"max_home_appliances": 3,
@@ -249,7 +273,8 @@ config path from ``self.device_id`` without needing an external index.
"ev0-power-l1-w",
"ev0-power-l2-w",
"ev0-power-l3-w",
"ev0-power-3-phase-sym-w"
"ev0-power-3-phase-sym-w",
"dishwasher.cycles_completed"
]
}
}
+20 -3
View File
@@ -67,7 +67,9 @@
0.5,
0.75,
1.0
]
],
"min_soc_deadline_datetime": null,
"min_soc_max_duration_h": null
}
},
"max_batteries": 1,
@@ -102,7 +104,9 @@
0.5,
0.75,
1.0
]
],
"min_soc_deadline_datetime": null,
"min_soc_max_duration_h": null
}
},
"max_electric_vehicles": 1,
@@ -116,7 +120,14 @@
"num_cycles": 1,
"cycle_time_windows": null,
"min_cycle_gap_h": 0,
"cycles_completed_measurement_key": null
"cycles_completed_measurement_key": null,
"load_profile_power_w": null,
"load_profile_interval_seconds": null,
"schedule_mode": "ONCE",
"time_windows": null,
"earliest_start_datetime": null,
"deadline_datetime": null,
"deadline_policy": "BEST_EFFORT"
}
},
"max_home_appliances": 3
@@ -172,6 +183,7 @@
"mode": "OPTIMIZATION"
},
"feedintariff": {
"direct_marketing_enabled": false,
"provider": "FeedInTariffFixed",
"feedintarifffixed": {
"feed_in_tariff_amt_kwh": {
@@ -245,6 +257,11 @@
"individuals": 400,
"generations": 400,
"seed": null,
"measurement_max_age_seconds": 300,
"tail_horizon_hours": 48,
"terminal_value_mode": "AUTO",
"terminal_value_euro_per_kwh": 0.0,
"terminal_value_window_hours": 24,
"penalties": {
"ev_soc_miss": 10
}
+3
View File
@@ -7,6 +7,7 @@
| Name | Environment Variable | Type | Read-Only | Default | Description |
| ---- | -------------------- | ---- | --------- | ------- | ----------- |
| direct_marketing_enabled | `EOS_FEEDINTARIFF__DIRECT_MARKETING_ENABLED` | `bool` | `rw` | `False` | Enable export-aware GENETIC optimization. Sale revenues remain those of the configured feed-in provider or explicit forecast; purchase prices never replace them. |
| dvhubonline | `EOS_FEEDINTARIFF__DVHUBONLINE` | `FeedInTariffDvhubOnlineCommonSettings` | `rw` | `required` | DvhubOnline feed in tariff provider settings. |
| energycharts | `EOS_FEEDINTARIFF__ENERGYCHARTS` | `FeedInTariffEnergyChartsCommonSettings` | `rw` | `required` | EnergyCharts feed in tariff provider settings. |
| feedintarifffixed | `EOS_FEEDINTARIFF__FEEDINTARIFFFIXED` | `FeedInTariffFixedCommonSettings` | `rw` | `required` | Fixed feed in tariff provider settings. |
@@ -25,6 +26,7 @@
```json
{
"feedintariff": {
"direct_marketing_enabled": false,
"provider": "FeedInTariffFixed",
"feedintarifffixed": {
"feed_in_tariff_amt_kwh": {
@@ -56,6 +58,7 @@
```json
{
"feedintariff": {
"direct_marketing_enabled": false,
"provider": "FeedInTariffFixed",
"feedintarifffixed": {
"feed_in_tariff_amt_kwh": {
+26 -1
View File
@@ -30,6 +30,11 @@
"individuals": 400,
"generations": 400,
"seed": null,
"measurement_max_age_seconds": 300,
"tail_horizon_hours": 48,
"terminal_value_mode": "AUTO",
"terminal_value_euro_per_kwh": 0.0,
"terminal_value_window_hours": 24,
"penalties": {
"ev_soc_miss": 10
}
@@ -63,6 +68,11 @@
"individuals": 400,
"generations": 400,
"seed": null,
"measurement_max_age_seconds": 300,
"tail_horizon_hours": 48,
"terminal_value_mode": "AUTO",
"terminal_value_euro_per_kwh": 0.0,
"terminal_value_window_hours": 24,
"penalties": {
"ev_soc_miss": 10
},
@@ -167,9 +177,14 @@
| horizon | `int` | `ro` | `N/A` | Number of optimization steps. |
| horizon_hours | `int` | `rw` | `24` | The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours. |
| individuals | `Optional[int]` | `rw` | `300` | Number of individuals (solutions) in the population [>= 10]. Defaults to 300. |
| interval_sec | `int` | `rw` | `3600` | The optimization interval [sec]. Defaults to 3600 seconds (1 hour) |
| interval_sec | `Literal[900, 3600]` | `rw` | `3600` | The optimization interval [sec]. Defaults to 3600 seconds (1 hour) |
| measurement_max_age_seconds | `int` | `rw` | `300` | Maximum age of SoC measurements for configuration-based optimization [s]. |
| penalties | `dict[str, Union[float, int, str]]` | `rw` | `required` | Penalty parameters used in fitness evaluation. |
| seed | `Optional[int]` | `rw` | `None` | Random seed for reproducibility. None = random. |
| tail_horizon_hours | `int` | `rw` | `48` | Forecast lookahead after the control horizon [h]. No tail commands are issued. Set 0 to disable. |
| terminal_value_euro_per_kwh | `float` | `rw` | `0.0` | Value assigned to usable battery energy remaining at the end of the optimization horizon [EUR/kWh]. This terminal value is independent of the battery LCOS. Only used with terminal_value_mode = FIXED. Defaults to 0 EUR/kWh. |
| terminal_value_mode | `<enum 'TerminalValueMode'>` | `rw` | `AUTO` | How to value the energy left in the battery at the end of the control horizon. AUTO solves the forecast tail with an AUTO continuation proxy at its end (or only the proxy if tail is zero); FIXED uses 'terminal_value_euro_per_kwh'. Defaults to AUTO. |
| terminal_value_window_hours | `int` | `rw` | `24` | Length of the trailing window at the effective tail end the AUTO continuation curve is derived from [h]. One day covers a full load and PV cycle. Defaults to 24 hours. |
:::
<!-- pyml enable line-length -->
@@ -187,6 +202,11 @@
"individuals": 300,
"generations": 400,
"seed": null,
"measurement_max_age_seconds": 300,
"tail_horizon_hours": 48,
"terminal_value_mode": "AUTO",
"terminal_value_euro_per_kwh": 0.0,
"terminal_value_window_hours": 24,
"penalties": {
"ev_soc_miss": 10
}
@@ -210,6 +230,11 @@
"individuals": 300,
"generations": 400,
"seed": null,
"measurement_max_age_seconds": 300,
"tail_horizon_hours": 48,
"terminal_value_mode": "AUTO",
"terminal_value_euro_per_kwh": 0.0,
"terminal_value_window_hours": 24,
"penalties": {
"ev_soc_miss": 10
},
+63 -1
View File
@@ -1,6 +1,6 @@
# Akkudoktor-EOS
**Version**: `v0.3.0.dev2609171651094774`
**Version**: `v0.3.0.dev2609171762020752`
<!-- pyml disable line-length -->
**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.
@@ -750,6 +750,32 @@ Get the latest solution of the optimization.
---
## GET /v1/energy-management/optimization/solution/GENETIC/pdf
<!-- pyml disable line-length -->
**Links**: [local](http://localhost:8503/docs#/default/fastapi_energy_management_optimization_solution_genetic_pdf_get_v1_energy-management_optimization_solution_genetic_pdf_get), [eos](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/fastapi_energy_management_optimization_solution_genetic_pdf_get_v1_energy-management_optimization_solution_genetic_pdf_get)
<!-- pyml enable line-length -->
Fastapi Energy Management Optimization Solution Genetic Pdf Get
<!-- pyml disable line-length -->
```python
"""
Render the retained GENETIC result without rerunning optimization.
Rendering runs outside the event loop. Copy the result before offloading;
its recorded timestamp, interval and inputs own the report's time grid.
The legacy /visualization_results.pdf route continues to serve GENETIC0.
"""
```
<!-- pyml enable line-length -->
**Responses**:
- **200**: Successful Response
---
## GET /v1/energy-management/optimization/solution/{algorithm}
<!-- pyml disable line-length -->
@@ -1343,6 +1369,42 @@ Merge the measurement of given key and value into EOS measurements at given date
---
## POST /v1/optimize
<!-- pyml disable line-length -->
**Links**: [local](http://localhost:8503/docs#/default/fastapi_optimize_config_v1_optimize_post), [eos](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/fastapi_optimize_config_v1_optimize_post)
<!-- pyml enable line-length -->
Fastapi Optimize Config
<!-- pyml disable line-length -->
```python
"""
Optimize GENETIC using configured devices and optional fresh runtime inputs.
Static settings belong in configuration; query overrides are rejected.
Forecast arrays start at local
midnight and contain Wh per configured GENETIC slot; prices are currency/Wh.
An empty body uses configured providers and fresh measured states of charge.
The deprecated /optimize endpoint continues to run hourly GENETIC0.
"""
```
<!-- pyml enable line-length -->
**Request Body**:
- `application/json`: {
"$ref": "#/components/schemas/ConfigOptimizationRequest"
}
**Responses**:
- **200**: Successful Response
- **422**: Validation Error
---
## GET /v1/prediction/dataframe
<!-- pyml disable line-length -->
+270
View File
@@ -0,0 +1,270 @@
# EOS consolidation — integration record
Historical integration record, updated 2026-09-16. The completed optimizer port and
current merge instructions are in [review handoff](review-handoff.md). Pending-work
statements below describe earlier checkpoints.
Current PR-ready packages, dependency order and development guidance:
[PR workflow](pr-workflow.md). The first independent fix is ready locally on main;
the complete feature consolidation remains open.
## Pinned sources
- Official main: `4a3724424f98b5ce814d0340ddf3b6b5f1364752` (refreshed after initial checkpoint).
- Feature branch: `d2e2d58237339454dd8bb226f92677c5987f8b27`.
- PR #1256: `6ebd343047b87819b57778359784a616452b5f76`, open, conflicts.
- PR #1305: `60b77f6da2da3a0f59518873fe7fbe9a92e733f4`, open, main target. Latest added commit is formatting only.
- PR #1224: `0b12a34c11685822d97c3bc9f56c6292774cd930`, open, feature target.
- PR #1304: `4b4b49f29902579a8deb34999d71eb2142c0620d`, open, feature target.
- PR #1190 merged into main: GENETIC0 retained separately.
Sources: [issue #1192](https://github.com/Akkudoktor-EOS/EOS/issues/1192) and linked PRs.
Issue body, all issue comments (none), and PR metadata were retrieved from the GitHub API.
Use `refs/remotes/origin/main`: local `refs/heads/origin/main` is ambiguous.
## Isolation and recovery
Original EOS worktree remains on the feature branch. No reset, stash or checkout there.
Integration branch: `integration/eos-consolidation-20260916`, sibling directory
`EOS-integration-20260916`. Unmodified main comparison worktree: `EOS-reference-20260916`.
Private backup: `%USERPROFILE%/.codex/backups/eos-20260916-120324`.
102 modified/untracked files copied byte-for-byte with SHA256 verification; binary
tracked and index patches, original status/refs, verified `repository.bundle`.
See private `RESTORE.md` and `manifest.json` for recovery into a NEW checkout.
Ignored runtime files stay in the original worktree; this is not an archive of its
virtual environment, caches or ignored production datasets. Backup is private and
may include credentials or real measurements; never stage or publish it.
Only explicitly reviewed source/test/document paths are staged for new commits.
## Functional matrix
<!-- pyml disable line-length -->
| Area | Evidence | Integration state |
| --- | --- | --- |
| Providers, SMARD, fee model | #1192 checked; main prediction modules and #1235 | Keep main implementation; provider equivalence not yet established |
| GENETIC0 / legacy API | #1190; `core/ems.py`, `server/eos.py`, `optimization/genetic0` | Preserved; device regressions pass, full endpoint validation pending |
| Device maps / algorithm conversion | #1256; `devices/settings`, `config/configmigrate.py` | Merged with original history; four merge conflicts resolved |
| Runtime updates | #1305; `config/config.py`, `core/pydantic.py` | Merged with original history; configuration tests pass |
| Stable IDs / LCOS migration | New `test_consolidation_config.py` | Four initial regression failures fixed; 55 configuration tests pass |
| 15-minute battery/inverter physics | Feature `devices/genetic`, `prediction/interpolator.py` | Ported in c0796e1; parameter classes retained in device modules, export/slot/efficiency tests pass |
| New GENETIC, adaptive evolution, export states | Feature `optimization/genetic/genetic.py` | Pending; current optimizer is not the complete feature optimizer |
| Warm start alignment | Feature d2e2d58, `test_genetic_warm_start_alignment.py` | Pending |
| Control horizon / forecast tail / terminal value | Feature a2f4ef6, `tailvalue.py`, `terminalvalue.py` | Primitives ported in f70a786; 22 tests pass. Optimizer/horizon/API integration pending |
| EV deadlines / flexible consumers | Feature f24d9ea; #1256 cycle windows | Pending reconciliation: preserve both per-cycle windows and profile/deadline semantics |
| Imported feed-in revenue | #1224 + #1304, 38 feature regression cases | Neither PR targets main; adapt after GENETIC preparation port, keep author credit |
| Local calibrated PV | Feature f976335/6dc58c3/faed0fd | Pending; keep public provider ID PVForecastAkkudoktor per #1192 |
| Algorithm-specific PDF | Main #1205 vs feature utils/visualize.py | Keep on-demand API; feature slot/tail fields pending |
| Measurement channels/energy/quality/capacity/household | Original uncommitted source + five new tests | Ported in 7338baf to async storage and keyed devices; 127 related tests passed |
| Request configuration learning | Original uncommitted `genetic/configrequest.py` | Secured only; pending |
| HA-private core differences | Separate private repo | Not ported; no HA files modified, no deployment |
<!-- pyml enable line-length -->
## Completed packages and tests
1. `d3f96c4`: merge #1256 onto pinned main. Conflicts in devices.py and three generated
documents; preserve maps/settings and main evolution. Regenerate OpenAPI from
merged code. Do not adopt PR build version bumps. 244 configuration/time-window
tests + 81 device/simulation tests passed before subsequent packages.
2. `b43c8df`: merge #1305 without conflicts. 275 config/configabc/configmigrate/pydantic
tests passed.
3. `e3987b2`: stable map identities, reject key/ID mismatch, preserve renamed LCOS,
repair legacy EV charge-rate migration. 4 regression cases failed before fix;
55 related tests passed afterwards. These are integration/PR defects, not claimed
as unchanged-main defects.
Tests currently use the existing Python 3.11.9 environment read-only; pytest adds
this worktree's src. No mutation of the original venv. Missing pypdf 6.19.0 installed into the private
backup test-packages directory and exposed only through test-process PYTHONPATH.
This is not yet validation against every pinned dependency / CI Python version.
Synthetic pytest fixtures; no real devices, server deployment or production config.
## HA handoff and acceptance
Do not relocate active feature development to this integration branch yet.
Device collections are keyed maps, with identical map key and device_id. New settings
live in devices/settings; conversion methods derive algorithm parameters. LCOS
configuration is `levelized_cost_of_storage_amt_kwh`; old files migrate.
Runtime config bulk/granular precedence follows #1305. Both algorithms remain
separate; `/optimize` uses GENETIC0 and cannot demonstrate the new GENETIC port.
Pin a final tested commit for HA only after all remaining packages and end-to-end
API/forecast/output tests. No public push, PR, comment or main update is authorized.
1. `c0796e1`: slot-duration-aware battery/inverter simulation, bounded per-slot
discharge/export, probabilistic direct-use energy model and GENETIC converter
support for LCOS/export levels. Original author co-authorship retained.
Fixed monetary goldens from the previous model are intentionally replaced with
independent grid-flow repricing. Original simulation test passed on unchanged
main; the changed result is not labelled a baseline failure.
2. `f70a786`: bounded forecast reader, concave terminal value and deterministic
forecast-tail primitives. 22 direct primitive tests passed. Does NOT activate
the new optimization algorithm, warmstart, horizon handling or diagnostic API.
3. `d9c434f`: keep main's charge-rate ndarray contract and exported constant after
`#1256`. The first broad collection found this missed overlap.
4. `7338baf`: typed measurement channels, sample quality, energy integration,
household accounting and battery capacity estimation. Adapt all storage calls
and endpoints to main's async API; map battery IDs through device collections.
Capacity estimates survive subsequent runtime config updates and never change
the active capacity_wh. 127 new/existing measurement tests passed, including
JSON/SQLite/LMDB restart, partial coverage and real FastAPI routes with no lifespan.
JSON measurement reload regression reproduced on unchanged afb7bcb (0 records
after reload); applying the previously local singleton fix resolves it.
5. `876756b`: replace stochastic GENETIC output equality with schema, independent
per-slot cost/revenue accounting and physical-range assertions. GENETIC0 goldens
remain unchanged. Short optimizer/PDF runs: 4 GENETIC0 and 4 GENETIC pass; one
400-generation case for each algorithm skipped by the existing --finalize rule.
New household configuration field has documentation metadata.
### Remaining integration risks and concrete next package
The current GENETIC orchestration is still the main-era optimizer with the newly
ported device physics. It must NOT be described as the complete feature optimizer.
Next port `optimization/genetic/genetic.py`, its parameter preparation and solution
model together, carrying feature d2e2d58 warmstart and #1304 revenue fixes. Translate
old top-level optimization settings to `optimization.genetic`. Check the actual
prediction record units: current `elecprice_marketprice_wh` and `feed_in_tariff_wh`
arrays already contain amount/Wh; only amount/kWh configuration is divided by 1000.
Do not apply a second conversion to the existing *_wh arrays. Use #1256 `to_genetic_*` converters
instead of reintroducing parallel settings. Reconcile flexible profiles/deadlines
with #1256 per-cycle windows/completed cycles before exposing the combined API.
Both porting sides must retain their regression cases. Follow with local PV and
algorithm-specific on-demand PDF; do not copy feature's synchronous/automatic-PDF
server paths over main. Then adapt local configrequest.py to the current API.
No feature-provider robustness fixes are claimed ported merely because a provider
with the same name exists on main. HA private-core differences remain unaudited.
No full 2030-test suite run, --finalize optimization run, dependency-pin CI matrix,
or physical installation validation has been completed.
### Backup verification
An independent clone of repository.bundle at the original feature HEAD was restored
using the backup files. All 102 restored SHA256 hashes match. The original worktree
HEAD and all 102 file hashes were also rechecked unchanged after the source ports.
## Reproducing the focused acceptance run
Use a disposable environment with the repository dependencies plus pytest,
pytest-asyncio, pytest-xprocess, pytest-cov and pypdf. The private backup contains
`test-environment.json`, `integration-final.xml` and the complete collection log.
Run from the integration worktree (not the original feature worktree):
```powershell
python -m pytest `
tests/test_typingmodels.py `
tests/test_config.py `
tests/test_configabc.py `
tests/test_configmigrate.py `
tests/test_configfile.py `
tests/test_pydantic.py `
tests/test_consolidation_config.py `
tests/test_genetichomeappliance.py `
tests/test_genetic0battery.py `
tests/test_genetic0inverterefficiency.py `
tests/test_genetic0simulation.py `
tests/test_battery.py `
tests/test_inverter.py `
tests/test_inverter_efficiency.py `
tests/test_geneticsimulation.py `
tests/test_geneticsimulation2.py `
tests/test_terminalvalue.py `
tests/test_tailvalue_physics.py `
tests/test_interpolator.py `
tests/test_measurement_channels.py `
tests/test_measurement_energy.py `
tests/test_measurement_household.py `
tests/test_battery_capacity.py `
tests/test_measurement_file_restore.py `
tests/test_measurement.py `
tests/test_genetic0optimize.py `
tests/test_geneticoptimize.py `
tests/test_doc.py `
-q `
--tb=short
```
Do not inherit EOS_DIR/EOS_CONFIG_DIR from documentation generation when running
pytest: these conflict with fixture-controlled temporary config directories.
Generate OpenAPI after committing source changes; source-dirty version timestamps
otherwise make exact documentation comparisons nondeterministic.
## Final verified checkpoint for this work session
Focused combined run: **633 passed, 2 skipped in 65.46 s**. The skips are the
existing 400-generation --finalize cases. Zero remaining failures in that run.
All 2030 collected tests were collectable after supplying pypdf and resolving the
charge-rate import overlap; collection alone is not a pass of the full suite.
Ruff passed for the ported measurement/device/interpolator/primitive source files;
git diff --check passed. OpenAPI/config documentation generation and the documentation
comparison tests passed. The first combined run caught fixture state leakage in the
new battery test; reset-before/after fixture isolation resolved it on the repeated run.
This is a tested partial integration checkpoint, NOT completion of the consolidation.
The source packages are locally committed; release, full feature acceptance and
upstream submission remain pending. Do not move active development or HA deployment
here until the remaining optimizer/prognosis/output/request packages are integrated.
## PR-readiness verification, 2026-09-16
Current main `7ebe6d7` is incorporated. Two reviewable local packages now exist:
`fix/measurement-json-reload` (`dba0c9c`, independently based on main) and
`feat/measurement-energy-quality-capacity` (`ea3383e`, depends on the configuration
integration base `d546f08`). The latter excludes the new optimizer/device physics.
See [PR workflow](pr-workflow.md) and its concrete draft descriptions.
The expanded integration run completed with 764 passed, 3 skipped and 2 documentation
failures in 453.99 seconds. Both failures were solely stale generated OpenAPI version
metadata, not schema or functional differences. Regenerated the two files; the focused
rerun of all 5 documentation tests passed. No remaining failure from that selection.
The complete expanded selection was not rerun after this documentation-only correction.
Three skips: the two existing long --finalize optimizer cases and the development-only
Energy-Charts forecast case. All 128 active Energy-Charts regressions passed.
Independent package checks: JSON fix 49 tests; isolated measurement package 453 tests
plus 5 documentation tests; capacity fixture isolation followed by 74 passing tests.
Ruff and source formatting passed for the independent fix; measurement source Ruff
passed. Full pinned Linux/Python 3.13 CI remains outstanding.
All original 102 saved file hashes and original feature HEAD were checked unchanged.
Both PR worktrees are clean, locally committed, and unpublished. This enables small
independent PRs now; it does not complete the still-open optimizer/PV/output port.
## First upstream PR published, 2026-09-16
Explicit user approval received for publishing the standalone JSON restore fix.
Fetched main `4a37244` (dependency update #1321) and rebased the standalone branch;
its new head is `bdc754d12fd08e0da18d3156642c695f4bf67bd2`. All 49 measurement tests,
source Ruff and formatting checks passed again. Pushed only
`fix/measurement-json-reload` and created [PR #1322](https://github.com/Akkudoktor-EOS/EOS/pull/1322)
against main. Verified the remote head and PR patch: one commit, exactly two files.
The other integration/measurement branches were not pushed. The PR is conflict-free,
not merged; GitHub CI was started and is being checked. Original HEAD and all 102
backup hashes were rechecked unchanged. Earlier notes saying all packages are
unpublished are historical checkpoints; this section supersedes them for this fix.
## Parallel package preparation and compatibility
On the user's explicit request, independent PV and tariff ports and Optimize-mode
compatibility coverage are being prepared in separate worktrees. The main/integration
configuration prerequisite was refreshed to main4a37244 and PR1305head60b77f6;
285 configuration/migration/Pydantic tests pass. The latest 1305 change only formats
three files. See [PR integration matrix](pr-integration-matrix.md) for the seven
remaining functional packages, existing upstream prerequisites, and combined
Optimize acceptance gates. Parallel work is not authorization to publish every lane.
## Completed parallel checkpoint and first green CI
Source checkpoint b684748 now combines local PV, imported tariff protection,
device physics/cache corrections and the confirmed Optimize failure-path fix.
The combined selection passed 277 tests (3 regular skips); measurement APIs and
persistence then passed 132 tests; regenerated documentation passed all 5 checks.
These are targeted local checks, not a full integration CI run. The complete new
GENETIC orchestration, config-owned request and result/PDF port remain outstanding.
See the updated integration matrix for exact dependencies and limitations.
PR #1322 now has two commits through ce132ea. Its initial mypy error in a test's
optional timestamp assertion was fixed; all current checks are green: 1,884 passed,
16 skipped in pinned Linux/Python 3.13 pytest, plus pre-commit/mypy, CodeQL and
Docker build. PR remains open and mergeable. Only this branch has been published;
no merge or HA deployment occurred. The finite CI follow-up was paused after
verification. Original feature HEAD and all 102 backed-up file hashes remain intact.
+67
View File
@@ -0,0 +1,67 @@
# EOS interface handoff to Home Assistant
2026-09-16 — complete EOS feature port prepared for review in eight PRs.
The combined branch is `feat/genetic-complete`, based on main `4a37244` and the
prerequisite PRs. Use [review handoff](review-handoff.md) for merge order and current
PR links, and [GENETIC rollout](genetic-rollout.md) for configuration and manual
acceptance. Pin the final merged EOS commit after CI and installation acceptance;
the earlier `integration/eos-consolidation-20260916` is a historical checkpoint.
- Configuration collections `devices.batteries`, `electric_vehicles`, `inverters`,
`home_appliances` are maps keyed by stable device ID. A supplied `device_id` must
match its map key; omitted IDs use that key. Old lists migrate. LCOS configuration
uses `levelized_cost_of_storage_amt_kwh`; old files retain their values.
- Algorithm settings are separate under `optimization.genetic` / `genetic0`.
Existing main configuration uses `interval_sec` and `horizon_hours` there.
Do not send feature-branch top-level interval/horizon settings without migration.
- `#1305` runtime bulk/granular changes are retained above file/environment sources;
config persistence is still an explicit existing save operation.
- New measurement routes: PUT/GET `/v1/measurement/samples`, GET
`/v1/measurement/energy`, GET `/v1/measurement/household`, POST
`/v1/measurement/battery-capacity/{battery_id}`. OpenAPI describes request schemas.
Sample times require timezone; values remain in declared raw units, derived energy
is Wh and includes coverage/quality. Missing data is not implicitly zero energy.
- Capacity estimates are separate evidence; even store_estimate=true never replaces
devices.batteries[id].capacity_wh. Explicitly stored estimates survive runtime
bulk changes; existing config save controls disk persistence.
- Python measurement storage-facing methods are async and must be awaited. The HTTP
schema remains ordinary JSON; HA does not need to mirror EOS internals.
- Legacy POST `/optimize` remains GENETIC0. New POST `/v1/optimize` runs GENETIC
from the typed EOS device configuration. Its body accepts runtime `soc`,
`forecasts`, `start_solution` and `start_solution_datetime`; hardware overrides
and query-string overrides are rejected. Request SoC is an integer percentage
keyed by device ID; automatic
measurement lookup uses recent `<device_id>-soc-factor` values from 0 to 1.
Missing, stale, future or invalid observations fail instead of implying zero.
- GENETIC supports 900/3600-second slots. Provider power in W is converted once to
Wh per slot; runtime forecast arrays are already Wh per slot and prices per Wh.
Runtime arrays begin at local midnight. Missing control forecasts fail the run;
shorter forecast tails are clipped with diagnostics. Explicit/imported sale
prices, including zero and negative values, remain authoritative.
- The new optimizer includes timestamp-aligned warmstarts, opt-in battery export,
EV deadlines, flexible consumer profiles and per-cycle windows/completed cycles.
AUTO/FIXED terminal value and forecast-tail diagnostics affect scoring, never
extend the executable control horizon, and remain separate from battery wear.
- Automatic GENETIC runs use the site's timezone derived from its coordinates;
explicit run starts and warmstart timestamps retain their timezone/instant.
Configure device IDs and schedules against this timezone, including DST.
- GET `/v1/energy-management/optimization/solution/{algorithm}` returns the stored
algorithm-specific result. Native/generic results and execution plans publish
atomically for the successful run. Failed Optimize calls return errors rather
than a previous successful result.
- GET `/v1/energy-management/optimization/solution/GENETIC/pdf` renders the stored
GENETIC snapshot on demand (404 before a result exists). The legacy PDF endpoint
and GENETIC0 implementation remain separate. The local calibrated Akkudoktor PV
backend and the measurement APIs are included in the prerequisite PRs.
No HA repository changes, lab deployment, real device control or production config
were performed. Retire the private HA core only after its differences are audited
and a final EOS commit passes the full feature/API acceptance scenarios.
GENETIC currently supports one inverter, one stationary battery, one EV and multiple
household appliances. Unsupported device counts or inconsistent inverter/battery
links are rejected. Verify both 15- and 60-minute Optimize runs, fresh measurements,
tariff units, result/plan timestamps and reports in the actual HA installation.
EOS code and synthetic scenarios are automated-test coverage; they do not validate
HA entities, physical hardware or the remaining private HA-core differences.
+93
View File
@@ -0,0 +1,93 @@
# GENETIC configuration and manual acceptance
Apply the dependencies in [review handoff](review-handoff.md) before the complete
GENETIC PR. These changes implement the feature branch on current EOS interfaces;
they do not migrate a running Home Assistant installation automatically.
## Configuration
Hardware and schedules belong in the typed `devices` maps, keyed by stable device
IDs. Configure an inverter and link its `battery_id` to the configured stationary
battery, or leave it empty when there is no battery. GENETIC currently supports one
inverter, one stationary battery, one EV and multiple household appliances. Explicit
validation rejects unsupported device counts and inconsistent links.
Example optimization settings (merge into an otherwise complete configuration):
```json
{
"optimization": {
"algorithm": "GENETIC",
"genetic": {
"interval_sec": 900,
"horizon_hours": 24,
"tail_horizon_hours": 48,
"terminal_value_mode": "AUTO",
"measurement_max_age_seconds": 300
}
}
}
```
Use `interval_sec: 3600` for hourly operation. AUTO evaluates available forecast
continuation; FIXED uses `terminal_value_euro_per_kwh`. Battery wear/LCOS remains a
separate cost. The tail affects scoring, never the emitted control horizon.
`prediction.hours` must cover the control horizon; shorter available tails are
clipped with diagnostics. Missing control forecasts cancel the run.
Old feature settings such as `optimization.interval` and flat terminal-value fields
migrate into `optimization.genetic`; explicit nested settings win. Check the saved
configuration after migration. GENETIC0 retains its separate settings and `/optimize` API.
Automatic GENETIC runs use the site's timezone derived from its coordinates, even
when the server runs in UTC. An explicitly supplied run start retains its timezone.
Stored results and warmstarts retain explicit timezones and absolute instants.
## Requests and measurements
`POST /v1/optimize` runs GENETIC from configuration. An empty JSON object uses the
configured forecasts and fresh measurements. Runtime input may contain `soc`,
`forecasts`, `start_solution` and `start_solution_datetime`. Hardware overrides in
this body and query-string overrides are rejected. For example, with a configured
device ID `storage`:
```json
{"soc": {"storage": 42}}
```
Request SoC values are integer percentages. Without an override, the measurement
key `<device_id>-soc-factor` must contain a recent factor from 0 to 1. Invalid,
future or stale measurements never become an assumed zero SoC. Completed appliance
cycles use their configured measurement key, default `<device_id>.cycles_completed`.
Optional forecast arrays begin at local midnight, with one value per configured
slot: `pv_forecast_wh` and `total_load` are Wh per slot; `electricity_price_per_wh`
and `feed_in_tariff_per_wh` are currency per Wh. Provider PV/load power in W is
converted by the resolver. Supplied/imported sale tariffs are never replaced by
purchase prices. Missing optional arrays come from the selected providers.
Successful requests return the native result for that run. A failed run returns an
error and cannot masquerade as a cached success. Generic solutions, plans and reports
are derived from the same stored result. Warmstarts retain their source time so a
later run shifts the previous controls to the new slot origin.
Retrieve the new GENETIC report with
`GET /v1/energy-management/optimization/solution/GENETIC/pdf`. It renders the stored
snapshot on demand; a missing result returns 404. The legacy PDF URL remains unchanged.
## Manual acceptance after merge
1. Back up the running configuration and update all merged EOS packages together.
2. Confirm device IDs, inverter linkage, capacities, efficiencies, import/export
limits, tariff units and the battery-export setting against your actual hardware.
3. Check fresh SoC measurements and forecast coverage, then run Optimize at 60 and
15 minutes. Inspect timestamps, slot energies, native/generic results and the PDF.
4. Compare AUTO and FIXED terminal value with identical inputs. Tail diagnostics
must not appear as extra commands; sale prices must retain their configured signs.
5. Exercise an EV departure and a flexible consumer schedule, including a completed
cycle. Check permitted windows, required energy and device commands in your setup.
6. Make a measurement stale or omit control forecast coverage and confirm the request
fails explicitly. Verify the legacy GENETIC0 endpoint if your installation uses it.
Automated checks cover modeled behavior. The final manual test validates the actual
HA entities, provider data and physical installation.
@@ -0,0 +1,25 @@
# fix(optimization): reject invalid imported feed-in tariffs without fallback
Published with user approval as [PR #1324](https://github.com/Akkudoktor-EOS/EOS/pull/1324).
Branch `fix/imported-feedin-main`, head `2a3b961`, base main `4a37244`.
Two commits, two changed files. Original contributions from #1224/#1304 are credited.
GENETIC preparation now cancels if FeedInTariffImport cannot supply a finite
one-dimensional tariff array matching the forecast length. It keeps the configured
provider instead of switching to demo tariffs. Positive, zero and negative valid
amount/Wh revenues remain unchanged. Other providers keep their fallback behavior.
Publication check: 68 passed, 2 regular long-running tests skipped; source Ruff,
format and diff checks passed. Scoped mypy passed for the two changed files with
transitive imports/untyped dependency diagnostics excluded. Full pinned Linux CI: 1921 passed, 16 skipped;
pre-commit including full mypy, CodeQL and Docker passed.
Regression coverage includes seven provider identities, simulation arithmetic and
actual timestamped imports. Eleven invalid-input regressions fail on unchanged main.
The existing forward-fill path is unchanged: validation of its resulting array
does not establish raw timestamp coverage or freshness. Feature direct-marketing
overrides and quarter-hour orchestration remain part of the later GENETIC port.
Legacy /optimize and GENETIC0 are unchanged. The source fix is already integrated
with PV, device/configuration and Optimize-result packages in local combined tests.
No merge or deployment. All current-head checks completed successfully.
@@ -0,0 +1,38 @@
# feat(measurement): add typed energy, quality and capacity APIs
Local branch: `feat/measurement-energy-quality-capacity`.
Review base: `feat/config-integration-base` (`9038b65`).
Eventual target: official main, after its configuration prerequisites land.
Status: published as [PR #1326](https://github.com/Akkudoktor-EOS/EOS/pull/1326), head `635ff2d`, against the
prerequisite branch.
## Proposed PR body
Add typed measurement channels with explicit units and timestamp semantics, sample
quality, energy integration, household energy balances and battery capacity estimates.
Adapt persistence and REST access to main's asynchronous storage. Battery estimates
use keyed device identities and remain separate from the configured active capacity;
storing an estimate requires an explicit request and preserves runtime config updates.
Expose `/v1/measurement/samples`, `/v1/measurement/energy`,
`/v1/measurement/household` and `/v1/measurement/battery-capacity/{battery_id}`.
Regenerate the configuration and OpenAPI contracts. Use synthetic data only.
Validation: 453 configuration/measurement/device simulation tests and 5 documentation
tests pass. A further 74 measurement/capacity tests pass after carrying over fixture
isolation. Ruff passes for measurement source and the measurement REST module.
Final pinned Linux/Python 3.13 CI: 2046 passed, 16 skipped; pre-commit with full mypy and Docker passed.
CodeQL becomes applicable after retargeting to main.
Depends on the device maps/converters in #1256, runtime configuration in #1305 and
local fixes for stable device IDs, LCOS migration and charge-rate compatibility.
Includes the independently prepared JSON restore fix; it should land separately first.
No new GENETIC orchestration or battery/inverter physics is included in this branch.
## Submission gate
Do not open this whole branch against main now: its ancestry still includes the
unmerged configuration PRs. Preserve those contributors' existing PRs and credit.
Once prerequisites are merged, rebuild/rebase the measurement package onto that
main, inspect the resulting diff and rerun combined tests and CodeQL before merging.
The comparison to `feat/config-integration-base` isolates today's measurement work.
@@ -0,0 +1,41 @@
# fix(measurement): restore JSON records into the existing singleton
Target: `Akkudoktor-EOS/EOS:main`.
Local branch: `fix/measurement-json-reload`.
Local head: `ce132ea` (based on main `4a37244`).
Status: published with explicit user approval as [PR #1322](https://github.com/Akkudoktor-EOS/EOS/pull/1322).
Two commits, two files; published head and diff verified. Not merged.
The second commit adds an explicit non-null timestamp assertion after CI mypy
flagged the test. All 49 local tests still pass. Pre-commit (including mypy),
CodeQL and Docker build passed on ce132ea. Full pinned Linux/Python 3.13 CI pytest
also passed: 1,884 passed, 16 skipped, including --finalize and config side-effect
checks (run 35118801197). PR remains open and mergeable; no merge performed.
## Proposed PR body
When measurement persistence falls back to JSON, loading a saved file reports success
but does not restore its records: validating a second `Measurement` returns the
existing singleton. Parse and validate the individual records before inserting them
into that singleton. Invalid files now return `False` instead of reporting success.
Regression coverage checks round trips, timestamp preservation, repeated loading,
merging with existing timestamps, malformed files without partial validation writes,
and database-provider precedence. Four regression cases fail on unchanged main.
With the fix, all 49 measurement tests pass.
Validation: `python -m pytest tests/test_measurement_file_restore.py tests/test_measurement.py -q`;
Ruff check and format check of the changed source; `git diff --check`.
Run locally on Windows/Python 3.11.9, followed by successful pinned Linux/Python 3.13
CI and its complete test suite as recorded above.
No settings or API schema changes. This fix is independent of #1256, #1305 and the
GENETIC port.
## Exact review scope
- `src/akkudoktoreos/measurement/measurement.py`
- `tests/test_measurement_file_restore.py`
Only these two files differ from the pinned main. Do not publish the integration
branch as part of this PR. Publication approval was received on 2026-09-16. Only this named branch was pushed.
Inspect CI and review before any merge; merging was not part of this publication request.
@@ -0,0 +1,24 @@
# fix(optimization): return only the current completed Optimize result
Published with user approval as [PR #1323](https://github.com/Akkudoktor-EOS/EOS/pull/1323).
Branch: `fix/optimize-run-result`; base main `4a37244`; head `ef8d913`.
Three commits, three changed files. No merge or deployment.
A failed explicit optimization previously returned an old cached solution as HTTP
200. Conversion failures could publish a new native result alongside the previous
generic result and plan. Return this run's completed solution directly to the route;
build all three representations before publishing them together. Preserve previous
consistent results after optimizer/conversion errors and skip the failed run's
control dispatch. Legacy `/optimize` remains GENETIC0; automatic mode keeps its
configured algorithm selection. No optimizer mathematics or slot behavior changes.
Validation: 38 tests passed, 2 regular long-running tests skipped. All 22 new
regression cases passed again after typing dynamic test keyword arguments. Source
Ruff, formatting and diff checks passed. Scoped mypy passed for all three changed
files (transitive imports/untyped dependency diagnostics excluded locally).
Pinned Linux CI: 1908 passed, 16 skipped; pre-commit including full mypy, CodeQL and Docker passed. Thirty
regressions also passed separately in UTC and Europe/Berlin; timezone expectations are now explicit.
The fix is already combined with PV, tariff and device packages in the integration
branch. Integration also carries the test annotation correction. Independent of
`#1322`, #1256 and #1305. Original working copy remains untouched.
+125
View File
@@ -0,0 +1,125 @@
# Remaining EOS packages and Optimize compatibility
Historical planning snapshot. The remaining packages below are now implemented;
see [review handoff](review-handoff.md) for current dependencies and acceptance.
Snapshot: 2026-09-16. Official main: `4a37244`. Feature source: `d2e2d58`, plus
separately backed-up local work. This is a functional package estimate, not a claim
that every differing commit requires its own PR.
## What is still missing on main
<!-- pyml disable line-length -->
| Package | Missing behavior relative to feature/local work | Local state | Dependency |
| --- | --- | --- | --- |
| Device physics | Slot-duration-aware battery/inverter flows, export control, efficiency and limits | Published as #1327 against the configuration prerequisite branch; combined in integration | #1256 device settings/converters |
| Complete GENETIC | Quarter-hour orchestration, adaptive evolution, export states, warmstart alignment, forecast tail/terminal value, EV deadlines and flexible consumer profiles | Physics and primitives exist; orchestration/parameter/output integration remains open | Device physics, #1256, tariff contract |
| Imported tariff protection | Preserve supplied/imported revenue; avoid silent demo or market-price replacement | Published as #1324 and combined in integration; feature-specific override still required inside GENETIC port | Main patch independent; second part belongs with GENETIC |
| Local calibrated PV | Local Akkudoktor PV calculation, measurement calibration and outage handling under existing provider ID | Published as #1325 against main; combined in integration | Provider-specific settings; combined forecast/optimizer acceptance later |
| Measurement APIs | Typed channels, quality, energy integration, household balance and capacity estimate APIs | Published as #1326 against the configuration prerequisite branch | #1256, #1305, configuration corrections; JSON fix #1322 |
| Config-owned Optimize request | Local ConfigOptimizationRequest, /v1/optimize, runtime observations and common parameter resolver | Backed up; async/maps/converters adaptation pending | New GENETIC and #1305 |
| Result/PDF output | Quarter-hour, flexible-consumer, export, tail/rest-value diagnostics in main's on-demand algorithm-specific output | Pending | Final GENETIC result contract |
<!-- pyml enable line-length -->
Four of the seven feature packages are now published as #1324–#1327. Three broader
packages remain unported: complete GENETIC, the config-owned Optimize API, and result/PDF
output. Their final PR split depends on the resulting contracts. #1256 and #1305 are existing
foundation PRs, not two newly invented replacements. #1224/#1304 already address
parts of the tariff work against the old feature branch; preserve/reconcile those
contributions rather than count duplicate implementations as separate deliverables.
PR #1322 is already published and is additional to this feature table.
One additional standalone defect fix is published as PR #1323, fix/optimize-run-result:
an explicit Optimize request must return only its own completed result. Cache
method identity was also corrected as a prerequisite within the device package;
it does not currently add another planned feature PR.
Local helper scripts and private HA-core divergence are not silently included in this
count. They remain separately secured/to be audited. Changelog/release work follows
acceptance; it is not another optimizer implementation.
## Parallel lanes
- Local PV: `feat/local-pv-main-port`, sibling worktree `EOS-pr-local-pv`.
- Tariff preparation: `fix/imported-feedin-main`, sibling `EOS-pr-feedin-main`.
- Device physics: `feat/slot-device-physics`, sibling `EOS-pr-device-physics`.
- Optimize failure correction: `fix/optimize-run-result`, sibling
`EOS-reference-optimize-main` (now a named review branch).
- Optimize compatibility tests: `test/optimize-pr-contracts`, sibling
`EOS-pr-optimize-contracts`. These are shared acceptance coverage, not necessarily
an extra standalone public PR.
- Root/integration: configuration prerequisites, shared API/algorithm contracts and
eventual combination of the tested packages.
Do not have multiple lanes independently rewrite geneticparams.py, EMS.run or the
same configuration structure. A tested tariff patch will be carried into the core
port; the core port must keep its regression cases. PV keeps the public
PVForecastAkkudoktor ID and existing remote behavior unless explicitly selected.
Parallel preparation does not imply parallel unreviewed merges or publishing all
branches. The JSON fix #1322 and subsequently the Optimize fix #1323 have publication
approval. Tariff fix #1324 was subsequently authorized and published. The user then
authorized all prepared packages: PV #1325, measurement #1326 and device physics
`#1327` are also published. The latter two target the explicit configuration comparison
branch 9038b65 pending their prerequisites. No upstream merges were performed.
## Three different Optimize contracts
1. Legacy POST `/optimize` on main explicitly runs GENETIC0, hourly. Existing payload
and legacy response aliases must keep working regardless of configured default.
2. Automatic EMS with `ems.mode=OPTIMIZATION` selects `optimization.algorithm`:
GENETIC or GENETIC0. Preparation and solution conversion are asynchronous.
PREDICTION and DISABLED must not accidentally run optimization or dispatch controls.
3. The feature worktree's LOCAL POST `/v1/optimize` and ConfigOptimizationRequest are
not present on main/integration yet. They must be adapted to current maps,
converters and algorithm-specific settings. Old feature `/optimize` meant a
different optimizer; clients need explicit migration, not a silent route switch.
Current blockers: GENETIC.prepare still forces interval_sec to 3600 and EMS start
alignment floors to the hour. Fifteen-minute device tests do not prove quarter-hour
Optimize-mode support. The core package must change preparation, slot alignment,
optimization and response metadata together.
Confirmed on unchanged main 4a37244: after a failed explicit optimization, the HTTP
route returned the previous stored solution with HTTP 200. Conversion errors could
also publish a new native result alongside an old generic result/plan. The local
fix prepares all three before publishing and returns the successful current-run
result directly to the route. Errors retain the previous coherent trio, restore
IDLE and do not dispatch controls for that failed optimization. Strict passing
regression tests replace the initial expected-failure audit cases.
## Shared acceptance before dependent PRs can land
<!-- pyml disable line-length -->
| Area | Required combined check |
| --- | --- |
| Routing | Legacy /optimize remains GENETIC0; automatic mode selects the configured algorithm; the new explicit API selects GENETIC deliberately |
| Configuration | Stable device IDs/maps and converters, runtime changes retained, old aliases migrated without losing values |
| Time | 900/3600-second slots, non-hour-aligned start and advancing warmstart, timezone/DST boundaries, matching output timestamps |
| Units | PV/load predictions are W; integrate once to slot Wh. `feed_in_tariff_wh` and `elecprice_marketprice_wh` already yield amount/Wh; convert amount/kWh configuration exactly once |
| Tariffs | Positive/zero/negative/imported revenue remains distinct from purchase cost; missing or invalid imported data does not silently become demo data |
| Devices | SOC/energy balance, charge/discharge and inverter limits, allowed/blocked battery export |
| Consumers | Feature profiles/deadlines and #1256 per-cycle windows/gaps both preserved; impossible schedules fail explicitly |
| Results | Raw algorithm result, generic optimization solution, execution plan and on-demand PDF agree on slots and device IDs |
| Errors | Failed preparation/optimization produces no new control dispatch and no misleading fresh-success response using old results |
<!-- pyml enable line-length -->
A passing individual PR is insufficient: after combining dependent packages, run
these synthetic end-to-end API cases together with the GENETIC0 regression suite.
No device control, HA deployment or production configuration is part of this work.
## Completed combined check
Integration source checkpoint b684748 combines tariff b2a4e2f, Optimize fix 8ed65ec,
device/cache corrections be184a6 and 38eb4ad, and local PV 84abe05, on the refreshed
configuration base. The combined API failure/algorithm-selection, tariff, cache,
battery/inverter, PV, configuration/migration and both optimizer test selection
passed: **277 passed, 3 regular skips**. No expected-failure markers were used to
hide Optimize defects. XML: private backup integration-parallel-compatibility.xml.
The complete measurement/channel/energy/household/capacity selection additionally
passed **132 tests** on this combined source checkpoint, including JSON persistence.
This is local Windows/Python 3.11 verification, not full pinned CI or acceptance of
the pending new GENETIC. The wider standalone PV suite separately reproduced an
existing Windows file-timestamp test failure on unchanged main; that test was
explicitly excluded from its reported 164-passing selection. Forecast retention
does not establish complete horizon coverage; that remains a core-port gate.
+134
View File
@@ -0,0 +1,134 @@
# EOS: Arbeitsstand und Weg zu kleinen PRs
Historischer Zwischenstand. Die damals offenen GENETIC-Pakete sind inzwischen
implementiert. Maßgeblich sind [Review-Handoff](review-handoff.md) für die acht PRs,
Abhängigkeiten und Merge-Reihenfolge sowie [GENETIC-Rollout](genetic-rollout.md)
für Konfiguration und manuelle Abnahme. Die Statusangaben unten dokumentieren
frühere Arbeitsschritte.
Stand: 16.09.2026. Offizielles main für PR #1322: `4a37244`.
Die lokale Integration enthält ebenfalls diesen main-Stand und den aktualisierten
Stand von #1305 (`60b77f6`).
## Was jetzt möglich ist
Ein unabhängiger, lokal getesteter PR ist veröffentlicht: `fix/measurement-json-reload`
auf aktuellem main. Sein Worktree ist `../EOS-pr-measurement-json`; er enthält nur
zwei geänderte Dateien. Der veröffentlichte [PR #1322](https://github.com/Akkudoktor-EOS/EOS/pull/1322)
enthält zwei Commits bis `ce132ea`. Der PR-Text steht in
[measurement-json-reload.md](pr-drafts/measurement-json-reload.md).
Das ist ein konkreter Einstieg in den PR-Workflow. Die vollständige Übernahme aller
Funktionen aus dem alten Feature-Branch ist noch NICHT abgeschlossen.
## Branches und ihre Aufgaben
<!-- pyml disable line-length -->
| Branch | Zweck | Freigabezustand |
| --- | --- | --- |
| `fix/measurement-json-reload` | Kleiner JSON-Ladefehler direkt auf main | Veröffentlicht als #1322; gesamte CI grün, noch nicht gemergt |
| `feat/config-integration-base` | Zusammengeführte #1256/#1305 plus Integrationskorrekturen | Veröffentlichter Vergleichsbranch 9038b65; kein konkurrierender Sammel-PR |
| `feat/measurement-energy-quality-capacity` | Messdatenfunktionen ohne neue Optimiererphysik | PR #1326 gegen Konfigurationsbasis; später auf main umstellen |
| `fix/imported-feedin-main` | Importierte Einspeisetarife erhalten und prüfen | Veröffentlicht als #1324; CI läuft |
| `feat/local-pv-main-port` | Lokale PV-Prognose und Kalibrierung | PR #1325 gegen main |
| `feat/slot-device-physics` | Slotphysik und getrennte Cache-Methoden | PR #1327 gegen Konfigurationsbasis; später auf main umstellen |
| `fix/optimize-run-result` | Nur das Ergebnis des erfolgreichen aktuellen Laufs zurückgeben | Veröffentlicht als #1323; CI läuft |
| `integration/eos-consolidation-20260916` | Zusammenführung und Prüfung aller Portierungspakete | Unvollständig; kein Gesamt-PR und kein HA-Release |
| `feat/direct-marketing-battery-grid-export` | Ursprüngliche Entwicklung mit lokalen Änderungen | Unverändert erhalten und gesichert |
<!-- pyml enable line-length -->
Der Worktree `../EOS-pr-measurement` gehört zum Messdatenpaket.
`../EOS-integration-20260916` bleibt der zeitlich begrenzte Portierungsarbeitsplatz.
`../EOS-reference-20260916` bleibt der unveränderte Vergleichsstand für Basisfehler.
Ein Worktree ist nur ein Arbeitsverzeichnis; Gegenstand eines PRs ist der Branch.
## Umgesetzt und geprüft
- Geräte-Maps, Algorithmuskonvertierung und Laufzeitkonfiguration aus #1256/#1305.
- Korrekturen für stabile Geräte-IDs, LCOS-Migration und Ladeleistungslisten.
- Messkanäle, Qualität, Energieintegration, Haushaltsbilanz und Kapazitätsschätzung,
einschließlich asynchroner Speicherung und REST-Schnittstellen.
- Im Integrationsbranch außerdem Viertelstunden-Gerätephysik, begrenzter
Batterieexport und Wirkungsgrade; Restwert-/Prognose-Nachlauf-Bausteine.
- GENETIC0 bleibt separat. Sein `/optimize`-Endpunkt beweist keine vollständige
Portierung des neuen GENETIC.
## Noch offen
1. Neues GENETIC vollständig auf main-Strukturen anpassen: Viertelstundenplanung,
Exportzustände, adaptive Evolution und zeitlich korrekter Warmstart.
2. Flexible Lastprofile/EV-Fristen mit den Mehrfachzyklen und Zeitfenstern aus
`#1256` verbinden. Beide vorhandenen Funktionssätze müssen erhalten bleiben.
3. Horizont, Prognoselücken, Nachlauf und Restwert mit Optimierer und Ergebnissen
verdrahten; bisher sind nur die Bausteine übernommen.
4. Den bereits portierten Tarifschutz auch in der neuen GENETIC-Parametervorbereitung
erhalten; dort Prognosegrenzen und Lücken verbindlich prüfen.
5. Algorithmusspezifische PDF-Ausgabe und die lokale Konfigurationslern-Anfrage
integrieren. Die lokale kalibrierte PV-Prognose ist inzwischen portiert.
6. Gesamtabnahme einschließlich API-Weg des neuen GENETIC und gepinnter CI.
Danach erst Übergabe eines festen EOS-Commits an HA und Release-Arbeiten.
## PR-Reihenfolge
1. JSON-Fix als PR #1322 veröffentlicht: CI und Review prüfen, danach über Merge entscheiden.
2. `#1256/#1305` über ihre vorhandenen PRs zusammenführen; lokale Korrekturen dort
zuordnen. Keine pauschale Veröffentlichung der kombinierten Integrationsbasis.
3. Das isolierte Messdatenpaket auf diesen main-Stand setzen, Diff prüfen und
nochmals testen; dann als eigenen PR einreichen.
4. Optimierer, Tarifschutz, PV und Ausgabe jeweils als abgegrenzte Pakete fertigstellen.
Abhängige PRs ausdrücklich als solche behandeln.
Für jede neue unabhängige Änderung: aktuellen `refs/remotes/origin/main` holen,
einen Themenbranch mit eigenem Worktree starten, lokal testen, den Diff prüfen,
dann PR gegen main. Nach Review und grüner CI mergen. Alte Worktrees erst nach
abgeschlossener Übernahme und Prüfung lokaler Änderungen aufräumen.
Wegen der vorhandenen gleichnamigen lokalen Branch-Referenz ausdrücklich
`refs/remotes/origin/main` verwenden. Keine neuen unabhängigen Funktionen auf den
alten großen Feature-Branch oder die Integrationsbasis stapeln.
## Kann die laufende Entwicklung schon umziehen?
Unabhängige Fehlerkorrekturen und neue Funktionen können ab jetzt in Themenbranches
auf main erfolgen. Für Entwicklung, die den vollständigen neuen GENETIC oder die
noch fehlenden Funktionen benötigt, ist der Integrationsstand noch nicht abgenommen.
Die ursprüngliche Arbeitskopie bleibt erhalten. JSON-Fix #1322, Optimize-Fix #1323
und Tarifschutz #1324 sind veröffentlicht. Auf weitere Freigabe folgten PV #1325,
Messdaten #1326 und Gerätephysik #1327; nichts wurde gemergt oder deployt.
Der aktuelle Review-Überblick steht in [review-handoff.md](review-handoff.md).
## HA-Übergabe
Noch keinen neuen Gesamtstand pinnen oder deployen. Die geplante Schnittstelle nutzt
Geräte-Maps mit stabilen IDs, `levelized_cost_of_storage_amt_kwh`, asynchrone
Messdatenzugriffe und getrennte GENETIC/GENETIC0-Pfade. Details stehen in
[eos-ha-handoff.md](eos-ha-handoff.md). HA-Dateien wurden nicht verändert.
## Nachweise und Grenzen
Der JSON-PR: 49 bestandene Tests, Ruff und Formatprüfung.
Das isolierte Messdatenpaket: 453 bestandene Tests plus 5 Dokumentationstests;
74 Tests nach Übernahme der Fixture-Isolation nochmals erfolgreich.
XML-Protokolle liegen in der privaten Sicherung `eos-20260916-120324`.
Für PR #1322 ist die gepinnte Linux/Python-3.13-CI inzwischen bestätigt:
1.884 Tests bestanden, 16 übersprungen; Pre-commit/Mypy, CodeQL und Docker-Build
erfolgreich auf `ce132ea`. Die aktuellen CI-Ergebnisse aller sechs PRs stehen im Review-Handoff.
Zusätzlicher Integrationslauf: 764 Tests bestanden, 3 übersprungen; zwei zunächst
fehlgeschlagene Dokumentationsvergleiche betrafen ausschließlich die Versionsangabe.
Nach Neugenerierung bestanden alle 5 Dokumentationstests. Darunter sind außerdem
128 bestandene Energy-Charts-Regressionen zum neuen main-Commit dokumentiert.
Früherer gemeinsamer Source-Stand `b684748`: 277 Tests bestanden, 3 regulär
übersprungen, für PV, Tarifschutz, Gerätephysik, Cache, Konfiguration und beide
bisherigen Optimierer einschließlich API-Fehlerbehandlung. Anschließend bestanden
alle 132 Messdaten-/Haushalts-/Kapazitätsprüfungen auf diesem gemeinsamen Stand.
Die vollständige neue GENETIC-Orchestrierung bleibt offen. Pakete und
Kompatibilitätsbedingungen stehen in [pr-integration-matrix.md](pr-integration-matrix.md).
Die neu generierte gemeinsame Dokumentation besteht ebenfalls alle fünf Prüfungen.
Aktueller Abschluss: Alle sechs veröffentlichten PRs haben ihre vorgesehenen
GitHub-Prüfungen bestanden. Gemeinsamer Stand: 253 Dateien ohne Mypy-Fehler,
37 gezielte Nachprüfungen und vollständiger Sphinx-Build erfolgreich. Die Grenzen
des Windows-Gesamtlaufs und die nachgewiesenen main-Baselinefehler sind im
[Review-Handoff](review-handoff.md) dokumentiert. Nichts wurde gemergt oder deployt.
+60
View File
@@ -0,0 +1,60 @@
# EOS review and merge handoff
The consolidation implements the remaining GENETIC optimizer, configuration-owned
Optimize request and result/PDF output. See [GENETIC rollout](genetic-rollout.md)
for configuration and manual acceptance. Historical planning documents in this
directory describe earlier checkpoints; this handoff supersedes their pending-work lists.
## Integration status
The seven prerequisite packages #1322-#1328 are merged into main. PR #1329 was
merged into `integration/genetic-prerequisites`, not into main. The final delivery
branch `feat/genetic-complete-main-port` brings that complete implementation directly
to main. Until that final PR is merged, main lacks the complete GENETIC port.
The foundation preserves the original #1256/#1305 contributions and compatibility
corrections; do not merge those original PRs again as extra prerequisites.
Main `3c86254` has exactly the production source tree of the original prerequisite
integration `5eacd54`. Only generated API version strings and one corrected test
import differ. The final delivery preserves the complete production source and
tests from #1329, including that corrected import, and regenerates API/configuration
documents from the combined code. Its merge conflicts arise from the rewritten
squash ancestry, not from additional production changes on main.
Review and merge the final PR with **main** as its target and all four current-head
checks green: pytest, pre-commit, Docker and CodeQL. Squash and merge is supported.
Do not use a merge into the comparison branch as a release. Keep the original
working copy and backup until manual installation acceptance is complete.
## Review and validation
- Optimize: preparation or conversion failure never returns a previous successful
result; native result, generic solution and execution plan publish atomically.
- Forecasts: provider power is converted from W to slot Wh exactly once. Raw missing
records stay missing, control coverage is mandatory, and a shorter tail is clipped.
- Economics: explicit/imported sale prices remain authoritative, including zero and
negative values. Battery export is opt-in; terminal value is separate from LCOS.
- Time: 15/60-minute slots, repeated DST hours, local-midnight forecast origins and
non-integer timezone offsets are covered. Old GENETIC reports use saved timestamps.
- Devices: EV deadlines, flexible power profiles, crossed per-cycle windows, completed
cycles and minimum gaps are tested through the optimizer and result conversion.
- Compatibility: GENETIC0 keeps its legacy request and device implementation. Both
algorithms retain finite solution validity and existing persistent plan instructions.
Exact workflow results belong to each PR's current head; superseded green heads do
not prove a later revision. The combined checks cover pinned mypy, generated OpenAPI
and configuration, physics, native HTTP, automatic preparation, PDF generation and
the 400-generation optimizer regression.
Local Windows server-PID tests and Docker builds have known environment failures
that also reproduce on unchanged main. Linux CI remains the full-suite gate. Local
PDF pixel comparison needs an optional converter; report semantics and PDF bytes
are tested independently. Production HA/device behavior still needs manual acceptance.
## Preservation and release boundary
The original feature working copy is read-only. Its original HEAD and 102 saved files
are checked against the private preservation manifest. Runtime configuration, secrets
and real measurements are excluded from the PRs. No remote PR is merged and no HA
configuration, production server or physical device is changed by this preparation.