Anzeige fuer den Pi ist jetzt die Solar-Seite im Anzeige-Modus
Statt einer eigenen Vorlage mit nachgebauter Verdrahtung: ?action=anzeige ist der Eintrag solar mit "anzeige" => true. header.php laesst Kopfleiste und Menue weg und setzt die schmale Leiste (neu: Diagramme), footer.php den Fuss; die Realtime-Karte fuellt einen Bildschirm, Diagramme folgen darunter. Damit gehen am Display auch die Bedienfenster (Heizstab, Wallboxen, Bewaesserung) und die Diagramme - alles aus solarMQTT.js. anzeige.js bleibt fuer Leerlauf, Verbindungspunkt, Uhr und Neuladen; restricted/anzeige.php, "eigen" in index.php und klickFilter entfallen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -192,15 +192,20 @@ genau die Topics, die auf den Kacheln stehen (`tileTopics()` in `rooms.php`).
|
|||||||
| `settings` | `settings.php` | `settings.js`, `grundriss.js`, `energieflussKatalog.js`, `energiefluss.js`, `energieflussEinstellungen.js` | `settings.php?action=…` | — |
|
| `settings` | `settings.php` | `settings.js`, `grundriss.js`, `energieflussKatalog.js`, `energiefluss.js`, `energieflussEinstellungen.js` | `settings.php?action=…` | — |
|
||||||
| `logs` | `logs.php` | `logs.js` | `logs.php?action=…` | — |
|
| `logs` | `logs.php` | `logs.js` | `logs.php?action=…` | — |
|
||||||
| `einfuehrung` | `einfuehrung.php` | `einfuehrung.js` | nichts — Folien und Bilder stehen in der Seite | — |
|
| `einfuehrung` | `einfuehrung.php` | `einfuehrung.js` | nichts — Folien und Bilder stehen in der Seite | — |
|
||||||
| `anzeige` | `anzeige.php` (ganze Seite, ohne `header.php`/`footer.php`) | `anlageKatalog.js`, `anlage.js`, `speicher.js`, `energieflussKatalog.js`, `energiefluss.js`, `bewaesserungAnzeige.js`, `anzeige.js` | `speicher` | `solarManager/#`, `Gartenwasser/#` |
|
| `anzeige` | `solar.php` im Anzeige-Modus | wie `solar`, dazu `anzeige.js` | wie `solar` | wie `solar` |
|
||||||
|
|
||||||
**Vollbildanzeige** (`?action=anzeige`) — Übersicht, Anlage und Speicher für ein
|
**Anzeige-Modus** (`?action=anzeige`) — die Solar-Seite für ein festes Display,
|
||||||
festes Display, gebaut für den Raspberry Pi 3B+ mit 800 × 480 im Kioskmodus:
|
gebaut für den Raspberry Pi 3B+ mit 800 × 480 im Kioskmodus. In `index.php` eine
|
||||||
schmale Leiste links, immer quer, nach zwei Minuten ohne Berührung zurück zur
|
Kopie des Eintrags `solar` mit `"anzeige" => true`: `header.php` lässt Kopfleiste
|
||||||
Übersicht, MQTT verbindet still neu (Punkt unten: grün/gelb/rot), alle sechs
|
und Menü weg und setzt eine schmale Leiste (Übersicht, Anlage, Speicher,
|
||||||
Stunden lädt die Seite neu. Ohne weitere Parameter läuft sie „ruhig“ — nur die
|
Diagramme), `footer.php` den Fuß; die Realtime-Karte füllt einen Bildschirm,
|
||||||
Flüsse bewegen sich, in groben Stufen — und ohne Mauszeiger;
|
die Übersicht bleibt quer. Bedienfenster, Diagramme und Bewässerung kommen
|
||||||
`&animation=1` schaltet alle Animationen ein, `&maus=1` zeigt den Zeiger.
|
unverändert aus `solarMQTT.js`. `anzeige.js` ergänzt nur: zurück zur Übersicht
|
||||||
|
nach zwei Minuten ohne Berührung (offene Fenster schließen), Verbindungspunkt
|
||||||
|
(grün/gelb/rot) und Uhr in der Leiste, Neuladen alle sechs Stunden. Ohne
|
||||||
|
weitere Parameter läuft sie „ruhig“ — nur die Flüsse bewegen sich, in groben
|
||||||
|
Stufen — und ohne Mauszeiger; `&animation=1` schaltet alle Animationen ein,
|
||||||
|
`&maus=1` zeigt den Zeiger.
|
||||||
|
|
||||||
Und dasselbe als Abhängigkeitsbild — von der Seite bis in den Speicher:
|
Und dasselbe als Abhängigkeitsbild — von der Seite bis in den Speicher:
|
||||||
|
|
||||||
|
|||||||
+35
-21
@@ -3869,22 +3869,19 @@ svg.anl-plan.anl-hoch { max-height: none; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
Vollbildanzeige (restricted/anzeige.php, js/solar/anzeige.js), z.B. am
|
Anzeige-Modus (index.php?action=anzeige, js/solar/anzeige.js): die
|
||||||
Raspberry Pi mit 800 x 480: schmale Leiste links, Ansicht auf voller Höhe.
|
Solar-Seite auf einem festen Display, z.B. Raspberry Pi mit 800 x 480.
|
||||||
|
Leiste links; die Realtime-Karte füllt genau einen Bildschirm, die
|
||||||
|
Diagramme folgen darunter und rasten beim Wischen karteweise ein.
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
body.anzeige {
|
html:has(body.anzeige) { scroll-snap-type: y proximity; scrollbar-width: none; }
|
||||||
margin: 0; height: 100vh; overflow: hidden;
|
html:has(body.anzeige)::-webkit-scrollbar { display: none; }
|
||||||
display: grid; grid-template-columns: 3.2rem minmax(0, 1fr);
|
body.anzeige { -webkit-tap-highlight-color: transparent; }
|
||||||
background: var(--ton-grund, #191d20); color: var(--ton-schrift, #e8ede9);
|
|
||||||
font-family: "Poppins", system-ui, sans-serif; font-size: 14px;
|
|
||||||
-webkit-tap-highlight-color: transparent; user-select: none;
|
|
||||||
}
|
|
||||||
body.anzeige-ohne-maus,
|
body.anzeige-ohne-maus,
|
||||||
body.anzeige-ohne-maus * { cursor: none !important; }
|
body.anzeige-ohne-maus * { cursor: none !important; }
|
||||||
/* Die Seite lädt AdminLTE nicht - dessen Grundregeln fehlen hier. */
|
|
||||||
body.anzeige [hidden] { display: none !important; }
|
|
||||||
body.anzeige *, body.anzeige *::before, body.anzeige *::after { box-sizing: border-box; }
|
|
||||||
.anzeige .anzeige-leiste {
|
.anzeige .anzeige-leiste {
|
||||||
|
position: fixed; left: 0; top: 0; bottom: 0; width: 3.2rem; z-index: 1030;
|
||||||
display: flex; flex-direction: column; align-items: center; gap: .4rem; padding: .5rem 0;
|
display: flex; flex-direction: column; align-items: center; gap: .4rem; padding: .5rem 0;
|
||||||
background: var(--ton-flaeche, #23282c); border-right: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
background: var(--ton-flaeche, #23282c); border-right: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
|
||||||
}
|
}
|
||||||
@@ -3898,14 +3895,26 @@ body.anzeige *, body.anzeige *::before, body.anzeige *::after { box-sizing: bord
|
|||||||
.anzeige .anzeige-punkt.warn { background: #e8a33a; }
|
.anzeige .anzeige-punkt.warn { background: #e8a33a; }
|
||||||
.anzeige .anzeige-punkt.gut { background: #6cbe58; }
|
.anzeige .anzeige-punkt.gut { background: #6cbe58; }
|
||||||
.anzeige .anzeige-uhr { font-size: .72rem; font-weight: 600; color: var(--ton-schrift-2, #9aa6ad); font-variant-numeric: tabular-nums; }
|
.anzeige .anzeige-uhr { font-size: .72rem; font-weight: 600; color: var(--ton-schrift-2, #9aa6ad); font-variant-numeric: tabular-nums; }
|
||||||
.anzeige .anzeige-inhalt { min-width: 0; height: 100vh; }
|
|
||||||
.anzeige .anzeige-flaeche { box-sizing: border-box; height: 100vh; padding: .35rem; overflow-y: auto; scrollbar-width: none; }
|
.anzeige .app-wrapper { display: block; }
|
||||||
.anzeige .anzeige-flaeche::-webkit-scrollbar { display: none; }
|
.anzeige .app-main { margin-left: 3.2rem; }
|
||||||
.anzeige .anzeige-flaeche[hidden] { display: none; }
|
.anzeige .app-content-header { display: none; }
|
||||||
.anzeige #anzeige-fluss { overflow: hidden; padding: .2rem; }
|
.anzeige .app-content { padding: 0 .35rem; }
|
||||||
.anzeige #anzeige-fluss svg.enf { width: 100%; height: 100%; max-height: none; }
|
.anzeige .app-content .container-fluid { padding: 0; }
|
||||||
/* Anlage und Speicher: die Umbrüche der Solar-Seite richten sich nach der
|
.anzeige .app-content .row { --bs-gutter-x: 0; }
|
||||||
Fensterbreite und würden am 800er-Display zu früh greifen. */
|
.anzeige .app-content .card { scroll-snap-align: start; }
|
||||||
|
|
||||||
|
/* Realtime-Karte: ein ganzer Bildschirm, ohne Kopf - die Leiste ersetzt ihn. */
|
||||||
|
.anzeige .card#energieflussKarte { height: 100vh; margin-bottom: .5rem !important; border: 0; border-radius: 0; background: transparent; }
|
||||||
|
.anzeige .card#energieflussKarte .card-header { display: none !important; }
|
||||||
|
.anzeige .card#energieflussKarte .card-body { height: 100%; padding: .2rem 0 !important; }
|
||||||
|
.anzeige #energiefluss { height: 100%; overflow: hidden; }
|
||||||
|
.anzeige #energiefluss svg.enf { width: 100%; height: 100%; max-height: none; }
|
||||||
|
.anzeige #anlage,
|
||||||
|
.anzeige #speicher { height: 100%; overflow-y: auto; scrollbar-width: none; }
|
||||||
|
|
||||||
|
/* Die Umbrüche von Anlage und Speicher richten sich nach der Fensterbreite
|
||||||
|
und würden am 800er-Display zu früh greifen. */
|
||||||
.anzeige .anl svg.anl-plan { max-height: calc(100vh - 7.5rem); }
|
.anzeige .anl svg.anl-plan { max-height: calc(100vh - 7.5rem); }
|
||||||
.anzeige .spk .spk-kennzahlen { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .35rem; }
|
.anzeige .spk .spk-kennzahlen { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .35rem; }
|
||||||
.anzeige .spk .spk-kachel { padding: .4rem .55rem .45rem; }
|
.anzeige .spk .spk-kachel { padding: .4rem .55rem .45rem; }
|
||||||
@@ -3921,6 +3930,12 @@ body.anzeige *, body.anzeige *::before, body.anzeige *::after { box-sizing: bord
|
|||||||
.anzeige .spk .spk-werte small { font-size: .62rem; }
|
.anzeige .spk .spk-werte small { font-size: .62rem; }
|
||||||
.anzeige .spk .spk-gross b { font-size: 1.4rem; }
|
.anzeige .spk .spk-gross b { font-size: 1.4rem; }
|
||||||
|
|
||||||
|
/* Bedienfenster: passen auf 480 px Höhe und lassen sich mit dem Finger bedienen. */
|
||||||
|
.anzeige .modal-dialog { margin: .4rem auto; }
|
||||||
|
.anzeige .modal-body { max-height: calc(100vh - 9rem); overflow-y: auto; }
|
||||||
|
.anzeige .modal .form-range { height: 2rem; }
|
||||||
|
.anzeige .modal .btn { min-height: 2.4rem; }
|
||||||
|
|
||||||
/* Ruhig (Vorgabe am Pi): alles Zierwerk aus, nur die Flüsse laufen - in
|
/* Ruhig (Vorgabe am Pi): alles Zierwerk aus, nur die Flüsse laufen - in
|
||||||
groben Stufen, damit ein Raspberry Pi 3 nicht dauernd neu zeichnet. */
|
groben Stufen, damit ein Raspberry Pi 3 nicht dauernd neu zeichnet. */
|
||||||
.anzeige-ruhig *,
|
.anzeige-ruhig *,
|
||||||
@@ -3932,7 +3947,6 @@ body.anzeige *, body.anzeige *::before, body.anzeige *::after { box-sizing: bord
|
|||||||
.anzeige-ruhig .enf .enf-rueck .enf-strom { animation-direction: reverse !important; }
|
.anzeige-ruhig .enf .enf-rueck .enf-strom { animation-direction: reverse !important; }
|
||||||
.anzeige-ruhig .enf .enf-still .enf-strom,
|
.anzeige-ruhig .enf .enf-still .enf-strom,
|
||||||
.anzeige-ruhig .enf.enf-pause .enf-strom { animation: none !important; }
|
.anzeige-ruhig .enf.enf-pause .enf-strom { animation: none !important; }
|
||||||
.anzeige-ruhig .enf .enf-strahlen,
|
|
||||||
.anzeige-ruhig .enf .enf-koerper { transition: none; }
|
.anzeige-ruhig .enf .enf-koerper { transition: none; }
|
||||||
|
|
||||||
/* --- Karte: Umschalter Übersicht / Anlage -------------------------------- */
|
/* --- Karte: Umschalter Übersicht / Anlage -------------------------------- */
|
||||||
|
|||||||
@@ -120,13 +120,13 @@ if (isset($_GET["addUser"])) {
|
|||||||
"scripts" => ["js/solar/logs.js"]],
|
"scripts" => ["js/solar/logs.js"]],
|
||||||
"einfuehrung" => ["template" => "einfuehrung.php", "mqtt" => false, "charts" => false, "meteogram" => false, "leaflet" => false,
|
"einfuehrung" => ["template" => "einfuehrung.php", "mqtt" => false, "charts" => false, "meteogram" => false, "leaflet" => false,
|
||||||
"scripts" => ["js/solar/einfuehrung.js"]],
|
"scripts" => ["js/solar/einfuehrung.js"]],
|
||||||
// Vollbild für ein festes Display (Raspberry Pi). "eigen": die Vorlage
|
|
||||||
// ist eine ganze Seite, ohne header.php und footer.php.
|
|
||||||
"anzeige" => ["template" => "anzeige.php", "eigen" => true, "mqtt" => true, "charts" => false, "meteogram" => false, "leaflet" => false,
|
|
||||||
"scripts" => ["js/solar/anlageKatalog.js", "js/solar/anlage.js", "js/solar/speicher.js",
|
|
||||||
"js/solar/energieflussKatalog.js", "js/solar/energiefluss.js",
|
|
||||||
"js/solar/bewaesserungAnzeige.js", "js/solar/anzeige.js"]],
|
|
||||||
];
|
];
|
||||||
|
// Anzeige für ein festes Display (Raspberry Pi, 800 x 480): die Solar-Seite
|
||||||
|
// ohne Kopfleiste und Menue, mit schmaler Leiste - header.php und
|
||||||
|
// footer.php richten sich nach "anzeige", js/solar/anzeige.js macht den Rest.
|
||||||
|
$pages["anzeige"] = $pages["solar"];
|
||||||
|
$pages["anzeige"]["anzeige"] = true;
|
||||||
|
$pages["anzeige"]["scripts"][] = "js/solar/anzeige.js";
|
||||||
|
|
||||||
// Unbekannte oder fehlende Aktion faellt auf die Solar-Seite zurueck.
|
// Unbekannte oder fehlende Aktion faellt auf die Solar-Seite zurueck.
|
||||||
if (!isset($_GET["action"]) || !isset($pages[$_GET["action"]])) {
|
if (!isset($_GET["action"]) || !isset($pages[$_GET["action"]])) {
|
||||||
@@ -134,13 +134,9 @@ if (isset($_GET["addUser"])) {
|
|||||||
}
|
}
|
||||||
$page = $pages[$_GET["action"]];
|
$page = $pages[$_GET["action"]];
|
||||||
|
|
||||||
if (!empty($page["eigen"])) {
|
|
||||||
include "restricted/" . $page["template"];
|
|
||||||
} else {
|
|
||||||
include "restricted/header.php";
|
include "restricted/header.php";
|
||||||
include "restricted/" . $page["template"];
|
include "restricted/" . $page["template"];
|
||||||
include "restricted/footer.php";
|
include "restricted/footer.php";
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
echo <<<ENDE
|
echo <<<ENDE
|
||||||
<html>
|
<html>
|
||||||
|
|||||||
+66
-85
@@ -1,122 +1,103 @@
|
|||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
Vollbildanzeige (restricted/anzeige.php): Übersicht, Anlage und Speicher
|
Anzeige-Modus der Solar-Seite (index.php?action=anzeige) für ein festes
|
||||||
für ein festes Display, z.B. den Raspberry Pi mit 800 x 480.
|
Display, gebaut für den Raspberry Pi 3B+ mit 800 x 480.
|
||||||
|
|
||||||
Dieselben Bausteine wie auf der Solar-Seite, nur anders verdrahtet:
|
Die Seite selbst ist die Solar-Seite: solarMQTT.js zeichnet Übersicht,
|
||||||
- immer quer (hochkantUnter: 0), das Display steht quer
|
Anlage, Speicher und Diagramme und öffnet die Bedienfenster. header.php
|
||||||
- antippbar sind nur Kreise, die eine andere Ansicht öffnen; Bedienfenster
|
lässt Kopfleiste und Menü weg und setzt stattdessen die schmale Leiste
|
||||||
(Heizstab, Wallbox) gibt es hier nicht
|
links; die Übersicht bleibt quer (energieflussStarten() in solarMQTT.js
|
||||||
- nach LEERLAUF ohne Berührung zurück zur Übersicht
|
fragt nach der Klasse "anzeige" am body).
|
||||||
- MQTT verbindet nach einem Abbruch still neu; der Punkt unten in der
|
|
||||||
Leiste zeigt den Zustand (grün verbunden, gelb keine frischen Werte,
|
Hier steht nur, was ein Display ohne Bediener braucht:
|
||||||
rot getrennt). Kein alert() - am Display ist niemand, der ihn wegklickt.
|
- Leiste: Übersicht, Anlage, Speicher, Diagramme
|
||||||
- alle NEU_LADEN Stunden lädt die Seite neu, damit ein Browser, der
|
- nach LEERLAUF ohne Berührung zurück zur Übersicht, ganz nach oben
|
||||||
wochenlang läuft, keinen Speicher ansammelt und Änderungen am Code
|
- Punkt in der Leiste: grün verbunden, gelb keine frischen Werte,
|
||||||
ankommen
|
rot getrennt
|
||||||
|
- alle NEU_LADEN Stunden neu laden - ein Browser, der wochenlang läuft,
|
||||||
|
sammelt sonst Speicher an, und Änderungen am Code kämen nie an
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|
||||||
var mqttData = {};
|
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const ADRESSE = "wss://mqtt.nas.el-wa.org:443"; // wie in solarMQTT.js
|
|
||||||
const LEERLAUF = 2 * 60000;
|
const LEERLAUF = 2 * 60000;
|
||||||
const NEU_LADEN = 6 * 3600000;
|
const NEU_LADEN = 6 * 3600000;
|
||||||
const TAKT = typeof anzeigeRuhig !== "undefined" && anzeigeRuhig ? 2000 : 1000;
|
|
||||||
const VERALTET = 30000;
|
const VERALTET = 30000;
|
||||||
const THEMEN = [
|
|
||||||
"solarManager/#",
|
|
||||||
"Gartenwasser/vorn/Timers", "Gartenwasser/vorn/status", "Gartenwasser/vorn/WateringToday",
|
|
||||||
"Gartenwasser/hinten/Timers", "Gartenwasser/hinten/status", "Gartenwasser/hinten/WateringToday",
|
|
||||||
];
|
|
||||||
|
|
||||||
const $ = id => document.getElementById(id);
|
const $ = id => document.getElementById(id);
|
||||||
let bild = null, anlage = null, speicher = null;
|
let leerlaufUhr = null;
|
||||||
let leerlaufUhr = null, geplant = false, verbunden = false, letzteDaten = 0;
|
let letzteNachricht = null, letzteZeit = 0;
|
||||||
|
|
||||||
|
function leisteSetzen(name) {
|
||||||
|
document.querySelectorAll("[data-anzeige]").forEach(b =>
|
||||||
|
b.setAttribute("aria-pressed", b.dataset.anzeige === name ? "true" : "false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function karte() {
|
||||||
|
return $("energieflussKarte");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Eine Ansicht der Realtime-Karte zeigen - über ihren eigenen Umschalter. */
|
||||||
function zeige(name) {
|
function zeige(name) {
|
||||||
document.querySelectorAll("[data-anzeige]").forEach(b => b.setAttribute("aria-pressed", b.dataset.anzeige === name ? "true" : "false"));
|
if (name === "diagramme") {
|
||||||
document.querySelectorAll("[data-anzeige-flaeche]").forEach(f => { f.hidden = f.dataset.anzeigeFlaeche !== name; });
|
// Die nächste Spalte nach der Realtime-Karte - dazwischen steht ein <script>.
|
||||||
wachHalten();
|
let ziel = karte() && karte().closest("[class*=col-]").nextElementSibling;
|
||||||
|
while (ziel && !ziel.matches("[class*=col-]")) ziel = ziel.nextElementSibling;
|
||||||
|
if (ziel) ziel.scrollIntoView({ behavior: "instant", block: "start" });
|
||||||
|
leisteSetzen("diagramme");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.scrollTo({ top: 0, behavior: "instant" });
|
||||||
|
const knopf = document.querySelector('[data-enf-ansicht="' + name + '"]');
|
||||||
|
if (knopf) knopf.click();
|
||||||
|
leisteSetzen(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function wachHalten() {
|
function wachHalten() {
|
||||||
clearTimeout(leerlaufUhr);
|
clearTimeout(leerlaufUhr);
|
||||||
leerlaufUhr = setTimeout(() => zeige("fluss"), LEERLAUF);
|
leerlaufUhr = setTimeout(() => {
|
||||||
|
const offen = document.querySelector(".modal.show");
|
||||||
|
if (offen && window.bootstrap) bootstrap.Modal.getOrCreateInstance(offen).hide();
|
||||||
|
zeige("fluss");
|
||||||
|
}, LEERLAUF);
|
||||||
}
|
}
|
||||||
|
|
||||||
function zustand() {
|
function zustand() {
|
||||||
|
const nachricht = window.mqttData && mqttData["solarManager/P_Load"];
|
||||||
|
if (nachricht && nachricht !== letzteNachricht) { letzteNachricht = nachricht; letzteZeit = Date.now(); }
|
||||||
|
const verbunden = typeof solarMqttClient !== "undefined" && solarMqttClient && solarMqttClient.connected;
|
||||||
|
const frisch = Date.now() - letzteZeit < VERALTET;
|
||||||
const punkt = $("anzeigeVerbindung");
|
const punkt = $("anzeigeVerbindung");
|
||||||
const frisch = Date.now() - letzteDaten < VERALTET;
|
|
||||||
const klasse = "anzeige-punkt" + (verbunden && frisch ? " gut" : verbunden ? " warn" : "");
|
const klasse = "anzeige-punkt" + (verbunden && frisch ? " gut" : verbunden ? " warn" : "");
|
||||||
if (punkt.className !== klasse) {
|
if (punkt.className !== klasse) {
|
||||||
punkt.className = klasse;
|
punkt.className = klasse;
|
||||||
punkt.title = verbunden && frisch ? "verbunden" : verbunden ? "verbunden, aber keine frischen Werte" : "Verbindung getrennt – wird neu aufgebaut";
|
punkt.title = verbunden && frisch ? "verbunden" : verbunden ? "verbunden, aber keine frischen Werte" : "Verbindung getrennt";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function uhr() {
|
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
const text = String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
|
const uhr = String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
|
||||||
if ($("anzeigeUhr").textContent !== text) $("anzeigeUhr").textContent = text;
|
if ($("anzeigeUhr").textContent !== uhr) $("anzeigeUhr").textContent = uhr;
|
||||||
}
|
|
||||||
|
|
||||||
function aktualisieren() {
|
|
||||||
if (geplant) return;
|
|
||||||
geplant = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
geplant = false;
|
|
||||||
bild.aktualisieren(mqttData);
|
|
||||||
anlage.aktualisieren(mqttData);
|
|
||||||
speicher.aktualisieren(mqttData);
|
|
||||||
}, TAKT);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verbinden() {
|
|
||||||
const client = mqtt.connect(ADRESSE, { rejectUnauthorized: false, reconnectPeriod: 5000, connectTimeout: 15000 });
|
|
||||||
client.on("connect", () => {
|
|
||||||
verbunden = true;
|
|
||||||
client.subscribe(THEMEN);
|
|
||||||
zustand();
|
|
||||||
});
|
|
||||||
const weg = () => { verbunden = false; zustand(); };
|
|
||||||
client.on("close", weg);
|
|
||||||
client.on("offline", weg);
|
|
||||||
client.on("error", () => {});
|
|
||||||
client.on("message", (topic, nachricht) => {
|
|
||||||
mqttData[topic] = nachricht;
|
|
||||||
if (topic.endsWith("/Timers")) wateringTimersLastSeen[topic] = Date.now();
|
|
||||||
if (topic === "solarManager/P_Load") {
|
|
||||||
letzteDaten = Date.now();
|
|
||||||
aktualisieren();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const bewaesserung = () => wateringSymboleSetzen(id => document.getElementById(id));
|
if (!document.body.classList.contains("anzeige")) return;
|
||||||
|
|
||||||
bild = new Energiefluss($("anzeige-fluss"), {
|
|
||||||
einstellungen: typeof energieflussEinstellungen !== "undefined" ? energieflussEinstellungen : {},
|
|
||||||
hochkantUnter: 0,
|
|
||||||
klickFilter: k => !!k.klick.ansicht,
|
|
||||||
onKlick: k => zeige(k.klick.ansicht),
|
|
||||||
onGezeichnet: bewaesserung,
|
|
||||||
});
|
|
||||||
bild.zeichnen();
|
|
||||||
anlage = new Anlage($("anzeige-anlage"), { hochkantUnter: 0 });
|
|
||||||
anlage.zeichnen();
|
|
||||||
speicher = new Speicher($("anzeige-speicher"));
|
|
||||||
speicher.zeichnen();
|
|
||||||
|
|
||||||
document.querySelectorAll("[data-anzeige]").forEach(b => b.addEventListener("click", () => zeige(b.dataset.anzeige)));
|
document.querySelectorAll("[data-anzeige]").forEach(b => b.addEventListener("click", () => zeige(b.dataset.anzeige)));
|
||||||
document.addEventListener("pointerdown", wachHalten, { passive: true });
|
// Klick auf Sonne oder Batterie wechselt über den Umschalter der Karte -
|
||||||
|
// die Leiste zieht nach.
|
||||||
|
document.querySelectorAll("[data-enf-ansicht]").forEach(b =>
|
||||||
|
b.addEventListener("click", () => leisteSetzen(b.dataset.enfAnsicht)));
|
||||||
|
window.addEventListener("scroll", () => {
|
||||||
|
const k = karte();
|
||||||
|
if (!k) return;
|
||||||
|
const unten = k.getBoundingClientRect().bottom < window.innerHeight / 2;
|
||||||
|
const aktiv = document.querySelector("[data-enf-ansicht].active");
|
||||||
|
leisteSetzen(unten ? "diagramme" : aktiv ? aktiv.dataset.enfAnsicht : "fluss");
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
setInterval(() => { uhr(); zustand(); bewaesserung(); }, 1000);
|
document.addEventListener("pointerdown", wachHalten, { passive: true });
|
||||||
uhr();
|
wachHalten();
|
||||||
|
setInterval(zustand, 1000);
|
||||||
zustand();
|
zustand();
|
||||||
verbinden();
|
|
||||||
setTimeout(() => location.reload(), NEU_LADEN);
|
setTimeout(() => location.reload(), NEU_LADEN);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
onKlick(knoten), // Kreis mit klick-Ziel angetippt
|
onKlick(knoten), // Kreis mit klick-Ziel angetippt
|
||||||
onVerschoben(id, x, y), // nur beim Bearbeiten
|
onVerschoben(id, x, y), // nur beim Bearbeiten
|
||||||
onGezeichnet(), // nach jedem (Neu-)Zeichnen
|
onGezeichnet(), // nach jedem (Neu-)Zeichnen
|
||||||
klickFilter(knoten), // optional: welche Kreise antippbar sind
|
|
||||||
hochkantUnter: 720, // optional: px Breite, darunter hochkant (0 = nie)
|
hochkantUnter: 720, // optional: px Breite, darunter hochkant (0 = nie)
|
||||||
});
|
});
|
||||||
bild.aktualisieren(mqttData);
|
bild.aktualisieren(mqttData);
|
||||||
@@ -222,9 +221,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Antippbar: hat ein Ziel, nicht im Bearbeiten, und opt.klickFilter lässt es zu. */
|
/** Antippbar: hat ein Ziel und wird nicht gerade bearbeitet. */
|
||||||
klickbar(k) {
|
klickbar(k) {
|
||||||
return !!k.klick && !this.bearbeiten && (!this.opt.klickFilter || this.opt.klickFilter(k));
|
return !!k.klick && !this.bearbeiten;
|
||||||
}
|
}
|
||||||
|
|
||||||
hochkantGewuenscht() {
|
hochkantGewuenscht() {
|
||||||
|
|||||||
@@ -196,7 +196,10 @@ function energieflussStarten() {
|
|||||||
knopf.addEventListener("click", function () { zeigeAnsicht(knopf.dataset.enfAnsicht); });
|
knopf.addEventListener("click", function () { zeigeAnsicht(knopf.dataset.enfAnsicht); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Anzeige-Modus (festes Display, js/solar/anzeige.js): immer quer.
|
||||||
|
const anzeigeModus = document.body.classList.contains("anzeige");
|
||||||
energiefluss = new Energiefluss(behaelter, {
|
energiefluss = new Energiefluss(behaelter, {
|
||||||
|
hochkantUnter: anzeigeModus ? 0 : undefined,
|
||||||
einstellungen: typeof energieflussEinstellungen !== "undefined" ? energieflussEinstellungen : {},
|
einstellungen: typeof energieflussEinstellungen !== "undefined" ? energieflussEinstellungen : {},
|
||||||
onKlick: function (knoten) {
|
onKlick: function (knoten) {
|
||||||
if (knoten.klick.ansicht) zeigeAnsicht(knoten.klick.ansicht);
|
if (knoten.klick.ansicht) zeigeAnsicht(knoten.klick.ansicht);
|
||||||
@@ -211,7 +214,7 @@ function energieflussStarten() {
|
|||||||
|
|
||||||
const anlageBehaelter = document.getElementById("anlage");
|
const anlageBehaelter = document.getElementById("anlage");
|
||||||
if (anlageBehaelter && typeof Anlage !== "undefined") {
|
if (anlageBehaelter && typeof Anlage !== "undefined") {
|
||||||
anlage = new Anlage(anlageBehaelter);
|
anlage = new Anlage(anlageBehaelter, { hochkantUnter: anzeigeModus ? 0 : undefined });
|
||||||
anlage.zeichnen();
|
anlage.zeichnen();
|
||||||
}
|
}
|
||||||
const speicherBehaelter = document.getElementById("speicher");
|
const speicherBehaelter = document.getElementById("speicher");
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Vollbildanzeige für ein festes Display, gedacht für den Raspberry Pi mit
|
|
||||||
* dem 7-Zoll-Display (800 x 480) im Kioskmodus:
|
|
||||||
*
|
|
||||||
* index.php?action=anzeige ruhige Animationen, kein Mauszeiger
|
|
||||||
* index.php?action=anzeige&animation=1 alle Animationen (schnellere Geräte)
|
|
||||||
* index.php?action=anzeige&maus=1 Mauszeiger sichtbar (zum Einrichten)
|
|
||||||
*
|
|
||||||
* Eine eigene Seite ohne Kopf, Seitenleiste und die Karten der Solar-Seite:
|
|
||||||
* links eine schmale Leiste mit Übersicht, Anlage und Speicher, rechts die
|
|
||||||
* Ansicht auf voller Höhe. Gezeichnet wird mit denselben Bausteinen wie auf
|
|
||||||
* der Solar-Seite (energiefluss.js, anlage.js, speicher.js); verdrahtet
|
|
||||||
* werden sie von js/solar/anzeige.js.
|
|
||||||
*
|
|
||||||
* index.php bindet für diese Seite weder header.php noch footer.php ein
|
|
||||||
* ("eigen" in der Seitentabelle) - deshalb steht der Kopf hier selbst.
|
|
||||||
*/
|
|
||||||
require_once(__DIR__ . "/energiefluss.php");
|
|
||||||
|
|
||||||
$ruhig = empty($_GET["animation"]);
|
|
||||||
$maus = !empty($_GET["maus"]);
|
|
||||||
?>
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="de" data-bs-theme="dark">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
|
|
||||||
<title>Energiefluss</title>
|
|
||||||
<link rel="icon" type="image/png" href="assets/img/favicon.png" />
|
|
||||||
<link rel="stylesheet" href="assets/fonts/font_poppins.css" />
|
|
||||||
<link rel="stylesheet" href="css/bootstrap-icons.min.css" />
|
|
||||||
<link rel="stylesheet" href="./css/solar.css?v=<?= filemtime("css/solar.css") ?>" />
|
|
||||||
</head>
|
|
||||||
<body class="anzeige<?= $ruhig ? " anzeige-ruhig" : "" ?><?= $maus ? "" : " anzeige-ohne-maus" ?>">
|
|
||||||
<nav class="anzeige-leiste" aria-label="Ansicht">
|
|
||||||
<button type="button" data-anzeige="fluss" aria-pressed="true" title="Übersicht"><i class="bi bi-diagram-3"></i></button>
|
|
||||||
<button type="button" data-anzeige="anlage" aria-pressed="false" title="Anlage"><i class="bi bi-grid-3x2-gap"></i></button>
|
|
||||||
<button type="button" data-anzeige="speicher" aria-pressed="false" title="Speicher"><i class="bi bi-battery-half"></i></button>
|
|
||||||
<div class="anzeige-fuss">
|
|
||||||
<span class="anzeige-punkt" id="anzeigeVerbindung" title="Verbindung wird aufgebaut"></span>
|
|
||||||
<span class="anzeige-uhr" id="anzeigeUhr"></span>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<main class="anzeige-inhalt">
|
|
||||||
<div class="anzeige-flaeche" data-anzeige-flaeche="fluss" id="anzeige-fluss"></div>
|
|
||||||
<div class="anzeige-flaeche" data-anzeige-flaeche="anlage" id="anzeige-anlage" hidden></div>
|
|
||||||
<div class="anzeige-flaeche" data-anzeige-flaeche="speicher" id="anzeige-speicher" hidden></div>
|
|
||||||
</main>
|
|
||||||
<script>
|
|
||||||
// Abweichungen dieses Hauses vom Katalog, siehe restricted/energiefluss.php.
|
|
||||||
const energieflussEinstellungen = <?= json_encode((object)energieflussLaden(), JSON_UNESCAPED_UNICODE) ?>;
|
|
||||||
const anzeigeRuhig = <?= $ruhig ? "true" : "false" ?>;
|
|
||||||
</script>
|
|
||||||
<script src="js/mqtt.min.js"></script>
|
|
||||||
<?php foreach ($page["scripts"] as $script): ?>
|
|
||||||
<script src="<?= $script ?>?v=<?= filemtime($script) ?>"></script>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<?php if (empty($page["anzeige"])): ?>
|
||||||
<!--begin::Footer-->
|
<!--begin::Footer-->
|
||||||
<footer class="app-footer">
|
<footer class="app-footer">
|
||||||
<!--begin::To the end-->
|
<!--begin::To the end-->
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
<!--end::Copyright-->
|
<!--end::Copyright-->
|
||||||
</footer>
|
</footer>
|
||||||
<!--end::Footer-->
|
<!--end::Footer-->
|
||||||
|
<?php endif; ?>
|
||||||
<div class="modal fade" id="modalEV" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
|
<div class="modal fade" id="modalEV" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|||||||
+27
-1
@@ -59,9 +59,34 @@ if (!isset($_GET["floor"]))
|
|||||||
<!--end::Head-->
|
<!--end::Head-->
|
||||||
<!--begin::Body-->
|
<!--begin::Body-->
|
||||||
|
|
||||||
<body class="layout-fixed sidebar-expand-lg bg-body-tertiary" data-bs-theme="dark">
|
<?php
|
||||||
|
// Anzeige-Modus (?action=anzeige, siehe index.php): dieselbe Seite fuer ein
|
||||||
|
// festes Display, ohne Kopfleiste und Menue, dafuer mit schmaler Leiste.
|
||||||
|
// ?animation=1 schaltet alle Animationen ein, ?maus=1 den Mauszeiger.
|
||||||
|
$anzeige = !empty($page["anzeige"]);
|
||||||
|
$koerperKlassen = "layout-fixed sidebar-expand-lg bg-body-tertiary";
|
||||||
|
if ($anzeige) {
|
||||||
|
$koerperKlassen .= " anzeige" . (empty($_GET["animation"]) ? " anzeige-ruhig" : "")
|
||||||
|
. (empty($_GET["maus"]) ? " anzeige-ohne-maus" : "");
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<body class="<?= $koerperKlassen ?>" data-bs-theme="dark">
|
||||||
|
<?php if ($anzeige): ?>
|
||||||
|
<!-- Leiste der Anzeige; verdrahtet in js/solar/anzeige.js -->
|
||||||
|
<nav class="anzeige-leiste" aria-label="Ansicht">
|
||||||
|
<button type="button" data-anzeige="fluss" aria-pressed="true" title="Übersicht"><i class="bi bi-diagram-3"></i></button>
|
||||||
|
<button type="button" data-anzeige="anlage" aria-pressed="false" title="Anlage"><i class="bi bi-grid-3x2-gap"></i></button>
|
||||||
|
<button type="button" data-anzeige="speicher" aria-pressed="false" title="Speicher"><i class="bi bi-battery-half"></i></button>
|
||||||
|
<button type="button" data-anzeige="diagramme" aria-pressed="false" title="Diagramme"><i class="bi bi-graph-up"></i></button>
|
||||||
|
<div class="anzeige-fuss">
|
||||||
|
<span class="anzeige-punkt" id="anzeigeVerbindung" title="Verbindung wird aufgebaut"></span>
|
||||||
|
<span class="anzeige-uhr" id="anzeigeUhr"></span>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<?php endif; ?>
|
||||||
<!--begin::App Wrapper-->
|
<!--begin::App Wrapper-->
|
||||||
<div class="app-wrapper">
|
<div class="app-wrapper">
|
||||||
|
<?php if (!$anzeige): ?>
|
||||||
<!--begin::Header-->
|
<!--begin::Header-->
|
||||||
<nav class="app-header navbar navbar-expand bg-body">
|
<nav class="app-header navbar navbar-expand bg-body">
|
||||||
<!--begin::Container-->
|
<!--begin::Container-->
|
||||||
@@ -214,3 +239,4 @@ if (!isset($_GET["floor"]))
|
|||||||
<!--end::Sidebar Wrapper-->
|
<!--end::Sidebar Wrapper-->
|
||||||
</aside>
|
</aside>
|
||||||
<!--end::Sidebar-->
|
<!--end::Sidebar-->
|
||||||
|
<?php endif; ?>
|
||||||
Reference in New Issue
Block a user