Files
Smart-Dashboard/restricted/settings.php
T
adminandClaude Opus 5 07bca5104e Einstellungsseite: Reiter, Kachelwerte und Geraeteliste
Die Einstellungen bestanden aus einer einzigen langen Spalte mit den
Preistabellen. Sie bekommt Reiter, und darin zwei neue Flaechen: was die
Kacheln der Startseite anzeigen, und welche Geraete es ueberhaupt gibt.

Reiter gab es vorher dreimal in drei Ausfuehrungen - die Etagen ueber den
Automatismen mit eigenem switchTab(), die Geraetegruppen im Raum-Modal mit
eigener CSS-Klasse, und die Einstellungsseite noch einmal anders. Jetzt baut
restricted/reiter.php alle drei; umgeschaltet wird von Bootstrap, den offenen
Reiter merkt reiterMerken() in der Adresse.

Kachelwerte: was auf einer Kachel steht, stand fest in rooms.php. Die neue
Tabelle kachelWerte (solarLog_kacheln.sql) ueberschreibt das je Raum, die
Maske waehlt die Messwerte aus demselben Katalog wie der Automatik-Editor -
nach Etage sortiert, die Geraete des eigenen Raums oben. Fehlt die Tabelle,
zeigt jede Kachel weiter ihre Vorgabe.

Geraete: die Zuordnung Geraet -> Raum war ein Modal, erreichbar ueber ein
Zahnrad neben den Automatismen. Sie ist eine Einstellung und steht jetzt dort,
zusammen mit dem, was fehlte: seit wann ein Geraet nichts mehr meldet, in
welchen Automatiken es steckt, und ein Loeschknopf. Letzterer ist noetig, weil
der Suchlauf nichts loescht - er legt an und frischt auf, damit die Automatiken
ihre Zustands-Ids behalten; ein abgebautes Geraet bliebe sonst fuer immer
stehen. Geloescht wird nur, was in keiner Automatik vorkommt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 22:45:24 +02:00

