Skip to content

[codex] Fix cumulative EVCC energy export - #1

Merged
okuegow merged 2 commits into
okuegow:mainfrom
fbettag:codex/fix-charge-total-import-energy
May 28, 2026
Merged

okuegow merged 2 commits into
okuegow:mainfrom
fbettag:codex/fix-charge-total-import-energy

Conversation

@fbettag

@fbettag fbettag commented May 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Parse EVCC chargeTotalImport from /api/state.
  • Prefer chargeTotalImport for Victron /Ac/Energy/Forward so continuous/heating devices with chargedEnergy == 0 still publish a cumulative kWh counter to VRM.
  • Keep chargedEnergy / 1000 as fallback for older/missing EVCC payloads.

Root Cause

On a live heat-pump loadpoint, EVCC reports a valid cumulative import meter but keeps the session energy at zero:

chargePower       ~= 27-90 W
chargedEnergy     = 0
chargeTotalImport ~= 19342 kWh

The bridge previously exported /Ac/Energy/Forward from chargedEnergy / 1000, so VRM received 0 kWh even though the loadpoint had a valid cumulative meter value.

The EVCC setup uses a sgready-relay charger with a separate Shelly 3EM meter. EVCC logs confirm that loadpoint 3 has charge total import values, so the bridge can forward that cumulative meter directly.

Validation

178 passed in 0.22s

Run with:

nix-shell -p python3 python3Packages.pytest python3Packages.requests python3Packages.requests-mock shellcheck --run 'python3 -m pytest -q'

@fbettag
fbettag force-pushed the codex/fix-charge-total-import-energy branch from fdcb450 to 5c8c173 Compare May 27, 2026 10:49
@okuegow

okuegow commented May 28, 2026

Copy link
Copy Markdown
Owner

Thanks — clean fix, the logic is right. Two small things before merging:

Unit is kWh, confirmed. Cross-checked on a live /api/state: with an EV loadpoint showing a recent session of ~35 kWh while chargeTotalImport was ~15.3k, only kWh is consistent (lifetime can't be smaller than a single session). IntegratedDevice chargers (sgready-relay, heatpump, sgready, mypv, vaillant) explicitly have no session semantics in EVCC, so chargedEnergy is structurally unsuitable for those — chargeTotalImport is the right field.

Source switch breaks monotonicity. Victron's /Ac/Energy/Forward is expected monotonic, but the current branching switches source dynamically. On a loadpoint where EVCC returns chargeTotalImport: null (e.g. no meter on the charger), the fallback flips to chargedEnergy/1000 — which is 0 on a heating LP, rolling the counter back. Minimal guard, no new state:

total_import = float(lp.charge_total_import or 0.0)
if total_import > 0:
    candidate = total_import
elif status != STATUS_DISCONNECTED:
    candidate = float(lp.charged_energy) / 1000.0
else:
    candidate = None

if candidate is not None:
    previous = float(s.get("/Ac/Energy/Forward") or 0.0)
    s["/Ac/Energy/Forward"] = max(candidate, previous)

Plus a test that flips the source mid-run (high → 0 → high, no regression).

Side note worth a line in the description: /Ac/Energy/Forward is now written even when DISCONNECTED (whenever total_import > 0). That's the right semantics for a lifetime counter but is a deliberate change from the original "keep last value on disconnect" — a test for the disconnected+positive case would make it explicit.

Happy to push a follow-up commit if easier.

Victron expects /Ac/Energy/Forward to be monotonically increasing.
The dynamic source switch (chargeTotalImport -> chargedEnergy/1000)
can roll the counter backwards if EVCC briefly returns
chargeTotalImport: null/0 — observed on a loadpoint without an
attached meter. For heating loadpoints chargedEnergy is structurally
0 (IntegratedDevice chargers have no session semantics), so the
fallback would publish 0 to VRM and cause a counter reset.

Guard the write with max(candidate, previous_vrm_value); no new
state, identical behaviour while EVCC is stable. Also publish the
cumulative counter when status == DISCONNECTED but a positive
chargeTotalImport is present (lifetime meter semantics).

Tests:
- monotonic across high -> 0 -> high source switch
- DISCONNECTED + positive total_import still publishes /Ac/Energy/Forward
- existing per-phase-voltages test now uses per-key getitem mock
  (previous return_value=5 conflicted with the monotonic guard)
@okuegow
okuegow marked this pull request as ready for review May 28, 2026 13:43
@okuegow
okuegow merged commit a5efde8 into okuegow:main May 28, 2026
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.

2 participants