Was draussen haengt, stand als Pseudo-Raum "aussen" in automations.php:
"Geraete, die zu keinem Raum der Wohnung gehoeren - Carport, Garage,
Regensensor". Das waren inzwischen sechzehn Geraete in einem einzigen Topf,
der groesste ueberhaupt - in der Geraeteauswahl des Editors standen die fuenf
Carport-Wechselrichter, das Sonnensegel und das Garagentor unsortiert
nebeneinander.
Jetzt ist das Aussengelaende eine Etage wie OG, EG und UG, mit eigenen
Raeumen: Carport, Veranda, Pergola, Terrasse, Garage und Garten. Die Namen
stammen von den Geraeten selbst - die Hoymiles heissen seit jeher "Carport
1-6" und "Veranda OG2-4". Die achtzehn Geraete sind verteilt (die beiden
Ventilsteuerungen hatten noch gar keinen Raum und stehen jetzt im Garten,
zusammen mit dem Regensensor, der ihre Automatiken ausloest); "Ohne Raum"
faellt damit von acht auf sechs, und die sind es zu Recht.
AG ist eine Etage ohne Grundriss. Ihre Raeume haben kein x/y, es gibt ja
kein Bild, auf das eine Kachel zeigen koennte - ein Raum ohne Position war
schon immer vorgesehen. Das Menue und das SVG richten sich deshalb nach der
neuen floorsWithPlan(), die Zuordnung und die Automatiken nach $floors.
Bekommt der erste AG-Raum eine Kachel, erscheint die Etage von selbst auch
im Menue; es gibt keine zweite Liste, die man nachziehen muesste.
Die Etagen standen bisher an sechs Stellen noch einmal im Code. Jetzt gibt
es genau eine Liste, $floors in rooms.php, und alles andere leitet sich ab:
* automations.php prueft gegen $floors statt gegen eine eigene Whitelist
* ajax/AutoAction.php baut seine Etagen-Auswahl daraus
* home.php baut Reiter und Inhaltsbereiche in einer Schleife statt
dreimal fast dasselbe HTML
* header.php baut die Menuepunkte daraus
* switchTab() in homeMQTT.js liest die Reiter aus der Seite, statt OG, EG
und UG sechsmal aufzuzaehlen
* refreshAutomations() ebenso - eine feste Liste haette die vierte Etage
still ausgelassen, die Tabelle waere ohne Fehlermeldung leer geblieben
Dazu die ausgeschriebenen Namen in $floorLabels: das Kuerzel steht in URLs,
SVG-Ids und im MQTT-Baum, "Aussengelaende" nur als Titel am Menuepunkt und
am Reiter.
In der Datenbank ist das Enum um 'AG' erweitert; header.php laedt rooms.php
jetzt selbst, weil es vor home.php eingebunden wird.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
223 lines
14 KiB
PHP
223 lines
14 KiB
PHP
<?php require_once(__DIR__ . "/rooms.php"); ?>
|
|
<!--begin::App Main-->
|
|
<main class="app-main">
|
|
<!--begin::App Content Header-->
|
|
<div class="app-content-header">
|
|
<!--begin::Container-->
|
|
<div class="container-fluid">
|
|
<!--begin::Row-->
|
|
<div class="row">
|
|
<div class="col-sm-12">
|
|
<h3 class="mb-0">Home</h3>
|
|
</div>
|
|
</div>
|
|
<!--end::Row-->
|
|
</div>
|
|
<!--end::Container-->
|
|
</div>
|
|
<!--end::App Content Header-->
|
|
<!--begin::App Content-->
|
|
<div class="app-content">
|
|
<!--begin::Container-->
|
|
<div class="container-fluid">
|
|
<!--begin::Row-->
|
|
<div class="row">
|
|
<!-- Start col -->
|
|
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
|
|
<div class="card p-0 mb-4" style="width:100%">
|
|
<div class="card-header pb-0 pt-1">
|
|
<h3 class="card-title">Home</h3>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body pb-1">
|
|
<div lass="img-overlay-wrap">
|
|
<svg viewBox="0 0 400 300" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
<defs>
|
|
<g id="floorBtn">
|
|
<path fill="transparent" stroke-width="4"
|
|
d="M 99.86386438745993 22.00011880076356 A 50 50 0 1 0 100 22" />
|
|
</g>
|
|
<g id="heater" filter="url(#shadow)" transform="translate(1.5,1),scale(0.2,0.2)" fill="#993311" stroke="#FF2211">
|
|
<path clip-path="M-22 2.24h42V22h-42z"
|
|
d="M16.543 8.028c-.023 1.503-.523 3.538-2.867 4.327.734-1.746.846-3.417.326-4.979-.695-2.097-3.014-3.735-4.557-4.627-.527-.306-1.203.074-1.193.683.02 1.112-.318 2.737-1.959 4.378C4.107 9.994 3 12.251 3 14.517 3 17.362 5 21 9 21c-4.041-4.041-1-7.483-1-7.483C8.846 19.431 12.988 21 15 21c1.711 0 5-1.25 5-6.448 0-3.133-1.332-5.511-2.385-6.899-.347-.458-1.064-.198-1.072.375" />
|
|
</g>
|
|
<g id="buffer" filter="url(#shadow)" transform="translate(18,1),scale(0.3,0.3)" fill="#ffffff">
|
|
<path d="M5 12.5a1.5 1.5 0 1 1-2-1.415V2.5a.5.5 0 0 1 1 0v8.585A1.5 1.5 0 0 1 5 12.5"/>
|
|
<path d="M1 2.5a2.5 2.5 0 0 1 5 0v7.55a3.5 3.5 0 1 1-5 0zM3.5 1A1.5 1.5 0 0 0 2 2.5v7.987l-.167.15a2.5 2.5 0 1 0 3.333 0L5 10.486V2.5A1.5 1.5 0 0 0 3.5 1m5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5m4.243 1.757a.5.5 0 0 1 0 .707l-.707.708a.5.5 0 1 1-.708-.708l.708-.707a.5.5 0 0 1 .707 0M8 5.5a.5.5 0 0 1 .5-.5 3 3 0 1 1 0 6 .5.5 0 0 1 0-1 2 2 0 0 0 0-4 .5.5 0 0 1-.5-.5M12.5 8a.5.5 0 0 1 .5-.5h1a.5.5 0 1 1 0 1h-1a.5.5 0 0 1-.5-.5m-1.172 2.828a.5.5 0 0 1 .708 0l.707.708a.5.5 0 0 1-.707.707l-.708-.707a.5.5 0 0 1 0-.708M8.5 12a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5"/>
|
|
</g>
|
|
</defs>
|
|
<g>
|
|
<g id="UGimg" <?php if($_GET["floor"] != "UG"){echo "opacity='0'";} ?> >
|
|
<animate begin="UG.click" attributetype="CSS" attributeName="opacity" to=1.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<animate begin="EG.click" attributetype="CSS" attributeName="opacity" to=0.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<animate begin="OG.click" attributetype="CSS" attributeName="opacity" to=0.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<image href="assets/img/UG.png" height="100%" width="100%" x=0
|
|
y=0 />
|
|
</g>
|
|
<g id="EGimg" <?php if($_GET["floor"] != "EG"){echo "opacity='0'";} ?> >
|
|
<animate begin="EG.click" attributetype="CSS" attributeName="opacity" to=1.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<animate begin="OG.click" attributetype="CSS" attributeName="opacity" to=0.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<animate begin="UG.click" attributetype="CSS" attributeName="opacity" to=0.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<image href="assets/img/EG.png" height="100%" width="100%" x=0
|
|
y=0 />
|
|
|
|
</g>
|
|
<g id="OGimg" <?php if($_GET["floor"] != "OG"){echo "opacity='0'";} ?> >
|
|
<animate begin="OG.click" attributetype="CSS" attributeName="opacity" to=1.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<animate begin="EG.click" attributetype="CSS" attributeName="opacity" to=0.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<animate begin="UG.click" attributetype="CSS" attributeName="opacity" to=0.0 dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<image href="assets/img/OG.png" border="1px solid white" height="100%" width="100%" x=0
|
|
y=0 />
|
|
</g>
|
|
<g id="OG" onclick="switchFloor('OG')" xcursor="pointer" transform="translate(0,0), scale(0.50,0.50)">
|
|
<animate id="showOG" begin=click attributetype="CSS" attributeName="opacity" from=0 to=1
|
|
dur="0.1s" repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1"
|
|
fill="freeze">
|
|
</animate>
|
|
<use href="#floorBtn" stroke="#aa7713" />
|
|
<text fill="#aa7713" style="font: 3em sans-serif;" transform="translate(62,90)">OG</text>
|
|
</g>
|
|
<g id="EG" onclick="switchFloor('EG')" cursor="pointer" transform="translate(0,100), scale(0.50,0.50)">
|
|
<animate id="showEG" begin=click attributetype="CSS" attributeName="opacity" from=0 to=1
|
|
dur="0.1s" repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1"
|
|
fill="freeze"></animate>
|
|
<use href="#floorBtn" stroke="#aa7713" />
|
|
<text fill="#aa7713" style="font: 3em sans-serif;" transform="translate(64,90)">EG</text>
|
|
</g>
|
|
<g id="UG" onclick="switchFloor('UG')" cursor="pointer" transform="translate(0,200), scale(0.50,0.50)">
|
|
<animate id="showUG" begin=click attributetype="CSS" attributeName="opacity" from=0 to=1
|
|
dur="0.1s" repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1"
|
|
fill="freeze"></animate>
|
|
<use href="#floorBtn" stroke="#aa7713" />
|
|
<text fill="#aa7713" style="font: 3em sans-serif;" transform="translate(63,90)">UG</text>
|
|
</g>
|
|
<?php foreach (floorsWithPlan() as $floor): ?>
|
|
<g id="<?= $floor ?>_Info" <?php if ($floor != "OG") echo 'opacity="0" '; ?><?php if($_GET["floor"] != $floor){echo "display='none'";} ?> >
|
|
<?php foreach (array_map("mitWerten", roomsOnFloor($floor)) as $room): ?>
|
|
<?php
|
|
/*
|
|
* Grundlinien je nach Anzahl der Werte. Die Kachel ist 19 hoch; mit
|
|
* drei Werten sind es dieselben Zeilen wie eh und je (klein, gross,
|
|
* klein), mit weniger ruecken sie in die Mitte.
|
|
*/
|
|
$zeilen = [1 => [12], 2 => [9, 16], 3 => [5, 11.5, 16.5]];
|
|
$y = $zeilen[count($room["werte"])] ?? [];
|
|
?>
|
|
<g transform="translate(<?= $room["x"] ?>,<?= $room["y"] ?>)" onclick="openHeaterSettings('<?= $floor ?>_<?= $room["mqtt"] ?>');" cursor="pointer">
|
|
<rect width="24" height="19" rx="5" fill="#0006" />
|
|
<?php if ($room["heizung"]): ?>
|
|
<use id="<?= $floor ?>_<?= $room["id"] ?>_heater" href="#heater" display="none" />
|
|
<use id="<?= $floor ?>_<?= $room["id"] ?>_buffer" href="#buffer" display="none" />
|
|
<?php endif; ?>
|
|
<text fill="#fff" style="font: 4px sans-serif;">
|
|
<?php if (!$room["werte"]): ?>
|
|
<!-- Kein Messwert: die Kachel traegt ihren Namen und
|
|
oeffnet den Raum. Ein leeres Rechteck saehe aus
|
|
wie ein Fehler. -->
|
|
<tspan x="12" y="11.5" font-size="1.2em" text-anchor="middle"><?= htmlspecialchars($room["mqtt"]) ?></tspan>
|
|
<?php endif; ?>
|
|
<?php foreach ($room["werte"] as $i => $wert): ?>
|
|
<!-- Der Platzhalter ist nur ein Strich: bevor der
|
|
erste Wert da ist, soll hier keine Einheit
|
|
stehen, die vielleicht gar nicht passt. -->
|
|
<tspan id="<?= $floor ?>_w<?= $i ?>_<?= $room["id"] ?>" x="12" y="<?= $y[$i] ?>"
|
|
<?= empty($wert["gross"]) ? "" : 'font-size="1.6em"' ?> text-anchor="middle">–</tspan>
|
|
<?php endforeach; ?>
|
|
</text>
|
|
</g>
|
|
<?php endforeach; ?>
|
|
</g>
|
|
<?php endforeach; ?>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /.card -->
|
|
</div>
|
|
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
|
|
<div class="card p-0 mb-4" style="width:100%">
|
|
<div class="card-header pb-0 pt-1">
|
|
<h3 class="card-title">Automatismen</h3>
|
|
<div class="card-tools">
|
|
<!-- Zuordnung Geraet -> Raum: sie ordnet die Geraeteliste im
|
|
Editor nach Raeumen statt nach Geraeteart. -->
|
|
<button type="button" class="btn btn-tool" title="Geräte den Räumen zuordnen"
|
|
onclick="openRoomModal()">
|
|
<i class="bi bi-house-gear"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
|
|
<i class="bi bi-x-lg"></i>
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="card-body pb-1">
|
|
<div class="card-header p-0 pt-1">
|
|
<ul class="nav nav-tabs" id="actions-tab" role="tablist">
|
|
<?php foreach ($floors as $etage): $offen = $_GET["floor"] == $etage; ?>
|
|
<li class="nav-item">
|
|
<a class="nav-link <?php if ($offen) echo "active"; ?>" id="actions-<?= $etage ?>-tab"
|
|
href="#actions-<?= $etage ?>" onclick="switchTab('actions-<?= $etage ?>')"
|
|
title="<?= htmlspecialchars(floorLabel($etage)) ?>"
|
|
aria-controls="actions-<?= $etage ?>" aria-selected="<?= $offen ? "true" : "false" ?>"><?= $etage ?></a>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
<div class="tab-content" id="actions-tabContent">
|
|
<?php foreach ($floors as $etage): ?>
|
|
<div class="tab-pane fade <?php if ($_GET["floor"] == $etage) echo "active show"; ?>"
|
|
id="actions-<?= $etage ?>" role="tabpanel" aria-labelledby="actions-<?= $etage ?>-tab">
|
|
<!-- Inhalt kommt aus ajax/AutoAction.php?action=list, gefuellt von
|
|
refreshAutomations() in js/solar/autoActionFuncs.js -->
|
|
<div id="actions-<?= $etage ?>-list">Wird geladen...</div>
|
|
<button type="button" class="btn btn-success" title="Neue Automatik"
|
|
onclick="openAutoActionModal('?action=editor&floor=<?= $etage ?>')"><i class="bi bi-plus-square"></i></button>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /.Start col -->
|
|
</div>
|
|
|
|
<!-- /.row (main row) -->
|
|
</div>
|
|
<!--end::Container-->
|
|
</div>
|
|
<!--end::App Content-->
|
|
</main>
|
|
<!--end::App Main-->
|
|
|
|
<script>
|
|
// Raumtabelle aus restricted/rooms.php - homeMQTT.js aktualisiert darueber
|
|
// die SVG-Kacheln, ohne die Raeume noch einmal aufzufuehren. In "werte"
|
|
// steht, welche Messwerte eine Kachel zeigt; homeTopics sind die Zweige,
|
|
// die der Browser dafuer beim Broker anmelden muss.
|
|
const homeRooms = <?= json_encode(roomsWithTile(), JSON_UNESCAPED_UNICODE) ?>;
|
|
const homeTopics = <?= json_encode(tileTopics(), JSON_UNESCAPED_UNICODE) ?>;
|
|
<?php if ($_GET["floor"] != "OG" && in_array($_GET["floor"], floorsWithPlan())): ?>
|
|
document.addEventListener('readystatechange', function () {
|
|
if (event.target.readyState === 'complete') {
|
|
switchFloor('<?= $_GET["floor"] ?>');
|
|
}
|
|
});
|
|
<?php endif; ?>
|
|
</script>
|