Skip to content

fix(car-charging): don't let an empty exclusive plan block later cars - #4364

Merged
springfall2008 merged 3 commits into
springfall2008:mainfrom
chalfontchubby:fix/car-charging-exclusive-shared-charger
Jul 29, 2026
Merged

fix(car-charging): don't let an empty exclusive plan block later cars#4364
springfall2008 merged 3 commits into
springfall2008:mainfrom
chalfontchubby:fix/car-charging-exclusive-shared-charger

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

Summary

Reported in #4305 - a shared-charger, 2-car setup (both car profiles pointed at the same Zappi's status sensor) showed zero Car kWh for an actively-charging session, despite every individual signal (plugged in, dispatch active, real session energy accumulating, genuine SoC/limit gap) confirming the car really was charging and really did need more.

Root cause, confirmed against the reporter's own debug.yaml: in a shared-charger setup, car_charging_planned/car_charging_now can read true for every car profile sharing the same physical sensor, not just the one actually plugged in. fetch_sensor_data_car_planning()'s exclusive-charging break (fetch.py:1141) fired on car_charging_planned[car_n] and car_charging_exclusive[car_n] alone - so car 0 (not actually plugged in, but reading planned=True off the shared sensor) hit the break and stopped the loop before car 1 (the car genuinely charging) was ever considered. Car 0's own charging need happened to be zero (SoC already at its own limit), so plan_car_charging() correctly returned nothing for it - but the loop still treated it as "the" exclusive session and bailed out, silently eating car 1's turn. car_charging_slots ended up [[], []].

Fix: only let the exclusive break fire once a car has actually produced a non-empty plan:

if self.car_charging_planned[car_n] and self.car_charging_exclusive[car_n] and self.car_charging_slots[car_n]:
    self.log("Car {} charging is exclusive, will not plan other cars".format(car_n))
    break

Behaviour is unchanged for the normal case (a car with a genuine charging need and exclusive=True still breaks immediately, same as before) - the only change is that a car with planned=True but an empty resulting plan no longer blocks later cars from getting their turn.

(A fix based on checking a car_charging_plugged_in entity was considered first, but that concept doesn't actually exist anywhere in Predbat's config today - would have meant introducing a whole new per-car entity rather than using data already computed a few lines above.)

Test plan

  • New regression test run_multi_car_shared_charger_exclusive_test in test_multi_car_iog.py, modelled on the setup from the reporter's debug.yaml (car 0: planned=True, exclusive=True, SoC already at its own limit; car 1: genuine SoC/limit gap) - asserts car 1 still gets planned
  • Verified the new test actually fails without the fix (reverted fetch.py locally, confirmed the test catches it, then restored)
  • ./run_all --test multi_car_iog - all pass
  • pre-commit (ruff, black, cspell) run against the changed files, passed

Fixes #4305

🤖 Generated with Claude Code

In a shared-charger multi-car setup, car_charging_planned/now can read
true for every car profile that shares the same physical sensor (e.g.
two car entries both pointed at one Zappi's status entity), even when
only one is actually plugged in. The exclusive-charging break in
fetch_sensor_data_car_planning() fired on car_charging_planned +
car_charging_exclusive alone, so a car with no real charging need (SoC
already at its own limit, so plan_car_charging() correctly produced an
empty plan) could still stop the loop before a later car - the one
genuinely plugged in and charging - ever got planned at all.

Confirmed against the reporter's own debug.yaml: octopus_intelligent_charging
was off (not on, as originally assumed), car_charging_planned/now read
[true, true] for both cars, and car_charging_slots ended up [[], []] even
though the second car had a real ~4% SoC/limit gap.

Only lets the exclusive break fire once a car has actually produced a
non-empty plan. Fixes springfall2008#4305.

Copilot AI 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.

Pull request overview

This PR fixes a multi-car shared-charger edge case where an “exclusive charging” early-exit in fetch_sensor_data_car_planning() could prevent later car profiles from being planned when an earlier car reports planned=True via a shared sensor but produces an empty plan (e.g., already at its limit). It also adds a regression test based on the reported scenario in #4305.

Changes:

  • Refines the exclusive-charging break condition in fetch_sensor_data_car_planning() to only trigger once an actual non-empty charging plan exists.
  • Adds a regression test to ensure a later car still gets planned in shared-sensor + exclusive setups.
  • Registers the new regression test in the multi-car IOG test suite.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
apps/predbat/fetch.py Adjusts exclusive-charging loop break logic during car planning to avoid skipping later cars when the current car produced no plan.
apps/predbat/tests/test_multi_car_iog.py Adds and wires a regression test for the shared-charger exclusive-planning scenario from issue #4305.

Comment thread apps/predbat/fetch.py Outdated
Comment thread apps/predbat/tests/test_multi_car_iog.py
springfall2008 and others added 2 commits July 29, 2026 15:32
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
low_rates windows were absolute minute offsets (0-150), but
plan_car_charging() skips windows before minutes_now - once the test
suite runs later than ~02:30, all three windows would be stale.
Car 1's outcome was actually still deterministic because
car_charging_now=True unconditionally injects an extra "charging now"
slot, but making the windows relative to minutes_now removes the
ambiguity entirely (Copilot review finding on springfall2008#4364).
@springfall2008
springfall2008 merged commit 4df8a9e into springfall2008:main Jul 29, 2026
2 checks passed
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.

Active, dispatched car charging session not reflected in plan (Car kWh) despite all entities confirmed correct

3 participants