Files
Smart-Dashboard/restricted/home.php
T
adminandClaude Opus 5 79a55aeafd Kacheln zeigen frei gewaehlte Messwerte, Heizung nur wo eine haengt
Drei Sachen an den Kacheln und am Raum-Modal.

Die Platzhalter sind nur noch Striche. Vorher stand dort "---- hPa",
gefuellt wurde die Zeile aber mit der Solltemperatur - eine Einheit, die
nie kam, an einer Stelle, die etwas anderes zeigt.

Der Heizungs-Reiter erscheint nur noch, wo auch ein Thermostat haengt.
Frueher entschied das "hat der Raum eine Kachel" - das stimmte, solange
nur Raeume mit Thermostat eine bekamen. Seit auch Kueche und Technik eine
haben, bekamen sie einen Regler fuer eine Heizung, die es dort nicht gibt.
Jetzt zaehlt, ob im Raum ein Geraet der Bedienform "heizung" steht.

Und eine Kachel zeigt jetzt ein bis drei frei gewaehlte Messwerte. In
restricted/rooms.php steht je Raum unter "werte", welche das sind: topic,
optional pfad (Schluessel in einer JSON-Nachricht), einheit, stellen. Ohne
Angabe bleibt es bei den drei Thermostatwerten samt Heizsymbol, also genau
wie bisher - nur diese Raeume zeichnen die Symbole ueberhaupt noch.

Alles kommt live ueber MQTT, auch die beiden Shelly-Zaehler: sie schicken
ihre sechzehn Werte als eine JSON-Nachricht auf Power_EG/status/em:0, aus
der "pfad" den gesuchten Schluessel greift. Der Browser meldet nicht mehr
fest "Raumtemp/#" an, sondern die Zweige, die die Raumtabelle nennt -
derzeit vier statt vierzig einzelner Topics.

Damit steht auf EG Technik die Leistung (gemessen 293 W), auf UG Technik
Leistung und Wasserzaehler (78 W, 400,4 m3). Raeume ohne Messwert tragen
ihren Namen und oeffnen den Raum; ein leeres Rechteck saehe aus wie ein
Fehler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 22:55:58 +02:00

235 lines
15 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 ($floors 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">&ndash;</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">
<li class="nav-item">
<a class="nav-link <?php if($_GET["floor"] == "OG") echo "active"; ?>" id="actions-OG-tab"href="#actions-OG" onclick="switchTab('actions-OG')" aria-controls="actions-OG" aria-selected="true">OG</a>
</li>
<li class="nav-item">
<a class="nav-link <?php if($_GET["floor"] == "EG") echo "active"; ?>" id="actions-EG-tab" href="#actions-EG" onclick="switchTab('actions-EG')" aria-controls="actions-EG" aria-selected="false">EG</a>
</li>
<li class="nav-item">
<a class="nav-link <?php if($_GET["floor"] == "UG") echo "active"; ?>" id="actions-UG-tab" href="#actions-UG" onclick="switchTab('actions-UG')" aria-controls="actions-UG" aria-selected="false">UG</a>
</li>
</ul>
</div>
<div class="tab-content" id="actions-tabContent">
<div class="tab-pane fade <?php if($_GET["floor"] == "OG") echo "active show"; ?>" id="actions-OG" role="tabpanel" aria-labelledby="actions-OG-tab">
<!-- Inhalt kommt aus ajax/AutoAction.php?action=list, gefuellt von
refreshAutomations() in js/solar/autoActionFuncs.js -->
<div id="actions-OG-list">Wird geladen...</div>
<button type="button" class="btn btn-success" title="Neue Automatik"
onclick="openAutoActionModal('?action=editor&floor=OG')"><i class="bi bi-plus-square"></i></button>
</div>
<div class="tab-pane fade <?php if($_GET["floor"] == "EG") echo "active show"; ?>" id="actions-EG" role="tabpanel" aria-labelledby="actions-EG-tab">
<!-- Inhalt kommt aus ajax/AutoAction.php?action=list, gefuellt von
refreshAutomations() in js/solar/autoActionFuncs.js -->
<div id="actions-EG-list">Wird geladen...</div>
<button type="button" class="btn btn-success" title="Neue Automatik"
onclick="openAutoActionModal('?action=editor&floor=EG')"><i class="bi bi-plus-square"></i></button>
</div>
<div class="tab-pane fade <?php if($_GET["floor"] == "UG") echo "active show"; ?>" id="actions-UG" role="tabpanel" aria-labelledby="actions-UG-tab">
<!-- Inhalt kommt aus ajax/AutoAction.php?action=list, gefuellt von
refreshAutomations() in js/solar/autoActionFuncs.js -->
<div id="actions-UG-list">Wird geladen...</div>
<button type="button" class="btn btn-success" title="Neue Automatik"
onclick="openAutoActionModal('?action=editor&floor=UG')"><i class="bi bi-plus-square"></i></button>
</div>
</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"], $floors)): ?>
document.addEventListener('readystatechange', function () {
if (event.target.readyState === 'complete') {
switchFloor('<?= $_GET["floor"] ?>');
}
});
<?php endif; ?>
</script>