Conversation
Document the design for price-based peak shaving using a new "next" logic module that limits PV-to-battery charging (mode 8) during low-price hours to maximize grid feed-in and reserve battery capacity for peak solar hours. https://claude.ai/code/session_013TjsRDZGUJM5YT15HaS6YK
- Rename EVCC section: use existing charging state only, drop chargePower subscription - Algorithm uses net PV surplus (production - consumption) instead of raw production - Move peak shaving config to CalculationParameters (consistent with existing interface) - Add always_allow_discharge region bypass for high SOC - Add force_charge (MODE -1) priority over peak shaving with warning log - Remove unused ev_charge_power field from data model - Add known limitations section (flat charge distribution, no intra-day adjustment) - Reduce modified files from 9 to 6 https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
EVCC is an external integration concern, not part of calculation logic. The evcc_is_charging guard now lives in core.py (similar to discharge_blocked), keeping the logic layer clean and independent of EVCC. - Remove evcc_is_charging from CalculationParameters - Add EVCC charging guard in core.py after logic.calculate() - Remove EVCC check from _apply_peak_shaving() in default.py - Update tests section accordingly https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
…opics Major changes: - Peak shaving lives in new NextLogic class (type: next), DefaultLogic untouched - EVCC: subscribe to loadpoint mode + connected topics (derived from loadpoint_topic) - Disable peak shaving when EV connected + mode=pv (immediately, not just on charging) - evcc_ev_expects_pv_surplus property for the connected+pv check - Updated file list: new next.py, evcc_api.py now modified, default.py not modified https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
- Skip peak shaving calculation when current production <= 0 (no PV at night) - Add docs/peak_shaving.md to implementation order and file list https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
Document the design for price-based peak shaving using a new "next" logic module that limits PV-to-battery charging (mode 8) during low-price hours to maximize grid feed-in and reserve battery capacity for peak solar hours. https://claude.ai/code/session_013TjsRDZGUJM5YT15HaS6YK
- Rename EVCC section: use existing charging state only, drop chargePower subscription - Algorithm uses net PV surplus (production - consumption) instead of raw production - Move peak shaving config to CalculationParameters (consistent with existing interface) - Add always_allow_discharge region bypass for high SOC - Add force_charge (MODE -1) priority over peak shaving with warning log - Remove unused ev_charge_power field from data model - Add known limitations section (flat charge distribution, no intra-day adjustment) - Reduce modified files from 9 to 6 https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
EVCC is an external integration concern, not part of calculation logic. The evcc_is_charging guard now lives in core.py (similar to discharge_blocked), keeping the logic layer clean and independent of EVCC. - Remove evcc_is_charging from CalculationParameters - Add EVCC charging guard in core.py after logic.calculate() - Remove EVCC check from _apply_peak_shaving() in default.py - Update tests section accordingly https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
…opics Major changes: - Peak shaving lives in new NextLogic class (type: next), DefaultLogic untouched - EVCC: subscribe to loadpoint mode + connected topics (derived from loadpoint_topic) - Disable peak shaving when EV connected + mode=pv (immediately, not just on charging) - evcc_ev_expects_pv_surplus property for the connected+pv check - Updated file list: new next.py, evcc_api.py now modified, default.py not modified https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
- Skip peak shaving calculation when current production <= 0 (no PV at night) - Add docs/peak_shaving.md to implementation order and file list https://claude.ai/code/session_01T62gAfD8CBB9uSU4w3uq1y
… feat-peak-shaving
…ndow and ensure valid reserves within it
…ear ramp and update related tests
…ic and update limitations
There was a problem hiding this comment.
Pull request overview
Adds a new opt-in battery control logic (type: next) that implements “peak shaving” (throttling PV charging so the battery fills later in the day), with supporting evcc-topic handling, MQTT entities, documentation, and extensive tests/simulation scripts.
Changes:
- Introduce
NextLogicwith peak shaving (time/price/combined modes) and new peak-shaving fields inCalculationParameters. - Extend core + MQTT integration to configure, publish, and remotely toggle peak shaving; extend evcc MQTT handling with derived
/modeand/connectedtopics for guard logic. - Add docs (
WIKI_peak_shaving.md,PLAN.md), scripts for simulation, and new/updated tests.
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/batcontrol/test_mqtt_api.py | Adds regression tests for MQTT bytes payload decoding + peak_shaving enabled parsing. |
| tests/batcontrol/test_evcc_mode.py | Adds tests for evcc derived mode/connected topics and related state logic. |
| tests/batcontrol/test_core.py | Adds tests around evcc peak-shaving guard behavior (plus minor comment tweaks). |
| tests/batcontrol/logic/test_peak_shaving.py | Comprehensive test suite for peak shaving algorithms and logic factory selection. |
| tests/batcontrol/logic/test_default.py | Minor comment wording update (EUR). |
| tests/batcontrol/logic/test_common.py | Adds tests for enforce_min_pv_charge_rate. |
| tests/batcontrol/forecastsolar/test_baseclass_alignment.py | ASCII-only comment updates. |
| tests/batcontrol/dynamictariff/test_tariffzones.py | ASCII-only comment updates. |
| tests/batcontrol/dynamictariff/test_evcc.py | Docstring/comment casing updates (“evcc”). |
| tests/batcontrol/dynamictariff/test_baseclass.py | Docstring/comment casing updates (“evcc”). |
| src/batcontrol/mqtt_api.py | Decode bytes payloads before conversion; add peak shaving publish + HA discovery entities. |
| src/batcontrol/logic/next.py | New NextLogic implementing peak shaving as a post-processing step. |
| src/batcontrol/logic/logic.py | Add next logic selection; read logic type from battery_control.type. |
| src/batcontrol/logic/logic_interface.py | Extend CalculationParameters with peak shaving config + validation. |
| src/batcontrol/logic/common.py | Add enforce_min_pv_charge_rate helper for peak shaving limits. |
| src/batcontrol/logic/init.py | Export NextLogic. |
| src/batcontrol/inverter/fronius.py | Replace non-ASCII umlaut in error string. |
| src/batcontrol/interval_utils.py | ASCII-only comment updates. |
| src/batcontrol/forecastsolar/evcc_solar.py | Comment casing update (“evcc”). |
| src/batcontrol/forecastsolar/baseclass.py | ASCII-only log message updates. |
| src/batcontrol/forecastconsumption/baseclass.py | ASCII-only log message updates. |
| src/batcontrol/evcc_api.py | Add derived mode/connected topic subscriptions + state tracking and evcc_ev_expects_pv_surplus. |
| src/batcontrol/dynamictariff/tariffzones.py | ASCII-only docstring/comment updates. |
| src/batcontrol/dynamictariff/evcc.py | ASCII-only doc/log updates (“evcc”). |
| src/batcontrol/dynamictariff/baseclass.py | ASCII-only doc/log updates. |
| src/batcontrol/core.py | Add peak shaving config parsing, evcc guard, MQTT callbacks, and charge-limit publishing. |
| scripts/verify_production_offset.py | Replace non-ASCII checkmark in output. |
| scripts/test_evcc.py | Replace “€/kWh” with “EUR/kWh” in output. |
| scripts/simulate_peak_shaving_price.py | New script to simulate price-based peak shaving behavior. |
| scripts/simulate_peak_shaving_day.py | New script to simulate time-based peak shaving behavior. |
| PLAN.md | Detailed implementation plan for peak shaving + evcc/MQTT integration. |
| docs/WIKI_peak_shaving.md | User-facing documentation for peak shaving configuration/behavior. |
| docs/15-min-transform.md | Documentation casing/ASCII updates (“evcc”). |
| config/batcontrol_config_dummy.yaml | Add battery_control.type and a peak_shaving config section. |
| .github/copilot-instructions.md | Add repo guideline: ASCII-only in code/logs/docs. |
| self.min_price_difference, | ||
| self.min_price_difference_rel, | ||
| self.get_max_capacity() | ||
| self.get_max_capacity(), | ||
| peak_shaving_enabled=peak_shaving_config_enabled and not evcc_disable_peak_shaving, | ||
| peak_shaving_allow_full_after=self.peak_shaving_config.allow_full_battery_after, |
There was a problem hiding this comment.
evcc_disable_peak_shaving is only defined inside the if peak_shaving_config_enabled: block, but it is referenced unconditionally when building CalculationParameters. If peak shaving is disabled in config, this will raise an UnboundLocalError at runtime. Initialize evcc_disable_peak_shaving = False before the if peak_shaving_config_enabled: (and optionally reset _evcc_peak_shaving_disabled when peak shaving is disabled).
| @@ -1,5 +1,6 @@ | |||
| from abc import ABC, abstractmethod | |||
| from dataclasses import dataclass | |||
| from dataclasses import dataclass, field | |||
There was a problem hiding this comment.
field is imported but never used. This will trip linting (pylint/flake) and should be removed to keep imports clean.
| from dataclasses import dataclass, field | |
| from dataclasses import dataclass |
| max_charging_from_grid_limit=0.8, | ||
| min_price_difference=0.05, | ||
| min_price_difference_rel=0.0, | ||
| max_capacity=10000, | ||
| peak_shaving_enabled=False and not evcc_disable_peak_shaving, |
There was a problem hiding this comment.
This test appears internally inconsistent: it claims to cover the case where peak shaving is off in config, but the fixture config sets peak_shaving.enabled to True, and the assertion is forced by peak_shaving_enabled=False and ... (always False). This makes the test non-informative and risks masking regressions; consider either flipping the fixture config to enabled: False for this test or deriving the value from peak_shaving_config.get('enabled', False) like the other cases.
Introduces batcontrol logic "next", which will be frontrunning on larger changes.
Here we include several features for enabling batcontrol for peak shaving. This means we are not charging the battery in the morning to capture solar peak into the battery and avoid putting stress on the grid.