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

4.9 KiB

Writing an optimizer

An optimizer is a server that speaks the protocol: 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 optimizer fits in about fifty lines of Python.

Minimal session, for illustration (the exact shape is in the contract):

→ {"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-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"}}

Three things not to miss:

  • one action per available load in the current slot, each with its reason as 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 optimizer needs, forecasts included, is in the context.

The reference optimizer

The engine repository contains a free reference optimizer (GPL-3.0), in Python, standard library only: 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.

It is a starting point for writing your own, and a point of comparison: it listens on the 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.

Pointing the engine at your optimizer

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 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

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 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 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:

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.

Expected mode: plan or abstain

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 then checks that the engine drives by its rules on every cycle, without counting a failure, and that safety still holds; the clamping check is skipped, as there is no setpoint to clamp.

Any other value makes the suite fail. The mode is not inferred from the handshake: planning and capabilities describe the shape of plans, not whether any are produced. The mode is given, not guessed.

Comparing on the same inputs

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 optimizer and summarises the differences from the recorded plan. Traces stay on site.