Skip to content

Joint cost-aware EV and home-battery scheduling under a site import limit #5490

Open

Description

@arturonaredo

Feature request

Joint cost-aware scheduling of a fixed-rate EV and home battery under a site import limit

This is a request for one native Predbat plan which jointly decides when the EV
charges and when the home battery charges, holds or discharges
, using household
and heating demand, PV forecasts, actual import/export prices and physical limits.
It is not a request for a second Home Assistant optimiser or a fast protective loop.

I am developing an opt-in implementation in my personal fork. This issue describes
the intended behaviour and acceptance criteria, not a claim that the feature is
complete or deployed
. Diagnostic draft PR #5486 remains separate and does not
close this request.

Problem

The current Predbat-led car planner selects low-import-price windows before the
household battery optimisation. It does not jointly search EV placement, battery
preparation and whole-site import feasibility. A fixed-rate charger cannot follow
whatever PV surplus happens to remain: when enabled, its real full demand must be
accounted for, with the balance supplied by an executable combination of battery
and grid.

Avoiding expensive hours alone can miss a better overall outcome. Charging a car
with PV plus some battery now can be cheaper when later sunlight will replenish
the battery anyway. Conversely, buying cheap electricity overnight for the car
and/or home battery may preserve valuable stored energy for household demand later.
There should be no unconditional rule to fill the home battery before the EV, use
the battery at all costs, or avoid grid charging altogether.

Representative configurable example

The following is a synthetic acceptance profile, not a deployment configuration:

Quantity Example
Whole-site grid import ceiling 6,000 W
Fixed EV charging demand 6,770 W, on/off only
Home battery capacity approximately 32 kWh
Home battery charge/discharge limits 3,000 / 8,000 W
Minimum domestic SoC 10%
EV pause/restart domestic SoC 15% / 25%
EV target / departure 100% / 07:30, subject to physical feasibility
Import bands 8.1 / 11.2 / 19.1 currency cents per kWh
Export compensation zero in this example, configurable elsewhere

Grid import, battery power, inverter conversion and AC load/passthrough limits
are distinct constraints. A 6 kW import ceiling is not a 6 kW household-load
ceiling: PV and battery can supply additional load within the verified topology.
An inverter's nominal rating alone is not proof of available power at every SoC.

Use cases and expected behaviour

1. Charge on solar now, recover the home battery later

Given 5 kW usable PV, 1 kW household demand and a 6.77 kW fixed EV, a candidate
may use approximately 2.77 kW of battery support, subject to losses and limits.
If later PV can replenish the home battery after the EV finishes, compare this
with delaying the EV or buying overnight. Do not reject the daytime candidate
merely because PV alone cannot cover the EV, and do not pretend the charger
has reduced itself to the available surplus.

2. Prepare the home battery during cheap hours

When PV is insufficient, the planner may buy cheap energy for the home battery
before an EV slot. Include charging power, charging/discharging losses, SoC,
household consumption and the time available. Grid-charge preparation and EV
charging must be co-scheduled, not assumed to fit simultaneously under the cap.
Do not assume an ECO inverter supplies only the amount above the import limit;
simulate its actual battery-first/hold/charge behaviour.

3. Use the next more expensive eligible hours automatically

If the lowest-price windows cannot meet the target, include the next cheapest
eligible windows before departure, rather than reporting a shortfall merely
because cheap time ran out. Compare the full system cost, including solar and
storage opportunity cost, not only the import-price label on an EV slot.
Only a genuine absence of a physically feasible plan should remain unmet. A
bounded search must distinguish "no plan found" from a proof of impossibility.

4. Heating and household demand remain the priority

When heating or other household demand rises, re-plan the flexible EV/battery
schedule. Do not silently shed household demand, move heating without permission,
increase electrical limits or invent a reduced EV charging rate. Include actual
EV restart hysteresis and minimum switching intervals to avoid a plan the executor
will repeatedly pause.

5. Anticipate arrival without inventing a connection

An expected return time and a recent EV SoC may establish a future energy need,
even while the car is away. No EV energy may be assigned before its availability
window. Expected availability is not permission to energise an absent/unplugged
charger: actual execution needs a separate trustworthy connection/session gate.
Re-plan on delayed arrival, cancellation, stale/unknown SoC or disconnection.

6. Do not use tomorrow's sun to satisfy an earlier deadline

PV arriving after a 07:30 departure cannot count towards that departure's EV
energy. It can affect the value of energy left in the house battery afterwards.
Price changes on weekdays must be respected from the supplied tariff series.

7. Respect power and energy at every step

With no PV, a battery already at reserve, a 1.2 kW house and a fixed 6.77 kW EV,
the demand is 7.97 kW. A 6 kW import ceiling cannot be satisfied by clipping the
graph or debiting an empty battery. Move the EV or prepare energy earlier.
Preserve raw energy balance, efficiencies, charge/discharge limits and SoC bounds.
Curtailing grid-funded battery charging must not unnecessarily curtail usable PV.

8. Compare equivalent end conditions and handle forecast changes

Account for terminal stored energy or an explicit terminal target, so an emptier
battery does not masquerade as a saving. Include uncertainty/re-planning when PV
underperforms or household load increases. A safe forecast is not a guarantee
against every instantaneous unforecast load spike.

