A `valveGroupControl` (VGC) coordinates a group of `valve` children that share a common manifold — selector-valve banks, dosing-valve trains, mixing manifolds. It accepts a group-level total flow target, splits the flow proportional to each valve's Kv rating, runs a residual-reconciliation pass against what every valve actually accepted, and aggregates max delta-P across the group. It also reconciles upstream-source fluid-contract advertisements (`liquid` / `gas`) into one group-level service-type view that downstream consumers can read.
| What it represents | A parallel valve manifold — 2 + valves sharing a header, distributing one total flow target |
| S88 level | Unit |
| Use it when | You have 2 + parallel valves that should share an upstream flow target proportional to their Kv |
| Don't use it for | A single valve (wire `valve` directly), series valves (the Kv-share solver assumes parallel branches), or a manifold whose upstream already publishes per-branch setpoints |
1.`child.register` for each `valve` — or rely on Port-2 wiring to auto-register.
2.`set.mode = auto` — lets the parent source drive the group.
3.`data.totalFlow = 80` (with `unit: 'm3/h'`) — VGC splits 80 m³/h across the available valves by Kv share; runs up to `maxPasses: 2` residual passes; writes back `atEquipment_predicted_flow` = sum of accepted per-valve flows.
4.`cmd.execSequence` with `{action: "startup"}` — runs the group-wide startup sequence through `executeSequence`, transitioning the group state machine through each step.
5.`cmd.emergencyStop` — runs the `emergencystop` sequence on all valves (`[emergencystop, off]`).
6.`set.reconcileInterval = 2` — re-tunes the periodic tick to 2 s (`reconcileIntervalChange` event triggers the adapter to restart its tick loop; minimum 100 ms).
> **GIF needed.** Demo recording of steps 1–6 with the live status panel. Save as `wiki/_partial-gifs/valveGroupControl/01-basic-demo.gif`, target ≤ 1 MB after `gifsicle -O3 --lossy=80`.
| `set.mode` | `setMode` | `"auto"` \| `"virtualControl"` \| `"fysicalControl"` \| `"maintenance"` | Switch operational mode. Each mode has its own allow-list of sources (`mode.allowedSources`). |
| `set.position` | `setpoint` | any | **No-op pending Phase 7.** Reserved for future per-valve positional override. Debug-logged only. |
| `child.register` | `registerChild` | `string` (child node id) | Manually register a child via `RED.nodes.getNode`; Port 2 wiring does this automatically in most flows. |
| `data.totalFlow` | `totalFlowChange` | number, `{ value, position?, variant?, unit? }`, or `{ source, action, ... }` | Update the total measured/predicted flow at the configured position; triggers `calcValveFlows` to re-distribute across valves. |
| `cmd.emergencyStop` | `emergencyStop`, `emergencystop` | optional `{ source }` | Run the `emergencystop` sequence on all valves. |
| `set.reconcileInterval` | `setReconcileInterval` | number — seconds (> 0) | Re-tune the periodic flow-reconciliation interval. Min clamp 100 ms. |
Key shape: **`<position>_<variant>_<type>`** — same as MGC's key shape (inverse of `rotatingMachine`'s per-measurement form). The output reflects the group aggregate, not per-valve snapshots; per-valve detail comes off each valve's own Port 0.
| `maxDeltaP` | Max delta-P across registered valves — refreshed whenever a child emits `deltaPChange`. Also surfaced as `deltaMax_predicted_pressure` via the measurement container. |
| `atEquipment_measured_flow` | Total measured flow at the group inlet (from an upstream source's `flow.measured.*` event). |
| `atEquipment_predicted_flow` | Sum of per-valve accepted flows after the Kv-share + residual pass. |
| `deltaMax_predicted_pressure` | Max delta-P across the group, written via the measurement container at position `delta` / variant `predicted` / type `pressure`. |
A valve is **available** if: `state.getCurrentState() !== 'off'` and `!== 'maintenance'`, `currentMode !== 'maintenance'`, and `kv > 0`. Unavailable valves are skipped and receive `updateFlow('predicted', 0, 'downstream')`.
VGC has **no FSM of its own** — state semantics belong to the child valves. `specificClass` instantiates a state object internally and stamps it `operational` at boot for sequence dispatch; the group's only coordination loop is the Kv-share solver above.