Skip to content

Optimizer: derive per-device action suggestions from result#30834

Merged
andig merged 9 commits into
masterfrom
feat/optimizer-suggestions-reapply
Jul 10, 2026
Merged

Optimizer: derive per-device action suggestions from result#30834
andig merged 9 commits into
masterfrom
feat/optimizer-suggestions-reapply

Conversation

@andig

@andig andig commented Jun 14, 2026

Copy link
Copy Markdown
Member

Replaces reverted #30783, builds on #31383

Converts the optimizer schedule into a per-device advisory action for the current slot, published on the battery device and as a dedicated loadpoint key. Because the optimization is linear, the first slot sits at an operating-range extreme, so it maps cleanly onto the discrete action that control would later apply. Advisory only, control is intentionally not wired up yet.

💡 new optimizer icon (magic suggest bulb)
🔋 Battery page (🧪): each battery card shows its suggestion; with a single battery, suggestion and soc forecast share the optimizer tile
🚗 Vehicle status: optimizer icon (charge/pause variant) with explanatory tooltip, shown only when the suggestion differs from the current charging state
💡 Suggestion actions: home battery normal|hold|charge|holdcharge, loadpoint/vehicle charge|stop; idle battery interpreted from grid flow (importing → hold, exporting → holdcharge)
🧹 Suggestions are replaced on every optimizer run, cleared (null) for disconnected loadpoints and on optimizer errors
📡 Published state structure. Power values are published but not shown (device support required first)

battery.devices[i].suggestion: { "action": "hold", "charge": 0, "discharge": 0 }  
loadpoints/{id}/suggestion:    { "action": "charge", "charge": 11000, "discharge": 0 }

Wording

action EN DE
charge Charge from grid Aus dem Netz laden
hold Prevent discharging Entladen verhindern
holdcharge Prevent charging Laden verhindern
normal Normal operation Normaler Betrieb

Screenshots

battery view: one battery, dedicated optimizer box
optimizer box

batterie view: multiple batteries, suggestions per device, forecast combined
multiple batteries

loadpoint charge advice
loadpoint charge suggest

loadpoint pause advice
loadpoint pause suggest

@andig andig added enhancement New feature or request ux User experience/ interface experimental Experimental feature labels Jun 14, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The suggestionThreshold constant is a magic number in currentSlotSuggestion; consider either naming it to convey why 50 W is appropriate (e.g. reference to measurement noise / optimizer resolution) or adding a brief comment explaining how it was chosen so future changes don’t accidentally invalidate the assumption.
  • For non-battery types, currentSlotSuggestion always returns "stop" when charge <= suggestionThreshold regardless of discharge power or grid direction; if discharge or grid flow should influence EV/loadpoint recommendations, it may be worth revisiting this logic or documenting that this simplification is intentional.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `suggestionThreshold` constant is a magic number in `currentSlotSuggestion`; consider either naming it to convey why 50 W is appropriate (e.g. reference to measurement noise / optimizer resolution) or adding a brief comment explaining how it was chosen so future changes don’t accidentally invalidate the assumption.
- For non-battery types, `currentSlotSuggestion` always returns `"stop"` when `charge <= suggestionThreshold` regardless of discharge power or grid direction; if discharge or grid flow should influence EV/loadpoint recommendations, it may be worth revisiting this logic or documenting that this simplification is intentional.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@github-actions github-actions Bot added the stale Outdated and ready to close label Jun 21, 2026
@naltatis naltatis removed the stale Outdated and ready to close label Jun 25, 2026
@naltatis

Copy link
Copy Markdown
Member

@copilot resolve the merge conflicts in this pull request

Copilot AI commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved by merging origin/master into this branch and fixing the conflict in core/site_optimizer_test.go. Included in commit 014accd.

