diff --git a/Ladestation/module.php b/Ladestation/module.php index 48f0f11..34eac6d 100644 --- a/Ladestation/module.php +++ b/Ladestation/module.php @@ -271,6 +271,25 @@ class Ladestation extends IPSModule // Einen noch vorhandenen Token-Timer aus Altinstanzen sicher stilllegen. $this->SetTimerInterval("Timer_Refresh_Token", 0); $stationType = $this->ReadPropertyInteger("Ladestation"); + + // Bestehende Pico-Instanzen können aus der bisherigen Erkennung einen + // ungültigen Grenzwert von 2.5 A und dadurch "voll" gespeichert haben. + // Diese Altlast wird beim ersten Laden der korrigierten Version einmalig + // zurückgesetzt. + if ( + $stationType === 3 && + $this->GetBuffer("SmartMeFullDetectionVersion") !== "2" + ) { + $this->SetValue("Car_is_full", false); + $this->SetValue("Pending_Counter", 0); + $this->SetValue( + "Max_Current", + (float)$this->ReadPropertyInteger("Max_Current_abs") + ); + $this->SetBuffer("SmartMeChargingEnabled", "0"); + $this->SetBuffer("SmartMeFullDetectionVersion", "2"); + } + $usesEaseeGateway = $this->UsesEaseeGateway($stationType); IPS_SetHidden( $this->GetIDForIdent("Easee_Gateway_Connected"), @@ -918,6 +937,10 @@ class Ladestation extends IPSModule private function DetectSmartMeCar(): bool { + // Bei einem fehlgeschlagenen Abruf darf eine alte Freigabe keinesfalls + // zur Ladeende-Erkennung weiterverwendet werden. + $this->SetBuffer("SmartMeChargingEnabled", "0"); + $deviceID = trim($this->ReadPropertyString("ID")); if ($deviceID === "") { return $this->GetValue("Car_detected"); @@ -954,6 +977,37 @@ class Ladestation extends IPSModule ); } + // Die Pico-API unterscheidet zwischen der extern vorgegebenen + // Stromgrenze und der effektiv verfügbaren Stromgrenze. Nur wenn beide + // vorhandenen Grenzen mindestens dem Stationsminimum entsprechen, ist + // die Ladung wirklich freigegeben. Eine Regelungsunterbrechung mit 0 A + // darf nicht als abgeschlossenes Laden ausgewertet werden. + $minStationCurrent = $chargingData["MinStationCurrent"] ?? + $chargingData["minStationCurrent"] ?? 6; + $minStationCurrent = is_numeric($minStationCurrent) + ? max(1.0, (float)$minStationCurrent) + : 6.0; + + $maxDynamicCurrent = $chargingData["MaxDynamicCurrent"] ?? + $chargingData["maxDynamicCurrent"] ?? null; + $maxAllowedCurrent = $chargingData["MaxAllowedChargingCurrent"] ?? + $chargingData["maxAllowedChargingCurrent"] ?? null; + + $hasCurrentLimit = is_numeric($maxDynamicCurrent) || + is_numeric($maxAllowedCurrent); + $dynamicCurrentAllowsCharging = !is_numeric($maxDynamicCurrent) || + (float)$maxDynamicCurrent >= $minStationCurrent; + $allowedCurrentAllowsCharging = !is_numeric($maxAllowedCurrent) || + (float)$maxAllowedCurrent >= $minStationCurrent; + $chargingEnabled = $hasCurrentLimit + ? $dynamicCurrentAllowsCharging && $allowedCurrentAllowsCharging + : $this->GetValue("Aktuelle_Leistung") > 0; + + $this->SetBuffer( + "SmartMeChargingEnabled", + $chargingEnabled ? "1" : "0" + ); + $isOnePhase = null; $deviceData = $this->ReadJsonEndpoint( "https://api.smart-me.com/Devices/" . $deviceID, @@ -984,13 +1038,17 @@ class Ladestation extends IPSModule } $carDetected = in_array($stationState, [2, 3, 4, 6], true); - $carIsFull = $this->GetValue("Car_is_full"); - if (!$carIsFull) { - $learnedMaxCurrent = (float)$this->GetValue("Max_Current"); - $carIsFull = $carDetected && - $this->GetValue("Car_detected") && - $learnedMaxCurrent > 0 && - $learnedMaxCurrent < 6; + $wasFull = $this->GetValue("Car_is_full"); + $carIsCharging = is_numeric($chargingPowerWatts) && + abs((float)$chargingPowerWatts) >= 100; + $carIsFull = $carDetected && $wasFull && !$carIsCharging; + + // Ein aus einer früheren Regelungsunterbrechung gelernter Wert unter + // dem Mindestladestrom darf die Station nicht dauerhaft blockieren. + // Ein korrekt bestätigtes Ladeende wird dagegen bis zum Abstecken oder + // bis zur erneuten Leistungsaufnahme gehalten. + if (!$carIsFull && (float)$this->GetValue("Max_Current") < 6) { + $this->SetValue("Max_Current", $maxCurrent); } return $this->ApplyDirectStationDetection( @@ -1517,12 +1575,15 @@ class Ladestation extends IPSModule $maxPowerStep = count($powerSteps) > 0 ? max($powerSteps) : 0; $goEEnabled = !in_array($stationType, [1, 2], true) || $this->GetBuffer("GoEChargingEnabledFromStatus") === "1"; + $smartMeEnabled = $stationType !== 3 || + $this->GetBuffer("SmartMeChargingEnabled") === "1"; $canEvaluate = $ladebereit && $this->GetValue("Car_detected") && !$this->GetValue("Car_is_full") && $requestedPower > 0 && $maxPowerStep > 0 && - $goEEnabled; + $goEEnabled && + $smartMeEnabled; $requestedMismatch = $requestedPower > (1.11 * $actualPower); $availableMismatch = $maxAvailablePower < (1.11 * $actualPower); @@ -1537,6 +1598,21 @@ class Ladestation extends IPSModule $this->SetValue("Pending_Counter", 0); if ($requestedMismatch) { $this->Calc_Max_Current($this->GetValue("Is_1_ph")); + + // Smart-Me liefert keinen eindeutigen Status "Auto + // voll". Erst wenn während einer echten Freigabe über + // 90 Sekunden praktisch kein Strom angenommen wird, + // bestätigt der gelernte Wert unter 6 A das Ladeende. + if ( + $stationType === 3 && + $this->GetBuffer("SmartMeChargingEnabled") === "1" && + (float)$this->GetValue("Max_Current") > 0 && + (float)$this->GetValue("Max_Current") < 6 + ) { + $this->SetValue("Car_is_full", true); + $this->ResetTimer(); + $this->ResetNullTimer(); + } } } } else { diff --git a/Ladestation_v2/module.php b/Ladestation_v2/module.php index 106e4ae..0954dc2 100644 --- a/Ladestation_v2/module.php +++ b/Ladestation_v2/module.php @@ -281,6 +281,25 @@ class Ladestation_v2 extends IPSModule ); $this->SetTimerInterval("Timer_Do_UserCalc_EVC",$this->ReadPropertyInteger("Interval")*1000); $stationType = $this->ReadPropertyInteger("Ladestation"); + + // Bestehende Pico-Instanzen können aus der bisherigen Erkennung einen + // ungültigen Grenzwert von 2.5 A und dadurch "voll" gespeichert haben. + // Diese Altlast wird beim ersten Laden der korrigierten Version einmalig + // zurückgesetzt. + if ( + $stationType === 3 && + $this->GetBuffer("SmartMeFullDetectionVersion") !== "2" + ) { + $this->SetValue("Car_is_full", false); + $this->SetValue("Pending_Counter", 0); + $this->SetValue( + "Max_Current", + (float)$this->ReadPropertyInteger("Max_Current_abs") + ); + $this->SetBuffer("SmartMeChargingEnabled", "0"); + $this->SetBuffer("SmartMeFullDetectionVersion", "2"); + } + $usesEaseeGateway = $this->UsesEaseeGateway($stationType); IPS_SetHidden( $this->GetIDForIdent("Easee_Gateway_Connected"), @@ -938,6 +957,10 @@ class Ladestation_v2 extends IPSModule private function DetectSmartMeCar(): bool { + // Bei einem fehlgeschlagenen Abruf darf eine alte Freigabe keinesfalls + // zur Ladeende-Erkennung weiterverwendet werden. + $this->SetBuffer("SmartMeChargingEnabled", "0"); + $deviceID = trim($this->ReadPropertyString("ID")); if ($deviceID === "") { return $this->GetValue("Car_detected"); @@ -974,6 +997,37 @@ class Ladestation_v2 extends IPSModule ); } + // Die Pico-API unterscheidet zwischen der extern vorgegebenen + // Stromgrenze und der effektiv verfügbaren Stromgrenze. Nur wenn beide + // vorhandenen Grenzen mindestens dem Stationsminimum entsprechen, ist + // die Ladung wirklich freigegeben. Eine Regelungsunterbrechung mit 0 A + // darf nicht als abgeschlossenes Laden ausgewertet werden. + $minStationCurrent = $chargingData["MinStationCurrent"] ?? + $chargingData["minStationCurrent"] ?? 6; + $minStationCurrent = is_numeric($minStationCurrent) + ? max(1.0, (float)$minStationCurrent) + : 6.0; + + $maxDynamicCurrent = $chargingData["MaxDynamicCurrent"] ?? + $chargingData["maxDynamicCurrent"] ?? null; + $maxAllowedCurrent = $chargingData["MaxAllowedChargingCurrent"] ?? + $chargingData["maxAllowedChargingCurrent"] ?? null; + + $hasCurrentLimit = is_numeric($maxDynamicCurrent) || + is_numeric($maxAllowedCurrent); + $dynamicCurrentAllowsCharging = !is_numeric($maxDynamicCurrent) || + (float)$maxDynamicCurrent >= $minStationCurrent; + $allowedCurrentAllowsCharging = !is_numeric($maxAllowedCurrent) || + (float)$maxAllowedCurrent >= $minStationCurrent; + $chargingEnabled = $hasCurrentLimit + ? $dynamicCurrentAllowsCharging && $allowedCurrentAllowsCharging + : $this->GetValue("Aktuelle_Leistung") > 0; + + $this->SetBuffer( + "SmartMeChargingEnabled", + $chargingEnabled ? "1" : "0" + ); + $isOnePhase = null; $deviceData = $this->ReadJsonEndpoint( "https://api.smart-me.com/Devices/" . $deviceID, @@ -1004,13 +1058,17 @@ class Ladestation_v2 extends IPSModule } $carDetected = in_array($stationState, [2, 3, 4, 6], true); - $carIsFull = $this->GetValue("Car_is_full"); - if (!$carIsFull) { - $learnedMaxCurrent = (float)$this->GetValue("Max_Current"); - $carIsFull = $carDetected && - $this->GetValue("Car_detected") && - $learnedMaxCurrent > 0 && - $learnedMaxCurrent < 6; + $wasFull = $this->GetValue("Car_is_full"); + $carIsCharging = is_numeric($chargingPowerWatts) && + abs((float)$chargingPowerWatts) >= 100; + $carIsFull = $carDetected && $wasFull && !$carIsCharging; + + // Ein aus einer früheren Regelungsunterbrechung gelernter Wert unter + // dem Mindestladestrom darf die Station nicht dauerhaft blockieren. + // Ein korrekt bestätigtes Ladeende wird dagegen bis zum Abstecken oder + // bis zur erneuten Leistungsaufnahme gehalten. + if (!$carIsFull && (float)$this->GetValue("Max_Current") < 6) { + $this->SetValue("Max_Current", $maxCurrent); } return $this->ApplyDirectStationDetection( @@ -1530,12 +1588,15 @@ class Ladestation_v2 extends IPSModule $maxPowerStep = count($powerSteps) > 0 ? max($powerSteps) : 0; $goEEnabled = !in_array($stationType, [1, 2], true) || $this->GetBuffer("GoEChargingEnabledFromStatus") === "1"; + $smartMeEnabled = $stationType !== 3 || + $this->GetBuffer("SmartMeChargingEnabled") === "1"; $canEvaluate = $ladebereit && $this->GetValue("Car_detected") && !$this->GetValue("Car_is_full") && $requestedPower > 0 && $maxPowerStep > 0 && - $goEEnabled; + $goEEnabled && + $smartMeEnabled; $requestedMismatch = $requestedPower > (1.11 * $actualPower); $availableMismatch = $maxAvailablePower < (1.11 * $actualPower); @@ -1550,6 +1611,21 @@ class Ladestation_v2 extends IPSModule $this->SetValue("Pending_Counter", 0); if ($requestedMismatch) { $this->Calc_Max_Current($this->GetValue("Is_1_ph")); + + // Smart-Me liefert keinen eindeutigen Status "Auto + // voll". Erst wenn während einer echten Freigabe über + // 90 Sekunden praktisch kein Strom angenommen wird, + // bestätigt der gelernte Wert unter 6 A das Ladeende. + if ( + $stationType === 3 && + $this->GetBuffer("SmartMeChargingEnabled") === "1" && + (float)$this->GetValue("Max_Current") > 0 && + (float)$this->GetValue("Max_Current") < 6 + ) { + $this->SetValue("Car_is_full", true); + $this->ResetTimer(); + $this->ResetNullTimer(); + } } } } else { diff --git a/library.json b/library.json index 7b018fb..9dd7aa6 100644 --- a/library.json +++ b/library.json @@ -9,4 +9,4 @@ "version": "2.1", "build": 0, "date": 0 -} \ No newline at end of file +}