210 lines
9.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Einstellungsseite: Parameter, die von Hand gepflegt werden.
*
* Gegliedert in Reiter, einer je Thema - Preise und die Anzeigewerte der
* Home-Kacheln. Die Liste $reiter unten ist die einzige Stelle, die davon
* weiss: ein neues Thema ist ein Eintrag dort plus der Inhalt, alles Uebrige
* (Navigation, Umschalten, Merken des Reiters in der Adresse) ergibt sich
* daraus. Vorher stand jedes Thema als Karte untereinander; bei drei Themen
* waere die Seite ein Scrollband geworden.
*
* Innerhalb eines Reiters bleibt es bei Karten - eine je Tabelle bzw. je
* Etage.
*
* Die Zeilen zeichnet js/solar/settings.js aus dem Modell, das hier
* eingebettet wird - dieselbe Arbeitsteilung wie im Automatik-Editor. Sonst
* gaebe es die erste Zeile als PHP und jede weitere als JavaScript, und die
* beiden Fassungen liefen frueher oder spaeter auseinander. Aus demselben
* Grund kommen die Spaltenbreiten aus preisSpalten(): die Kopfzeile hier und
* die Felder dort muessen dieselben sein.
*/
require_once(__DIR__ . "/costs.php");
require_once(__DIR__ . "/kacheln.php");
require_once(__DIR__ . "/reiter.php");
/*
* Die Reiter. Reihenfolge ist Anzeigereihenfolge; der erste ist der offene,
* solange die Adresse nichts anderes sagt (#kacheln).
*/
$reiter = [
["id" => "preise", "titel" => "Preise", "symbol" => "bi-currency-euro"],
["id" => "kacheln", "titel" => "Home-Kacheln", "symbol" => "bi-grid-1x2"],
["id" => "geraete", "titel" => "Geräte", "symbol" => "bi-house-gear"],
];
// "merken": der offene Reiter steht in der Adresse und uebersteht ein
// Neuladen - siehe reiterMerken() in js/solar/common.js.
$reiterOpt = ["name" => "einstellung", "merken" => true, "klasse" => "mb-3"];
$reiterOpt["aktiv"] = reiterAktiv($reiter, $reiterOpt);
$reihen = preisReihen();
$daten = [];
$fehler = "";
try {
foreach ($reihen as $key => $reihe) {
$daten[$key] = [
"felder" => $reihe["felder"],
"spalten" => preisSpalten($reihe["felder"]),
"zeilen" => preiseLaden($key),
];
}
} catch (Throwable $e) {
$fehler = $e->getMessage();
}
?>
<!--begin::App Main-->
<main class="app-main">
<!--begin::App Content Header-->
<div class="app-content-header">
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<h3 class="mb-0">Einstellungen</h3>
</div>
</div>
</div>
</div>
<!--end::App Content Header-->
<!--begin::App Content-->
<div class="app-content">
<div class="container-fluid">
<?php if ($fehler): ?>
<div class="alert alert-danger">Die Einstellungen sind nicht erreichbar: <?= htmlspecialchars($fehler) ?></div>
<?php endif; ?>
<!-- Reiter. Welcher offen ist, merkt sich die Adresse (#kacheln) -
ein Neuladen nach dem Speichern landet damit wieder dort. -->
<?= reiterLeiste($reiter, $reiterOpt) ?>
<div class="tab-content">
<?= reiterFlaecheAuf("preise", $reiterOpt) ?>
<?php foreach ($reihen as $key => $reihe): ?>
<?php $breiten = preisSpalten($reihe["felder"]); ?>
<div class="row">
<div class="col-12 col-xxl-9">
<div class="card mb-4 preis-karte" data-reihe="<?= htmlspecialchars($key) ?>">
<div class="card-header pb-2 pt-2">
<h3 class="card-title"><i class="bi <?= $reihe["symbol"] ?> me-2"></i><?= htmlspecialchars($reihe["titel"]) ?></h3>
<div class="card-tools">
<button type="button" class="btn btn-tool preis-neu" title="Zeitraum hinzufügen">
<i class="bi bi-plus-lg"></i>
</button>
<button type="button" class="btn btn-tool preis-zurueck" title="Änderungen verwerfen">
<i class="bi bi-arrow-counterclockwise"></i>
</button>
</div>
</div>
<div class="card-body pb-2">
<p class="text-secondary small mb-3"><?= htmlspecialchars($reihe["hinweis"]) ?></p>
<!-- Spaltenköpfe nur auf breiten Schirmen; auf dem Handy trägt
jedes Feld seine eigene Beschriftung. -->
<div class="row g-2 d-none d-xl-flex preis-kopf text-secondary small fw-semibold">
<div class="col-xl-<?= $breiten["ab"] ?>">Gültig ab</div>
<div class="col-xl-<?= $breiten["bis"] ?>">Gültig bis</div>
<?php foreach (array_values($reihe["felder"]) as $i => $feld): ?>
<div class="col-xl-<?= $breiten["felder"][$i] ?>"><?= htmlspecialchars($feld["titel"]) ?></div>
<?php endforeach; ?>
<div class="col-xl-<?= $breiten["aktion"] ?>"></div>
</div>
<div class="preis-zeilen"></div>
<button type="button" class="btn btn-sm btn-outline-secondary mt-3 preis-neu">
<i class="bi bi-plus-lg me-1"></i>Zeitraum hinzufügen
</button>
</div>
<div class="card-footer d-flex align-items-center gap-3">
<button type="button" class="btn btn-primary preis-speichern">Speichern</button>
<span class="preis-hinweis small"></span>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?= reiterFlaecheZu() ?><!-- /Preise -->
<!-- ================================================ Home-Kacheln ==== -->
<!-- Der Inhalt kommt aus js/solar/settings.js: eine Karte je Etage, darin
je Raum eine Zeile mit ihren Werten. Gezeichnet wird aus dem Modell,
das ajax/settings.php?action=kacheln liefert - dieselbe Arbeits-
teilung wie bei den Preisen. -->
<?= reiterFlaecheAuf("kacheln", $reiterOpt) ?>
<div class="row">
<div class="col-12 col-xxl-9">
<div class="card mb-4" id="kachelKarte">
<div class="card-header pb-2 pt-2">
<h3 class="card-title"><i class="bi bi-grid-1x2 me-2"></i>Werte auf den Kacheln</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" id="kachelNeuLaden"
title="Neu laden">
<i class="bi bi-arrow-counterclockwise"></i>
</button>
</div>
</div>
<div class="card-body">
<p class="text-secondary small mb-3">
Jede Kachel im Grundriss zeigt bis zu drei Messwerte. Ohne eigene
Angabe sind das die Werte aus <code>restricted/rooms.php</code>
bei Räumen mit Thermostat Soll, Ist und Feuchte. Was hier gesetzt
wird, gilt stattdessen; „Standard“ nimmt es wieder zurück.
</p>
<div id="kachelListe">
<p class="text-secondary small mb-0">Wird geladen …</p>
</div>
</div>
</div>
</div>
</div>
<?= reiterFlaecheZu() ?><!-- /Home-Kacheln -->
<!-- ===================================================== Geräte ==== -->
<!-- Die Zuordnung Gerät -> Raum lag vorher als Modal auf der Home-Seite.
Sie gehört hierher: sie wird selten geändert, sie ist eine
Einstellung, und hier ist Platz für das, was daneben gehört - wie
lange ein Gerät nichts mehr gemeldet hat, in welchen Automatiken es
steckt, und der Weg, eine Karteileiche loszuwerden. -->
<?= reiterFlaecheAuf("geraete", $reiterOpt) ?>
<div class="row">
<div class="col-12 col-xxl-10">
<div class="card mb-4" id="geraeteKarte">
<div class="card-header pb-2 pt-2">
<h3 class="card-title"><i class="bi bi-house-gear me-2"></i>Geräte und Räume</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" id="geraeteNeuLaden" title="Neu laden">
<i class="bi bi-arrow-counterclockwise"></i>
</button>
</div>
</div>
<div class="card-body">
<p class="text-secondary small mb-3">
Der Raum ordnet die Geräteliste im Automatik-Editor und bestimmt,
welche Messwerte bei einer Home-Kachel oben stehen. Er wird von Hand
vergeben und bleibt bei einem neuen Suchlauf erhalten der Suchlauf
legt nur an und aktualisiert, er löscht nichts. Abgebaute Geräte
bleiben deshalb stehen, bis sie hier entfernt werden.
</p>
<div id="geraeteListe">
<p class="text-secondary small mb-0">Wird geladen …</p>
</div>
</div>
</div>
</div>
</div>
<?= reiterFlaecheZu() ?><!-- /Geräte -->
</div><!-- /tab-content -->
</div>
</div>
<!--end::App Content-->
</main>
<!--end::App Main-->
<script>
// Ausgangsstand der Preistabellen. Werte in der Anzeigeeinheit (ct statt
// Euro); umgerechnet wird in restricted/costs.php - an genau einer Stelle.
const preisDaten = <?= json_encode($daten, JSON_UNESCAPED_UNICODE) ?>;
// Aelteste Messung in EnergyFlow. Beginnt eine Preisreihe spaeter, fehlt
// den Werten davor der Preis - dann warnt die Maske.
const ersteMessung = <?= json_encode(ersteMessung()) ?>;
</script>