fix(easee): Ereignisverbindung konsistent neu aufbauen
Tests / test (push) Successful in 1m2s

This commit is contained in:
dh
2026-09-29 16:47:03 +00:00
parent 393969594e
commit f611da975d
3 changed files with 136 additions and 19 deletions
+78 -19
View File
@@ -51,6 +51,11 @@ class EaseeGateway extends IPSModule
0,
"IPS_RequestAction(\$_IPS['TARGET'], 'RefreshToken', false);"
);
$this->RegisterTimer(
'ReconnectTimer',
0,
"IPS_RequestAction(\$_IPS['TARGET'], 'ReconnectAfterLoss', false);"
);
$this->RequireParent(self::WEBSOCKET_MODULE_ID);
}
@@ -61,6 +66,7 @@ class EaseeGateway extends IPSModule
$this->SetTimerInterval('MaintainConnectionTimer', 0);
$this->SetTimerInterval('TokenRefreshTimer', 0);
$this->SetTimerInterval('ReconnectTimer', 0);
$this->SetBuffer('SignalRReady', '0');
$this->SetBuffer('ReceiveBuffer', '');
$this->SetBuffer('Subscriptions', '{}');
@@ -112,6 +118,17 @@ class EaseeGateway extends IPSModule
$this->pflegeVerbindung();
return;
case 'ReconnectAfterLoss':
$this->SetTimerInterval('ReconnectTimer', 0);
if (
$this->ReadPropertyBoolean('Active')
&& !$this->ReadPropertyBoolean('Testmodus')
&& $this->GetBuffer('SignalRReady') !== '1'
) {
$this->verbindeSignalR();
}
return;
case 'RefreshToken':
if (
$this->erneuereZugangsdaten(false)
@@ -146,6 +163,15 @@ class EaseeGateway extends IPSModule
$daten['value'] ?? null
);
return;
case 'TestSignalRFrame':
if (!$this->ReadPropertyBoolean('Testmodus') || !is_string($wert)) {
throw new InvalidArgumentException(
'TestSignalRFrame ist nur im Testmodus zulaessig.'
);
}
$this->verarbeiteSignalRRahmen($wert);
return;
}
throw new InvalidArgumentException('Unbekannte Aktion: ' . $ident);
@@ -281,8 +307,10 @@ class EaseeGateway extends IPSModule
if ($letzterEmpfang > 0 && ($jetzt - $letzterEmpfang) <= 90) {
return;
}
$this->SetBuffer('SignalRReady', '0');
$this->setzeVerbunden(false);
$this->planeWiederverbindung(
'Seit mehr als 90 Sekunden keine Easee-Ereignisdaten empfangen.'
);
return;
}
if (($jetzt - $letzteAushandlung) >= 45) {
@@ -295,16 +323,22 @@ class EaseeGateway extends IPSModule
private function verbindeSignalR(): bool
{
$this->SetTimerInterval('ReconnectTimer', 0);
if ($this->ReadPropertyBoolean('Testmodus')) {
$this->SetStatus(102);
$this->setzeVerbunden(true);
$this->setzeEreignisverbindung(true);
return true;
}
if (!$this->stelleAccessTokenSicher()) {
$this->SetBuffer('SignalRReady', '0');
$this->setzeVerbunden(false);
$this->SetStatus(202);
return false;
}
$this->setzeEreignisverbindung(
false,
'Easee-Ereignisverbindung wird aufgebaut.'
);
$this->SetBuffer('LastNegotiation', (string) time());
$token = $this->GetBuffer('AccessToken');
$antwort = $this->httpAnfrage(
@@ -323,11 +357,11 @@ class EaseeGateway extends IPSModule
);
}
if (!$antwort['success']) {
$this->SetStatus(203);
$this->setzeLetztenFehler(
$this->setzeEreignisverbindung(
false,
'Easee-Ereignisverbindung fehlgeschlagen: HTTP '
. $antwort['httpCode']
. ($antwort['error'] !== '' ? ' / ' . $antwort['error'] : '')
. $antwort['httpCode']
. ($antwort['error'] !== '' ? ' / ' . $antwort['error'] : '')
);
return false;
}
@@ -337,8 +371,10 @@ class EaseeGateway extends IPSModule
? (string) ($daten['connectionToken'] ?? $daten['connectionId'] ?? '')
: '';
if ($verbindungstoken === '') {
$this->SetStatus(203);
$this->setzeLetztenFehler('Easee liefert kein Verbindungstoken.');
$this->setzeEreignisverbindung(
false,
'Easee liefert kein Verbindungstoken.'
);
return false;
}
@@ -413,10 +449,7 @@ class EaseeGateway extends IPSModule
private function verarbeiteSignalRRahmen(string $rahmen): void
{
if ($rahmen === '{}') {
$this->SetBuffer('SignalRReady', '1');
$this->SetStatus(102);
$this->setzeLetztenFehler('');
$this->setzeVerbunden(true);
$this->setzeEreignisverbindung(true);
$this->sendeAlleAbonnements();
return;
}
@@ -427,8 +460,9 @@ class EaseeGateway extends IPSModule
return;
}
if (isset($nachricht['error'])) {
$this->SetStatus(203);
$this->setzeLetztenFehler('Easee-Ereignisfehler: ' . $nachricht['error']);
$this->planeWiederverbindung(
'Easee-Ereignisfehler: ' . $nachricht['error']
);
return;
}
@@ -437,9 +471,9 @@ class EaseeGateway extends IPSModule
return;
}
if ($typ === 7) {
$this->SetBuffer('SignalRReady', '0');
$this->setzeVerbunden(false);
$this->setzeLetztenFehler('Easee-Ereignisverbindung wurde beendet.');
$this->planeWiederverbindung(
'Easee-Ereignisverbindung wurde beendet.'
);
return;
}
@@ -868,6 +902,31 @@ class EaseeGateway extends IPSModule
];
}
private function setzeEreignisverbindung(
bool $verbunden,
string $fehler = ''
): void {
$this->SetBuffer('SignalRReady', $verbunden ? '1' : '0');
$this->SetStatus($verbunden ? 102 : 203);
$this->setzeLetztenFehler($verbunden ? '' : $fehler);
if ($verbunden) {
$this->SetTimerInterval('ReconnectTimer', 0);
}
$this->setzeVerbunden($verbunden);
}
private function planeWiederverbindung(string $fehler): void
{
$this->setzeEreignisverbindung(false, $fehler);
$this->SetBuffer('LastNegotiation', '0');
if (
$this->ReadPropertyBoolean('Active')
&& !$this->ReadPropertyBoolean('Testmodus')
) {
$this->SetTimerInterval('ReconnectTimer', 1000);
}
}
private function setzeVerbunden(bool $verbunden): void
{
$warVerbunden = (bool) $this->GetValue('Connected');
+32
View File
@@ -24,6 +24,38 @@ return static function (TestContext $test): void {
);
});
$test->runCase('SignalR-Abbruch synchronisiert den Gatewaystatus', static function (
TestContext $test
) use ($gateway): void {
IPS_RequestAction(
$gateway,
'TestSignalRFrame',
json_encode(['type' => 7], JSON_THROW_ON_ERROR)
);
$test->assertInstanceStatus($gateway, 203);
$test->assertSame(
false,
GetValue($test->objectByIdent('Connected', $gateway))
);
$test->assertSame(
'Easee-Ereignisverbindung wurde beendet.',
GetValue($test->objectByIdent('LastError', $gateway))
);
IPS_RequestAction($gateway, 'TestSignalRFrame', '{}');
$test->assertInstanceStatus($gateway);
$test->assertSame(
true,
GetValue($test->objectByIdent('Connected', $gateway))
);
$test->assertSame(
'',
GetValue($test->objectByIdent('LastError', $gateway))
);
});
$test->runCase('Abonnement liefert injizierten API-Zustand', static function (
TestContext $test
) use ($gateway): void {
@@ -184,6 +184,32 @@ return static function (TestContext $test): void {
$test->assertInstanceStatus($station);
});
$test->runCase('SignalR-Abbruch erreicht die Ladestation', static function (
TestContext $test
) use ($gateway, $station): void {
IPS_RequestAction(
$gateway,
'TestSignalRFrame',
json_encode(['type' => 7], JSON_THROW_ON_ERROR)
);
$test->assertInstanceStatus($gateway, 203);
$test->assertInstanceStatus($station, 202);
$test->assertSame(
false,
GetValue($test->objectByIdent('GatewayVerbunden', $station))
);
IPS_RequestAction($gateway, 'TestSignalRFrame', '{}');
$test->assertInstanceStatus($gateway);
$test->assertInstanceStatus($station);
$test->assertSame(
true,
GetValue($test->objectByIdent('GatewayVerbunden', $station))
);
});
$test->runCase('Gateway-Ausfall verwirft Fahrzeug- und Phasenstatus', static function (
TestContext $test
) use ($gateway, $station): void {