powersync-docs/docs/optimiseur/protocole.de.md
Patrick Schurig eb7d727d17 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
2026-10-18 13:48:22 +02:00

861 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Protokoll
Das Protokoll verbindet die **Energie-Engine** der Box mit einem **Optimierer**, über einen
eigenen Kanal. Es ist nicht die API der App: Diese läuft über die
[JSON-RPC-API](../integrations/jsonrpc-api.md).
!!! info "Was maßgeblich ist"
- **Die Form** der Nachrichten (Felder, Typen, zulässige Werte, was die Engine
ablehnt) legt der [maschinenlesbare Vertrag](contrat.md) fest. Die Tabellen dieser
Seite werden bei jeder Veröffentlichung daraus **erzeugt**: Rahmen auf Englisch,
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://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.
## Transport und Rollen {#transport}
- Der Optimierer ist der **Server**, die Engine der **Client**: Die Engine verbindet sich
und stellt die Fragen. Die Box öffnet keinen Port.
- Zwei Transporte, dieselben Nachrichten: `unix://` (lokaler Socket, Standard) und
`tcp://` (Rechner im lokalen Netz).
- JSON-RPC 2.0, eine Nachricht pro Zeile (NDJSON), UTF-8.
- Bricht die Verbindung ab, verbindet sich die Engine mit wachsender Wartezeit neu und
steuert in der Zwischenzeit nach ihren Regeln.
<!-- BEGIN GENERATED: __optimizer_transport__ -->
| Property | Value (contract) |
|---|---|
| Protocol version | `1.1` |
| Application protocol | JSON-RPC `2.0` |
| Framing | `ndjson` (one message per line) |
| Encoding | `utf-8` |
| Endpoint schemes | `unix://` · `tcp://` |
| Default endpoint | `unix:///run/powersync/optimizer.sock` |
| Client | `engine` |
| Server | `optimizer` |
| Max line size | 1048576 bytes |
| Handshake timeout | 5 s |
| Request timeout | 10 s |
| Reconnect backoff | 1 s → 60 s, ×2 |
| Requests in flight | 1 |
| Request ids | entiers croissants sur toute la vie du client, à travers les reconnexions |
| Line errors | ligne non JSON, sans id, id jamais émis, ligne > maxLineBytes : comptée, connexion CONSERVÉE |
| Token masking | toute occurrence du token dans un message reçu est remplacée par « [token masqué] » dès l'analyse |
**Servers**
| Name | URL | Summary (contract) | Example values |
|---|---|---|---|
| `unix` | `unix:///run/powersync/optimizer.sock` | Défaut : optimiseur local, socket possédé par root:powersync, mode 0660. | |
| `tcp` | `tcp://{host}:{port}` | Optimiseur sur le LAN. Token recommandé ; il circule en clair sans TLS (E16). | `host` = `192.168.1.10`, `port` = `7400` |
**Methods**
| Method | Direction | Kind | Timeout | Summary (contract) |
|---|---|---|---|---|
| `Handshake` | `engine→optimizer` | request | 5 s | Ouverture de session, à chaque (re)connexion (§3). |
| `GetPlan` | `engine→optimizer` | request | 10 s | Demande d'un plan pour le contexte courant (§4-§6). |
| `UpdateLearned` | `optimizer→engine` | notification (no id) | — | Estimations apprises de l'optimiseur (§8) — notification, sans id. |
<!-- END GENERATED -->
## Handshake {#handshake}
Bei jeder Verbindung sendet die Engine `Handshake` mit ihrer Protokollversion und, falls
konfiguriert, dem gemeinsamen Geheimnis. Der Optimierer antwortet mit seiner Version und,
wenn er möchte, seinem Namen und seinen Fähigkeiten. Andere **Hauptversion**, abgelehntes
Geheimnis oder unlesbare Antwort: Die Verbindung wird abgelehnt, und die Engine steuert
nach ihren Regeln. Kündigt der Optimierer die Aktionsarten (`kinds`) an, die er sendet,
lehnt die Engine danach jede Aktion einer anderen Art ab.
<!-- BEGIN GENERATED: __optimizer_handshake__ -->
#### `Handshake` — params
Ouverture de session, à chaque (re)connexion (§3).
Envoyé par le moteur (client) dès la connexion. Délai : 5 s depuis le DÉBUT de la tentative ; au-delà, repli et nouvelle tentative au backoff. Le message porte le secret : il n'est jamais journalisé.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `protocolVersion` | [`ProtocolVersion`](#schema-protocolversion) | yes | | Version du moteur ; seule la majeure est comparée. |
| `engine` | string | yes | pattern `^etm-powersync-energy-plugin-etm/` | Moteur et sa version. |
| `token` | [`HandshakeToken`](#schema-handshaketoken) | no | | |
**result** (`handshake`): [`HandshakeResult`](#schema-handshakeresult)
**Errors**
| Code | Message | Note (contract) |
|---|---|---|
| `-32001` | token refusé | Recommandé (optimiseur de référence). Toute réponse error est traitée pareil : handshake-refused, connexion fermée. |
#### `HandshakeToken` {#schema-handshaketoken}
Secret partagé (§3, E-T2-8) : 16 caractères au moins, 4 096 octets au plus (borne en octets non exprimable ici : contrôlée par le moteur), imprimable, sans blanc ni caractère de contrôle, UTF-8 admis. Envoyé seulement s'il est configuré ; jamais journalisé.
Type: string — ≥ 16 char. ; pattern `^[^\s\x00-\x1f\x7f]+$`
#### `HandshakeResult` {#schema-handshakeresult}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `protocolVersion` | string | yes | ≥ 1 char. ; pattern `^[0-9]+(\.[0-9]+)*$` | Version semver de l'optimiseur ; même MAJEURE que le moteur exigée (règle major-mismatch). |
| `name` | string | no | | Nom affiché (mode installateur), non contrôlé par le moteur (E-T7-3). |
| `planning` | boolean | no | | Vrai si l'optimiseur produit des plans multi-créneaux. Informatif. |
| `capabilities` | [`Capabilities`](#schema-capabilities) | no | | |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `Capabilities` {#schema-capabilities}
Capacités annoncées (§3, 1.1, optionnel). Affichées en mode installateur, réduites aux clés et valeurs connues (capacitesPubliees) ; un champ inconnu est ignoré (§9).
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `maxHorizonH` | number | no | ≥ 0 | Horizon maximal planifié, en heures. |
| `resolutionS` | number | no | ≥ 0 | Pas des créneaux, en secondes. |
| `kinds` | [[`ActionKind`](#schema-actionkind)] | no | | PRÉSENT, il restreint les kinds acceptés (règle kind-not-announced). |
| `usesForecast` | [string] | no | `pv` · `baseload` · `price` · `temperature` | |
| `reasonCodes` | boolean | no | | L'optimiseur émet des reasonCode du catalogue. |
Undeclared fields: **ignored** (optimizer-emitted form, open).
<!-- END GENERATED -->
## Der Kontext: `GetPlan` {#contexte}
In jedem Zyklus und bei Ereignissen (Fahrzeug angesteckt, neue Prognose, geänderte
Konfiguration…) sendet die Engine `GetPlan` mit einem **Kontext**: Zustand der Anlage und
des Netzanschlusszählers, Erzeugung, Speicher, die verteilten Verbraucher mit ihren
Grenzen, Sperren und Bedarfen, Prognosen auf einem einzigen Zeitraster.
Einige Leseregeln, die Entscheidungen verändern:
- der Zähler wird als **Rohmessung** übertragen, nie als Bilanz nach der Steuerung;
- ein unbekannter Wert ist `null`, **nie 0**;
- ein nicht verfügbarer Verbraucher (`available: false`) darf keinen Sollwert erhalten,
verbraucht aber: Er wird weiter gezählt;
- laufende Sperren (`lockMinPowerW`, `lockMaxPowerW`) sagen, was der Verbraucher **jetzt**
kann; die Engine wendet sie in jedem Fall an;
- was ein Verbraucher misst, steht in `measurement.source`, nie in seinem Mechanismus
(`adapter`); eine angegebene Leistung ist nie eine Messung.
Die von der Engine gesendete Form ist **geschlossen**: Sie sendet kein Feld außerhalb des
Vertrags.
<!-- BEGIN GENERATED: __optimizer_context__ -->
#### `GetPlan` — params
Demande d'un plan pour le contexte courant (§4-§6).
À chaque cycle (~1/min) et sur événement (trigger). Une requête en vol au plus : une plus récente remplace l'attente de la précédente (sa réponse est ignorée par id, ce n'est pas un échec). Délai : 10 s par requête. La réponse à la requête N sert au cycle N+1 (modèle cache). Le JSON Schema dit la FORME ; x-etm-semantic-rules dit ce que le validateur refuse en plus.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `timestamp` | string | yes | instant with offset | Instant du cycle — le « maintenant » du validateur. |
| `trigger` | [`Trigger`](#schema-trigger) | yes | | |
| `site` | [`Site`](#schema-site) | yes | | |
| `meter` | [`Meter`](#schema-meter) | yes | | |
| `pv` | [`Pv`](#schema-pv) | yes | | |
| `battery` | [`Battery`](#schema-battery) | yes | | |
| `loads` | [[`Load`](#schema-load)] | yes | | Charges arbitrées, ⊆ GetLoadConfig. |
| `forecast` | [`Forecast`](#schema-forecast) \| null | yes | | null sans instantané de prévision. |
| `tariff` | [`Tariff`](#schema-tariff) \| null | yes | | |
**Errors**
| Code | Message | Note (contract) |
|---|---|---|
| `-32603` | erreur interne | Toute réponse error compte comme un échec de la requête (rpc-error, §7) ; le code n'est pas lu. |
#### `Site` {#schema-site}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `contractedPowerW` | number \| null | yes | | Puissance souscrite (W) ; null si inconnue (E1), jamais 0. |
| `gridCapW` | null | yes | | RÉSERVÉ (signaux réseau type §14a EnWG) : toujours null en 1.1. |
| `phaseLimitA` | [number] \| null | yes | | Limite par phase en A ; null si inconnue. |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `Meter` {#schema-meter}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `source` | string \| null | yes | | ThingId du compteur racine désigné ; null si inconnu. |
| `importW` | number | yes | ≥ 0 | Puissance importée (W). |
| `exportW` | number | yes | ≥ 0 | Puissance exportée (W), mesure BRUTE (AGENTS.md règle 8) — jamais le net après pilotage. |
| `perPhaseA` | [number] | yes | | Courant par phase (A). |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `Pv` {#schema-pv}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `currentW` | number \| null | yes | ≥ 0 | Production instantanée (W), positive ; null sans producteur (jamais 0). |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `Battery` {#schema-battery}
One of: [`BatteryAbsent`](#schema-batteryabsent) · [`BatteryPresent`](#schema-batterypresent).
#### `Load` {#schema-load}
Une charge arbitrée (§5.3) — la forme dépend du mécanisme (adapter).
One of: [`LoadWatts`](#schema-loadwatts) · [`LoadEvCharger`](#schema-loadevcharger) · [`LoadSgReady`](#schema-loadsgready).
#### `Forecast` {#schema-forecast}
Prévisions sur UNE grille (§5.2) ; tableaux alignés index par index.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `start` | string | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `resolutionS` | integer | yes | ≥ 1 | Pas de la grille (s) — fait foi, jamais supposé. |
| `steps` | integer | yes | ≥ 0 | Nombre de cases ; toutes les séries ont cette longueur. |
| `pv` | [`PvForecast`](#schema-pvforecast) \| null | yes | | null sans courbe (site non désigné, charge utile refusée, canal muet). |
| `baseload` | [`Baseload`](#schema-baseload) | yes | | |
| `weather` | null | yes | | Aucune source de température en 1.1 : toujours null. |
| `price` | [`Price`](#schema-price) \| null | yes | | null sans tarif saisi. |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `Tariff` {#schema-tariff}
Tarif 1.0, conservé pour compatibilité ; un optimiseur 1.1 lit forecast.price. Aucun écrivain aujourd'hui (E3) : null en pratique.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `provider` | string \| null | yes | | |
| `current` | [`TariffEntry`](#schema-tariffentry) | yes | | |
| `next` | [[`TariffNextEntry`](#schema-tariffnextentry)] | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `BatteryAbsent` {#schema-batteryabsent}
Aucun stockage déclaré : rien d'autre n'est émis.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `present` | boolean | yes | `false` | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `BatteryPresent` {#schema-batterypresent}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `present` | boolean | yes | `true` | |
| `socPercent` | number | yes | | |
| `powerW` | number | yes | | Positif = charge, négatif = décharge. |
| `capacityWh` | number \| null | yes | | null si inconnue. |
| `maxChargeW` | number \| null | yes | | null si inconnue. |
| `maxDischargeW` | number \| null | yes | | null si inconnue. |
| `reserveSocPercent` | integer \| null | yes | | Réserve de secours (%) ; null si inconnue. |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `LoadWatts` {#schema-loadwatts}
Charge pilotée en watts : combinaison de relais (relay-router) ou consigne continue (etmvariableload).
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `id` | string | yes | ≥ 1 char. | Identifiant de la charge — clé des actions. |
| `label` | string | yes | | Nom lisible. |
| `priority` | integer | yes | | Rang effectif (1 = premier servi). |
| `domain` | [`Domain`](#schema-domain) | no | | |
| `learned` | [`LoadLearned`](#schema-loadlearned) | no | | |
| `needs` | [`LoadNeeds`](#schema-loadneeds) | no | | |
| `adapter` | string | yes | `relay-router` · `etmvariableload` | |
| `declared` | object | yes | | |
| `declared.minPowerW` | number | yes | | 0 = aucun plancher. |
| `declared.maxPowerW` | number | yes | | |
| `telemetry` | object | yes | | |
| `telemetry.currentPowerW` | number | yes | | Puissance courante (W) telle que l'adaptateur la publie. |
| `telemetry.available` | boolean | yes | | Gouverne l'ALLOCATION, pas la COMPTABILITÉ (§5.3). |
| `telemetry.measurement` | [`Measurement`](#schema-measurement) | no | | |
| `telemetry.lockMinPowerW` | number | yes | | Plancher imposé MAINTENANT par un verrou. |
| `telemetry.lockMaxPowerW` | number | yes | | Plafond imposé maintenant ; négatif = aucun plafond. |
| `limits` | object | yes | | |
| `limits.minOnS` | integer | yes | | |
| `limits.minOffS` | integer | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `LoadEvCharger` {#schema-loadevcharger}
Borne. Une borne CONFIGURÉE dont l'appareil manque n'a ni declared, ni limits, ni pluggedIn/charging : ses zéros diraient « inconnu ».
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `id` | string | yes | ≥ 1 char. | Identifiant de la charge — clé des actions. |
| `label` | string | yes | | Nom lisible. |
| `priority` | integer | yes | | Rang effectif (1 = premier servi). |
| `domain` | [`Domain`](#schema-domain) | no | | |
| `learned` | [`LoadLearned`](#schema-loadlearned) | no | | |
| `needs` | [`LoadNeeds`](#schema-loadneeds) | no | | |
| `adapter` | string | yes | `"evcharger"` | |
| `declared` | object | no | | |
| `declared.minA` | number | yes | | |
| `declared.maxA` | number | yes | | |
| `declared.phases` | integer | yes | | Phases VISÉES ce cycle, pas celles de la plaque (E26). |
| `telemetry` | object | yes | | |
| `telemetry.currentPowerW` | number | yes | | Puissance courante (W) telle que l'adaptateur la publie. |
| `telemetry.available` | boolean | yes | | Gouverne l'ALLOCATION, pas la COMPTABILITÉ (§5.3). |
| `telemetry.measurement` | [`Measurement`](#schema-measurement) | no | | |
| `telemetry.pluggedIn` | boolean | no | | |
| `telemetry.charging` | boolean | no | | |
| `telemetry.sessionWh` | number | no | | Absent si la borne ne compte pas sa session (LM-1009 C1). |
| `limits` | object | no | | |
| `limits.chargingEnabledLockS` | integer | yes | | |
| `limits.currentChangeLockS` | integer | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `LoadSgReady` {#schema-loadsgready}
PAC SG-Ready : 4 états normés, kind state.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `id` | string | yes | ≥ 1 char. | Identifiant de la charge — clé des actions. |
| `label` | string | yes | | Nom lisible. |
| `priority` | integer | yes | | Rang effectif (1 = premier servi). |
| `domain` | [`Domain`](#schema-domain) | no | | |
| `learned` | [`LoadLearned`](#schema-loadlearned) | no | | |
| `needs` | [`LoadNeeds`](#schema-loadneeds) | no | | |
| `adapter` | string | yes | `"sg-ready"` | |
| `declared` | object | yes | | |
| `declared.states` | [integer] | yes | ≥ 1 ; ≤ 4 | |
| `declared.estimatedPowerW` | map of number | yes | | |
| `telemetry` | object | yes | | |
| `telemetry.currentPowerW` | number | yes | | Puissance courante (W) telle que l'adaptateur la publie. |
| `telemetry.available` | boolean | yes | | Gouverne l'ALLOCATION, pas la COMPTABILITÉ (§5.3). |
| `telemetry.measurement` | [`Measurement`](#schema-measurement) | no | | |
| `telemetry.state` | integer | yes | ≥ 0 ; ≤ 4 | État commandé (1-4) ; 0 = non applicable. |
| `telemetry.minState` | integer | yes | | Fenêtre d'états autorisée MAINTENANT par minStateHold. |
| `telemetry.maxState` | integer | yes | | |
| `limits` | object | yes | | |
| `limits.minStateHoldS` | integer | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `PvForecast` {#schema-pvforecast}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `p50W` | [number \| null] | yes | | Puissance AC moyenne sur [t, t+pas), sommée sur les plans puis plafonnée à acLimitW ; null = case inconnue. Un plafond, pas une espérance (§5.2). |
| `conversion` | string | yes | `physics` | Seule valeur émise par le moteur GPL ; un consommateur ignore une valeur inconnue. |
| `corrections` | [[`Correction`](#schema-correction)] | yes | | [] = conversion brute. |
| `weatherModel` | string \| null | yes | | Modèle qui a RÉPONDU ; null si inconnu. |
| `issuedAt` | string \| null | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `contentChangedAt` | string \| null | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `acLimitW` | integer | no | | Plafond AC du site (W), s'il est configuré. |
| `perField` | [[`PvField`](#schema-pvfield)] | no | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `Baseload` {#schema-baseload}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `model` | string | yes | `profile` · `none` | Le moteur GPL émet profile ou none (learned n'est pas émis). |
| `p50W` | [number \| null] | yes | | |
| `quality` | [`BaseloadQuality`](#schema-baseloadquality) | no | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `Price` {#schema-price}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `currency` | string | yes | `"EUR"` | |
| `importCtkWh` | [number \| null] | yes | | |
| `exportCtkWh` | [number \| null] | yes | | |
| `includes` | [[`PriceInclude`](#schema-priceinclude)] | yes | | |
| `source` | string | yes | `fixed` · `tou` | Tarif SAISI : une zone (fixed) ou des zones horaires (tou). tempo et day-ahead ne sont pas émis en 1.1. |
| `knownUntil` | string | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `TariffEntry` {#schema-tariffentry}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `label` | string | yes | | |
| `priceCtkWh` | number | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `TariffNextEntry` {#schema-tariffnextentry}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `label` | string | yes | | |
| `priceCtkWh` | number | yes | | |
| `from` | string \| null | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `LoadLearned` {#schema-loadlearned}
Absent tant qu'aucun profil n'a été reçu (E12).
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `dailyEnergyWh` | number | yes | | |
| `confidence` | number | yes | ≥ 0 ; ≤ 1 | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `LoadNeeds` {#schema-loadneeds}
Besoins (§5.3). Champs 1.0 émis tels quels quand ils sont déclarés ; un optimiseur 1.1 préfère energyTargets.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `dailyDeadline` | string | no | pattern `^[0-9]{2}:[0-9]{2}$` | Heure murale locale HH:MM (1.0). |
| `minEnergyWhPerDay` | integer | no | > 0 | |
| `targetSocPercent` | integer | no | > 0 | |
| `deadline` | string | no | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `energyTargets` | [[`EnergyTarget`](#schema-energytarget)] | no | ≥ 1 items | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `Measurement` {#schema-measurement}
Mesure par charge (§5.3, LM-1105/1106) : décider sur source, jamais sur adapter.
**variant 1** — Rien ne mesure : état positif, pas une erreur.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `source` | string | yes | `"none"` | |
Undeclared fields: **rejected** (engine-emitted form, closed).
**variant 2**
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `source` | string | yes | `meter` · `device` | |
| `powerW` | number | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `PvField` {#schema-pvfield}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `id` | string | yes | | ThingId du plan. |
| `corrections` | [[`Correction`](#schema-correction)] | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `BaseloadQuality` {#schema-baseloadquality}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `windowDays` | integer | yes | | |
| `minDays` | integer | yes | | |
| `resolutionS` | integer | yes | | |
| `daysCovered` | object | yes | | |
| `daysCovered.workday` | integer | yes | | |
| `daysCovered.weekend` | integer | yes | | |
| `minutes` | integer | yes | | |
| `commandedMinutes` | integer | yes | | |
| `negativeMinutes` | integer | yes | | |
| `incompleteMinutes` | integer | yes | | |
| `computedAt` | string | no | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `loads` | [[`BaseloadLoadQuality`](#schema-baseloadloadquality)] | yes | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `EnergyTarget` {#schema-energytarget}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `by` | string | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `minWh` | number | yes | > 0 | Énergie minimale à avoir délivrée à l'échéance. |
| `source` | string | yes | `installer` · `user-intent` | learned n'est pas émis en 1.1. |
Undeclared fields: **rejected** (engine-emitted form, closed).
#### `BaseloadLoadQuality` {#schema-baseloadloadquality}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `loadId` | string | yes | | |
| `source` | string | yes | `meter` · `device` · `none` | |
| `minutesBySource` | map of integer | yes | keys `meter` · `device` · `none` | |
| `constantMeasurement` | boolean | no | | |
Undeclared fields: **rejected** (engine-emitted form, closed).
<!-- END GENERATED -->
## Die Antwort: ein Plan oder eine Enthaltung {#reponse}
Der Optimierer antwortet **schnell** — mit seinem zuletzt zwischengespeicherten Plan, wenn
er lange rechnet — und rechnet im Hintergrund neu. Er antwortet entweder:
- mit einem **Plan in Zeitfenstern**: Jedes Fenster enthält eine Aktion je verfügbarem
Verbraucher (Sollwert in Watt oder Ampere, oder SG-Ready-Zustand), jede mit ihrem Grund;
nur das aktuelle Fenster wird ausgeführt, und `validUntil` begrenzt die Gültigkeit des
Plans;
- mit einer **Enthaltung** (`abstain: true`): „jetzt kein Plan“. Die Engine steuert für
diesen Zyklus nach ihren Regeln, ohne den Optimierer als ausgefallen zu betrachten.
Der Grund einer Aktion hat zwei Formen: einen freien Text auf Französisch,
**verpflichtend**, für die Aufzeichnungen; und optional einen Code aus dem Katalog
(`reasonCode`) mit seinen Parametern. An die App wird **nur der Code** übertragen, und die
App übersetzt ihn. Ein unbekannter Code geht als `OPTIMIZER_DECISION` hinaus.
Die von der Engine empfangene Form ist **offen**: Ein unbekanntes Feld wird ignoriert.
<!-- BEGIN GENERATED: __optimizer_plan__ -->
#### `GetPlan` — result
`plan`: [`GetPlanResult`](#schema-getplanresult)
**Errors**
| Code | Message | Note (contract) |
|---|---|---|
| `-32603` | erreur interne | Toute réponse error compte comme un échec de la requête (rpc-error, §7) ; le code n'est pas lu. |
#### `GetPlanResult` {#schema-getplanresult}
One of: [`Abstention`](#schema-abstention) · [`Plan`](#schema-plan).
#### `Abstention` {#schema-abstention}
« Pas de plan maintenant » (§7) : repli pour ce cycle, pas un échec.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `abstain` | boolean | yes | `true` | |
| `reason` | string | no | | |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `Plan` {#schema-plan}
Plan par créneaux (§6).
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `abstain` | boolean | no | `false` | |
| `planId` | string | no | | |
| `strategy` | string | no | | Chaîne libre, informative. |
| `computedAt` | string | no | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `validUntil` | string \| null | no | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `slots` | [[`Slot`](#schema-slot)] | yes | | [] = abstention (empty-plan). Seul le créneau couvrant maintenant est exécuté. |
| `diagnostics` | [`Diagnostics`](#schema-diagnostics) | no | | |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `Slot` {#schema-slot}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `from` | string | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `to` | string | yes | instant with offset | Instant ISO 8601 AVEC son décalage (Z ou ±hh:mm) — une heure murale nue est refusée. |
| `actions` | [[`Action`](#schema-action)] \| null | no | | Absent ou null = aucune action. |
| `expected` | [`Expected`](#schema-expected) | no | | |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `Diagnostics` {#schema-diagnostics}
Informatif : mode installateur et traces.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `solver` | string | no | | |
| `status` | string | no | `optimal` · `feasible` · `best-so-far` · `infeasible-relaxed` | |
| `gapPercent` | number | no | | |
| `computeMs` | number | no | ≥ 0 | |
| `inputsUsed` | object | no | | |
| `inputsUsed.pv` | string | no | `physics` · `hybrid` · `none` | |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `Action` {#schema-action}
One of: [`SetpointAction`](#schema-setpointaction) · [`StateAction`](#schema-stateaction) · [`ConstraintAction`](#schema-constraintaction).
#### `Expected` {#schema-expected}
Trajectoire prévue : affichage et mesure d'écart, JAMAIS pour commander.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `gridImportW` | number | no | | |
| `gridExportW` | number | no | | |
| `batterySocPercent` | any | no | | RÉSERVÉ (annexe F) : ignoré. |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `SetpointAction` {#schema-setpointaction}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `loadId` | string | yes | ≥ 1 char. | Charge visée : connue du contexte (règle unknown-load), une fois par créneau (duplicate-load). |
| `reason` | string | yes | ≥ 1 char. ; pattern `\S` | Texte français, OBLIGATOIRE et non vide (règle missing-reason). Traces et journal seulement, jamais vers l'app. |
| `reasonCode` | [`ReasonCode`](#schema-reasoncode) | no | | |
| `reasonParams` | [`ReasonParams`](#schema-reasonparams) | no | | |
| `force` | any | no | | INTERDIT sur le fil (§6) : ignoré, signalé une fois. |
| `kind` | string | yes | `"setpoint"` | |
| `powerW` | number | no | ≥ 0 | Consigne en watts (relay-router, etmvariableload ; ou borne). |
| `currentA` | number | no | ≥ 0 | Borne : courant (0 = suspendre). |
| `phases` | enum | no | `1` · `3` | Borne : phases visées ; absent = inchangé. |
Undeclared fields: **ignored** (optimizer-emitted form, open).
At least one of: `powerW`, `currentA`.
#### `StateAction` {#schema-stateaction}
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `loadId` | string | yes | ≥ 1 char. | Charge visée : connue du contexte (règle unknown-load), une fois par créneau (duplicate-load). |
| `reason` | string | yes | ≥ 1 char. ; pattern `\S` | Texte français, OBLIGATOIRE et non vide (règle missing-reason). Traces et journal seulement, jamais vers l'app. |
| `reasonCode` | [`ReasonCode`](#schema-reasoncode) | no | | |
| `reasonParams` | [`ReasonParams`](#schema-reasonparams) | no | | |
| `force` | any | no | | INTERDIT sur le fil (§6) : ignoré, signalé une fois. |
| `kind` | string | yes | `"state"` | |
| `state` | integer | yes | ≥ 1 ; ≤ 4 | 1 = verrouillage, 2 = normal, 3 = recommandation, 4 = forcé. |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `ConstraintAction` {#schema-constraintaction}
RÉSERVÉ (annexe F) : forme décrite, toujours refusée (kind-mismatch).
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `loadId` | string | yes | ≥ 1 char. | Charge visée : connue du contexte (règle unknown-load), une fois par créneau (duplicate-load). |
| `reason` | string | yes | ≥ 1 char. ; pattern `\S` | Texte français, OBLIGATOIRE et non vide (règle missing-reason). Traces et journal seulement, jamais vers l'app. |
| `reasonCode` | [`ReasonCode`](#schema-reasoncode) | no | | |
| `reasonParams` | [`ReasonParams`](#schema-reasonparams) | no | | |
| `force` | any | no | | INTERDIT sur le fil (§6) : ignoré, signalé une fois. |
| `kind` | string | yes | `"constraint"` | |
| `charge` | enum | no | `allow` · `forbid` | |
| `discharge` | enum | no | `allow` · `forbid` | |
Undeclared fields: **ignored** (optimizer-emitted form, open).
#### `ReasonParams` {#schema-reasonparams}
Paramètres du motif : valeurs nombre, booléen ou code [A-Za-z]+ ; clés admises par code : x-etm-reason-params. Toute autre clé est SUPPRIMÉE avant l'app (filtrée, pas refusée).
Type: map of number \| boolean \| string \| null
<!-- END GENERATED -->
### Zulässige Grund-Codes {#codes-de-motif}
<!-- BEGIN GENERATED: __optimizer_reason_codes__ -->
| `reasonCode` | Accepted `reasonParams` keys |
|---|---|
| `SURPLUS_SETPOINT` | `budgetW`, `setpointW`, `stepped` |
| `SURPLUS_INSUFFICIENT` | `budgetW` |
| `BELOW_MIN_POWER` | `budgetW`, `minPowerW`, `state` |
| `EV_DEADLINE` | — |
| `SG_FORCED` | `budgetW`, `estimatedW` |
| `SG_RECOMMENDED` | `budgetW`, `estimatedW` |
| `SG_NORMAL` | `budgetW` |
| `ECO_FLOOR_GRID` | `gridW`, `atMaximum`, `remainingWh`, `targetWh`, `remainingS`, `regime`, `minAlwaysW`, `boundBy` |
| `PRICE_SLOT` | `priceCtkWh`, `setpointW` |
| `OPTIMIZER_DECISION` | — |
<!-- END GENERATED -->
## `UpdateLearned` {#updatelearned}
Benachrichtigung vom Optimierer an die Engine, ohne Antwort, um mitzuteilen, was er über
die Verbraucher gelernt hat. In Version 1.1 der Engine wird sie empfangen und protokolliert,
aber noch nicht ausgewertet.
<!-- BEGIN GENERATED: __optimizer_update_learned__ -->
#### `UpdateLearned` — params
Estimations apprises de l'optimiseur (§8) — notification, sans id.
Envoyée par l'OPTIMISEUR (sens inverse des deux autres), sans id, sans réponse. État 1.1 du moteur : reçue, journalisée une fois, IGNORÉE — rien n'est persisté, le contenu n'est pas lu. Forme non normée en 1.1.
| Field | Type | Required | Values | Description (contract, French) |
|---|---|---|---|---|
| `loads` | [object] | no | | Non normé en 1.1 : intention §8 (learned + confidence par charge). |
<!-- END GENERATED -->
## Rückfall-Automat {#repli}
Es gibt keinen eigenen Herzschlag: Eine gültige Antwort auf `GetPlan` bedeutet, dass der
Optimierer lebt. Fehler werden je Anfrage gezählt; nach mehreren aufeinanderfolgenden
Fehlern gilt der Optimierer als abwesend, und die Engine steuert bis zum nächsten gültigen
Plan nach ihren Regeln. Die App sieht diesen Zustand laufend (`optimizerAlive`,
`activeStrategy`).
<!-- BEGIN GENERATED: __optimizer_fallback__ -->
Machine de repli §7 (OptimizerStatus) : heartbeat implicite — une réponse valide à GetPlan = optimiseur vivant. Les échecs se comptent PAR REQUÊTE.
Consecutive failed requests before `optimizerAlive = false`: **2**.
| Situation (contract) | `optimizerAlive` | `activeStrategy` |
|---|---|---|
| réponse plan valide | `true` | `optimizer` |
| abstain, plan vide, aucun créneau courant | `inchangé` | `rules (ce cycle)` |
| plan expiré (validUntil dépassé) | `inchangé` | `rules jusqu'au prochain plan valide` |
| timeout / erreur / plan invalide sur deux requêtes consécutives | `false` | `rules` |
| socket absent / fermé / handshake refusé | `false` | `rules` |
| aucun endpoint configuré | `false` | `rules` |
| mode dégradé L2 (compteur muet > 90 s) | `inchangé` | `none` |
<!-- END GENERATED -->
## Prüfregeln {#regles}
Das JSON Schema des Vertrags beschreibt die Form. Danach prüft die Engine jede Antwort
gegen den Kontext **ihrer** Anfrage, Regel für Regel, in der Reihenfolge unten; die erste,
die scheitert, liefert ihren Code (`fallbackCode`, in der App sichtbar).
<!-- BEGIN GENERATED: __optimizer_rules__ -->
Ce que le moteur refuse EN PLUS de la forme, dans l'ordre de ses contrôles (OptimizerPlan::valider, OptimizerClient::handleHandshakeReply). name = code du verdict. issue : abstention (repli ce cycle, pas un échec), expired, invalid (plan refusé en entier, compté comme échec), connection-refused (handshake). needs : ce qu'il faut connaître en plus de la réponse (la requête, le handshake).
| # | Rule | Method | Outcome | `fallbackCode` | `lastErrorCode` | Counts as failure | Also needs | Schema catches it too | What is rejected (contract) |
|---|---|---|---|---|---|---|---|---|---|
| 1 | `abstain` | `GetPlan` | `abstention` | `abstain` | — | no | — | no | result.abstain vaut true. |
| 2 | `malformed-plan` | `GetPlan` | `invalid` | `malformed-plan` | `malformed-plan` | yes | — | yes | slots absent ou non liste ; un créneau qui n'est pas un objet ; actions non liste. |
| 3 | `empty-plan` | `GetPlan` | `abstention` | `empty-plan` | — | no | — | no | slots est une liste vide. |
| 4 | `bad-time` | `GetPlan` | `invalid` | `bad-time` | `bad-time` | yes | — | yes | from/to illisibles ou sans décalage, ou from ≥ to ; validUntil illisible ou sans décalage. |
| 5 | `slots-unordered` | `GetPlan` | `invalid` | `slots-unordered` | `slots-unordered` | yes | — | no | un créneau commence avant la fin du précédent. |
| 6 | `malformed-action` | `GetPlan` | `invalid` | `malformed-action` | `malformed-action` | yes | — | yes | action qui n'est pas un objet, ou sans loadId (chaîne non vide). |
| 7 | `unknown-load` | `GetPlan` | `invalid` | `unknown-load` | `unknown-load` | yes | `request` | no | loadId absent des loads[] de la requête. |
| 8 | `unknown-kind` | `GetPlan` | `invalid` | `unknown-kind` | `unknown-kind` | yes | — | yes | kind hors ActionKind. |
| 9 | `kind-mismatch` | `GetPlan` | `invalid` | `kind-mismatch` | `kind-mismatch` | yes | `request` | no | kind différent de celui du mécanisme de la charge (setpoint : evcharger, relay-router, etmvariableload ; state : sg-ready ; constraint : aucun). |
| 10 | `kind-not-announced` | `GetPlan` | `invalid` | `kind-not-announced` | `kind-not-announced` | yes | `handshake` | no | capabilities.kinds annoncé au handshake et kind absent de la liste. |
| 11 | `missing-reason` | `GetPlan` | `invalid` | `missing-reason` | `missing-reason` | yes | — | yes | reason absent, non textuel, ou vide une fois les caractères de contrôle retirés. |
| 12 | `bad-value` | `GetPlan` | `invalid` | `bad-value` | `bad-value` | yes | `request` | yes | state absent ou hors 1-4 ; borne : currentA/powerW absents ou négatifs, phases hors {1, 3} ; charge en watts : powerW absent ou négatif. |
| 13 | `duplicate-load` | `GetPlan` | `invalid` | `duplicate-load` | `duplicate-load` | yes | — | no | deux actions pour la même charge dans un créneau. |
| 14 | `expired` | `GetPlan` | `expired` | `expired` | — | no | `request` | no | validUntil ≤ timestamp de la requête. |
| 15 | `no-current-slot` | `GetPlan` | `abstention` | `no-current-slot` | — | no | `request` | no | aucun créneau ne couvre le timestamp de la requête. |
| 16 | `incomplete-slot` | `GetPlan` | `invalid` | `incomplete-slot` | `incomplete-slot` | yes | `request` | no | une charge available de la requête n'a pas d'action dans le créneau courant (décision Q1). |
| 17 | `handshake-refused` | `Handshake` | `connection-refused` | `no-plan` | `handshake-refused` | no | — | no | réponse error (token absent ou différent : -32001 recommandé) → connexion fermée, nouvelle tentative au backoff. |
| 18 | `invalid-handshake` | `Handshake` | `connection-refused` | `no-plan` | `invalid-handshake` | no | — | yes | result non objet, protocolVersion absent ou vide, capabilities présent et non objet. |
| 19 | `major-mismatch` | `Handshake` | `connection-refused` | `no-plan` | `major-mismatch` | no | — | no | MAJEURE de protocolVersion (ce qui précède le premier point) différente de celle du moteur. |
<!-- END GENERATED -->
### Toleranzen {#tolerances}
Was die Engine über den Vertrag hinaus akzeptiert. Ein konformer Optimierer nutzt das nicht.
<!-- BEGIN GENERATED: __optimizer_tolerances__ -->
Ce que le moteur ACCEPTE au-delà du schéma — un optimiseur conforme ne s'en sert pas.
- reasonCode absent ou hors ReasonCode : action acceptée, motif transmis sous OPTIMIZER_DECISION sans paramètres.
- reasonParams : clé non admise pour le code (x-etm-reason-params) ou valeur refusée : clé supprimée, action acceptée.
- planId, strategy : lus comme chaînes quel que soit leur type ; computedAt illisible : ignoré.
- diagnostics, expected : non objets ignorés ; valeurs inconnues filtrées avant l'app.
- abstain : toute valeur « vraie » au sens de QVariant::toBool (true, chaîne non vide autre que « false »/« 0 », nombre non nul).
- champs inconnus (params et result) : ignorés (§9) ; force et champs réservés (annexe F) : ignorés et signalés une fois.
- state : 3.0 accepté comme 3 (entier au sens JSON Schema).
<!-- END GENERATED -->
## Geschlossene Listen {#listes-fermees}
Zulässige Werte, aus dem Code der Engine neu erzeugt.
<!-- BEGIN GENERATED: __optimizer_closed_lists__ -->
#### `ProtocolVersion` {#schema-protocolversion}
Version du protocole parlée par le moteur (GEN : OptimizerProtocol::ProtocolVersion).
`1.1`
Regenerated from the engine code: `OptimizerProtocol::ProtocolVersion`.
#### `Trigger` {#schema-trigger}
Cause de la requête (§5.1). GEN : SocketScheduler::knownTriggers() — du moins au plus significatif.
`cycle` · `forecast` · `config` · `load-change` · `intent`
Regenerated from the engine code: `SocketScheduler::knownTriggers()`.
#### `Domain` {#schema-domain}
Intention déclarée par l'installateur, jamais déduite du mécanisme, non lue par l'arbitre (§5.3). GEN : LoadConfig::knownDomains().
`ecs` · `ev` · `heating` · `hvac` · `battery`
Regenerated from the engine code: `LoadConfig::knownDomains()`.
#### `ActionKind` {#schema-actionkind}
Type d'action (§6). GEN : OptimizerPlan::kinds(). `constraint` est RÉSERVÉ (annexe F) : reconnu par le validateur, toujours refusé (règle kind-mismatch) faute de mécanisme porteur.
`setpoint` · `state` · `constraint`
Regenerated from the engine code: `OptimizerPlan::kinds()`.
#### `ReasonCode` {#schema-reasoncode}
Codes de motif qu'un optimiseur peut émettre (§6, liste blanche). GEN : DecisionCode::optimizerEmittableCodes(). Clés de paramètres admises par code : x-etm-reason-params.
`SURPLUS_SETPOINT` · `SURPLUS_INSUFFICIENT` · `BELOW_MIN_POWER` · `EV_DEADLINE` · `SG_FORCED` · `SG_RECOMMENDED` · `SG_NORMAL` · `ECO_FLOOR_GRID` · `PRICE_SLOT` · `OPTIMIZER_DECISION`
Regenerated from the engine code: `DecisionCode::optimizerEmittableCodes()`.
#### `PriceInclude` {#schema-priceinclude}
Composante incluse dans le prix (§5.2). GEN : TariffConfig::includes().
`energy` · `grid` · `taxes`
Regenerated from the engine code: `TariffConfig::includes()`.
#### `FallbackCode` {#schema-fallbackcode}
Côté app (NymeaEnergy.GetPlan.fallbackCode, INTERFACE.md) : pourquoi l'optimiseur n'a pas commandé le cycle. Hors fil. GEN : SocketScheduler::knownFallbackCodes().
`no-plan` · `abstain` · `empty-plan` · `no-current-slot` · `expired` · `malformed-plan` · `bad-time` · `slots-unordered` · `malformed-action` · `unknown-load` · `unknown-kind` · `kind-mismatch` · `kind-not-announced` · `missing-reason` · `bad-value` · `duplicate-load` · `incomplete-slot` · `no-shadow-slot`
Regenerated from the engine code: `SocketScheduler::knownFallbackCodes()`.
#### `LastErrorCode` {#schema-lasterrorcode}
Côté app (NymeaEnergy.GetOptimizerStatus.lastErrorCode, INTERFACE.md) : dernier code d'erreur vu (connexion, requête, plan). Hors fil. GEN : SocketScheduler::knownLastErrorCodes().
`invalid-endpoint` · `token-too-short` · `connect-failed` · `closed-during-handshake` · `connection-lost` · `handshake-timeout` · `handshake-refused` · `invalid-handshake` · `major-mismatch` · `line-too-long` · `invalid-json` · `missing-id` · `unknown-id` · `timeout` · `rpc-error` · `invalid-response` · `disconnected` · `malformed-plan` · `bad-time` · `slots-unordered` · `malformed-action` · `unknown-load` · `unknown-kind` · `kind-mismatch` · `kind-not-announced` · `missing-reason` · `bad-value` · `duplicate-load` · `incomplete-slot`
Regenerated from the engine code: `SocketScheduler::knownLastErrorCodes()`.
#### `Correction` {#schema-correction}
`temperature` · `ac-limit`
<!-- END GENERATED -->