feat: Demoanlage um Batterie und Tagesprofile erweitern
This commit is contained in:
@@ -2,13 +2,16 @@
|
||||
|
||||
Die Demo erzeugt idempotent eine vollständige, spielbare EMS-Anlage in IP-Symcon 8.x:
|
||||
|
||||
- Enelix Manager mit manueller Zuordnung,
|
||||
- Enelix Manager mit manueller Zuordnung von fünf steuerbaren Teilnehmern,
|
||||
- zwei mehrstufige Boiler (1,2/2,4 kW und 1,5/3,0 kW),
|
||||
- einen einstufigen Entfeuchter (0,9 kW),
|
||||
- simulierte PV-Erzeugung, Grundlast, Netzleistung und Boilertemperaturen,
|
||||
- native Symcon-Instanz `Energy Distribution` für den Energiefluss,
|
||||
- eigene Kachelansichten mit allen Laufzeitparametern je Boiler,
|
||||
- kombinierten Verlauf für PV-Leistung, Hausverbrauch und Netzleistung,
|
||||
- einen zweistufigen Pufferspeicher (1,8/3,6 kW) mit Heizkurve,
|
||||
- einen bidirektionalen Batteriespeicher (10 kWh, +/-3,5 kW),
|
||||
- simulierte PV-Erzeugung, variable Bewölkung, Tageslastgang, Zusatzlast,
|
||||
Außentemperatur, Wärmeverluste, Netzleistung und Speicherzustände,
|
||||
- native Symcon-Instanz `Energy Distribution` für acht Energieflussknoten,
|
||||
- eigene Kachelansichten mit Laufzeitparametern für Boiler, Puffer und Batterie,
|
||||
- kombinierten Verlauf für PV-Leistung, Hausverbrauch, EMS-Leistung und Netzleistung,
|
||||
- kombinierten Verlauf der kumulierten PV-, Haus-, Bezugs- und Einspeiseenergie,
|
||||
- Archivierung der Leistungs-, Energie- und Temperaturwerte.
|
||||
|
||||
@@ -27,26 +30,43 @@ print_r($result);
|
||||
Ein weiterer Lauf aktualisiert dieselbe Anlage anhand stabiler Idents und erzeugt keine Duplikate.
|
||||
Bestehende, nicht zur Demo gehörende Objekte werden nicht verändert oder gelöscht.
|
||||
|
||||
Der Bootstrap hinterlegt absichtlich keinen Lizenzcode und überschreibt eine
|
||||
bereits konfigurierte Manager-Lizenz nicht. Bei einer neuen Anlage wird die
|
||||
Struktur vollständig erstellt; der Manager bleibt bis zur regulären
|
||||
Lizenzaktivierung im Konfigurationsformular auf Status 203. Solange der Manager
|
||||
nicht freigegeben ist, bleiben seine Zuordnung und die Verbraucher deaktiviert,
|
||||
damit keine Lizenzfehler protokolliert werden. Nach der Aktivierung den
|
||||
Bootstrap erneut ausführen; er ordnet dann alle fünf Teilnehmer zu und startet
|
||||
die Regelung.
|
||||
|
||||
## Bedienung
|
||||
|
||||
In der Kachelvisualisierung öffnet die Startkategorie `Enelix 2 Demoanlage`.
|
||||
Unter `Simulation` lassen sich Tageslauf, Tageszeit, Geschwindigkeit,
|
||||
PV-Spitzenleistung, Bewölkung und Haus-Grundlast verändern. Der Manager verteilt
|
||||
den verfügbaren PV-Überschuss nach Priorität auf beide Boiler und den Entfeuchter.
|
||||
dynamische Profile, PV-Spitzenleistung, mittlere Bewölkung, Haus-Grundlast und
|
||||
eine ungeregelte Zusatzlast verändern. Bei aktivierten dynamischen Profilen
|
||||
entstehen reproduzierbare Morgen- und Abendspitzen, ziehende Bewölkung und ein
|
||||
Tagesgang der Außentemperatur. Der Manager verteilt den verfügbaren
|
||||
PV-Überschuss nach Priorität auf beide Boiler, Entfeuchter, Pufferspeicher und
|
||||
Batterie.
|
||||
|
||||
`Übersicht` zeigt die native Symcon-Energieverteilung. Die Kacheln `Boiler 1`
|
||||
und `Boiler 2` öffnen direkt sämtliche Bedien-, Status- und Diagnosevariablen,
|
||||
ohne zuerst das Instanz-Konfigurationsformular zu zeigen. Unter `Leistung und
|
||||
Energie` liegen die beiden kombinierten Zeitdiagramme und die Energiezähler.
|
||||
Die Energiesummen verwenden die beschleunigte Simulationszeit.
|
||||
`Übersicht` zeigt die native Symcon-Energieverteilung. Die Kacheln `Boiler 1`,
|
||||
`Boiler 2`, `Pufferspeicher` und `Batteriespeicher` öffnen direkt die jeweiligen
|
||||
Bedien-, Status- und Diagnosevariablen, ohne zuerst das
|
||||
Instanz-Konfigurationsformular zu zeigen. Positive Batterieleistung bedeutet
|
||||
Laden, negative Leistung Entladen. Unter `Leistung und Energie` liegen die
|
||||
beiden kombinierten Zeitdiagramme und die Energiezähler. Die Energiesummen,
|
||||
Temperaturmodelle und der Ladezustand verwenden die beschleunigte
|
||||
Simulationszeit.
|
||||
|
||||
## Validierung
|
||||
|
||||
- Alle vier Enelix-Instanzen müssen Status 102 besitzen.
|
||||
- Der Manager muss drei Verbraucher melden.
|
||||
- Bei ausreichendem PV-Überschuss müssen Verbraucher schalten.
|
||||
- Netzleistung, PV-Leistung, Hausverbrauch, Verbraucherleistungen, Energiesummen und Temperaturen werden archiviert.
|
||||
- Die native Energieverteilung muss sechs Knoten enthalten.
|
||||
- Die fünf Verbraucher müssen Status 102 besitzen; der Manager nach Lizenzaktivierung ebenfalls (vorher erwarteter Status 203).
|
||||
- Der Manager muss fünf Teilnehmer aus vier unterschiedlichen Modultypen melden.
|
||||
- Bei ausreichendem PV-Überschuss müssen thermische Verbraucher schalten und die Batterie laden.
|
||||
- Puffertemperatur, Außentemperatur und Batterie-Ladezustand müssen sich über den Tageslauf verändern.
|
||||
- Netzleistung, PV-Leistung, Hausverbrauch, EMS-Leistung, Einzelverbräuche, Speicherleistung, Energiesummen und Temperaturen werden archiviert.
|
||||
- Die native Energieverteilung muss acht Knoten enthalten.
|
||||
- Beide Diagrammkacheln müssen mindestens drei beschriftete Zeitreihen anzeigen.
|
||||
- Ein erneuter Installationslauf muss dieselbe Root-ID zurückgeben.
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ const ENELIX_DEMO_ROOT_IDENT = 'ENELIX_DEMO_2';
|
||||
const ENELIX_DEMO_MANAGER_MODULE = '{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}';
|
||||
const ENELIX_DEMO_BOILER_MODULE = '{B7C54AF4-AD7D-4FE4-B75D-203693906251}';
|
||||
const ENELIX_DEMO_SINGLE_MODULE = '{15879A4E-D0C2-4495-83DE-46E1E462591E}';
|
||||
const ENELIX_DEMO_BUFFER_MODULE = '{C92D5EEF-9632-47A5-9659-4B02BF40FBE9}';
|
||||
const ENELIX_DEMO_BATTERY_MODULE = '{437FB683-517F-4FEC-8CCB-FE6B0A62B69E}';
|
||||
const ENELIX_DEMO_DUMMY_MODULE = '{485D0419-BE97-4548-AA9C-C083EB82E61E}';
|
||||
const ENELIX_DEMO_TILE_MODULE = '{B5B875BB-9B76-45FD-4E67-2607E45B3AC4}';
|
||||
const ENELIX_DEMO_ARCHIVE_MODULE = '{43192F0B-135B-4CE7-A0A7-1475603F3060}';
|
||||
@@ -28,15 +30,17 @@ function enelixDemoInstall(): array
|
||||
|
||||
enelixDemoEnsureProfiles();
|
||||
|
||||
$legacyFlow = enelixDemoEnsureVariable($dashboard, 'Energiefluss', 'Energiefluss (Legacy)', 3, '~HTMLBox', '', 90);
|
||||
$legacyFlow = enelixDemoEnsureVariable($dashboard, 'Energiefluss', 'Energiefluss (Legacy)', 3, '~HTMLBox', '', 900);
|
||||
IPS_SetHidden($legacyFlow, true);
|
||||
$pvDisplay = enelixDemoEnsureVariable($dashboard, 'PVLeistung', 'PV-Leistung', 2, 'ENELIX.Demo.PowerPositive', 0.0, 20);
|
||||
$houseDisplay = enelixDemoEnsureVariable($dashboard, 'Hausverbrauch', 'Hausverbrauch gesamt', 2, 'ENELIX.Demo.PowerPositive', 0.0, 30);
|
||||
$loadDisplay = enelixDemoEnsureVariable($dashboard, 'Verbraucherleistung', 'Geregelte Verbraucher', 2, 'ENELIX.Demo.PowerPositive', 0.0, 40);
|
||||
$loadDisplay = enelixDemoEnsureVariable($dashboard, 'Verbraucherleistung', 'EMS-Leistung inkl. Speicher', 2, 'ENELIX.Demo.Power', 0.0, 40);
|
||||
$grid = enelixDemoEnsureVariable($dashboard, 'Netzleistung', 'Netzleistung', 2, 'ENELIX.Demo.Power', 0.0, 50);
|
||||
$boiler1Power = enelixDemoEnsureVariable($dashboard, 'Boiler1Leistung', 'Boiler 1 Leistung', 2, 'ENELIX.Demo.PowerPositive', 0.0, 60);
|
||||
$boiler2Power = enelixDemoEnsureVariable($dashboard, 'Boiler2Leistung', 'Boiler 2 Leistung', 2, 'ENELIX.Demo.PowerPositive', 0.0, 70);
|
||||
$dryerPower = enelixDemoEnsureVariable($dashboard, 'EntfeuchterLeistung', 'Entfeuchter Leistung', 2, 'ENELIX.Demo.PowerPositive', 0.0, 80);
|
||||
$bufferPower = enelixDemoEnsureVariable($dashboard, 'PufferLeistung', 'Pufferspeicher Leistung', 2, 'ENELIX.Demo.PowerPositive', 0.0, 90);
|
||||
$batteryPower = enelixDemoEnsureVariable($dashboard, 'BatterieLeistung', 'Batterieleistung', 2, 'ENELIX.Demo.Power', 0.0, 100);
|
||||
|
||||
$powerChart = enelixDemoEnsureVariable($history, 'Leistungsdiagramm', 'Leistungsverlauf (Legacy)', 3, '~HTMLBox', '', 90);
|
||||
$energyChart = enelixDemoEnsureVariable($history, 'Energiediagramm', 'Kumulierte Energien (Legacy)', 3, '~HTMLBox', '', 100);
|
||||
@@ -50,9 +54,11 @@ function enelixDemoInstall(): array
|
||||
$auto = enelixDemoEnsureVariable($simulation, 'Automatik', 'Tageslauf automatisch', 0, '~Switch', true, 10);
|
||||
$hour = enelixDemoEnsureVariable($simulation, 'Tageszeit', 'Simulierte Tageszeit', 2, 'ENELIX.Demo.Hour', 8.0, 20);
|
||||
$speed = enelixDemoEnsureVariable($simulation, 'Geschwindigkeit', 'Minuten je Schritt', 1, 'ENELIX.Demo.Speed', 10, 30);
|
||||
$peak = enelixDemoEnsureVariable($simulation, 'PVSpitze', 'PV-Spitzenleistung', 2, 'ENELIX.Demo.PowerPositive', 7000.0, 40);
|
||||
$cloud = enelixDemoEnsureVariable($simulation, 'Bewoelkung', 'Bewölkung', 1, 'ENELIX.Demo.Percent', 20, 50);
|
||||
$base = enelixDemoEnsureVariable($simulation, 'Grundlast', 'Haus-Grundlast', 2, 'ENELIX.Demo.PowerPositive', 900.0, 60);
|
||||
$dynamic = enelixDemoEnsureVariable($simulation, 'DynamischeProfile', 'Dynamische Tagesprofile', 0, '~Switch', true, 40);
|
||||
$peak = enelixDemoEnsureVariable($simulation, 'PVSpitze', 'PV-Spitzenleistung', 2, 'ENELIX.Demo.PowerPositive', 9000.0, 50);
|
||||
$cloud = enelixDemoEnsureVariable($simulation, 'Bewoelkung', 'Mittlere Bewölkung', 1, 'ENELIX.Demo.Percent', 20, 60);
|
||||
$base = enelixDemoEnsureVariable($simulation, 'Grundlast', 'Mittlere Haus-Grundlast', 2, 'ENELIX.Demo.PowerPositive', 900.0, 70);
|
||||
$extraLoad = enelixDemoEnsureVariable($simulation, 'Zusatzlast', 'Ungeregelte Zusatzlast', 2, 'ENELIX.Demo.PowerPositive', 0.0, 80);
|
||||
|
||||
$actorScript = enelixDemoEnsureScript(
|
||||
$technical,
|
||||
@@ -61,6 +67,13 @@ function enelixDemoInstall(): array
|
||||
"<?php\nSetValue((int) \$_IPS['VARIABLE'], (bool) \$_IPS['VALUE']);\n",
|
||||
10
|
||||
);
|
||||
$registerScript = enelixDemoEnsureScript(
|
||||
$technical,
|
||||
'RegisterAktion',
|
||||
'Simulierte Register',
|
||||
"<?php\nSetValue((int) \$_IPS['VARIABLE'], \$_IPS['VALUE']);\n",
|
||||
15
|
||||
);
|
||||
$tickScript = enelixDemoEnsureScript(
|
||||
$technical,
|
||||
'SimulationTick',
|
||||
@@ -76,7 +89,7 @@ function enelixDemoInstall(): array
|
||||
30
|
||||
);
|
||||
|
||||
foreach ([$auto, $hour, $speed, $peak, $cloud, $base] as $variableId) {
|
||||
foreach ([$auto, $hour, $speed, $dynamic, $peak, $cloud, $base, $extraLoad] as $variableId) {
|
||||
IPS_SetVariableCustomAction($variableId, $controlScript);
|
||||
}
|
||||
|
||||
@@ -84,36 +97,57 @@ function enelixDemoInstall(): array
|
||||
|
||||
$temp1 = enelixDemoEnsureVariable($technical, 'Boiler1Temperatur', 'Boiler 1 Temperatur', 2, '~Temperature', 49.0, 100);
|
||||
$temp2 = enelixDemoEnsureVariable($technical, 'Boiler2Temperatur', 'Boiler 2 Temperatur', 2, '~Temperature', 47.0, 110);
|
||||
$boiler11 = enelixDemoEnsureVariable($technical, 'Boiler1Stufe1', 'Boiler 1 Stufe 1', 0, '~Switch', false, 120);
|
||||
$boiler12 = enelixDemoEnsureVariable($technical, 'Boiler1Stufe2', 'Boiler 1 Stufe 2', 0, '~Switch', false, 130);
|
||||
$boiler21 = enelixDemoEnsureVariable($technical, 'Boiler2Stufe1', 'Boiler 2 Stufe 1', 0, '~Switch', false, 140);
|
||||
$boiler22 = enelixDemoEnsureVariable($technical, 'Boiler2Stufe2', 'Boiler 2 Stufe 2', 0, '~Switch', false, 150);
|
||||
$dryContact = enelixDemoEnsureVariable($technical, 'EntfeuchterKontakt', 'Entfeuchter Kontakt', 0, '~Switch', false, 160);
|
||||
$chartUpdated = enelixDemoEnsureVariable($technical, 'LetztesDiagrammUpdate', 'Letztes Diagramm-Update', 1, '', 0, 170);
|
||||
$bufferTemp = enelixDemoEnsureVariable($technical, 'PufferTemperatur', 'Puffertemperatur', 2, '~Temperature', 36.0, 120);
|
||||
$outsideTemp = enelixDemoEnsureVariable($technical, 'AussenTemperatur', 'Aussentemperatur', 2, '~Temperature', 6.0, 130);
|
||||
$boiler11 = enelixDemoEnsureVariable($technical, 'Boiler1Stufe1', 'Boiler 1 Stufe 1', 0, '~Switch', false, 200);
|
||||
$boiler12 = enelixDemoEnsureVariable($technical, 'Boiler1Stufe2', 'Boiler 1 Stufe 2', 0, '~Switch', false, 210);
|
||||
$boiler21 = enelixDemoEnsureVariable($technical, 'Boiler2Stufe1', 'Boiler 2 Stufe 1', 0, '~Switch', false, 220);
|
||||
$boiler22 = enelixDemoEnsureVariable($technical, 'Boiler2Stufe2', 'Boiler 2 Stufe 2', 0, '~Switch', false, 230);
|
||||
$dryContact = enelixDemoEnsureVariable($technical, 'EntfeuchterKontakt', 'Entfeuchter Kontakt', 0, '~Switch', false, 240);
|
||||
$buffer1 = enelixDemoEnsureVariable($technical, 'PufferStufe1', 'Puffer Stufe 1', 0, '~Switch', false, 250);
|
||||
$buffer2 = enelixDemoEnsureVariable($technical, 'PufferStufe2', 'Puffer Stufe 2', 0, '~Switch', false, 260);
|
||||
$batteryMaxCharge = enelixDemoEnsureVariable($technical, 'BatterieMaxLaden', 'Batterie max. laden', 2, 'ENELIX.Demo.PowerPositive', 3500.0, 300);
|
||||
$batteryMaxDischarge = enelixDemoEnsureVariable($technical, 'BatterieMaxEntladen', 'Batterie max. entladen', 2, 'ENELIX.Demo.PowerPositive', 3500.0, 310);
|
||||
$batterySoc = enelixDemoEnsureVariable($technical, 'BatterieLadezustand', 'Batterie Ladezustand', 2, 'ENELIX.Demo.PercentFloat', 52.0, 320);
|
||||
$batteryActual = enelixDemoEnsureVariable($technical, 'BatterieIstleistung', 'Batterie Istleistung', 2, 'ENELIX.Demo.Power', 0.0, 330);
|
||||
$batteryManagement = enelixDemoEnsureVariable($technical, 'BatterieManagement', 'Batterie Managementregister', 1, '', 0, 340);
|
||||
$batteryMode = enelixDemoEnsureVariable($technical, 'BatterieModus', 'Batterie Modusregister', 1, '', 0, 350);
|
||||
$batteryRegisterPower = enelixDemoEnsureVariable($technical, 'BatterieRegisterLeistung', 'Batterie Leistungsregister', 2, 'ENELIX.Demo.PowerPositive', 0.0, 360);
|
||||
$chartUpdated = enelixDemoEnsureVariable($technical, 'LetztesDiagrammUpdate', 'Letztes Diagramm-Update', 1, '', 0, 390);
|
||||
IPS_SetHidden($chartUpdated, true);
|
||||
|
||||
$flowPv = enelixDemoEnsureVariable($technical, 'EnergieflussPVkW', 'PV-Leistung', 2, '~Power', 0.0, 300);
|
||||
$flowGrid = enelixDemoEnsureVariable($technical, 'EnergieflussNetzkW', 'Netzleistung', 2, '~Power', 0.0, 310);
|
||||
$flowBase = enelixDemoEnsureVariable($technical, 'EnergieflussGrundlastkW', 'Haus-Grundlast', 2, '~Power', 0.0, 320);
|
||||
$flowBoiler1 = enelixDemoEnsureVariable($technical, 'EnergieflussBoiler1kW', 'Boiler 1', 2, '~Power', 0.0, 330);
|
||||
$flowBoiler2 = enelixDemoEnsureVariable($technical, 'EnergieflussBoiler2kW', 'Boiler 2', 2, '~Power', 0.0, 340);
|
||||
$flowDryer = enelixDemoEnsureVariable($technical, 'EnergieflussEntfeuchterkW', 'Entfeuchter', 2, '~Power', 0.0, 350);
|
||||
$flowHouse = enelixDemoEnsureVariable($technical, 'DiagrammHausverbrauchkW', 'Hausverbrauch gesamt', 2, '~Power', 0.0, 360);
|
||||
$flowLoads = enelixDemoEnsureVariable($technical, 'DiagrammVerbraucherkW', 'Geregelte Verbraucher', 2, '~Power', 0.0, 370);
|
||||
$flowPv = enelixDemoEnsureVariable($technical, 'EnergieflussPVkW', 'PV-Leistung', 2, '~Power', 0.0, 500);
|
||||
$flowGrid = enelixDemoEnsureVariable($technical, 'EnergieflussNetzkW', 'Netzleistung', 2, '~Power', 0.0, 510);
|
||||
$flowBase = enelixDemoEnsureVariable($technical, 'EnergieflussGrundlastkW', 'Haus-Grundlast', 2, '~Power', 0.0, 520);
|
||||
$flowBoiler1 = enelixDemoEnsureVariable($technical, 'EnergieflussBoiler1kW', 'Boiler 1', 2, '~Power', 0.0, 530);
|
||||
$flowBoiler2 = enelixDemoEnsureVariable($technical, 'EnergieflussBoiler2kW', 'Boiler 2', 2, '~Power', 0.0, 540);
|
||||
$flowDryer = enelixDemoEnsureVariable($technical, 'EnergieflussEntfeuchterkW', 'Entfeuchter', 2, '~Power', 0.0, 550);
|
||||
$flowBuffer = enelixDemoEnsureVariable($technical, 'EnergieflussPufferkW', 'Pufferspeicher', 2, '~Power', 0.0, 560);
|
||||
$flowBattery = enelixDemoEnsureVariable($technical, 'EnergieflussBatteriekW', 'Batteriespeicher', 2, '~Power', 0.0, 570);
|
||||
$flowHouse = enelixDemoEnsureVariable($technical, 'DiagrammHausverbrauchkW', 'Hausverbrauch gesamt', 2, '~Power', 0.0, 580);
|
||||
$flowLoads = enelixDemoEnsureVariable($technical, 'DiagrammVerbraucherkW', 'EMS-Leistung inkl. Speicher', 2, '~Power', 0.0, 590);
|
||||
foreach ([
|
||||
$flowPv, $flowGrid, $flowBase, $flowBoiler1,
|
||||
$flowBoiler2, $flowDryer, $flowHouse, $flowLoads,
|
||||
$flowPv, $flowGrid, $flowBase, $flowBoiler1, $flowBoiler2,
|
||||
$flowDryer, $flowBuffer, $flowBattery, $flowHouse, $flowLoads,
|
||||
] as $flowVariableId) {
|
||||
IPS_SetHidden($flowVariableId, true);
|
||||
}
|
||||
|
||||
foreach ([$boiler11, $boiler12, $boiler21, $boiler22, $dryContact] as $actorId) {
|
||||
foreach ([$boiler11, $boiler12, $boiler21, $boiler22, $dryContact, $buffer1, $buffer2] as $actorId) {
|
||||
IPS_SetVariableCustomAction($actorId, $actorScript);
|
||||
}
|
||||
foreach ([$batteryManagement, $batteryMode, $batteryRegisterPower] as $registerId) {
|
||||
IPS_SetVariableCustomAction($registerId, $registerScript);
|
||||
}
|
||||
foreach ([$temp1, $temp2, $bufferTemp, $outsideTemp, $batteryMaxCharge, $batteryMaxDischarge, $batterySoc, $batteryActual] as $sensorId) {
|
||||
SetValue($sensorId, GetValue($sensorId));
|
||||
}
|
||||
|
||||
$boiler1 = enelixDemoEnsureInstance($ems, 'Boiler1', 'Boiler 1 · 300 Liter', ENELIX_DEMO_BOILER_MODULE, 20);
|
||||
$boiler2 = enelixDemoEnsureInstance($ems, 'Boiler2', 'Boiler 2 · 500 Liter', ENELIX_DEMO_BOILER_MODULE, 30);
|
||||
$dryer = enelixDemoEnsureInstance($ems, 'Entfeuchter', 'Entfeuchter · 1-stufig', ENELIX_DEMO_SINGLE_MODULE, 40);
|
||||
$buffer = enelixDemoEnsureInstance($ems, 'Pufferspeicher', 'Pufferspeicher · Heizkurve', ENELIX_DEMO_BUFFER_MODULE, 50);
|
||||
$battery = enelixDemoEnsureInstance($ems, 'Batterie', 'Batteriespeicher · 10 kWh', ENELIX_DEMO_BATTERY_MODULE, 60);
|
||||
|
||||
enelixDemoConfigure($boiler1, [
|
||||
'Boilerfuehler_PT1' => $temp1,
|
||||
@@ -157,6 +191,52 @@ function enelixDemoInstall(): array
|
||||
'Mindestausschaltdauer' => 2,
|
||||
'Mindestlaufzeit' => 0,
|
||||
'PrioritaetPV' => 2,
|
||||
'PrioritaetPeak' => 3,
|
||||
'Meldeintervall' => 5,
|
||||
'VorgabeTimeout' => 30,
|
||||
'EinstellungenInVisu' => true,
|
||||
'DiagnosevariablenAnzeigen' => true,
|
||||
]);
|
||||
enelixDemoConfigure($buffer, [
|
||||
'LeistungsStufen' => json_encode([
|
||||
['Stufe' => 1, 'Leistung' => 1800, 'Schaltkontakt_Stufe' => $buffer1],
|
||||
['Stufe' => 2, 'Leistung' => 3600, 'Schaltkontakt_Stufe' => $buffer2],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
'PufferfuehlerVariableID' => $bufferTemp,
|
||||
'AussentemperaturVariableID' => $outsideTemp,
|
||||
'FusspunktVorlauftemperatur' => 34.0,
|
||||
'HeizkurvenSteigung' => 0.8,
|
||||
'HeizkurveMinimaltemperatur' => 28.0,
|
||||
'HeizkurveMaximaltemperatur' => 55.0,
|
||||
'Hysterese' => 4.0,
|
||||
'MindesttemperaturModus' => 0,
|
||||
'LastwechselSperrzeit' => 2,
|
||||
'TemperaturMaxAlter' => 30,
|
||||
'PuffertemperaturGlaetten' => false,
|
||||
'PrioritaetPV' => 3,
|
||||
'PrioritaetPeak' => 4,
|
||||
'Meldeintervall' => 5,
|
||||
'VorgabeTimeout' => 30,
|
||||
'EinstellungenInVisu' => true,
|
||||
'DiagnosevariablenAnzeigen' => true,
|
||||
]);
|
||||
enelixDemoConfigure($battery, [
|
||||
'Batterietyp' => 2,
|
||||
'Batteriemanagement' => 2,
|
||||
'MaxLadeleistungVariableID' => $batteryMaxCharge,
|
||||
'MaxEntladeleistungVariableID' => $batteryMaxDischarge,
|
||||
'LadezustandVariableID' => $batterySoc,
|
||||
'NetzleistungVariableID' => $grid,
|
||||
'IstleistungVariableID' => $batteryActual,
|
||||
'ManagementRegisterVariableID' => $batteryManagement,
|
||||
'ModusRegisterVariableID' => $batteryMode,
|
||||
'LeistungsRegisterVariableID' => $batteryRegisterPower,
|
||||
'ReserveLadezustand' => 20.0,
|
||||
'MindestLadezustand' => 10.0,
|
||||
'LadezustandHysterese' => 3.0,
|
||||
'MesswertMaxAlter' => 30,
|
||||
'Aenderungssperre' => 0,
|
||||
'PrioritaetPV' => 4,
|
||||
'PrioritaetPeak' => 0,
|
||||
'Meldeintervall' => 5,
|
||||
'VorgabeTimeout' => 30,
|
||||
@@ -178,6 +258,8 @@ function enelixDemoInstall(): array
|
||||
enelixDemoEnergyNode($flowBoiler1, 'Boiler 1', 'consumer', $temp1),
|
||||
enelixDemoEnergyNode($flowBoiler2, 'Boiler 2', 'consumer', $temp2),
|
||||
enelixDemoEnergyNode($flowDryer, 'Entfeuchter', 'consumer'),
|
||||
enelixDemoEnergyNode($flowBuffer, 'Pufferspeicher', 'consumer', $bufferTemp),
|
||||
enelixDemoEnergyNode($flowBattery, 'Batteriespeicher', 'battery', $batterySoc),
|
||||
]);
|
||||
IPS_SetHidden($energyDistribution, false);
|
||||
$oldEnergyDistribution = enelixDemoFind($dashboard, 'Energieverteilung');
|
||||
@@ -188,34 +270,47 @@ function enelixDemoInstall(): array
|
||||
foreach ([$boiler1, $boiler2] as $boilerId) {
|
||||
IPS_RequestAction($boilerId, 'Mindesttemperatur', 42.0);
|
||||
IPS_RequestAction($boilerId, 'Maximaltemperatur', 62.0);
|
||||
IPS_RequestAction($boilerId, 'Aktiv', true);
|
||||
}
|
||||
IPS_RequestAction($dryer, 'Aktiv', true);
|
||||
|
||||
$manager = enelixDemoEnsureInstance($ems, 'Manager', 'Enelix Manager', ENELIX_DEMO_MANAGER_MODULE, 10);
|
||||
$assignment = array_map(
|
||||
static fn (int $id): array => ['InstanzID' => $id, 'Aktiv' => true],
|
||||
[$boiler1, $boiler2, $dryer]
|
||||
[$boiler1, $boiler2, $dryer, $buffer, $battery]
|
||||
);
|
||||
enelixDemoConfigure($manager, [
|
||||
$managerProperties = [
|
||||
'NetzleistungVariableID' => $grid,
|
||||
'Netzleistungsfaktor' => 1.0,
|
||||
'MesswertMaxAlter' => 10,
|
||||
'AutomatischeSuche' => false,
|
||||
'SuchbereichID' => $root,
|
||||
'VerbraucherZuordnung' => json_encode($assignment, JSON_THROW_ON_ERROR),
|
||||
'VerbraucherZuordnung' => '[]',
|
||||
'KeepAlive' => 5,
|
||||
'VerbraucherTimeout' => 15,
|
||||
'SollwertSolarladen' => 0.0,
|
||||
'Umschaltdifferenz' => 0.0,
|
||||
'DiagnosevariablenAnzeigen' => true,
|
||||
'LoggingEin' => false,
|
||||
]);
|
||||
|
||||
foreach ([$boiler1, $boiler2, $dryer] as $consumerId) {
|
||||
IPS_RequestAction($consumerId, 'Melden', true);
|
||||
];
|
||||
enelixDemoConfigure($manager, $managerProperties, [102, 203]);
|
||||
$managerReady = (int) IPS_GetInstance($manager)['InstanceStatus'] === 102;
|
||||
if ($managerReady) {
|
||||
$managerProperties['VerbraucherZuordnung'] = json_encode($assignment, JSON_THROW_ON_ERROR);
|
||||
enelixDemoConfigure($manager, $managerProperties, [102, 203]);
|
||||
$managerReady = (int) IPS_GetInstance($manager)['InstanceStatus'] === 102;
|
||||
}
|
||||
IPS_RequestAction($manager, 'Aktiv', true);
|
||||
if (!$managerReady && IPS_GetProperty($manager, 'VerbraucherZuordnung') !== '[]') {
|
||||
$managerProperties['VerbraucherZuordnung'] = '[]';
|
||||
enelixDemoConfigure($manager, $managerProperties, [203]);
|
||||
}
|
||||
|
||||
$consumerIds = [$boiler1, $boiler2, $dryer, $buffer, $battery];
|
||||
foreach ($consumerIds as $consumerId) {
|
||||
IPS_RequestAction($consumerId, 'Aktiv', $managerReady);
|
||||
if ($managerReady) {
|
||||
IPS_RequestAction($consumerId, 'Melden', true);
|
||||
}
|
||||
}
|
||||
IPS_RequestAction($manager, 'Aktiv', $managerReady);
|
||||
|
||||
$boiler1List = enelixDemoEnsureInstance(
|
||||
$dashboard,
|
||||
@@ -231,14 +326,32 @@ function enelixDemoInstall(): array
|
||||
ENELIX_DEMO_DUMMY_MODULE,
|
||||
120
|
||||
);
|
||||
enelixDemoConfigure($boiler1List, []);
|
||||
enelixDemoConfigure($boiler2List, []);
|
||||
$bufferList = enelixDemoEnsureInstance(
|
||||
$dashboard,
|
||||
'PufferListe',
|
||||
'Pufferspeicher',
|
||||
ENELIX_DEMO_DUMMY_MODULE,
|
||||
130
|
||||
);
|
||||
$batteryList = enelixDemoEnsureInstance(
|
||||
$dashboard,
|
||||
'BatterieListe',
|
||||
'Batteriespeicher',
|
||||
ENELIX_DEMO_DUMMY_MODULE,
|
||||
140
|
||||
);
|
||||
foreach ([$boiler1List, $boiler2List, $bufferList, $batteryList] as $listId) {
|
||||
enelixDemoConfigure($listId, []);
|
||||
}
|
||||
enelixDemoLinkInstanceVariables($boiler1List, $boiler1, $temp1);
|
||||
enelixDemoLinkInstanceVariables($boiler2List, $boiler2, $temp2);
|
||||
enelixDemoLinkInstanceVariables($bufferList, $buffer, $bufferTemp, 'Puffertemperatur', 'SensorPuffertemperatur');
|
||||
enelixDemoLinkInstanceVariables($batteryList, $battery, $batterySoc, 'Ladezustand', 'SensorLadezustand');
|
||||
IPS_SetHidden($boiler1View, true);
|
||||
IPS_SetHidden($boiler2View, true);
|
||||
IPS_SetHidden($boiler1, true);
|
||||
IPS_SetHidden($boiler2, true);
|
||||
foreach ([$boiler1, $boiler2, $buffer, $battery] as $hiddenInstanceId) {
|
||||
IPS_SetHidden($hiddenInstanceId, true);
|
||||
}
|
||||
|
||||
foreach (['Boiler1Link', 'Boiler2Link'] as $oldLinkIdent) {
|
||||
$oldLink = enelixDemoFind($dashboard, $oldLinkIdent);
|
||||
@@ -248,22 +361,25 @@ function enelixDemoInstall(): array
|
||||
}
|
||||
|
||||
enelixDemoEnsureLink($dashboard, 'ManagerLink', 'Manager', $manager, 100);
|
||||
enelixDemoEnsureLink($dashboard, 'EntfeuchterLink', 'Entfeuchter', $dryer, 130);
|
||||
enelixDemoEnsureLink($simulation, 'Boiler1TemperaturLink', 'Boiler 1 Temperatur', $temp1, 70);
|
||||
enelixDemoEnsureLink($simulation, 'Boiler2TemperaturLink', 'Boiler 2 Temperatur', $temp2, 80);
|
||||
enelixDemoEnsureLink($dashboard, 'EntfeuchterLink', 'Entfeuchter', $dryer, 150);
|
||||
enelixDemoEnsureLink($simulation, 'Boiler1TemperaturLink', 'Boiler 1 Temperatur', $temp1, 100);
|
||||
enelixDemoEnsureLink($simulation, 'Boiler2TemperaturLink', 'Boiler 2 Temperatur', $temp2, 110);
|
||||
enelixDemoEnsureLink($simulation, 'PufferTemperaturLink', 'Puffertemperatur', $bufferTemp, 120);
|
||||
enelixDemoEnsureLink($simulation, 'AussenTemperaturLink', 'Aussentemperatur', $outsideTemp, 130);
|
||||
enelixDemoEnsureLink($simulation, 'BatterieLadezustandLink', 'Batterie Ladezustand', $batterySoc, 140);
|
||||
|
||||
enelixDemoConfigureArchive([
|
||||
$pvDisplay, $houseDisplay, $loadDisplay, $grid,
|
||||
$boiler1Power, $boiler2Power, $dryerPower,
|
||||
$boiler1Power, $boiler2Power, $dryerPower, $bufferPower, $batteryPower,
|
||||
$pvEnergy, $houseEnergy, $gridImportEnergy, $gridExportEnergy,
|
||||
$temp1, $temp2,
|
||||
$temp1, $temp2, $bufferTemp, $outsideTemp, $batterySoc, $batteryActual,
|
||||
$flowPv, $flowGrid, $flowBase, $flowBoiler1, $flowBoiler2, $flowDryer,
|
||||
$flowHouse, $flowLoads,
|
||||
$flowBuffer, $flowBattery, $flowHouse, $flowLoads,
|
||||
]);
|
||||
enelixDemoEnsureChart($history, 'LeistungsdiagrammNativ', 'Leistungsverlauf', [
|
||||
enelixDemoChartDataset($flowPv, 'PV', '#f4c542', '~Power'),
|
||||
enelixDemoChartDataset($flowHouse, 'Hausverbrauch', '#2bb673', '~Power'),
|
||||
enelixDemoChartDataset($flowLoads, 'Geregelte Verbraucher', '#2786ac', '~Power'),
|
||||
enelixDemoChartDataset($flowLoads, 'EMS-Leistung inkl. Speicher', '#2786ac', '~Power'),
|
||||
enelixDemoChartDataset($flowGrid, 'Netzleistung', '#e95d5d', '~Power'),
|
||||
], 10);
|
||||
enelixDemoEnsureChart($history, 'EnergiediagrammNativ', 'Kumulierte Energien', [
|
||||
@@ -279,7 +395,7 @@ function enelixDemoInstall(): array
|
||||
'rootId' => $root,
|
||||
'visualizationId' => enelixDemoFirstInstance(ENELIX_DEMO_TILE_MODULE),
|
||||
'managerId' => $manager,
|
||||
'consumerIds' => [$boiler1, $boiler2, $dryer],
|
||||
'consumerIds' => $consumerIds,
|
||||
'tickScriptId' => $tickScript,
|
||||
];
|
||||
}
|
||||
@@ -301,13 +417,20 @@ function enelixDemoTick(string $source = 'Manual'): void
|
||||
SetValue($hourId, $hour);
|
||||
}
|
||||
|
||||
$dynamic = (bool) GetValue(enelixDemoRequiredId($simulation, 'DynamischeProfile'));
|
||||
$environment = enelixDemoEnvironment(
|
||||
$hour,
|
||||
(float) GetValue(enelixDemoRequiredId($simulation, 'Grundlast')),
|
||||
(int) GetValue(enelixDemoRequiredId($simulation, 'Bewoelkung')),
|
||||
$dynamic
|
||||
);
|
||||
$base = $environment['base']
|
||||
+ (float) GetValue(enelixDemoRequiredId($simulation, 'Zusatzlast'));
|
||||
$peak = (float) GetValue(enelixDemoRequiredId($simulation, 'PVSpitze'));
|
||||
$cloud = (int) GetValue(enelixDemoRequiredId($simulation, 'Bewoelkung'));
|
||||
$base = (float) GetValue(enelixDemoRequiredId($simulation, 'Grundlast'));
|
||||
$sun = ($hour >= 6.0 && $hour <= 18.0)
|
||||
? max(0.0, sin(M_PI * ($hour - 6.0) / 12.0))
|
||||
: 0.0;
|
||||
$pv = $peak * $sun * (1.0 - (min(100, max(0, $cloud)) * 0.0075));
|
||||
$pv = $peak * $sun * (1.0 - ($environment['cloud'] * 0.0085));
|
||||
|
||||
$contacts = [
|
||||
'boiler11' => enelixDemoRequiredId($technical, 'Boiler1Stufe1'),
|
||||
@@ -315,15 +438,46 @@ function enelixDemoTick(string $source = 'Manual'): void
|
||||
'boiler21' => enelixDemoRequiredId($technical, 'Boiler2Stufe1'),
|
||||
'boiler22' => enelixDemoRequiredId($technical, 'Boiler2Stufe2'),
|
||||
'dryer' => enelixDemoRequiredId($technical, 'EntfeuchterKontakt'),
|
||||
'buffer1' => enelixDemoRequiredId($technical, 'PufferStufe1'),
|
||||
'buffer2' => enelixDemoRequiredId($technical, 'PufferStufe2'),
|
||||
'battery' => enelixDemoRequiredId($technical, 'BatterieIstleistung'),
|
||||
];
|
||||
$powerBefore = enelixDemoControlledPower($contacts);
|
||||
$temp1 = enelixDemoRequiredId($technical, 'Boiler1Temperatur');
|
||||
$temp2 = enelixDemoRequiredId($technical, 'Boiler2Temperatur');
|
||||
$bufferTemp = enelixDemoRequiredId($technical, 'PufferTemperatur');
|
||||
$outsideTemp = enelixDemoRequiredId($technical, 'AussenTemperatur');
|
||||
$batterySoc = enelixDemoRequiredId($technical, 'BatterieLadezustand');
|
||||
$batteryMaxCharge = enelixDemoRequiredId($technical, 'BatterieMaxLaden');
|
||||
$batteryMaxDischarge = enelixDemoRequiredId($technical, 'BatterieMaxEntladen');
|
||||
$previousBatteryPower = (float) GetValue($contacts['battery']);
|
||||
|
||||
SetValue($outsideTemp, round($environment['outside'], 2));
|
||||
SetValue($batteryMaxCharge, GetValue($batteryMaxCharge));
|
||||
SetValue($batteryMaxDischarge, GetValue($batteryMaxDischarge));
|
||||
if ($source === 'Timer') {
|
||||
enelixDemoUpdateTemperature($temp1, (bool) GetValue($contacts['boiler12']) ? 2400 : ((bool) GetValue($contacts['boiler11']) ? 1200 : 0), $minutes);
|
||||
enelixDemoUpdateTemperature($temp2, (bool) GetValue($contacts['boiler22']) ? 3000 : ((bool) GetValue($contacts['boiler21']) ? 1500 : 0), $minutes);
|
||||
enelixDemoUpdateTemperature(
|
||||
$temp1,
|
||||
(bool) GetValue($contacts['boiler12']) ? 2400 : ((bool) GetValue($contacts['boiler11']) ? 1200 : 0),
|
||||
$minutes
|
||||
);
|
||||
enelixDemoUpdateTemperature(
|
||||
$temp2,
|
||||
(bool) GetValue($contacts['boiler22']) ? 3000 : ((bool) GetValue($contacts['boiler21']) ? 1500 : 0),
|
||||
$minutes
|
||||
);
|
||||
enelixDemoUpdateBufferTemperature(
|
||||
$bufferTemp,
|
||||
(bool) GetValue($contacts['buffer2']) ? 3600 : ((bool) GetValue($contacts['buffer1']) ? 1800 : 0),
|
||||
$environment['outside'],
|
||||
$minutes
|
||||
);
|
||||
enelixDemoUpdateBatterySoc($batterySoc, $previousBatteryPower, $minutes);
|
||||
} else {
|
||||
SetValue($bufferTemp, GetValue($bufferTemp));
|
||||
SetValue($batterySoc, GetValue($batterySoc));
|
||||
}
|
||||
|
||||
$powerBefore = enelixDemoControlledPower($contacts);
|
||||
$gridId = enelixDemoRequiredId($dashboard, 'Netzleistung');
|
||||
SetValue($gridId, round($base + $powerBefore - $pv, 1));
|
||||
|
||||
@@ -331,38 +485,55 @@ function enelixDemoTick(string $source = 'Manual'): void
|
||||
enelixDemoRequiredId($ems, 'Boiler1'),
|
||||
enelixDemoRequiredId($ems, 'Boiler2'),
|
||||
enelixDemoRequiredId($ems, 'Entfeuchter'),
|
||||
enelixDemoRequiredId($ems, 'Pufferspeicher'),
|
||||
enelixDemoRequiredId($ems, 'Batterie'),
|
||||
];
|
||||
foreach ($consumers as $consumerId) {
|
||||
IPS_RequestAction($consumerId, 'Melden', true);
|
||||
}
|
||||
$manager = enelixDemoRequiredId($ems, 'Manager');
|
||||
IPS_RequestAction($manager, 'Regeln', true);
|
||||
$managerReady = (int) IPS_GetInstance($manager)['InstanceStatus'] === 102;
|
||||
if ($managerReady) {
|
||||
foreach ($consumers as $consumerId) {
|
||||
IPS_RequestAction($consumerId, 'Melden', true);
|
||||
}
|
||||
IPS_RequestAction($manager, 'Regeln', true);
|
||||
}
|
||||
|
||||
$battery = $managerReady
|
||||
? enelixDemoBatteryRegisterPower(
|
||||
(int) GetValue(enelixDemoRequiredId($technical, 'BatterieManagement')),
|
||||
(int) GetValue(enelixDemoRequiredId($technical, 'BatterieModus')),
|
||||
(float) GetValue(enelixDemoRequiredId($technical, 'BatterieRegisterLeistung'))
|
||||
)
|
||||
: 0.0;
|
||||
SetValue($contacts['battery'], $battery);
|
||||
|
||||
$boiler1 = (bool) GetValue($contacts['boiler12']) ? 2400 : ((bool) GetValue($contacts['boiler11']) ? 1200 : 0);
|
||||
$boiler2 = (bool) GetValue($contacts['boiler22']) ? 3000 : ((bool) GetValue($contacts['boiler21']) ? 1500 : 0);
|
||||
$dryer = (bool) GetValue($contacts['dryer']) ? 900 : 0;
|
||||
$power = $boiler1 + $boiler2 + $dryer;
|
||||
$house = $base + $power;
|
||||
$grid = $house - $pv;
|
||||
$buffer = (bool) GetValue($contacts['buffer2']) ? 3600 : ((bool) GetValue($contacts['buffer1']) ? 1800 : 0);
|
||||
$loads = $boiler1 + $boiler2 + $dryer + $buffer;
|
||||
$emsPower = $loads + $battery;
|
||||
$house = $base + $loads;
|
||||
$grid = $house + $battery - $pv;
|
||||
|
||||
$pvId = enelixDemoRequiredId($dashboard, 'PVLeistung');
|
||||
$houseId = enelixDemoRequiredId($dashboard, 'Hausverbrauch');
|
||||
$loadId = enelixDemoRequiredId($dashboard, 'Verbraucherleistung');
|
||||
SetValue($gridId, round($grid, 1));
|
||||
SetValue($pvId, round($pv, 1));
|
||||
SetValue($houseId, round($house, 1));
|
||||
SetValue($loadId, (float) $power);
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'PVLeistung'), round($pv, 1));
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'Hausverbrauch'), round($house, 1));
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'Verbraucherleistung'), (float) $emsPower);
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'Boiler1Leistung'), (float) $boiler1);
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'Boiler2Leistung'), (float) $boiler2);
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'EntfeuchterLeistung'), (float) $dryer);
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'PufferLeistung'), (float) $buffer);
|
||||
SetValue(enelixDemoRequiredId($dashboard, 'BatterieLeistung'), $battery);
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussPVkW'), round($pv / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussNetzkW'), round($grid / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussGrundlastkW'), round($base / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussBoiler1kW'), round($boiler1 / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussBoiler2kW'), round($boiler2 / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussEntfeuchterkW'), round($dryer / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussPufferkW'), round($buffer / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'EnergieflussBatteriekW'), round($battery / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'DiagrammHausverbrauchkW'), round($house / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'DiagrammVerbraucherkW'), round($power / 1000.0, 3));
|
||||
SetValue(enelixDemoRequiredId($technical, 'DiagrammVerbraucherkW'), round($emsPower / 1000.0, 3));
|
||||
|
||||
$history = enelixDemoRequiredId($root, 'Historie');
|
||||
$energyIds = [
|
||||
@@ -378,7 +549,6 @@ function enelixDemoTick(string $source = 'Manual'): void
|
||||
enelixDemoAddEnergy($energyIds['import'], max(0.0, $grid), $hours);
|
||||
enelixDemoAddEnergy($energyIds['export'], max(0.0, -$grid), $hours);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function enelixDemoRenderFlow(array $state): string
|
||||
@@ -390,9 +560,15 @@ function enelixDemoRenderFlow(array $state): string
|
||||
$gridLabel = $grid < -25 ? 'Einspeisung' : ($grid > 25 ? 'Netzbezug' : 'Ausgeglichen');
|
||||
$gridArrow = $grid < -25 ? '→' : ($grid > 25 ? '←' : '↔');
|
||||
$gridClass = abs($grid) <= 250 ? 'good' : ($grid < 0 ? 'export' : 'import');
|
||||
$buffer = (float) ($state['buffer'] ?? 0.0);
|
||||
$battery = (float) ($state['battery'] ?? 0.0);
|
||||
$bufferTemp = (float) ($state['bufferTemp'] ?? 36.0);
|
||||
$batterySoc = (float) ($state['batterySoc'] ?? 50.0);
|
||||
$b1Class = (float) $state['boiler1'] > 0 ? 'on' : '';
|
||||
$b2Class = (float) $state['boiler2'] > 0 ? 'on' : '';
|
||||
$dryClass = (float) $state['dryer'] > 0 ? 'on' : '';
|
||||
$bufferClass = $buffer > 0 ? 'on' : '';
|
||||
$batteryClass = abs($battery) > 25 ? 'on' : '';
|
||||
|
||||
return '<style>
|
||||
.enelix-flow{box-sizing:border-box;font-family:Inter,Segoe UI,Arial,sans-serif;background:#10151c;color:#f4f7fa;padding:18px;min-height:420px}
|
||||
@@ -403,14 +579,14 @@ function enelixDemoRenderFlow(array $state): string
|
||||
.enelix-node small{display:block;color:#9dacbd;margin-bottom:8px}.enelix-node b{font-size:22px}
|
||||
.enelix-node.solar{border-color:#b99628}.enelix-node.bus{border-color:#2786ac}.enelix-node.good{border-color:#2bb673}
|
||||
.enelix-node.export{border-color:#38a3db}.enelix-node.import{border-color:#e95d5d}.enelix-arrow{text-align:center;font-size:30px;color:#9dacbd}
|
||||
.enelix-loads{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin-top:18px}
|
||||
.enelix-loads{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-top:18px}
|
||||
.enelix-load{border-left:4px solid #566371;background:#17202a;padding:13px 14px;border-radius:6px}
|
||||
.enelix-load.on{border-left-color:#2bb673}.enelix-load b{display:block;font-size:17px;margin:4px 0}.enelix-load span{color:#9dacbd;font-size:12px}
|
||||
.enelix-foot{display:flex;justify-content:space-between;gap:12px;margin-top:16px;padding-top:12px;border-top:1px solid #2a3541;color:#9dacbd;font-size:12px}
|
||||
@media(max-width:640px){.enelix-line{grid-template-columns:1fr}.enelix-arrow{transform:rotate(90deg)}.enelix-loads{grid-template-columns:1fr}.enelix-head{align-items:start;flex-direction:column;gap:4px}}
|
||||
</style><div class="enelix-flow"><div class="enelix-head"><div><div class="enelix-title">Enelix 2 Energiefluss</div><div class="enelix-sub">Manager regelt drei flexible Verbraucher</div></div><div class="enelix-sub">Simulation ' . $time . ' Uhr</div></div>
|
||||
<div class="enelix-line"><div class="enelix-node solar"><small>PV-Erzeugung</small><b>' . $format((float) $state['pv']) . ' W</b></div><div class="enelix-arrow">→</div><div class="enelix-node bus"><small>Haus + flexible Lasten</small><b>' . $format((float) $state['base'] + (float) $state['boiler1'] + (float) $state['boiler2'] + (float) $state['dryer']) . ' W</b></div><div class="enelix-arrow">' . $gridArrow . '</div><div class="enelix-node ' . $gridClass . '"><small>' . $gridLabel . '</small><b>' . $format(abs($grid)) . ' W</b></div></div>
|
||||
<div class="enelix-loads"><div class="enelix-load ' . $b1Class . '"><span>Priorität 1</span><b>Boiler 1 · ' . $format((float) $state['boiler1']) . ' W</b><span>' . number_format((float) $state['temp1'], 1, ',', '') . ' °C</span></div><div class="enelix-load ' . $b2Class . '"><span>Priorität 2</span><b>Boiler 2 · ' . $format((float) $state['boiler2']) . ' W</b><span>' . number_format((float) $state['temp2'], 1, ',', '') . ' °C</span></div><div class="enelix-load ' . $dryClass . '"><span>Priorität 3</span><b>Entfeuchter · ' . $format((float) $state['dryer']) . ' W</b><span>einstufiger Verbraucher</span></div></div>
|
||||
</style><div class="enelix-flow"><div class="enelix-head"><div><div class="enelix-title">Enelix 2 Energiefluss</div><div class="enelix-sub">Manager regelt fünf flexible Teilnehmer aus vier Modultypen</div></div><div class="enelix-sub">Simulation ' . $time . ' Uhr</div></div>
|
||||
<div class="enelix-line"><div class="enelix-node solar"><small>PV-Erzeugung</small><b>' . $format((float) $state['pv']) . ' W</b></div><div class="enelix-arrow">→</div><div class="enelix-node bus"><small>Haus + EMS-Leistung</small><b>' . $format((float) $state['base'] + (float) $state['boiler1'] + (float) $state['boiler2'] + (float) $state['dryer'] + $buffer + $battery) . ' W</b></div><div class="enelix-arrow">' . $gridArrow . '</div><div class="enelix-node ' . $gridClass . '"><small>' . $gridLabel . '</small><b>' . $format(abs($grid)) . ' W</b></div></div>
|
||||
<div class="enelix-loads"><div class="enelix-load ' . $b1Class . '"><span>Priorität 1</span><b>Boiler 1 · ' . $format((float) $state['boiler1']) . ' W</b><span>' . number_format((float) $state['temp1'], 1, ',', '') . ' °C</span></div><div class="enelix-load ' . $b2Class . '"><span>Priorität 2</span><b>Boiler 2 · ' . $format((float) $state['boiler2']) . ' W</b><span>' . number_format((float) $state['temp2'], 1, ',', '') . ' °C</span></div><div class="enelix-load ' . $dryClass . '"><span>Priorität 3</span><b>Entfeuchter · ' . $format((float) $state['dryer']) . ' W</b><span>einstufiger Verbraucher</span></div><div class="enelix-load ' . $bufferClass . '"><span>Priorität 4</span><b>Pufferspeicher · ' . $format($buffer) . ' W</b><span>' . number_format($bufferTemp, 1, ',', '') . ' °C · Heizkurve</span></div><div class="enelix-load ' . $batteryClass . '"><span>Priorität 5 / Peak 1</span><b>Batterie · ' . $format($battery) . ' W</b><span>' . number_format($batterySoc, 1, ',', '') . ' % · bidirektional</span></div></div>
|
||||
<div class="enelix-foot"><span>Grundlast ' . $format((float) $state['base']) . ' W</span><span>Manager: ' . htmlspecialchars((string) $state['manager'], ENT_QUOTES, 'UTF-8') . '</span></div></div>';
|
||||
}
|
||||
|
||||
@@ -418,7 +594,65 @@ function enelixDemoControlledPower(array $contacts): int
|
||||
{
|
||||
$boiler1 = (bool) GetValue($contacts['boiler12']) ? 2400 : ((bool) GetValue($contacts['boiler11']) ? 1200 : 0);
|
||||
$boiler2 = (bool) GetValue($contacts['boiler22']) ? 3000 : ((bool) GetValue($contacts['boiler21']) ? 1500 : 0);
|
||||
return $boiler1 + $boiler2 + ((bool) GetValue($contacts['dryer']) ? 900 : 0);
|
||||
$buffer = (bool) GetValue($contacts['buffer2']) ? 3600 : ((bool) GetValue($contacts['buffer1']) ? 1800 : 0);
|
||||
$battery = (int) round((float) GetValue($contacts['battery']));
|
||||
|
||||
return $boiler1 + $boiler2 + ((bool) GetValue($contacts['dryer']) ? 900 : 0) + $buffer + $battery;
|
||||
}
|
||||
|
||||
/** @return array{base: float, cloud: float, outside: float} */
|
||||
function enelixDemoEnvironment(float $hour, float $base, int $cloud, bool $dynamic): array
|
||||
{
|
||||
$hour = fmod(max(0.0, $hour), 24.0);
|
||||
$cloud = min(100, max(0, $cloud));
|
||||
if (!$dynamic) {
|
||||
return ['base' => $base, 'cloud' => (float) $cloud, 'outside' => 6.0];
|
||||
}
|
||||
|
||||
$morning = exp(-pow(($hour - 7.5) / 1.5, 2));
|
||||
$evening = exp(-pow(($hour - 19.0) / 2.2, 2));
|
||||
$night = ($hour < 5.0 || $hour >= 23.0) ? 0.65 : 0.82;
|
||||
$baseFactor = $night + (0.48 * $morning) + (0.72 * $evening);
|
||||
$cloudVariation = (18.0 * sin(($hour + 0.4) * 1.7)) + (8.0 * sin($hour * 4.6));
|
||||
$outside = 7.0 + (6.0 * sin(2.0 * M_PI * ($hour - 9.0) / 24.0));
|
||||
|
||||
return [
|
||||
'base' => max(150.0, $base * $baseFactor),
|
||||
'cloud' => min(100.0, max(0.0, $cloud + $cloudVariation)),
|
||||
'outside' => $outside,
|
||||
];
|
||||
}
|
||||
|
||||
function enelixDemoBatteryRegisterPower(int $management, int $mode, float $power): float
|
||||
{
|
||||
if ($management !== 2) {
|
||||
return 0.0;
|
||||
}
|
||||
if ($mode === 11) {
|
||||
return max(0.0, $power);
|
||||
}
|
||||
if ($mode === 12) {
|
||||
return -max(0.0, $power);
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
function enelixDemoUpdateBufferTemperature(int $variableId, int $power, float $outside, int $minutes): void
|
||||
{
|
||||
$current = (float) GetValue($variableId);
|
||||
$heating = ($power / 1000.0) * 0.018 * $minutes;
|
||||
$loss = max(0.0, $current - $outside) * 0.0007 * $minutes;
|
||||
SetValue($variableId, round(min(65.0, max(22.0, $current + $heating - $loss)), 2));
|
||||
}
|
||||
|
||||
function enelixDemoUpdateBatterySoc(int $variableId, float $power, int $minutes): void
|
||||
{
|
||||
$current = (float) GetValue($variableId);
|
||||
$energy = ($power / 1000.0) * ($minutes / 60.0);
|
||||
$storedEnergy = $energy >= 0.0 ? $energy * 0.94 : $energy / 0.94;
|
||||
$delta = ($storedEnergy / 10.0) * 100.0;
|
||||
SetValue($variableId, round(min(100.0, max(5.0, $current + $delta)), 2));
|
||||
}
|
||||
|
||||
function enelixDemoUpdateTemperature(int $variableId, int $power, int $minutes): void
|
||||
@@ -439,6 +673,8 @@ function enelixDemoValidateRuntime(): void
|
||||
ENELIX_DEMO_MANAGER_MODULE,
|
||||
ENELIX_DEMO_BOILER_MODULE,
|
||||
ENELIX_DEMO_SINGLE_MODULE,
|
||||
ENELIX_DEMO_BUFFER_MODULE,
|
||||
ENELIX_DEMO_BATTERY_MODULE,
|
||||
ENELIX_DEMO_DUMMY_MODULE,
|
||||
ENELIX_DEMO_ENERGY_DISTRIBUTION_MODULE,
|
||||
] as $moduleId) {
|
||||
@@ -450,10 +686,11 @@ function enelixDemoValidateRuntime(): void
|
||||
|
||||
function enelixDemoEnsureProfiles(): void
|
||||
{
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.Power', 2, -12000, 12000, 100, 0, ' W');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.PowerPositive', 2, 0, 12000, 100, 0, ' W');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.Power', 2, -20000, 20000, 100, 0, ' W');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.PowerPositive', 2, 0, 20000, 100, 0, ' W');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.Hour', 2, 0, 23.75, 0.25, 2, ' h');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.Percent', 1, 0, 100, 5, 0, ' %');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.PercentFloat', 2, 0, 100, 0.1, 1, ' %');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.Speed', 1, 1, 60, 1, 0, ' min');
|
||||
enelixDemoEnsureProfile('ENELIX.Demo.Energy', 2, 0, 1000000, 0.01, 2, ' kWh');
|
||||
}
|
||||
@@ -628,15 +865,34 @@ function enelixDemoEnsureCyclicEvent(int $scriptId, string $ident, int $seconds)
|
||||
return $id;
|
||||
}
|
||||
|
||||
function enelixDemoConfigure(int $instanceId, array $properties): void
|
||||
function enelixDemoConfigure(
|
||||
int $instanceId,
|
||||
array $properties,
|
||||
array $allowedStatuses = [102]
|
||||
): void
|
||||
{
|
||||
foreach ($properties as $name => $value) {
|
||||
IPS_SetProperty($instanceId, (string) $name, $value);
|
||||
}
|
||||
IPS_ApplyChanges($instanceId);
|
||||
if ((int) IPS_GetInstance($instanceId)['InstanceStatus'] !== 102) {
|
||||
throw new RuntimeException('Instanz ist nach der Konfiguration nicht aktiv: ' . IPS_GetName($instanceId));
|
||||
$deadline = microtime(true) + 2.0;
|
||||
do {
|
||||
$status = (int) IPS_GetInstance($instanceId)['InstanceStatus'];
|
||||
if ($status === 102) {
|
||||
return;
|
||||
}
|
||||
IPS_Sleep(50);
|
||||
} while (microtime(true) < $deadline);
|
||||
|
||||
if (in_array($status, $allowedStatuses, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException(
|
||||
'Instanz ist nach der Konfiguration nicht aktiv: '
|
||||
. IPS_GetName($instanceId)
|
||||
. ' (Status ' . $status . ', erwartet: ' . implode(', ', $allowedStatuses) . ')'
|
||||
);
|
||||
}
|
||||
|
||||
function enelixDemoConfigureArchive(array $variableIds): void
|
||||
@@ -713,7 +969,7 @@ function enelixDemoRemove(bool $confirmed = false): void
|
||||
IPS_ApplyChanges($visualization);
|
||||
}
|
||||
enelixDemoDeleteTree($root);
|
||||
foreach (['ENELIX.Demo.Power', 'ENELIX.Demo.PowerPositive', 'ENELIX.Demo.Hour', 'ENELIX.Demo.Percent', 'ENELIX.Demo.Speed', 'ENELIX.Demo.Energy'] as $profile) {
|
||||
foreach (['ENELIX.Demo.Power', 'ENELIX.Demo.PowerPositive', 'ENELIX.Demo.Hour', 'ENELIX.Demo.Percent', 'ENELIX.Demo.PercentFloat', 'ENELIX.Demo.Speed', 'ENELIX.Demo.Energy'] as $profile) {
|
||||
if (IPS_VariableProfileExists($profile)) {
|
||||
IPS_DeleteVariableProfile($profile);
|
||||
}
|
||||
@@ -771,9 +1027,14 @@ function enelixDemoConfigureEnergyDistribution(int $instanceId, array $nodes): v
|
||||
]);
|
||||
}
|
||||
|
||||
function enelixDemoLinkInstanceVariables(int $categoryId, int $instanceId, int $sensorId): void
|
||||
{
|
||||
enelixDemoEnsureLink($categoryId, 'SensorTemperatur', 'Gemessene Temperatur', $sensorId, 5);
|
||||
function enelixDemoLinkInstanceVariables(
|
||||
int $categoryId,
|
||||
int $instanceId,
|
||||
int $sensorId,
|
||||
string $sensorName = 'Gemessene Temperatur',
|
||||
string $sensorIdent = 'SensorTemperatur'
|
||||
): void {
|
||||
enelixDemoEnsureLink($categoryId, $sensorIdent, $sensorName, $sensorId, 5);
|
||||
$position = 10;
|
||||
foreach (IPS_GetChildrenIDs($instanceId) as $childId) {
|
||||
if ((int) IPS_GetObject($childId)['ObjectType'] !== 2) {
|
||||
|
||||
Reference in New Issue
Block a user