etm-powersync-energy-plugin.../docs/INTERFACE_energyratios.md
Patrick Schurig 444b13dbf8 feat(ratios): NymeaEnergy.GetEnergyRatios + EnergyRatiosChanged (voie c)
Expose autoconsommation/autonomie comme source canonique de l'energymanager,
pour remplacer le seam interim app-side (EnergyRatiosInterim.compute).

Voie (c) : méthode + notification sur le handler NymeaEnergy de NOTRE plugin
(déjà forké/buildé), calculées depuis EnergyManager::totalX() — interface
abstraite stable. PAS de champs sur Energy.PowerBalance → aucun fork du coeur
nymea (nymea-experience-plugin-energy) à rebaser perpétuellement.

- EnergyRatiosCalculator (GPL pur mesure) : Δ de cumuls, baseline minuit local,
  reseed (1er appel / nouveau jour / non-monotone Δ<0), den<=0 -> n/a, clamp
  [0,100]. Porté 1:1 du seam interim app.
- NymeaEnergy.GetEnergyRatios -> {o:selfConsumptionRate, o:autonomyRate} Double,
  champ OMIS si n/a (jamais null).
- NymeaEnergy.EnergyRatiosChanged : meme forme, branchee sur powerBalanceChanged(),
  emise uniquement quand une valeur (ou sa disponibilite) change.
- testEnergyRatiosAlignment : vecteurs joues 1:1 contre l'interim (seed, normal,
  clamp-bas, den<=0->n/a, non-monotone, nouveau jour local).
- docs/INTERFACE_energyratios.md : contrat pour l'agent app (RPC a consommer).

Build prod 0/0. Suite simulation 25/0 (testLoadConfigRpc traverse le handler
modifie -> pas de regression).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 07:56:35 +02:00

4.1 KiB
Raw Permalink Blame History

Contrat — Ratios énergétiques canoniques (NymeaEnergy.GetEnergyRatios)

Statut : Phase 2, voie (c). Source canonique côté energymanager. Remplace le seam interim app-side EnergyRatiosInterim.compute(). Exposé par powersync-energy-plugin-nymea (experience-plugin, namespace JSON-RPC NymeaEnergy) — pas sur Energy.PowerBalance (voir §5).

1. Pourquoi (c) et pas des champs sur PowerBalance

PowerBalance (namespace Energy) appartient à nymea-experience-plugin-energy, un plugin cœur nymea. Y ajouter des champs imposerait de forker et rebaser ce plugin à chaque montée de version nymea — coût d'infra perpétuel. La voie (c) expose les ratios sur le handler NymeaEnergy de notre plugin (déjà forké, déjà buildé), calculés à partir des mêmes cumuls canoniques de l'energymanager. Valeurs strictement identiques à ce que (a) aurait produit ; seul le canal diffère.

2. Sémantique

Ratios dérivés par Δ de cumuls sur la journée locale courante (depuis minuit local), pas par intégration du signal live → déterministe, pas de drift.

autonomie        = (Δ totalConsumption  Δ totalAcquisition) / Δ totalConsumption
autoconsommation = (Δ totalProduction   Δ totalReturn)      / Δ totalProduction
  • Unité : pourcent, borné [0, 100].
  • Δ X = X_courant X_baseline, baseline = cumuls au début de la période.
  • Reseed de la baseline si : 1er calcul, nouveau jour local, OU compteur non monotone (Δ<0 : restart nymead / re-add thing / rollover).
  • Dénominateur ≤ 0 (nuit sans prod, etc.) → ratio n/a : le champ est OMIS de la réponse. Jamais null, jamais NaN.

C'est le portage 1:1 de EnergyRatiosInterim.compute() — l'app peut supprimer ce calcul et lire la source.

3. Méthode : NymeaEnergy.GetEnergyRatios

Params : aucun.

Returns :

Champ Type Présence
selfConsumptionRate Double (%) optionnel — omis si n/a
autonomyRate Double (%) optionnel — omis si n/a

Exemple (les deux disponibles) :

{"id": 1, "method": "NymeaEnergy.GetEnergyRatios", "params": {}}
{"id": 1, "status": "success",
 "params": {"selfConsumptionRate": 66.6667, "autonomyRate": 50.0}}

Exemple nuit (autoconso n/a, le champ disparaît) :

{"id": 1, "status": "success", "params": {"autonomyRate": 50.0}}

⚠️ Champ absent = n/a. Ne pas confondre avec 0 %. Mapper l'absence sur ton double? (Dart null).

4. Notification : NymeaEnergy.EnergyRatiosChanged

Mêmes champs que GetEnergyRatios. Émise sur powerBalanceChanged() de l'energymanager, uniquement quand une valeur (ou sa disponibilité) change.

{"notification": "NymeaEnergy.EnergyRatiosChanged",
 "params": {"selfConsumptionRate": 80.0, "autonomyRate": 50.0}}

S'abonner via JSONRPC.SetNotificationStatus avec le namespace NymeaEnergy (le même que LoadConfigChanged, ChargingSchedulesChanged, etc. — donc même session, pas de nouvelle connexion).

5. Migration côté app

  1. Au boot : NymeaEnergy.GetEnergyRatios pour l'état initial.
  2. S'abonner à NymeaEnergy.EnergyRatiosChanged pour les MAJ.
  3. Supprimer EnergyRatiosInterim.compute() et son état de baseline (_baseProduction/_baseReturn/…/_baseDay) : la baseline/reseed/minuit-local vivent désormais côté plugin.
  4. Mapper champ-absent → null (n/a). Conserver EnergyRatios{autoconsommation, autonomie} côté app ; seul le producteur change.

Si nymea expose un jour selfConsumptionRate/autonomyRate directement sur PowerBalance (feature request upstream ouverte séparément), la migration app sera triviale : lire les champs PowerBalance au lieu d'appeler GetEnergyRatios. Jamais bloqué, jamais de fork du cœur à porter.

6. Frontière GPL

Pur mesure (Δ de compteurs cumulés). Aucune logique d'optimisation / prévision / pondération — celle-ci reste dans l'optimiseur propriétaire. Implémenté dans energyplugin/etm/ratios/energyratioscalculator.{h,cpp} (GPL-3.0-or-later).