Skip to content

fix(inverter): settle before verifying a GivTCP discharge target write - #4492

Merged
springfall2008 merged 2 commits into
mainfrom
fix/discharge-target-rest-race
Aug 12, 2026
Merged

springfall2008 merged 2 commits into
mainfrom
fix/discharge-target-rest-race

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

Summary

Fixes #4421 - spurious "REST failed to setExportTarget got 0" warnings even though GivTCP's own log confirms the write succeeded.

rest_setDischargeTarget() POSTs the write, then immediately GETs GivTCP's /runAll status to verify it landed. That status is a separately-cached snapshot, refreshed on GivTCP's own polling cycle rather than synchronously with the POST - reading it back with no delay can catch data from before GivTCP has applied and exposed the change. Traced against multiple reporters' logs on the issue: writes do succeed at inconsistent retry counts (rules out a broken field path, which would fail every time), and GivTCP's own log consistently shows the write landing at the exact moment Predbat reports the failure.

Fix: a short settle delay between the POST and each readback (every attempt, not just between retries).

Test plan

  • New regression test (test_discharge_target_settle_delay in test_inverter.py) simulating the exact race: a stale cached readback followed by the real post-write value, asserting the write is still recognised as successful and that a settle sleep happens before each readback.
  • ./run_all --quick - all passing
  • ./run_pre_commit - clean
  • Would appreciate someone hitting this live testing it - @davemilsom2, @FileGo, @Redding43, @kitenski, any of you able to dogfood this on your setup?

🤖 Generated with Claude Code

GivTCP's runAll status is a separately-cached snapshot refreshed on its own
polling cycle, not synchronously with the setDischargeTarget POST. Reading it
back immediately after the write can catch data from before GivTCP has
applied and exposed the change, reporting a spurious "REST failed to
setExportTarget got 0" even though the write actually succeeded - confirmed
against multiple reporters' logs on #4421, where GivTCP's own log shows the
write landing at the exact moment Predbat reports the failure.

Adds a short settle delay between the POST and each readback in
rest_setDischargeTarget, so a slow-to-update cache isn't misread as a failed
write.
…rge target write

Traced against GivTCP's own source (write.py's setDischargeTarget()):
Control.Discharge_Target_SOC_1 is updated synchronously the moment GivTCP
accepts a write, via updateControlCache(). raw.invertor.discharge_target_soc_1,
which rest_setDischargeTarget() was checking exclusively, is only refreshed by
GivTCP's separate background self_run poll cycle - tens of seconds away on
some installs, not remotely covered by a short settle delay.

Check Control.Discharge_Target_SOC_1 first, falling back to the raw register
only if Control doesn't have the key. The settle delay from the previous
commit is still useful for the much smaller residual window (the physical
inverter itself taking a moment to apply the write).
@springfall2008
springfall2008 merged commit 4bb7841 into main Aug 12, 2026
2 checks passed
@springfall2008
springfall2008 deleted the fix/discharge-target-rest-race branch August 12, 2026 17:17
springfall2008 added a commit that referenced this pull request Aug 15, 2026
…ng whether to write (#4518)

* fix(inverter): also prefer Control.Discharge_Target_SOC_1 when deciding whether to write

adjust_force_export() still decided whether a discharge target write was needed by
reading raw.invertor.discharge_target_soc_1 alone - the same slow, self_run-poll-refreshed
field #4492 moved away from as the primary signal inside rest_setDischargeTarget() itself.
On hardware where that field never catches up, the write looked permanently "needed" and
fired every cycle even when nothing had changed (#4517).

rest_readDischargeTarget() centralises the same Control-first/raw-fallback read used for
write verification, so the caller now correctly skips the write once it's actually landed.

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

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Trefor Southwell <48591903+springfall2008@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
springfall2008 pushed a commit that referenced this pull request Aug 16, 2026
…nverters (#4517) (#4543)

AC Coupled inverters don't have a working Discharge_Target_SOC_1 register -
GivTCP still reports each write as successful, but it never persists
between cycles, so adjust_force_export() saw a permanent mismatch and
rewrote it every single cycle indefinitely.

Confirmed against GivEnergy's own firmware archive
(github.com/DJBenson/giv-firmware): "AC Coupled" is a single product line
with exactly two firmware releases ever published, not an early/late
generational split the way the Hybrid line has Gen1/2/3 - so there's no
newer AC-coupled variant this would need to keep working for. Skip the
write outright when raw.invertor.model reports "Ac", rather than attempting
one already known to be doomed.

Supersedes the earlier enable-before-write approach on this same issue -
that theory relied on a correlation with a second reporter's data that
turned out to be from before #4492/#4518 existed and was confounded by the
exact race condition those fixes address, so it was never solid evidence
that AC Coupled's register works anywhere. Dropped rather than kept
alongside this fix, since it has no remaining supporting evidence.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

Reoccurence of #4404 - Warn: Inverter 0 REST failed to setExportTarget when exporting

2 participants