diff --git a/Ladestation/README.md b/Ladestation/README.md index 2043e84..5262b24 100644 --- a/Ladestation/README.md +++ b/Ladestation/README.md @@ -67,10 +67,14 @@ Iststrom mal `1.11`, wird der erreichbare Maximalstrom aus `Iststrom * 1.11` gelernt und `PowerSteps` noch im selben Zyklus neu aufgebaut. Der Lernwert bleibt bis zum Ausstecken, einem neuen Fahrzeug oder einem Phasenwechsel aktiv. -Fällt der berechnete Wert bei Typ 1 bis 4 unter 6 A, wird -`Auto fertig geladen` gesetzt und nur `[0]` angeboten. Easee verwendet dafür -ausschließlich Observation 109. Eine Sollvorgabe von 0 W setzt die Zähler -zurück und kann weder eine Strombegrenzung noch ein Ladeende auslösen. +Fällt der berechnete Wert unter 6 A, wird `Auto fertig geladen` gesetzt und +nur `[0]` angeboten. Bei Easee gilt das nur bei verbundenem Gateway und +Fahrzeugstatus 4 oder 6: Das Modul muss dabei trotz positiver Sollvorgabe +90 Sekunden lang einen Iststrom messen, der mit Faktor `1.11` unter 6 A +bleibt. Status 4 oder 6 allein wird nicht als Ladeende gewertet. Eine +Sollvorgabe von 0 W setzt die Zähler zurück und kann weder eine +Strombegrenzung noch ein Ladeende auslösen. Das bestätigte Ladeende bleibt bis +zum Ausstecken gespeichert. Beim Update bestehender Instanzen wird die frühere Eigenschaft `Token_Easee` aus Kompatibilitätsgründen intern beibehalten, aber nicht mehr verwendet oder diff --git a/Ladestation/module.php b/Ladestation/module.php index 95bd86b..96d7217 100644 --- a/Ladestation/module.php +++ b/Ladestation/module.php @@ -531,10 +531,18 @@ class Ladestation extends IPSModule return in_array($stationType, [1, 2, 3, 4, 5, 6], true); } - private function HasReliableFullStatus(int $stationType): bool + private function CanUseAdaptiveFullDetection(int $stationType): bool { - // Easee meldet das Ladeende eindeutig über Observation 109. - return in_array($stationType, [5, 6], true); + if (!$this->UsesEaseeGateway($stationType)) { + return true; + } + + // Easee-Status 4 bedeutet "pausiert/gestoppt", Status 6 bedeutet, + // dass die Station auf eine Stromaufnahme des Fahrzeugs wartet. Beide + // sind nur zusammen mit positivem Soll und einer 90 Sekunden lang + // bestätigten, zu geringen Stromaufnahme ein Ladeende-Kandidat. + return $this->GetValue("Easee_Gateway_Connected") && + in_array((int)$this->GetValue("Fahrzeugstatus"), [4, 6], true); } private function SetAdaptiveReferenceMaxCurrent(float $apiMaxCurrent): float @@ -669,6 +677,13 @@ class Ladestation extends IPSModule $actualPower = abs( (float)$this->GetValue("Ladeleistung_Effektiv") ); + // Ohne messbaren Strom ist keine Phasenerkennung möglich. Vor allem + // darf eine Ladepause die zuletzt bekannte Phase nicht auf 1-phasig + // umschalten. Die Ladeende-Erkennung läuft separat weiter. + if ($actualPower < 100.0) { + return false; + } + $onePhaseExpectedPower = $requestedCurrent * 230.0; $threePhaseExpectedPower = $requestedCurrent * 1.71 * 400.0; $isOnePhase = $actualPower < @@ -687,7 +702,7 @@ class Ladestation extends IPSModule ($actualPower / $phaseDivisor); if ( $detectedCurrent < 6.0 && - !$this->HasReliableFullStatus($stationType) + $this->CanUseAdaptiveFullDetection($stationType) ) { $this->SetBuffer( "AdaptiveLearnedMaxCurrent", @@ -710,14 +725,18 @@ class Ladestation extends IPSModule 0.0, (float)$this->GetValue("Aktuelle_Leistung") ); + $actualPower = abs( + (float)$this->GetValue("Ladeleistung_Effektiv") + ); + $hasKnownPhase = $stationType === 4 || + $this->GetBuffer("AdaptivePhaseKnown") === "1"; $canEvaluate = $this->UsesAdaptiveCurrentDetection($stationType) && $ladebereit && $this->GetValue("Car_detected") && !$this->GetValue("Car_is_full") && $this->GetValue("Idle") && - ($stationType === 4 || - $this->GetBuffer("AdaptivePhaseKnown") === "1") && + ($hasKnownPhase || $actualPower < 100.0) && $requestedPower > 0 && $this->IsAdaptiveChargingEnabled($stationType) && $this->HasFreshAdaptiveMeasurement($stationType); @@ -729,9 +748,7 @@ class Ladestation extends IPSModule $phaseDivisor = $this->GetValue("Is_1_ph") ? 230.0 : 1.71 * 400.0; $requestedCurrent = $requestedPower / $phaseDivisor; - $actualCurrent = abs( - (float)$this->GetValue("Ladeleistung_Effektiv") - ) / $phaseDivisor; + $actualCurrent = $actualPower / $phaseDivisor; if ( $requestedCurrent <= @@ -762,10 +779,7 @@ class Ladestation extends IPSModule ); if ($learnedMaxCurrent < 6.0) { - // Bei Easee hat der eindeutige API-Ladeendestatus Vorrang. Ein - // Kommunikations- oder Freigabefehler darf dort kein Ladeende - // simulieren. - if ($this->HasReliableFullStatus($stationType)) { + if (!$this->CanUseAdaptiveFullDetection($stationType)) { return false; } @@ -905,7 +919,7 @@ class Ladestation extends IPSModule return $this->ApplyDirectStationDetection( $carDetected, - $chargerOpMode === 4, + false, $isOnePhase, $maxCurrent ); @@ -1062,14 +1076,10 @@ class Ladestation extends IPSModule } $this->SetValue("Car_detected", true); - if ($this->HasReliableFullStatus($this->ReadPropertyInteger("Ladestation"))) { - $this->SetValue("Car_is_full", $carIsFull); - } else { - $this->SetValue( - "Car_is_full", - $carIsFull || ($wasDetected && !$phaseChanged && $wasFull) - ); - } + $this->SetValue( + "Car_is_full", + $carIsFull || ($wasDetected && !$phaseChanged && $wasFull) + ); if (!$wasDetected || $phaseChanged) { if ($this->GetValue("Is_1_ph")) { @@ -1633,7 +1643,7 @@ class Ladestation extends IPSModule if ( $maxCurrent < 6.0 && - !$this->HasReliableFullStatus( + $this->CanUseAdaptiveFullDetection( $this->ReadPropertyInteger("Ladestation") ) ) { diff --git a/Ladestation_v2/README.md b/Ladestation_v2/README.md index 6a7b3f4..827b31b 100644 --- a/Ladestation_v2/README.md +++ b/Ladestation_v2/README.md @@ -67,10 +67,14 @@ Iststrom mal `1.11`, wird der erreichbare Maximalstrom aus `Iststrom * 1.11` gelernt und `PowerSteps` noch im selben Zyklus neu aufgebaut. Der Lernwert bleibt bis zum Ausstecken, einem neuen Fahrzeug oder einem Phasenwechsel aktiv. -Fällt der berechnete Wert bei Typ 1 bis 4 unter 6 A, wird -`Auto fertig geladen` gesetzt und nur `[0]` angeboten. Easee verwendet dafür -ausschließlich Observation 109. Eine Sollvorgabe von 0 W setzt die Zähler -zurück und kann weder eine Strombegrenzung noch ein Ladeende auslösen. +Fällt der berechnete Wert unter 6 A, wird `Auto fertig geladen` gesetzt und +nur `[0]` angeboten. Bei Easee gilt das nur bei verbundenem Gateway und +Fahrzeugstatus 4 oder 6: Das Modul muss dabei trotz positiver Sollvorgabe +90 Sekunden lang einen Iststrom messen, der mit Faktor `1.11` unter 6 A +bleibt. Status 4 oder 6 allein wird nicht als Ladeende gewertet. Eine +Sollvorgabe von 0 W setzt die Zähler zurück und kann weder eine +Strombegrenzung noch ein Ladeende auslösen. Das bestätigte Ladeende bleibt bis +zum Ausstecken gespeichert. ## Einrichtung diff --git a/Ladestation_v2/module.php b/Ladestation_v2/module.php index b076101..41137d2 100644 --- a/Ladestation_v2/module.php +++ b/Ladestation_v2/module.php @@ -551,10 +551,18 @@ class Ladestation_v2 extends IPSModule return in_array($stationType, [1, 2, 3, 4, 5, 6], true); } - private function HasReliableFullStatus(int $stationType): bool + private function CanUseAdaptiveFullDetection(int $stationType): bool { - // Easee meldet das Ladeende eindeutig über Observation 109. - return in_array($stationType, [5, 6], true); + if (!$this->UsesEaseeGateway($stationType)) { + return true; + } + + // Easee-Status 4 bedeutet "pausiert/gestoppt", Status 6 bedeutet, + // dass die Station auf eine Stromaufnahme des Fahrzeugs wartet. Beide + // sind nur zusammen mit positivem Soll und einer 90 Sekunden lang + // bestätigten, zu geringen Stromaufnahme ein Ladeende-Kandidat. + return $this->GetValue("Easee_Gateway_Connected") && + in_array((int)$this->GetValue("Fahrzeugstatus"), [4, 6], true); } private function SetAdaptiveReferenceMaxCurrent(float $apiMaxCurrent): float @@ -689,6 +697,13 @@ class Ladestation_v2 extends IPSModule $actualPower = abs( (float)$this->GetValue("Ladeleistung_Effektiv") ); + // Ohne messbaren Strom ist keine Phasenerkennung möglich. Vor allem + // darf eine Ladepause die zuletzt bekannte Phase nicht auf 1-phasig + // umschalten. Die Ladeende-Erkennung läuft separat weiter. + if ($actualPower < 100.0) { + return false; + } + $onePhaseExpectedPower = $requestedCurrent * 230.0; $threePhaseExpectedPower = $requestedCurrent * 1.71 * 400.0; $isOnePhase = $actualPower < @@ -707,7 +722,7 @@ class Ladestation_v2 extends IPSModule ($actualPower / $phaseDivisor); if ( $detectedCurrent < 6.0 && - !$this->HasReliableFullStatus($stationType) + $this->CanUseAdaptiveFullDetection($stationType) ) { $this->SetBuffer( "AdaptiveLearnedMaxCurrent", @@ -730,14 +745,18 @@ class Ladestation_v2 extends IPSModule 0.0, (float)$this->GetValue("Aktuelle_Leistung") ); + $actualPower = abs( + (float)$this->GetValue("Ladeleistung_Effektiv") + ); + $hasKnownPhase = $stationType === 4 || + $this->GetBuffer("AdaptivePhaseKnown") === "1"; $canEvaluate = $this->UsesAdaptiveCurrentDetection($stationType) && $ladebereit && $this->GetValue("Car_detected") && !$this->GetValue("Car_is_full") && $this->GetValue("Idle") && - ($stationType === 4 || - $this->GetBuffer("AdaptivePhaseKnown") === "1") && + ($hasKnownPhase || $actualPower < 100.0) && $requestedPower > 0 && $this->IsAdaptiveChargingEnabled($stationType) && $this->HasFreshAdaptiveMeasurement($stationType); @@ -749,9 +768,7 @@ class Ladestation_v2 extends IPSModule $phaseDivisor = $this->GetValue("Is_1_ph") ? 230.0 : 1.71 * 400.0; $requestedCurrent = $requestedPower / $phaseDivisor; - $actualCurrent = abs( - (float)$this->GetValue("Ladeleistung_Effektiv") - ) / $phaseDivisor; + $actualCurrent = $actualPower / $phaseDivisor; if ( $requestedCurrent <= @@ -782,10 +799,7 @@ class Ladestation_v2 extends IPSModule ); if ($learnedMaxCurrent < 6.0) { - // Bei Easee hat der eindeutige API-Ladeendestatus Vorrang. Ein - // Kommunikations- oder Freigabefehler darf dort kein Ladeende - // simulieren. - if ($this->HasReliableFullStatus($stationType)) { + if (!$this->CanUseAdaptiveFullDetection($stationType)) { return false; } @@ -925,7 +939,7 @@ class Ladestation_v2 extends IPSModule return $this->ApplyDirectStationDetection( $carDetected, - $chargerOpMode === 4, + false, $isOnePhase, $maxCurrent ); @@ -1082,14 +1096,10 @@ class Ladestation_v2 extends IPSModule } $this->SetValue("Car_detected", true); - if ($this->HasReliableFullStatus($this->ReadPropertyInteger("Ladestation"))) { - $this->SetValue("Car_is_full", $carIsFull); - } else { - $this->SetValue( - "Car_is_full", - $carIsFull || ($wasDetected && !$phaseChanged && $wasFull) - ); - } + $this->SetValue( + "Car_is_full", + $carIsFull || ($wasDetected && !$phaseChanged && $wasFull) + ); if (!$wasDetected || $phaseChanged) { if ($this->GetValue("Is_1_ph")) { @@ -1646,7 +1656,7 @@ class Ladestation_v2 extends IPSModule if ( $maxCurrent < 6.0 && - !$this->HasReliableFullStatus( + $this->CanUseAdaptiveFullDetection( $this->ReadPropertyInteger("Ladestation") ) ) {