Integration and acceptance criteria

  • Opt-in; disabled behaviour remains unchanged.
  • Native Predbat owns the plan; existing inverter mappings and one existing EV
    executor remain authoritative. No competing controller or external service.
  • Real fixed-rate EV energy, battery losses, source accounting and terminal energy
    are covered by synthetic regression tests for the use cases above.
  • Candidate selection treats physical feasibility and the departure requirement
    separately from monetary cost; no arbitrary large monetary penalty presented
    as a hard constraint.
  • Any candidate applied to native control must match executable inverter modes,
    window resolution/count and rates. Unsupported configurations must be explicit,
    not silently run an idealised dispatch model against different hardware.
  • Python, native-kernel, cache and final HA publication paths must agree, or an
    unsupported accelerated path must be explicitly disabled for the feature.
  • Planning is not electrical protection. Hardware protection and a verified fast
    control path are still needed; a five-minute optimisation loop cannot enforce
    an instantaneous whole-site ceiling by itself.

Relation to existing work

The new issue is intentionally an integration-level behaviour contract linking
those pieces, not a duplicate request for another import-limit setting. No issue
above should be closed merely by publishing diagnostics or this proposal.

Activity

  1. springfall2008 commented on Oct 11, 2026

    @springfall2008
    Owner

    🤖 Automated first-pass triage — a maintainer will review this before any action is taken.

    Classification: enhancement (feature request, so no priority label).

    Verified against current main (v9.3.7-6-gd6985b6d) — the gap the issue describes is real:

    • EV slots are placed by plan_car_charging() (apps/predbat/plan.py:5969), called from fetch_sensor_data_car_planning() (apps/predbat/fetch.py:1424, slot creation at fetch.py:1454) before the battery optimisation runs. It sorts the handed-over low-rate windows cheapest-first (car_charging_plan_smart only reorders them) and fills to car_charging_limit by the ready time; PV, battery SoC and any import cap never enter that search, and when the handed windows can't reach the target the plan just ends up short — there is no expansion into other windows. The battery solver then runs with the placed car slots as fixed load, so the plan's cost is car-aware, but EV placement itself is never co-searched. That is exactly the joint-scheduling gap the issue names.
    • There is no whole-site import limit today: import_limit appears only as per-inverter discovery ratings (sunsynk.py:1605, fox.py:2614 — device-reported caps) and never as a planner setting. The shipped precedent it would mirror is the export-side export_limit clamp inside the per-minute prediction loop (prediction.py:664).
    • The kernel/Python-parity acceptance criterion is a real constraint: run_prediction() dispatches to the native kernel when available (prediction.py:538), and the kernel source lives in prediction_kernel.cpp. A new constraint must be mirrored in both engines or the accelerated path disabled while the feature is on, which is what the issue asks for and which draft PR Add configurable whole-site import diagnostics (no enforcement) #5486's change list (prediction.py, prediction_kernel.py, tests) already anticipates.

    Not a duplicate — none of the linked pieces asks for the joint objective; each covers a subset:

    Shape for maintainer review: this is one umbrella request whose pieces could land in the recorded order above (cap → diagnostics → ranking cost → joint search), and the maintainer may equally decide the joint EV+battery search shouldn't live inside the 5-minute-loop planner at all — the issue's own acceptance criteria treat that loop as not-electrical-protection and leave hardware protection explicitly out of scope. Both the scope decision and where the joint search lives (car planner vs main solver) are maintainer calls on a change this wide.

    Nothing further is needed for triage — as a feature request it doesn't require a debug yaml or log.

  2. arturonaredo commented on Oct 11, 2026

    @arturonaredo
    Author

    Personal-fork progress, not an installation recommendation or a claim that this issue is solved:

    Published commit: 11f3d385, signature verified by GitHub.

    • Development branch: joint-site-energy-planner, with scope and limitations. The separate diagnostic PR Add configurable whole-site import diagnostics (no enforcement) #5486 remains unchanged.
    • The experimental bounded search jointly chooses fixed-rate EV on/off and domestic-battery demand/hold/charge across the horizon. It accounts for losses, curtailment, terminal battery value, domestic-battery EV hysteresis/cooldown, expected availability, departure and more-expensive eligible slots.
    • Native integration is shadow-only: each candidate is independently replayed with fresh native central/PV10 predictions. It never replaces executable inverter/EV windows, the active prediction/cache, or the actuator-facing EV-slot signal. execution_enabled and enforced remain false.
    • Offline validation: independent energy-conservation tests and a separate tiny exhaustive reference; real calculate_plan → solver → native replay tests; full quick suite with the native kernel required; both committed golden debug replays; pre-commit and documentation build. Four slow suites remain excluded. No change to either native prediction engine's dispatch semantics.

    This is a bounded approximation, not proof of a global optimum or infeasibility when no plan is found. Executable window mapping (including adjacent unequal targets), reliable real connection/session permission, and hardware validation are still unresolved. No live deployment, forced charging, hardware-limit change, or new upstream implementation PR has been made. Maintainer scope/design approval is still separate from automated triage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BOT_TRIAGEDHas been through the triage botenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions