Verbrauchermodule und betriebsartabhängige Angebote ergänzen
Tests / test (push) Successful in 42s

This commit is contained in:
dh
2026-09-21 18:46:17 +00:00
parent 7b6d1b2f07
commit 3c71626b5f
46 changed files with 4513 additions and 311 deletions
+763
View File
@@ -0,0 +1,763 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../libs/VerbraucherSchnittstelle.php';
require_once __DIR__ . '/../libs/VerbraucherBasisTrait.php';
require_once __DIR__ . '/../libs/Nachrichtenvertrag.php';
require_once __DIR__ . '/../libs/LadestationAdapter.php';
require_once __DIR__ . '/../libs/LadestationRegler.php';
use Belevo\EnelixEMS\LadestationAdapter;
use Belevo\EnelixEMS\LadestationRegler;
use Belevo\EnelixEMS\LadestationTransport;
use Belevo\EnelixEMS\Nachrichtenvertrag;
use Belevo\EnelixEMS\VerbraucherBasisTrait;
use Belevo\EnelixEMS\VerbraucherSchnittstelle;
class LadestationStandAlone extends IPSModule implements VerbraucherSchnittstelle
{
use VerbraucherBasisTrait;
private const MANAGER_MODULE_ID = '{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}';
private const STATUS_AKTIV = 102;
private const STATUS_KONFIGURATION_UNGUELTIG = 201;
private const STATUS_KOMMUNIKATIONSFEHLER = 202;
/** @var list<string> */
private const DIAGNOSE_VARIABLEN = [
'Istleistung',
'Leistungsquelle',
'Sollleistung',
'SollwertGueltig',
'Verfuegbar',
'AenderungMoeglich',
'Stoerung',
'Stoertext',
'LetzterGeraetebefehl',
'LeistungsangebotDiagnose',
];
public function Create(): void
{
parent::Create();
$this->registriereVerbraucherBasis();
$this->RegisterPropertyInteger('Geraetetyp', 0);
$this->RegisterPropertyString('Geraeteadresse', '');
$this->RegisterPropertyString('GeraeteID', '');
$this->RegisterPropertyString('Seriennummer', '');
$this->RegisterPropertyString('Benutzername', '');
$this->RegisterPropertyString('Passwort', '');
$this->RegisterPropertyInteger('MaximalerLadestrom', 16);
$this->RegisterPropertyInteger('Abfrageintervall', 5);
$this->RegisterPropertyBoolean('Ladefreigabe', false);
$this->RegisterPropertyBoolean('Solarladen', true);
$this->RegisterPropertyBoolean('DiagnosevariablenAnzeigen', false);
// Ausschliesslich fuer den automatisierten Symcon-Funktionstest.
$this->RegisterPropertyBoolean('Testmodus', false);
$this->RegisterPropertyString('Testantwort', '');
$this->RegisterVariableBoolean('FahrzeugVerbunden', 'Fahrzeug verbunden', '~Switch', 100);
$this->RegisterVariableBoolean('FahrzeugGeladen', 'Fahrzeug geladen', '~Switch', 110);
$this->RegisterVariableInteger('Fahrzeugstatus', 'Fahrzeugstatus', '', 120);
$this->RegisterVariableFloat('Ladestrom', 'Ladestrom', '', 130);
$this->RegisterVariableInteger('Phasenzahl', 'Phasenzahl', '', 140);
$this->RegisterAttributeBoolean('InitialwerteGesetzt', false);
$this->RegisterAttributeBoolean('KonfigurationLadefreigabe', false);
$this->RegisterAttributeBoolean('KonfigurationSolarladen', true);
$this->RegisterAttributeBoolean('ZustandLadefreigabe', false);
$this->RegisterAttributeBoolean('ZustandSolarladen', true);
$this->RegisterAttributeBoolean('ZustandFahrzeugVerbunden', false);
$this->RegisterAttributeBoolean('ZustandFahrzeugGeladen', false);
$this->RegisterAttributeInteger('ZustandFahrzeugstatus', 0);
$this->RegisterAttributeFloat('ZustandLadestrom', 0.0);
$this->RegisterAttributeInteger('ZustandPhasenzahl', 0);
$this->RegisterAttributeFloat('ZustandIstleistung', 0.0);
$this->RegisterAttributeInteger('ZustandSollleistung', 0);
$this->RegisterAttributeBoolean('ZustandSollwertGueltig', false);
$this->RegisterAttributeBoolean('ZustandVerfuegbar', false);
$this->RegisterAttributeBoolean('ZustandAenderungMoeglich', false);
$this->RegisterAttributeBoolean('ZustandStoerung', false);
$this->RegisterAttributeString('ZustandStoertext', '');
$this->RegisterAttributeString('Leistungsangebot', '[0]');
$this->RegisterAttributeString('Betriebsart', Nachrichtenvertrag::BETRIEBSART_PV);
$this->RegisterAttributeInteger('LetzteManagerID', 0);
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
$this->RegisterAttributeInteger('LetzterGesetzterStrom', -1);
$this->RegisterTimer(
'Statuszyklus',
0,
"IPS_RequestAction(\$_IPS['TARGET'], 'Statuszyklus', false);"
);
$this->RegisterTimer(
'Meldezyklus',
0,
"IPS_RequestAction(\$_IPS['TARGET'], 'Melden', false);"
);
$this->RegisterTimer(
'RueckmeldungVerzoegert',
0,
"IPS_RequestAction(\$_IPS['TARGET'], 'Melden', true);"
);
$this->RegisterTimer(
'VorgabeTimeout',
0,
"IPS_RequestAction(\$_IPS['TARGET'], 'VorgabeTimeout', false);"
);
}
public function ApplyChanges(): void
{
parent::ApplyChanges();
$this->initialisiereLokaleEinstellungen();
$this->aktualisiereVariablen();
try {
$this->pruefeKonfiguration();
} catch (Throwable $fehler) {
$this->deaktiviereTimer();
$this->setzeZustand('Verfuegbar', false);
$this->setzeZustand('AenderungMoeglich', false);
$this->setzeStoerung($fehler->getMessage());
$this->SetStatus(self::STATUS_KONFIGURATION_UNGUELTIG);
$this->protokolliere('Konfiguration', $fehler->getMessage());
return;
}
$this->SetTimerInterval('Statuszyklus', $this->ReadPropertyInteger('Abfrageintervall') * 1000);
$this->SetTimerInterval('Meldezyklus', $this->ReadPropertyInteger('Meldeintervall') * 1000);
$this->aktualisiere(true);
}
public function RequestAction($ident, $wert): void
{
switch ($ident) {
case 'Aktiv':
$this->SetValue('Aktiv', (bool) $wert);
if (!(bool) $wert) {
$this->setzeZustand('SollwertGueltig', false);
}
$this->aktualisiere(true);
return;
case 'Ladefreigabe':
case 'Solarladen':
$this->WriteAttributeBoolean('Zustand' . $ident, (bool) $wert);
$this->setzeSichtbareVariable($ident, (bool) $wert);
$this->setzeZustand('SollwertGueltig', false);
$this->aktualisiere(true);
return;
case 'Statuszyklus':
$this->aktualisiere(true);
return;
case 'VorgabeTimeout':
$this->SetTimerInterval('VorgabeTimeout', 0);
$this->setzeZustand('SollwertGueltig', false);
$this->aktualisiere(true);
return;
case 'Melden':
if ((bool) $wert) {
$this->SetTimerInterval('RueckmeldungVerzoegert', 0);
}
$this->aktualisiere(false);
$this->sendeVerbraucherdaten();
return;
case 'ManagerdatenEmpfangen':
if (!is_string($wert)) {
throw new InvalidArgumentException('Managerdaten muessen als JSON uebergeben werden.');
}
$daten = json_decode($wert, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($daten)) {
throw new InvalidArgumentException('Managerdaten muessen ein JSON-Objekt sein.');
}
$this->ManagerdatenEmpfangen($daten);
return;
}
throw new InvalidArgumentException('Unbekannte Aktion: ' . $ident);
}
/** @param array<string, mixed> $daten */
public function ManagerdatenEmpfangen(array $daten): void
{
Nachrichtenvertrag::pruefeManagerdaten($daten);
if ($daten['Kopf']['EmpfaengerID'] !== $this->InstanceID) {
throw new InvalidArgumentException('Managerdaten sind an eine andere Instanz adressiert.');
}
$managerID = $daten['Kopf']['AbsenderID'];
if (!in_array($managerID, $this->zugeordneteManagerIDs(), true)) {
throw new InvalidArgumentException('Der Manager hat diese Ladestation nicht aktiv zugeordnet.');
}
$this->WriteAttributeString('Betriebsart', $daten['Betriebsart']);
$this->aktualisiere(false, false);
$sollleistung = $daten['Sollleistung_W'];
if ($sollleistung === null) {
if ((bool) $this->leseZustand('SollwertGueltig')
&& !in_array((int) $this->leseZustand('Sollleistung'), $this->leseLeistungsangebot(), true)
) {
$this->setzeZustand('SollwertGueltig', false);
$this->aktualisiere(false, false);
}
$this->SetTimerInterval('RueckmeldungVerzoegert', 100);
return;
}
if (!in_array($sollleistung, $this->leseLeistungsangebot(), true)) {
throw new InvalidArgumentException(
'Sollleistung_W liegt nicht im aktuell gemeldeten Leistungsangebot.'
);
}
$this->WriteAttributeInteger('LetzteManagerID', $managerID);
$this->WriteAttributeInteger('LetzteVorgabeZeit', time());
$this->setzeZustand('Sollleistung', $sollleistung);
$this->setzeZustand('SollwertGueltig', true);
$this->SetTimerInterval('VorgabeTimeout', $this->ReadPropertyInteger('VorgabeTimeout') * 1000);
$this->aktualisiere(false, false);
$this->SetTimerInterval('RueckmeldungVerzoegert', 100);
}
private function aktualisiere(bool $meldungPlanen, bool $statusLesen = true): void
{
try {
if ($statusLesen) {
$this->leseGeraetestatus();
}
$aktiv = (bool) $this->GetValue('Aktiv');
$ladefreigabe = $this->ReadAttributeBoolean('ZustandLadefreigabe');
$verbunden = $this->ReadAttributeBoolean('ZustandFahrzeugVerbunden');
$geladen = $this->ReadAttributeBoolean('ZustandFahrzeugGeladen');
$phasenzahl = $this->ReadAttributeInteger('ZustandPhasenzahl');
$maximalstrom = $this->ReadPropertyInteger('MaximalerLadestrom');
$solarladen = $this->ReadAttributeBoolean('ZustandSolarladen');
$peakbetrieb = $this->ReadAttributeString('Betriebsart')
=== Nachrichtenvertrag::BETRIEBSART_PEAK;
$verfuegbar = LadestationRegler::leistungsangebot(
$aktiv,
$ladefreigabe,
$verbunden,
$geladen,
$phasenzahl,
$maximalstrom,
$solarladen,
false
) !== [0];
$angebot = LadestationRegler::leistungsangebot(
$aktiv,
$ladefreigabe,
$verbunden,
$geladen,
$phasenzahl,
$maximalstrom,
$solarladen,
$peakbetrieb
);
$angebotJson = json_encode($angebot, JSON_THROW_ON_ERROR);
$this->WriteAttributeString('Leistungsangebot', $angebotJson);
$this->setzeSichtbareVariable('LeistungsangebotDiagnose', $angebotJson);
$this->setzeZustand('Verfuegbar', $verfuegbar);
$this->setzeZustand('AenderungMoeglich', $verfuegbar && count($angebot) > 1);
$sollleistung = $this->bestimmeWirksameSollleistung($angebot);
$this->setzeLadestrom($sollleistung);
$this->setzeStoerung('');
$this->SetStatus(self::STATUS_AKTIV);
} catch (Throwable $fehler) {
$this->setzeZustand('Verfuegbar', false);
$this->setzeZustand('AenderungMoeglich', false);
$this->setzeStoerung($fehler->getMessage());
$this->SetStatus(self::STATUS_KOMMUNIKATIONSFEHLER);
$this->protokolliere('Geraetekommunikation', $fehler->getMessage());
}
if ($meldungPlanen) {
$this->SetTimerInterval('RueckmeldungVerzoegert', 100);
}
}
private function leseGeraetestatus(): void
{
$transport = new LadestationTransport(
fn (array $anfrage): string => $this->fuehreHttpAnfrageAus($anfrage)
);
$antwort = $transport->statusLesen(
$this->ReadPropertyInteger('Geraetetyp'),
$this->geraetekonfiguration()
);
$rohstatus = LadestationAdapter::dekodiereStatus(
$this->ReadPropertyInteger('Geraetetyp'),
$antwort
);
$status = LadestationRegler::normalisiereStatus(
$rohstatus,
$this->ReadAttributeBoolean('ZustandFahrzeugVerbunden')
);
foreach ($status as $ident => $wert) {
$this->setzeZustand($ident, $wert);
}
}
/** @param list<int> $angebot */
private function bestimmeWirksameSollleistung(array $angebot): int
{
if ((bool) $this->leseZustand('SollwertGueltig')) {
$sollleistung = (int) $this->leseZustand('Sollleistung');
if (in_array($sollleistung, $angebot, true)) {
return $sollleistung;
}
$this->setzeZustand('SollwertGueltig', false);
}
if (!$this->ReadAttributeBoolean('ZustandSolarladen') && $angebot !== [0]) {
return $angebot[count($angebot) - 1];
}
return 0;
}
private function setzeLadestrom(int $leistung): void
{
$phasenzahl = $this->ReadAttributeInteger('ZustandPhasenzahl');
$strom = $leistung === 0
? 0
: LadestationRegler::stromFuerLeistung($leistung, $phasenzahl);
if ($strom === $this->ReadAttributeInteger('LetzterGesetzterStrom')) {
return;
}
$transport = new LadestationTransport(
fn (array $anfrage): string => $this->fuehreHttpAnfrageAus($anfrage)
);
$transport->ladestromSetzen(
$this->ReadPropertyInteger('Geraetetyp'),
$this->geraetekonfiguration(),
$strom
);
$this->WriteAttributeInteger('LetzterGesetzterStrom', $strom);
}
/**
* @param array<string, mixed> $anfrage
*/
private function fuehreHttpAnfrageAus(array $anfrage): string
{
$protokoll = [
'Methode' => $anfrage['Methode'],
'URL' => $anfrage['URL'],
];
$this->setzeSichtbareVariable(
'LetzterGeraetebefehl',
json_encode($protokoll, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR)
);
if ($this->ReadPropertyBoolean('Testmodus')) {
$url = (string) $anfrage['URL'];
if (str_ends_with($url, '/api/status')
|| str_ends_with($url, '/mqtt?payload=')
|| str_contains($url, '/pico/charging/')
) {
return $this->ReadPropertyString('Testantwort');
}
return '{}';
}
$curl = curl_init();
if ($curl === false) {
throw new RuntimeException('HTTP-Anfrage konnte nicht initialisiert werden.');
}
curl_setopt($curl, CURLOPT_URL, (string) $anfrage['URL']);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($curl, CURLOPT_TIMEOUT, 10);
if ($anfrage['Methode'] === 'POST') {
curl_setopt($curl, CURLOPT_POST, true);
}
if (isset($anfrage['Benutzername'], $anfrage['Passwort'])) {
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt(
$curl,
CURLOPT_USERPWD,
(string) $anfrage['Benutzername'] . ':' . (string) $anfrage['Passwort']
);
}
$antwort = curl_exec($curl);
if ($antwort === false) {
$fehler = curl_error($curl);
curl_close($curl);
throw new RuntimeException('HTTP-Anfrage fehlgeschlagen: ' . $fehler);
}
$status = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($status >= 400) {
throw new RuntimeException('Geraet antwortet mit HTTP-Status ' . $status . '.');
}
return (string) $antwort;
}
/** @return array<string, string> */
private function geraetekonfiguration(): array
{
return [
'Geraeteadresse' => $this->ReadPropertyString('Geraeteadresse'),
'GeraeteID' => $this->ReadPropertyString('GeraeteID'),
'Seriennummer' => $this->ReadPropertyString('Seriennummer'),
'Benutzername' => $this->ReadPropertyString('Benutzername'),
'Passwort' => $this->ReadPropertyString('Passwort'),
];
}
private function sendeVerbraucherdaten(): void
{
foreach ($this->zugeordneteManagerIDs() as $managerID) {
$daten = $this->baueVerbraucherdaten($managerID);
Nachrichtenvertrag::pruefeVerbraucherdaten($daten);
try {
IPS_RequestAction(
$managerID,
'VerbraucherdatenEmpfangen',
json_encode($daten, JSON_THROW_ON_ERROR)
);
} catch (Throwable $fehler) {
$this->protokolliere('Managerkommunikation', $fehler->getMessage());
}
}
}
/** @return array<string, mixed> */
private function baueVerbraucherdaten(int $managerID): array
{
$stoerung = (bool) $this->leseZustand('Stoerung');
return [
'Kopf' => [
'Version' => Nachrichtenvertrag::VERSION,
'AbsenderID' => $this->InstanceID,
'EmpfaengerID' => $managerID,
'Zeitpunkt' => time(),
],
'Betriebsart' => $this->ReadAttributeString('Betriebsart'),
'PrioritaetPV' => $this->ReadPropertyInteger('PrioritaetPV'),
'PrioritaetPeak' => $this->ReadPropertyInteger('PrioritaetPeak'),
'Leistungswerte_W' => $this->leseLeistungsangebot(),
'AenderungMoeglich' => (bool) $this->leseZustand('AenderungMoeglich'),
'Verfuegbar' => (bool) $this->leseZustand('Verfuegbar'),
'Istleistung_W' => (float) $this->leseZustand('Istleistung'),
'Leistungsquelle' => Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN,
'Zustand' => [
[
'Kennung' => 'Sollleistung_W',
'Art' => 'Sollwert',
'Wert' => (bool) $this->leseZustand('SollwertGueltig')
? (int) $this->leseZustand('Sollleistung')
: null,
'Einheit' => 'W',
],
[
'Kennung' => 'FahrzeugVerbunden',
'Art' => 'Status',
'Wert' => (bool) $this->leseZustand('FahrzeugVerbunden'),
'Einheit' => '',
],
[
'Kennung' => 'FahrzeugGeladen',
'Art' => 'Status',
'Wert' => (bool) $this->leseZustand('FahrzeugGeladen'),
'Einheit' => '',
],
[
'Kennung' => 'Fahrzeugstatus',
'Art' => 'Status',
'Wert' => (int) $this->leseZustand('Fahrzeugstatus'),
'Einheit' => '',
],
[
'Kennung' => 'Phasenzahl',
'Art' => 'Status',
'Wert' => (int) $this->leseZustand('Phasenzahl'),
'Einheit' => '',
],
[
'Kennung' => 'Ladestrom_A',
'Art' => 'Istwert',
'Wert' => (float) $this->leseZustand('Ladestrom'),
'Einheit' => 'A',
],
[
'Kennung' => 'Ladefreigabe',
'Art' => 'Status',
'Wert' => $this->ReadAttributeBoolean('ZustandLadefreigabe'),
'Einheit' => '',
],
[
'Kennung' => 'Solarladen',
'Art' => 'Status',
'Wert' => $this->ReadAttributeBoolean('ZustandSolarladen'),
'Einheit' => '',
],
[
'Kennung' => 'Ladefehler',
'Art' => 'Stoerung',
'Wert' => $stoerung,
'Einheit' => '',
'Text' => (string) $this->leseZustand('Stoertext'),
],
],
];
}
/** @return list<int> */
private function zugeordneteManagerIDs(): array
{
$ergebnis = [];
foreach (IPS_GetInstanceListByModuleID(self::MANAGER_MODULE_ID) as $managerID) {
try {
$automatisch = (bool) IPS_GetProperty($managerID, 'AutomatischeSuche');
$property = $automatisch ? 'AutomatischeVerbraucherZuordnung' : 'VerbraucherZuordnung';
$zuordnung = $this->leseManagerZuordnung($managerID, $property);
if ($automatisch && $zuordnung === []) {
$zuordnung = $this->leseManagerZuordnung($managerID, 'VerbraucherZuordnung');
}
} catch (Throwable $fehler) {
continue;
}
foreach ($zuordnung as $eintrag) {
if (!is_array($eintrag) || ($eintrag['Aktiv'] ?? false) !== true) {
continue;
}
$instanzID = $eintrag['InstanzID'] ?? $eintrag['Verbraucher'] ?? null;
if ($instanzID === $this->InstanceID) {
$ergebnis[] = (int) $managerID;
break;
}
}
}
return array_values(array_unique($ergebnis));
}
/** @return array<mixed> */
private function leseManagerZuordnung(int $managerID, string $property): array
{
$zuordnung = json_decode(
(string) IPS_GetProperty($managerID, $property),
true,
512,
JSON_THROW_ON_ERROR
);
return is_array($zuordnung) ? $zuordnung : [];
}
private function pruefeKonfiguration(): void
{
foreach (['PrioritaetPV', 'PrioritaetPeak'] as $property) {
if ($this->ReadPropertyInteger($property) < 0) {
throw new InvalidArgumentException($property . ' muss mindestens 0 sein.');
}
}
foreach (['Meldeintervall', 'VorgabeTimeout', 'Abfrageintervall'] as $property) {
if ($this->ReadPropertyInteger($property) <= 0) {
throw new InvalidArgumentException($property . ' muss groesser als 0 sein.');
}
}
$maximalstrom = $this->ReadPropertyInteger('MaximalerLadestrom');
if ($maximalstrom < 6 || $maximalstrom > 32) {
throw new InvalidArgumentException('MaximalerLadestrom muss zwischen 6 und 32 A liegen.');
}
$geraetetyp = $this->ReadPropertyInteger('Geraetetyp');
$konfiguration = $this->geraetekonfiguration();
LadestationAdapter::statusAnfrage($geraetetyp, $konfiguration);
LadestationAdapter::steuerAnfragen($geraetetyp, $konfiguration, 0);
}
private function initialisiereLokaleEinstellungen(): void
{
$initialisiert = $this->ReadAttributeBoolean('InitialwerteGesetzt');
$ladefreigabe = $this->ReadPropertyBoolean('Ladefreigabe');
$solarladen = $this->ReadPropertyBoolean('Solarladen');
if (!$initialisiert
|| $ladefreigabe !== $this->ReadAttributeBoolean('KonfigurationLadefreigabe')
) {
$this->WriteAttributeBoolean('ZustandLadefreigabe', $ladefreigabe);
}
if (!$initialisiert
|| $solarladen !== $this->ReadAttributeBoolean('KonfigurationSolarladen')
) {
$this->WriteAttributeBoolean('ZustandSolarladen', $solarladen);
}
$this->WriteAttributeBoolean('KonfigurationLadefreigabe', $ladefreigabe);
$this->WriteAttributeBoolean('KonfigurationSolarladen', $solarladen);
$this->WriteAttributeBoolean('InitialwerteGesetzt', true);
}
private function aktualisiereVariablen(): void
{
if ($this->ReadPropertyBoolean('EinstellungenInVisu')) {
$this->RegisterVariableBoolean('Ladefreigabe', 'Ladefreigabe', '~Switch', 150);
$this->RegisterVariableBoolean('Solarladen', 'Solarladen', '~Switch', 160);
$this->EnableAction('Ladefreigabe');
$this->EnableAction('Solarladen');
$this->SetValue('Ladefreigabe', $this->ReadAttributeBoolean('ZustandLadefreigabe'));
$this->SetValue('Solarladen', $this->ReadAttributeBoolean('ZustandSolarladen'));
} else {
$this->entferneVariable('Ladefreigabe');
$this->entferneVariable('Solarladen');
}
if ($this->ReadPropertyBoolean('DiagnosevariablenAnzeigen')) {
$this->registriereVerbraucherDiagnose();
$this->RegisterVariableString('LetzterGeraetebefehl', 'Letzter Geraetebefehl', '', 200);
$this->RegisterVariableString('LeistungsangebotDiagnose', 'Leistungsangebot', '', 210);
$this->SetValue(
'LeistungsangebotDiagnose',
$this->ReadAttributeString('Leistungsangebot')
);
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
if (!in_array($ident, ['LetzterGeraetebefehl', 'LeistungsangebotDiagnose'], true)) {
$this->setzeSichtbareVariable($ident, $this->leseZustand($ident));
}
}
} else {
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
$this->entferneVariable($ident);
}
}
}
private function entferneVariable(string $ident): void
{
$id = @$this->GetIDForIdent($ident);
if (is_int($id) && $id > 0 && IPS_VariableExists($id)) {
$this->UnregisterVariable($ident);
}
}
/** @param mixed $wert */
private function setzeZustand(string $ident, $wert): void
{
switch ($ident) {
case 'FahrzeugVerbunden':
case 'FahrzeugGeladen':
case 'SollwertGueltig':
case 'Verfuegbar':
case 'AenderungMoeglich':
case 'Stoerung':
$this->WriteAttributeBoolean('Zustand' . $ident, (bool) $wert);
break;
case 'Fahrzeugstatus':
case 'Phasenzahl':
case 'Sollleistung':
$this->WriteAttributeInteger('Zustand' . $ident, (int) $wert);
break;
case 'Ladestrom_A':
$ident = 'Ladestrom';
$this->WriteAttributeFloat('ZustandLadestrom', (float) $wert);
break;
case 'Istleistung_W':
$ident = 'Istleistung';
$this->WriteAttributeFloat('ZustandIstleistung', (float) $wert);
break;
case 'Stoertext':
$this->WriteAttributeString('ZustandStoertext', (string) $wert);
break;
default:
throw new LogicException('Unbekannter Zustand: ' . $ident);
}
$this->setzeSichtbareVariable($ident, $wert);
if ($ident === 'Istleistung') {
$this->setzeSichtbareVariable(
'Leistungsquelle',
Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN
);
}
}
/** @return mixed */
private function leseZustand(string $ident)
{
if ($ident === 'Ladefreigabe' || $ident === 'Solarladen') {
return $this->ReadAttributeBoolean('Zustand' . $ident);
}
if (in_array($ident, [
'FahrzeugVerbunden', 'FahrzeugGeladen', 'SollwertGueltig',
'Verfuegbar', 'AenderungMoeglich', 'Stoerung',
], true)) {
return $this->ReadAttributeBoolean('Zustand' . $ident);
}
if (in_array($ident, ['Fahrzeugstatus', 'Phasenzahl', 'Sollleistung'], true)) {
return $this->ReadAttributeInteger('Zustand' . $ident);
}
if ($ident === 'Ladestrom' || $ident === 'Istleistung') {
return $this->ReadAttributeFloat('Zustand' . $ident);
}
if ($ident === 'Stoertext') {
return $this->ReadAttributeString('ZustandStoertext');
}
if ($ident === 'Leistungsquelle') {
return Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN;
}
throw new LogicException('Unbekannter Zustand: ' . $ident);
}
/** @param mixed $wert */
private function setzeSichtbareVariable(string $ident, $wert): void
{
$id = @$this->GetIDForIdent($ident);
if (is_int($id) && $id > 0 && IPS_VariableExists($id)) {
$this->SetValue($ident, $wert);
}
}
/** @return list<int> */
private function leseLeistungsangebot(): array
{
$angebot = json_decode(
$this->ReadAttributeString('Leistungsangebot'),
true,
512,
JSON_THROW_ON_ERROR
);
return is_array($angebot) ? array_map('intval', $angebot) : [0];
}
private function setzeStoerung(string $text): void
{
$this->setzeZustand('Stoerung', $text !== '');
$this->setzeZustand('Stoertext', $text);
}
private function deaktiviereTimer(): void
{
foreach (['Statuszyklus', 'Meldezyklus', 'RueckmeldungVerzoegert', 'VorgabeTimeout'] as $timer) {
$this->SetTimerInterval($timer, 0);
}
}
private function protokolliere(string $bereich, string $nachricht): void
{
if (!$this->ReadPropertyBoolean('LoggingEin')) {
return;
}
$this->SendDebug($bereich, $nachricht, 0);
}
}