Repository navigation
Ignore a frozen car SoC sensor while the car is plugged in - #45
Merged
Merged
Conversation
A car SoC sensor that stops updating mid-charge keeps reporting the value it last reached, which is normally just under the charge limit. Predbat then plans ~0 kWh of car charging: the real overnight load vanishes from the plan (observed: the optimiser cancelled a live 100% charge window to export the house battery instead, while the charger drew a steady 7 kW), and the car-charging battery hold in execute.py never fires because it requires a slot with kwh > 0. Add car_charging_soc_max_age (minutes, default 0 = disabled). When set, and when car_charging_planned reports the car plugged in, a SoC reading whose own last_updated is older than the limit is ignored and the car is planned as if empty - the recoverable direction. Unplugged cars are never checked (a sleeping car legitimately goes quiet for days), manual-SoC cars read no sensor, and an unreadable last_updated leaves the reading alone. A stale verdict sticks through the Octopus battery-size recalculation so the frozen percentage cannot be reinstated there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merged
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
An EV integration that stops updating the car's SoC sensor while the car is plugged in leaves Predbat planning against a frozen value — and the frozen value is the worst one to be wrong about, because it is whatever the sensor reached before it died, which is normally just under the charge limit.
Observed on a live system over the night of 17–18 August.
sensor.<vin>_soclast updated at 04:33 the previous day and sat at 89.9% against a 90% limit, while the charger delivered a steady 7 kW for the whole cheap-rate window:Two things follow from a car that looks like it needs 0.06 kWh:
The real load disappears from the plan. With no overnight car load to save the battery for, and a flat 12p export against 3.49p import, the optimiser scored exporting the house battery above keeping it charged. It zeroed the live charge window mid-window and scheduled a forced export instead:
The battery went 8% → 72%, then got cancelled twice and finished the window at ~65% instead of 100%.
The battery hold never engages.
switch.predbat_car_charging_from_batterywas off, but the hold inexecute.pyrequires a slot withwindow.get("kwh", 0) > 0. With ~0 kWh planned it was skipped every cycle —Disabling battery discharge whilst car ... is chargingappears zero times in the log — so the house battery discharged into the car.Nothing in the logs identifies the sensor as the cause; the plan changes all look like ordinary optimisation.
Change
Adds an optional
car_charging_soc_max_age(minutes) toapps.yaml. When set, and whencar_charging_plannedreports the car as plugged in, Predbat checks the SoC sensor's ownlast_updated. If it is older than the limit, Predbat logs a warning and plans as if the car were empty:The true SoC is unrecoverable, so this picks the recoverable direction. Over-planning car charging costs some cheap-rate allocation; under-planning it strands the house battery and then drains it into the car.
Deliberate limits on when it fires:
0, which disables the check, so behaviour is unchanged for everyone who does not set it.switch.predbat_car_charging_manual_soc), which read no sensor.last_updatedis treated as "cannot tell" and the reading is left alone, rather than assumed stale.Tests
tests/test_car_soc_stale.py, registered ascar_soc_staleandcar_soc_age:max_age: 0leaves the frozen reading alone — the backwards-compatibility guaranteelast_updatedall degrade to trusting the reading rather than raisingNonefor a missing entity or a plain numeric config valueVerification
unit_test.py --keyword car_socpasses, and the warning fires exactly once for the stale case.--quicksuite run withblack/ruffclean.web_ifwas not exercised locally: it binds port 5052, which the live Predbat instance on the test machine holds. CI runs it on a clean runner.🤖 Generated with Claude Code