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>
4.1 KiB
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é parpowersync-energy-plugin-nymea(experience-plugin, namespace JSON-RPCNymeaEnergy) — pas surEnergy.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, jamaisNaN.
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?(Dartnull).
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
- Au boot :
NymeaEnergy.GetEnergyRatiospour l'état initial. - S'abonner à
NymeaEnergy.EnergyRatiosChangedpour les MAJ. - Supprimer
EnergyRatiosInterim.compute()et son état de baseline (_baseProduction/_baseReturn/…/_baseDay) : la baseline/reseed/minuit-local vivent désormais côté plugin. - Mapper champ-absent →
null(n/a). ConserverEnergyRatios{autoconsommation, autonomie}côté app ; seul le producteur change.
Si nymea expose un jour
selfConsumptionRate/autonomyRatedirectement surPowerBalance(feature request upstream ouverte séparément), la migration app sera triviale : lire les champsPowerBalanceau lieu d'appelerGetEnergyRatios. 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).