diff --git a/Ladestation/module.php b/Ladestation/module.php index be9c878..b711506 100644 --- a/Ladestation/module.php +++ b/Ladestation/module.php @@ -278,7 +278,7 @@ class Ladestation extends IPSModule // zurückgesetzt. if ( $stationType === 3 && - $this->GetBuffer("SmartMeFullDetectionVersion") !== "3" + $this->GetBuffer("SmartMeFullDetectionVersion") !== "4" ) { $this->SetValue("Car_is_full", false); $this->SetValue("Pending_Counter", 0); @@ -287,7 +287,13 @@ class Ladestation extends IPSModule (float)$this->ReadPropertyInteger("Max_Current_abs") ); $this->SetBuffer("SmartMeChargingEnabled", "0"); - $this->SetBuffer("SmartMeFullDetectionVersion", "3"); + $this->SetBuffer("SmartMeLearnedMaxCurrent", "0"); + $this->SetBuffer( + "SmartMeStationMaxCurrent", + (string)$this->ReadPropertyInteger("Max_Current_abs") + ); + $this->SetBuffer("SmartMeMinStationCurrent", "6"); + $this->SetBuffer("SmartMeFullDetectionVersion", "4"); } $usesEaseeGateway = $this->UsesEaseeGateway($stationType); @@ -987,6 +993,10 @@ class Ladestation extends IPSModule $minStationCurrent = is_numeric($minStationCurrent) ? max(1.0, (float)$minStationCurrent) : 6.0; + $this->SetBuffer( + "SmartMeMinStationCurrent", + (string)$minStationCurrent + ); $maxDynamicCurrent = $chargingData["MaxDynamicCurrent"] ?? $chargingData["maxDynamicCurrent"] ?? null; @@ -1037,24 +1047,44 @@ class Ladestation extends IPSModule } } + $this->SetBuffer("SmartMeStationMaxCurrent", (string)$maxCurrent); + $carDetected = in_array($stationState, [2, 3, 4, 6], true); + $wasDetected = $this->GetValue("Car_detected"); + $wasOnePhase = $this->GetValue("Is_1_ph"); + $phaseChanged = is_bool($isOnePhase) && + $wasDetected && + $wasOnePhase !== $isOnePhase; + + // Beim Abstecken, bei einem neuen Fahrzeug oder bei einem echten + // Phasenwechsel wird die zuvor gelernte Fahrzeug-/Kabelgrenze verworfen. + if (!$carDetected || !$wasDetected || $phaseChanged) { + $this->SetBuffer("SmartMeLearnedMaxCurrent", "0"); + } + + $learnedMaxCurrent = (float)$this->GetBuffer( + "SmartMeLearnedMaxCurrent" + ); + $effectiveMaxCurrent = $maxCurrent; + if ($learnedMaxCurrent >= $minStationCurrent) { + $effectiveMaxCurrent = min($maxCurrent, $learnedMaxCurrent); + } + $wasFull = $this->GetValue("Car_is_full"); $carIsCharging = is_numeric($chargingPowerWatts) && abs((float)$chargingPowerWatts) >= 100; $carIsFull = $carDetected && $wasFull && !$carIsCharging; - // Pico liefert die verfügbare Stations-/Gruppenobergrenze direkt über - // die API. Ein zuvor über den Soll-/Istvergleich gelernter Fahrzeugwert - // darf deshalb den möglichen Ladestrom nicht dauerhaft begrenzen. - if (!$carIsFull) { - $this->SetValue("Max_Current", $maxCurrent); - } + // Die API-Grenze bleibt die absolute Obergrenze. Eine bestätigte + // Fahrzeug-/Kabelgrenze darf darunter liegen und bleibt erhalten, + // solange dasselbe Fahrzeug mit derselben Phasenart angeschlossen ist. + $this->SetValue("Max_Current", $effectiveMaxCurrent); return $this->ApplyDirectStationDetection( $carDetected, $carIsFull, $isOnePhase, - $maxCurrent, + $effectiveMaxCurrent, false ); } @@ -1572,6 +1602,14 @@ class Ladestation extends IPSModule ? max($availableSteps) : 0; $maxPowerStep = count($powerSteps) > 0 ? max($powerSteps) : 0; + $picoAtMaximumRequest = $stationType !== 3 || + ( + $maxAvailablePower > 0 && + $requestedPower >= (0.95 * $maxAvailablePower) + ); + $picoCanEvaluate = $stationType !== 3 || + $actualPower < 100 || + $picoAtMaximumRequest; $goEEnabled = !in_array($stationType, [1, 2], true) || $this->GetBuffer("GoEChargingEnabledFromStatus") === "1"; $smartMeEnabled = $stationType !== 3 || @@ -1582,7 +1620,8 @@ class Ladestation extends IPSModule $requestedPower > 0 && $maxPowerStep > 0 && $goEEnabled && - $smartMeEnabled; + $smartMeEnabled && + $picoCanEvaluate; $requestedMismatch = $requestedPower > (1.11 * $actualPower); $availableMismatch = $maxAvailablePower < (1.11 * $actualPower); @@ -1614,6 +1653,41 @@ class Ladestation extends IPSModule $this->SetValue("Car_is_full", true); $this->ResetTimer(); $this->ResetNullTimer(); + } elseif ($picoAtMaximumRequest) { + $minStationCurrent = max( + 1.0, + (float)$this->GetBuffer( + "SmartMeMinStationCurrent" + ) + ); + $phaseDivisor = $this->GetValue("Is_1_ph") + ? 230 + : 1.71 * 400; + $actualCurrent = $actualPower / $phaseDivisor; + + // Erst eine stabile Stromaufnahme mindestens + // nahe dem Stationsminimum darf als Kabel- oder + // Fahrzeuggrenze gespeichert werden. + if ($actualCurrent >= 0.9 * $minStationCurrent) { + $stationMaxCurrent = max( + $minStationCurrent, + (float)$this->GetBuffer( + "SmartMeStationMaxCurrent" + ) + ); + $learnedMaxCurrent = min( + $stationMaxCurrent, + $learnedMaxCurrent + ); + $this->SetBuffer( + "SmartMeLearnedMaxCurrent", + (string)$learnedMaxCurrent + ); + $this->SetValue( + "Max_Current", + $learnedMaxCurrent + ); + } } } else { $this->Calc_Max_Current($this->GetValue("Is_1_ph")); diff --git a/Ladestation_v2/module.php b/Ladestation_v2/module.php index 4c7e00e..0dea035 100644 --- a/Ladestation_v2/module.php +++ b/Ladestation_v2/module.php @@ -288,7 +288,7 @@ class Ladestation_v2 extends IPSModule // zurückgesetzt. if ( $stationType === 3 && - $this->GetBuffer("SmartMeFullDetectionVersion") !== "3" + $this->GetBuffer("SmartMeFullDetectionVersion") !== "4" ) { $this->SetValue("Car_is_full", false); $this->SetValue("Pending_Counter", 0); @@ -297,7 +297,13 @@ class Ladestation_v2 extends IPSModule (float)$this->ReadPropertyInteger("Max_Current_abs") ); $this->SetBuffer("SmartMeChargingEnabled", "0"); - $this->SetBuffer("SmartMeFullDetectionVersion", "3"); + $this->SetBuffer("SmartMeLearnedMaxCurrent", "0"); + $this->SetBuffer( + "SmartMeStationMaxCurrent", + (string)$this->ReadPropertyInteger("Max_Current_abs") + ); + $this->SetBuffer("SmartMeMinStationCurrent", "6"); + $this->SetBuffer("SmartMeFullDetectionVersion", "4"); } $usesEaseeGateway = $this->UsesEaseeGateway($stationType); @@ -1007,6 +1013,10 @@ class Ladestation_v2 extends IPSModule $minStationCurrent = is_numeric($minStationCurrent) ? max(1.0, (float)$minStationCurrent) : 6.0; + $this->SetBuffer( + "SmartMeMinStationCurrent", + (string)$minStationCurrent + ); $maxDynamicCurrent = $chargingData["MaxDynamicCurrent"] ?? $chargingData["maxDynamicCurrent"] ?? null; @@ -1057,24 +1067,44 @@ class Ladestation_v2 extends IPSModule } } + $this->SetBuffer("SmartMeStationMaxCurrent", (string)$maxCurrent); + $carDetected = in_array($stationState, [2, 3, 4, 6], true); + $wasDetected = $this->GetValue("Car_detected"); + $wasOnePhase = $this->GetValue("Is_1_ph"); + $phaseChanged = is_bool($isOnePhase) && + $wasDetected && + $wasOnePhase !== $isOnePhase; + + // Beim Abstecken, bei einem neuen Fahrzeug oder bei einem echten + // Phasenwechsel wird die zuvor gelernte Fahrzeug-/Kabelgrenze verworfen. + if (!$carDetected || !$wasDetected || $phaseChanged) { + $this->SetBuffer("SmartMeLearnedMaxCurrent", "0"); + } + + $learnedMaxCurrent = (float)$this->GetBuffer( + "SmartMeLearnedMaxCurrent" + ); + $effectiveMaxCurrent = $maxCurrent; + if ($learnedMaxCurrent >= $minStationCurrent) { + $effectiveMaxCurrent = min($maxCurrent, $learnedMaxCurrent); + } + $wasFull = $this->GetValue("Car_is_full"); $carIsCharging = is_numeric($chargingPowerWatts) && abs((float)$chargingPowerWatts) >= 100; $carIsFull = $carDetected && $wasFull && !$carIsCharging; - // Pico liefert die verfügbare Stations-/Gruppenobergrenze direkt über - // die API. Ein zuvor über den Soll-/Istvergleich gelernter Fahrzeugwert - // darf deshalb den möglichen Ladestrom nicht dauerhaft begrenzen. - if (!$carIsFull) { - $this->SetValue("Max_Current", $maxCurrent); - } + // Die API-Grenze bleibt die absolute Obergrenze. Eine bestätigte + // Fahrzeug-/Kabelgrenze darf darunter liegen und bleibt erhalten, + // solange dasselbe Fahrzeug mit derselben Phasenart angeschlossen ist. + $this->SetValue("Max_Current", $effectiveMaxCurrent); return $this->ApplyDirectStationDetection( $carDetected, $carIsFull, $isOnePhase, - $maxCurrent, + $effectiveMaxCurrent, false ); } @@ -1585,6 +1615,14 @@ class Ladestation_v2 extends IPSModule ? max($availableSteps) : 0; $maxPowerStep = count($powerSteps) > 0 ? max($powerSteps) : 0; + $picoAtMaximumRequest = $stationType !== 3 || + ( + $maxAvailablePower > 0 && + $requestedPower >= (0.95 * $maxAvailablePower) + ); + $picoCanEvaluate = $stationType !== 3 || + $actualPower < 100 || + $picoAtMaximumRequest; $goEEnabled = !in_array($stationType, [1, 2], true) || $this->GetBuffer("GoEChargingEnabledFromStatus") === "1"; $smartMeEnabled = $stationType !== 3 || @@ -1595,7 +1633,8 @@ class Ladestation_v2 extends IPSModule $requestedPower > 0 && $maxPowerStep > 0 && $goEEnabled && - $smartMeEnabled; + $smartMeEnabled && + $picoCanEvaluate; $requestedMismatch = $requestedPower > (1.11 * $actualPower); $availableMismatch = $maxAvailablePower < (1.11 * $actualPower); @@ -1627,6 +1666,41 @@ class Ladestation_v2 extends IPSModule $this->SetValue("Car_is_full", true); $this->ResetTimer(); $this->ResetNullTimer(); + } elseif ($picoAtMaximumRequest) { + $minStationCurrent = max( + 1.0, + (float)$this->GetBuffer( + "SmartMeMinStationCurrent" + ) + ); + $phaseDivisor = $this->GetValue("Is_1_ph") + ? 230 + : 1.71 * 400; + $actualCurrent = $actualPower / $phaseDivisor; + + // Erst eine stabile Stromaufnahme mindestens + // nahe dem Stationsminimum darf als Kabel- oder + // Fahrzeuggrenze gespeichert werden. + if ($actualCurrent >= 0.9 * $minStationCurrent) { + $stationMaxCurrent = max( + $minStationCurrent, + (float)$this->GetBuffer( + "SmartMeStationMaxCurrent" + ) + ); + $learnedMaxCurrent = min( + $stationMaxCurrent, + $learnedMaxCurrent + ); + $this->SetBuffer( + "SmartMeLearnedMaxCurrent", + (string)$learnedMaxCurrent + ); + $this->SetValue( + "Max_Current", + $learnedMaxCurrent + ); + } } } else { $this->Calc_Max_Current($this->GetValue("Is_1_ph"));