Skip to content

Priorities and conflicts

A gateway runs one battery command at a time. This page says which entry gets it, and what happens when you, Home Assistant or another tool are already driving the battery.

Priority, conflict and missed-fire settings

Setting Values Default
Priority 0–1000; higher wins 0
If already controlled defer (skip), override, wait defer

Entries on the same target

Entries compete only with entries that have the same target (same type and id) and a battery action. Release, Reserve % and Operating Mode entries are battery actions too; No battery action entries never compete.

On each tick, for each gateway of the target, the engine picks one entry:

  1. The entry already running there keeps it, for as long as its window is open, even if its gate turns false and even if a higher-priority entry becomes active. Priority decides who gets a free gateway; it does not interrupt a run.
  2. Otherwise, the highest-priority active entry whose entry conditions pass wins. Ties go to the oldest entry (created first).
  3. If no active entry's gate passes, the top-ranked one is picked so that it logs its own gated row.

Each entry that loses is logged once per contest as skipped — "gateway held by '' this tick".

Give one-shot entries a short window

A Mode or Reserve entry holds the gateway for its whole window, although it only writes once. Use a fire-based trigger with no duration (one minute) for them, so they do not block a force entry on the same gateway.

Example

Entry Priority Window Gate
Evening discharge 10 17:00–21:00 SoC > 20 %
Peak-demand shave 20 cron */30, 29 min in demand window

At 17:00 both are active. If the demand gate passes, Peak-demand shave wins (20 > 10) and the evening entry logs skipped. If the demand gate fails, the evening entry runs. Once the evening entry runs, it keeps the gateway until its window ends, even when the demand gate starts to pass at 17:30. Read the window-end warning before you combine entries like this.

Manual control

Already controlled means the gateway's command handler is running a force command that this target's entries did not send, or a different command from the one the owner sent. That covers the Controls tab, the top-bar buttons, the Home Assistant / MQTT command entities, the REST API, Run now, a late fire, and entries on other targets.

If already controlled

Option (UI) API conflict Behaviour
defer (skip) defer (default) Leave the manual command alone. Logged once as deferred — "target under manual control (Force Charge)". The entry takes over when the manual command ends, if its window is still open.
override override Replace the manual command with this entry's action. The control log records the manual command as superseded.
wait wait Behaves exactly like defer.

Only force entries honour this setting. Release, Mode and Reserve entries act regardless (see Actions).

What the bridge cannot see

  • Another tool writing the setpoint straight to the aGate is not manual control here: the command handler did not send it. An entry dispatches over it. The control log records it as external_dispatch_started, and dispatch.source reads manual.

  • A manual command with the same action as the running entry looks like the entry's own command. The engine adopts it and releases it at the window end.

  • A manual command sent over a running entry is checked by the entry's supervision before the conflict policy. Within the first 90 seconds, or before the run is verified, the entry can judge the battery not effective and re-send its own command over yours. Once the run is verified, a different manual command makes the entry give up ownership (deferred with defer).

Entries on different targets

Priority only compares entries with the same target. An entry for Site, one for a Service and one for the gateway itself (or one with no target id and one with default) are arbitrated separately, even when they end up on the same physical gateway:

  • each treats the other's running command as manual control, so its If already controlled setting decides;
  • with defer the second entry waits; with override it takes the gateway, and the first entry may then try to take it back. Priority plays no part.

Keep all battery entries for one gateway on the same target type.

Who sent what (requester)

Every battery command records who asked for it. Scheduled commands carry schedule:<entry id>; Run now and late fires carry the same; the Controls tab and the REST API carry user:<account>; Home Assistant / MQTT commands carry mqtt. You see it in Logs → control log ("— by schedule:sch_1a2b3c4d") and in the dispatch record. The engine does not use it to decide ownership: ownership lives in the engine's memory and is lost on restart.