docs(optimiseur): liens vers le moteur sur Gitea, plus de renvoi à INTERFACE.md, « optimizer » en anglais

Le miroir GitHub n'est pas encore synchronisé : les liens pointent sur git.etm-powersync.fr.
INTERFACE.md n'est plus dans le dépôt public : la forme des méthodes NymeaEnergy.* se lit sur
la box (JSONRPC.Introspect) ; GetOptimizerStatus n'est pas réservée à l'administrateur.
Orthographe anglaise unifiée : optimizer, optimization.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ry3vBrJT1tHdxAaXdCWi6L
This commit is contained in:
Patrick Schurig 2026-10-18 13:48:22 +02:00
parent 0b8ccc6c9d
commit eb7d727d17
17 changed files with 90 additions and 96 deletions

View File

@ -14,11 +14,11 @@ app. That is an architectural decision, not an option.
## Is the project open source?
The energy engine (`etm-powersync-energy-plugin-etm`) and the plugins are under
GPL-3.0, including the optimiser client built into the engine — everything linked against
nymea is. The engine works on its own, without an optimiser. The
[protocol between the engine and an optimiser](optimiseur/index.md) is open and
published, with a free reference optimiser and a conformance suite: anyone can write their
own. The **ETM optimiser** and its business logic (tariff arbitration, forecasts learned
GPL-3.0, including the optimizer client built into the engine — everything linked against
nymea is. The engine works on its own, without an optimizer. The
[protocol between the engine and an optimizer](optimiseur/index.md) is open and
published, with a free reference optimizer and a conformance suite: anyone can write their
own. The **ETM optimizer** and its business logic (tariff arbitration, forecasts learned
from your installation), however, are proprietary: they run in a separate process, reached
through that protocol, at your place — on the box or on a machine in your local network.
That process boundary is the heart of the Open Core model — the engine stays free, the

View File

@ -12,7 +12,7 @@ An overview of the HEMS features and their real state.
| Baseload forecast | <span class="badge nightly">COMING</span> |
| [JSON-RPC API](../integrations/jsonrpc-api.md) / [MQTT](../integrations/mqtt.md) | <span class="badge stable">AVAILABLE</span> |
| Dynamic tariffs — FR: Tempo, off-peak · DE: §14a EnWG, EPEX spot | <span class="badge nightly">ROADMAP</span> |
| CO₂ optimisation — RTE (FR) / SMARD (DE) | <span class="badge nightly">ROADMAP</span> |
| CO₂ optimization — RTE (FR) / SMARD (DE) | <span class="badge nightly">ROADMAP</span> |
| Charge planner | <span class="badge nightly">ROADMAP</span> |
| Heat pump | <span class="badge nightly">ROADMAP</span> |

View File

@ -18,7 +18,7 @@ different rates and with different priorities.
there is headroom again. It does not wait for the cycle and overrides everything else.
- **The allocation cycle, about once a minute.** It shares the available power between
the loads. This is the only place a strategy acts: the built-in rules, or an
[optimiser](../optimiseur/index.md) if one is connected.
[optimizer](../optimiseur/index.md) if one is connected.
If the grid meter stops answering, the engine suspends allocation and falls back to safety
mode: no measurement, no decision.
@ -60,7 +60,7 @@ whatever the strategy:
The engine takes 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 — "surplus insufficient", "below minimum power", "charging deadline"… The
list of codes is published with the [optimiser protocol](../optimiseur/protocole.md#reponse).
list of codes is published with the [optimizer protocol](../optimiseur/protocole.md#reponse).
## The measurement, never the result of control
@ -75,8 +75,8 @@ enforces safety and answers the app on your local network. Only the
edition, any more than the [entered tariff](../tarifs.md): both are displayed and
recorded, never used by the rules.
## What about an optimiser?
## What about an optimizer?
An optimiser can propose a plan over several hours, based on the forecasts. It replaces
An optimizer can propose a plan over several hours, based on the forecasts. It replaces
the allocation step, **never** the safety loop or the bounds. See the
[Optimizer](../optimiseur/index.md) section.

View File

@ -71,7 +71,7 @@ therefore neither an "expected error" nor a "confidence interval".
## What the forecast does not do
This is the central rule of the Community edition: **without an optimiser, control is
This is the central rule of the Community edition: **without an optimizer, control is
rule-based** (solar surplus, load shedding, priorities). The PV forecast, the
[entered tariff](../tarifs.md) and the baseload are **displayed** and **recorded
locally**, but never used to decide. An automated test checks this: the engine makes
@ -95,12 +95,12 @@ coordinates** (rounded to about 1 km in the plugin versions that carry this feat
tariff and consumption stay on the box. Remote access is disabled by default: it opens
only if you enable it yourself in the app.
## What about an optimiser?
## What about an optimizer?
An optimiser, on the other hand, can use these forecasts to plan: the engine hands them
An optimizer, on the other hand, can use these forecasts to plan: the engine hands them
over, together with the entered tariff and the baseload, through an
[open, published protocol](../optimiseur/protocole.md). The optimiser runs at your place,
[open, published protocol](../optimiseur/protocole.md). The optimizer runs at your place,
on the box or on a machine in your local network, and gets its data from the engine only;
its setpoints stay capped by the engine's bounds and safety. Connecting an optimiser is
its setpoints stay capped by the engine's bounds and safety. Connecting an optimizer is
being tested; the Community edition works entirely without one. See the
[Optimizer](../optimiseur/index.md) section.

View File

@ -1,11 +1,11 @@
# ETM PowerSync
**Local HEMS for solar PV, heat pump and EV charging installations.** ETM PowerSync
coordinates photovoltaics, battery, heat pump and EV charger under a single optimisation
coordinates photovoltaics, battery, heat pump and EV charger under a single optimization
logic — running at your site, on your hardware, with no cloud dependency.
Built on [nymea.io](https://nymea.io), following an **Open Core** architecture: a free
foundation (GPL-3.0) and a proprietary optimisation layer.
foundation (GPL-3.0) and a proprietary optimization layer.
!!! info "Honest statuses"
Every feature shows its real state: <span class="badge stable">STABLE</span>
@ -17,7 +17,7 @@ foundation (GPL-3.0) and a proprietary optimisation layer.
- [Installation](installation/index.md) — deploy ETM PowerSync from the APT repository
- [Features](fonctionnalites/index.md) — what the HEMS does, and its real state;
[how the engine decides](fonctionnalites/moteur.md)
- [Optimizer](optimiseur/index.md) — the open protocol between the engine and an optimiser
- [Optimizer](optimiseur/index.md) — the open protocol between the engine and an optimizer
- [Compatibility](appareils/compatibilite.md) — supported equipment
- [Integrations](integrations/jsonrpc-api.md) — JSON-RPC API, MQTT, Home Assistant

View File

@ -42,10 +42,8 @@ Der hier veröffentlichte Vertrag folgt der Entwicklung der Engine; Ihre Box kan
die Box: Die JSON-RPC-Methode `NymeaEnergy.GetOptimizerProtocol` liefert den, den sie
mitbringt. Dieser ist für einen an diese Box angebundenen Optimierer maßgeblich.
Die Methode ist in
[`INTERFACE.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/INTERFACE.md)
im Engine-Repository beschrieben, zusammen mit den übrigen `NymeaEnergy.*`-Methoden des
Optimierers (Verbindungspunkt, gemeinsames Geheimnis, Zustand). Die Verbindung zur API
Sie gehört zu den `NymeaEnergy.*`-Methoden des Optimierers (Verbindungspunkt, gemeinsames
Geheimnis, Zustand); ihre genaue Form liefert die Box selbst über `JSONRPC.Introspect`. Die Verbindung zur API
beschreibt die Seite [JSON-RPC-API](../integrations/jsonrpc-api.md).
## Nachrichten prüfen {#valider}

View File

@ -39,12 +39,10 @@ repository. Its record:
The contract published here follows the engine's development; your box may carry an
earlier version. To get **the** contract of the installed engine, ask the box: the
JSON-RPC method `NymeaEnergy.GetOptimizerProtocol` returns the one it embeds. That is the
one that is authoritative for an optimiser connected to that box.
one that is authoritative for an optimizer connected to that box.
The method is described in
[`INTERFACE.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/INTERFACE.md),
in the engine repository, along with the other optimiser `NymeaEnergy.*` methods
(endpoint, shared secret, status). Connecting to the API is described on the
It belongs to the optimizer `NymeaEnergy.*` methods (endpoint, shared secret, status); the
box itself returns their exact form through `JSONRPC.Introspect`. Connecting to the API is described on the
[JSON-RPC API](../integrations/jsonrpc-api.md) page.
## Validating messages {#valider}

View File

@ -41,10 +41,8 @@ antérieure. Pour connaître **le** contrat du moteur installé, demandez-le à
méthode JSON-RPC `NymeaEnergy.GetOptimizerProtocol` rend celui qu'elle embarque. C'est
celui-là qui fait foi pour un optimiseur branché sur cette box.
La méthode est décrite dans
[`INTERFACE.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/INTERFACE.md),
dans le dépôt du moteur, avec les autres méthodes `NymeaEnergy.*` de l'optimiseur
(point de connexion, secret partagé, état). La connexion à l'API est décrite sur la page
Elle fait partie des méthodes `NymeaEnergy.*` de l'optimiseur (point de connexion, secret
partagé, état) ; la box en rend la forme exacte par `JSONRPC.Introspect`. La connexion à l'API est décrite sur la page
[API JSON-RPC](../integrations/jsonrpc-api.md).
## Valider des messages {#valider}

View File

@ -27,7 +27,7 @@ Drei Punkte, die man nicht übersehen sollte:
Das Engine-Repository enthält einen freien Referenz-Optimierer (GPL-3.0) in Python, nur
mit der Standardbibliothek:
[`tools/etm-powersync-ref-optimizer/`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/tree/main/tools/etm-powersync-ref-optimizer).
[`tools/etm-powersync-ref-optimizer/`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tools/etm-powersync-ref-optimizer).
Er antwortet mit einem Plan aus einem Zeitfenster, der die Kaskade der Engine-Regeln
nachbildet, und liest dabei nur den Kontext. Beim Handshake meldet er sich als
`powersync-optimizer`.
@ -36,12 +36,12 @@ Er ist Ausgangspunkt für einen eigenen Optimierer und Vergleichsmaßstab: Er la
standardmäßig am lokalen Socket oder an `tcp://` mit einem gemeinsamen Geheimnis, das aus
einer Datei gelesen wird. Er kann auch Fehler einspeisen (Enthaltung, Langsamkeit,
ungültiger Plan, andere Hauptversion…), um den Rückfall der Engine zu testen.
Anleitung: [seine `README.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tools/etm-powersync-ref-optimizer/README.md).
Anleitung: [seine `README.md`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tools/etm-powersync-ref-optimizer/README.md).
## Die Engine an den eigenen Optimierer anbinden {#brancher}
Drei JSON-RPC-Methoden der Box, dem Administrator vorbehalten, beschrieben in
[`INTERFACE.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/INTERFACE.md):
Drei JSON-RPC-Methoden der Box (die ersten beiden dem Administrator vorbehalten); ihre genaue
Form liefert die Box selbst über `JSONRPC.Introspect`:
- `NymeaEnergy.SetOptimizerEndpoint` — wohin verbinden (`unix:///…` oder `tcp://host:port`);
- `NymeaEnergy.SetOptimizerToken` — das gemeinsame Geheimnis, dasselbe wie beim Optimierer;
@ -78,7 +78,7 @@ export ETM_POWERSYNC_CONFORMANCE_EXPECT=plan # oder abstain
Jede Funktion läuft in einem eigenen Prozess. Der vollständige Befehl (Netzisolierung
empfohlen), die übrigen Variablen (gemeinsames Geheimnis, Startfrist…) und das Lesen der
Fehlschläge stehen in der
[`README.md` der Suite](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tests/auto/conformance/README.md).
[`README.md` der Suite](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tests/auto/conformance/README.md).
### Erwarteter Modus: `plan` oder `abstain` {#mode-attendu}

View File

@ -1,14 +1,14 @@
# Writing an optimizer
An optimiser is a **server** that speaks the [protocol](protocole.md): it answers the
An optimizer is a **server** that speaks the [protocol](protocole.md): it answers the
handshake, then every `GetPlan` with a plan of one or more slots, or with an abstention.
Any language will do. A minimal optimiser fits in about fifty lines of Python.
Any language will do. A minimal optimizer fits in about fifty lines of Python.
Minimal session, for illustration (the exact shape is in the [contract](contrat.md)):
```text
→ {"jsonrpc":"2.0","id":1,"method":"Handshake","params":{"protocolVersion":"1.1","engine":"etm-powersync-energy-plugin-etm/…"}}
← {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"1.1","name":"my-optimiser","planning":false}}
← {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"1.1","name":"my-optimizer","planning":false}}
→ {"jsonrpc":"2.0","id":2,"method":"GetPlan","params":{ …context… }}
← {"jsonrpc":"2.0","id":2,"result":{"abstain":true,"reason":"premier calcul en cours"}}
```
@ -19,14 +19,14 @@ Three things not to miss:
text: otherwise the whole plan is rejected (the reason text is in French);
- **answer quickly**: serve the last cached plan, or abstain, and compute in the
background;
- **fetch nothing elsewhere**: everything the optimiser needs, forecasts included, is in
- **fetch nothing elsewhere**: everything the optimizer needs, forecasts included, is in
the context.
## The reference optimiser {#reference}
## The reference optimizer {#reference}
The engine repository contains a free reference optimiser (GPL-3.0), in Python, standard
The engine repository contains a free reference optimizer (GPL-3.0), in Python, standard
library only:
[`tools/etm-powersync-ref-optimizer/`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/tree/main/tools/etm-powersync-ref-optimizer).
[`tools/etm-powersync-ref-optimizer/`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tools/etm-powersync-ref-optimizer).
It answers a one-slot plan that reproduces the cascade of the engine's rules, reading only
the context. At handshake it announces itself as `powersync-optimizer`.
@ -34,52 +34,52 @@ It is a starting point for writing your own, and a point of comparison: it liste
local socket by default, or on `tcp://` with a shared secret read from a file. It can also
inject faults (abstention, slowness, invalid plan, different major version…) to test the
engine's fallback. Instructions:
[its `README.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tools/etm-powersync-ref-optimizer/README.md).
[its `README.md`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tools/etm-powersync-ref-optimizer/README.md).
## Pointing the engine at your optimiser {#brancher}
## Pointing the engine at your optimizer {#brancher}
Three JSON-RPC methods of the box, reserved to the administrator, described in
[`INTERFACE.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/INTERFACE.md):
Three JSON-RPC methods of the box (the first two reserved to the administrator); the box itself
returns their exact form through `JSONRPC.Introspect`:
- `NymeaEnergy.SetOptimizerEndpoint` — where to connect (`unix:///…` or `tcp://host:port`);
- `NymeaEnergy.SetOptimizerToken` — the shared secret, the same as the optimiser's;
- `NymeaEnergy.GetOptimizerStatus` — connection status: optimiser alive, active strategy,
- `NymeaEnergy.SetOptimizerToken` — the shared secret, the same as the optimizer's;
- `NymeaEnergy.GetOptimizerStatus` — connection status: optimizer alive, active strategy,
announced name, last error code.
Over `tcp://` without encryption, the secret travels in clear text on the local network.
## The conformance suite {#conformite}
The `etm-powersync-optimizer-conformance` suite starts **your** optimiser and connects the
The `etm-powersync-optimizer-conformance` suite starts **your** optimizer and connects the
**real** engine to it, on simulated devices. It checks, one function per point:
1. the handshake is accepted;
2. every `GetPlan` is answered within the engine's timeout;
3. the whole session, both ways, conforms to the contract embedded in the engine,
validation rules included;
4. every plan is applied: the optimiser is in control;
4. every plan is applied: the optimizer is in control;
5. the safety loop stays on top: an out-of-bounds setpoint is clamped, a phase overload is
shed.
It does not judge the **quality** of the plans.
### Running it against your optimiser
### Running it against your optimizer
Build the engine with its tests, then give the command that starts your server;
`{endpoint}` in it is replaced by the address to listen on:
```sh
export ETM_POWERSYNC_OPTIMIZER_CMD='exec /path/to/my-optimiser --listen {endpoint}'
export ETM_POWERSYNC_OPTIMIZER_CMD='exec /path/to/my-optimizer --listen {endpoint}'
export ETM_POWERSYNC_CONFORMANCE_EXPECT=plan # or abstain
```
Each function runs in its own process. The full command (network isolation recommended),
the other variables (shared secret, start-up delay…) and how to read failures are in the
[suite's `README.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tests/auto/conformance/README.md).
[suite's `README.md`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tests/auto/conformance/README.md).
### Expected mode: `plan` or `abstain` {#mode-attendu}
`ETM_POWERSYNC_CONFORMANCE_EXPECT` tells the suite what your optimiser does:
`ETM_POWERSYNC_CONFORMANCE_EXPECT` tells the suite what your optimizer does:
- **`plan`** (default): it is in control. Every cycle must apply its plan.
- **`abstain`**: it abstains on every cycle — that is allowed, and conformant. The suite
@ -95,5 +95,5 @@ The mode is given, not guessed.
The engine can record, on the box, a trace of every cycle (off by default,
`NymeaEnergy.SetOptimizerTrace`). The `tools/etm-powersync-trace-replay` tool replays these
traces against a candidate optimiser and summarises the differences from the recorded plan.
traces against a candidate optimizer and summarises the differences from the recorded plan.
Traces stay on site.

View File

@ -27,7 +27,7 @@ Trois points à ne pas manquer :
Le dépôt du moteur contient un optimiseur de référence libre (GPL-3.0), en Python,
bibliothèque standard seule :
[`tools/etm-powersync-ref-optimizer/`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/tree/main/tools/etm-powersync-ref-optimizer).
[`tools/etm-powersync-ref-optimizer/`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tools/etm-powersync-ref-optimizer).
Il répond un plan à un créneau qui reproduit la cascade des règles du moteur, en ne lisant
que le contexte. Il s'annonce `powersync-optimizer` au handshake.
@ -35,12 +35,12 @@ C'est un point de départ pour écrire le sien, et un point de comparaison : il
le socket local par défaut, ou sur `tcp://` avec un secret partagé lu dans un fichier. Il
sait aussi injecter des défauts (abstention, lenteur, plan invalide, majeure différente…)
pour tester le repli du moteur. Mode d'emploi :
[son `README.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tools/etm-powersync-ref-optimizer/README.md).
[son `README.md`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tools/etm-powersync-ref-optimizer/README.md).
## Brancher le moteur sur son optimiseur {#brancher}
Trois méthodes JSON-RPC de la box, réservées à l'administrateur, décrites dans
[`INTERFACE.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/INTERFACE.md) :
Trois méthodes JSON-RPC de la box (les deux premières réservées à l'administrateur) ; la box
en rend la forme exacte par `JSONRPC.Introspect` :
- `NymeaEnergy.SetOptimizerEndpoint` — où se connecter (`unix:///…` ou `tcp://hôte:port`) ;
- `NymeaEnergy.SetOptimizerToken` — le secret partagé, le même que celui de l'optimiseur ;
@ -77,7 +77,7 @@ export ETM_POWERSYNC_CONFORMANCE_EXPECT=plan # ou abstain
Chaque fonction se lance dans son propre processus. La commande complète (isolement
réseau recommandé), les autres variables (secret partagé, délai de démarrage…) et la
lecture des échecs sont dans le
[`README.md` de la suite](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/tests/auto/conformance/README.md).
[`README.md` de la suite](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/tests/auto/conformance/README.md).
### Mode attendu : `plan` ou `abstain` {#mode-attendu}

View File

@ -2,57 +2,57 @@
<span class="badge testing">IN PROGRESS</span>
The protocol between the engine and an optimiser is published (version 1.1). Connecting
an optimiser is being tested on our test installation; the box does not need one to work.
The protocol between the engine and an optimizer is published (version 1.1). Connecting
an optimizer is being tested on our test installation; the box does not need one to work.
## What an optimiser is for
## What an optimizer is for
The [engine's rules](../fonctionnalites/moteur.md) decide on the present: this minute's
surplus, shared according to your priorities. An optimiser looks further ahead. From the
surplus, shared according to your priorities. An optimizer looks further ahead. From the
forecasts the engine hands it — PV production, base consumption, entered tariff — it
proposes a **plan in time slots** over several hours: shift the water heater to the
expected production peak, keep headroom for evening charging, and so on.
## The optimiser advises, the engine decides
## The optimizer advises, the engine decides
- The engine only executes the **current slot** of the plan. The rest is shown as a
forecast, and requested again on the next cycle.
- Every setpoint is **capped** by the load's bounds (floor, ceiling, anti-flapping locks)
and by the real-time **safety loop**, which stays with the engine and overrides
everything. An optimiser cannot bypass a lock.
everything. An optimizer cannot bypass a lock.
- Every action must carry its **reason**. An action without one is rejected.
- A malformed plan, or one that targets an unknown load, is rejected as a whole.
At worst, a faulty optimiser can therefore only propose a poor allocation — bounded by the
At worst, a faulty optimizer can therefore only propose a poor allocation — bounded by the
engine.
## The box works without an optimiser
## The box works without an optimizer
Optimiser missing, silent, abstaining, plan expired or rejected: the engine returns to its
Optimizer missing, silent, abstaining, plan expired or rejected: the engine returns to its
**rules** (`rules`) for that cycle, with no interruption of control. The app shows at all
times which strategy is in charge. The Community edition works entirely this way.
## Its data come from the engine only
- The engine connects to the optimiser, never the other way round: the box opens no port.
- The optimiser runs on the box (local socket) or on a machine in your local network
- The engine connects to the optimizer, never the other way round: the box opens no port.
- The optimizer runs on the box (local socket) or on a machine in your local network
(TCP). On the local network, a shared secret (token) is recommended.
- It receives **only** the context the engine sends, forecasts included: it calls neither
a weather service nor a tariff provider, and opens no connection to the outside.
- What it learns stays on site.
## Who can write an optimiser
## Who can write an optimizer
Anyone. The protocol is open and described by a [machine-readable contract](contrat.md)
published under GPL-3.0. A free **reference optimiser** and a **conformance suite** let you
published under GPL-3.0. A free **reference optimizer** and a **conformance suite** let you
write your own and check it against the real engine — see
[Writing an optimizer](ecrire.md).
## The ETM optimiser
## The ETM optimizer
ETM-Schurig offers its own proprietary optimiser, included in the Pro edition. It connects
ETM-Schurig offers its own proprietary optimizer, included in the Pro edition. It connects
through this same protocol, within the same limits: it advises, the engine decides. At
handshake it announces itself as `etm-optimizer`; the reference optimiser announces itself
handshake it announces itself as `etm-optimizer`; the reference optimizer announces itself
as `powersync-optimizer`.
## In this section
@ -60,4 +60,4 @@ as `powersync-optimizer`.
- [Protocol](protocole.md) — transport, handshake, context, plan, fallback, validation
rules.
- [Contract](contrat.md) — the OpenRPC file that is authoritative on the shape of messages.
- [Writing an optimizer](ecrire.md) — reference optimiser and conformance suite.
- [Writing an optimizer](ecrire.md) — reference optimizer and conformance suite.

View File

@ -11,7 +11,7 @@ eigenen Kanal. Es ist nicht die API der App: Diese läuft über die
Beschreibungen unverändert aus dem Vertrag, auf Französisch. Sie sind in allen drei
Sprachen identisch.
- **Die Bedeutung** ist ausführlich, auf Französisch, in
[`OPTIMIZER_PROTOCOL.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/docs/OPTIMIZER_PROTOCOL.md)
[`OPTIMIZER_PROTOCOL.md`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/docs/OPTIMIZER_PROTOCOL.md)
im Repository der Engine beschrieben. Diese Seite fasst das Wesentliche zusammen; bei
Abweichungen gelten die Dokumente der Engine.

View File

@ -1,6 +1,6 @@
# Protocol
The protocol links the box's energy **engine** to an **optimiser**, over a dedicated
The protocol links the box's energy **engine** to an **optimizer**, over a dedicated
channel. It is not the app's API: the app goes through the
[JSON-RPC API](../integrations/jsonrpc-api.md).
@ -10,13 +10,13 @@ channel. It is not the app's API: the app goes through the
**generated** from it on every publication: framing in English, descriptions taken
as-is from the contract, in French. They are identical in all three languages.
- **The meaning** is described in detail, in French, in
[`OPTIMIZER_PROTOCOL.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/docs/OPTIMIZER_PROTOCOL.md),
[`OPTIMIZER_PROTOCOL.md`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/docs/OPTIMIZER_PROTOCOL.md),
in the engine repository. This page gives the essentials; where they differ, the
engine's documents prevail.
## Transport and roles {#transport}
- The optimiser is the **server**, the engine is the **client**: the engine connects and
- The optimizer is the **server**, the engine is the **client**: the engine connects and
asks the questions. The box opens no port.
- Two transports, same messages: `unix://` (local socket, default) and `tcp://` (machine
on the local network).
@ -65,10 +65,10 @@ channel. It is not the app's API: the app goes through the
## Handshake {#handshake}
On every connection, the engine sends `Handshake` with its protocol version and, if
configured, the shared secret. The optimiser answers with its version and, if it wishes,
configured, the shared secret. The optimizer answers with its version and, if it wishes,
its name and capabilities. A different **major** version, a refused secret or an
unreadable answer: the connection is refused, and the engine drives by its rules. If the
optimiser announces the action kinds (`kinds`) it emits, the engine then rejects any action
optimizer announces the action kinds (`kinds`) it emits, the engine then rejects any action
of another kind.
<!-- BEGIN GENERATED: __optimizer_handshake__ -->
@ -505,14 +505,14 @@ Undeclared fields: **rejected** (engine-emitted form, closed).
## The answer: a plan or an abstention {#reponse}
The optimiser answers **quickly** — with its last cached plan if it computes for long —
The optimizer answers **quickly** — with its last cached plan if it computes for long —
and recomputes in the background. It answers either:
- a **plan in time slots**: each slot carries one action per available load (setpoint in
watts or amps, or SG-Ready state), each with its reason; only the current slot is
executed, and `validUntil` bounds the plan's lifetime;
- an **abstention** (`abstain: true`): "no plan right now". The engine drives by its rules
for that cycle, without treating the optimiser as failed.
for that cycle, without treating the optimizer as failed.
An action's reason has two forms: a free text in French, **mandatory**, for the traces;
and, optionally, a code from the catalogue (`reasonCode`) with its parameters. Only **the
@ -684,7 +684,7 @@ Type: map of number \| boolean \| string \| null
## `UpdateLearned` {#updatelearned}
Notification sent by the optimiser to the engine, with no answer, to pass on what it has
Notification sent by the optimizer to the engine, with no answer, to pass on what it has
learned about the loads. In version 1.1 of the engine, it is received and logged, but not
yet used.
@ -704,8 +704,8 @@ Envoyée par l'OPTIMISEUR (sens inverse des deux autres), sans id, sans réponse
## Fallback state machine {#repli}
There is no separate heartbeat: a valid answer to `GetPlan` means the optimiser is alive.
Failures are counted per request; after several consecutive failures, the optimiser is
There is no separate heartbeat: a valid answer to `GetPlan` means the optimizer is alive.
Failures are counted per request; after several consecutive failures, the optimizer is
declared absent and the engine drives by its rules until the next valid plan. The app sees
this state continuously (`optimizerAlive`, `activeStrategy`).
@ -763,7 +763,7 @@ Ce que le moteur refuse EN PLUS de la forme, dans l'ordre de ses contrôles (Opt
### Tolerances {#tolerances}
What the engine accepts beyond the contract. A conformant optimiser does not rely on it.
What the engine accepts beyond the contract. A conformant optimizer does not rely on it.
<!-- BEGIN GENERATED: __optimizer_tolerances__ -->

View File

@ -9,7 +9,7 @@ dédié. Ce n'est pas l'API de l'application : celle-ci passe par l'[API JSON-RP
**générés** à chaque publication : cadre en anglais, descriptions reprises telles
quelles du contrat, en français. Ils sont identiques dans les trois langues.
- **Le sens** est décrit en détail, en français, dans
[`OPTIMIZER_PROTOCOL.md`](https://github.com/etmschurig/etm-powersync-energy-plugin-etm/blob/main/docs/OPTIMIZER_PROTOCOL.md),
[`OPTIMIZER_PROTOCOL.md`](https://git.etm-powersync.fr/ETM-Schurig/etm-powersync-energy-plugin-etm/src/branch/main/docs/OPTIMIZER_PROTOCOL.md),
dans le dépôt du moteur. Cette page en donne l'essentiel ; en cas d'écart, les
documents du moteur l'emportent.

View File

@ -6,6 +6,6 @@ The box integrates with your tools through two local interfaces, on your network
conventions — and its [full reference](api/index.md), generated from the box;
- [MQTT](integrations/mqtt.md).
!!! note "What about the optimiser protocol?"
The channel between the engine and an optimiser is **not** an integration API: it
!!! note "What about the optimizer protocol?"
The channel between the engine and an optimizer is **not** an integration API: it
has its own protocol, described in the [Optimizer](optimiseur/protocole.md) section.

View File

@ -17,15 +17,15 @@ range. A tariff with several ranges is shown there read-only.
!!! note "Exposed, not used to decide"
The engine **exposes** the tariff (display, local recording); it does not decide
with it. Without an optimiser, control stays rule-based — see
with it. Without an optimizer, control stays rule-based — see
[PV production forecast](fonctionnalites/prevision-pv.md#what-the-forecast-does-not-do).
## Dynamic tariffs <span class="badge nightly">ROADMAP</span>
Arbitration on national tariff signals — **France**: Tempo (blue/white/red), off-peak
and peak hours, dynamic offers; CO₂ optimisation from the RTE eco2mix signal.
and peak hours, dynamic offers; CO₂ optimization from the RTE eco2mix signal.
**Germany**: variable tariffs, spot offers (EPEX), grid-responsive control of
controllable loads under **§14a EnWG**; CO₂ optimisation from **SMARD**
controllable loads under **§14a EnWG**; CO₂ optimization from **SMARD**
(Bundesnetzagentur).
!!! note "Roadmap"