Zusammenlegung
- Die sechs Endpunkte getConsData_/getProdData_{month,year,decade}.php sind
ajax/energyHistory.php?series=&range= gewichen, der gemeinsame Aufbau der
Chart-Daten steht in ajax/chartData.php. Die erzeugte SQL wurde gegen alle
sechs Originale abgeglichen.
- Die 13 Raeume der Home-Ansicht stehen nur noch in restricted/rooms.php.
home.php rendert die SVG-Kacheln daraus, homeMQTT.js bekommt dieselbe
Tabelle als JSON (346 -> 222 bzw. 428 -> 197 Zeilen).
- Gemeinsame JS-Helfer in js/solar/common.js; geteilte Bausteine fuer
Wetterkacheln, Chart-Skripte und das Formular der Ladesteuerung.
- Seitenvorlagen sind jetzt .php und werden eingebunden, statt per
file_get_contents zusammengesetzt zu werden.
Fehlerbehebungen
- ?action=weather fuehrte auf eine leere Seite, es gibt jetzt weather.php.
- homeMQTT.js griff auf ein #meteogram zu, das home.php nie hatte, und brach
damit den readystatechange-Handler ab.
- Die Heizungsseite abonnierte weatherStation/#, zeigte die Werte aber nie an.
Ladelast
- Chart.js, MQTT und Meteogramm werden nur noch auf den Seiten geladen, die
sie brauchen; die Seitentabelle dafuer steht in index.php.
- mqtt.js durch die minifizierte Fassung ersetzt (859 -> 359 KB). MQTT.js
5.14.1, byte-identisch mit dem signierten npm-Tarball.
- Rund 3,1 MB ungenutzte Vendor-Reste und AdminLTE-Demobilder entfernt.
- Solar 1796 -> 1240 KB, Home 1796 -> 873 KB, Historie 1796 -> 843 KB.
Speicher im Browser
- Chart.defaults.devicePixelRatio auf 1.5 gedeckelt. Die Zeichenpuffer der
Diagramme waren mit 20-45 MB der groesste Posten der Seite.
- Das Meteogramm-iframe laedt erst beim Hinscrollen.
Realtime-SVG
- Filterbereiche auf das noetige Mass verkleinert und den wirkungslosen
feOffset-Durchgang entfernt: 60 % weniger gerasterte Filterflaeche bei
pixelgleichem Ergebnis.
- Die Fluss-Animation laeuft ueber transform statt stroke-dashoffset, mit
Punkten statt Strichmuster; sie ruht, wo kein Fluss anliegt.
- Der Wertupdate schreibt nur noch bei echter Aenderung und benutzt
textContent statt innerHTML: 9 statt 86 Elemente pro Update, davon keines
mit Filter. describeArc rundet auf volle Grad.
- waterInfo auf scale(0.90) wie die uebrigen Info-Gruppen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
223 lines
16 KiB
PHP
223 lines
16 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 (roomsOnFloor($floor) as $room): ?>
|
|
<g transform="translate(<?= $room["x"] ?>,<?= $room["y"] ?>)" onclick="openHeaterSettings('<?= $floor ?>_<?= $room["mqtt"] ?>');" cursor="pointer">
|
|
<rect width="24" height="19" rx="5" fill="#0006" />
|
|
<use id="<?= $floor ?>_<?= $room["id"] ?>_heater" href="#heater" display="none" />
|
|
<use id="<?= $floor ?>_<?= $room["id"] ?>_buffer" href="#buffer" display="none" />
|
|
<text fill="#fff" style="font: 4px sans-serif;">
|
|
<tspan id="<?= $floor ?>_pres_<?= $room["id"] ?>" y="5" x="12" text-anchor="middle">---- hPa</tspan>
|
|
<tspan id="<?= $floor ?>_tmp_<?= $room["id"] ?>" x="12" dy="1em" font-size="1.6em" text-anchor="middle">--,- °C</tspan>
|
|
<tspan id="<?= $floor ?>_hum_<?= $room["id"] ?>" x="12" dy="1.2em" text-anchor="middle">-- %rF</tspan>
|
|
</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">
|
|
<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">
|
|
<table class="table table-hover">
|
|
<thead><tr><th>Name</th><th>Aktiv</th><th>Auslöser</th><th>Zeitfenster</th><th>Aktion</th><th>Wochentage</th><th></th></tr></thead>
|
|
<tbody>
|
|
<tr><td>Kinder zu</td>
|
|
<td><input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked></td>
|
|
<td>Uhrzeit: 16:30</td>
|
|
<td>n/a</td>
|
|
<td>Rollo "Magdalena Tür" schließen<br />Rollo "Magdalena Fenster" schließen</td>
|
|
<td>
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Mo.</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Di.</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Mi.</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Do.</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Fr.</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Sa.</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">So.</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Feiertag</label><br />
|
|
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault" disabled checked /> <label class="form-check-label" for="flexCheckDefault">Ferien</label><br />
|
|
</td>
|
|
<td>
|
|
<div class="tools">
|
|
<button type="button" class="btn btn-info btn-sm"><i class="bi bi-pause-fill"></i></button>
|
|
<button type="button" class="btn btn-warning btn-sm"><i class="bi bi-pencil-square"></i></button>
|
|
<button type="button" class="btn btn-danger btn-sm"><i class="bi bi-trash3"></i></button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<button type="button" class="btn btn-success" onclick="openAutoActionModal('?action=new&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">
|
|
<button class="btn btn-primary" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasWithBackdrop" aria-controls="offcanvasWithBackdrop">Enable backdrop (default)</button>
|
|
Mauris tincidunt mi at erat gravida, eget tristique urna bibendum. Mauris pharetra purus ut ligula tempor, et vulputate metus facilisis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas sollicitudin, nisi a luctus interdum, nisl ligula placerat mi, quis posuere purus ligula eu lectus. Donec nunc tellus, elementum sit amet ultricies at, posuere nec nunc. Nunc euismod pellentesque diam.
|
|
</div>
|
|
<div class="tab-pane fade <?php if($_GET["floor"] == "UG") echo "active show"; ?>" id="actions-UG" role="tabpanel" aria-labelledby="actions-UG-tab">
|
|
Morbi turpis dolor, vulputate vitae felis non, tincidunt congue mauris. Phasellus volutpat augue id mi placerat mollis. Vivamus faucibus eu massa eget condimentum. Fusce nec hendrerit sem, ac tristique nulla. Integer vestibulum orci odio. Cras nec augue ipsum. Suspendisse ut velit condimentum, mattis urna a, malesuada nunc. Curabitur eleifend facilisis velit finibus tristique. Nam vulputate, eros non luctus efficitur, ipsum odio volutpat massa, sit amet sollicitudin est libero sed ipsum. Nulla lacinia, ex vitae gravida fermentum, lectus ipsum gravida arcu, id fermentum metus arcu vel metus. Curabitur eget sem eu risus tincidunt eleifend ac ornare magna.
|
|
</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.
|
|
const homeRooms = <?= json_encode($rooms) ?>;
|
|
<?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>
|