Patrick Schurig 01a0db7d25
All checks were successful
Build & Deploy docs / build-deploy (push) Successful in 3m32s
docs(moteur): schémas Mermaid — vue d'ensemble des briques, rangs et domaines
Vue d'ensemble : entrées, protection contre la surcharge, cycle de répartition en six
étapes, optimiseur en option, adaptateurs, charges. Rangs et domaines : une seule liste
pour le moteur ; les domaines ne servent qu'à l'affichage de l'app, un chauffe-eau peut
être servi en dernier. Encadré : une borne détectée arrive en fin de liste.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ry3vBrJT1tHdxAaXdCWi6L
2026-10-18 14:21:33 +02:00

191 lines
8.9 KiB
Markdown

# How the engine decides
<span class="badge community">COMMUNITY</span>
The box's energy engine drives your loads on its own: EV chargers, water heater,
SG-Ready heat pump, relays. It is free software (GPL-3.0) and it is the one that decides
in the **Community** edition. This page describes its logic for installers and
integrators; the following pages detail each feature.
## Overview
```mermaid
flowchart TB
CPT["Grid meter<br/>current per phase, export, import"]
BAT["Battery<br/>state of charge"]
APP["App<br/>one-off requests"]
CFG["Installer settings<br/>ranks, bounds, obligations"]
subgraph M["Energy engine, on the box"]
PROT["Overload protection<br/>on every measurement, overrides"]
subgraph C["Allocation cycle, every minute"]
C1["1. Measurement and draw margin"] --> C2["2. Charging deadlines"]
C2 --> C3["3. Battery reserve"]
C3 --> C4["4. Eco pass<br/>one-off requests, obligations"]
C4 --> C5["5. Comfort pass<br/>surplus in cascade, by rank"]
C5 --> C6["6. One command per load"]
end
AD["Adapters<br/>bounds and locks of each load"]
end
OPT["Optimizer, optional<br/>proposes the allocation"]
VE["EV chargers"]
ECS["Water heaters<br/>relay or adjustable"]
PAC["SG-Ready heat pump"]
REL["Relays"]
CPT --> PROT
CPT --> C1
BAT --> C3
APP --> C4
CFG --> C
OPT -. "plan" .-> C6
C6 --> AD
PROT --> AD
AD --> VE & ECS & PAC & REL
```
Overload protection and the adapters always stay with the engine: an
[optimizer](../optimiseur/index.md), if connected, only proposes the allocation.
## Two loops, two priorities
The engine keeps safety apart from allocation. They are two separate loops, running at
different rates and with different priorities.
- **Overload protection, on every measurement.** As soon as the measured balance
changes, the engine reads the current of each phase at the grid meter. If a phase
exceeds the configured limit, it **immediately** lowers the current of the EV chargers,
then releases it when the margin returns. The other loads (water heater, heat pump,
relays) are shed at the start of the next cycle ([load management](delestage.md)). This
protection overrides any allocation decision.
- **The allocation cycle, every minute.** It shares the available power between the
loads, on the one-minute measurement of the electrical balance, and runs again right
away when you change a setting. It is the only place where a strategy comes in: the
built-in rules, or an [optimizer](../optimiseur/index.md) if one is connected.
**If the grid meter goes silent** (more than 90 s without a reading), the engine suspends
allocation and puts the loads in a safe position: adjustable loads at 0 W, relays open,
heat pump in normal operation, chargers already charging brought down to minimum
current, no new load started. Overload protection stays active. Allocation resumes on the
cycle after the meter comes back.
## The rules, in order
On every cycle, the engine applies the same steps, in the same order.
1. **Measure.** The starting point is what the grid meter measures (export and import).
The engine also derives the **draw margin** from it: what the installation can still
take from the grid without exceeding the phase limit. No grid purchase, whatever its
reason, goes beyond it.
2. **Serve charging deadlines.** A vehicle that must be charged by a given time is served
from the grid, outside the surplus sharing.
3. **Protect the battery.** If a home battery is below its reserve threshold, the surplus
goes to it first: the controlled loads get none of it. Above the threshold, what it
absorbs counts as available surplus. Its discharge is never counted as surplus.
4. **Guaranteed needs ("eco" pass).** First the **one-off requests** made from the app
("heat now"): the load gets its full power, whatever its rank. Then the **daily
obligations**, for example a minimum amount of energy for the water heater before a
given time: the engine buys the missing energy from the grid, spread evenly until the
deadline, and switches to full power when only the time strictly needed is left. These
purchases stay within the draw margin.
5. **Share the surplus in cascade ("comfort" pass).** Loads are served in the
**priority** order you set (1 = served first). Each receives what is left of the
budget, within its bounds, and the budget is decreased accordingly. There is **one**
budget: without it, three managers would each see "2 kW of surplus" and draw 6. In
turn, for each load:
- what it already consumes is given back to it: a running load is not counted against
itself;
- below its minimum power, it stays off rather than running at half power, and the
budget moves on to the next one. An EV charger may still start with a small top-up
from the grid, within the configured tolerance;
- a load under **lock** (minimum on or off time, delay between two changes) keeps its
current power;
- an **unavailable** load keeps its current power, with no new setpoint; what it
consumes is still counted.
6. **One command per load.** The guaranteed need and the surplus share of the same load
are merged: each load receives a single setpoint, with its reason.
### Ranks and domains
The engine knows **only one list**: each load has a rank, across all categories, and the
cascade follows that rank. A load's **domain** (electric vehicle, domestic hot water, air
conditioning, heating, battery) is a label chosen by the installer: the engine does not
read it to decide.
The app uses it to present the service order **by domain**: you set the order of the
domains, then the order of the loads within each domain. At commissioning it proposes:
electric vehicle, domestic hot water, air conditioning, heating. Since everything rests on
the single list, nothing forces the loads of a domain to follow each other: a backup water
heater can be served **last of all**, after the heating, and one charger can come before
the water heater while another comes after.
```mermaid
flowchart LR
subgraph AV["Proposed order"]
direction TB
a1["1 · EV garage"] --> a2["2 · EV visitor"] --> a3["3 · Main water heater"] --> a4["4 · Backup water heater"] --> a5["5 · Heat pump"]
end
subgraph AP["After adjustment"]
direction TB
b1["1 · EV garage"] --> b2["2 · Main water heater"] --> b3["3 · EV visitor"] --> b4["4 · Heat pump"] --> b5["5 · Backup water heater"]
end
AV ==>|"only ranks change"| AP
classDef ev stroke:#3b82f6,stroke-width:3px
classDef ecs stroke:#ef4444,stroke-width:3px
classDef chauf stroke:#f59e0b,stroke-width:3px
class a1,a2,b1,b3 ev
class a3,a4,b2,b5 ecs
class a5,b4 chauf
```
Blue border: electric vehicle · red: domestic hot water · orange: heating.
!!! tip "Set the ranks at commissioning"
A detected charger is added automatically **at the end of the list**, whatever its
domain. Two loads with the same rank are separated by their identifier, which nobody
chose. Set the order in the app at commissioning.
### The bounds of each load
Each load carries bounds entered by the installer, which **every** decision respects,
whatever the strategy:
- a power **floor** and **ceiling**. For an EV charger they are in current (times the
number of phases), and the ceiling is further capped by the phase limit;
- **anti-flapping locks**: minimum on and off time, delay between two changes. An EV
charger has its own (delay between on and off, delay between two current changes);
- for an SG-Ready heat pump: the state chosen from the estimated power of each state,
with hysteresis and a minimum hold time. The blocking state is never commanded by the
surplus.
## The reason for every decision
The engine makes no decision without stating why. Every decision carries a non-empty
**reason** (`decisionReason`): a stable code and its parameters, which the app displays
in your language — "insufficient surplus", "below minimum power", "charging deadline"…
The list of codes is published with the
[optimizer protocol](../optimiseur/protocole.md#reponse).
## The measurement, never the result of control
The surplus is computed on the **raw measurement** of the grid meter, as measured. Never
on a balance recomputed after control: the engine would be reading itself and would
oscillate.
## Without internet
Everything above runs on the box. Without internet, the engine controls, applies safety
and answers the app on your local network. Only the [PV forecast](prevision-pv.md)
stops. In the Community edition, neither the forecast nor the
[entered tariff](../tarifs.md) enters the decisions of the rules: both are displayed and
recorded, never used to decide.
## And an optimizer?
An optimizer can propose a plan over several hours, based on forecasts. It replaces the
allocation step, **never** the overload protection or the bounds. See the
[Optimizer](../optimiseur/index.md) section.