Einstellungsseite, Skoda-Seite und Karte

Sammelstand der offenen Arbeit im Dashboard.

Einstellungen: eine Eingabemaske fuer die Energiepreise unter
restricted/settings.php, dazu costs.php als Modell und ajax/settings.php
als Endpunkt. Die Preise stehen jetzt in gridCosts, gasCosts und fuelCosts
statt fest im Quelltext von getStats.php; die Jahresstatistik rechnet ueber
LEAD() den jeweils gueltigen Zeitraum aus und schlaegt den anteiligen
Grundpreis auf. Eingegeben wird in gewohnten Einheiten - l/100 km,
kWh/100 km, Euro je Liter -, umgerechnet wird beim Speichern.
solarLog_costs.sql beschreibt die Umstellung der Tabellen.

Skoda: eigene Seite mit Live-Werten ueber MQTT und Historie ueber
ajax/skoda.php, Kommandos ueber ajax/skodaCmd.php.

Karte: Leaflet mit einem eigenen Kachel-Zwischenspeicher (ajax/tile.php),
dessen Ablage unter tiles/ nicht ins Repository gehoert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 21:21:27 +02:00
co-authored by Claude Opus 5
parent c9a9e16b21
commit d285ec27a0
26 changed files with 4862 additions and 190 deletions
+109
View File
@@ -0,0 +1,109 @@
<?php
/**
* Einstellungsseite: Parameter, die von Hand gepflegt werden.
*
* Bisher sind das die Preistabellen aus restricted/costs.php. Die Seite ist
* als Sammlung von Karten angelegt - eine Karte je Thema. Kommt spaeter ein
* weiterer Parameter dazu, kommt er als eigene Karte hinzu, ohne dass die
* vorhandenen etwas davon merken.
*
* 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");
$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; ?>
<?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; ?>
</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>