1220 lines
48 KiB
PHP
1220 lines
48 KiB
PHP
<?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/WaermepumpenRegler.php';
|
|
|
|
use Belevo\EnelixEMS\Nachrichtenvertrag;
|
|
use Belevo\EnelixEMS\VerbraucherBasisTrait;
|
|
use Belevo\EnelixEMS\VerbraucherSchnittstelle;
|
|
use Belevo\EnelixEMS\WaermepumpenRegler;
|
|
|
|
class VerbraucherWaermepumpe 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_SCHALTFEHLER = 202;
|
|
private const VM_UPDATE = 10603;
|
|
|
|
/** @var list<string> */
|
|
private const DIAGNOSE_VARIABLEN = [
|
|
'Istleistung',
|
|
'Leistungsquelle',
|
|
'Sollleistung',
|
|
'SollwertGueltig',
|
|
'Verfuegbar',
|
|
'AenderungMoeglich',
|
|
'Stoerung',
|
|
'Stoertext',
|
|
'AnlaufAusstehend',
|
|
'WiederholsperreAktiv',
|
|
'SperrerholungAktiv',
|
|
'RestMindestlaufzeit',
|
|
'RestMindestsperrzeit',
|
|
'Sperrzeit24h',
|
|
];
|
|
|
|
public function Create(): void
|
|
{
|
|
parent::Create();
|
|
|
|
$this->registriereVerbraucherBasis();
|
|
$this->RegisterPropertyInteger('Kontaktart', WaermepumpenRegler::KONTAKTART_SPERRE_ERHOEHUNG);
|
|
$this->RegisterPropertyInteger('Kontakt1VariableID', 0);
|
|
$this->RegisterPropertyInteger('Kontakt2VariableID', 0);
|
|
$this->RegisterPropertyBoolean('Kontakt1Invertiert', false);
|
|
$this->RegisterPropertyBoolean('Kontakt2Invertiert', false);
|
|
$this->RegisterPropertyInteger('Nennleistung', 0);
|
|
$this->RegisterPropertyInteger('Rueckmeldungsart', WaermepumpenRegler::RUECKMELDUNG_LEISTUNG);
|
|
$this->RegisterPropertyInteger('IstleistungVariableID', 0);
|
|
$this->RegisterPropertyFloat('Laufschwelle', 100.0);
|
|
$this->RegisterPropertyInteger('Anlaufwartezeit', 30);
|
|
$this->RegisterPropertyInteger('Wiederholsperre', 300);
|
|
$this->RegisterPropertyInteger('Mindestlaufzeit', 1200);
|
|
$this->RegisterPropertyInteger('Mindestsperrzeit', 1200);
|
|
$this->RegisterPropertyInteger('MaximaleSperrzeit', 7200);
|
|
$this->RegisterPropertyInteger('MaximaleSperrzeit24h', 21600);
|
|
$this->RegisterPropertyBoolean('DiagnosevariablenAnzeigen', false);
|
|
|
|
$this->RegisterVariableInteger('Waermepumpenzustand', 'Waermepumpenzustand', '', 100);
|
|
$this->RegisterVariableBoolean('WaermepumpeLaeuft', 'Waermepumpe laeuft', '~Switch', 110);
|
|
$this->RegisterVariableBoolean('SperreAktiv', 'Sperre aktiv', '~Switch', 120);
|
|
$this->RegisterVariableBoolean('ErhoehungAktiv', 'Erhoehung aktiv', '~Switch', 130);
|
|
$this->RegisterVariableInteger('SGReadyZustand', 'SG-Ready-Zustand', '', 140);
|
|
$this->RegisterVariableInteger('Laufzeit', 'Bestaetigte Laufzeit', '', 150);
|
|
|
|
foreach (['Kontakt1', 'Kontakt2', 'Istleistung'] as $name) {
|
|
$this->RegisterAttributeInteger('Registrierte' . $name . 'ID', 0);
|
|
}
|
|
$this->RegisterAttributeInteger('LetzteManagerID', 0);
|
|
$this->RegisterAttributeInteger('LetzteVorgabeZeit', 0);
|
|
$this->RegisterAttributeString('Betriebsart', Nachrichtenvertrag::BETRIEBSART_PV);
|
|
$this->RegisterAttributeString('Leistungsangebot', '[0]');
|
|
$this->RegisterAttributeBoolean('Initialisiert', false);
|
|
$this->RegisterAttributeBoolean('SchaltvorgangLaeuft', false);
|
|
$this->RegisterAttributeString('Schaltfehler', '');
|
|
$this->RegisterAttributeInteger('Steuerzustand', WaermepumpenRegler::STEUERZUSTAND_NORMAL);
|
|
$this->RegisterAttributeBoolean('LetzterLaufstatus', false);
|
|
$this->RegisterAttributeInteger('Laufbeginn', 0);
|
|
$this->RegisterAttributeInteger('LaufzeitStand', 0);
|
|
$this->RegisterAttributeInteger('Sperrbeginn', 0);
|
|
$this->RegisterAttributeString('Sperrhistorie', '[]');
|
|
$this->RegisterAttributeBoolean('Sperrerholung', false);
|
|
$this->RegisterAttributeInteger('ErholungsLaufbeginn', 0);
|
|
$this->RegisterAttributeInteger('AnlaufDeadline', 0);
|
|
$this->RegisterAttributeInteger('WiederholsperreBis', 0);
|
|
|
|
$this->RegisterAttributeFloat('ZustandIstleistung', 0.0);
|
|
$this->RegisterAttributeInteger('ZustandLeistungsquelle', 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->RegisterAttributeBoolean('ZustandAnlaufAusstehend', false);
|
|
$this->RegisterAttributeBoolean('ZustandWiederholsperreAktiv', false);
|
|
$this->RegisterAttributeBoolean('ZustandSperrerholungAktiv', false);
|
|
$this->RegisterAttributeInteger('ZustandRestMindestlaufzeit', 0);
|
|
$this->RegisterAttributeInteger('ZustandRestMindestsperrzeit', 0);
|
|
$this->RegisterAttributeInteger('ZustandSperrzeit24h', 0);
|
|
|
|
$this->RegisterTimer(
|
|
'Meldezyklus',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'Melden', false);"
|
|
);
|
|
$this->RegisterTimer(
|
|
'MeldungVerzoegert',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'Melden', true);"
|
|
);
|
|
$this->RegisterTimer(
|
|
'Zustandspruefung',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'Aktualisieren', 0);"
|
|
);
|
|
$this->RegisterTimer(
|
|
'VorgabeTimeout',
|
|
0,
|
|
"IPS_RequestAction(\$_IPS['TARGET'], 'VorgabeTimeout', 0);"
|
|
);
|
|
}
|
|
|
|
public function ApplyChanges(): void
|
|
{
|
|
parent::ApplyChanges();
|
|
|
|
$this->aktualisiereVariablen();
|
|
$this->registriereMeldungen();
|
|
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('Meldezyklus', $this->ReadPropertyInteger('Meldeintervall') * 1000);
|
|
$this->initialisiereZustand(time());
|
|
$this->aktualisiere(true);
|
|
}
|
|
|
|
public function MessageSink($zeitstempel, $senderID, $nachricht, $daten): void
|
|
{
|
|
if ((int) $nachricht !== self::VM_UPDATE || $this->ReadAttributeBoolean('SchaltvorgangLaeuft')) {
|
|
return;
|
|
}
|
|
|
|
$ids = [
|
|
$this->ReadPropertyInteger('Kontakt1VariableID'),
|
|
$this->ReadPropertyInteger('Kontakt2VariableID'),
|
|
$this->ReadPropertyInteger('IstleistungVariableID'),
|
|
];
|
|
if (in_array((int) $senderID, $ids, true)) {
|
|
$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->SetTimerInterval('VorgabeTimeout', 0);
|
|
}
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'Melden':
|
|
if ((bool) $wert) {
|
|
$this->SetTimerInterval('MeldungVerzoegert', 0);
|
|
}
|
|
$this->aktualisiere(false);
|
|
$this->sendeVerbraucherdaten();
|
|
return;
|
|
|
|
case 'Aktualisieren':
|
|
$this->SetTimerInterval('Zustandspruefung', 0);
|
|
$this->aktualisiere(true);
|
|
return;
|
|
|
|
case 'VorgabeTimeout':
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
$this->aktualisiere(true);
|
|
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 diesen Verbraucher nicht aktiv zugeordnet.');
|
|
}
|
|
|
|
$this->WriteAttributeString('Betriebsart', $daten['Betriebsart']);
|
|
$this->aktualisiere(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->planeMeldung();
|
|
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->planeVorgabeTimeout(time());
|
|
$this->aktualisiere(false);
|
|
$this->planeMeldung();
|
|
}
|
|
|
|
private function aktualisiere(bool $meldungPlanen): void
|
|
{
|
|
try {
|
|
$jetzt = time();
|
|
$rueckmeldung = $this->leseRueckmeldung();
|
|
$laeuft = WaermepumpenRegler::laeuft(
|
|
$rueckmeldung,
|
|
$this->ReadPropertyInteger('Rueckmeldungsart'),
|
|
$this->ReadPropertyFloat('Laufschwelle')
|
|
);
|
|
$istleistung = WaermepumpenRegler::istleistung(
|
|
$rueckmeldung,
|
|
$this->ReadPropertyInteger('Rueckmeldungsart'),
|
|
$this->ReadPropertyInteger('Nennleistung')
|
|
);
|
|
$this->pruefeKontaktzustand();
|
|
$this->aktualisiereLaufzustand($laeuft, $jetzt);
|
|
$this->pruefeVorgabeTimeout($jetzt);
|
|
|
|
$steuerzustand = $this->ReadAttributeInteger('Steuerzustand');
|
|
$this->aktualisiereSperrhistorie($steuerzustand, $jetzt);
|
|
$sperrzeit24h = $this->sperrsekunden24h($steuerzustand, $jetzt);
|
|
$maxAmStueckErreicht = $steuerzustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
&& $this->ReadPropertyInteger('MaximaleSperrzeit') > 0
|
|
&& $jetzt - $this->ReadAttributeInteger('Sperrbeginn')
|
|
>= $this->ReadPropertyInteger('MaximaleSperrzeit');
|
|
$max24hErreicht = $this->ReadPropertyInteger('MaximaleSperrzeit24h') > 0
|
|
&& $sperrzeit24h >= $this->ReadPropertyInteger('MaximaleSperrzeit24h');
|
|
if (($maxAmStueckErreicht || $max24hErreicht)
|
|
&& $steuerzustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
) {
|
|
$this->WriteAttributeBoolean('Sperrerholung', true);
|
|
$this->WriteAttributeInteger('ErholungsLaufbeginn', $laeuft ? $jetzt : 0);
|
|
$this->WriteAttributeInteger('Laufbeginn', $laeuft ? $jetzt : 0);
|
|
$this->schalte(WaermepumpenRegler::STEUERZUSTAND_NORMAL, $jetzt);
|
|
$steuerzustand = WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
}
|
|
|
|
$this->aktualisiereSperrerholung($laeuft, $jetzt);
|
|
$sperrerholung = $this->ReadAttributeBoolean('Sperrerholung');
|
|
$restMindestlaufzeit = $this->restMindestlaufzeit($laeuft, $jetzt);
|
|
$restMindestsperrzeit = $steuerzustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
? WaermepumpenRegler::restzeit(
|
|
$this->ReadAttributeInteger('Sperrbeginn'),
|
|
$this->ReadPropertyInteger('Mindestsperrzeit'),
|
|
$jetzt
|
|
)
|
|
: 0;
|
|
$wiederholsperreAktiv = $jetzt < $this->ReadAttributeInteger('WiederholsperreBis');
|
|
$startenZulaessig = !$wiederholsperreAktiv
|
|
&& !$sperrerholung
|
|
&& $restMindestsperrzeit === 0;
|
|
$sperrenZulaessig = !$sperrerholung
|
|
&& $restMindestlaufzeit === 0
|
|
&& !$max24hErreicht;
|
|
|
|
$aktiv = (bool) $this->GetValue('Aktiv');
|
|
$fehlerfrei = $this->ReadAttributeString('Schaltfehler') === '';
|
|
$verfuegbar = $aktiv && $fehlerfrei;
|
|
$angebot = WaermepumpenRegler::leistungsangebot(
|
|
$this->ReadPropertyInteger('Nennleistung'),
|
|
$this->ReadAttributeString('Betriebsart'),
|
|
$laeuft,
|
|
$verfuegbar,
|
|
$restMindestlaufzeit > 0,
|
|
$restMindestsperrzeit > 0,
|
|
$sperrenZulaessig,
|
|
$startenZulaessig,
|
|
$sperrerholung
|
|
);
|
|
$this->WriteAttributeString('Leistungsangebot', json_encode($angebot, JSON_THROW_ON_ERROR));
|
|
if ((bool) $this->leseZustand('SollwertGueltig')
|
|
&& !in_array((int) $this->leseZustand('Sollleistung'), $angebot, true)
|
|
) {
|
|
$this->setzeZustand('Sollleistung', $angebot[0]);
|
|
}
|
|
|
|
$zielzustand = $this->bestimmeZielzustand(
|
|
$aktiv,
|
|
$laeuft,
|
|
$restMindestlaufzeit,
|
|
$restMindestsperrzeit,
|
|
$sperrenZulaessig,
|
|
$sperrerholung
|
|
);
|
|
if ($zielzustand !== $steuerzustand) {
|
|
$this->schalte($zielzustand, $jetzt);
|
|
$steuerzustand = $zielzustand;
|
|
}
|
|
$this->aktualisiereAnlauf($laeuft, $steuerzustand, $jetzt);
|
|
$steuerzustand = $this->ReadAttributeInteger('Steuerzustand');
|
|
$sperrerholung = $this->ReadAttributeBoolean('Sperrerholung');
|
|
$wiederholsperreAktiv = $jetzt < $this->ReadAttributeInteger('WiederholsperreBis');
|
|
$startenZulaessig = !$wiederholsperreAktiv
|
|
&& !$sperrerholung
|
|
&& $restMindestsperrzeit === 0;
|
|
$sperrenZulaessig = !$sperrerholung
|
|
&& $restMindestlaufzeit === 0
|
|
&& !$max24hErreicht;
|
|
$angebot = WaermepumpenRegler::leistungsangebot(
|
|
$this->ReadPropertyInteger('Nennleistung'),
|
|
$this->ReadAttributeString('Betriebsart'),
|
|
$laeuft,
|
|
$verfuegbar,
|
|
$restMindestlaufzeit > 0,
|
|
$restMindestsperrzeit > 0,
|
|
$sperrenZulaessig,
|
|
$startenZulaessig,
|
|
$sperrerholung
|
|
);
|
|
$this->WriteAttributeString(
|
|
'Leistungsangebot',
|
|
json_encode($angebot, JSON_THROW_ON_ERROR)
|
|
);
|
|
|
|
$aenderungMoeglich = $verfuegbar
|
|
&& count($angebot) > 1
|
|
&& !$wiederholsperreAktiv
|
|
&& !$sperrerholung;
|
|
$this->setzeZustand('Istleistung', $istleistung);
|
|
$this->setzeZustand(
|
|
'Leistungsquelle',
|
|
$this->ReadPropertyInteger('Rueckmeldungsart') === WaermepumpenRegler::RUECKMELDUNG_LEISTUNG
|
|
? Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN
|
|
: Nachrichtenvertrag::LEISTUNGSQUELLE_BERECHNET
|
|
);
|
|
$this->setzeZustand('Verfuegbar', $verfuegbar);
|
|
$this->setzeZustand('AenderungMoeglich', $aenderungMoeglich);
|
|
$this->setzeZustand('AnlaufAusstehend', $this->ReadAttributeInteger('AnlaufDeadline') > $jetzt);
|
|
$this->setzeZustand('WiederholsperreAktiv', $wiederholsperreAktiv);
|
|
$this->setzeZustand('SperrerholungAktiv', $sperrerholung);
|
|
$this->setzeZustand('RestMindestlaufzeit', $restMindestlaufzeit);
|
|
$this->setzeZustand('RestMindestsperrzeit', $restMindestsperrzeit);
|
|
$this->setzeZustand('Sperrzeit24h', $sperrzeit24h);
|
|
$this->aktualisiereFachvariablen($laeuft, $steuerzustand);
|
|
$this->aktualisiereStoerung();
|
|
$this->planeTimer($jetzt);
|
|
$this->SetStatus($fehlerfrei ? self::STATUS_AKTIV : self::STATUS_SCHALTFEHLER);
|
|
|
|
if ($meldungPlanen) {
|
|
$this->planeMeldung();
|
|
}
|
|
} catch (Throwable $fehler) {
|
|
$this->WriteAttributeString('Schaltfehler', $fehler->getMessage());
|
|
$this->setzeZustand('Verfuegbar', false);
|
|
$this->setzeZustand('AenderungMoeglich', false);
|
|
$this->setzeStoerung($fehler->getMessage());
|
|
$this->SetStatus(self::STATUS_SCHALTFEHLER);
|
|
$this->protokolliere('Aktualisierung', $fehler->getMessage());
|
|
}
|
|
}
|
|
|
|
private function bestimmeZielzustand(
|
|
bool $aktiv,
|
|
bool $laeuft,
|
|
int $restMindestlaufzeit,
|
|
int $restMindestsperrzeit,
|
|
bool $sperrenZulaessig,
|
|
bool $sperrerholung
|
|
): int {
|
|
$aktuell = $this->ReadAttributeInteger('Steuerzustand');
|
|
if (!$aktiv) {
|
|
return WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
}
|
|
if ($aktuell === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
&& $restMindestsperrzeit > 0
|
|
) {
|
|
return WaermepumpenRegler::STEUERZUSTAND_SPERRE;
|
|
}
|
|
if ($sperrerholung) {
|
|
return WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
}
|
|
|
|
$pv = $this->ReadAttributeString('Betriebsart') === Nachrichtenvertrag::BETRIEBSART_PV;
|
|
if (!(bool) $this->leseZustand('SollwertGueltig')) {
|
|
return WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
}
|
|
|
|
$sollleistung = (int) $this->leseZustand('Sollleistung');
|
|
if ($pv) {
|
|
return $sollleistung > 0
|
|
? WaermepumpenRegler::STEUERZUSTAND_ERHOEHUNG
|
|
: WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
}
|
|
if ($laeuft && $restMindestlaufzeit > 0) {
|
|
return WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
}
|
|
|
|
return $sollleistung === 0 && $sperrenZulaessig
|
|
? WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
: WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
}
|
|
|
|
private function aktualisiereAnlauf(bool $laeuft, int $steuerzustand, int $jetzt): void
|
|
{
|
|
if ($laeuft) {
|
|
$this->WriteAttributeInteger('AnlaufDeadline', 0);
|
|
$this->WriteAttributeInteger('WiederholsperreBis', 0);
|
|
return;
|
|
}
|
|
|
|
$pv = $this->ReadAttributeString('Betriebsart') === Nachrichtenvertrag::BETRIEBSART_PV;
|
|
$anlaufAngefordert = $pv
|
|
&& $steuerzustand === WaermepumpenRegler::STEUERZUSTAND_ERHOEHUNG
|
|
&& (bool) $this->leseZustand('SollwertGueltig')
|
|
&& (int) $this->leseZustand('Sollleistung') === $this->ReadPropertyInteger('Nennleistung');
|
|
if (!$anlaufAngefordert) {
|
|
$this->WriteAttributeInteger('AnlaufDeadline', 0);
|
|
return;
|
|
}
|
|
|
|
$deadline = $this->ReadAttributeInteger('AnlaufDeadline');
|
|
if ($deadline <= 0) {
|
|
$this->WriteAttributeInteger(
|
|
'AnlaufDeadline',
|
|
$jetzt + $this->ReadPropertyInteger('Anlaufwartezeit')
|
|
);
|
|
return;
|
|
}
|
|
if ($jetzt < $deadline) {
|
|
return;
|
|
}
|
|
|
|
$this->schalte(WaermepumpenRegler::STEUERZUSTAND_NORMAL, $jetzt);
|
|
$this->WriteAttributeInteger('AnlaufDeadline', 0);
|
|
$this->WriteAttributeInteger(
|
|
'WiederholsperreBis',
|
|
$jetzt + $this->ReadPropertyInteger('Wiederholsperre')
|
|
);
|
|
$this->setzeZustand('Sollleistung', 0);
|
|
$this->setzeZustand('SollwertGueltig', true);
|
|
}
|
|
|
|
private function aktualisiereLaufzustand(bool $laeuft, int $jetzt): void
|
|
{
|
|
$stand = $this->ReadAttributeInteger('LaufzeitStand');
|
|
$vorher = $this->ReadAttributeBoolean('LetzterLaufstatus');
|
|
if ($stand > 0 && $stand <= $jetzt && $vorher) {
|
|
$this->SetValue('Laufzeit', (int) $this->GetValue('Laufzeit') + $jetzt - $stand);
|
|
}
|
|
if ($laeuft !== $vorher) {
|
|
$this->WriteAttributeInteger('Laufbeginn', $laeuft ? $jetzt : 0);
|
|
if ($this->ReadAttributeBoolean('Sperrerholung')) {
|
|
$this->WriteAttributeInteger('ErholungsLaufbeginn', $laeuft ? $jetzt : 0);
|
|
}
|
|
}
|
|
$this->WriteAttributeBoolean('LetzterLaufstatus', $laeuft);
|
|
$this->WriteAttributeInteger('LaufzeitStand', $jetzt);
|
|
$this->SetValue('WaermepumpeLaeuft', $laeuft);
|
|
}
|
|
|
|
private function restMindestlaufzeit(bool $laeuft, int $jetzt): int
|
|
{
|
|
if (!$laeuft) {
|
|
return 0;
|
|
}
|
|
$beginn = $this->ReadAttributeBoolean('Sperrerholung')
|
|
? $this->ReadAttributeInteger('ErholungsLaufbeginn')
|
|
: $this->ReadAttributeInteger('Laufbeginn');
|
|
|
|
return WaermepumpenRegler::restzeit(
|
|
$beginn,
|
|
$this->ReadPropertyInteger('Mindestlaufzeit'),
|
|
$jetzt
|
|
);
|
|
}
|
|
|
|
private function aktualisiereSperrerholung(bool $laeuft, int $jetzt): void
|
|
{
|
|
if (!$this->ReadAttributeBoolean('Sperrerholung')) {
|
|
return;
|
|
}
|
|
$beginn = $this->ReadAttributeInteger('ErholungsLaufbeginn');
|
|
if (!$laeuft) {
|
|
if ($beginn > 0) {
|
|
$this->WriteAttributeInteger('ErholungsLaufbeginn', 0);
|
|
}
|
|
return;
|
|
}
|
|
if ($beginn <= 0) {
|
|
$this->WriteAttributeInteger('ErholungsLaufbeginn', $jetzt);
|
|
return;
|
|
}
|
|
if (WaermepumpenRegler::restzeit(
|
|
$beginn,
|
|
$this->ReadPropertyInteger('Mindestlaufzeit'),
|
|
$jetzt
|
|
) === 0) {
|
|
$this->WriteAttributeBoolean('Sperrerholung', false);
|
|
$this->WriteAttributeInteger('ErholungsLaufbeginn', 0);
|
|
}
|
|
}
|
|
|
|
private function schalte(int $steuerzustand, int $jetzt): void
|
|
{
|
|
$werte = WaermepumpenRegler::kontaktwerte(
|
|
$this->ReadPropertyInteger('Kontaktart'),
|
|
$steuerzustand
|
|
);
|
|
$this->WriteAttributeBoolean('SchaltvorgangLaeuft', true);
|
|
try {
|
|
$this->setzeKontakt(1, false);
|
|
$this->setzeKontakt(2, false);
|
|
if ($werte['Kontakt1']) {
|
|
$this->setzeKontakt(1, true);
|
|
}
|
|
if ($werte['Kontakt2']) {
|
|
$this->setzeKontakt(2, true);
|
|
}
|
|
} catch (Throwable $fehler) {
|
|
throw new RuntimeException('Kontakte konnten nicht gesetzt werden: ' . $fehler->getMessage());
|
|
} finally {
|
|
$this->WriteAttributeBoolean('SchaltvorgangLaeuft', false);
|
|
}
|
|
|
|
if ($this->leseLogischenKontakt(1) !== $werte['Kontakt1']
|
|
|| $this->leseLogischenKontakt(2) !== $werte['Kontakt2']
|
|
) {
|
|
throw new RuntimeException('Mindestens ein Kontakt hat den Schaltbefehl nicht uebernommen.');
|
|
}
|
|
|
|
$alt = $this->ReadAttributeInteger('Steuerzustand');
|
|
if ($alt === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
&& $steuerzustand !== WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
) {
|
|
$this->schliesseSperrintervall($jetzt);
|
|
}
|
|
if ($alt !== WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
&& $steuerzustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
) {
|
|
$this->WriteAttributeInteger('Sperrbeginn', $jetzt);
|
|
}
|
|
$this->WriteAttributeInteger('Steuerzustand', $steuerzustand);
|
|
$this->WriteAttributeString('Schaltfehler', '');
|
|
$this->protokolliere('Steuerzustand', ['Alt' => $alt, 'Neu' => $steuerzustand]);
|
|
}
|
|
|
|
private function setzeKontakt(int $nummer, bool $logischerWert): void
|
|
{
|
|
$id = $this->ReadPropertyInteger('Kontakt' . $nummer . 'VariableID');
|
|
$invertiert = $this->ReadPropertyBoolean('Kontakt' . $nummer . 'Invertiert');
|
|
RequestAction($id, $logischerWert !== $invertiert);
|
|
}
|
|
|
|
private function leseLogischenKontakt(int $nummer): bool
|
|
{
|
|
$id = $this->ReadPropertyInteger('Kontakt' . $nummer . 'VariableID');
|
|
$invertiert = $this->ReadPropertyBoolean('Kontakt' . $nummer . 'Invertiert');
|
|
|
|
return (bool) GetValue($id) !== $invertiert;
|
|
}
|
|
|
|
private function pruefeKontaktzustand(): void
|
|
{
|
|
$erwartet = WaermepumpenRegler::kontaktwerte(
|
|
$this->ReadPropertyInteger('Kontaktart'),
|
|
$this->ReadAttributeInteger('Steuerzustand')
|
|
);
|
|
if ($this->leseLogischenKontakt(1) !== $erwartet['Kontakt1']
|
|
|| $this->leseLogischenKontakt(2) !== $erwartet['Kontakt2']
|
|
) {
|
|
$this->schalte($this->ReadAttributeInteger('Steuerzustand'), time());
|
|
return;
|
|
}
|
|
$this->WriteAttributeString('Schaltfehler', '');
|
|
}
|
|
|
|
private function aktualisiereSperrhistorie(int $steuerzustand, int $jetzt): void
|
|
{
|
|
$historie = $this->leseSperrhistorie();
|
|
$historie = WaermepumpenRegler::bereinigeSperrhistorie($historie, $jetzt);
|
|
$this->WriteAttributeString('Sperrhistorie', json_encode($historie, JSON_THROW_ON_ERROR));
|
|
if ($steuerzustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
&& $this->ReadAttributeInteger('Sperrbeginn') <= 0
|
|
) {
|
|
$this->WriteAttributeInteger('Sperrbeginn', $jetzt);
|
|
}
|
|
}
|
|
|
|
private function schliesseSperrintervall(int $jetzt): void
|
|
{
|
|
$beginn = $this->ReadAttributeInteger('Sperrbeginn');
|
|
if ($beginn > 0 && $beginn <= $jetzt) {
|
|
$historie = $this->leseSperrhistorie();
|
|
$historie[] = ['Von' => $beginn, 'Bis' => $jetzt];
|
|
$historie = WaermepumpenRegler::bereinigeSperrhistorie($historie, $jetzt);
|
|
$this->WriteAttributeString('Sperrhistorie', json_encode($historie, JSON_THROW_ON_ERROR));
|
|
}
|
|
$this->WriteAttributeInteger('Sperrbeginn', 0);
|
|
}
|
|
|
|
private function sperrsekunden24h(int $steuerzustand, int $jetzt): int
|
|
{
|
|
return WaermepumpenRegler::sperrsekunden24h(
|
|
$this->leseSperrhistorie(),
|
|
$jetzt,
|
|
$steuerzustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
? $this->ReadAttributeInteger('Sperrbeginn')
|
|
: null
|
|
);
|
|
}
|
|
|
|
/** @return list<array{Von: int, Bis: int}> */
|
|
private function leseSperrhistorie(): array
|
|
{
|
|
$historie = json_decode(
|
|
$this->ReadAttributeString('Sperrhistorie'),
|
|
true,
|
|
512,
|
|
JSON_THROW_ON_ERROR
|
|
);
|
|
|
|
return is_array($historie) ? array_values($historie) : [];
|
|
}
|
|
|
|
private function aktualisiereFachvariablen(bool $laeuft, int $steuerzustand): void
|
|
{
|
|
$sperre = $steuerzustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE;
|
|
$erhoehung = $steuerzustand === WaermepumpenRegler::STEUERZUSTAND_ERHOEHUNG;
|
|
$zustand = $sperre ? 4 : ($erhoehung ? 3 : ($laeuft ? 2 : 1));
|
|
$this->SetValue('Waermepumpenzustand', $zustand);
|
|
$this->SetValue('SperreAktiv', $sperre);
|
|
$this->SetValue('ErhoehungAktiv', $erhoehung);
|
|
$this->SetValue(
|
|
'SGReadyZustand',
|
|
($this->leseLogischenKontakt(1) ? 1 : 0) + ($this->leseLogischenKontakt(2) ? 2 : 0)
|
|
);
|
|
}
|
|
|
|
private function initialisiereZustand(int $jetzt): void
|
|
{
|
|
if (!$this->ReadAttributeBoolean('Initialisiert')) {
|
|
$kontakt1 = $this->leseLogischenKontakt(1);
|
|
$kontakt2 = $this->leseLogischenKontakt(2);
|
|
if (!$kontakt1 && !$kontakt2) {
|
|
$zustand = WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
} elseif ($kontakt1 && !$kontakt2) {
|
|
$zustand = WaermepumpenRegler::STEUERZUSTAND_SPERRE;
|
|
} elseif ($kontakt2 && !$kontakt1) {
|
|
$zustand = WaermepumpenRegler::STEUERZUSTAND_ERHOEHUNG;
|
|
} else {
|
|
$zustand = WaermepumpenRegler::STEUERZUSTAND_NORMAL;
|
|
}
|
|
$this->WriteAttributeInteger('Steuerzustand', $zustand);
|
|
$this->WriteAttributeInteger(
|
|
'Sperrbeginn',
|
|
$zustand === WaermepumpenRegler::STEUERZUSTAND_SPERRE ? $jetzt : 0
|
|
);
|
|
$laeuft = WaermepumpenRegler::laeuft(
|
|
$this->leseRueckmeldung(),
|
|
$this->ReadPropertyInteger('Rueckmeldungsart'),
|
|
$this->ReadPropertyFloat('Laufschwelle')
|
|
);
|
|
$this->WriteAttributeBoolean('LetzterLaufstatus', $laeuft);
|
|
$this->WriteAttributeInteger('Laufbeginn', $laeuft ? $jetzt : 0);
|
|
$this->WriteAttributeInteger('LaufzeitStand', $jetzt);
|
|
$this->WriteAttributeBoolean('Initialisiert', true);
|
|
}
|
|
}
|
|
|
|
/** @return bool|int|float */
|
|
private function leseRueckmeldung()
|
|
{
|
|
return GetValue($this->ReadPropertyInteger('IstleistungVariableID'));
|
|
}
|
|
|
|
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'] as $property) {
|
|
if ($this->ReadPropertyInteger($property) <= 0) {
|
|
throw new InvalidArgumentException($property . ' muss groesser als 0 sein.');
|
|
}
|
|
}
|
|
if ($this->ReadPropertyInteger('Nennleistung') <= 0) {
|
|
throw new InvalidArgumentException('Nennleistung muss groesser als 0 sein.');
|
|
}
|
|
if ($this->ReadPropertyFloat('Laufschwelle') < 0) {
|
|
throw new InvalidArgumentException('Laufschwelle darf nicht negativ sein.');
|
|
}
|
|
|
|
$kontaktart = $this->ReadPropertyInteger('Kontaktart');
|
|
WaermepumpenRegler::kontaktwerte(
|
|
$kontaktart,
|
|
WaermepumpenRegler::STEUERZUSTAND_NORMAL
|
|
);
|
|
$rueckmeldungsart = $this->ReadPropertyInteger('Rueckmeldungsart');
|
|
WaermepumpenRegler::laeuft(false, $rueckmeldungsart, 0.0);
|
|
|
|
$kontakt1 = $this->ReadPropertyInteger('Kontakt1VariableID');
|
|
$kontakt2 = $this->ReadPropertyInteger('Kontakt2VariableID');
|
|
$this->pruefeVariable($kontakt1, 'Kontakt 1', [0], true);
|
|
$this->pruefeVariable($kontakt2, 'Kontakt 2', [0], true);
|
|
if ($kontakt1 === $kontakt2) {
|
|
throw new InvalidArgumentException('Kontakt 1 und Kontakt 2 muessen verschieden sein.');
|
|
}
|
|
|
|
$rueckmeldung = $this->ReadPropertyInteger('IstleistungVariableID');
|
|
$typen = $rueckmeldungsart === WaermepumpenRegler::RUECKMELDUNG_BETRIEB
|
|
? [0]
|
|
: [1, 2];
|
|
$this->pruefeVariable($rueckmeldung, 'Rueckmeldung', $typen, false);
|
|
|
|
foreach ([
|
|
'Anlaufwartezeit',
|
|
'Wiederholsperre',
|
|
'Mindestlaufzeit',
|
|
'Mindestsperrzeit',
|
|
'MaximaleSperrzeit',
|
|
'MaximaleSperrzeit24h',
|
|
] as $property) {
|
|
if ($this->ReadPropertyInteger($property) < 0) {
|
|
throw new InvalidArgumentException($property . ' darf nicht negativ sein.');
|
|
}
|
|
}
|
|
if ($this->ReadPropertyInteger('MaximaleSperrzeit24h') > 86400) {
|
|
throw new InvalidArgumentException('MaximaleSperrzeit24h darf hoechstens 86400 Sekunden betragen.');
|
|
}
|
|
}
|
|
|
|
/** @param list<int> $typen */
|
|
private function pruefeVariable(
|
|
int $variableID,
|
|
string $bezeichnung,
|
|
array $typen,
|
|
bool $aktionErforderlich
|
|
): void {
|
|
if ($variableID <= 0 || !IPS_VariableExists($variableID)) {
|
|
throw new InvalidArgumentException($bezeichnung . ' ist nicht eingerichtet.');
|
|
}
|
|
$variable = IPS_GetVariable($variableID);
|
|
if (!in_array((int) $variable['VariableType'], $typen, true)) {
|
|
throw new InvalidArgumentException($bezeichnung . ' hat einen ungueltigen Variablentyp.');
|
|
}
|
|
if ($aktionErforderlich
|
|
&& (int) $variable['VariableAction'] <= 0
|
|
&& (int) $variable['VariableCustomAction'] <= 0
|
|
) {
|
|
throw new InvalidArgumentException(
|
|
$bezeichnung . ' benoetigt eine Standard- oder benutzerdefinierte Aktion.'
|
|
);
|
|
}
|
|
}
|
|
|
|
private function registriereMeldungen(): void
|
|
{
|
|
$definitionen = [
|
|
'Kontakt1' => $this->ReadPropertyInteger('Kontakt1VariableID'),
|
|
'Kontakt2' => $this->ReadPropertyInteger('Kontakt2VariableID'),
|
|
'Istleistung' => $this->ReadPropertyInteger('IstleistungVariableID'),
|
|
];
|
|
|
|
foreach ($definitionen as $name => $neueID) {
|
|
$attribut = 'Registrierte' . $name . 'ID';
|
|
$alteID = $this->ReadAttributeInteger($attribut);
|
|
if ($alteID > 0 && $alteID !== $neueID) {
|
|
$this->UnregisterMessage($alteID, self::VM_UPDATE);
|
|
}
|
|
if ($neueID > 0 && IPS_VariableExists($neueID)) {
|
|
$this->RegisterMessage($neueID, self::VM_UPDATE);
|
|
}
|
|
$this->WriteAttributeInteger($attribut, $neueID);
|
|
}
|
|
}
|
|
|
|
private function pruefeVorgabeTimeout(int $jetzt): void
|
|
{
|
|
if (!(bool) $this->leseZustand('SollwertGueltig')) {
|
|
return;
|
|
}
|
|
$ablauf = $this->ReadAttributeInteger('LetzteVorgabeZeit')
|
|
+ $this->ReadPropertyInteger('VorgabeTimeout');
|
|
if ($this->ReadAttributeInteger('LetzteVorgabeZeit') <= 0 || $jetzt >= $ablauf) {
|
|
$this->setzeZustand('SollwertGueltig', false);
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
}
|
|
}
|
|
|
|
private function planeVorgabeTimeout(int $jetzt): void
|
|
{
|
|
if (!(bool) $this->leseZustand('SollwertGueltig')) {
|
|
$this->SetTimerInterval('VorgabeTimeout', 0);
|
|
return;
|
|
}
|
|
|
|
$ablauf = $this->ReadAttributeInteger('LetzteVorgabeZeit')
|
|
+ $this->ReadPropertyInteger('VorgabeTimeout');
|
|
$this->SetTimerInterval('VorgabeTimeout', max(1, ($ablauf - $jetzt) * 1000));
|
|
}
|
|
|
|
private function planeTimer(int $jetzt): void
|
|
{
|
|
$zeitpunkte = [];
|
|
foreach (['AnlaufDeadline', 'WiederholsperreBis'] as $attribut) {
|
|
$zeitpunkt = $this->ReadAttributeInteger($attribut);
|
|
if ($zeitpunkt > $jetzt) {
|
|
$zeitpunkte[] = $zeitpunkt;
|
|
}
|
|
}
|
|
$laufbeginn = $this->ReadAttributeInteger('Laufbeginn');
|
|
if ($this->ReadAttributeBoolean('LetzterLaufstatus') && $laufbeginn > 0) {
|
|
$zeitpunkte[] = $laufbeginn + $this->ReadPropertyInteger('Mindestlaufzeit');
|
|
}
|
|
if ($this->ReadAttributeInteger('Steuerzustand')
|
|
=== WaermepumpenRegler::STEUERZUSTAND_SPERRE
|
|
) {
|
|
$sperrbeginn = $this->ReadAttributeInteger('Sperrbeginn');
|
|
if ($sperrbeginn > 0) {
|
|
$zeitpunkte[] = $sperrbeginn + $this->ReadPropertyInteger('Mindestsperrzeit');
|
|
if ($this->ReadPropertyInteger('MaximaleSperrzeit') > 0) {
|
|
$zeitpunkte[] = $sperrbeginn + $this->ReadPropertyInteger('MaximaleSperrzeit');
|
|
}
|
|
$maximum24h = $this->ReadPropertyInteger('MaximaleSperrzeit24h');
|
|
if ($maximum24h > 0) {
|
|
$rest24h = $maximum24h - $this->sperrsekunden24h(
|
|
WaermepumpenRegler::STEUERZUSTAND_SPERRE,
|
|
$jetzt
|
|
);
|
|
if ($rest24h > 0) {
|
|
$zeitpunkte[] = $jetzt + $rest24h;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$zeitpunkte = array_values(array_filter(
|
|
$zeitpunkte,
|
|
static fn (int $zeitpunkt): bool => $zeitpunkt > $jetzt
|
|
));
|
|
$this->SetTimerInterval(
|
|
'Zustandspruefung',
|
|
$zeitpunkte === [] ? 0 : max(1, (min($zeitpunkte) - $jetzt) * 1000)
|
|
);
|
|
$this->planeVorgabeTimeout($jetzt);
|
|
}
|
|
|
|
private function planeMeldung(): void
|
|
{
|
|
$this->SetTimerInterval('MeldungVerzoegert', 100);
|
|
}
|
|
|
|
private function aktualisiereStoerung(): void
|
|
{
|
|
$text = $this->ReadAttributeString('Schaltfehler');
|
|
$this->setzeStoerung($text);
|
|
}
|
|
|
|
private function setzeStoerung(string $text): void
|
|
{
|
|
$this->setzeZustand('Stoerung', $text !== '');
|
|
$this->setzeZustand('Stoertext', $text);
|
|
}
|
|
|
|
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
|
|
{
|
|
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' => (int) $this->leseZustand('Leistungsquelle'),
|
|
'Zustand' => $this->zustandsdaten(),
|
|
];
|
|
}
|
|
|
|
/** @return list<array<string, mixed>> */
|
|
private function zustandsdaten(): array
|
|
{
|
|
return [
|
|
[
|
|
'Kennung' => 'Sollleistung_W',
|
|
'Art' => 'Sollwert',
|
|
'Wert' => (bool) $this->leseZustand('SollwertGueltig')
|
|
? (int) $this->leseZustand('Sollleistung')
|
|
: null,
|
|
'Einheit' => 'W',
|
|
],
|
|
[
|
|
'Kennung' => 'WaermepumpeLaeuft',
|
|
'Art' => 'Status',
|
|
'Wert' => (bool) $this->GetValue('WaermepumpeLaeuft'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Steuerzustand',
|
|
'Art' => 'Status',
|
|
'Wert' => $this->ReadAttributeInteger('Steuerzustand'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'RestMindestlaufzeit_s',
|
|
'Art' => 'Istwert',
|
|
'Wert' => (int) $this->leseZustand('RestMindestlaufzeit'),
|
|
'Einheit' => 's',
|
|
],
|
|
|
|
[
|
|
'Kennung' => 'RestMindestsperrzeit_s',
|
|
'Art' => 'Istwert',
|
|
'Wert' => (int) $this->leseZustand('RestMindestsperrzeit'),
|
|
'Einheit' => 's',
|
|
],
|
|
[
|
|
'Kennung' => 'Sperrzeit24h_s',
|
|
'Art' => 'Istwert',
|
|
'Wert' => (int) $this->leseZustand('Sperrzeit24h'),
|
|
'Einheit' => 's',
|
|
],
|
|
[
|
|
'Kennung' => 'SperrerholungAktiv',
|
|
'Art' => 'Status',
|
|
'Wert' => (bool) $this->leseZustand('SperrerholungAktiv'),
|
|
'Einheit' => '',
|
|
],
|
|
[
|
|
'Kennung' => 'Schaltfehler',
|
|
'Art' => 'Stoerung',
|
|
'Wert' => (bool) $this->leseZustand('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 : [];
|
|
}
|
|
|
|
/** @return list<int> */
|
|
private function leseLeistungsangebot(): array
|
|
{
|
|
$angebot = json_decode(
|
|
$this->ReadAttributeString('Leistungsangebot'),
|
|
true,
|
|
512,
|
|
JSON_THROW_ON_ERROR
|
|
);
|
|
|
|
return is_array($angebot) ? array_values(array_map('intval', $angebot)) : [];
|
|
}
|
|
|
|
private function aktualisiereVariablen(): void
|
|
{
|
|
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
|
$variablenID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
if (is_int($variablenID) && $variablenID > 0 && IPS_VariableExists($variablenID)) {
|
|
$this->setzeZustand($ident, GetValue($variablenID));
|
|
}
|
|
}
|
|
|
|
if ($this->ReadPropertyBoolean('DiagnosevariablenAnzeigen')) {
|
|
$this->registriereVerbraucherDiagnose();
|
|
$this->RegisterVariableBoolean('AnlaufAusstehend', 'Anlauf ausstehend', '~Switch', 200);
|
|
$this->RegisterVariableBoolean(
|
|
'WiederholsperreAktiv',
|
|
'Wiederholsperre aktiv',
|
|
'~Switch',
|
|
210
|
|
);
|
|
$this->RegisterVariableBoolean(
|
|
'SperrerholungAktiv',
|
|
'Sperrerholung aktiv',
|
|
'~Switch',
|
|
220
|
|
);
|
|
|
|
$this->RegisterVariableInteger(
|
|
'RestMindestlaufzeit',
|
|
'Restliche Mindestlaufzeit',
|
|
'',
|
|
230
|
|
);
|
|
$this->RegisterVariableInteger(
|
|
'RestMindestsperrzeit',
|
|
'Restliche Mindestsperrzeit',
|
|
'',
|
|
240
|
|
);
|
|
$this->RegisterVariableInteger('Sperrzeit24h', 'Sperrzeit letzte 24 h', '', 250);
|
|
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
|
$this->setzeZustand($ident, $this->leseZustand($ident));
|
|
}
|
|
return;
|
|
}
|
|
|
|
foreach (self::DIAGNOSE_VARIABLEN as $ident) {
|
|
$variablenID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
if (is_int($variablenID) && $variablenID > 0 && IPS_VariableExists($variablenID)) {
|
|
$this->UnregisterVariable($ident);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @param bool|int|float|string $wert */
|
|
private function setzeZustand(string $ident, $wert): void
|
|
{
|
|
$booleanAttribute = [
|
|
'SollwertGueltig' => 'ZustandSollwertGueltig',
|
|
'Verfuegbar' => 'ZustandVerfuegbar',
|
|
'AenderungMoeglich' => 'ZustandAenderungMoeglich',
|
|
'Stoerung' => 'ZustandStoerung',
|
|
'AnlaufAusstehend' => 'ZustandAnlaufAusstehend',
|
|
'WiederholsperreAktiv' => 'ZustandWiederholsperreAktiv',
|
|
'SperrerholungAktiv' => 'ZustandSperrerholungAktiv',
|
|
];
|
|
|
|
$integerAttribute = [
|
|
'Leistungsquelle' => 'ZustandLeistungsquelle',
|
|
'Sollleistung' => 'ZustandSollleistung',
|
|
'RestMindestlaufzeit' => 'ZustandRestMindestlaufzeit',
|
|
'RestMindestsperrzeit' => 'ZustandRestMindestsperrzeit',
|
|
'Sperrzeit24h' => 'ZustandSperrzeit24h',
|
|
];
|
|
if (isset($booleanAttribute[$ident])) {
|
|
$wert = (bool) $wert;
|
|
$this->WriteAttributeBoolean($booleanAttribute[$ident], $wert);
|
|
} elseif (isset($integerAttribute[$ident])) {
|
|
$wert = (int) $wert;
|
|
$this->WriteAttributeInteger($integerAttribute[$ident], $wert);
|
|
} elseif ($ident === 'Istleistung') {
|
|
$wert = (float) $wert;
|
|
$this->WriteAttributeFloat('ZustandIstleistung', $wert);
|
|
} elseif ($ident === 'Stoertext') {
|
|
$wert = (string) $wert;
|
|
$this->WriteAttributeString('ZustandStoertext', $wert);
|
|
} else {
|
|
throw new LogicException('Unbekannter interner Zustand: ' . $ident);
|
|
}
|
|
|
|
$variablenID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
if (is_int($variablenID) && $variablenID > 0 && IPS_VariableExists($variablenID)) {
|
|
SetValue($variablenID, $wert);
|
|
}
|
|
}
|
|
|
|
/** @return bool|int|float|string */
|
|
private function leseZustand(string $ident)
|
|
{
|
|
$attribute = [
|
|
'Istleistung' => ['Float', 'ZustandIstleistung'],
|
|
'Leistungsquelle' => ['Integer', 'ZustandLeistungsquelle'],
|
|
'Sollleistung' => ['Integer', 'ZustandSollleistung'],
|
|
|
|
'SollwertGueltig' => ['Boolean', 'ZustandSollwertGueltig'],
|
|
'Verfuegbar' => ['Boolean', 'ZustandVerfuegbar'],
|
|
'AenderungMoeglich' => ['Boolean', 'ZustandAenderungMoeglich'],
|
|
'Stoerung' => ['Boolean', 'ZustandStoerung'],
|
|
'Stoertext' => ['String', 'ZustandStoertext'],
|
|
'AnlaufAusstehend' => ['Boolean', 'ZustandAnlaufAusstehend'],
|
|
'WiederholsperreAktiv' => ['Boolean', 'ZustandWiederholsperreAktiv'],
|
|
'SperrerholungAktiv' => ['Boolean', 'ZustandSperrerholungAktiv'],
|
|
'RestMindestlaufzeit' => ['Integer', 'ZustandRestMindestlaufzeit'],
|
|
'RestMindestsperrzeit' => ['Integer', 'ZustandRestMindestsperrzeit'],
|
|
'Sperrzeit24h' => ['Integer', 'ZustandSperrzeit24h'],
|
|
];
|
|
if (!isset($attribute[$ident])) {
|
|
throw new LogicException('Unbekannter interner Zustand: ' . $ident);
|
|
}
|
|
|
|
[$typ, $name] = $attribute[$ident];
|
|
$methode = 'ReadAttribute' . $typ;
|
|
|
|
return $this->{$methode}($name);
|
|
}
|
|
|
|
private function deaktiviereTimer(): void
|
|
{
|
|
foreach ([
|
|
'Meldezyklus',
|
|
'MeldungVerzoegert',
|
|
'Zustandspruefung',
|
|
'VorgabeTimeout',
|
|
] as $timer) {
|
|
$this->SetTimerInterval($timer, 0);
|
|
}
|
|
}
|
|
|
|
/** @param mixed $daten */
|
|
private function protokolliere(string $bezeichnung, $daten): void
|
|
{
|
|
if (!$this->ReadPropertyBoolean('LoggingEin')) {
|
|
return;
|
|
}
|
|
|
|
$this->SendDebug(
|
|
$bezeichnung,
|
|
is_string($daten) ? $daten : json_encode($daten, JSON_THROW_ON_ERROR),
|
|
0
|
|
);
|
|
}
|
|
}
|