docs: document internal data flow and EnergyManagerConfiguration in INTERFACE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2931d295bc
commit
b8e882616b
121
INTERFACE.md
121
INTERFACE.md
@ -495,11 +495,129 @@ Le plugin détecte les appareils **par interface**, jamais par ThingClassId.
|
|||||||
| Interface | États lus | Actions envoyées |
|
| Interface | États lus | Actions envoyées |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `evcharger` | `chargingEnabled`, `maxChargingCurrent`, `pluggedIn`, `charging`, `currentPhaseA/B/C`, `currentPowerPhaseA/B/C` | `setChargingEnabled`, `setMaxChargingCurrent` |
|
| `evcharger` | `chargingEnabled`, `maxChargingCurrent`, `pluggedIn`, `charging`, `currentPhaseA/B/C`, `currentPowerPhaseA/B/C` | `setChargingEnabled`, `setMaxChargingCurrent` |
|
||||||
| `electricvehicle` | `batteryLevel`, `maxChargingCurrent`, `capacity` | — |
|
| `electricvehicle` | `batteryLevel`, `maxChargingCurrent`, `capacity`, `minChargingCurrent`, `phaseCount` | — |
|
||||||
| `rootmeter` / `energymeter` | `currentPowerPhaseA/B/C`, `currentPhaseA/B/C` | — |
|
| `rootmeter` / `energymeter` | `currentPowerPhaseA/B/C`, `currentPhaseA/B/C` | — |
|
||||||
| `energystorage` | `currentPower`, `batteryLevel` | — |
|
| `energystorage` | `currentPower`, `batteryLevel` | — |
|
||||||
|
|
||||||
**Déclencheur du cycle :** signal `PowerBalanceEntryAdded` de `nymea-experience-plugin-energy` (~1 min).
|
**Déclencheur du cycle :** signal `PowerBalanceEntryAdded` de `nymea-experience-plugin-energy` (~1 min).
|
||||||
|
**Déclencheur overload :** signal `EnergyManager::powerBalanceChanged` (temps réel, découplé du cycle).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## EnergyManagerConfiguration
|
||||||
|
|
||||||
|
Paramètres de tuning chargés **une seule fois au démarrage** depuis un fichier JSON.
|
||||||
|
Pas de setters — un changement nécessite un redémarrage du daemon nymea.
|
||||||
|
|
||||||
|
**Chemin (ordre de priorité) :**
|
||||||
|
1. `$NYMEA_ENERGY_MANAGER_CONFIG` (variable d'environnement)
|
||||||
|
2. `/var/lib/nymea/energy-manager-configuration.json`
|
||||||
|
3. Valeurs par défaut si aucun fichier trouvé
|
||||||
|
|
||||||
|
**Format JSON :**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"chargingEnabledLockDuration": 300,
|
||||||
|
"chargingCurrentLockDuration": 10,
|
||||||
|
"minimumScheduleDuration": 15,
|
||||||
|
"spotMarketChargePredictableEnergyPercentage": 0.5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Paramètre | Défaut | Unité | Rôle |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `chargingEnabledLockDuration` | 300 | secondes | Anti-flapping : durée de verrouillage après changement ON/OFF |
|
||||||
|
| `chargingCurrentLockDuration` | 10 | secondes | Anti-flapping : durée de verrouillage après changement de courant |
|
||||||
|
| `minimumScheduleDuration` | 15 | minutes | Durée minimale d'un créneau spot market planifié |
|
||||||
|
| `spotMarketChargePredictableEnergyPercentage` | 0.5 | ratio [0–1] | Fraction de l'énergie spot considérée "prédictible" dans le planning |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flux interne — `SmartChargingManager`
|
||||||
|
|
||||||
|
### Entrées de données
|
||||||
|
|
||||||
|
```
|
||||||
|
EnergyManager::logs()::powerBalanceEntryAdded (SampleRate1Min)
|
||||||
|
└─→ update(now) ← cycle principal ~1/min
|
||||||
|
|
||||||
|
EnergyManager::powerBalanceChanged ← temps réel
|
||||||
|
└─→ verifyOverloadProtection(now) ← safety loop immédiate, découplée du cycle
|
||||||
|
|
||||||
|
RootMeter (wraps Thing interface=rootmeter/energymeter)
|
||||||
|
├── currentPower() ← total W (négatif = surplus / export)
|
||||||
|
├── currentPowerPhaseA/B/C() ← W par phase
|
||||||
|
└── currentPhaseA/B/C() ← A par phase
|
||||||
|
|
||||||
|
ThingManager → interface "energystorage"
|
||||||
|
├── batteryLevel ← % (moyenne de tous les stockages)
|
||||||
|
└── currentPower ← W total (+ = charge, − = décharge)
|
||||||
|
|
||||||
|
EvCharger → interface "evcharger" + "electricvehicle"
|
||||||
|
├── currentPower(), maxChargingCurrent(), phaseCount()
|
||||||
|
├── meteredPhases() ← phases réelles via currentPhaseA/B/C live
|
||||||
|
└── car: batteryLevel, capacity, minChargingCurrent, phaseCount
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pipeline `update()` — exécuté à chaque cycle
|
||||||
|
|
||||||
|
```
|
||||||
|
update(currentDateTime)
|
||||||
|
│
|
||||||
|
├─ 1. updateManualSoCsWithoutMeter()
|
||||||
|
│ Estime le SoC à partir de l'énergie intégrée si pas de compteur sur le VE.
|
||||||
|
│
|
||||||
|
├─ 2. prepareInformation()
|
||||||
|
│ - Filtre les EV chargers actifs (plugged, car assignée, mode ≠ Normal)
|
||||||
|
│ - Calcule par charger : phases effectives, SoC, temps restant, phaseLimitPower
|
||||||
|
│ - Reset m_chargingActions à OFF/minCurrent pour les 3 issuers
|
||||||
|
│
|
||||||
|
├─ 3. verifyOverloadProtection()
|
||||||
|
│ - Lit rootMeter->currentPowerPhaseA/B/C()
|
||||||
|
│ - Si une phase dépasse phasePowerLimit × 230 W → throttle immédiat (issuer=OverloadProtection)
|
||||||
|
│
|
||||||
|
├─ 4. verifyOverloadProtectionRecovery()
|
||||||
|
│ - Ré-active les chargers throttlés si la marge est suffisante
|
||||||
|
│
|
||||||
|
├─ 5. planSpotMarketCharging()
|
||||||
|
│ - SpotMarketManager::scheduleChargingTime() → TimeFrames (créneaux bon marché)
|
||||||
|
│ - Remplit m_chargingSchedules + m_chargingActions[SpotMarket] = ON
|
||||||
|
│
|
||||||
|
├─ 6. planSurplusCharging()
|
||||||
|
│ - currentLoad = rootMeter->currentPower()
|
||||||
|
│ + correction batteries (fromBatteries)
|
||||||
|
│ + puissance ajoutée dans ce cycle (addedPower)
|
||||||
|
│ - allowanceInAmpere = −currentLoad / 230
|
||||||
|
│ - Si allowance ≥ minCurrent × acquisitionTolerance → chargingActions[Surplus] = ON
|
||||||
|
│
|
||||||
|
└─ 7. adjustEvChargers()
|
||||||
|
Applique la décision finale par priorité décroissante :
|
||||||
|
1. TimeRequirement → ON au max courant disponible (deadline imminente)
|
||||||
|
2. SurplusCharging → ON au courant surplus calculé
|
||||||
|
3. SpotMarketCharging → ON au courant max dans le créneau
|
||||||
|
4. EcoWithMinCurrent fallback → ON à 6 A (EcoMinChargingCurrent)
|
||||||
|
5. Idle → OFF
|
||||||
|
│
|
||||||
|
├── executeChargingAction()
|
||||||
|
│ └─→ Thing::executeAction(setChargingEnabled, setMaxChargingCurrent)
|
||||||
|
│
|
||||||
|
├── emit chargingInfoChanged() ← met à jour ChargingState (lu par JSON-RPC)
|
||||||
|
└── emit chargingSchedulesChanged() ← planning rafraîchi (lu par JSON-RPC)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Persistance des settings utilisateur
|
||||||
|
|
||||||
|
| Données | Fichier |
|
||||||
|
|---|---|
|
||||||
|
| `phasePowerLimit`, `acquisitionTolerance`, `batteryLevelConsideration` | `EnergySettings` (QSettings INI, `energy.conf`) |
|
||||||
|
| `ChargingInfo` par charger (mode, endDateTime, repeatDays, etc.) | même `EnergySettings`, groupe `ChargingInfos/` |
|
||||||
|
| `lockOnUnplug` | `/var/lib/nymea/energy.conf` (QSettings séparé) |
|
||||||
|
| SpotMarket `enabled`, `providerId` | `EnergySettings` (géré par `SpotMarketManager`) |
|
||||||
|
|
||||||
|
### Point d'injection pour `powersync-optimizer`
|
||||||
|
|
||||||
|
Le point naturel est **entre `prepareInformation()` et `adjustEvChargers()`**.
|
||||||
|
L'optimizer reçoit les données de contexte (`SurplusData`) et retourne une `ChargingAction` qui remplace ou complète les actions calculées localement. Voir `PowerSyncClient::requestOptimization()` dans `etm/`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -510,4 +628,5 @@ Le plugin détecte les appareils **par interface**, jamais par ThingClassId.
|
|||||||
- `phasePowerLimit` est en **Ampères** (par phase), pas en Watts.
|
- `phasePowerLimit` est en **Ampères** (par phase), pas en Watts.
|
||||||
- `weighting` des `ScoreEntry` : 1.0 = créneau le moins cher, 0.0 = le plus cher.
|
- `weighting` des `ScoreEntry` : 1.0 = créneau le moins cher, 0.0 = le plus cher.
|
||||||
- `SetChargingInfo` est partiel : seuls les champs présents sont appliqués, sauf `evChargerId` qui est toujours requis.
|
- `SetChargingInfo` est partiel : seuls les champs présents sont appliqués, sauf `evChargerId` qui est toujours requis.
|
||||||
|
- `currentPower` du root meter est **négatif** quand il y a surplus solaire (export réseau).
|
||||||
- Le plugin fonctionne sans `powersync-optimizer` (mode Community, dégradé proprement).
|
- Le plugin fonctionne sans `powersync-optimizer` (mode Community, dégradé proprement).
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user