All checks were successful
Build & Deploy docs / build-deploy (push) Successful in 3m32s
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
191 lines
8.9 KiB
Markdown
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.
|