This commit is contained in:
+333
-41
@@ -130,11 +130,15 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->RegisterPropertyFloat('Umschaltdifferenz', 5.0);
|
||||
$this->RegisterPropertyBoolean('EinspeisebegrenzungAktiv', false);
|
||||
$this->RegisterPropertyFloat('Einspeisegrenze', 0.0);
|
||||
$this->RegisterPropertyFloat('Einspeisetoleranz', 100.0);
|
||||
$this->RegisterPropertyBoolean('PrognoseAktiv', false);
|
||||
$this->RegisterPropertyBoolean('NetzfahrplanAktiv', false);
|
||||
$this->RegisterPropertyString('AnlagenWechselrichter', '[]');
|
||||
$this->RegisterPropertyString('AnlagenPVFlaechen', '[]');
|
||||
$this->RegisterPropertyString('AnlagenBatterien', '[]');
|
||||
$this->RegisterPropertyInteger('NetzbezugEnergieVariableID', 0);
|
||||
$this->RegisterPropertyFloat('NetzbezugEnergiefaktor', 1.0);
|
||||
$this->RegisterPropertyInteger('NetzeinspeisungEnergieVariableID', 0);
|
||||
$this->RegisterPropertyFloat('NetzeinspeisungEnergiefaktor', 1.0);
|
||||
// Verdeckte Altproperties; Messquellen liegen neu ausschliesslich in der Anlagentopologie.
|
||||
$this->RegisterPropertyInteger('PrognosePVVariableID', 0);
|
||||
$this->RegisterPropertyInteger('PrognoseHausverbrauchVariableID', 0);
|
||||
@@ -196,6 +200,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->RegisterAttributeInteger('MessungLetzteLeistungsaufzeichnung', 0);
|
||||
$this->RegisterAttributeInteger('MessungLetzteEnergieaufzeichnung', 0);
|
||||
$this->RegisterAttributeString('MessungEnergiePuffer', '{}');
|
||||
$this->RegisterAttributeString('MessungZaehlerstaende', '{}');
|
||||
|
||||
// Die alten Timer bleiben deaktiviert registriert, damit bestehende Instanzen sauber migrieren.
|
||||
$this->RegisterTimer('Regelzyklus', 0, '');
|
||||
@@ -261,8 +266,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
}
|
||||
$this->setzeDiagnosewert('Monatsgrenzen', $this->ReadPropertyString('Monatsgrenzen'));
|
||||
$this->aktualisiereAnschlussstatus();
|
||||
$geraetezugangFehlt = ($this->hatAnlagentopologie()
|
||||
|| $this->prognoseTelemetrieKonfiguriert()
|
||||
$geraetezugangFehlt = ($this->ReadPropertyBoolean('PrognoseAktiv')
|
||||
|| $this->ReadPropertyBoolean('StoerueberwachungAktiv'))
|
||||
&& $this->ReadAttributeString('PrognoseInstallationsToken') === '';
|
||||
if (!$this->aktualisiereLizenzfreigabe(
|
||||
@@ -306,6 +310,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$automatisch = $this->ReadPropertyBoolean('AutomatischeSuche');
|
||||
$lastspitzenmodus = $this->ReadPropertyInteger('Lastspitzenmodus');
|
||||
$peakShavingLizenziert = $this->peakShavingLizenziert();
|
||||
$prognoseLizenziert = $this->prognoseLizenziert();
|
||||
$netzfahrplanLizenziert = $this->berechtigungLizenziert(
|
||||
Lizenzpruefung::NETZFAHRPLAN
|
||||
);
|
||||
@@ -352,19 +357,32 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
'options',
|
||||
$lastspitzenOptionen
|
||||
);
|
||||
foreach (['EinspeisebegrenzungAktiv', 'Einspeisegrenze', 'Einspeisetoleranz'] as $feld) {
|
||||
foreach (['PeakShavingTitel', 'EinspeisebegrenzungAktiv', 'Einspeisegrenze'] as $feld) {
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
$feld,
|
||||
'enabled',
|
||||
'visible',
|
||||
$peakShavingLizenziert
|
||||
);
|
||||
}
|
||||
$prognoseAktiv = $this->ReadPropertyBoolean('PrognoseAktiv');
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'PrognoseAktiv',
|
||||
'enabled',
|
||||
$prognoseLizenziert || $prognoseAktiv
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'PrognoseLizenzInformation',
|
||||
'caption',
|
||||
$this->prognoseLizenzStatustext($prognoseAktiv, $prognoseLizenziert)
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
'NetzfahrplanAktiv',
|
||||
'enabled',
|
||||
$peakShavingLizenziert && $netzfahrplanLizenziert
|
||||
$prognoseAktiv && $peakShavingLizenziert && $netzfahrplanLizenziert
|
||||
);
|
||||
$this->setzeFormularParameter(
|
||||
$formular['elements'],
|
||||
@@ -441,6 +459,27 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->zeichneMesswerteAuf();
|
||||
return;
|
||||
|
||||
case 'FormPrognoseAktiv':
|
||||
$aktiv = (bool) $wert;
|
||||
$lizenziert = $this->prognoseLizenziert();
|
||||
$netzfahrplanLizenziert = $this->berechtigungLizenziert(
|
||||
Lizenzpruefung::NETZFAHRPLAN
|
||||
);
|
||||
$this->UpdateFormField(
|
||||
'PrognoseLizenzInformation',
|
||||
'caption',
|
||||
$this->prognoseLizenzStatustext($aktiv, $lizenziert)
|
||||
);
|
||||
$this->UpdateFormField(
|
||||
'NetzfahrplanAktiv',
|
||||
'enabled',
|
||||
$aktiv && $netzfahrplanLizenziert && $this->peakShavingLizenziert()
|
||||
);
|
||||
if (!$aktiv) {
|
||||
$this->UpdateFormField('NetzfahrplanAktiv', 'value', false);
|
||||
}
|
||||
return;
|
||||
|
||||
case 'FormEnergyPieAnzeigen':
|
||||
$this->synchronisiereEnergyPie((bool) $wert);
|
||||
return;
|
||||
@@ -463,7 +502,8 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
}
|
||||
$this->aktualisiereLizenzfreigabe($wert, true);
|
||||
$this->UpdateFormField('LizenzInformation', 'caption', $this->lizenzStatustext($wert));
|
||||
$this->aktualisiereLastspitzenformular();
|
||||
$this->aktualisiereLastspitzenformular($wert);
|
||||
$this->aktualisierePrognoseformular($wert);
|
||||
return;
|
||||
|
||||
case 'FormAutomatischeSuche':
|
||||
@@ -737,7 +777,6 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$ergebnis = EinspeiseRegler::verteile(
|
||||
$netzleistung,
|
||||
$this->ReadPropertyFloat('Einspeisegrenze'),
|
||||
$this->ReadPropertyFloat('Einspeisetoleranz'),
|
||||
$wechselrichter,
|
||||
$letzteGrenzen
|
||||
);
|
||||
@@ -1713,6 +1752,105 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
return $quellen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<array{Schluessel: string, VariableID: int, Faktor: float}>>|null
|
||||
*/
|
||||
private function energieZaehlerquellen(): ?array
|
||||
{
|
||||
$wechselrichter = $this->leseAnlagenKonfigurationsliste('AnlagenWechselrichter');
|
||||
$pvFlaechen = $this->leseAnlagenKonfigurationsliste('AnlagenPVFlaechen');
|
||||
$batterien = $this->leseAnlagenKonfigurationsliste('AnlagenBatterien');
|
||||
$pvWechselrichter = [];
|
||||
foreach ($pvFlaechen as $pvFlaeche) {
|
||||
$id = trim((string) ($pvFlaeche['WechselrichterID'] ?? ''));
|
||||
if ($id !== '') {
|
||||
$pvWechselrichter[$id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$hatZaehler = $this->ReadPropertyInteger('NetzbezugEnergieVariableID') > 0
|
||||
|| $this->ReadPropertyInteger('NetzeinspeisungEnergieVariableID') > 0;
|
||||
foreach ($wechselrichter as $eintrag) {
|
||||
$hatZaehler = $hatZaehler || (int) ($eintrag['ErzeugungsenergieVariableID'] ?? 0) > 0;
|
||||
}
|
||||
foreach ($batterien as $eintrag) {
|
||||
$hatZaehler = $hatZaehler
|
||||
|| (int) ($eintrag['LadeenergieVariableID'] ?? 0) > 0
|
||||
|| (int) ($eintrag['EntladeenergieVariableID'] ?? 0) > 0;
|
||||
}
|
||||
if (!$hatZaehler) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$quellen = [
|
||||
'PVEnergie' => [],
|
||||
'NetzbezugEnergie' => [],
|
||||
'EinspeisungEnergie' => [],
|
||||
'BatterieLadenEnergie' => [],
|
||||
'BatterieEntladenEnergie' => [],
|
||||
];
|
||||
$netz = [
|
||||
'NetzbezugEnergie' => [
|
||||
$this->ReadPropertyInteger('NetzbezugEnergieVariableID'),
|
||||
$this->ReadPropertyFloat('NetzbezugEnergiefaktor'),
|
||||
'Netzbezug',
|
||||
],
|
||||
'EinspeisungEnergie' => [
|
||||
$this->ReadPropertyInteger('NetzeinspeisungEnergieVariableID'),
|
||||
$this->ReadPropertyFloat('NetzeinspeisungEnergiefaktor'),
|
||||
'Netzeinspeisung',
|
||||
],
|
||||
];
|
||||
foreach ($netz as $ident => [$variableID, $faktor, $name]) {
|
||||
$quellen[$ident][] = $this->energieZaehlerquelle(
|
||||
$name,
|
||||
$variableID,
|
||||
$faktor,
|
||||
strtolower($ident)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($wechselrichter as $index => $eintrag) {
|
||||
$typ = (string) ($eintrag['Typ'] ?? '');
|
||||
$id = trim((string) ($eintrag['ID'] ?? ''));
|
||||
if ($pvWechselrichter !== []) {
|
||||
if (!isset($pvWechselrichter[$id])) {
|
||||
continue;
|
||||
}
|
||||
} elseif (!in_array($typ, ['pv', 'hybrid'], true)) {
|
||||
continue;
|
||||
}
|
||||
$quellen['PVEnergie'][] = $this->energieZaehlerquelle(
|
||||
'Wechselrichter ' . ($index + 1) . ': Erzeugungsenergie',
|
||||
(int) ($eintrag['ErzeugungsenergieVariableID'] ?? 0),
|
||||
$eintrag['Erzeugungsenergiefaktor'] ?? 1.0,
|
||||
'pv:' . $id
|
||||
);
|
||||
}
|
||||
if ($quellen['PVEnergie'] === []) {
|
||||
throw new InvalidArgumentException('Mindestens ein PV-Erzeugungszaehler ist erforderlich.');
|
||||
}
|
||||
|
||||
foreach ($batterien as $index => $eintrag) {
|
||||
$id = trim((string) ($eintrag['ID'] ?? ''));
|
||||
$faktor = $eintrag['Energiefaktor'] ?? 1.0;
|
||||
$quellen['BatterieLadenEnergie'][] = $this->energieZaehlerquelle(
|
||||
'Batterie ' . ($index + 1) . ': Ladeenergie',
|
||||
(int) ($eintrag['LadeenergieVariableID'] ?? 0),
|
||||
$faktor,
|
||||
'batterie-laden:' . $id
|
||||
);
|
||||
$quellen['BatterieEntladenEnergie'][] = $this->energieZaehlerquelle(
|
||||
'Batterie ' . ($index + 1) . ': Entladeenergie',
|
||||
(int) ($eintrag['EntladeenergieVariableID'] ?? 0),
|
||||
$faktor,
|
||||
'batterie-entladen:' . $id
|
||||
);
|
||||
}
|
||||
|
||||
return $quellen;
|
||||
}
|
||||
|
||||
/** @return list<array<string, mixed>> */
|
||||
private function leseAnlagenKonfigurationsliste(string $property): array
|
||||
{
|
||||
@@ -1738,6 +1876,37 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
return $faktor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $faktor
|
||||
* @return array{Schluessel: string, VariableID: int, Faktor: float}
|
||||
*/
|
||||
private function energieZaehlerquelle(
|
||||
string $bezeichnung,
|
||||
int $variableID,
|
||||
$faktor,
|
||||
string $schluessel
|
||||
): array {
|
||||
if (!$this->istNumerischeVariable($variableID)) {
|
||||
throw new InvalidArgumentException(
|
||||
$bezeichnung . ' muss eine numerische Variable sein.'
|
||||
);
|
||||
}
|
||||
if ((!is_int($faktor) && !is_float($faktor))
|
||||
|| !is_finite((float) $faktor)
|
||||
|| (float) $faktor <= 0.0
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
$bezeichnung . ': Energiefaktor muss groesser als 0 sein.'
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'Schluessel' => $schluessel,
|
||||
'VariableID' => $variableID,
|
||||
'Faktor' => (float) $faktor,
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array{PV: float, Haus: float, Netz: float, Batterie: float} */
|
||||
private function leseMessleistungen(): array
|
||||
{
|
||||
@@ -1780,6 +1949,58 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
return $gewicht > 0.0 ? $summe / $gewicht : null;
|
||||
}
|
||||
|
||||
/** @return array<string, float>|null */
|
||||
private function leseEnergieZaehlerDeltas(): ?array
|
||||
{
|
||||
$quellen = $this->energieZaehlerquellen();
|
||||
if ($quellen === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$alt = $this->leseJsonAttribut('MessungZaehlerstaende');
|
||||
$neu = [];
|
||||
$deltas = [];
|
||||
$vollstaendigeBasis = true;
|
||||
foreach ($quellen as $ident => $identQuellen) {
|
||||
$deltas[$ident] = 0.0;
|
||||
foreach ($identQuellen as $quelle) {
|
||||
$schluessel = $quelle['Schluessel'];
|
||||
$stand = (float) GetValue($quelle['VariableID']) * $quelle['Faktor'];
|
||||
if (!is_finite($stand) || $stand < 0.0) {
|
||||
throw new RuntimeException(
|
||||
'Energiezaehler ' . $schluessel . ' liefert keinen gueltigen Stand.'
|
||||
);
|
||||
}
|
||||
$neu[$schluessel] = $stand;
|
||||
if (!isset($alt[$schluessel]) || !is_numeric($alt[$schluessel])) {
|
||||
$vollstaendigeBasis = false;
|
||||
continue;
|
||||
}
|
||||
$delta = $stand - (float) $alt[$schluessel];
|
||||
if ($delta >= 0.0) {
|
||||
$deltas[$ident] += $delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->WriteAttributeString(
|
||||
'MessungZaehlerstaende',
|
||||
json_encode($neu, JSON_THROW_ON_ERROR | JSON_PRESERVE_ZERO_FRACTION)
|
||||
);
|
||||
if (!$vollstaendigeBasis) {
|
||||
return array_fill_keys(self::ENERGIE_VARIABLEN, 0.0);
|
||||
}
|
||||
|
||||
$deltas['Hausenergie'] = EnergieMessung::hausenergie(
|
||||
$deltas['PVEnergie'],
|
||||
$deltas['NetzbezugEnergie'],
|
||||
$deltas['EinspeisungEnergie'],
|
||||
$deltas['BatterieLadenEnergie'],
|
||||
$deltas['BatterieEntladenEnergie']
|
||||
);
|
||||
|
||||
return $deltas;
|
||||
}
|
||||
|
||||
private function zeichneMesswerteAuf(): void
|
||||
{
|
||||
if (!$this->messungKonfiguriert()) {
|
||||
@@ -1789,11 +2010,13 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$leistungen = $this->leseMessleistungen();
|
||||
$letzterZeitstempel = $this->ReadAttributeInteger('MessungLetzterZeitstempel');
|
||||
$puffer = $this->leseJsonAttribut('MessungEnergiePuffer');
|
||||
if ($letzterZeitstempel > 0) {
|
||||
$energieDeltas = $this->leseEnergieZaehlerDeltas();
|
||||
if ($energieDeltas === null && $letzterZeitstempel > 0) {
|
||||
$sekunden = min(300, max(0, $jetzt - $letzterZeitstempel));
|
||||
foreach (EnergieMessung::energieDeltas($leistungen, $sekunden) as $ident => $delta) {
|
||||
$puffer[$ident] = max(0.0, (float) ($puffer[$ident] ?? 0.0)) + $delta;
|
||||
}
|
||||
$energieDeltas = EnergieMessung::energieDeltas($leistungen, $sekunden);
|
||||
}
|
||||
foreach ($energieDeltas ?? [] as $ident => $delta) {
|
||||
$puffer[$ident] = max(0.0, (float) ($puffer[$ident] ?? 0.0)) + $delta;
|
||||
}
|
||||
$this->WriteAttributeInteger('MessungLetzterZeitstempel', $jetzt);
|
||||
$this->WriteAttributeString(
|
||||
@@ -1899,8 +2122,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
&& $this->lizenzBerechtigungenPassen($lease, $lastspitzenmodus);
|
||||
$gueltig = $leaseGueltig && $berechtigungenPassen;
|
||||
$berechtigungenVeraltet = $basisGueltig && !$berechtigungenPassen;
|
||||
$geraetezugangFehlt = ($this->hatAnlagentopologie()
|
||||
|| $this->prognoseTelemetrieKonfiguriert()
|
||||
$geraetezugangFehlt = ($this->ReadPropertyBoolean('PrognoseAktiv')
|
||||
|| $this->ReadPropertyBoolean('StoerueberwachungAktiv'))
|
||||
&& $this->ReadAttributeString('PrognoseInstallationsToken') === '';
|
||||
$routineFaellig = (!$gueltig && !$berechtigungenVeraltet)
|
||||
@@ -2022,15 +2244,36 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
return $this->ReadPropertyBoolean('EinspeisebegrenzungAktiv') ? 1 : 0;
|
||||
}
|
||||
|
||||
private function peakShavingLizenziert(): bool
|
||||
private function peakShavingLizenziert(?string $lizenzcode = null): bool
|
||||
{
|
||||
return $this->berechtigungLizenziert(Lizenzpruefung::MANAGER_PEAK);
|
||||
return $this->berechtigungLizenziert(Lizenzpruefung::MANAGER_PEAK, $lizenzcode);
|
||||
}
|
||||
|
||||
private function berechtigungLizenziert(string $kennung): bool
|
||||
private function prognoseLizenziert(?string $lizenzcode = null): bool
|
||||
{
|
||||
return $this->berechtigungLizenziert(Lizenzpruefung::NETZFAHRPLAN, $lizenzcode)
|
||||
|| ($this->berechtigungLizenziert(Lizenzpruefung::PROGNOSE_PV, $lizenzcode)
|
||||
&& $this->berechtigungLizenziert(
|
||||
Lizenzpruefung::PROGNOSE_VERBRAUCH,
|
||||
$lizenzcode
|
||||
));
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $lease */
|
||||
private function prognoseBerechtigungInLease(array $lease): bool
|
||||
{
|
||||
return Lizenzpruefung::berechtigungsMenge($lease, Lizenzpruefung::NETZFAHRPLAN) > 0
|
||||
|| (Lizenzpruefung::berechtigungsMenge($lease, Lizenzpruefung::PROGNOSE_PV) > 0
|
||||
&& Lizenzpruefung::berechtigungsMenge(
|
||||
$lease,
|
||||
Lizenzpruefung::PROGNOSE_VERBRAUCH
|
||||
) > 0);
|
||||
}
|
||||
|
||||
private function berechtigungLizenziert(string $kennung, ?string $lizenzcode = null): bool
|
||||
{
|
||||
$lizenzcode = Lizenzpruefung::normalisiereCode(
|
||||
$this->ReadPropertyString('Lizenzcode')
|
||||
$lizenzcode ?? $this->ReadPropertyString('Lizenzcode')
|
||||
);
|
||||
if (!Lizenzpruefung::istLizenzcodeGueltig($lizenzcode)) {
|
||||
return false;
|
||||
@@ -2068,6 +2311,11 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if ($this->ReadPropertyBoolean('PrognoseAktiv')
|
||||
&& !$this->prognoseBerechtigungInLease($lease)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if ($this->ReadPropertyBoolean('NetzfahrplanAktiv')
|
||||
&& Lizenzpruefung::berechtigungsMenge(
|
||||
$lease,
|
||||
@@ -2108,9 +2356,9 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
return $optionen;
|
||||
}
|
||||
|
||||
private function aktualisiereLastspitzenformular(): void
|
||||
private function aktualisiereLastspitzenformular(?string $lizenzcode = null): void
|
||||
{
|
||||
$lizenziert = $this->peakShavingLizenziert();
|
||||
$lizenziert = $this->peakShavingLizenziert($lizenzcode);
|
||||
$modus = $lizenziert ? $this->ReadPropertyInteger('Lastspitzenmodus') : 0;
|
||||
$this->UpdateFormField(
|
||||
'Lastspitzenmodus',
|
||||
@@ -2118,6 +2366,9 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
json_encode($this->lastspitzenOptionen($lizenziert), JSON_THROW_ON_ERROR)
|
||||
);
|
||||
$this->UpdateFormField('Lastspitzenmodus', 'value', $modus);
|
||||
foreach (['PeakShavingTitel', 'EinspeisebegrenzungAktiv', 'Einspeisegrenze'] as $feld) {
|
||||
$this->UpdateFormField($feld, 'visible', $lizenziert);
|
||||
}
|
||||
$this->UpdateFormField(
|
||||
'Lastspitzengrenze',
|
||||
'visible',
|
||||
@@ -2134,6 +2385,43 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
}
|
||||
}
|
||||
|
||||
private function aktualisierePrognoseformular(?string $lizenzcode = null): void
|
||||
{
|
||||
$aktiv = $this->ReadPropertyBoolean('PrognoseAktiv');
|
||||
$lizenziert = $this->prognoseLizenziert($lizenzcode);
|
||||
$netzfahrplanLizenziert = $this->berechtigungLizenziert(
|
||||
Lizenzpruefung::NETZFAHRPLAN,
|
||||
$lizenzcode
|
||||
);
|
||||
$this->UpdateFormField('PrognoseAktiv', 'enabled', $lizenziert || $aktiv);
|
||||
$this->UpdateFormField(
|
||||
'PrognoseLizenzInformation',
|
||||
'caption',
|
||||
$this->prognoseLizenzStatustext($aktiv, $lizenziert)
|
||||
);
|
||||
$this->UpdateFormField(
|
||||
'NetzfahrplanAktiv',
|
||||
'enabled',
|
||||
$aktiv && $netzfahrplanLizenziert
|
||||
&& $this->peakShavingLizenziert($lizenzcode)
|
||||
);
|
||||
}
|
||||
|
||||
private function prognoseLizenzStatustext(bool $aktiv, bool $lizenziert): string
|
||||
{
|
||||
$managerID = $this->ReadAttributeString('LizenzInstallationID');
|
||||
if (!$lizenziert) {
|
||||
return 'Prognosen sind deaktiviert. Fuer die Manager-ID '
|
||||
. $managerID
|
||||
. ' fehlen forecast_pv plus forecast_load oder grid_schedule.';
|
||||
}
|
||||
|
||||
return ($aktiv ? 'Prognosen sind aktiviert. ' : 'Prognosen sind deaktiviert. ')
|
||||
. 'Die passende Lizenz fuer die Manager-ID '
|
||||
. $managerID
|
||||
. ' ist vorhanden.';
|
||||
}
|
||||
|
||||
private function lizenzStatustext(?string $lizenzcode = null): string
|
||||
{
|
||||
$code = Lizenzpruefung::normalisiereCode(
|
||||
@@ -2184,8 +2472,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$nutzlast = json_encode([
|
||||
'code' => $lizenzcode,
|
||||
'installationId' => $installationID,
|
||||
'issueDeviceToken' => ($this->hatAnlagentopologie()
|
||||
|| $this->prognoseTelemetrieKonfiguriert()
|
||||
'issueDeviceToken' => ($this->ReadPropertyBoolean('PrognoseAktiv')
|
||||
|| $this->ReadPropertyBoolean('StoerueberwachungAktiv'))
|
||||
&& $this->ReadAttributeString('PrognoseInstallationsToken') === '',
|
||||
], JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
|
||||
@@ -2256,10 +2543,11 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
|
||||
private function aktualisiereAnschlussstatus(): void
|
||||
{
|
||||
foreach ([
|
||||
'PrognoseAnschluss' => 'Prognosestatus',
|
||||
'SDLAnschluss' => 'SDLStatus',
|
||||
] as $property => $variable) {
|
||||
$this->setzeDiagnosewert(
|
||||
'Prognosestatus',
|
||||
$this->ReadPropertyBoolean('PrognoseAktiv') ? 'Wartet' : 'NichtVerwendet'
|
||||
);
|
||||
foreach (['SDLAnschluss' => 'SDLStatus'] as $property => $variable) {
|
||||
$konfiguration = json_decode($this->ReadPropertyString($property), true, 512, JSON_THROW_ON_ERROR);
|
||||
$aktiv = is_array($konfiguration) && ($konfiguration['Aktiv'] ?? false) === true;
|
||||
$this->setzeDiagnosewert($variable, $aktiv ? 'Fehler' : 'NichtVerwendet');
|
||||
@@ -2306,16 +2594,22 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
) {
|
||||
throw new InvalidArgumentException('Umschaltdifferenz muss zwischen 0 und 100 liegen.');
|
||||
}
|
||||
if ($this->ReadPropertyFloat('Einspeisegrenze') < 0.0
|
||||
|| $this->ReadPropertyFloat('Einspeisetoleranz') < 0.0
|
||||
) {
|
||||
throw new InvalidArgumentException('Einspeisegrenze und Einspeisetoleranz duerfen nicht negativ sein.');
|
||||
if ($this->ReadPropertyFloat('Einspeisegrenze') < 0.0) {
|
||||
throw new InvalidArgumentException('Einspeisegrenze darf nicht negativ sein.');
|
||||
}
|
||||
if ($this->ReadPropertyBoolean('EinspeisebegrenzungAktiv')) {
|
||||
$this->regelbareWechselrichter();
|
||||
}
|
||||
if ($this->ReadPropertyBoolean('PrognoseAktiv')
|
||||
&& !$this->prognoseTelemetrieKonfiguriert()
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Aktive Prognosen benoetigen Netzleistung, PV-Leistung und Batterie-Ladezustand.'
|
||||
);
|
||||
}
|
||||
if ($this->ReadPropertyBoolean('NetzfahrplanAktiv')
|
||||
&& (!$this->ReadPropertyBoolean('EinspeisebegrenzungAktiv')
|
||||
&& (!$this->ReadPropertyBoolean('PrognoseAktiv')
|
||||
|| !$this->ReadPropertyBoolean('EinspeisebegrenzungAktiv')
|
||||
|| !$this->prognoseTelemetrieKonfiguriert())
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
@@ -2329,6 +2623,7 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
$this->pvMessquellen();
|
||||
$this->batterieMessquellen();
|
||||
$this->batterieSocQuellen();
|
||||
$this->energieZaehlerquellen();
|
||||
foreach (['PrognoseAnschluss', 'SDLAnschluss'] as $property) {
|
||||
$wert = json_decode($this->ReadPropertyString($property), true, 512, JSON_THROW_ON_ERROR);
|
||||
if (!is_array($wert)) {
|
||||
@@ -2348,18 +2643,10 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
);
|
||||
}
|
||||
|
||||
private function hatAnlagentopologie(): bool
|
||||
{
|
||||
$topologie = $this->leseAnlagentopologie();
|
||||
|
||||
return $topologie['inverters'] !== []
|
||||
|| $topologie['pvArrays'] !== []
|
||||
|| $topologie['batteries'] !== [];
|
||||
}
|
||||
|
||||
private function prognoseTelemetrieKonfiguriert(): bool
|
||||
{
|
||||
return $this->messquellenKonfiguriert()
|
||||
return $this->ReadPropertyBoolean('PrognoseAktiv')
|
||||
&& $this->messquellenKonfiguriert()
|
||||
&& $this->aktuelleBatterieSoc() !== null;
|
||||
}
|
||||
|
||||
@@ -2407,6 +2694,11 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
|
||||
private function synchronisiereAnlagentopologie(): void
|
||||
{
|
||||
if (!$this->ReadPropertyBoolean('PrognoseAktiv')) {
|
||||
$this->setzeDiagnosewert('Prognosestatus', 'NichtVerwendet');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$topologie = $this->leseAnlagentopologie();
|
||||
if ($topologie['inverters'] === []
|
||||
|
||||
Reference in New Issue
Block a user