fix(inverter): stop the export target sitting below the min reserve SoC - #4384
Open
springfall2008 wants to merge 3 commits into
Open
fix(inverter): stop the export target sitting below the min reserve SoC#4384springfall2008 wants to merge 3 commits into
springfall2008 wants to merge 3 commits into
Conversation
adjust_force_export only ever pulled the export target register down to reserve_percent - a target already below the minimum reserve SoC was logged as "already set" and left alone. GE Cloud resets that register (dc_discharge_1_lower_soc_percent_limit) to 4% in enable_default_options at startup and every 24 hours, so a user with a higher minimum reserve ended up with an inverter side backstop of 4% that Predbat never corrected, letting a timed export drain past the reserve between Predbat cycles. Compare against the target instead of only testing for a higher value so the register tracks reserve_percent in both directions, on both the REST v3 and the entity write paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
.claude/worktrees/ holds locally created git worktrees and was showing up as untracked in every git status. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes Inverter.adjust_force_export() so the inverter-side export target SoC (discharge_target_soc) always matches the configured minimum reserve SoC in both directions (raising targets that are too low as well as lowering targets that are too high). This prevents timed exports from draining the battery below the configured reserve between Predbat’s 5‑minute control cycles.
Changes:
- Update
adjust_force_export()to set the discharge target whencurrent != reserve_target(instead of only whencurrent > reserve_percent) for both REST v3 and HA entity paths. - Hoist
target_soc = int(self.reserve_percent)to avoid duplicated conversions and keep the logic consistent. - Add a regression test covering “below reserve”, “above reserve”, and REST v3 behavior.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| apps/predbat/inverter.py | Ensures discharge_target_soc is corrected to the configured reserve even when it’s below reserve (prevents under-reserve draining during export). |
| apps/predbat/tests/test_inverter.py | Adds regression coverage verifying the target tracks reserve in both directions, including REST v3. |
…arget enable_default_options resets every "lower SoC percent limit" register to 4%, which overlaps the DC discharge lower SoC limit that Predbat has configured as discharge_target_soc. Predbat tracks the minimum reserve SoC on that register, so the reset was fighting adjust_force_export. Resolve the configured discharge_target_soc entities and skip the matching register. The match is per device and uses the same entity ID form as publish_registers, so a register on an inverter Predbat is not driving is still reset, as is the case where the inverter has no discharge target register at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
apps/predbat/inverter.py:2499
target_soc = int(self.reserve_percent)floors whenreserve_percentis a float (it’s read using a float default), which can still set the inverter’s discharge target below the configured minimum reserve (e.g. 20.5% -> 20%). Since this register is a safety backstop, it should be rounded up to avoid dipping under the minimum reserve.
if force_export:
target_soc = int(self.reserve_percent)
if self.rest_data and self.rest_v3:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Reported against GE Cloud: the export target ends up set below the minimum reserve SoC.
adjust_force_exportonly ever pulled the export target register down toreserve_percent:A target already below the minimum reserve was treated as acceptable and left alone.
GE Cloud's
enable_default_optionsresets every "lower SoC percent limit" register to 4%, at startup and again every 24 hours. That set overlapsdc_discharge_1_lower_soc_percent_limit— the exact register Predbat configures asdischarge_target_soc. So a user with a higher minimum reserve was left with an inverter-side backstop of 4% that Predbat never corrected.That register is what stops a timed export draining the battery below the reserve between Predbat's 5-minute cycles, so the battery could discharge past the configured minimum reserve.
Reproduced with
battery_min_soc: 20and the register at 4:Fixes
inverter.py— track the reserve in both directions. Compare against the target rather than only testing for a higher value, so the register followsreserve_percentup as well as down. Applied to both the REST v3 and the entity write paths;target_socis hoisted out of the two branches.This part is not GE Cloud specific — it applies to every inverter exposing
discharge_target_soc(fox, solax, solis, deye, enphase, sigenergy, teslemetry).gecloud.py— stop resetting the register Predbat owns.enable_default_optionsnow resolves the configureddischarge_target_socentities and skips the matching register before any reset rule runs, rather than fightingadjust_force_exportevery 24 hours. The match is per device and rebuilds the entity ID the same waypublish_registersandasync_automatic_configdo, so it is exact rather than a substring guess. Registers on an inverter Predbat is not driving, and inverters with no discharge target register at all, keep the existing 4% reset.Tests
test_discharge_target_tracks_reserveinapps/predbat/tests/test_inverter.py:_test_enable_default_options_skips_discharge_targetinapps/predbat/tests/test_ge_cloud.py:changedflag unsetBoth were written first and confirmed failing before the fix.
MockGECloudDirect.get_arggained**kwargsso it accepts theindirectkeyword the realComponentBase.get_argtakes; the realget_argwas checked separately to confirmindirect=Falsereturns the raw entity list rather than resolving entity states../run_all --quickand./run_pre_commitboth exit 0.Also included
A
.gitignoreentry for.claude/worktrees/, which was showing up as untracked in everygit status. Unrelated to the fix — happy to split it out if you would rather keep this PR single-purpose.🤖 Generated with Claude Code