Skip to content

Feature: "Charge Deadline" strategy — be at a target by a deadline, using the cheapest hours #166

Description

@CodeRubbere

Feature: "Charge Deadline" strategy — be at a target by a deadline, using the cheapest hours

CONTRIBUTING.md asks for an issue before larger changes, so here is the design before I open
a PR. Working implementation is running on my own fleet (6× Marstek Venus) and
contribute/check.ps1 passes with it in place.

Related: #167 (the battery_time_remaining entity_id fix) — unrelated to this, just the first
thing I hit.

What it does

Charge to a target before a deadline, choosing when to draw from the grid:

  • Goal — a target SoC, an energy reserve in kWh, or simply "batteries are full".
  • PacingSpread evenly (constant power across a window you set) or Cheapest hours
    (charge only during the periods Dynamic v2 marks low, paced across all of them).
  • If the cheap periods cannot carry the energy in time, the deadline wins: the plan spreads
    the remainder over the rest of the window.
  • Once the target is reached it is latched for the rest of the window, optionally keeping
    discharge blocked so the reserve is still there at the deadline.

The window never runs past midnight, so "full for the night" stays true and the plan never needs
tomorrow's prices (which are not published until the afternoon).

How it integrates

It does not touch the control loop. Each tick it computes a grid setpoint and delegates to
the existing Self-consumption strategy, so the PID, peak shaving and every safety limit keep
working unchanged:

required_W   = energy_still_needed / charge_time_left
grid_target  = clamp(required_W - measured_PV_surplus, 0, pack_limit, import_limit)
msg.target   = "Self-consumption"          # or the user's fallback, when idle/holding

Dispatch is the existing extension mechanism — a link in named Charge Deadline plus the
option added to the strategy selectors. No edit to 01 start-flow.json.

What it adds

node-red/02 strategy-deadline-charge.json 38 nodes, 6 function nodes, tab Strategy Charge Deadline v4.15.0
home assistant/packages/house_battery_deadline_charge.yaml 784 lines: 16 helpers, 13 template sensors, 1 binary sensor
home assistant/packages/house_battery_control.yaml the strategy option in 5 selectors (11 via the &SubStrategies anchor) + 4 recorder exclusions
home assistant/dashboard.yaml one view
docs/03-strategies.md a ### ⏰ Charge Deadline section
node-red/all-flows-in-one-file.json the tab + 36 nodes (the shared server config node is reused, global-config not duplicated)

Every external entity it reads is either a core HBC helper or part of the documented integrator
contract in docs/09-for-integrators.md — I checked them all mechanically.

Design points I would like your view on

  1. Fleet SoC. HBC has no combined-SoC entity, so the package defines
    sensor.hbc_battery_fleet_soc (energy-weighted across the packs, silent packs excluded
    rather than counted as 0 %). It feels more like a core-package citizen than a
    Charge-Deadline one — would you rather it lived in house_battery_control.yaml?
  2. Battery count. range(1, 7) in that sensor and the six-entity lists in
    sensor.deadline_estimated_soc are the only hard-coded counts, matching the existing
    convention in house_battery_control.yaml. Happy to follow whatever you prefer long term.
  3. Recorder. Four sensors use now() and so re-render every minute by design; I added them
    to the exclusion list. None carries state_class, so no statistics are lost.
  4. Where the view goes. I put it in its own view. It could equally be folded into
    Charge/Sell.
  5. Versioning. bump-version.ps1 is maintainer-only, so I have not bumped anything or
    written release notes. Tell me what you would like in the PR.
  6. Naming. File is 02 strategy-deadline-charge.json to sit next to the other strategies;
    the strategy is called Charge Deadline. Shout if you would rather have them agree.

Testing

Beyond running it live: the Jinja templates render against a mocked state machine (70 assertions
— window clamping, merged price runs, malformed marks, unavailable packs) and the Compute plan
function runs against scenario fixtures in Node (35 assertions — pacing, the target-reached
latch, PV reservation, import limits, stale settings).

Next

If the shape sounds right I will open the PR. If you would prefer it split — package + flow
first, dashboard and docs after — that is easy to do.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions