no message

This commit is contained in:
belevo\mh
2026-05-12 13:30:31 +02:00
parent b013e90616
commit 6f335c1fc8
2 changed files with 175 additions and 41 deletions
+64 -40
View File
@@ -1,9 +1,9 @@
{
"elements": [
{
"type": "Label",
"caption": "Aufgepasst: Bei Goodwe nur Ladenvariabel auswählen und entladen Dummy Variabel.\nGoodwe braucht nur eine Leistungssoll Variabel. Entlade NICHT auf gleiche Variabel setzen wie Laden\nGoodwe: Laden=11, Entladen=12,\nSolaredge: Laden=3, Entladen=4\nDefault: Laden=1, Entladen=2"
},
{
"type": "Label",
"caption": "Aufgepasst: Bei Goodwe nur Ladenvariabel auswählen und entladen Dummy Variabel.\nGoodwe braucht nur eine Leistungssoll Variabel. Entlade NICHT auf gleiche Variabel setzen wie Laden\nGoodwe: Laden=11, Entladen=12,\nSolaredge: Laden=3, Entladen=4\nDefault: Laden=1, Entladen=2"
},
{
"type": "List",
"name": "Batteries",
@@ -95,17 +95,17 @@
}
},
{
"caption": "Min. SoC",
"name": "minPhysicalSocPct",
"width": "100px",
"suffix": " %",
"add": 5,
"edit": {
"caption": "Min. SoC",
"name": "minPhysicalSocPct",
"width": "100px",
"suffix": " %",
"add": 5,
"edit": {
"type": "NumberSpinner",
"minimum": 0,
"maximum": 30,
"digits": 1
}
}
}
],
"values": []
@@ -131,13 +131,13 @@
"digits": 0
},
{
"type": "NumberSpinner",
"name": "ReserveHours",
"caption": "SDL Reservezeit",
"suffix": " h",
"minimum": 0,
"maximum": 24,
"digits": 2
"type": "NumberSpinner",
"name": "ReserveHours",
"caption": "SDL Reservezeit",
"suffix": " h",
"minimum": 0,
"maximum": 24,
"digits": 2
},
{
"type": "NumberSpinner",
@@ -167,42 +167,66 @@
"type": "NumberSpinner",
"name": "UpdateInterval",
"caption": "Update Intervall",
"suffix": "S",
"suffix": " s",
"minimum": 1,
"digits": 0
},
{
"type": "Label",
"caption": "EV-SoC Nachberechnung:\n\nAlle X Stunden wird geprüft, ob der virtuelle SDL-SoC bei 50% liegt.\nWenn ja, wird der EV-SoC anhand der physischen Batterie-SoCs neu berechnet.\nDie EV-Toleranz verhindert kleine unnötige Korrekturen.\n\n0 Stunden = automatische EV-Neuberechnung deaktiviert."
},
{
"type": "NumberSpinner",
"name": "EV_Recalc_IntervalHours",
"caption": "EV SoC neu berechnen alle",
"suffix": " h",
"minimum": 0,
"maximum": 168,
"digits": 2
},
{
"type": "NumberSpinner",
"name": "EV_Recalc_TolerancePct",
"caption": "EV Recalc Toleranz",
"suffix": " %",
"minimum": 0,
"maximum": 100,
"digits": 2
},
{
"type": "CheckBox",
"name": "FilterAktiv",
"caption": "Filter für aktuelle EV/SDL Leistung aktiv. Dient für die Visualisierung, um Leistungssprünge zu vermeiden."
},
{
"type": "Label",
"caption": "Filter für aktuelle Leistung:\n\nToleranz (%): Wie stark der Istwert vom Soll abweichen darf\nRampe (W/s): Wie schnell die Leistung verändert wird (Trägheit)\nTreffer: Wie oft ein Wert passen muss, bevor er übernommen wird\n\n→ Höhere Rampe = schneller, aber sprunghafter\n→ Niedrigere Rampe = ruhiger, aber träger"
"type": "Label",
"caption": "Filter für aktuelle Leistung:\n\nToleranz (%): Wie stark der Istwert vom Soll abweichen darf\nRampe (W/s): Wie schnell die Leistung verändert wird (Trägheit)\nTreffer: Wie oft ein Wert passen muss, bevor er übernommen wird\n\n→ Höhere Rampe = schneller, aber sprunghafter\n→ Niedrigere Rampe = ruhiger, aber träger"
},
{
"type": "NumberSpinner",
"name": "FilterTolerancePct",
"caption": "Filter Toleranz (%)",
"minimum": 0,
"maximum": 100,
"digits": 1
"type": "NumberSpinner",
"name": "FilterTolerancePct",
"caption": "Filter Toleranz",
"suffix": " %",
"minimum": 0,
"maximum": 100,
"digits": 1
},
{
"type": "NumberSpinner",
"name": "FilterRampWPerSec",
"caption": "Filter Rampe (W/s)",
"minimum": 100,
"maximum": 20000,
"digits": 0
"type": "NumberSpinner",
"name": "FilterRampWPerSec",
"caption": "Filter Rampe",
"suffix": " W/s",
"minimum": 100,
"maximum": 20000,
"digits": 0
},
{
"type": "NumberSpinner",
"name": "FilterHits",
"caption": "Filter Treffer bis Übernahme",
"minimum": 1,
"maximum": 10
"type": "NumberSpinner",
"name": "FilterHits",
"caption": "Filter Treffer bis Übernahme",
"minimum": 1,
"maximum": 10,
"digits": 0
}
]
}
@@ -219,4 +243,4 @@
"onClick": "GEF_Update($id);"
}
]
}
}
+111 -1
View File
@@ -18,6 +18,8 @@ class Bat_EV_SDL_V4 extends IPSModule
$this->RegisterPropertyFloat("FilterTolerancePct", 15.0); // %
$this->RegisterPropertyFloat("FilterRampWPerSec", 2000.0); // W/s
$this->RegisterPropertyInteger("FilterHits", 1);
$this->RegisterPropertyFloat("EV_Recalc_IntervalHours", 6.0);
$this->RegisterPropertyFloat("EV_Recalc_TolerancePct", 2.0);
// Status
$this->RegisterVariableBoolean("State", "Aktiv", "~Switch", 1);
@@ -424,7 +426,8 @@ class Bat_EV_SDL_V4 extends IPSModule
$eSDL = ($sdlTotal > 0.0) ? $sdlTotal * $lastSdlPct / 100.0 : 0.0;
$eEV = ($evTotal > 0.0) ? $evTotal * $lastEvPct / 100.0 : 0.0;
$this->MaybeRecalculateEVFromPhysical($plan, $eSDL, $eEV, $sdlTotal, $evTotal);
$this->SetBuffer("Int_E_SDL_kWh", (string)$eSDL);
$this->SetBuffer("Int_E_EV_kWh", (string)$eEV);
$this->SetBuffer("Int_LastTs", (string)$now);
@@ -916,6 +919,113 @@ class Bat_EV_SDL_V4 extends IPSModule
}
private function MaybeRecalculateEVFromPhysical(array $plan, float $eSDL, float &$eEV, float $sdlTotal, float $evTotal): void
{
if ($evTotal <= 0.0 || $sdlTotal <= 0.0) {
return;
}
$intervalHours = max(0.0, (float)$this->ReadPropertyFloat("EV_Recalc_IntervalHours"));
if ($intervalHours <= 0.0) {
return;
}
$now = microtime(true);
$lastCheckTs = (float)$this->GetBufferSafe("EV_Recalc_LastCheckTs");
// Nur alle X Stunden prüfen
if (
$lastCheckTs > 0.0 &&
($now - $lastCheckTs) < ($intervalHours * 3600.0)
) {
return;
}
$this->SetBuffer("EV_Recalc_LastCheckTs", (string)$now);
// SDL muss bei 50% sein
$sdlPct = ($eSDL / $sdlTotal) * 100.0;
if (abs($sdlPct - 50.0) > 0.001) {
$this->SendDebug(
"EV_Recalc",
"Skip: SDL nicht bei 50%, aktuell=" . round($sdlPct, 3) . "%",
0
);
return;
}
$newEVkWh = 0.0;
foreach (($plan["bats"] ?? []) as $bat) {
$capKWh = (float)($bat["capKWh"] ?? 0.0);
$socVarId = (int)($bat["socVarId"] ?? 0);
$realSocPct = $this->ReadSocPercent($socVarId);
$realKWh = $capKWh * $realSocPct / 100.0;
$underKWh = (float)($bat["underKWh"] ?? 0.0);
// Maximales EV Fenster dieser Batterie
$evBatTotal = (float)($bat["EV_kWh_total"] ?? 0.0);
// EV Anteil relativ zur unteren Grenze
$evPart = $realKWh - $underKWh;
// Begrenzung:
// unter underKWh => 0
// über upKWh => max EV Fenster
$evPart = max(0.0, min($evBatTotal, $evPart));
$newEVkWh += $evPart;
}
// Global begrenzen
$newEVkWh = max(0.0, min($evTotal, $newEVkWh));
$newEVpct = ($evTotal > 0.0)
? ($newEVkWh / $evTotal * 100.0)
: 0.0;
$oldEVpct = ($evTotal > 0.0)
? ($eEV / $evTotal * 100.0)
: 0.0;
$tolPct = max(0.0, (float)$this->ReadPropertyFloat("EV_Recalc_TolerancePct"));
// Nur übernehmen wenn Differenz gross genug
if (abs($newEVpct - $oldEVpct) <= $tolPct) {
$this->SendDebug(
"EV_Recalc",
"Skip: Differenz innerhalb Toleranz. Alt=" .
round($oldEVpct, 3) .
"% Neu=" .
round($newEVpct, 3) .
"%",
0
);
return;
}
$eEV = $newEVkWh;
$this->SendDebug(
"EV_Recalc",
"EV neu berechnet. Alt=" .
round($oldEVpct, 3) .
"% Neu=" .
round($newEVpct, 3) .
"%",
0
);
}
private function RefreshDynamicPlanValues(array $plan): array
{
foreach ($plan["bats"] as &$bat) {