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:
parent
0b8ccc6c9d
commit
eb7d727d17
@ -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
|
||||
|
||||
@ -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> |
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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__ -->
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user