diff --git a/EaseeGateway/README.md b/EaseeGateway/README.md index 1e5a695..358d675 100644 --- a/EaseeGateway/README.md +++ b/EaseeGateway/README.md @@ -17,6 +17,6 @@ Jeder Aufruf der Ladestationsinstanz wird sofort als Version wird dabei `minutes: 0` gesendet. Das Gateway puffert, begrenzt oder wiederholt die Stromvorgabe nicht selbst. -Der benötigte WebSocket-Client wird nach dem Speichern der Easee-Zugangsdaten -automatisch erstellt und konfiguriert. Im WebSocket-Client muss keine URL -eingetragen werden. +Der benötigte WebSocket-Client wird beim Erstellen des Gateways automatisch +und genau einmal angelegt. Nach dem Speichern der Easee-Zugangsdaten wird er +konfiguriert. Im WebSocket-Client muss keine URL eingetragen werden. diff --git a/EaseeGateway/module.php b/EaseeGateway/module.php index 171c89b..95887ca 100644 --- a/EaseeGateway/module.php +++ b/EaseeGateway/module.php @@ -37,6 +37,11 @@ class EaseeGateway extends IPSModule 'IPS_RequestAction(' . $this->InstanceID . ', "RefreshToken", 0);' ); + // Ein Splitter benötigt einen eigenen I/O-Parent. RequireParent() + // prüft zuerst die bestehende Verbindung und erzeugt daher beim + // Instanzieren keine zweite WebSocket-Verbindung. + $this->RequireParent(self::WEBSOCKET_MODULE_ID); + } public function ApplyChanges() @@ -88,28 +93,21 @@ class EaseeGateway extends IPSModule $instance = IPS_GetInstance($this->InstanceID); $parentID = (int)$instance["ConnectionID"]; - if ($parentID > 0) { - $parent = IPS_GetInstance($parentID); - if ($parent["ModuleInfo"]["ModuleID"] === self::WEBSOCKET_MODULE_ID) { - return true; - } - IPS_DisconnectInstance($this->InstanceID); - } - - $parentID = IPS_CreateInstance(self::WEBSOCKET_MODULE_ID); - if ($parentID <= 0) { + if ($parentID <= 0 && !$this->RequireParent(self::WEBSOCKET_MODULE_ID)) { $this->SetLastError("WebSocket-Client konnte nicht erstellt werden"); return false; } - IPS_SetName($parentID, "Easee SignalR WebSocket"); - $objectParentID = IPS_GetParent($this->InstanceID); - if ($objectParentID > 0) { - IPS_SetParent($parentID, $objectParentID); + $instance = IPS_GetInstance($this->InstanceID); + $parentID = (int)$instance["ConnectionID"]; + if ($parentID <= 0 || !IPS_InstanceExists($parentID)) { + $this->SetLastError("WebSocket-Client ist nicht verbunden"); + return false; } - if (!IPS_ConnectInstance($this->InstanceID, $parentID)) { - $this->SetLastError("WebSocket-Client konnte nicht verbunden werden"); + $parent = IPS_GetInstance($parentID); + if ($parent["ModuleInfo"]["ModuleID"] !== self::WEBSOCKET_MODULE_ID) { + $this->SetLastError("Ungültige WebSocket-Schnittstelle"); return false; } diff --git a/Ladestation/README.md b/Ladestation/README.md index 48da42e..ea2200f 100644 --- a/Ladestation/README.md +++ b/Ladestation/README.md @@ -28,7 +28,7 @@ Das Gateway liefert: Bei Easee erfolgt die Fahrzeug- und Phasenerkennung ohne den zeitbasierten `Pending_Counter`. Der maximale Ladestrom wird aus Observation 47 übernommen und zusätzlich durch `Max_Current_abs` begrenzt. Die Typen 1 bis 4 verwenden -weiterhin unverändert die bisherige zeit- und leistungsbasierte Erkennung. +weiterhin die adaptive Soll-/Istleistungserkennung mit dem `Pending_Counter`. Die Fahrzeugphasen werden anhand der tatsächlich gemessenen Leiterströme erkannt; alte Phasenwerte werden beim Trennen oder Neuverbinden verworfen. Während einer Ladepause mit 0 W bleibt die zuletzt erkannte Phasenanzahl @@ -48,21 +48,25 @@ Go-e-Fahrzeugstatus. Die tatsächlich verwendeten Phasen werden aus den drei gemessenen Strömen des `nrg`-Arrays bestimmt. Der maximale Ladestrom wird durch die Stations-, Kabel- und Modulgrenze begrenzt. -`Auto fertig geladen` wird bei Go-E erst gesetzt, wenn die Ladung freigegeben -ist, der Fahrzeugstatus 4 anliegt und während 90 Sekunden weniger als 100 W -fließen. Bei einer durch das Modul angeforderten 0-W-Pause wird der -`Pending_Counter` zurückgesetzt. Die Easee-Erkennung bleibt davon unberührt. +Ist die angeforderte Ladeleistung während 90 Sekunden um den Faktor `1.11` +höher als die tatsächliche Leistung, wird der fahrzeugseitige Maximalstrom +aus dem Istwert neu berechnet. Fällt er unter 6 A, wird `Auto fertig geladen` +gesetzt. Bei einer durch das Modul angeforderten 0-W-Pause wird der +`Pending_Counter` zurückgesetzt. Zusätzlich muss bei Go-E die Ladung +freigegeben sein und der Fahrzeugstatus 4 anliegen. ### Smart-Me Pico Typ 3 erkennt ein verbundenes Fahrzeug über den Pico-Ladestatus. Die Phasenerkennung verwendet `CurrentL1`, `CurrentL2` und `CurrentL3` des Smart-Me-Geräte-Endpunkts. Der maximale Stationsstrom wird direkt aus den -Pico-Ladedaten übernommen. +Pico-Ladedaten übernommen. Der fahrzeugseitige Maximalstrom und das Ladeende +werden weiterhin über den 90-Sekunden-Soll-/Istvergleich ermittelt. -Bei Typ 1 bis 3 entfällt die zeitbasierte Fahrzeug- und Phasenerkennung über -den `Pending_Counter`. Die Phasenzahl wird aktualisiert, sobald ein messbarer -Ladestrom fließt. Beim Abstecken werden die erkannten Phasen zurückgesetzt. +Bei Typ 1 bis 3 entfällt nur die frühere zeitbasierte Phasenerkennung. Die +Phasenzahl wird direkt aus den API-Messwerten aktualisiert, sobald ein +messbarer Ladestrom fließt. Beim Abstecken werden die erkannten Phasen +zurückgesetzt. Die adaptive Maximalstrom- und Ladeenderkennung bleibt aktiv. Typ 4 bleibt unverändert. diff --git a/Ladestation/module.php b/Ladestation/module.php index 44c34d7..48f0f11 100644 --- a/Ladestation/module.php +++ b/Ladestation/module.php @@ -468,9 +468,11 @@ class Ladestation extends IPSModule return $stationType === 5 || $stationType === 6; } - private function UsesDirectStationDetection(int $stationType): bool + private function UsesAdaptiveCurrentDetection(int $stationType): bool { - return in_array($stationType, [1, 2, 3, 5, 6], true); + // Easee (5/6) liefert den Ladeendestatus direkt über das Gateway. + // Alle anderen Typen benötigen weiterhin die Erkennung über Soll-/Istleistung. + return in_array($stationType, [1, 2, 3, 4], true); } private function ApplyEaseeGatewayResponse($response) @@ -716,14 +718,15 @@ class Ladestation extends IPSModule ): bool { $wasDetected = $this->GetValue("Car_detected"); $wasOnePhase = $this->GetValue("Is_1_ph"); + $currentMax = (float)$this->GetValue("Max_Current"); if ($resetPendingCounter) { $this->SetValue("Pending_Counter", 0); } - $this->SetValue("Max_Current", $maxCurrent); if (!$carDetected) { $this->SetValue("Pending_Counter", 0); + $this->SetValue("Max_Current", $maxCurrent); $this->SetValue("Car_detected", false); $this->SetValue("Car_is_full", false); $this->SetValue("Leistung_Delta", 0); @@ -740,10 +743,18 @@ class Ladestation extends IPSModule $this->SetValue("Is_1_ph", $isOnePhase); } + $phaseChanged = $wasOnePhase !== $this->GetValue("Is_1_ph"); + if (!$wasDetected || $phaseChanged || $currentMax <= 0) { + $this->SetValue("Max_Current", $maxCurrent); + } else { + // Einen vom Soll-/Istvergleich gelernten Fahrzeuggrenzwert nicht + // bei jedem API-Abruf wieder mit dem Stationsgrenzwert überschreiben. + $this->SetValue("Max_Current", min($currentMax, $maxCurrent)); + } + $this->SetValue("Car_detected", true); $this->SetValue("Car_is_full", $carIsFull); - $phaseChanged = $wasOnePhase !== $this->GetValue("Is_1_ph"); if (!$wasDetected || $phaseChanged) { if ($this->GetValue("Is_1_ph")) { $power = $this->GetValue("Mindestaldestrom") * 230; @@ -813,7 +824,6 @@ class Ladestation extends IPSModule is_numeric($chargingPowerWatts) && abs((float)$chargingPowerWatts) >= 100 ) { - $this->SetValue("Pending_Counter", 0); return false; } @@ -828,21 +838,15 @@ class Ladestation extends IPSModule !is_numeric($chargingPowerWatts) || abs((float)$chargingPowerWatts) >= 100 ) { - $this->SetValue("Pending_Counter", 0); return false; } - $counter = $this->GetValue("Pending_Counter") + 1; - $this->SetValue("Pending_Counter", $counter); - - $interval = max(1, $this->ReadPropertyInteger("Interval")); - $requiredCycles = max(1, (int)ceil(90 / $interval)); - if ($counter < $requiredCycles) { - return false; - } - - $this->SetValue("Pending_Counter", 0); - return true; + // Der zentrale Soll-/Istvergleich setzt Max_Current nach 90 Sekunden. + // Unter 6 A entspricht dies der bisherigen Erkennung "Auto geladen". + $learnedMaxCurrent = (float)$this->GetValue("Max_Current"); + return $this->GetValue("Car_detected") && + $learnedMaxCurrent > 0 && + $learnedMaxCurrent < 6; } private function DetectGoECar(int $stationType): bool @@ -862,6 +866,10 @@ class Ladestation extends IPSModule } $this->SetValue("Fahrzeugstatus", $carState); + $this->SetBuffer( + "GoEChargingEnabledFromStatus", + $this->IsGoEChargingEnabled($data, $stationType) ? "1" : "0" + ); $isOnePhase = null; $chargingPowerWatts = null; @@ -887,18 +895,23 @@ class Ladestation extends IPSModule } $carDetected = in_array($carState, [2, 3, 4, 5], true); + $stationMaxCurrent = $this->GetGoEMaxCurrent($data); + $wasFull = $this->GetValue("Car_is_full"); $carIsFull = $carDetected && $this->DetectGoEFullCharge( $data, $stationType, $carState, $chargingPowerWatts ); + if ($wasFull && !$carIsFull) { + $this->SetValue("Max_Current", $stationMaxCurrent); + } return $this->ApplyDirectStationDetection( $carDetected, $carIsFull, $isOnePhase, - $this->GetGoEMaxCurrent($data), + $stationMaxCurrent, false ); } @@ -932,10 +945,12 @@ class Ladestation extends IPSModule $chargingPower = $chargingData["ActiveChargingPower"] ?? $chargingData["activeChargingPower"] ?? null; + $chargingPowerWatts = null; if (is_numeric($chargingPower)) { + $chargingPowerWatts = (float)$chargingPower * 1000; $this->SetValue( "Ladeleistung_Effektiv", - round((float)$chargingPower * 1000) + round($chargingPowerWatts) ); } @@ -968,11 +983,22 @@ 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; + } + return $this->ApplyDirectStationDetection( - in_array($stationState, [2, 3, 4, 6], true), - false, + $carDetected, + $carIsFull, $isOnePhase, - $maxCurrent + $maxCurrent, + false ); } @@ -1475,43 +1501,48 @@ class Ladestation extends IPSModule // PowerSteps in der RegisterVariable speichern SetValue($this->GetIDForIdent("PowerSteps"), json_encode($powerSteps)); // Rückgabe der Powersteps - if (!$this->UsesDirectStationDetection($this->ReadPropertyInteger("Ladestation"))) { - $counter = $this->GetValue("Pending_Counter"); + $stationType = $this->ReadPropertyInteger("Ladestation"); + if ($this->UsesAdaptiveCurrentDetection($stationType)) { + $requestedPower = max(0, (float)$this->GetValue("Aktuelle_Leistung")); + $actualPower = abs((float)$this->GetValue("Ladeleistung_Effektiv")); + $availableSteps = $this->Get_Array_From_Current( + $this->GetValue("Is_1_ph"), + $this->GetValue("Max_Current"), + $this->GetValue("Aktuelle_Leistung"), + $this->GetValue("IsTimerActive_Null_Timer") + ); + $maxAvailablePower = count($availableSteps) > 0 + ? max($availableSteps) + : 0; + $maxPowerStep = count($powerSteps) > 0 ? max($powerSteps) : 0; + $goEEnabled = !in_array($stationType, [1, 2], true) || + $this->GetBuffer("GoEChargingEnabledFromStatus") === "1"; + $canEvaluate = $ladebereit && + $this->GetValue("Car_detected") && + !$this->GetValue("Car_is_full") && + $requestedPower > 0 && + $maxPowerStep > 0 && + $goEEnabled; - if($this->GetValue("Aktuelle_Leistung")>(1.11*$this->GetValue("Ladeleistung_Effektiv"))){ - $this->SetValue("Pending_Counter", $counter+1); - - }elseif(max($this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"), $this->GetValue("Aktuelle_Leistung"), $this->GetValue("IsTimerActive_Null_Timer")))<(1.11*$this->GetValue("Ladeleistung_Effektiv"))){ // diesen elseif ggf wieder rauslöschen - $this->SetValue("Pending_Counter", $counter+1); - - } - else{ - - $this->SetValue("Pending_Counter", 0); - - } - - if($counter>(90/($this->ReadPropertyInteger("Interval")))){ - - $this->SetValue("Pending_Counter", 0); - - if((max(json_decode($this->GetValue("PowerSteps")))) >0){ - - if($this->GetValue("Aktuelle_Leistung")>(1.11*$this->GetValue("Ladeleistung_Effektiv"))) { - - $this->Calc_Max_Current($this->GetValue("Is_1_ph")); + $requestedMismatch = $requestedPower > (1.11 * $actualPower); + $availableMismatch = $maxAvailablePower < (1.11 * $actualPower); + if ($canEvaluate && ($requestedMismatch || $availableMismatch)) { + $counter = $this->GetValue("Pending_Counter") + 1; + $this->SetValue("Pending_Counter", $counter); + $interval = max(1, $this->ReadPropertyInteger("Interval")); + $requiredCycles = max(1, (int)ceil(90 / $interval)); + if ($counter >= $requiredCycles) { + $this->SetValue("Pending_Counter", 0); + if ($requestedMismatch) { + $this->Calc_Max_Current($this->GetValue("Is_1_ph")); } - } - + } else { + $this->SetValue("Pending_Counter", 0); } - } elseif (!in_array( - $this->ReadPropertyInteger("Ladestation"), - [1, 2], - true - )) { + } else { $this->SetValue("Pending_Counter", 0); } diff --git a/Ladestation_v2/README.md b/Ladestation_v2/README.md index 8dd6652..22d1226 100644 --- a/Ladestation_v2/README.md +++ b/Ladestation_v2/README.md @@ -28,7 +28,7 @@ Das Gateway liefert: Bei Easee erfolgt die Fahrzeug- und Phasenerkennung ohne den zeitbasierten `Pending_Counter`. Der maximale Ladestrom wird aus Observation 47 übernommen und zusätzlich durch `Max_Current_abs` begrenzt. Die Typen 1 bis 4 verwenden -weiterhin unverändert die bisherige zeit- und leistungsbasierte Erkennung. +weiterhin die adaptive Soll-/Istleistungserkennung mit dem `Pending_Counter`. Die Fahrzeugphasen werden anhand der tatsächlich gemessenen Leiterströme erkannt; alte Phasenwerte werden beim Trennen oder Neuverbinden verworfen. Während einer Ladepause mit 0 W bleibt die zuletzt erkannte Phasenanzahl @@ -48,21 +48,25 @@ Go-e-Fahrzeugstatus. Die tatsächlich verwendeten Phasen werden aus den drei gemessenen Strömen des `nrg`-Arrays bestimmt. Der maximale Ladestrom wird durch die Stations-, Kabel- und Modulgrenze begrenzt. -`Auto fertig geladen` wird bei Go-E erst gesetzt, wenn die Ladung freigegeben -ist, der Fahrzeugstatus 4 anliegt und während 90 Sekunden weniger als 100 W -fließen. Bei einer durch das Modul angeforderten 0-W-Pause wird der -`Pending_Counter` zurückgesetzt. Die Easee-Erkennung bleibt davon unberührt. +Ist die angeforderte Ladeleistung während 90 Sekunden um den Faktor `1.11` +höher als die tatsächliche Leistung, wird der fahrzeugseitige Maximalstrom +aus dem Istwert neu berechnet. Fällt er unter 6 A, wird `Auto fertig geladen` +gesetzt. Bei einer durch das Modul angeforderten 0-W-Pause wird der +`Pending_Counter` zurückgesetzt. Zusätzlich muss bei Go-E die Ladung +freigegeben sein und der Fahrzeugstatus 4 anliegen. ### Smart-Me Pico Typ 3 erkennt ein verbundenes Fahrzeug über den Pico-Ladestatus. Die Phasenerkennung verwendet `CurrentL1`, `CurrentL2` und `CurrentL3` des Smart-Me-Geräte-Endpunkts. Der maximale Stationsstrom wird direkt aus den -Pico-Ladedaten übernommen. +Pico-Ladedaten übernommen. Der fahrzeugseitige Maximalstrom und das Ladeende +werden weiterhin über den 90-Sekunden-Soll-/Istvergleich ermittelt. -Bei Typ 1 bis 3 entfällt die zeitbasierte Fahrzeug- und Phasenerkennung über -den `Pending_Counter`. Die Phasenzahl wird aktualisiert, sobald ein messbarer -Ladestrom fließt. Beim Abstecken werden die erkannten Phasen zurückgesetzt. +Bei Typ 1 bis 3 entfällt nur die frühere zeitbasierte Phasenerkennung. Die +Phasenzahl wird direkt aus den API-Messwerten aktualisiert, sobald ein +messbarer Ladestrom fließt. Beim Abstecken werden die erkannten Phasen +zurückgesetzt. Die adaptive Maximalstrom- und Ladeenderkennung bleibt aktiv. Typ 4 bleibt unverändert. @@ -75,3 +79,5 @@ Typ 4 bleibt unverändert. `Schnittstelle ändern` die gewünschte Gateway-Instanz auswählen. Mehrere Easee-Ladestationen desselben Kontos verwenden gemeinsam ein Gateway. +Beim Erstellen einer weiteren Instanz wird ein vorhandenes Gateway automatisch +verwendet; es wird keine zweite Parent-Verbindung erzeugt. diff --git a/Ladestation_v2/module.php b/Ladestation_v2/module.php index a2f353d..106e4ae 100644 --- a/Ladestation_v2/module.php +++ b/Ladestation_v2/module.php @@ -113,6 +113,10 @@ class Ladestation_v2 extends IPSModule $this->RegisterVariableInteger("Mindestaldestrom", "Mindestaldestrom", "", 0); $this->EnableAction("Mindestaldestrom"); + // Vorhandenes Gateway verwenden; nur wenn keines existiert, wird eines + // erstellt. ConnectParent() ist auch bei bereits gesetztem Parent sicher. + $this->ConnectParent(self::EASEE_GATEWAY_MODULE_ID); + } public function Migrate($JSONData) @@ -286,14 +290,12 @@ class Ladestation_v2 extends IPSModule $instance = IPS_GetInstance($this->InstanceID); $parentID = (int)$instance["ConnectionID"]; - // Eine in Version 1.2/1.3 gespeicherte Gateway-Auswahl wieder als - // normale IP-Symcon-Schnittstelle verbinden. + // Bei einer aktualisierten, bisher noch unverbundenen Bestandsinstanz + // die normale IP-Symcon-Parent-Verbindung nachziehen. if ($parentID <= 0) { - $storedGatewayID = $this->GetConfiguredEaseeGatewayID(); - if ($storedGatewayID > 0) { - IPS_ConnectInstance($this->InstanceID, $storedGatewayID); - $parentID = $storedGatewayID; - } + $this->ConnectParent(self::EASEE_GATEWAY_MODULE_ID); + $instance = IPS_GetInstance($this->InstanceID); + $parentID = (int)$instance["ConnectionID"]; } if ($usesEaseeGateway && $this->IsEaseeGatewayInstance($parentID)) { @@ -486,9 +488,11 @@ class Ladestation_v2 extends IPSModule return $stationType === 5 || $stationType === 6; } - private function UsesDirectStationDetection(int $stationType): bool + private function UsesAdaptiveCurrentDetection(int $stationType): bool { - return in_array($stationType, [1, 2, 3, 5, 6], true); + // Easee (5/6) liefert den Ladeendestatus direkt über das Gateway. + // Alle anderen Typen benötigen weiterhin die Erkennung über Soll-/Istleistung. + return in_array($stationType, [1, 2, 3, 4], true); } private function ApplyEaseeGatewayResponse($response) @@ -734,14 +738,15 @@ class Ladestation_v2 extends IPSModule ): bool { $wasDetected = $this->GetValue("Car_detected"); $wasOnePhase = $this->GetValue("Is_1_ph"); + $currentMax = (float)$this->GetValue("Max_Current"); if ($resetPendingCounter) { $this->SetValue("Pending_Counter", 0); } - $this->SetValue("Max_Current", $maxCurrent); if (!$carDetected) { $this->SetValue("Pending_Counter", 0); + $this->SetValue("Max_Current", $maxCurrent); $this->SetValue("Car_detected", false); $this->SetValue("Car_is_full", false); $this->SetValue("Leistung_Delta", 0); @@ -758,10 +763,18 @@ class Ladestation_v2 extends IPSModule $this->SetValue("Is_1_ph", $isOnePhase); } + $phaseChanged = $wasOnePhase !== $this->GetValue("Is_1_ph"); + if (!$wasDetected || $phaseChanged || $currentMax <= 0) { + $this->SetValue("Max_Current", $maxCurrent); + } else { + // Einen vom Soll-/Istvergleich gelernten Fahrzeuggrenzwert nicht + // bei jedem API-Abruf wieder mit dem Stationsgrenzwert überschreiben. + $this->SetValue("Max_Current", min($currentMax, $maxCurrent)); + } + $this->SetValue("Car_detected", true); $this->SetValue("Car_is_full", $carIsFull); - $phaseChanged = $wasOnePhase !== $this->GetValue("Is_1_ph"); if (!$wasDetected || $phaseChanged) { if ($this->GetValue("Is_1_ph")) { $power = $this->GetValue("Mindestaldestrom") * 230; @@ -831,7 +844,6 @@ class Ladestation_v2 extends IPSModule is_numeric($chargingPowerWatts) && abs((float)$chargingPowerWatts) >= 100 ) { - $this->SetValue("Pending_Counter", 0); return false; } @@ -846,21 +858,15 @@ class Ladestation_v2 extends IPSModule !is_numeric($chargingPowerWatts) || abs((float)$chargingPowerWatts) >= 100 ) { - $this->SetValue("Pending_Counter", 0); return false; } - $counter = $this->GetValue("Pending_Counter") + 1; - $this->SetValue("Pending_Counter", $counter); - - $interval = max(1, $this->ReadPropertyInteger("Interval")); - $requiredCycles = max(1, (int)ceil(90 / $interval)); - if ($counter < $requiredCycles) { - return false; - } - - $this->SetValue("Pending_Counter", 0); - return true; + // Der zentrale Soll-/Istvergleich setzt Max_Current nach 90 Sekunden. + // Unter 6 A entspricht dies der bisherigen Erkennung "Auto geladen". + $learnedMaxCurrent = (float)$this->GetValue("Max_Current"); + return $this->GetValue("Car_detected") && + $learnedMaxCurrent > 0 && + $learnedMaxCurrent < 6; } private function DetectGoECar(int $stationType): bool @@ -880,6 +886,10 @@ class Ladestation_v2 extends IPSModule } $this->SetValue("Fahrzeugstatus", $carState); + $this->SetBuffer( + "GoEChargingEnabledFromStatus", + $this->IsGoEChargingEnabled($data, $stationType) ? "1" : "0" + ); $isOnePhase = null; $chargingPowerWatts = null; @@ -905,18 +915,23 @@ class Ladestation_v2 extends IPSModule } $carDetected = in_array($carState, [2, 3, 4, 5], true); + $stationMaxCurrent = $this->GetGoEMaxCurrent($data); + $wasFull = $this->GetValue("Car_is_full"); $carIsFull = $carDetected && $this->DetectGoEFullCharge( $data, $stationType, $carState, $chargingPowerWatts ); + if ($wasFull && !$carIsFull) { + $this->SetValue("Max_Current", $stationMaxCurrent); + } return $this->ApplyDirectStationDetection( $carDetected, $carIsFull, $isOnePhase, - $this->GetGoEMaxCurrent($data), + $stationMaxCurrent, false ); } @@ -950,10 +965,12 @@ class Ladestation_v2 extends IPSModule $chargingPower = $chargingData["ActiveChargingPower"] ?? $chargingData["activeChargingPower"] ?? null; + $chargingPowerWatts = null; if (is_numeric($chargingPower)) { + $chargingPowerWatts = (float)$chargingPower * 1000; $this->SetValue( "Ladeleistung_Effektiv", - round((float)$chargingPower * 1000) + round($chargingPowerWatts) ); } @@ -986,11 +1003,22 @@ 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; + } + return $this->ApplyDirectStationDetection( - in_array($stationState, [2, 3, 4, 6], true), - false, + $carDetected, + $carIsFull, $isOnePhase, - $maxCurrent + $maxCurrent, + false ); } @@ -1486,43 +1514,48 @@ class Ladestation_v2 extends IPSModule // PowerSteps in der RegisterVariable speichern SetValue($this->GetIDForIdent("PowerSteps"), json_encode($powerSteps)); // Rückgabe der Powersteps - if (!$this->UsesDirectStationDetection($this->ReadPropertyInteger("Ladestation"))) { - $counter = $this->GetValue("Pending_Counter"); + $stationType = $this->ReadPropertyInteger("Ladestation"); + if ($this->UsesAdaptiveCurrentDetection($stationType)) { + $requestedPower = max(0, (float)$this->GetValue("Aktuelle_Leistung")); + $actualPower = abs((float)$this->GetValue("Ladeleistung_Effektiv")); + $availableSteps = $this->Get_Array_From_Current( + $this->GetValue("Is_1_ph"), + $this->GetValue("Max_Current"), + $this->GetValue("Aktuelle_Leistung"), + $this->GetValue("IsTimerActive_Null_Timer") + ); + $maxAvailablePower = count($availableSteps) > 0 + ? max($availableSteps) + : 0; + $maxPowerStep = count($powerSteps) > 0 ? max($powerSteps) : 0; + $goEEnabled = !in_array($stationType, [1, 2], true) || + $this->GetBuffer("GoEChargingEnabledFromStatus") === "1"; + $canEvaluate = $ladebereit && + $this->GetValue("Car_detected") && + !$this->GetValue("Car_is_full") && + $requestedPower > 0 && + $maxPowerStep > 0 && + $goEEnabled; - if($this->GetValue("Aktuelle_Leistung")>(1.11*$this->GetValue("Ladeleistung_Effektiv"))){ - $this->SetValue("Pending_Counter", $counter+1); - - }elseif(max($this->Get_Array_From_Current($this->GetValue("Is_1_ph"),$this->GetValue("Max_Current"), $this->GetValue("Aktuelle_Leistung"), $this->GetValue("IsTimerActive_Null_Timer")))<(1.11*$this->GetValue("Ladeleistung_Effektiv"))){ // diesen elseif ggf wieder rauslöschen - $this->SetValue("Pending_Counter", $counter+1); - - } - else{ - - $this->SetValue("Pending_Counter", 0); - - } - - if($counter>(90/($this->ReadPropertyInteger("Interval")))){ - - $this->SetValue("Pending_Counter", 0); - - if((max(json_decode($this->GetValue("PowerSteps")))) >0){ - - if($this->GetValue("Aktuelle_Leistung")>(1.11*$this->GetValue("Ladeleistung_Effektiv"))) { - - $this->Calc_Max_Current($this->GetValue("Is_1_ph")); + $requestedMismatch = $requestedPower > (1.11 * $actualPower); + $availableMismatch = $maxAvailablePower < (1.11 * $actualPower); + if ($canEvaluate && ($requestedMismatch || $availableMismatch)) { + $counter = $this->GetValue("Pending_Counter") + 1; + $this->SetValue("Pending_Counter", $counter); + $interval = max(1, $this->ReadPropertyInteger("Interval")); + $requiredCycles = max(1, (int)ceil(90 / $interval)); + if ($counter >= $requiredCycles) { + $this->SetValue("Pending_Counter", 0); + if ($requestedMismatch) { + $this->Calc_Max_Current($this->GetValue("Is_1_ph")); } - } - + } else { + $this->SetValue("Pending_Counter", 0); } - } elseif (!in_array( - $this->ReadPropertyInteger("Ladestation"), - [1, 2], - true - )) { + } else { $this->SetValue("Pending_Counter", 0); } diff --git a/library.json b/library.json index 36e1390..0a6c89b 100644 --- a/library.json +++ b/library.json @@ -2,7 +2,7 @@ "id": "{89CAF37C-8E6E-5312-8195-6DA8AB7E5E70}", "name": "Manager", "author": "Daniel Haefliger", - "url": "https://git.belevo.ch/dh/Symcon_Belevo_Energiemanagement", + "url": "https://git.belevo.ch/dh/Symcon_Belevo_Energiemanagement_testing", "compatibility": { "version": "8.0" },