Fuer die Symbole vor den Zahlen standen drei Codepunkte von Hand in rooms.php, und die Maske bot genau diese drei an. Jedes weitere Symbol waere eine Codeaenderung gewesen. Die Codepunkte stehen aber schon im Haus: css/bootstrap-icons.min.css fuehrt sie unter .bi-<name>::before - dieselbe Quelle, aus der auch der Browser liest. bootstrapIcons() liest sie von dort (2078 Stueck, einmal je Anfrage), kachelIcon() schlaegt darin nach. Damit ist jedes Symbol moeglich, und ein Versionswechsel der Icons zieht von selbst mit. In der Maske steht deshalb kein Auswahlfeld mehr, sondern der Name mit einer Vorschau davor - "moisture" sagt nichts, das Symbol daneben schon. Die Vorschlagsliste beginnt mit gut dreissig ueblichen Symbolen unter deutscher Beschriftung (Temperatur, Feuchte, Leistung, Wallbox, Regen ...), danach folgt der ganze Rest alphabetisch. Ein Name, den es nicht gibt, zeigt sofort ein rotes Fragezeichen und wird beim Speichern mit Begruendung abgelehnt. In der eingeklappten Kurzfassung einer Kachel steht das Symbol jetzt mit - es ist das schnellste Erkennungszeichen dafuer, welcher der drei Werte gemeint ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
136 lines
5.0 KiB
PHP
136 lines
5.0 KiB
PHP
<?php
|
|
/**
|
|
* Endpunkt der Einstellungsseite.
|
|
*
|
|
* GET ?action=list&reihe=strom Zeitraeume einer Preistabelle
|
|
* POST ?action=save {"reihe": "strom", "zeilen": [ ... ]}
|
|
* GET ?action=kacheln Anzeigewerte aller Home-Kacheln
|
|
* POST ?action=kacheln-save {"floor": "OG", "room": "bad",
|
|
* "werte": [ ... ] oder null}
|
|
* GET ?action=geraete Geraete mit Raum, Verwendung und Alter
|
|
* POST ?action=geraete-raeume {"rooms": {"12": "OG/Bad", ...}}
|
|
* POST ?action=geraet-loeschen {"id": 12}
|
|
*
|
|
* Ein reiner Verteiler: geprueft und geschrieben wird in
|
|
* restricted/costs.php bzw. restricted/kacheln.php, damit Maske und Endpunkt
|
|
* dieselbe Wahrheit ueber einen Zeitraum oder eine Kachel haben.
|
|
*/
|
|
|
|
require_once("../helper.php");
|
|
require_once("../restricted/costs.php");
|
|
require_once("../restricted/kacheln.php");
|
|
require_once("../restricted/automations.php");
|
|
|
|
if (!checkLogin()) {
|
|
http_response_code(403);
|
|
exit;
|
|
}
|
|
|
|
function jsonAntwort($data, $code = 200)
|
|
{
|
|
http_response_code($code);
|
|
header("Content-Type: application/json; charset=utf-8");
|
|
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$action = $_GET["action"] ?? "list";
|
|
|
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|
$body = json_decode(file_get_contents("php://input"), true);
|
|
if (!is_array($body)) {
|
|
jsonAntwort(["error" => "Konnte die Anfrage nicht lesen."], 400);
|
|
}
|
|
try {
|
|
switch ($action) {
|
|
case "save":
|
|
$anzahl = preiseSpeichern($body["reihe"] ?? "", $body["zeilen"] ?? []);
|
|
// Die gespeicherte Fassung zurueckgeben: die Maske zeigt danach
|
|
// die abgeleiteten Enddaten, nicht die eingetippten.
|
|
jsonAntwort([
|
|
"gespeichert" => $anzahl,
|
|
"zeilen" => preiseLaden($body["reihe"]),
|
|
]);
|
|
|
|
case "kacheln-save":
|
|
/*
|
|
* Fehlt "werte" oder steht dort null, faellt die Kachel auf
|
|
* ihre Vorgabe zurueck - dann kommt diese zurueck, damit die
|
|
* Maske zeigt, was jetzt gilt, statt zu leeren Zeilen.
|
|
*/
|
|
$floor = $body["floor"] ?? "";
|
|
$room = $body["room"] ?? "";
|
|
$werte = array_key_exists("werte", $body) ? $body["werte"] : null;
|
|
$gespeichert = kachelWerteSpeichern($floor, $room, $werte);
|
|
|
|
$vorgabe = [];
|
|
foreach (allRooms() as $r) {
|
|
if ($r["floor"] === $floor && $r["id"] === $room) {
|
|
$vorgabe = kachelVorgabe($r);
|
|
}
|
|
}
|
|
jsonAntwort([
|
|
"eigen" => $gespeichert !== null,
|
|
"werte" => $gespeichert !== null ? $gespeichert : $vorgabe,
|
|
]);
|
|
case "geraete-raeume":
|
|
jsonAntwort(["gespeichert" => saveRooms($body["rooms"] ?? [])]);
|
|
|
|
case "geraet-loeschen":
|
|
/*
|
|
* Loeschen ist endgueltig - abgelehnt wird es, wenn eine
|
|
* Automatik auf das Geraet zeigt (siehe geraetLoeschen()).
|
|
* Zurueck kommt die neue Liste, damit die Maske nicht raten
|
|
* muss, was jetzt gilt.
|
|
*/
|
|
$name = geraetLoeschen($body["id"] ?? 0);
|
|
jsonAntwort(["geloescht" => $name, "geraete" => geraeteListe()]);
|
|
|
|
default:
|
|
jsonAntwort(["error" => "Unbekannte Aktion."], 400);
|
|
}
|
|
} catch (InvalidArgumentException $e) {
|
|
jsonAntwort(["error" => $e->getMessage()], 400);
|
|
} catch (Throwable $e) {
|
|
jsonAntwort(["error" => "Speichern fehlgeschlagen: " . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
if ($action === "kacheln") {
|
|
try {
|
|
jsonAntwort([
|
|
"kacheln" => kachelListe(),
|
|
// Alle Symbolnamen - die üblichen zuerst und mit Beschriftung.
|
|
"symbole" => kachelIconNamen(),
|
|
// Auswahlliste je Geraet - dieselbe Quelle wie im Automatik-Editor.
|
|
"katalog" => kachelKatalog(),
|
|
// Fuer die Eingabe von Hand, wenn etwas nicht im Katalog steht.
|
|
"topics" => kachelTopicVorschlaege(),
|
|
"tabelleDa" => kachelTabelleDa(),
|
|
"max" => KACHEL_MAX,
|
|
]);
|
|
} catch (Throwable $e) {
|
|
jsonAntwort(["error" => "Kacheln nicht abrufbar: " . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
if ($action === "geraete") {
|
|
try {
|
|
jsonAntwort(["geraete" => geraeteListe(), "raeume" => raumListe()]);
|
|
} catch (Throwable $e) {
|
|
jsonAntwort(["error" => "Geräte nicht abrufbar: " . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
if ($action === "list") {
|
|
try {
|
|
jsonAntwort(["zeilen" => preiseLaden($_GET["reihe"] ?? "")]);
|
|
} catch (InvalidArgumentException $e) {
|
|
jsonAntwort(["error" => $e->getMessage()], 400);
|
|
} catch (Throwable $e) {
|
|
jsonAntwort(["error" => "Laden fehlgeschlagen: " . $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
jsonAntwort(["error" => "Unbekannte Aktion."], 400);
|