Das Rendering des Grundstuecks fuellt seinen Rahmen aus, die drei Geschossbilder haben dagegen breiten schwarzen Rand - deshalb lagen die Etagenschalter nur beim Aussengelaende auf dem Bild. Der Rand gehoert ins Bild und nicht in home.php: dort einzurechnen haette auch OG, EG und UG verschoben, und deren Kacheln sind auf die heutige Lage eingemessen. AG.png bekommt links 114 durchsichtige Punkte, die sieben Kacheln sind mit derselben Abbildung umgerechnet. Die Schalter selbst waren ein duenner Ring in einem Amber (#aa7713), das in css/solar.css nirgends vorkommt - und alle vier sahen immer gleich aus, die aktive Etage war an ihnen nicht zu erkennen. Jetzt tragen sie dieselbe Scheibe wie die Kacheln im Grundriss, die aktive bekommt die Akzentfarbe der Schieberegler (#00788F), und switchFloor() zieht den Zustand mit. Dort classList und nicht die Helfer addClass/removeClass weiter oben: deren "el.className +=" wirkt auf SVG-Elementen nicht, className ist dort schreibgeschuetzt. Ausserdem sind sie kleiner. Mit halber Groesse beherrschten sie die linke Seite, und das Bild musste umso weiter einruecken, um sie freizuhalten - bei 0,38 statt 0,50 bleibt mehr Plan uebrig. Die Groesse steht als eine Zahl in home.php, Abstand und Bildrand folgen daraus. Die Beschriftung sitzt jetzt ueber text-anchor und dominant-baseline im Kreismittelpunkt. Vorher stand je Etage ein von Hand gesuchter Versatz im Markup (62, 63, 64), und jede neue Etage haette einen neuen gebraucht. Weggefallen: die Definition #floorBtn, die nur einmal benutzt wurde, und die Animationen showOG/showEG/showUG, die ein bereits sichtbares Element von 0 auf 1 blendeten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
254 lines
13 KiB
PHP
254 lines
13 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="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>
|
|
<?php
|
|
/*
|
|
* Ein Bild je Etage, dazu fuer jede Etage eine Blende.
|
|
* Ausgeschrieben waren das bei drei Etagen neun Zeilen
|
|
* und bei vier schon sechzehn - und jede neue Etage
|
|
* haette alle anderen mit angefasst.
|
|
*
|
|
* floorsWithPlan() liefert nur Etagen, deren Raeume
|
|
* Koordinaten haben. Eine Etage ohne angeklickte
|
|
* Kachelpositionen taucht hier also gar nicht erst auf.
|
|
*/
|
|
$etagen = floorsWithPlan();
|
|
?>
|
|
<?php /* Von unten nach oben gestapelt, wie bisher: waehrend der Blende
|
|
sind zwei Bilder gleichzeitig halb sichtbar, und dann soll das
|
|
obere Geschoss obenauf liegen. */ ?>
|
|
<?php foreach (array_reverse($etagen) as $etage): ?>
|
|
<g id="<?= $etage ?>img" <?php if ($_GET["floor"] != $etage) echo "opacity='0'"; ?> >
|
|
<?php foreach ($etagen as $andere): ?>
|
|
<animate begin="<?= $andere ?>.click" attributetype="CSS" attributeName="opacity"
|
|
to=<?= $andere === $etage ? "1.0" : "0.0" ?> dur="0.7s"
|
|
repeatCount="1" calcMode="spline" keyTimes="0;1" keySplines=".1,0,.27,1" fill="freeze" />
|
|
<?php endforeach; ?>
|
|
<image href="assets/img/<?= $etage ?>.png" height="100%" width="100%" x=0 y=0 />
|
|
</g>
|
|
<?php endforeach; ?>
|
|
<?php
|
|
/*
|
|
* Groesse der Etagenschalter. Der Kreis ist im eigenen
|
|
* Koordinatensystem 88 Einheiten breit und reicht von 28
|
|
* bis 116 unter die Oberkante seiner Gruppe; alles andere
|
|
* folgt daraus. Sie waren einmal doppelt so gross und
|
|
* beherrschten damit die linke Seite - und das Bild des
|
|
* Aussengelaendes musste umso weiter einruecken, um sie
|
|
* freizuhalten.
|
|
*/
|
|
$mass = 0.38;
|
|
$kreisUnten = 116 * $mass;
|
|
|
|
/*
|
|
* Bei drei Etagen bleibt es bei den gewohnten 100
|
|
* Einheiten Abstand; ab der vierten ruecken sie zusammen,
|
|
* sonst faellt die unterste aus der 300 hohen viewBox.
|
|
*/
|
|
$abstand = count($etagen) > 1
|
|
? min(100, (295 - $kreisUnten) / (count($etagen) - 1))
|
|
: 0;
|
|
?>
|
|
<?php foreach ($etagen as $i => $etage): ?>
|
|
<g id="<?= $etage ?>" class="floorBtn<?= $_GET["floor"] == $etage ? " active" : "" ?>"
|
|
onclick="switchFloor('<?= $etage ?>')"
|
|
transform="translate(0,<?= round($i * $abstand, 1) ?>), scale(<?= $mass ?>)">
|
|
<!-- Die Beschriftung sitzt ueber text-anchor und
|
|
dominant-baseline im Kreismittelpunkt. Vorher stand
|
|
je Etage ein von Hand gesuchter Versatz im Markup
|
|
(62, 63, 64), und eine neue Etage brauchte einen
|
|
neuen. Aussehen und Zustand stehen in css/solar.css. -->
|
|
<circle cx="50" cy="72" r="44" />
|
|
<text x="50" y="72" text-anchor="middle"
|
|
dominant-baseline="central"><?= $etage ?></text>
|
|
</g>
|
|
<?php endforeach; ?>
|
|
<?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"]): ?>
|
|
<?php
|
|
/*
|
|
* Kein Messwert: die Kachel traegt ihren Namen und oeffnet den Raum.
|
|
* Ein leeres Rechteck saehe aus wie ein Fehler.
|
|
*
|
|
* Lange Namen brechen am letzten Leerzeichen um. Die Kachel ist 24
|
|
* Einheiten breit, und "Terrasse OG" stand einzeilig links und rechts
|
|
* darueber hinaus. Aufgefallen ist das erst mit dem Aussengelaende:
|
|
* dort besteht jeder Raumname aus zwei Woertern.
|
|
*/
|
|
$luecke = strrpos($room["mqtt"], " ");
|
|
$teile = (strlen($room["mqtt"]) > 8 && $luecke !== false)
|
|
? [substr($room["mqtt"], 0, $luecke), substr($room["mqtt"], $luecke + 1)]
|
|
: [$room["mqtt"]];
|
|
$namenszeilen = count($teile) === 2 ? [9, 15.5] : [11.5];
|
|
?>
|
|
<?php foreach ($teile as $i => $teil): ?>
|
|
<tspan x="12" y="<?= $namenszeilen[$i] ?>" font-size="1.2em" text-anchor="middle"><?= htmlspecialchars($teil) ?></tspan>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
<?php foreach ($room["werte"] as $i => $wert): ?>
|
|
<?php $zeichen = kachelIcon($wert["icon"] ?? ""); ?>
|
|
<?php if ($zeichen !== ""): ?>
|
|
<!-- Ein Symbol vor der Zahl. "0 W" allein sagt nicht,
|
|
ob geladen, erzeugt oder verbraucht wird; die
|
|
Einheit beantwortet das nicht. Die Zahl rueckt
|
|
dafuer nach rechts, damit Symbol und Wert
|
|
zusammen mittig stehen. -->
|
|
<tspan x="4" y="<?= $y[$i] ?>" text-anchor="middle" opacity=".8"
|
|
style="font-family:bootstrap-icons"><?= $zeichen ?></tspan>
|
|
<?php endif; ?>
|
|
<!-- 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="<?= $zeichen !== "" ? 15 : 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>
|