1293 lines
49 KiB
PHP
1293 lines
49 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Belevo\EnelixEMS;
|
|
|
|
trait ManagerEnergieTrait
|
|
{
|
|
private function konfiguriereEnergiearchiv(): void
|
|
{
|
|
$archivID = $this->archivID();
|
|
if ($archivID === 0) {
|
|
$this->protokolliere('Energiearchiv', 'Kein Archiv Control vorhanden.');
|
|
return;
|
|
}
|
|
$aktiv = $this->ReadPropertyBoolean('EnergieaufzeichnungAktiv');
|
|
$geaendert = [];
|
|
$leistungstyp = $this->verdichtungstyp(
|
|
$this->ReadPropertyInteger('LeistungsaufzeichnungMinuten')
|
|
);
|
|
$energietyp = $this->verdichtungstyp(
|
|
$this->ReadPropertyInteger('EnergieaufzeichnungMinuten')
|
|
);
|
|
foreach (self::LEISTUNGS_VARIABLEN as $ident) {
|
|
$variableID = $this->GetIDForIdent($ident);
|
|
AC_SetLoggingStatus($archivID, $variableID, $aktiv);
|
|
AC_SetAggregationType($archivID, $variableID, 0);
|
|
AC_SetGraphStatus($archivID, $variableID, $aktiv);
|
|
$regeln = [-1 => $leistungstyp];
|
|
$loeschen = $this->ReadPropertyInteger('LeistungLoeschenMonate');
|
|
if ($loeschen > 0) {
|
|
$regeln[$loeschen] = 7;
|
|
}
|
|
if ($aktiv && $this->setzeVerdichtung($archivID, $variableID, $regeln)) {
|
|
$geaendert[] = $variableID;
|
|
}
|
|
}
|
|
|
|
foreach (self::ENERGIE_VARIABLEN as $ident) {
|
|
$variableID = $this->GetIDForIdent($ident);
|
|
AC_SetLoggingStatus($archivID, $variableID, $aktiv);
|
|
AC_SetAggregationType($archivID, $variableID, 1);
|
|
AC_SetGraphStatus($archivID, $variableID, $aktiv);
|
|
$regeln = [-1 => $energietyp];
|
|
$verdichten = $this->ReadPropertyInteger('EnergieVerdichtenMonate');
|
|
if ($verdichten > 0) {
|
|
$regeln[$verdichten] = 3;
|
|
}
|
|
$loeschen = $this->ReadPropertyInteger('EnergieLoeschenMonate');
|
|
if ($loeschen > 0) {
|
|
$regeln[$loeschen] = 7;
|
|
}
|
|
if ($aktiv && $this->setzeVerdichtung($archivID, $variableID, $regeln)) {
|
|
$geaendert[] = $variableID;
|
|
}
|
|
}
|
|
|
|
foreach (IPS_GetChildrenIDs($this->InstanceID) as $variableID) {
|
|
$objekt = IPS_GetObject($variableID);
|
|
$ident = (string) ($objekt['ObjectIdent'] ?? '');
|
|
if ((int) ($objekt['ObjectType'] ?? -1) !== 2
|
|
|| !str_starts_with($ident, 'Diagramm')
|
|
) {
|
|
continue;
|
|
}
|
|
$istEnergie = str_contains($ident, 'Energie')
|
|
|| str_contains($ident, 'BatterieLaden_')
|
|
|| str_contains($ident, 'BatterieEntladen_');
|
|
AC_SetLoggingStatus($archivID, $variableID, $aktiv);
|
|
AC_SetAggregationType($archivID, $variableID, $istEnergie ? 1 : 0);
|
|
AC_SetGraphStatus($archivID, $variableID, $aktiv);
|
|
$regeln = [-1 => $istEnergie ? $energietyp : $leistungstyp];
|
|
$verdichten = $istEnergie
|
|
? $this->ReadPropertyInteger('EnergieVerdichtenMonate')
|
|
: 0;
|
|
if ($verdichten > 0) {
|
|
$regeln[$verdichten] = 3;
|
|
}
|
|
$loeschen = $istEnergie
|
|
? $this->ReadPropertyInteger('EnergieLoeschenMonate')
|
|
: $this->ReadPropertyInteger('LeistungLoeschenMonate');
|
|
if ($loeschen > 0) {
|
|
$regeln[$loeschen] = 7;
|
|
}
|
|
if ($aktiv && $this->setzeVerdichtung($archivID, $variableID, $regeln)) {
|
|
$geaendert[] = $variableID;
|
|
}
|
|
}
|
|
|
|
IPS_ApplyChanges($archivID);
|
|
foreach (array_unique($geaendert) as $variableID) {
|
|
AC_ReAggregateVariable($archivID, $variableID);
|
|
}
|
|
}
|
|
|
|
/** @param array<int, int> $regeln */
|
|
private function setzeVerdichtung(int $archivID, int $variableID, array $regeln): bool
|
|
{
|
|
ksort($regeln);
|
|
$aktuell = [];
|
|
$eintraege = AC_GetCompaction($archivID, $variableID);
|
|
if (is_array($eintraege)) {
|
|
foreach ($eintraege as $eintrag) {
|
|
$monat = (int) ($eintrag['MonthOffset'] ?? -999);
|
|
$typ = (int) ($eintrag['CompactionType'] ?? -1);
|
|
if ($monat >= -1 && $typ >= 0) {
|
|
$aktuell[$monat] = $typ;
|
|
}
|
|
}
|
|
}
|
|
ksort($aktuell);
|
|
if ($aktuell === $regeln) {
|
|
return false;
|
|
}
|
|
foreach (array_keys($aktuell) as $monat) {
|
|
AC_SetCompaction($archivID, $variableID, $monat, -1);
|
|
}
|
|
foreach ($regeln as $monat => $typ) {
|
|
AC_SetCompaction($archivID, $variableID, $monat, $typ);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function verdichtungstyp(int $minuten): int
|
|
{
|
|
return [1 => 0, 5 => 1, 60 => 2][$minuten] ?? 0;
|
|
}
|
|
|
|
private function archivID(): int
|
|
{
|
|
$archive = IPS_GetInstanceListByModuleID(self::ARCHIVE_MODULE_ID);
|
|
|
|
return $archive === [] ? 0 : (int) $archive[0];
|
|
}
|
|
|
|
private function synchronisiereEnergievisualisierungen(): void
|
|
{
|
|
$this->synchronisiereEnergyPie($this->ReadPropertyBoolean('EnergyPieAnzeigen'));
|
|
$this->synchronisiereEnergiediagramme(
|
|
$this->ReadPropertyBoolean('EnergiediagrammeAnzeigen')
|
|
);
|
|
$this->synchronisiereFunFacts($this->ReadPropertyBoolean('FunFactsAnzeigen'));
|
|
$this->synchronisiereEnergiefluss($this->ReadPropertyBoolean('EnergieflussAnzeigen'));
|
|
}
|
|
|
|
private function synchronisiereEnergyPie(bool $anzeigen): void
|
|
{
|
|
if ($anzeigen) {
|
|
$this->erstelleEnergyPie();
|
|
return;
|
|
}
|
|
$this->entferneInstanzUnterobjekt('EnergyPie', self::ENERGY_PIE_MODULE_ID);
|
|
}
|
|
|
|
private function synchronisiereEnergiediagramme(bool $anzeigen): void
|
|
{
|
|
if ($anzeigen) {
|
|
$this->erstelleEnergiediagramme();
|
|
return;
|
|
}
|
|
$this->entferneDiagrammUnterobjekt('Leistungsdiagramm');
|
|
$this->entferneDiagrammUnterobjekt('Energiediagramm');
|
|
$this->WriteAttributeString('EnergiediagrammKonfiguration', '{}');
|
|
}
|
|
|
|
private function synchronisiereFunFacts(bool $anzeigen): void
|
|
{
|
|
if ($anzeigen) {
|
|
$this->erstelleFunFacts();
|
|
return;
|
|
}
|
|
$variableID = $this->findeUnterobjekt('EnergieFunFacts');
|
|
if ($variableID === 0) {
|
|
return;
|
|
}
|
|
if ((int) IPS_GetObject($variableID)['ObjectType'] !== 2) {
|
|
throw new RuntimeException('Unterobjekt EnergieFunFacts ist keine Variable.');
|
|
}
|
|
$this->UnregisterVariable('EnergieFunFacts');
|
|
}
|
|
|
|
private function synchronisiereEnergiefluss(bool $anzeigen): void
|
|
{
|
|
if ($anzeigen) {
|
|
$this->erstelleEnergiefluss();
|
|
return;
|
|
}
|
|
$instanzID = $this->findeUnterobjekt('Energiefluss');
|
|
if ($instanzID !== 0) {
|
|
$objekt = IPS_GetObject($instanzID);
|
|
$modulID = $objekt['ObjectType'] === 1
|
|
? (string) IPS_GetInstance($instanzID)['ModuleInfo']['ModuleID']
|
|
: '';
|
|
if ($modulID !== self::ENERGY_FLOW_MODULE_ID) {
|
|
throw new RuntimeException('Unterobjekt Energiefluss hat einen unpassenden Typ.');
|
|
}
|
|
IPS_SetHidden($instanzID, true);
|
|
}
|
|
$this->WriteAttributeString('EnergieflussKonfiguration', '{}');
|
|
$this->WriteAttributeBoolean('EnergieflussNeuErzeugen', true);
|
|
}
|
|
|
|
private function entferneInstanzUnterobjekt(string $ident, string $modulID): void
|
|
{
|
|
$instanzID = $this->findeUnterobjekt($ident);
|
|
if ($instanzID === 0) {
|
|
return;
|
|
}
|
|
$objekt = IPS_GetObject($instanzID);
|
|
$istModulID = $objekt['ObjectType'] === 1
|
|
? (string) IPS_GetInstance($instanzID)['ModuleInfo']['ModuleID']
|
|
: '';
|
|
if ($istModulID !== $modulID) {
|
|
throw new RuntimeException('Unterobjekt ' . $ident . ' hat einen unpassenden Typ.');
|
|
}
|
|
IPS_DeleteInstance($instanzID);
|
|
}
|
|
|
|
private function entferneDiagrammUnterobjekt(string $ident): void
|
|
{
|
|
$medienID = $this->findeUnterobjekt($ident);
|
|
if ($medienID === 0) {
|
|
return;
|
|
}
|
|
$objekt = IPS_GetObject($medienID);
|
|
if ($objekt['ObjectType'] !== 5 || (int) IPS_GetMedia($medienID)['MediaType'] !== 4) {
|
|
throw new RuntimeException('Unterobjekt ' . $ident . ' ist kein Diagramm.');
|
|
}
|
|
IPS_DeleteMedia($medienID, true);
|
|
}
|
|
|
|
private function erstelleEnergyPie(): string
|
|
{
|
|
if (!in_array(self::ENERGY_PIE_MODULE_ID, IPS_GetModuleList(), true)) {
|
|
throw new RuntimeException(
|
|
'Das Modul Energy Pie aus Enelix Utils ist nicht installiert.'
|
|
);
|
|
}
|
|
$instanzID = $this->findeUnterobjekt('EnergyPie');
|
|
if ($instanzID === 0) {
|
|
$instanzID = IPS_CreateInstance(self::ENERGY_PIE_MODULE_ID);
|
|
IPS_SetParent($instanzID, $this->InstanceID);
|
|
IPS_SetIdent($instanzID, 'EnergyPie');
|
|
} else {
|
|
$objekt = IPS_GetObject($instanzID);
|
|
$modulID = $objekt['ObjectType'] === 1
|
|
? (string) IPS_GetInstance($instanzID)['ModuleInfo']['ModuleID']
|
|
: '';
|
|
if ($modulID !== self::ENERGY_PIE_MODULE_ID) {
|
|
throw new RuntimeException('Unterobjekt EnergyPie hat einen unpassenden Typ.');
|
|
}
|
|
IPS_SetHidden($instanzID, false);
|
|
|
|
return 'Bestehende Energy-Pie-Konfiguration bleibt unveraendert.';
|
|
}
|
|
IPS_SetName($instanzID, 'Energy Pie');
|
|
IPS_SetPosition($instanzID, 500);
|
|
IPS_SetHidden($instanzID, false);
|
|
foreach ([
|
|
'ProduktionZaehlerID' => $this->GetIDForIdent('PVEnergie'),
|
|
'EinspeisungZaehlerID' => $this->GetIDForIdent('EinspeisungEnergie'),
|
|
'NetzbezugZaehlerID' => $this->GetIDForIdent('NetzbezugEnergie'),
|
|
'VerbrauchZaehlerID' => $this->GetIDForIdent('Hausenergie'),
|
|
'Zaehlerfaktor' => 1.0,
|
|
] as $property => $wert) {
|
|
IPS_SetProperty($instanzID, $property, $wert);
|
|
}
|
|
IPS_ApplyChanges($instanzID);
|
|
|
|
return 'Energy Pie wurde erstellt oder aktualisiert.';
|
|
}
|
|
|
|
private function erstelleEnergiefluss(): string
|
|
{
|
|
if (!in_array(self::ENERGY_FLOW_MODULE_ID, IPS_GetModuleList(), true)) {
|
|
throw new RuntimeException('Das Symcon-Modul Energy Distribution ist nicht installiert.');
|
|
}
|
|
$neuErzeugen = $this->ReadAttributeBoolean('EnergieflussNeuErzeugen');
|
|
$instanzID = $this->findeUnterobjekt('Energiefluss');
|
|
if ($instanzID === 0) {
|
|
$instanzID = IPS_CreateInstance(self::ENERGY_FLOW_MODULE_ID);
|
|
IPS_SetParent($instanzID, $this->InstanceID);
|
|
IPS_SetIdent($instanzID, 'Energiefluss');
|
|
} else {
|
|
$objekt = IPS_GetObject($instanzID);
|
|
$modulID = $objekt['ObjectType'] === 1
|
|
? (string) IPS_GetInstance($instanzID)['ModuleInfo']['ModuleID']
|
|
: '';
|
|
if ($modulID !== self::ENERGY_FLOW_MODULE_ID) {
|
|
throw new RuntimeException('Unterobjekt Energiefluss hat einen unpassenden Typ.');
|
|
}
|
|
if (!$neuErzeugen) {
|
|
$this->visualisierungsKonfiguration('EnergieflussKonfiguration');
|
|
IPS_SetHidden($instanzID, false);
|
|
$this->aktualisiereEnergieflusswerte();
|
|
|
|
return 'Bestehende Energiefluss-Konfiguration bleibt unveraendert.';
|
|
}
|
|
}
|
|
$this->visualisierungsKonfiguration('EnergieflussKonfiguration');
|
|
IPS_SetName($instanzID, 'Energiefluss');
|
|
IPS_SetPosition($instanzID, 540);
|
|
IPS_SetHidden($instanzID, false);
|
|
|
|
$profil = $this->energieflussLeistungsprofil();
|
|
$pvID = $this->energieflussLeistungsvariable(
|
|
'EnergieflussPV',
|
|
'Energiefluss PV',
|
|
$profil,
|
|
600
|
|
);
|
|
$netzID = $this->energieflussLeistungsvariable(
|
|
'EnergieflussNetz',
|
|
'Energiefluss Netz',
|
|
$profil,
|
|
610
|
|
);
|
|
$hausID = $this->energieflussLeistungsvariable(
|
|
'EnergieflussHaus',
|
|
'Energiefluss Hausverbrauch',
|
|
$profil,
|
|
620
|
|
);
|
|
$batterieID = $this->energieflussLeistungsvariable(
|
|
'EnergieflussBatterie',
|
|
'Energiefluss Batteriespeicher',
|
|
$profil,
|
|
630
|
|
);
|
|
|
|
$verbraucher = $this->energieflussVerbraucher();
|
|
$knoten = [
|
|
$this->energieflussKnoten($pvID, 'PV-Anlage', 'solar'),
|
|
$this->energieflussKnoten($netzID, 'Stromnetz', 'grid'),
|
|
$this->energieflussKnoten($hausID, 'Hausverbrauch', 'consumer'),
|
|
];
|
|
$position = 640;
|
|
foreach ($verbraucher as $eintrag) {
|
|
$istID = $this->energieflussLeistungsvariable(
|
|
'EnergieflussIst_' . $eintrag['InstanzID'],
|
|
$eintrag['Name'] . ' Istleistung',
|
|
$profil,
|
|
$position
|
|
);
|
|
$sollID = $this->energieflussLeistungsvariable(
|
|
'EnergieflussSoll_' . $eintrag['InstanzID'],
|
|
$eintrag['Name'] . ' Sollleistung',
|
|
$profil,
|
|
$position + 1
|
|
);
|
|
$knoten[] = $this->energieflussKnoten(
|
|
$istID,
|
|
$eintrag['Name'],
|
|
$eintrag['Typ'],
|
|
true,
|
|
$sollID
|
|
);
|
|
$position += 10;
|
|
}
|
|
$knoten[] = $this->energieflussKnoten(
|
|
$batterieID,
|
|
'Batteriespeicher',
|
|
'storage'
|
|
);
|
|
|
|
IPS_SetProperty($instanzID, 'Calculation', true);
|
|
IPS_SetProperty(
|
|
$instanzID,
|
|
'Config',
|
|
json_encode($knoten, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES)
|
|
);
|
|
IPS_SetProperty($instanzID, 'EnableLimits', false);
|
|
IPS_SetProperty($instanzID, 'Restrictions', '[]');
|
|
IPS_ApplyChanges($instanzID);
|
|
$this->WriteAttributeBoolean('EnergieflussNeuErzeugen', false);
|
|
$this->aktualisiereEnergieflusswerte($verbraucher);
|
|
|
|
return 'Der Energiefluss wurde erstellt oder aktualisiert.';
|
|
}
|
|
|
|
/** @return array<string, mixed> */
|
|
private function energieflussKnoten(
|
|
int $variableID,
|
|
string $name,
|
|
string $typ,
|
|
bool $gruppiert = false,
|
|
int $infoVariableID = 1
|
|
): array {
|
|
return [
|
|
'VariableConfigType' => 0,
|
|
'VariableID' => $variableID,
|
|
'SecondaryVariableID' => 1,
|
|
'Name' => $name,
|
|
'Type' => $typ,
|
|
'Grouped' => $gruppiert,
|
|
'CalculationType' => 0,
|
|
'InfoVariableID' => $infoVariableID,
|
|
'Invert' => false,
|
|
];
|
|
}
|
|
|
|
private function energieflussLeistungsprofil(): string
|
|
{
|
|
$konfiguration = $this->visualisierungsKonfiguration(
|
|
'EnergieflussKonfiguration'
|
|
);
|
|
if ($konfiguration['EnergieflussLeistungseinheit'] !== 1) {
|
|
return '~Power';
|
|
}
|
|
|
|
$profil = 'Enelix.Power.kW';
|
|
if (!IPS_VariableProfileExists($profil)) {
|
|
IPS_CreateVariableProfile($profil, 2);
|
|
IPS_SetVariableProfileValues($profil, -1000000.0, 1000000.0, 0.001);
|
|
IPS_SetVariableProfileDigits($profil, 3);
|
|
IPS_SetVariableProfileText($profil, '', ' kW');
|
|
}
|
|
|
|
return $profil;
|
|
}
|
|
|
|
private function energieflussLeistungsvariable(
|
|
string $ident,
|
|
string $name,
|
|
string $profil,
|
|
int $position
|
|
): int {
|
|
$this->RegisterVariableFloat($ident, $name, $profil, $position);
|
|
$variableID = $this->GetIDForIdent($ident);
|
|
IPS_SetHidden($variableID, true);
|
|
|
|
return $variableID;
|
|
}
|
|
|
|
/** @return array<string, bool|int> */
|
|
private function visualisierungsKonfiguration(string $attribut): array
|
|
{
|
|
$konfiguration = $this->leseJsonAttribut($attribut);
|
|
$gueltig = isset(
|
|
$konfiguration['DiagrammPVModus'],
|
|
$konfiguration['DiagrammBatterieModus'],
|
|
$konfiguration['LadestationenSeparatAnzeigen'],
|
|
$konfiguration['VerbraucherSeparatAnzeigen'],
|
|
$konfiguration['EnergieflussLeistungseinheit']
|
|
);
|
|
if ($gueltig) {
|
|
return $konfiguration;
|
|
}
|
|
|
|
$konfiguration = [
|
|
'DiagrammPVModus' => $this->ReadPropertyInteger('DiagrammPVModus'),
|
|
'DiagrammBatterieModus' => $this->ReadPropertyInteger('DiagrammBatterieModus'),
|
|
'LadestationenSeparatAnzeigen' =>
|
|
$this->ReadPropertyBoolean('LadestationenSeparatAnzeigen'),
|
|
'VerbraucherSeparatAnzeigen' =>
|
|
$this->ReadPropertyBoolean('VerbraucherSeparatAnzeigen'),
|
|
'EnergieflussLeistungseinheit' =>
|
|
$this->ReadPropertyInteger('EnergieflussLeistungseinheit'),
|
|
];
|
|
$this->WriteAttributeString(
|
|
$attribut,
|
|
json_encode($konfiguration, JSON_THROW_ON_ERROR)
|
|
);
|
|
|
|
return $konfiguration;
|
|
}
|
|
|
|
/**
|
|
* @return list<array{
|
|
* InstanzID: int,
|
|
* Name: string,
|
|
* Typ: string,
|
|
* Istleistung_W: float,
|
|
* Sollleistung_W: float,
|
|
* Energie_Wh: float
|
|
* }>
|
|
*/
|
|
private function energieflussVerbraucher(
|
|
string $konfigurationsAttribut = 'EnergieflussKonfiguration'
|
|
): array {
|
|
$konfiguration = $this->visualisierungsKonfiguration($konfigurationsAttribut);
|
|
$cache = $this->leseJsonAttribut('VerbraucherCache');
|
|
$energien = $this->leseJsonAttribut('VerbraucherEnergie');
|
|
$ergebnis = [];
|
|
foreach ($this->konfigurierteAktiveVerbraucherIDs() as $instanzID) {
|
|
if (!IPS_InstanceExists($instanzID)) {
|
|
continue;
|
|
}
|
|
$daten = $cache[(string) $instanzID]['Daten'] ?? null;
|
|
$istleistung = 0.0;
|
|
$sollleistung = 0.0;
|
|
if (is_array($daten)) {
|
|
$istleistung = $this->verbraucherleistung($daten);
|
|
$sollleistung = (float) (ManagerRegler::angenommenerSollwert($daten) ?? 0);
|
|
}
|
|
$modulID = strtoupper(
|
|
(string) IPS_GetInstance($instanzID)['ModuleInfo']['ModuleID']
|
|
);
|
|
$katalog = self::VERBRAUCHER_LIZENZEN[$modulID]['Katalog'] ?? '';
|
|
if ($katalog === 'battery') {
|
|
continue;
|
|
}
|
|
if ($katalog === 'ev_charger'
|
|
&& !$konfiguration['LadestationenSeparatAnzeigen']
|
|
) {
|
|
continue;
|
|
}
|
|
if ($katalog !== 'ev_charger'
|
|
&& !$konfiguration['VerbraucherSeparatAnzeigen']
|
|
) {
|
|
continue;
|
|
}
|
|
$verbraucherEnergie = $energien[(string) $instanzID]['Energie_Wh'] ?? 0.0;
|
|
|
|
$ergebnis[] = [
|
|
'InstanzID' => $instanzID,
|
|
'Name' => IPS_GetName($instanzID),
|
|
'Typ' => $katalog === 'ev_charger' ? 'wallbox' : 'consumer',
|
|
'Istleistung_W' => $istleistung,
|
|
'Sollleistung_W' => $sollleistung,
|
|
'Energie_Wh' => is_numeric($verbraucherEnergie)
|
|
? (float) $verbraucherEnergie
|
|
: 0.0,
|
|
];
|
|
}
|
|
|
|
return $ergebnis;
|
|
}
|
|
|
|
/**
|
|
* @param list<array<string, mixed>>|null $verbraucher
|
|
*/
|
|
private function aktualisiereEnergieflusswerte(?array $verbraucher = null): void
|
|
{
|
|
if (!$this->ReadPropertyBoolean('EnergieflussAnzeigen')) {
|
|
return;
|
|
}
|
|
$verbraucher = $verbraucher ?? $this->energieflussVerbraucher();
|
|
$verbraucherleistung = 0.0;
|
|
foreach ($verbraucher as $eintrag) {
|
|
$this->setzeEnergieflussLeistungswert(
|
|
'EnergieflussIst_' . $eintrag['InstanzID'],
|
|
(float) $eintrag['Istleistung_W']
|
|
);
|
|
$this->setzeEnergieflussLeistungswert(
|
|
'EnergieflussSoll_' . $eintrag['InstanzID'],
|
|
(float) $eintrag['Sollleistung_W']
|
|
);
|
|
$verbraucherleistung += max(0.0, (float) $eintrag['Istleistung_W']);
|
|
}
|
|
|
|
try {
|
|
$leistungen = $this->leseMessleistungen();
|
|
} catch (Throwable $fehler) {
|
|
$this->protokolliere('Energiefluss', $fehler->getMessage());
|
|
return;
|
|
}
|
|
$this->setzeEnergieflussLeistungswert('EnergieflussPV', $leistungen['PV']);
|
|
$this->setzeEnergieflussLeistungswert('EnergieflussNetz', $leistungen['Netz']);
|
|
$this->setzeEnergieflussLeistungswert(
|
|
'EnergieflussHaus',
|
|
max(0.0, $leistungen['Haus'] - $verbraucherleistung)
|
|
);
|
|
$this->setzeEnergieflussLeistungswert(
|
|
'EnergieflussBatterie',
|
|
$leistungen['Batterie']
|
|
);
|
|
}
|
|
|
|
private function setzeEnergieflussLeistungswert(string $ident, float $watt): void
|
|
{
|
|
$variableID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
if (!is_int($variableID) || $variableID <= 0 || !IPS_VariableExists($variableID)) {
|
|
return;
|
|
}
|
|
$konfiguration = $this->visualisierungsKonfiguration(
|
|
'EnergieflussKonfiguration'
|
|
);
|
|
$faktor = $konfiguration['EnergieflussLeistungseinheit'] === 1 ? 0.001 : 1.0;
|
|
SetValue($variableID, $watt * $faktor);
|
|
}
|
|
|
|
private function erstelleEnergiediagramme(): string
|
|
{
|
|
$konfiguration = $this->visualisierungsKonfiguration(
|
|
'EnergiediagrammKonfiguration'
|
|
);
|
|
$pvAnlagen = $this->diagrammPVAnlagen();
|
|
$batterien = $this->diagrammBatterien();
|
|
$verbraucher = $this->energieflussVerbraucher(
|
|
'EnergiediagrammKonfiguration'
|
|
);
|
|
$this->registriereDiagrammVariablen($pvAnlagen, $batterien, $verbraucher);
|
|
$this->aktualisiereDiagrammMesswerte(true);
|
|
|
|
$leistung = [];
|
|
$energie = [];
|
|
if ($konfiguration['DiagrammPVModus'] !== 1) {
|
|
$leistung[] = $this->chartDatensatz(
|
|
'PVLeistungArchiv',
|
|
'PV gesamt',
|
|
'#F4C542',
|
|
'~Power'
|
|
);
|
|
$energie[] = $this->chartDatensatz(
|
|
'PVEnergie',
|
|
'PV gesamt',
|
|
'#F4C542',
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
}
|
|
if ($konfiguration['DiagrammPVModus'] !== 0) {
|
|
foreach ($pvAnlagen as $index => $anlage) {
|
|
$farbe = $this->diagrammFarbe($index);
|
|
if ($anlage['LeistungVariableID'] > 0) {
|
|
$leistung[] = $this->chartDatensatz(
|
|
$anlage['LeistungIdent'],
|
|
$anlage['Name'],
|
|
$farbe,
|
|
'~Power'
|
|
);
|
|
}
|
|
if ($anlage['EnergieVariableID'] > 0) {
|
|
$energie[] = $this->chartDatensatz(
|
|
$anlage['EnergieIdent'],
|
|
$anlage['Name'],
|
|
$farbe,
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$hausTitel = $verbraucher === [] ? 'Haus' : 'Haus ohne separat dargestellte Verbraucher';
|
|
$leistung[] = $this->chartDatensatz(
|
|
'DiagrammHausLeistung',
|
|
$hausTitel,
|
|
'#22A06B',
|
|
'~Power'
|
|
);
|
|
$leistung[] = $this->chartDatensatz(
|
|
'NetzleistungArchiv',
|
|
'Netz',
|
|
'#E95D5D',
|
|
'~Power'
|
|
);
|
|
$energie[] = $this->chartDatensatz(
|
|
'DiagrammHausEnergie',
|
|
$hausTitel,
|
|
'#22A06B',
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
$energie[] = $this->chartDatensatz(
|
|
'NetzbezugEnergie',
|
|
'Netzbezug',
|
|
'#E95D5D',
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
$energie[] = $this->chartDatensatz(
|
|
'EinspeisungEnergie',
|
|
'Netzeinspeisung',
|
|
'#2F80ED',
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
|
|
if ($konfiguration['DiagrammBatterieModus'] !== 1) {
|
|
$leistung[] = $this->chartDatensatz(
|
|
'BatterieleistungArchiv',
|
|
'Batterie gesamt',
|
|
'#8B5CF6',
|
|
'~Power'
|
|
);
|
|
$energie[] = $this->chartDatensatz(
|
|
'BatterieLadenEnergie',
|
|
'Batterie laden gesamt',
|
|
'#8B5CF6',
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
$energie[] = $this->chartDatensatz(
|
|
'BatterieEntladenEnergie',
|
|
'Batterie entladen gesamt',
|
|
'#F59E0B',
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
}
|
|
if ($konfiguration['DiagrammBatterieModus'] !== 0) {
|
|
foreach ($batterien as $index => $batterie) {
|
|
$farbe = $this->diagrammFarbe($index + count($pvAnlagen));
|
|
if ($batterie['LeistungVariableID'] > 0) {
|
|
$leistung[] = $this->chartDatensatz(
|
|
$batterie['LeistungIdent'],
|
|
$batterie['Name'],
|
|
$farbe,
|
|
'~Power'
|
|
);
|
|
}
|
|
if ($batterie['LadeenergieVariableID'] > 0) {
|
|
$energie[] = $this->chartDatensatz(
|
|
$batterie['LadeenergieIdent'],
|
|
$batterie['Name'] . ' laden',
|
|
$farbe,
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
}
|
|
if ($batterie['EntladeenergieVariableID'] > 0) {
|
|
$energie[] = $this->chartDatensatz(
|
|
$batterie['EntladeenergieIdent'],
|
|
$batterie['Name'] . ' entladen',
|
|
$farbe,
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($verbraucher as $index => $eintrag) {
|
|
$farbe = $this->diagrammFarbe(
|
|
$index + count($pvAnlagen) + count($batterien)
|
|
);
|
|
$leistung[] = $this->chartDatensatz(
|
|
'DiagrammVerbraucherLeistung_' . $eintrag['InstanzID'],
|
|
$eintrag['Name'],
|
|
$farbe,
|
|
'~Power'
|
|
);
|
|
$energie[] = $this->chartDatensatz(
|
|
'DiagrammVerbraucherEnergie_' . $eintrag['InstanzID'],
|
|
$eintrag['Name'],
|
|
$farbe,
|
|
'~Electricity',
|
|
'bar'
|
|
);
|
|
}
|
|
|
|
$this->erstelleDiagramm('Leistungsdiagramm', 'Leistungen', $leistung, 510);
|
|
$this->erstelleDiagramm('Energiediagramm', 'Energien', $energie, 520);
|
|
|
|
return 'Die Diagramme Leistungen und Energien wurden erstellt oder aktualisiert.';
|
|
}
|
|
/** @return list<array<string, mixed>> */
|
|
private function diagrammPVAnlagen(): array
|
|
{
|
|
$verwendet = [];
|
|
foreach ($this->leseAnlagenKonfigurationsliste('AnlagenPVFlaechen') as $flaeche) {
|
|
$wechselrichterID = trim((string) ($flaeche['WechselrichterID'] ?? ''));
|
|
if ($wechselrichterID !== '') {
|
|
$verwendet[$wechselrichterID] = true;
|
|
}
|
|
}
|
|
|
|
$ergebnis = [];
|
|
foreach ($this->leseAnlagenKonfigurationsliste('AnlagenWechselrichter') as $eintrag) {
|
|
$id = trim((string) ($eintrag['ID'] ?? ''));
|
|
if ($id === '' || ($verwendet !== [] && !isset($verwendet[$id]))) {
|
|
continue;
|
|
}
|
|
if ($verwendet === []
|
|
&& !in_array((string) ($eintrag['Typ'] ?? ''), ['pv', 'hybrid'], true)
|
|
) {
|
|
continue;
|
|
}
|
|
$suffix = substr(hash('sha256', 'pv:' . $id), 0, 12);
|
|
$leistungID = (int) ($eintrag['IstleistungVariableID'] ?? 0);
|
|
$energieID = (int) ($eintrag['ErzeugungsenergieVariableID'] ?? 0);
|
|
$ergebnis[] = [
|
|
'Name' => trim((string) ($eintrag['Name'] ?? '')) ?: $id,
|
|
'LeistungVariableID' => $this->istNumerischeVariable($leistungID)
|
|
? $leistungID
|
|
: 0,
|
|
'Leistungsfaktor' => (float) ($eintrag['Istleistungsfaktor'] ?? 1.0),
|
|
'EnergieVariableID' => $this->istNumerischeVariable($energieID)
|
|
? $energieID
|
|
: 0,
|
|
'Energiefaktor' => (float) ($eintrag['Erzeugungsenergiefaktor'] ?? 1.0),
|
|
'LeistungIdent' => 'DiagrammPVLeistung_' . $suffix,
|
|
'EnergieIdent' => 'DiagrammPVEnergie_' . $suffix,
|
|
];
|
|
}
|
|
|
|
return $ergebnis;
|
|
}
|
|
|
|
/** @return list<array<string, mixed>> */
|
|
private function diagrammBatterien(): array
|
|
{
|
|
$ergebnis = [];
|
|
foreach ($this->leseAnlagenKonfigurationsliste('AnlagenBatterien') as $eintrag) {
|
|
$id = trim((string) ($eintrag['ID'] ?? ''));
|
|
if ($id === '') {
|
|
continue;
|
|
}
|
|
$suffix = substr(hash('sha256', 'batterie:' . $id), 0, 12);
|
|
$leistungID = (int) ($eintrag['LeistungVariableID'] ?? 0);
|
|
$ladenID = (int) ($eintrag['LadeenergieVariableID'] ?? 0);
|
|
$entladenID = (int) ($eintrag['EntladeenergieVariableID'] ?? 0);
|
|
$ergebnis[] = [
|
|
'Name' => trim((string) ($eintrag['Name'] ?? '')) ?: $id,
|
|
'LeistungVariableID' => $this->istNumerischeVariable($leistungID)
|
|
? $leistungID
|
|
: 0,
|
|
'Leistungsfaktor' => (float) ($eintrag['Leistungsfaktor'] ?? 1.0),
|
|
'LadeenergieVariableID' => $this->istNumerischeVariable($ladenID)
|
|
? $ladenID
|
|
: 0,
|
|
'EntladeenergieVariableID' => $this->istNumerischeVariable($entladenID)
|
|
? $entladenID
|
|
: 0,
|
|
'Energiefaktor' => (float) ($eintrag['Energiefaktor'] ?? 1.0),
|
|
'LeistungIdent' => 'DiagrammBatterieLeistung_' . $suffix,
|
|
'LadeenergieIdent' => 'DiagrammBatterieLaden_' . $suffix,
|
|
'EntladeenergieIdent' => 'DiagrammBatterieEntladen_' . $suffix,
|
|
];
|
|
}
|
|
|
|
return $ergebnis;
|
|
}
|
|
|
|
/**
|
|
* @param list<array<string, mixed>> $pvAnlagen
|
|
* @param list<array<string, mixed>> $batterien
|
|
* @param list<array<string, mixed>> $verbraucher
|
|
*/
|
|
private function registriereDiagrammVariablen(
|
|
array $pvAnlagen,
|
|
array $batterien,
|
|
array $verbraucher
|
|
): void {
|
|
$position = 800;
|
|
$this->diagrammMessvariable(
|
|
'DiagrammHausLeistung',
|
|
'Diagramm Hausleistung',
|
|
'~Power',
|
|
$position
|
|
);
|
|
$this->diagrammMessvariable(
|
|
'DiagrammHausEnergie',
|
|
'Diagramm Hausenergie',
|
|
'~Electricity',
|
|
$position + 1
|
|
);
|
|
$position += 10;
|
|
|
|
foreach ($pvAnlagen as $anlage) {
|
|
if ($anlage['LeistungVariableID'] > 0) {
|
|
$this->diagrammMessvariable(
|
|
$anlage['LeistungIdent'],
|
|
$anlage['Name'] . ' Leistung',
|
|
'~Power',
|
|
$position
|
|
);
|
|
}
|
|
if ($anlage['EnergieVariableID'] > 0) {
|
|
$this->diagrammMessvariable(
|
|
$anlage['EnergieIdent'],
|
|
$anlage['Name'] . ' Energie',
|
|
'~Electricity',
|
|
$position + 1
|
|
);
|
|
}
|
|
$position += 10;
|
|
}
|
|
foreach ($batterien as $batterie) {
|
|
if ($batterie['LeistungVariableID'] > 0) {
|
|
$this->diagrammMessvariable(
|
|
$batterie['LeistungIdent'],
|
|
$batterie['Name'] . ' Leistung',
|
|
'~Power',
|
|
$position
|
|
);
|
|
}
|
|
if ($batterie['LadeenergieVariableID'] > 0) {
|
|
$this->diagrammMessvariable(
|
|
$batterie['LadeenergieIdent'],
|
|
$batterie['Name'] . ' Ladeenergie',
|
|
'~Electricity',
|
|
$position + 1
|
|
);
|
|
}
|
|
if ($batterie['EntladeenergieVariableID'] > 0) {
|
|
$this->diagrammMessvariable(
|
|
$batterie['EntladeenergieIdent'],
|
|
$batterie['Name'] . ' Entladeenergie',
|
|
'~Electricity',
|
|
$position + 2
|
|
);
|
|
}
|
|
$position += 10;
|
|
}
|
|
foreach ($verbraucher as $eintrag) {
|
|
$this->diagrammMessvariable(
|
|
'DiagrammVerbraucherLeistung_' . $eintrag['InstanzID'],
|
|
$eintrag['Name'] . ' Leistung',
|
|
'~Power',
|
|
$position
|
|
);
|
|
$this->diagrammMessvariable(
|
|
'DiagrammVerbraucherEnergie_' . $eintrag['InstanzID'],
|
|
$eintrag['Name'] . ' Energie',
|
|
'~Electricity',
|
|
$position + 1
|
|
);
|
|
$position += 10;
|
|
}
|
|
}
|
|
|
|
private function diagrammMessvariable(
|
|
string $ident,
|
|
string $name,
|
|
string $profil,
|
|
int $position
|
|
): int {
|
|
$this->RegisterVariableFloat($ident, $name, $profil, $position);
|
|
$variableID = $this->GetIDForIdent($ident);
|
|
IPS_SetHidden($variableID, true);
|
|
|
|
return $variableID;
|
|
}
|
|
|
|
private function aktualisiereDiagrammMesswerte(bool $energieAktualisieren = false): void
|
|
{
|
|
if ($this->findeUnterobjekt('Leistungsdiagramm') === 0
|
|
&& $this->findeUnterobjekt('Energiediagramm') === 0
|
|
&& $this->findeUnterobjekt('DiagrammHausLeistung') === 0
|
|
) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$leistungen = $this->leseMessleistungen();
|
|
$pvAnlagen = $this->diagrammPVAnlagen();
|
|
$batterien = $this->diagrammBatterien();
|
|
$verbraucher = $this->energieflussVerbraucher(
|
|
'EnergiediagrammKonfiguration'
|
|
);
|
|
} catch (Throwable $fehler) {
|
|
$this->protokolliere('Energiediagramme', $fehler->getMessage());
|
|
return;
|
|
}
|
|
|
|
$separateLeistung = 0.0;
|
|
$separateEnergieKWh = 0.0;
|
|
foreach ($verbraucher as $eintrag) {
|
|
$leistung = max(0.0, (float) $eintrag['Istleistung_W']);
|
|
$separateLeistung += $leistung;
|
|
$separateEnergieKWh += max(0.0, (float) $eintrag['Energie_Wh']) / 1000.0;
|
|
$this->setzeDiagrammMesswert(
|
|
'DiagrammVerbraucherLeistung_' . $eintrag['InstanzID'],
|
|
$leistung
|
|
);
|
|
if ($energieAktualisieren) {
|
|
$this->setzeDiagrammMesswert(
|
|
'DiagrammVerbraucherEnergie_' . $eintrag['InstanzID'],
|
|
max(0.0, (float) $eintrag['Energie_Wh']) / 1000.0
|
|
);
|
|
}
|
|
}
|
|
$this->setzeDiagrammMesswert(
|
|
'DiagrammHausLeistung',
|
|
max(0.0, $leistungen['Haus'] - $separateLeistung)
|
|
);
|
|
if ($energieAktualisieren) {
|
|
$this->setzeDiagrammMesswert(
|
|
'DiagrammHausEnergie',
|
|
max(
|
|
0.0,
|
|
(float) $this->GetValue('Hausenergie') - $separateEnergieKWh
|
|
)
|
|
);
|
|
}
|
|
|
|
foreach ($pvAnlagen as $anlage) {
|
|
if ($anlage['LeistungVariableID'] > 0) {
|
|
$this->setzeDiagrammMesswert(
|
|
$anlage['LeistungIdent'],
|
|
max(
|
|
0.0,
|
|
(float) GetValue($anlage['LeistungVariableID'])
|
|
* $anlage['Leistungsfaktor']
|
|
)
|
|
);
|
|
}
|
|
if ($energieAktualisieren && $anlage['EnergieVariableID'] > 0) {
|
|
$this->setzeDiagrammMesswert(
|
|
$anlage['EnergieIdent'],
|
|
max(
|
|
0.0,
|
|
(float) GetValue($anlage['EnergieVariableID'])
|
|
* $anlage['Energiefaktor']
|
|
)
|
|
);
|
|
}
|
|
}
|
|
foreach ($batterien as $batterie) {
|
|
if ($batterie['LeistungVariableID'] > 0) {
|
|
$this->setzeDiagrammMesswert(
|
|
$batterie['LeistungIdent'],
|
|
(float) GetValue($batterie['LeistungVariableID'])
|
|
* $batterie['Leistungsfaktor']
|
|
);
|
|
}
|
|
if ($energieAktualisieren && $batterie['LadeenergieVariableID'] > 0) {
|
|
$this->setzeDiagrammMesswert(
|
|
$batterie['LadeenergieIdent'],
|
|
max(
|
|
0.0,
|
|
(float) GetValue($batterie['LadeenergieVariableID'])
|
|
* $batterie['Energiefaktor']
|
|
)
|
|
);
|
|
}
|
|
if ($energieAktualisieren && $batterie['EntladeenergieVariableID'] > 0) {
|
|
$this->setzeDiagrammMesswert(
|
|
$batterie['EntladeenergieIdent'],
|
|
max(
|
|
0.0,
|
|
(float) GetValue($batterie['EntladeenergieVariableID'])
|
|
* $batterie['Energiefaktor']
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function setzeDiagrammMesswert(string $ident, float $wert): void
|
|
{
|
|
$variableID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
if (is_int($variableID) && $variableID > 0 && IPS_VariableExists($variableID)) {
|
|
SetValue($variableID, $wert);
|
|
}
|
|
}
|
|
|
|
private function diagrammFarbe(int $index): string
|
|
{
|
|
$farben = [
|
|
'#A85D3C',
|
|
'#007C91',
|
|
'#6B5B95',
|
|
'#2E7D32',
|
|
'#C43E1C',
|
|
'#3D5A80',
|
|
'#8D6E63',
|
|
'#00897B',
|
|
];
|
|
|
|
return $farben[$index % count($farben)];
|
|
}
|
|
|
|
/** @param list<array<string, mixed>> $datensaetze */
|
|
private function erstelleDiagramm(
|
|
string $ident,
|
|
string $name,
|
|
array $datensaetze,
|
|
int $position
|
|
): int {
|
|
$medienID = $this->findeUnterobjekt($ident);
|
|
$neu = $medienID === 0;
|
|
if ($neu) {
|
|
$medienID = IPS_CreateMedia(4);
|
|
IPS_SetParent($medienID, $this->InstanceID);
|
|
IPS_SetIdent($medienID, $ident);
|
|
}
|
|
$objekt = IPS_GetObject($medienID);
|
|
if ($objekt['ObjectType'] !== 5 || (int) IPS_GetMedia($medienID)['MediaType'] !== 4) {
|
|
throw new RuntimeException('Unterobjekt ' . $ident . ' ist kein Diagramm.');
|
|
}
|
|
if (!$neu) {
|
|
IPS_SetHidden($medienID, false);
|
|
|
|
return $medienID;
|
|
}
|
|
IPS_SetName($medienID, $name);
|
|
IPS_SetPosition($medienID, $position);
|
|
IPS_SetHidden($medienID, false);
|
|
IPS_SetMediaFile($medienID, 'media/' . $medienID . '.chart', false);
|
|
IPS_SetMediaContent(
|
|
$medienID,
|
|
base64_encode(json_encode(['datasets' => $datensaetze], JSON_THROW_ON_ERROR))
|
|
);
|
|
|
|
return $medienID;
|
|
}
|
|
/** @return array<string, mixed> */
|
|
private function chartDatensatz(
|
|
string $ident,
|
|
string $titel,
|
|
string $farbe,
|
|
string $profil,
|
|
string $typ = 'line'
|
|
): array {
|
|
return [
|
|
'variableID' => $this->GetIDForIdent($ident),
|
|
'fillColor' => $typ === 'bar' ? $farbe : 'clear',
|
|
'strokeColor' => $farbe,
|
|
'timeOffset' => 0,
|
|
'visible' => true,
|
|
'title' => $titel,
|
|
'type' => $typ,
|
|
'side' => 'left',
|
|
'profile' => $profil,
|
|
];
|
|
}
|
|
|
|
private function findeUnterobjekt(string $ident): int
|
|
{
|
|
$objektID = @IPS_GetObjectIDByIdent($ident, $this->InstanceID);
|
|
|
|
return is_int($objektID) && $objektID > 0 ? $objektID : 0;
|
|
}
|
|
|
|
private function erstelleFunFacts(): string
|
|
{
|
|
$this->RegisterVariableString('EnergieFunFacts', 'Energy Facts', '~HTMLBox', 530);
|
|
$variableID = $this->GetIDForIdent('EnergieFunFacts');
|
|
IPS_SetHidden($variableID, false);
|
|
$this->aktualisiereFunFacts($variableID);
|
|
|
|
return 'Energy Facts wurden erstellt oder aktualisiert.';
|
|
}
|
|
|
|
private function aktualisiereFunFacts(int $variableID): void
|
|
{
|
|
$archivID = $this->archivID();
|
|
if ($archivID === 0) {
|
|
SetValue($variableID, '<div style="padding:16px">Archiv nicht verfuegbar.</div>');
|
|
return;
|
|
}
|
|
$heute = strtotime('today');
|
|
$jahr = mktime(0, 0, 0, 1, 1, (int) date('Y'));
|
|
$jetzt = time();
|
|
$pvID = $this->GetIDForIdent('PVEnergie');
|
|
$hausID = $this->GetIDForIdent('Hausenergie');
|
|
$bezugID = $this->GetIDForIdent('NetzbezugEnergie');
|
|
$exportID = $this->GetIDForIdent('EinspeisungEnergie');
|
|
$pvHeute = $this->energieSumme($archivID, $pvID, $heute, $jetzt);
|
|
$pvJahr = $this->energieSumme($archivID, $pvID, $jahr, $jetzt);
|
|
$pvTotal = max(0.0, (float) GetValue($pvID));
|
|
[$besterTag, $besterTagZeit] = $this->maximalerTageswert($archivID, $pvID);
|
|
[$maxPV, $maxPVZeit] = $this->maximaleLeistung(
|
|
$archivID,
|
|
$this->GetIDForIdent('PVLeistungArchiv')
|
|
);
|
|
[$maxHausTag, $maxHausTagZeit] = $this->maximalerTageswert($archivID, $hausID);
|
|
[$maxHaus, $maxHausZeit] = $this->maximaleLeistung(
|
|
$archivID,
|
|
$this->GetIDForIdent('HausverbrauchLeistungArchiv')
|
|
);
|
|
[$maxBezug, $maxBezugZeit] = $this->maximalerTageswert($archivID, $bezugID);
|
|
[$maxExport, $maxExportZeit] = $this->maximalerTageswert($archivID, $exportID);
|
|
|
|
$werte = [
|
|
'{{PV_HEUTE}}' => $this->formatiereEnergie($pvHeute),
|
|
'{{PV_JAHR}}' => $this->formatiereEnergie($pvJahr),
|
|
'{{PV_TOTAL}}' => $this->formatiereEnergie($pvTotal),
|
|
'{{BESTER_TAG}}' => $this->formatiereEnergie($besterTag),
|
|
'{{BESTER_TAG_DATUM}}' => $this->formatiereDatum($besterTagZeit),
|
|
'{{MAX_PV}}' => $this->formatiereLeistung($maxPV),
|
|
'{{MAX_PV_DATUM}}' => $this->formatiereDatum($maxPVZeit),
|
|
'{{MAX_HAUS_TAG}}' => $this->formatiereEnergie($maxHausTag),
|
|
'{{MAX_HAUS_TAG_DATUM}}' => $this->formatiereDatum($maxHausTagZeit),
|
|
'{{MAX_HAUS}}' => $this->formatiereLeistung($maxHaus),
|
|
'{{MAX_HAUS_DATUM}}' => $this->formatiereDatum($maxHausZeit),
|
|
'{{MAX_BEZUG}}' => $this->formatiereEnergie($maxBezug),
|
|
'{{MAX_BEZUG_DATUM}}' => $this->formatiereDatum($maxBezugZeit),
|
|
'{{MAX_EXPORT}}' => $this->formatiereEnergie($maxExport),
|
|
'{{MAX_EXPORT_DATUM}}' => $this->formatiereDatum($maxExportZeit),
|
|
'{{HEIZOEL}}' => number_format($pvTotal / 10.0, 0, ',', "'") . ' Liter',
|
|
'{{FLUGKM}}' => number_format($pvTotal * 2.4, 0, ',', "'") . ' km',
|
|
];
|
|
SetValue($variableID, strtr($this->funFactsHtml(), $werte));
|
|
}
|
|
|
|
private function energieSumme(int $archivID, int $variableID, int $start, int $ende): float
|
|
{
|
|
$werte = @AC_GetAggregatedValues($archivID, $variableID, 1, $start, $ende, 0);
|
|
if (!is_array($werte)) {
|
|
return 0.0;
|
|
}
|
|
|
|
return array_sum(array_map(
|
|
static fn (array $wert): float => max(0.0, (float) ($wert['Avg'] ?? 0.0)),
|
|
$werte
|
|
));
|
|
}
|
|
|
|
/** @return array{0: float, 1: int} */
|
|
private function maximalerTageswert(int $archivID, int $variableID): array
|
|
{
|
|
$werte = @AC_GetAggregatedValues($archivID, $variableID, 1, 0, time(), 0);
|
|
$maximum = 0.0;
|
|
$zeit = 0;
|
|
if (is_array($werte)) {
|
|
foreach ($werte as $wert) {
|
|
$kandidat = max(0.0, (float) ($wert['Avg'] ?? 0.0));
|
|
if ($kandidat > $maximum) {
|
|
$maximum = $kandidat;
|
|
$zeit = (int) ($wert['TimeStamp'] ?? 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return [$maximum, $zeit];
|
|
}
|
|
/** @return array{0: float, 1: int} */
|
|
private function maximaleLeistung(int $archivID, int $variableID): array
|
|
{
|
|
$werte = @AC_GetAggregatedValues($archivID, $variableID, 1, 0, time(), 0);
|
|
$maximum = 0.0;
|
|
$zeit = 0;
|
|
if (is_array($werte)) {
|
|
foreach ($werte as $wert) {
|
|
$kandidat = max(0.0, (float) ($wert['Max'] ?? 0.0));
|
|
if ($kandidat > $maximum) {
|
|
$maximum = $kandidat;
|
|
$zeit = (int) ($wert['MaxTime'] ?? $wert['TimeStamp'] ?? 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return [$maximum, $zeit];
|
|
}
|
|
|
|
private function formatiereEnergie(float $wert): string
|
|
{
|
|
if ($wert >= 1000.0) {
|
|
return number_format($wert / 1000.0, 2, ',', "'") . ' MWh';
|
|
}
|
|
|
|
return number_format($wert, 1, ',', "'") . ' kWh';
|
|
}
|
|
private function formatiereLeistung(float $wert): string
|
|
{
|
|
if ($wert >= 10000.0) {
|
|
return number_format($wert / 1000.0, 1, ',', "'") . ' kW';
|
|
}
|
|
|
|
return number_format($wert, 0, ',', "'") . ' W';
|
|
}
|
|
|
|
private function formatiereDatum(int $zeit): string
|
|
{
|
|
return $zeit > 0 ? date('d.m.Y', $zeit) : 'Noch keine Daten';
|
|
}
|
|
|
|
private function funFactsHtml(): string
|
|
{
|
|
return <<<'HTML'
|
|
<style>
|
|
.enx-facts{box-sizing:border-box;font-family:system-ui,-apple-system,sans-serif;color:#182230;padding:16px;background:#f4f7f6}
|
|
.enx-facts *{box-sizing:border-box}.enx-facts-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
|
|
.enx-fact-panel{min-width:0;background:#fff;border:1px solid #dfe7e4;border-radius:8px;overflow:hidden;box-shadow:0 4px 18px rgba(17,44,36,.08)}
|
|
.enx-fact-head{padding:15px 18px 13px;border-bottom:3px solid #22a06b;display:flex;align-items:center;justify-content:space-between}
|
|
.enx-fact-head b{font-size:18px}.enx-fact-head span{font-size:11px;letter-spacing:.08em;color:#60736c}
|
|
.enx-fact-body{padding:5px 18px 10px}.enx-fact-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;padding:11px 0;border-bottom:1px solid #edf1ef}
|
|
.enx-fact-row:last-child{border-bottom:0}.enx-fact-label{min-width:0;color:#52635d;font-size:13px}.enx-fact-value{text-align:right;font-size:15px;font-weight:700;white-space:nowrap}
|
|
.enx-fact-date{display:block;color:#81908b;font-size:11px;font-weight:500;margin-top:2px}.enx-fact-panel.grid .enx-fact-head{border-color:#2f80ed}
|
|
.enx-fact-panel.impact .enx-fact-head{border-color:#f4c542}.enx-impact{padding:18px}.enx-impact-value{font-size:30px;font-weight:750;line-height:1.15}
|
|
.enx-impact-label{font-size:12px;color:#60736c;margin:5px 0 20px}.enx-impact-note{font-size:11px;color:#81908b;line-height:1.45}
|
|
@media(max-width:820px){.enx-facts-grid{grid-template-columns:1fr}.enx-facts{padding:10px}}
|
|
</style>
|
|
<div class="enx-facts"><div class="enx-facts-grid">
|
|
<section class="enx-fact-panel"><div class="enx-fact-head"><b>Solar</b><span>ERZEUGUNG</span></div><div class="enx-fact-body">
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Heute</span><span class="enx-fact-value">{{PV_HEUTE}}</span></div>
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Dieses Jahr</span><span class="enx-fact-value">{{PV_JAHR}}</span></div>
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Total</span><span class="enx-fact-value">{{PV_TOTAL}}</span></div>
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Bester Tag</span><span class="enx-fact-value">{{BESTER_TAG}}<small class="enx-fact-date">{{BESTER_TAG_DATUM}}</small></span></div>
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Max. Leistung</span><span class="enx-fact-value">{{MAX_PV}}<small class="enx-fact-date">{{MAX_PV_DATUM}}</small></span></div>
|
|
</div></section>
|
|
<section class="enx-fact-panel grid"><div class="enx-fact-head"><b>Netz & Haus</b><span>SPITZENWERTE</span></div><div class="enx-fact-body">
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Max. Tagesverbrauch</span><span class="enx-fact-value">{{MAX_HAUS_TAG}}<small class="enx-fact-date">{{MAX_HAUS_TAG_DATUM}}</small></span></div>
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Max. Hauslast</span><span class="enx-fact-value">{{MAX_HAUS}}<small class="enx-fact-date">{{MAX_HAUS_DATUM}}</small></span></div>
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Max. Tagesbezug</span><span class="enx-fact-value">{{MAX_BEZUG}}<small class="enx-fact-date">{{MAX_BEZUG_DATUM}}</small></span></div>
|
|
<div class="enx-fact-row"><span class="enx-fact-label">Max. Einspeisung</span><span class="enx-fact-value">{{MAX_EXPORT}}<small class="enx-fact-date">{{MAX_EXPORT_DATUM}}</small></span></div>
|
|
</div></section>
|
|
<section class="enx-fact-panel impact"><div class="enx-fact-head"><b>Wirkung</b><span>VERGLEICHE</span></div><div class="enx-impact">
|
|
<div class="enx-impact-value">{{HEIZOEL}}</div><div class="enx-impact-label">rechnerisch kompensiertes Heizoel</div>
|
|
<div class="enx-impact-value">{{FLUGKM}}</div><div class="enx-impact-label">rechnerisch kompensierte Flugkilometer</div>
|
|
<div class="enx-impact-note">Vergleichswerte auf Basis von 10 kWh pro Liter Heizoel und 2,4 Flugkilometern pro erzeugter kWh.</div>
|
|
</div></section>
|
|
</div></div>
|
|
HTML;
|
|
}
|
|
}
|