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 |
|
||||
|---|---|---|
|
||||
| `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` | — |
|
||||
| `energystorage` | `currentPower`, `batteryLevel` | — |
|
||||
|
||||
**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.
|
||||
- `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.
|
||||
- `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).
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user