Add interactive Enelix demo system
Tests / test (push) Successful in 42s

This commit is contained in:
dh
2026-09-17 13:41:46 +00:00
parent 4a01e9df0f
commit 312b76aee5
3 changed files with 649 additions and 0 deletions
+555
View File
@@ -0,0 +1,555 @@
<?php
declare(strict_types=1);
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_TILE_MODULE = '{B5B875BB-9B76-45FD-4E67-2607E45B3AC4}';
const ENELIX_DEMO_ARCHIVE_MODULE = '{43192F0B-135B-4CE7-A0A7-1475603F3060}';
function enelixDemoInstall(): array
{
enelixDemoValidateRuntime();
$root = enelixDemoEnsureCategory(0, ENELIX_DEMO_ROOT_IDENT, 'Enelix 2 Demoanlage', 100);
IPS_SetInfo($root, 'Idempotent erzeugte Enelix-2-Demo. Entfernen nur mit enelixDemoRemove(true).');
$dashboard = enelixDemoEnsureCategory($root, 'Dashboard', 'Energiefluss', 10);
$simulation = enelixDemoEnsureCategory($root, 'Simulation', 'Simulation', 20);
$ems = enelixDemoEnsureCategory($root, 'EMS', 'Manager und Verbraucher', 30);
$technical = enelixDemoEnsureCategory($root, 'Technik', 'Simulationstechnik', 90, true);
enelixDemoEnsureProfiles();
$flow = enelixDemoEnsureVariable($dashboard, 'Energiefluss', 'Energiefluss', 3, '~HTMLBox', '', 10);
$pvDisplay = enelixDemoEnsureVariable($dashboard, 'PVLeistung', 'PV-Leistung', 2, 'ENELIX.Demo.PowerPositive', 0.0, 20);
$loadDisplay = enelixDemoEnsureVariable($dashboard, 'Verbraucherleistung', 'Geregelte Verbraucher', 2, 'ENELIX.Demo.PowerPositive', 0.0, 30);
$grid = enelixDemoEnsureVariable($dashboard, 'Netzleistung', 'Netzleistung', 2, 'ENELIX.Demo.Power', 0.0, 40);
$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);
$actorScript = enelixDemoEnsureScript(
$technical,
'AktorAktion',
'Simulierte Aktoren',
"<?php\nSetValue((int) \$_IPS['VARIABLE'], (bool) \$_IPS['VALUE']);\n",
10
);
$tickScript = enelixDemoEnsureScript(
$technical,
'SimulationTick',
'Simulation aktualisieren',
"<?php\nrequire_once IPS_GetKernelDir() . 'modules/Enelix-EMS/examples/Demoanlage/bootstrap.php';\nenelixDemoTick('Timer');\n",
20
);
$controlScript = enelixDemoEnsureScript(
$technical,
'BedienAktion',
'Bedienwerte übernehmen',
"<?php\nSetValue((int) \$_IPS['VARIABLE'], \$_IPS['VALUE']);\nIPS_RunScript(" . $tickScript . ");\n",
30
);
foreach ([$auto, $hour, $speed, $peak, $cloud, $base] as $variableId) {
IPS_SetVariableCustomAction($variableId, $controlScript);
}
enelixDemoEnsureCyclicEvent($tickScript, 'Simulationszyklus', 2);
$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);
foreach ([$boiler11, $boiler12, $boiler21, $boiler22, $dryContact] as $actorId) {
IPS_SetVariableCustomAction($actorId, $actorScript);
}
$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);
enelixDemoConfigure($boiler1, [
'Boilerfuehler_PT1' => $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' => 0,
'Meldeintervall' => 5,
'VorgabeTimeout' => 30,
'EinstellungenInVisu' => true,
'DiagnosevariablenAnzeigen' => true,
]);
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]
);
enelixDemoConfigure($manager, [
'NetzleistungVariableID' => $grid,
'Netzleistungsfaktor' => 1.0,
'MesswertMaxAlter' => 10,
'AutomatischeSuche' => false,
'SuchbereichID' => $root,
'VerbraucherZuordnung' => json_encode($assignment, JSON_THROW_ON_ERROR),
'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);
}
IPS_RequestAction($manager, 'Aktiv', true);
enelixDemoEnsureLink($dashboard, 'ManagerLink', 'Manager', $manager, 50);
enelixDemoEnsureLink($dashboard, 'Boiler1Link', 'Boiler 1', $boiler1, 60);
enelixDemoEnsureLink($dashboard, 'Boiler2Link', 'Boiler 2', $boiler2, 70);
enelixDemoEnsureLink($dashboard, 'EntfeuchterLink', 'Entfeuchter', $dryer, 80);
enelixDemoEnsureLink($simulation, 'Boiler1TemperaturLink', 'Boiler 1 Temperatur', $temp1, 70);
enelixDemoEnsureLink($simulation, 'Boiler2TemperaturLink', 'Boiler 2 Temperatur', $temp2, 80);
enelixDemoConfigureArchive([$pvDisplay, $loadDisplay, $grid, $temp1, $temp2]);
enelixDemoConfigureVisualization($root, $flow);
enelixDemoTick('Install');
return [
'rootId' => $root,
'visualizationId' => enelixDemoFirstInstance(ENELIX_DEMO_TILE_MODULE),
'managerId' => $manager,
'consumerIds' => [$boiler1, $boiler2, $dryer],
'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);
}
$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));
$contacts = [
'boiler11' => enelixDemoRequiredId($technical, 'Boiler1Stufe1'),
'boiler12' => enelixDemoRequiredId($technical, 'Boiler1Stufe2'),
'boiler21' => enelixDemoRequiredId($technical, 'Boiler2Stufe1'),
'boiler22' => enelixDemoRequiredId($technical, 'Boiler2Stufe2'),
'dryer' => enelixDemoRequiredId($technical, 'EntfeuchterKontakt'),
];
$powerBefore = enelixDemoControlledPower($contacts);
$temp1 = enelixDemoRequiredId($technical, 'Boiler1Temperatur');
$temp2 = enelixDemoRequiredId($technical, 'Boiler2Temperatur');
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);
}
$gridId = enelixDemoRequiredId($dashboard, 'Netzleistung');
SetValue($gridId, round($base + $powerBefore - $pv, 1));
$consumers = [
enelixDemoRequiredId($ems, 'Boiler1'),
enelixDemoRequiredId($ems, 'Boiler2'),
enelixDemoRequiredId($ems, 'Entfeuchter'),
];
foreach ($consumers as $consumerId) {
IPS_RequestAction($consumerId, 'Melden', true);
}
$manager = enelixDemoRequiredId($ems, 'Manager');
IPS_RequestAction($manager, 'Regeln', true);
$power = enelixDemoControlledPower($contacts);
$grid = $base + $power - $pv;
SetValue($gridId, round($grid, 1));
SetValue(enelixDemoRequiredId($dashboard, 'PVLeistung'), round($pv, 1));
SetValue(enelixDemoRequiredId($dashboard, 'Verbraucherleistung'), (float) $power);
$state = [
'hour' => $hour,
'pv' => $pv,
'base' => $base,
'grid' => $grid,
'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,
'temp1' => (float) GetValue($temp1),
'temp2' => (float) GetValue($temp2),
'manager' => (string) GetValue(enelixDemoRequiredId($manager, 'Betriebsart')),
];
SetValue(enelixDemoRequiredId($dashboard, 'Energiefluss'), enelixDemoRenderFlow($state));
}
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 ? '&rarr;' : ($grid > 25 ? '&larr;' : '&harr;');
$gridClass = abs($grid) <= 250 ? 'good' : ($grid < 0 ? 'export' : 'import');
$b1Class = (float) $state['boiler1'] > 0 ? 'on' : '';
$b2Class = (float) $state['boiler2'] > 0 ? 'on' : '';
$dryClass = (float) $state['dryer'] > 0 ? '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}
.enelix-flow *{box-sizing:border-box}.enelix-head{display:flex;justify-content:space-between;align-items:end;margin-bottom:18px}
.enelix-title{font-size:24px;font-weight:750;letter-spacing:0}.enelix-sub{color:#9dacbd;font-size:13px}
.enelix-line{display:grid;grid-template-columns:minmax(120px,1fr) 48px minmax(140px,1.2fr) 48px minmax(120px,1fr);align-items:center;gap:8px}
.enelix-node{border:1px solid #34404d;background:#17202a;padding:16px;text-align:center;border-radius:8px;min-height:92px}
.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-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">&rarr;</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>
<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>';
}
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);
}
function enelixDemoUpdateTemperature(int $variableId, int $power, int $minutes): void
{
$current = (float) GetValue($variableId);
$delta = (($power / 1000.0) * 0.03 * $minutes) - (0.01 * $minutes);
SetValue($variableId, round(min(70.0, max(38.0, $current + $delta)), 2));
}
function enelixDemoValidateRuntime(): void
{
$version = IPS_GetKernelVersion();
if (version_compare($version, '8.0', '<') || version_compare($version, '9.0', '>=')) {
throw new RuntimeException('Die Demo benötigt IP-Symcon 8.x; aktiv ist ' . $version . '.');
}
$available = IPS_GetModuleList();
foreach ([ENELIX_DEMO_MANAGER_MODULE, ENELIX_DEMO_BOILER_MODULE, ENELIX_DEMO_SINGLE_MODULE] as $moduleId) {
if (!in_array($moduleId, $available, true)) {
throw new RuntimeException('Benötigtes Enelix-Modul fehlt: ' . $moduleId);
}
}
}
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.Hour', 2, 0, 23.75, 0.25, 2, ' h');
enelixDemoEnsureProfile('ENELIX.Demo.Percent', 1, 0, 100, 5, 0, ' %');
enelixDemoEnsureProfile('ENELIX.Demo.Speed', 1, 1, 60, 1, 0, ' min');
}
function enelixDemoEnsureProfile(string $name, int $type, float $min, float $max, float $step, int $digits, string $suffix): void
{
if (!IPS_VariableProfileExists($name)) {
IPS_CreateVariableProfile($name, $type);
}
$profile = IPS_GetVariableProfile($name);
if ((int) $profile['ProfileType'] !== $type) {
throw new RuntimeException('Variablenprofil hat einen unpassenden Typ: ' . $name);
}
IPS_SetVariableProfileValues($name, $min, $max, $step);
IPS_SetVariableProfileDigits($name, $digits);
IPS_SetVariableProfileText($name, '', $suffix);
}
function enelixDemoEnsureCategory(int $parent, string $ident, string $name, int $position, bool $hidden = false): int
{
$id = enelixDemoFind($parent, $ident);
if ($id === 0) {
$id = IPS_CreateCategory();
IPS_SetParent($id, $parent);
IPS_SetIdent($id, $ident);
}
enelixDemoRequireType($id, 0);
IPS_SetName($id, $name);
IPS_SetPosition($id, $position);
IPS_SetHidden($id, $hidden);
return $id;
}
function enelixDemoEnsureVariable(int $parent, string $ident, string $name, int $type, string $profile, $default, int $position): int
{
$id = enelixDemoFind($parent, $ident);
$created = false;
if ($id === 0) {
$id = IPS_CreateVariable($type);
IPS_SetParent($id, $parent);
IPS_SetIdent($id, $ident);
$created = true;
}
enelixDemoRequireType($id, 2);
$variable = IPS_GetVariable($id);
if ((int) $variable['VariableType'] !== $type) {
throw new RuntimeException('Variablentyp-Konflikt bei ' . $ident);
}
IPS_SetName($id, $name);
IPS_SetPosition($id, $position);
if ($profile !== '') {
IPS_SetVariableCustomProfile($id, $profile);
}
if ($created) {
SetValue($id, $default);
}
return $id;
}
function enelixDemoEnsureScript(int $parent, string $ident, string $name, string $content, int $position): int
{
$id = enelixDemoFind($parent, $ident);
if ($id === 0) {
$id = IPS_CreateScript(0);
IPS_SetParent($id, $parent);
IPS_SetIdent($id, $ident);
}
enelixDemoRequireType($id, 3);
IPS_SetName($id, $name);
IPS_SetPosition($id, $position);
IPS_SetScriptContent($id, $content);
return $id;
}
function enelixDemoEnsureInstance(int $parent, string $ident, string $name, string $moduleId, int $position): int
{
$id = enelixDemoFind($parent, $ident);
if ($id === 0) {
$id = IPS_CreateInstance($moduleId);
IPS_SetParent($id, $parent);
IPS_SetIdent($id, $ident);
}
enelixDemoRequireType($id, 1);
$actualModule = IPS_GetInstance($id)['ModuleInfo']['ModuleID'];
if ($actualModule !== $moduleId) {
throw new RuntimeException('Modulkonflikt bei Instanz ' . $ident);
}
IPS_SetName($id, $name);
IPS_SetPosition($id, $position);
return $id;
}
function enelixDemoEnsureLink(int $parent, string $ident, string $name, int $target, int $position): int
{
$id = enelixDemoFind($parent, $ident);
if ($id === 0) {
$id = IPS_CreateLink();
IPS_SetParent($id, $parent);
IPS_SetIdent($id, $ident);
}
enelixDemoRequireType($id, 6);
IPS_SetName($id, $name);
IPS_SetPosition($id, $position);
IPS_SetLinkTargetID($id, $target);
return $id;
}
function enelixDemoEnsureCyclicEvent(int $scriptId, string $ident, int $seconds): int
{
$id = enelixDemoFind($scriptId, $ident);
if ($id === 0) {
$id = IPS_CreateEvent(1);
IPS_SetParent($id, $scriptId);
IPS_SetIdent($id, $ident);
}
enelixDemoRequireType($id, 4);
IPS_SetName($id, 'Alle ' . $seconds . ' Sekunden');
IPS_SetEventCyclic($id, 0, 0, 0, 0, 1, $seconds);
IPS_SetEventActive($id, true);
return $id;
}
function enelixDemoConfigure(int $instanceId, array $properties): 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));
}
}
function enelixDemoConfigureArchive(array $variableIds): void
{
$archive = enelixDemoFirstInstance(ENELIX_DEMO_ARCHIVE_MODULE);
if ($archive === 0) {
return;
}
foreach ($variableIds as $variableId) {
AC_SetLoggingStatus($archive, $variableId, true);
AC_SetAggregationType($archive, $variableId, 0);
}
IPS_ApplyChanges($archive);
}
function enelixDemoConfigureVisualization(int $root, int $flow): void
{
$visualization = enelixDemoFirstInstance(ENELIX_DEMO_TILE_MODULE);
if ($visualization === 0) {
return;
}
IPS_SetProperty($visualization, 'BaseID', $root);
IPS_SetProperty($visualization, 'CategoryDisplay', 'Cards');
IPS_SetProperty($visualization, 'ShowGreeting', 3);
IPS_SetProperty($visualization, 'GreetingName', 'Enelix 2 Demo');
IPS_SetProperty($visualization, 'ShowDateTime', true);
IPS_SetProperty($visualization, 'Favorites', json_encode([['ObjectID' => $flow]], JSON_THROW_ON_ERROR));
IPS_ApplyChanges($visualization);
}
function enelixDemoFirstInstance(string $moduleId): int
{
$ids = IPS_GetInstanceListByModuleID($moduleId);
return $ids === [] ? 0 : (int) $ids[0];
}
function enelixDemoFind(int $parent, string $ident): int
{
$id = @IPS_GetObjectIDByIdent($ident, $parent);
return $id === false ? 0 : (int) $id;
}
function enelixDemoRequiredId(int $parent, string $ident): int
{
$id = enelixDemoFind($parent, $ident);
if ($id === 0) {
throw new RuntimeException('Demo-Objekt fehlt: ' . $ident);
}
return $id;
}
function enelixDemoRequireType(int $objectId, int $type): void
{
$actual = (int) IPS_GetObject($objectId)['ObjectType'];
if ($actual !== $type) {
throw new RuntimeException('Objekttyp-Konflikt bei ID ' . $objectId);
}
}
function enelixDemoRemove(bool $confirmed = false): void
{
if (!$confirmed) {
throw new RuntimeException('Rollback nur mit enelixDemoRemove(true) ausführen.');
}
$root = enelixDemoFind(0, ENELIX_DEMO_ROOT_IDENT);
if ($root === 0) {
return;
}
$visualization = enelixDemoFirstInstance(ENELIX_DEMO_TILE_MODULE);
if ($visualization > 0 && (int) json_decode(IPS_GetConfiguration($visualization), true)['BaseID'] === $root) {
IPS_SetProperty($visualization, 'BaseID', 0);
IPS_ApplyChanges($visualization);
}
enelixDemoDeleteTree($root);
foreach (['ENELIX.Demo.Power', 'ENELIX.Demo.PowerPositive', 'ENELIX.Demo.Hour', 'ENELIX.Demo.Percent', 'ENELIX.Demo.Speed'] as $profile) {
if (IPS_VariableProfileExists($profile)) {
IPS_DeleteVariableProfile($profile);
}
}
}
function enelixDemoDeleteTree(int $objectId): void
{
foreach (array_reverse(IPS_GetChildrenIDs($objectId)) as $childId) {
enelixDemoDeleteTree((int) $childId);
}
$type = (int) IPS_GetObject($objectId)['ObjectType'];
if ($type === 1 && (int) IPS_GetInstance($objectId)['ConnectionID'] > 0) {
IPS_DisconnectInstance($objectId);
}
switch ($type) {
case 0: IPS_DeleteCategory($objectId); break;
case 1: IPS_DeleteInstance($objectId); break;
case 2: IPS_DeleteVariable($objectId); break;
case 3: IPS_DeleteScript($objectId, true); break;
case 4: IPS_DeleteEvent($objectId); break;
case 5: IPS_DeleteMedia($objectId, true); break;
case 6: IPS_DeleteLink($objectId); break;
default: throw new RuntimeException('Unbekannter Objekttyp: ' . $type);
}
}