Copilot AI requested a review from naltatis June 25, 2026 14:58
iseeberg79 added a commit to iseeberg79/evcc that referenced this pull request Jun 26, 2026
Adds an experimental battery HoldCharge mode driven by the optimizer
plan. When enabled, the battery's charging is limited to the optimizer's
planned per-slot power (read from the per-battery suggestion), so it can
withhold charging early and absorb the midday feed-in peak while keeping
discharge available. Builds on the optimizer suggestion (evcc-io/evcc
evcc-io#30834) and the withhold_charge capability (evcc-io/optimizer#79).

- setting batteryAutoHoldCharge: key, field, Get/Set + site.API, HTTP
  route, persist/publish/restore
- optimizer request: set the battery WithholdCharge flag
- battery mode: require BatteryHoldCharge while enabled (discharge stays
  allowed); the meter template applies the planned limit per slot
- kostal-plenticore-gen2: holdcharge case reads the suggestion via the
  state source (jq) and writes it to the charge-power-limit register;
  new batteryIndex param for multi-battery setups
- UI: experimental HoldCharge toggle in battery settings + en/de texts

TEMP: go.mod replaces the optimizer with the local fork (evcc-io#79); remove
before merge once the optimizer release with withhold_charge is bumped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
iseeberg79 added a commit to iseeberg79/evcc that referenced this pull request Jun 27, 2026
Adds an experimental battery HoldCharge mode driven by the optimizer
plan. When enabled, the battery's charging is limited to the optimizer's
planned per-slot power (read from the per-battery suggestion), so it can
withhold charging early and absorb the midday feed-in peak while keeping
discharge available. Builds on the optimizer suggestion (evcc-io/evcc
evcc-io#30834) and the withhold_charge capability (evcc-io/optimizer#79).

- setting batteryAutoHoldCharge: key, field, Get/Set + site.API, HTTP
  route, persist/publish/restore
- optimizer request: set the battery WithholdCharge flag
- battery mode: require BatteryHoldCharge while enabled (discharge stays
  allowed); the meter template applies the planned limit per slot
- kostal-plenticore-gen2: holdcharge case reads the suggestion via the
  state source (jq) and writes it to the charge-power-limit register;
  new batteryIndex param for multi-battery setups
- UI: experimental HoldCharge toggle in battery settings + en/de texts

TEMP: go.mod replaces the optimizer with the local fork (evcc-io#79); remove
before merge once the optimizer release with withhold_charge is bumped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
iseeberg79 added a commit to iseeberg79/evcc that referenced this pull request Jun 27, 2026
When the battery is in holdcharge mode (today reachable via external
control, REST/MQTT), apply the optimizer's planned per-slot charge power
instead of a fixed 0. The kostal-plenticore-gen2 holdcharge case reads the
planned charge from the per-battery optimizer suggestion (evcc-io/evcc
evcc-io#30834) via the state source (jq) and writes it to the charge-power-limit
register. A new batteryIndex param selects the battery in multi-battery
setups.

Depends on the state plugin (separate PR) and the optimizer suggestion
(evcc-io#30834).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
iseeberg79 added a commit to iseeberg79/evcc that referenced this pull request Jun 27, 2026
When batteryAutoHoldCharge is enabled, follow the optimizer's current-slot
plan instead of leaving the battery in normal mode: charge -> holdcharge
(the meter template applies the planned per-slot limit), discharge ->
normal (discharge allowed), otherwise hold (preserve capacity for a later
price peak - important in winter with scarce capacity). The plan is read
from the per-battery optimizer suggestion (evcc-io#30834); a stored
plan is trusted for 30 min.

Builds on the HoldCharge setting (request PR) and the suggestion (evcc-io#30834).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
iseeberg79 added a commit to iseeberg79/evcc that referenced this pull request Jun 28, 2026
When batteryAutoHoldCharge is enabled, follow the optimizer's current-slot
plan instead of leaving the battery in normal mode. The optimizer already
classifies each home battery as normal/hold/charge/holdcharge in its
per-slot suggestion (evcc-io#30834), accounting for grid flow
direction, so map that action directly onto the global battery mode rather
than re-deriving it from raw charge power. On conflicting per-battery
suggestions the peak-shaving intent wins: holdcharge > hold > charge >
normal. A stored plan is trusted for 30 min.

Builds on the HoldCharge setting (request PR) and the suggestion (evcc-io#30834).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@naltatis

naltatis commented Jul 2, 2026

Copy link
Copy Markdown
Member

Created UI for battery and loadpoints. Updated publishing structure. See description and screenshots above,.

@Johannes-del

Copy link
Copy Markdown
Contributor

Following this with interest. It looks like the direction that would replace a workaround I'm running.

I have a Vaillant aroTHERM heat pump modelled as a heating loadpoint (SoC = DHW
tank temperature), PV-only + dynamic tariff + smartCostLimit, no EV. Two behaviours
I currently emulate with a small external script that just flips the loadpoint's
limitSoc via the API:

  1. source-dependent target: store to 60 °C only from PV surplus, cap grid/smartCost
    heating at 50 °C (the last 10 K are the worst COP band, and on grid they're pure
    loss vs. feed-in);
  2. forecast gate: don't grid-charge in the morning if the solar forecast says PV
    will cover the tank for free later today.

Both are exactly what a price+forecast optimizer should decide on its own, so I'd
rather drop the script.

Will the per-slot suggestions (and later the wired-up control) extend to heating/thermal loadpoints, where "SoC" is a tank temperature and the store is thermal rather than a battery/EV? Or is the initial scope battery + EV only? Happy to test on a real thermal + PV + dynamic-tariff system if that's useful.

@naltatis naltatis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ready to merge

@andig

andig commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

NICE!

@andig andig merged commit ab3220c into master Jul 10, 2026
9 checks passed
@andig andig deleted the feat/optimizer-suggestions-reapply branch July 10, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request experimental Experimental feature ux User experience/ interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants