Skip to content

fix(plan): don't offer Freeze Export for inverters that always charge from PV surplus - #4538

Open
chalfontchubby wants to merge 2 commits into
mainfrom
fix/gate-freeze-on-inverter-capability
Open

fix(plan): don't offer Freeze Export for inverters that always charge from PV surplus#4538
chalfontchubby wants to merge 2 commits into
mainfrom
fix/gate-freeze-on-inverter-capability

Conversation

@chalfontchubby

@chalfontchubby chalfontchubby commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #4207. Supersedes #4425 (closed separately - see comment there).

Some inverters (e.g. FoxESS "Feed-in First") can't be commanded into a state where battery charging is disabled at all - they keep charging from any available surplus PV regardless of the mode Predbat has requested. That means Freeze Export (hold the battery flat, export the rest) can never actually be achieved on that hardware. Predbat's plan previously assumed freeze always holds SoC flat, so on this class of inverter it predicted "no charging" while the inverter actually kept charging from the surplus - a plan/reality mismatch.

#4425 tried to fix this by teaching the prediction model to simulate PV-charging during freeze too. That correctly modelled the physical behaviour, but as raised there: once applied, freeze (99%) and idle (100%) run through identical code for these inverters, so "Freeze Export" becomes a pure relabelling with zero distinguishing effect from doing nothing - the plan would keep showing a FrzExp slot that isn't actually achieving a freeze.

This PR takes the alternative raised in that discussion instead: rather than patch the prediction model to simulate a distinction that doesn't exist in reality, stop the optimiser offering/selecting freeze for these inverters in the first place.

Revision history on this PR, for anyone following the thread: this was originally gated on the existing inverter_can_charge_during_export flag, reusing it rather than adding something new. @springfall2008 correctly pointed out that's wrong - that flag is specifically about PV exceeding the inverter/export limit during active Force Export (see its own docs entry), not about whether Freeze Export can hold the battery flat at all. Two genuinely different questions. This is now gated on a new, dedicated inverter_can_freeze_export setting instead (apps.yaml only, defaults to true - most inverters genuinely can freeze, so "assume it works, let affected users opt out" is still the right default, it just needed to be its own flag).

  • optimise_export(): allow_freeze is forced off when inverter_can_freeze_export is false, alongside the existing set_export_freeze check.
  • optimise_solar() ("Export more solar"): sets export limits to 99.0 (freeze) directly, bypassing optimise_export()'s search entirely, so it needs the same gate independently or it reintroduces the bug through the back door.
  • No prediction.py or kernel changes - this is purely a search-space restriction, not a modelling change, so no parity bump or binary rebuild needed.

Consequence worth being upfront about: this is opt-out, not opt-in - Freeze Export keeps working for everyone by default, and only stops being offered for someone who's explicitly set inverter_can_freeze_export: false because their specific inverter genuinely can't achieve it.

Test plan

  • Regression test in test_optimise_solar.py confirming freeze is not offered when inverter_can_freeze_export=False, and unaffected (still offered) otherwise
  • Existing test_optimise_solar.py scenarios pass their original intent unchanged
  • ./run_all --quick passes
  • ./run_pre_commit passes

🤖 Generated with Claude Code

… from PV surplus (#4207)

Alternative to the stale #4425 (which patched the prediction model instead
- reworking it here per that PR's own follow-up discussion): on an inverter
with inverter_can_charge_during_export=true (the default), PV surplus
charges the battery regardless of what mode Predbat has commanded, so
Freeze Export (hold SoC flat, export the rest) can never actually be
achieved - it collapses to identical behaviour as Idle. Rather than teach
the prediction model to simulate a distinction that doesn't exist in
reality, stop the optimiser offering/selecting freeze for these inverters
in the first place:

- optimise_export(): allow_freeze is now also forced off when the inverter
  can charge during export, alongside the existing set_export_freeze check.
- optimise_solar() ("Export more solar"): sets export limits to 99.0
  (freeze) directly, bypassing optimise_export()'s search entirely, so it
  needs the same gate independently or it reintroduces the bug through the
  back door.

No prediction.py or kernel changes needed - this is purely a search-space
restriction, not a modelling change, so no parity bump or binary rebuild.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@daviessm

Copy link
Copy Markdown
Contributor

Does this mean that for inverters with inverter_can_charge_during_export=true, "feed-in first" mode won't be used at all and the only way to get the system to export before the battery is full would be to set force discharge?

@springfall2008

Copy link
Copy Markdown
Owner

I'm not sure this seems right, freeze export disables battery charging allowing all solar to be exported.

Can charge during export just means can the battery charge if the solar exceeds the capability of the inverter to export, this is different.

@chalfontchubby

chalfontchubby commented Aug 16, 2026 via email

Copy link
Copy Markdown
Collaborator Author

#4207/#4538)

springfall2008 pointed out on #4538 that gating on
inverter_can_charge_during_export was wrong: that flag is specifically
about PV exceeding the inverter/export limit during *active* Force Export
(see its own docstring), not about whether Freeze Export can hold the
battery flat at all - two genuinely different questions this PR had
conflated.

Add inverter_can_freeze_export as its own setting (apps.yaml only, defaults
to true) and gate optimise_export()/optimise_solar()'s freeze offering on
it instead. Reusing inverter_can_charge_during_export's default of true
would have been wrong here too - most inverters genuinely can freeze, so
"assume it works, let affected users opt out" is the right default for
this flag as well, it just needed to be a different flag with a different
meaning, not a shared one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@chalfontchubby

chalfontchubby commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

@springfall2008 You're right, thanks for catching that - inverter_can_charge_during_export is specifically about PV exceeding the inverter/export limit during active Force Export, not about whether Freeze Export can hold the battery flat at all. Conflating those was a mistake.

Pushed a fix using a new, dedicated inverter_can_freeze_export flag instead (default true, so nothing changes for anyone unless they explicitly set it false) - PR description updated to match.

@chalfontchubby

Copy link
Copy Markdown
Collaborator Author

@daviessm To answer directly: no, this shouldn't take away "feed-in first"/pre-emptive export for you. The gate now checks a new inverter_can_freeze_export setting (defaults to true) rather than inverter_can_charge_during_export - since you've presumably never set the new one (it didn't exist until this PR), Freeze Export is offered exactly as before. It would only stop being offered if you explicitly set inverter_can_freeze_export: false because you'd confirmed your specific inverter can't actually hold SoC flat - not something this change does on its own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FoxESS Freeze Export mode predicts no battery charge, but the battery does charge

3 participants