Skip to content

Feature "Peak Shaving"#315

Open
MaStr wants to merge 36 commits intomainfrom
feat-peak-shaving
Open

Feature "Peak Shaving"#315
MaStr wants to merge 36 commits intomainfrom
feat-peak-shaving

Conversation

@MaStr
Copy link
Copy Markdown
Owner

@MaStr MaStr commented Apr 7, 2026

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.

claude and others added 30 commits March 13, 2026 15:13
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
@MaStr MaStr added this to the 0.8.0 milestone Apr 7, 2026
@MaStr MaStr added the enhancement New feature or request label Apr 7, 2026
Copilot AI review requested due to automatic review settings April 7, 2026 16:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 NextLogic with peak shaving (time/price/combined modes) and new peak-shaving fields in CalculationParameters.
  • Extend core + MQTT integration to configure, publish, and remotely toggle peak shaving; extend evcc MQTT handling with derived /mode and /connected topics 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.

Comment on lines 572 to +576
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,
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
@@ -1,5 +1,6 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from dataclasses import dataclass, field
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

field is imported but never used. This will trip linting (pylint/flake) and should be removed to keep imports clean.

Suggested change
from dataclasses import dataclass, field
from dataclasses import dataclass

Copilot uses AI. Check for mistakes.
Comment on lines +463 to +467
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,
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants