$temp1, 'LeistungsStufen' => json_encode([ ['Stufe' => 1, 'Leistung' => 1200, 'Schaltkontakt_Stufe' => $boiler11], ['Stufe' => 2, 'Leistung' => 2400, 'Schaltkontakt_Stufe' => $boiler12], ], JSON_THROW_ON_ERROR), 'Boilervolumen' => 300, 'LastwechselSperrzeit' => 2, 'TemperaturMaxAlter' => 30, 'LegionellenfunktionAktiv' => false, 'PrioritaetPV' => 0, 'PrioritaetPeak' => 2, 'Meldeintervall' => 5, 'VorgabeTimeout' => 30, 'EinstellungenInVisu' => true, 'DiagnosevariablenAnzeigen' => true, ]); enelixDemoConfigure($boiler2, [ 'Boilerfuehler_PT1' => $temp2, 'LeistungsStufen' => json_encode([ ['Stufe' => 1, 'Leistung' => 1500, 'Schaltkontakt_Stufe' => $boiler21], ['Stufe' => 2, 'Leistung' => 3000, 'Schaltkontakt_Stufe' => $boiler22], ], JSON_THROW_ON_ERROR), 'Boilervolumen' => 500, 'LastwechselSperrzeit' => 2, 'TemperaturMaxAlter' => 30, 'LegionellenfunktionAktiv' => false, 'PrioritaetPV' => 1, 'PrioritaetPeak' => 1, 'Meldeintervall' => 5, 'VorgabeTimeout' => 30, 'EinstellungenInVisu' => true, 'DiagnosevariablenAnzeigen' => true, ]); enelixDemoConfigure($dryer, [ 'Nennleistung' => 900, 'SchaltkontaktVariableID' => $dryContact, 'RueckmeldungVariableID' => $dryContact, 'Mindesteinschaltdauer' => 2, '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, 'EinstellungenInVisu' => true, 'DiagnosevariablenAnzeigen' => true, ]); $energyDistribution = enelixDemoEnsureInstance( $dashboard, 'EnergieverteilungNativ', 'Energiefluss', ENELIX_DEMO_ENERGY_DISTRIBUTION_MODULE, 10 ); enelixDemoConfigureEnergyDistribution($energyDistribution, [ enelixDemoEnergyNode($flowPv, 'PV-Anlage', 'solar'), enelixDemoEnergyNode($flowGrid, 'Stromnetz', 'grid'), enelixDemoEnergyNode($flowBase, 'Haus-Grundlast', 'consumer'), 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'); if ($oldEnergyDistribution > 0 && $oldEnergyDistribution !== $energyDistribution) { IPS_SetHidden($oldEnergyDistribution, true); } foreach ([$boiler1, $boiler2] as $boilerId) { IPS_RequestAction($boilerId, 'Mindesttemperatur', 42.0); IPS_RequestAction($boilerId, 'Maximaltemperatur', 62.0); } $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, $buffer, $battery] ); $pvNennleistungKw = max(0.1, (float) GetValue($peak) / 1000.0); $wechselrichterKonfiguration = [[ 'ID' => 'demo-hybrid-1', 'Name' => 'Demo-Hybridwechselrichter', 'Typ' => 'hybrid', 'ACNennleistung_kW' => $pvNennleistungKw, 'ACEinspeisegrenze_kW' => 0.0, 'ACBezugsgrenze_kW' => 0.0, ]]; $pvKonfiguration = [[ 'ID' => 'demo-pv-1', 'Name' => 'Demo-PV-Anlage', 'DCLeistung_kWp' => $pvNennleistungKw, 'Neigung_Grad' => 30.0, 'Azimut_Grad' => 0.0, 'WechselrichterID' => 'demo-hybrid-1', 'MPPT' => 'A', 'Modulanzahl' => 0, 'Modulleistung_Wp' => 0.0, ]]; $batterieKonfiguration = [[ 'ID' => 'demo-battery-1', 'Name' => 'Batteriespeicher 10 kWh', 'Nennkapazitaet_kWh' => 10.0, 'Nutzkapazitaet_kWh' => 10.0, 'MaxLadeleistung_kW' => max(0.1, (float) GetValue($batteryMaxCharge) / 1000.0), 'MaxEntladeleistung_kW' => max( 0.1, (float) GetValue($batteryMaxDischarge) / 1000.0 ), 'WechselrichterID' => 'demo-hybrid-1', 'Kopplung' => 'hybrid', ]]; $managerProperties = [ 'NetzleistungVariableID' => $grid, 'Netzleistungsfaktor' => 1.0, 'PrognosePVVariableID' => $pvDisplay, 'PrognoseHausverbrauchVariableID' => $houseDisplay, 'PrognoseSOCVariableID' => $batterySoc, 'PrognoseSendeintervall' => 60, 'MesswertMaxAlter' => 10, 'AutomatischeSuche' => false, 'SuchbereichID' => $root, 'VerbraucherZuordnung' => '[]', 'KeepAlive' => 5, 'VerbraucherTimeout' => 15, 'SollwertSolarladen' => 0.0, 'Umschaltdifferenz' => 0.0, 'AnlagenWechselrichter' => json_encode( $wechselrichterKonfiguration, JSON_THROW_ON_ERROR ), 'AnlagenPVFlaechen' => json_encode($pvKonfiguration, JSON_THROW_ON_ERROR), 'AnlagenBatterien' => json_encode( $batterieKonfiguration, JSON_THROW_ON_ERROR ), 'DiagnosevariablenAnzeigen' => true, 'LoggingEin' => false, ]; 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; } 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, 'Boiler1Liste', 'Boiler 1', ENELIX_DEMO_DUMMY_MODULE, 110 ); $boiler2List = enelixDemoEnsureInstance( $dashboard, 'Boiler2Liste', 'Boiler 2', ENELIX_DEMO_DUMMY_MODULE, 120 ); $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); foreach ([$boiler1, $boiler2, $buffer, $battery] as $hiddenInstanceId) { IPS_SetHidden($hiddenInstanceId, true); } foreach (['Boiler1Link', 'Boiler2Link'] as $oldLinkIdent) { $oldLink = enelixDemoFind($dashboard, $oldLinkIdent); if ($oldLink > 0) { IPS_SetHidden($oldLink, true); } } enelixDemoEnsureLink($dashboard, 'ManagerLink', 'Manager', $manager, 100); 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, $bufferPower, $batteryPower, $pvEnergy, $houseEnergy, $gridImportEnergy, $gridExportEnergy, $temp1, $temp2, $bufferTemp, $outsideTemp, $batterySoc, $batteryActual, $flowPv, $flowGrid, $flowBase, $flowBoiler1, $flowBoiler2, $flowDryer, $flowBuffer, $flowBattery, $flowHouse, $flowLoads, ]); enelixDemoEnsureChart($history, 'LeistungsdiagrammNativ', 'Leistungsverlauf', [ enelixDemoChartDataset($flowPv, 'PV', '#f4c542', '~Power'), enelixDemoChartDataset($flowHouse, 'Hausverbrauch', '#2bb673', '~Power'), enelixDemoChartDataset($flowLoads, 'EMS-Leistung inkl. Speicher', '#2786ac', '~Power'), enelixDemoChartDataset($flowGrid, 'Netzleistung', '#e95d5d', '~Power'), ], 10); enelixDemoEnsureChart($history, 'EnergiediagrammNativ', 'Kumulierte Energien', [ enelixDemoChartDataset($pvEnergy, 'PV', '#f4c542', '~Electricity'), enelixDemoChartDataset($houseEnergy, 'Hausverbrauch', '#2bb673', '~Electricity'), enelixDemoChartDataset($gridImportEnergy, 'Netzbezug', '#e95d5d', '~Electricity'), enelixDemoChartDataset($gridExportEnergy, 'Einspeisung', '#38a3db', '~Electricity'), ], 20); enelixDemoConfigureVisualization($root, $energyDistribution); enelixDemoTick('Install'); return [ 'rootId' => $root, 'visualizationId' => enelixDemoFirstInstance(ENELIX_DEMO_TILE_MODULE), 'managerId' => $manager, 'consumerIds' => $consumerIds, 'tickScriptId' => $tickScript, ]; } function enelixDemoTick(string $source = 'Manual'): void { $root = enelixDemoRequiredId(0, ENELIX_DEMO_ROOT_IDENT); $dashboard = enelixDemoRequiredId($root, 'Dashboard'); $simulation = enelixDemoRequiredId($root, 'Simulation'); $ems = enelixDemoRequiredId($root, 'EMS'); $technical = enelixDemoRequiredId($root, 'Technik'); $autoId = enelixDemoRequiredId($simulation, 'Automatik'); $hourId = enelixDemoRequiredId($simulation, 'Tageszeit'); $minutes = max(1, (int) GetValue(enelixDemoRequiredId($simulation, 'Geschwindigkeit'))); $hour = (float) GetValue($hourId); if ($source === 'Timer' && (bool) GetValue($autoId)) { $hour = fmod($hour + ($minutes / 60.0), 24.0); 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')); $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 - ($environment['cloud'] * 0.0085)); $contacts = [ 'boiler11' => enelixDemoRequiredId($technical, 'Boiler1Stufe1'), 'boiler12' => enelixDemoRequiredId($technical, 'Boiler1Stufe2'), 'boiler21' => enelixDemoRequiredId($technical, 'Boiler2Stufe1'), 'boiler22' => enelixDemoRequiredId($technical, 'Boiler2Stufe2'), 'dryer' => enelixDemoRequiredId($technical, 'EntfeuchterKontakt'), 'buffer1' => enelixDemoRequiredId($technical, 'PufferStufe1'), 'buffer2' => enelixDemoRequiredId($technical, 'PufferStufe2'), 'battery' => enelixDemoRequiredId($technical, 'BatterieIstleistung'), ]; $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 ); 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)); $consumers = [ enelixDemoRequiredId($ems, 'Boiler1'), enelixDemoRequiredId($ems, 'Boiler2'), enelixDemoRequiredId($ems, 'Entfeuchter'), enelixDemoRequiredId($ems, 'Pufferspeicher'), enelixDemoRequiredId($ems, 'Batterie'), ]; $manager = enelixDemoRequiredId($ems, 'Manager'); $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; $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; SetValue($gridId, round($grid, 1)); 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($emsPower / 1000.0, 3)); $history = enelixDemoRequiredId($root, 'Historie'); $energyIds = [ 'pv' => enelixDemoRequiredId($history, 'PVEnergie'), 'house' => enelixDemoRequiredId($history, 'HausEnergie'), 'import' => enelixDemoRequiredId($history, 'NetzbezugEnergie'), 'export' => enelixDemoRequiredId($history, 'EinspeisungEnergie'), ]; if ($source === 'Timer') { $hours = $minutes / 60.0; enelixDemoAddEnergy($energyIds['pv'], $pv, $hours); enelixDemoAddEnergy($energyIds['house'], $house, $hours); enelixDemoAddEnergy($energyIds['import'], max(0.0, $grid), $hours); enelixDemoAddEnergy($energyIds['export'], max(0.0, -$grid), $hours); } } function enelixDemoRenderFlow(array $state): string { $format = static fn (float $value): string => number_format($value, 0, ',', "'"); $hour = (float) $state['hour']; $time = sprintf('%02d:%02d', (int) floor($hour), (int) round(($hour - floor($hour)) * 60) % 60); $grid = (float) $state['grid']; $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 '