fix(ladestation): Ladeanlauf und Easee-Verbindung stabilisieren
Tests / test (push) Successful in 1m23s
Tests / test (push) Successful in 1m23s
This commit is contained in:
+29
-3
@@ -16,6 +16,7 @@ class EaseeGateway extends IPSModule
|
|||||||
private const SIGNALR_BASE_URL = 'https://streams.easee.com/hubs/chargers';
|
private const SIGNALR_BASE_URL = 'https://streams.easee.com/hubs/chargers';
|
||||||
private const API_BASE_URL = 'https://api.easee.com';
|
private const API_BASE_URL = 'https://api.easee.com';
|
||||||
private const RECORD_SEPARATOR = "\x1e";
|
private const RECORD_SEPARATOR = "\x1e";
|
||||||
|
private const CACHE_MAXIMALALTER_SEKUNDEN = 60;
|
||||||
|
|
||||||
public function Create(): void
|
public function Create(): void
|
||||||
{
|
{
|
||||||
@@ -112,7 +113,10 @@ class EaseeGateway extends IPSModule
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 'RefreshToken':
|
case 'RefreshToken':
|
||||||
if ($this->erneuereZugangsdaten(false)) {
|
if (
|
||||||
|
$this->erneuereZugangsdaten(false)
|
||||||
|
&& $this->GetBuffer('SignalRReady') !== '1'
|
||||||
|
) {
|
||||||
$this->verbindeSignalR();
|
$this->verbindeSignalR();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -346,7 +350,7 @@ class EaseeGateway extends IPSModule
|
|||||||
$this->SetBuffer('ReceiveBuffer', '');
|
$this->SetBuffer('ReceiveBuffer', '');
|
||||||
$this->SetBuffer('SubscribedThisConnection', '{}');
|
$this->SetBuffer('SubscribedThisConnection', '{}');
|
||||||
$this->SetBuffer('PendingInvocations', '{}');
|
$this->SetBuffer('PendingInvocations', '{}');
|
||||||
$this->WriteAttributeString('ObservationCache', '{}');
|
$this->markiereBeobachtungscacheVeraltet();
|
||||||
$this->setzeVerbunden(false);
|
$this->setzeVerbunden(false);
|
||||||
|
|
||||||
if (!$this->konfiguriereWebSocketParent($url)) {
|
if (!$this->konfiguriereWebSocketParent($url)) {
|
||||||
@@ -572,12 +576,30 @@ class EaseeGateway extends IPSModule
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function markiereBeobachtungscacheVeraltet(): void
|
||||||
|
{
|
||||||
|
$cache = $this->leseAttributArray('ObservationCache');
|
||||||
|
foreach ($cache as &$zustand) {
|
||||||
|
if (is_array($zustand)) {
|
||||||
|
$zustand['updated'] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($zustand);
|
||||||
|
$this->WriteAttributeString(
|
||||||
|
'ObservationCache',
|
||||||
|
json_encode($cache, JSON_THROW_ON_ERROR)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function stelleAktuellenStatusSicher(string $seriennummer): void
|
private function stelleAktuellenStatusSicher(string $seriennummer): void
|
||||||
{
|
{
|
||||||
$cache = $this->leseAttributArray('ObservationCache');
|
$cache = $this->leseAttributArray('ObservationCache');
|
||||||
$zustand = $cache[$seriennummer] ?? [];
|
$zustand = $cache[$seriennummer] ?? [];
|
||||||
|
$cacheIstAktuell = is_array($zustand)
|
||||||
|
&& array_key_exists('109', $zustand)
|
||||||
|
&& (int) ($zustand['updated'] ?? 0) >= time() - self::CACHE_MAXIMALALTER_SEKUNDEN;
|
||||||
if (
|
if (
|
||||||
(is_array($zustand) && array_key_exists('109', $zustand))
|
$cacheIstAktuell
|
||||||
|| $this->ReadPropertyBoolean('Testmodus')
|
|| $this->ReadPropertyBoolean('Testmodus')
|
||||||
|| $this->GetBuffer('SignalRReady') !== '1'
|
|| $this->GetBuffer('SignalRReady') !== '1'
|
||||||
) {
|
) {
|
||||||
@@ -848,7 +870,11 @@ class EaseeGateway extends IPSModule
|
|||||||
|
|
||||||
private function setzeVerbunden(bool $verbunden): void
|
private function setzeVerbunden(bool $verbunden): void
|
||||||
{
|
{
|
||||||
|
$warVerbunden = (bool) $this->GetValue('Connected');
|
||||||
$this->SetValue('Connected', $verbunden);
|
$this->SetValue('Connected', $verbunden);
|
||||||
|
if ($warVerbunden === $verbunden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->SendDataToChildren(json_encode([
|
$this->SendDataToChildren(json_encode([
|
||||||
'DataID' => self::CHILD_EVENT_DATA_ID,
|
'DataID' => self::CHILD_EVENT_DATA_ID,
|
||||||
'Buffer' => json_encode([
|
'Buffer' => json_encode([
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
||||||
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
||||||
$this->RegisterAttributeInteger('LetzterGesetzterStrom', -1);
|
$this->RegisterAttributeInteger('LetzterGesetzterStrom', -1);
|
||||||
|
$this->RegisterAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
|
|
||||||
$this->RegisterTimer(
|
$this->RegisterTimer(
|
||||||
'Statuszyklus',
|
'Statuszyklus',
|
||||||
@@ -120,6 +121,7 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
// Ein persistiertes Ladeende nach Neueinrichtung oder Modulupdate einmal neu pruefen.
|
// Ein persistiertes Ladeende nach Neueinrichtung oder Modulupdate einmal neu pruefen.
|
||||||
$this->setzeZustand('FahrzeugGeladen', false);
|
$this->setzeZustand('FahrzeugGeladen', false);
|
||||||
$this->WriteAttributeInteger('LetzterGesetzterStrom', -1);
|
$this->WriteAttributeInteger('LetzterGesetzterStrom', -1);
|
||||||
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->pruefeKonfiguration();
|
$this->pruefeKonfiguration();
|
||||||
@@ -307,8 +309,14 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
$rohstatus,
|
$rohstatus,
|
||||||
$this->ReadAttributeBoolean('ZustandFahrzeugVerbunden'),
|
$this->ReadAttributeBoolean('ZustandFahrzeugVerbunden'),
|
||||||
$this->ReadAttributeBoolean('ZustandFahrzeugGeladen'),
|
$this->ReadAttributeBoolean('ZustandFahrzeugGeladen'),
|
||||||
$this->ReadAttributeInteger('LetzterGesetzterStrom') >= 6
|
$this->ReadAttributeInteger('LetzterGesetzterStrom') >= 6,
|
||||||
|
$this->ReadAttributeBoolean('LadeleistungSeitFreigabeErkannt')
|
||||||
);
|
);
|
||||||
|
if (!$status['FahrzeugVerbunden']) {
|
||||||
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
|
} elseif ($status['Ladestrom_A'] >= 3.5) {
|
||||||
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', true);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($status as $ident => $wert) {
|
foreach ($status as $ident => $wert) {
|
||||||
$this->setzeZustand($ident, $wert);
|
$this->setzeZustand($ident, $wert);
|
||||||
@@ -342,6 +350,7 @@ class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstell
|
|||||||
if ($strom === $this->ReadAttributeInteger('LetzterGesetzterStrom')) {
|
if ($strom === $this->ReadAttributeInteger('LetzterGesetzterStrom')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$this->WriteAttributeBoolean('LadeleistungSeitFreigabeErkannt', false);
|
||||||
|
|
||||||
$transport = new LadestationTransport(
|
$transport = new LadestationTransport(
|
||||||
fn (array $anfrage): string => $this->fuehreHttpAnfrageAus($anfrage)
|
fn (array $anfrage): string => $this->fuehreHttpAnfrageAus($anfrage)
|
||||||
|
|||||||
@@ -28,8 +28,9 @@ final class LadestationRegler
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Schwelle, die Voll-Erkennung und der Zuschlag von 2.5 A stammen
|
* Die Schwelle und der Zuschlag von 2.5 A stammen aus der Enelix-1-Ladestation.
|
||||||
* aus der Enelix-1-Ladestation und bleiben bewusst kompatibel.
|
* Ein Ladeende wird erst nach zuvor gemessener Ladeleistung angenommen, damit
|
||||||
|
* die Anlaufzeit nach einer neuen Stromvorgabe nicht als volles Fahrzeug gilt.
|
||||||
*
|
*
|
||||||
* @param array{Rohstatus: int, Istleistung_W: float} $rohstatus
|
* @param array{Rohstatus: int, Istleistung_W: float} $rohstatus
|
||||||
* @return array<string, int|float|bool>
|
* @return array<string, int|float|bool>
|
||||||
@@ -38,7 +39,8 @@ final class LadestationRegler
|
|||||||
array $rohstatus,
|
array $rohstatus,
|
||||||
bool $warVerbunden,
|
bool $warVerbunden,
|
||||||
bool $warGeladen = false,
|
bool $warGeladen = false,
|
||||||
bool $ladungErwartet = true
|
bool $ladungErwartet = true,
|
||||||
|
bool $ladeleistungWurdeErkannt = false
|
||||||
): array {
|
): array {
|
||||||
$verbunden = $rohstatus['Rohstatus'] !== 1;
|
$verbunden = $rohstatus['Rohstatus'] !== 1;
|
||||||
$leistung = max(0.0, $rohstatus['Istleistung_W']);
|
$leistung = max(0.0, $rohstatus['Istleistung_W']);
|
||||||
@@ -52,7 +54,10 @@ final class LadestationRegler
|
|||||||
$ladestrom = $leistung / $nenner;
|
$ladestrom = $leistung / $nenner;
|
||||||
$ermittelterMaximalstrom = 2.5 + $ladestrom;
|
$ermittelterMaximalstrom = 2.5 + $ladestrom;
|
||||||
$geladen = $warGeladen
|
$geladen = $warGeladen
|
||||||
|| ($warVerbunden && $ladungErwartet && $ermittelterMaximalstrom < 6.0);
|
|| ($warVerbunden
|
||||||
|
&& $ladungErwartet
|
||||||
|
&& $ladeleistungWurdeErkannt
|
||||||
|
&& $ermittelterMaximalstrom < 6.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$verbunden) {
|
if (!$verbunden) {
|
||||||
|
|||||||
@@ -33,12 +33,23 @@ final class LadestationReglerTest extends TestCase
|
|||||||
self::assertEqualsWithDelta(10.0, $status['Ladestrom_A'], 0.01);
|
self::assertEqualsWithDelta(10.0, $status['Ladestrom_A'], 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGeladenWirdNachVerbindungUnterSechsAmpereErkannt(): void
|
public function testAnlaufOhneGemesseneLadeleistungGiltNichtAlsLadeende(): void
|
||||||
{
|
{
|
||||||
$status = LadestationRegler::normalisiereStatus([
|
$status = LadestationRegler::normalisiereStatus([
|
||||||
'Rohstatus' => 2,
|
'Rohstatus' => 2,
|
||||||
'Istleistung_W' => 0.0,
|
'Istleistung_W' => 0.0,
|
||||||
], true);
|
], true, false, true, false);
|
||||||
|
|
||||||
|
self::assertFalse($status['FahrzeugGeladen']);
|
||||||
|
self::assertSame(2, $status['Fahrzeugstatus']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGeladenWirdErstNachZuvorGemessenerLadeleistungErkannt(): void
|
||||||
|
{
|
||||||
|
$status = LadestationRegler::normalisiereStatus([
|
||||||
|
'Rohstatus' => 2,
|
||||||
|
'Istleistung_W' => 0.0,
|
||||||
|
], true, false, true, true);
|
||||||
|
|
||||||
self::assertTrue($status['FahrzeugGeladen']);
|
self::assertTrue($status['FahrzeugGeladen']);
|
||||||
self::assertSame(4, $status['Fahrzeugstatus']);
|
self::assertSame(4, $status['Fahrzeugstatus']);
|
||||||
|
|||||||
@@ -228,4 +228,34 @@ return static function (TestContext $test): void {
|
|||||||
'alw=0'
|
'alw=0'
|
||||||
), 'Peak mit Solarladen muss die Ladestation stoppen.');
|
), 'Peak mit Solarladen muss die Ladestation stoppen.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$test->runCase('Sollwert bleibt waehrend der Ladeanlaufzeit gueltig', static function (
|
||||||
|
TestContext $test
|
||||||
|
) use ($goEAntwort, $stationen, $anforderungSenden): void {
|
||||||
|
$stationId = $stationen['go-e Gemini'];
|
||||||
|
IPS_SetProperty($stationId, 'Testantwort', $goEAntwort(2, 0));
|
||||||
|
IPS_ApplyChanges($stationId);
|
||||||
|
IPS_RequestAction($stationId, 'Aktiv', true);
|
||||||
|
IPS_RequestAction($stationId, 'Solarladen', true);
|
||||||
|
|
||||||
|
$anforderungSenden($stationId, 3850);
|
||||||
|
IPS_RequestAction($stationId, 'Statuszyklus', false);
|
||||||
|
|
||||||
|
$test->assertSame(
|
||||||
|
false,
|
||||||
|
GetValue($test->objectByIdent('FahrzeugGeladen', $stationId))
|
||||||
|
);
|
||||||
|
$test->assertSame(
|
||||||
|
true,
|
||||||
|
GetValue($test->objectByIdent('SollwertGueltig', $stationId))
|
||||||
|
);
|
||||||
|
$test->assertSame(
|
||||||
|
3850,
|
||||||
|
GetValue($test->objectByIdent('Sollleistung', $stationId))
|
||||||
|
);
|
||||||
|
$test->assertSame(
|
||||||
|
'[0,1450,1690,1930,2170,2410,2650,2890,3130,3370,3610,3850]',
|
||||||
|
(string) GetValue($test->objectByIdent('LeistungsangebotDiagnose', $stationId))
|
||||||
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user