Skip to content

feat(control): optional single-battery scope for manual hold #607

Description

@frahlg

Feature Description

Allow operators to pin a temporary battery setpoint to one specific battery driver, instead of always applying it to the whole site battery pool.

Default remains pool / aggregate control. Single-battery scope is an explicit opt-in at control time (UI + API).

Motivation

Community use case (Sourceful FTW Discord / #support):

  • Multi-battery site
  • One pack's internal cells are unbalanced
  • Operator wants to charge only that pack toward ~100% during high PV so the BMS can rebalance
  • Current charge mode and /api/battery/manual_hold act on the aggregate pool, so all online batteries start charging

Today's workarounds (priority, temporary driver offline, vendor app) are clumsy for a common service/balancing task.

Proposed Solution

Extend manual hold with an optional driver scope:

API

POST /api/battery/manual_hold

{
  "direction": "charge",
  "power_w": 5000,
  "hold_s": 900,
  "driver": "battery_a"
}
  • driver optional; omit/empty = pool (current behavior)
  • GET returns the same driver field when scoped
  • Scoped semantics: target power on that battery only; other online batteries held at 0 W
  • Existing SoC / per-driver caps / slew / fuse guard still apply
  • Unknown driver → 400
  • Expiry / DELETE clears hold and returns to selected site mode

UI

In <ftw-battery-control>:

  • Scope: All batteries (default) | selected battery
  • When opened from a battery card/planet, preselect that driver
  • Active-hold banner shows scoped driver

Control loop

In ComputeDispatch manual-hold path:

  • no driver → existing aggregate distribute
  • with driver → scoped targets (hold power on that driver, 0 on others) then existing clamp/slew/fuse pipeline

Alternatives Considered

  • priority mode — still site distribution, awkward for one-off balancing
  • Temporarily disable other drivers — drops telemetry, error-prone
  • Vendor app full-charge — breaks FTW closed-loop
  • Permanent per-battery modes — larger product surface than needed

Non-goals

  • Permanent independent operating modes per battery
  • Planner/MPC optimizing packs separately as first-class products

Scope / Effort Estimate

Medium — go/internal/control, go/internal/api battery manual hold, web/components/ftw-battery-control.js, unit tests.

Additional Context

  • Site-EMS by design; multi-battery split is internal (priority/weighted)
  • Manual hold already bypasses planner/idle short-circuits but keeps safety clamps — best extension point
  • Discord: Fredrik approved capturing as a feature and starting a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions