Einfuehrung fuer Einsteiger: Folien mit echten Aufnahmen und Markierungen

Neue Seite "Einfuehrung" im Menue: 31 Folien in zwei Teilen - Bedienen im
Alltag (Grundriss, Raum, Rollos, Heizung, Licht, Zeitleiste, Solar, Auto,
Heizstab und Wasser, Statistik, Historie, Heizung, Wetter, Fahrzeug, Hilfe)
und Einrichten und Verwalten (Preise, Grundriss, Kacheln, Geraete,
Automatik anlegen in Wenn/Wann/Dann, Ketten, Fahrzeugschluessel,
Protokolle).

- Bilder sind echte Aufnahmen der Oberflaeche mit Livewerten, Handy- und
  Desktopgroesse. tools/einfuehrung/aufnehmen.ps1 steuert Edge ohne
  Fenster ueber das DevTools-Protokoll nach plan.json und schreibt zu jedem
  Bild die Lage der markierten Elemente; daraus sitzen die nummerierten
  Punkte. Wetterort und Schluesselendungen werden vor der Aufnahme
  abgedeckt.
- Blaettern per Knopf, Pfeiltaste oder Wischen; Kapitelsprung; die Folie
  steht in der Adresse (#folie-7). Erklaerung und Punkt heben sich
  gegenseitig hervor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 14:32:14 +02:00
co-authored by Claude Opus 5
parent 05029889b9
commit 865dd61535
51 changed files with 1297 additions and 1 deletions
+20 -1
View File
@@ -48,7 +48,8 @@ smart/
├── restricted/ Vorlagen (was man sieht) und Modelle (was es weiß)
│ ├── header.php footer.php Rahmen jeder Seite, Menü, Skript-Einbindung
│ ├── home.php solar.php heat.php history.php
│ ├── skoda.php weather.php settings.php logs.php ← die acht Seitenvorlagen
│ ├── skoda.php weather.php settings.php logs.php
│ ├── einfuehrung.php ← die neun Seitenvorlagen
│ ├── rooms.php Etagen und Räume lesen (homeMesh), Kachelvorgabe, Symbole
│ ├── grundriss.php Etagen, Räume, Kachelpositionen, Bilder ändern (Reiter Grundriss)
│ ├── zeitleiste.php Automatiken eines Tages: Lage, Bahn, ob dran, Läufe
@@ -190,6 +191,7 @@ genau die Topics, die auf den Kacheln stehen (`tileTopics()` in `rooms.php`).
| `weather` | `weather.php` | `weatherMQTT.js` | nichts — das Meteogramm ist ein fremdes Dokument im `<iframe>` (`js/meteogram.js`) | `weatherStation/#` |
| `settings` | `settings.php` | `settings.js`, `grundriss.js` | `settings.php?action=…` | — |
| `logs` | `logs.php` | `logs.js` | `logs.php?action=…` | — |
| `einfuehrung` | `einfuehrung.php` | `einfuehrung.js` | nichts — Folien und Bilder stehen in der Seite | — |
Und dasselbe als Abhängigkeitsbild — von der Seite bis in den Speicher:
@@ -851,6 +853,23 @@ und das Skript lädt und `window.fetch` durch eine Attrappe mit echten Daten
ersetzt. Damit lässt sich das Zeichnen prüfen, ohne angemeldet zu sein und
ohne etwas zu speichern.
**Die Einführung** (Menü → Einführung) sind Folien für Einsteiger in zwei
Teilen: Bedienen im Alltag, Einrichten und Verwalten. Die Texte stehen in
`einfuehrungFolien()` in `restricted/einfuehrung.php`, die Bilder sind echte
Aufnahmen unter `assets/img/einfuehrung`. Ändert sich die Oberfläche, nimmt
`tools/einfuehrung/aufnehmen.ps1` sie auf dem Arbeitsrechner neu auf — Edge
ohne Fenster über das DevTools-Protokoll, mit echten Wartezeiten für die
Livewerte, Klicks und Scrollen laut `plan.json`:
```powershell
powershell -ExecutionPolicy Bypass -File tools\einfuehrung\aufnehmen.ps1 -Plan tools\einfuehrung\plan.json -Ausgabe assets\img\einfuehrung
```
Zu jedem Bild schreibt es eine `.json` mit der Lage der markierten Elemente;
daraus setzt die Seite ihre nummerierten Punkte, sie wandern also mit. Ort und
Koordinaten im Wetterkopf sowie die Endungen der Fahrzeugschlüssel deckt der
Plan vor der Aufnahme ab. Die Seite muss dafür im Heimnetz erreichbar sein.
Zwei Repositories, beide auf Gitea (`gitea.nas.el-wa.org`):
| Repo | Ort | Inhalt |
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":50,"y":75.3},"2":{"x":13,"y":84.5},"3":{"x":88.5,"y":92.5}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":81,"y":29.9},"2":{"x":16.5,"y":36.4},"3":{"x":19.7,"y":40.9},"4":{"x":8.1,"y":45},"5":{"x":50,"y":49.7}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":35.3,"y":16.7},"2":{"x":50,"y":37.8},"3":{"x":86,"y":23.7},"4":{"x":12.8,"y":50.9}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

@@ -0,0 +1 @@
{"1":{"x":36.2,"y":45.3},"2":{"x":65.9,"y":54},"3":{"x":95.5,"y":54.1}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

@@ -0,0 +1 @@
{"1":{"x":59.8,"y":23.4},"2":{"x":46.7,"y":56.2},"3":{"x":0,"y":0},"4":{"x":76.9,"y":88.3},"5":{"x":85.1,"y":115.3}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

@@ -0,0 +1 @@
{"1":{"x":59.8,"y":13},"2":{"x":59.8,"y":20.8},"3":{"x":28.9,"y":27.6},"4":{"x":26.5,"y":32.6}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":33.3,"y":16.6},"2":{"x":93.6,"y":23.5},"3":{"x":26.2,"y":81.4}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":11.5,"y":30.4},"2":{"x":49.4,"y":26.9},"3":{"x":64.9,"y":29.9}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":35.1,"y":16},"2":{"x":35.1,"y":51.5},"3":{"x":32.1,"y":66.5},"4":{"x":9.2,"y":3.3}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

@@ -0,0 +1 @@
{"1":{"x":50,"y":14},"2":{"x":14.1,"y":24.1},"3":{"x":59.2,"y":30.5}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":50,"y":51.3},"2":{"x":15.6,"y":54.7}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":50,"y":51.5}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":32.1,"y":32.2},"2":{"x":28.3,"y":52.1},"3":{"x":32,"y":71.4},"4":{"x":46.7,"y":52.5},"5":{"x":60.6,"y":52},"6":{"x":77.6,"y":51.5},"7":{"x":82.4,"y":75},"8":{"x":82.4,"y":27.5},"9":{"x":68,"y":27.7},"10":{"x":68,"y":75.2},"11":{"x":52.5,"y":75.2},"12":{"x":0,"y":0}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

+1
View File
@@ -0,0 +1 @@
{"1":{"x":34.9,"y":20.5},"2":{"x":49.3,"y":25.1},"3":{"x":25,"y":28.9},"4":{"x":30.8,"y":32.8},"5":{"x":-0.5,"y":36.8}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

@@ -0,0 +1 @@
{"1":{"x":50,"y":66.2},"2":{"x":24.3,"y":79},"3":{"x":51,"y":79},"4":{"x":84.7,"y":79}}
+259
View File
@@ -3166,6 +3166,265 @@ input[type='range']::-webkit-slider-runnable-track {
}
.zl-karte-knoepfe .btn.zl-karte-weg:hover { color: var(--ton-schlecht, #e06060); }
/* ===========================================================================
Einfuehrung (restricted/einfuehrung.php, js/solar/einfuehrung.js)
Eine Folie: links das Bild in einem ruhigen Rahmen (Handy-Aufnahmen
schmal wie ein Telefon, Desktop-Aufnahmen breit), rechts der Text mit den
nummerierten Erklaerungen. Auf schmalen Schirmen untereinander.
=========================================================================== */
.ef {
max-width: 1180px;
margin: 0 auto 2rem;
outline: none;
}
.ef-kopf {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .6rem 1rem;
margin-bottom: .6rem;
}
.ef-teile { display: flex; gap: .35rem; flex-wrap: wrap; }
.ef-teil {
display: inline-flex;
align-items: center;
gap: .5rem;
padding: .3rem .8rem .3rem .35rem;
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
border-radius: 999px;
background: transparent;
color: var(--ton-schrift-2, #9aa6ad);
font: 500 .85rem var(--font-anzeige, inherit);
cursor: pointer;
transition: background .15s, border-color .15s, color .15s;
}
.ef-teil-nr {
display: inline-grid;
place-items: center;
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
background: rgba(138, 148, 158, .18);
font-size: .75rem;
}
.ef-teil.aktiv {
border-color: var(--ton-sonne, #f0b429);
color: var(--ton-schrift, #e8ede9);
background: rgba(var(--ton-sonne-rgb, 240, 180, 41), .1);
}
.ef-teil.aktiv .ef-teil-nr { background: var(--ton-sonne, #f0b429); color: #1b1f22; }
.ef-kopf .form-select.ef-kapitel { width: auto; min-width: 12rem; margin-left: auto; }
.ef-zaehler {
font-size: .8rem;
color: var(--ton-schrift-3, #6f7a81);
font-variant-numeric: tabular-nums;
min-width: 3.5rem;
text-align: right;
}
.ef-fortschritt {
height: 3px;
border-radius: 2px;
background: var(--ton-linie, rgba(138, 148, 158, .2));
margin-bottom: 1.2rem;
overflow: hidden;
}
.ef-fortschritt span {
display: block;
height: 100%;
width: 0;
background: var(--ton-sonne, #f0b429);
transition: width .35s ease;
}
/* Buehne und Folie */
.ef-buehne { position: relative; }
.ef-folie {
display: grid;
gap: 2rem;
align-items: center;
padding: 1.6rem;
border-radius: var(--ton-radius, .875rem);
background: var(--ton-flaeche, #23282c);
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
min-height: 34rem;
}
.ef-folie.mit-bild.form-handy { grid-template-columns: minmax(0, 330px) minmax(0, 1fr); }
.ef-folie.mit-bild.form-breit { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); }
.ef-folie.ohne-bild { grid-template-columns: minmax(0, 46rem); justify-content: center; }
.ef-folie.rein-vor { animation: ef-rein-vor .32s ease both; }
.ef-folie.rein-rueck { animation: ef-rein-rueck .32s ease both; }
@keyframes ef-rein-vor { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: none; } }
@keyframes ef-rein-rueck { from { opacity: 0; transform: translateX(-18px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
.ef-folie.rein-vor, .ef-folie.rein-rueck { animation: none; }
}
.ef-bild { margin: 0; justify-self: center; width: 100%; }
.ef-rahmen {
position: relative;
border-radius: 14px;
overflow: visible;
box-shadow: 0 18px 40px rgba(0, 0, 0, .35);
}
.ef-rahmen img {
display: block;
width: 100%;
height: auto;
border-radius: 14px;
border: 1px solid var(--ton-linie-satt, rgba(138, 148, 158, .38));
}
/* Handy-Aufnahmen in einem angedeuteten Telefon. */
.form-handy .ef-rahmen {
max-width: 330px;
margin: 0 auto;
padding: 10px;
border-radius: 30px;
background: #0d0f11;
border: 1px solid rgba(138, 148, 158, .3);
}
.form-handy .ef-rahmen img { border-radius: 22px; border: 0; }
/* Die nummerierten Punkte im Bild */
.ef-marke {
position: absolute;
transform: translate(-50%, -50%);
display: grid;
place-items: center;
width: 26px;
height: 26px;
border-radius: 50%;
background: var(--ton-sonne, #f0b429);
color: #1b1f22;
font: 700 .8rem var(--font-anzeige, inherit);
box-shadow: 0 0 0 3px rgba(27, 31, 34, .85), 0 4px 10px rgba(0, 0, 0, .45);
cursor: pointer;
z-index: 2;
transition: transform .15s ease, box-shadow .15s ease;
}
.ef-marke::after {
content: "";
position: absolute;
inset: -6px;
border-radius: 50%;
border: 2px solid rgba(var(--ton-sonne-rgb, 240, 180, 41), .55);
animation: ef-puls 2.4s ease-out infinite;
}
.ef-marke.hell { transform: translate(-50%, -50%) scale(1.25); box-shadow: 0 0 0 3px rgba(27, 31, 34, .85), 0 0 0 8px rgba(var(--ton-sonne-rgb, 240, 180, 41), .35); }
@keyframes ef-puls { 0% { transform: scale(.8); opacity: .9; } 70%, 100% { transform: scale(1.5); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ef-marke::after { animation: none; opacity: 0; } }
/* Text */
.ef-kapitelname {
font-size: .72rem;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--ton-sonne, #f0b429);
margin-bottom: .4rem;
}
.ef-titel {
font: 600 1.7rem/1.2 var(--font-anzeige, inherit);
color: var(--ton-schrift, #e8ede9);
margin: 0 0 .8rem;
}
.ef-einleitung {
font-size: 1.02rem;
line-height: 1.6;
color: var(--ton-schrift-2, #9aa6ad);
margin-bottom: 1.1rem;
}
.ef-einleitung b, .ef-marken b, .ef-punkte b, .ef-tipp b { color: var(--ton-schrift, #e8ede9); font-weight: 600; }
.ef-marken {
list-style: none;
padding: 0;
margin: 0 0 1rem;
display: flex;
flex-direction: column;
gap: .35rem;
}
.ef-marken li {
display: flex;
gap: .7rem;
align-items: flex-start;
padding: .45rem .6rem;
border-radius: 10px;
color: var(--ton-schrift-2, #9aa6ad);
line-height: 1.5;
cursor: pointer;
transition: background .15s;
}
.ef-marken li:hover, .ef-marken li.hell { background: rgba(var(--ton-sonne-rgb, 240, 180, 41), .09); }
.ef-marke-nr {
flex: none;
display: grid;
place-items: center;
width: 22px;
height: 22px;
margin-top: 1px;
border-radius: 50%;
background: var(--ton-sonne, #f0b429);
color: #1b1f22;
font: 700 .72rem var(--font-anzeige, inherit);
}
.ef-punkte {
padding-left: 1.1rem;
margin: 0 0 1rem;
color: var(--ton-schrift-2, #9aa6ad);
line-height: 1.6;
}
.ef-punkte li { margin-bottom: .45rem; }
.ef-punkte li::marker { color: var(--ton-sonne, #f0b429); }
.ef-tipp {
display: flex;
gap: .6rem;
align-items: flex-start;
margin: .4rem 0 0;
padding: .7rem .9rem;
border-radius: 10px;
background: rgba(0, 120, 143, .14);
border: 1px solid rgba(0, 120, 143, .35);
color: var(--ton-schrift, #e8ede9);
font-size: .92rem;
line-height: 1.5;
}
.ef-tipp .bi { color: #35b6cf; margin-top: .1rem; }
/* Fuss */
.ef-fuss {
display: flex;
align-items: center;
gap: 1rem;
margin-top: 1rem;
}
.ef-punkte-nav {
flex: 1 1 auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 5px;
}
.ef-punkt {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(138, 148, 158, .3);
cursor: pointer;
transition: background .15s, transform .15s;
}
.ef-punkt.neuer-teil { margin-left: 10px; }
.ef-punkt.aktiv { background: var(--ton-sonne, #f0b429); transform: scale(1.35); }
@media (max-width: 991.98px) {
.ef-folie.mit-bild.form-handy,
.ef-folie.mit-bild.form-breit { grid-template-columns: minmax(0, 1fr); gap: 1.4rem; }
.ef-folie { padding: 1rem; min-height: 0; }
.form-handy .ef-rahmen { max-width: 280px; }
.ef-titel { font-size: 1.4rem; }
.ef-kopf .form-select.ef-kapitel { margin-left: 0; flex: 1 1 12rem; }
.ef-punkte-nav { display: none; }
.ef-fuss { justify-content: space-between; position: sticky; bottom: 0; padding: .6rem 0; background: var(--ton-grund, #191d20); z-index: 5; }
}
/* Ansicht umschalten: eine Klasse mehr als Bootstrap (solar.css laedt vorher). */
.zl-ansicht .btn.active {
background: rgba(0, 120, 143, .25);
+2
View File
@@ -115,6 +115,8 @@ if (isset($_GET["addUser"])) {
"scripts" => ["js/solar/settings.js", "js/solar/grundriss.js"]],
"logs" => ["template" => "logs.php", "mqtt" => false, "charts" => false, "meteogram" => false, "leaflet" => false,
"scripts" => ["js/solar/logs.js"]],
"einfuehrung" => ["template" => "einfuehrung.php", "mqtt" => false, "charts" => false, "meteogram" => false, "leaflet" => false,
"scripts" => ["js/solar/einfuehrung.js"]],
];
// Unbekannte oder fehlende Aktion faellt auf die Solar-Seite zurueck.
+133
View File
@@ -0,0 +1,133 @@
/* ==========================================================================
Einführung als Folien (restricted/einfuehrung.php).
Blättern mit den Knöpfen, den Pfeiltasten oder durch Wischen. Die Folie
steht in der Adresse (#folie-7), damit man sie verlinken und nach einem
Neuladen dort weitermachen kann. Eine Erklärung antippen lässt ihren Punkt
im Bild aufleuchten - und umgekehrt.
========================================================================== */
document.addEventListener("DOMContentLoaded", function () {
const ef = document.getElementById("einfuehrung");
if (!ef) return;
const folien = [...ef.querySelectorAll(".ef-folie")];
const zurueck = ef.querySelector(".ef-zurueck");
const weiter = ef.querySelector(".ef-weiter");
const zaehler = ef.querySelector(".ef-zaehler");
const balken = ef.querySelector(".ef-fortschritt span");
const kapitelWahl = ef.querySelector(".ef-kapitel");
const punkteNav = ef.querySelector(".ef-punkte-nav");
let aktuell = 0;
// Die Punkte unten: einer je Folie, Teile durch eine Lücke getrennt.
folien.forEach((f, i) => {
const p = document.createElement("span");
p.className = "ef-punkt" + (i > 0 && folien[i - 1].dataset.teil !== f.dataset.teil ? " neuer-teil" : "");
p.dataset.ziel = i;
punkteNav.appendChild(p);
});
punkteNav.removeAttribute("aria-hidden");
function zeige(nr, ohneAdresse) {
nr = Math.max(0, Math.min(folien.length - 1, nr));
const richtung = nr >= aktuell ? "vor" : "rueck";
folien.forEach((f, i) => {
f.hidden = i !== nr;
f.classList.remove("rein-vor", "rein-rueck");
});
const f = folien[nr];
void f.offsetWidth; // Animation neu starten
f.classList.add("rein-" + richtung);
aktuell = nr;
zaehler.textContent = (nr + 1) + " / " + folien.length;
balken.style.width = ((nr + 1) / folien.length * 100).toFixed(1) + "%";
zurueck.disabled = nr === 0;
weiter.innerHTML = nr === folien.length - 1
? 'Von vorn<i class="bi bi-arrow-counterclockwise ms-1"></i>'
: 'Weiter<i class="bi bi-arrow-right ms-1"></i>';
ef.querySelectorAll(".ef-teil").forEach(t => t.classList.toggle("aktiv", t.dataset.teil === f.dataset.teil));
punkteNav.querySelectorAll(".ef-punkt").forEach((p, i) => p.classList.toggle("aktiv", i === nr));
// Das Kapitel dieser Folie in der Auswahl: die letzte Kapitel-Folie <= nr.
let kap = null;
[...kapitelWahl.options].forEach(o => { if (Number(o.value) <= nr) kap = o.value; });
if (kap !== null) kapitelWahl.value = kap;
if (!ohneAdresse) history.replaceState(null, "", "#folie-" + (nr + 1));
// Auf dem Handy nach oben, damit die neue Folie mit dem Titel beginnt.
const oben = ef.getBoundingClientRect().top;
if (oben < 0) ef.scrollIntoView({ block: "start", behavior: "smooth" });
}
zurueck.addEventListener("click", () => zeige(aktuell - 1));
weiter.addEventListener("click", () => zeige(aktuell === folien.length - 1 ? 0 : aktuell + 1));
kapitelWahl.addEventListener("change", () => zeige(Number(kapitelWahl.value)));
ef.querySelectorAll(".ef-teil").forEach(t => t.addEventListener("click", () => {
zeige(folien.findIndex(f => f.dataset.teil === t.dataset.teil));
}));
punkteNav.addEventListener("click", e => {
const p = e.target.closest(".ef-punkt");
if (p) zeige(Number(p.dataset.ziel));
});
document.addEventListener("keydown", e => {
if (e.target.closest("input, select, textarea")) return;
if (e.key === "ArrowRight" || e.key === "PageDown") { zeige(aktuell + 1); e.preventDefault(); }
if (e.key === "ArrowLeft" || e.key === "PageUp") { zeige(aktuell - 1); e.preventDefault(); }
if (e.key === "Home") zeige(0);
if (e.key === "End") zeige(folien.length - 1);
});
// Wischen: waagerecht deutlich, senkrecht wenig - sonst ist es Scrollen.
let start = null;
ef.querySelector(".ef-buehne").addEventListener("touchstart", e => {
start = { x: e.touches[0].clientX, y: e.touches[0].clientY };
}, { passive: true });
ef.querySelector(".ef-buehne").addEventListener("touchend", e => {
if (!start) return;
const dx = e.changedTouches[0].clientX - start.x;
const dy = e.changedTouches[0].clientY - start.y;
start = null;
if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5) zeige(aktuell + (dx < 0 ? 1 : -1));
}, { passive: true });
// Erklärung und Punkt im Bild gehören zusammen: das eine hebt das andere hervor.
function hervorheben(folie, nr, an) {
folie.querySelectorAll('[data-marke="' + nr + '"]').forEach(el => el.classList.toggle("hell", an));
}
folien.forEach(folie => {
folie.addEventListener("mouseover", e => {
const el = e.target.closest("[data-marke]");
if (el) hervorheben(folie, el.dataset.marke, true);
});
folie.addEventListener("mouseout", e => {
const el = e.target.closest("[data-marke]");
if (el) hervorheben(folie, el.dataset.marke, false);
});
folie.addEventListener("click", e => {
const el = e.target.closest("[data-marke]");
if (!el) return;
const nr = el.dataset.marke;
folie.querySelectorAll("[data-marke]").forEach(x => x.classList.remove("hell"));
hervorheben(folie, nr, true);
// Auf dem Handy liegt das Bild über dem Text: zum Punkt scrollen.
const punkt = folie.querySelector('.ef-marke[data-marke="' + nr + '"]');
if (punkt && el.tagName === "LI") {
const r = punkt.getBoundingClientRect();
if (r.top < 60 || r.bottom > window.innerHeight) punkt.scrollIntoView({ block: "center", behavior: "smooth" });
}
});
});
// Ein Link auf eine Folie in einem schon offenen Tab ändert nur den Anker -
// die Seite lädt dann nicht neu, also hier nachziehen.
window.addEventListener("hashchange", () => {
const t = /^#folie-(\d+)$/.exec(location.hash);
if (t && Number(t[1]) - 1 !== aktuell) zeige(Number(t[1]) - 1, true);
});
const treffer = /^#folie-(\d+)$/.exec(location.hash);
zeige(treffer ? Number(treffer[1]) - 1 : 0, !treffer);
});
+452
View File
@@ -0,0 +1,452 @@
<?php
/**
* Einführung: der Smart Controller für Einsteiger, als Folien.
*
* Zwei Teile - "Bedienen im Alltag" für alle im Haus, "Einrichten und
* Verwalten" für die, die Räume, Geräte und Automatiken pflegen.
*
* Die Bilder sind echte Aufnahmen dieser Oberfläche und liegen unter
* assets/img/einfuehrung. Neu erzeugt werden sie mit
* tools/einfuehrung/aufnehmen.ps1 (Edge ohne Fenster, Plan in plan.json).
* Zu jedem Bild schreibt das Skript eine .json mit der Lage der markierten
* Elemente; daraus sitzen die nummerierten Punkte hier. Ändert sich die
* Oberfläche, genügt ein neuer Lauf - die Nummern wandern mit.
*
* Eine Folie:
* teil, kapitel Gliederung für Kopfzeile und Sprungliste
* titel, text Überschrift und Einleitung (HTML erlaubt)
* bild, form Datei in assets/img/einfuehrung, "handy" oder "breit"
* marken Nummer => Erklärung; die Lage kommt aus <bild>.json
* lage Nummer => [x, y] in Prozent, wo die .json nichts taugt
* punkte Aufzählung ohne Bildbezug
* tipp ein Satz zum Mitnehmen
*/
function einfuehrungFolien()
{
return [
// ================================================= Teil 1: Bedienen
["teil" => 1, "kapitel" => "Willkommen", "titel" => "Dein Haus auf einen Blick",
"text" => "Der Smart Controller zeigt, was im Haus passiert, und lässt dich vieles davon bedienen – vom Handy, Tablet oder Computer.",
"punkte" => [
"<b>Sehen</b>, wie viel Strom die Sonne gerade liefert und wohin er fließt.",
"<b>Bedienen</b>: Rollos, Licht, Heizung und das Laden des Autos.",
"<b>Automatiken</b> erledigen Wiederkehrendes von selbst – Rollos am Morgen, Hitzeschutz, Bewässerung.",
],
"tipp" => "Mit den Pfeilen unten oder durch Wischen blätterst du weiter. Oben kannst du direkt zu einem Kapitel springen."],
["teil" => 1, "kapitel" => "Erste Schritte", "titel" => "So findest du dich zurecht",
"text" => "Zu Hause im WLAN öffnet sich die Seite ohne Anmeldung. Von unterwegs meldest du dich mit einem <b>Passkey</b> an – das ist dein Fingerabdruck oder dein Gesicht am Handy, kein Passwort.",
"bild" => "menue.jpg", "form" => "handy",
"marken" => [
4 => "Das <b>Menü</b> öffnest du mit den drei Strichen oben links.",
1 => "<b>Solar</b>: Strom, Speicher, Heizung und Auto im Überblick.",
2 => "<b>Home</b>: dein Grundriss – hier bedienst du die Räume. Darunter die Etagen.",
3 => "<b>Einstellungen</b>: für die, die das System pflegen (Teil 2).",
]],
["teil" => 1, "kapitel" => "Räume bedienen", "titel" => "Die Startseite: dein Grundriss",
"text" => "Unter <b>Home</b> siehst du jede Etage als Bild. Die kleinen Kacheln stehen in den Räumen und zeigen, was dort gerade los ist.",
"bild" => "home.jpg", "form" => "handy",
"marken" => [
1 => "Die <b>Kreise links</b> wechseln die Etage.",
2 => "Eine <b>Kachel</b> zeigt groß die Raumtemperatur, klein darüber die gewünschte, darunter die Luftfeuchte. Eine kleine Flamme heißt: die Heizung läuft.",
3 => "<b>Tippe auf eine Kachel</b>, um den Raum zu öffnen.",
]],
["teil" => 1, "kapitel" => "Räume bedienen", "titel" => "Rollos und Jalousien",
"text" => "Im geöffneten Raum liegt alles, was dort bedienbar ist – nach Art sortiert in Reitern.",
"bild" => "raum_beschattung.jpg", "form" => "breit",
"marken" => [
1 => "Die <b>Reiter</b>: Beschattung, Heizung, Licht – je nachdem, was im Raum hängt.",
2 => "<b>Hoch, Stopp, Runter</b>: fährt ganz auf oder zu.",
3 => "Mit den <b>Reglern</b> stellst du eine Höhe ein, bei Jalousien auch die Neigung der Lamellen. Losgelassen fährt sie hin.",
],
"tipp" => "Das Bild neben den Knöpfen zeigt, wie die Jalousie gerade steht – während der Fahrt bewegt es sich mit."],
["teil" => 1, "kapitel" => "Räume bedienen", "titel" => "Die Heizung eines Raums",
"text" => "Im Reiter <b>Heizung</b> stellst du ein, wie warm es werden soll.",
"bild" => "raum_heizung.jpg", "form" => "handy",
"marken" => [
1 => "Den <b>Regler</b> ziehen – beim Loslassen geht die neue Solltemperatur an das Thermostat.",
2 => "Darunter die <b>gemessene Temperatur</b> und Luftfeuchte im Raum.",
]],
["teil" => 1, "kapitel" => "Räume bedienen", "titel" => "Licht",
"text" => "Lampen schaltest du im Reiter <b>Licht</b> ein und aus. Dimmbare Lampen haben einen Helligkeitsregler, farbige zusätzlich Farbe und Effekt.",
"bild" => "raum_licht.jpg", "form" => "handy",
"marken" => [
1 => "Jede Lampe hat ihre eigene Karte: <b>Schalter</b>, <b>Farbe</b>, <b>Helligkeit</b> und <b>Effekt</b>.",
]],
["teil" => 1, "kapitel" => "Automatiken", "titel" => "Der Tag auf einen Blick",
"text" => "Unter dem Grundriss stehen die <b>Automatismen</b>. Jede Zeile ist eine Art Gerät – Licht, Beschattung, Bewässerung – und von links nach rechts läuft der Tag.",
"bild" => "zeitleiste.jpg", "form" => "handy",
"marken" => [
1 => "Mit den <b>Pfeilen</b> schaust du dir andere Tage an – etwa, was am Wochenende passiert.",
2 => "<b>Etagen</b> ein- und ausblenden.",
3 => "Hier steht, dass <b>alle</b> Automatiken angezeigt werden – keine geht verloren.",
4 => "Die gelbe Linie ist <b>jetzt</b>.",
5 => "Ein <b>Haken</b> heißt: heute schon gelaufen, mit Uhrzeit.",
],
"punkte" => [
"<b>Punkt</b>: passiert zu einer festen Uhrzeit.",
"<b>Balken</b>: darf in diesem Zeitraum passieren, sobald eine Bedingung erfüllt ist – zum Beispiel starke Sonne.",
"<b>Gestrichelte Linie</b>: die untere Automatik startet nach der oberen.",
"<b>Kalender-Symbol</b>: an diesem Tag nicht dran, etwa am Wochenende.",
]],
["teil" => 1, "kapitel" => "Automatiken", "titel" => "Eine Automatik antippen",
"text" => "Ein Tipp auf einen Eintrag öffnet eine kleine Karte: was die Automatik tut, wann, und wie sie heute steht.",
"bild" => "zeitleiste_karte.jpg", "form" => "handy",
"marken" => [
1 => "Der <b>Stand</b> von heute – gelaufen, kommt noch, oder warum nicht.",
2 => "<b>Bearbeiten</b> öffnet alle Einstellungen (Teil 2).",
3 => "<b>Pausieren</b> hält die Automatik an, bis du sie wieder <b>fortsetzt</b>. Pausierte stehen ganz unten.",
4 => "<b>Löschen</b> fragt vorher nach.",
],
"tipp" => "Pausieren ist fast immer besser als Löschen – zum Beispiel für den Urlaub."],
["teil" => 1, "kapitel" => "Energie", "titel" => "Wohin fließt der Strom?",
"text" => "Die Seite <b>Solar</b> zeigt den Strom im Haus als Bild. Die Linien zwischen den Kreisen werden dicker, je mehr Strom fließt.",
"bild" => "solar_fluss.jpg", "form" => "breit",
"lage" => [1 => [31.6, 32.3], 2 => [27.7, 52.2], 3 => [31.4, 72.1], 4 => [43.9, 52.2], 5 => [59.9, 52.2],
6 => [76.9, 52.2], 7 => [81.6, 75], 8 => [81.6, 27.6], 9 => [67.1, 27.2], 10 => [67.3, 75],
11 => [51.7, 75], 12 => [48.8, 27.6]],
"marken" => [
1 => "<b>Sonne</b>: was die Solaranlage gerade liefert.",
2 => "<b>Stromnetz</b>: minus heißt, wir speisen ein; plus heißt, wir kaufen zu.",
3 => "<b>Hausbatterie</b>: lädt oder entlädt, darunter der Ladestand.",
4 => "<b>Wechselrichter</b>: Autarkie sagt, wie viel des Stroms gerade selbst erzeugt ist.",
5 => "<b>Verbrauch</b> im ganzen Haus.",
6 => "<b>Heizstab</b> im Wasserspeicher – nutzt Sonnenstrom zum Heizen.",
7 => "<b>Wallbox EG</b> mit Ladestand des Autos.",
8 => "<b>Wallbox OG</b>.",
9 => "Verbrauch <b>Obergeschoss</b>.",
10 => "Verbrauch <b>Erdgeschoss</b>.",
11 => "Verbrauch <b>Untergeschoss</b>.",
12 => "<b>Wasser</b>: Zisterne und Gartenbewässerung.",
],
"tipp" => "Auf die Symbole von Auto, Heizstab und Wasser kannst du tippen – dann öffnet sich ihre Steuerung."],
["teil" => 1, "kapitel" => "Energie", "titel" => "Das Auto laden",
"text" => "Ein Tipp auf das Auto-Symbol öffnet die <b>Wallbox</b>.",
"bild" => "wallbox.jpg", "form" => "handy",
"punkte" => [
"<b>Laden starten</b> bzw. stoppen.",
"<b>Immer laden</b>: lädt sofort, notfalls mit Strom aus dem Netz.",
"<b>Überschuss</b>: lädt nur mit Sonnenstrom, der sonst eingespeist würde.",
"<b>Ladeplanung</b>: sag, wie viel Prozent bis wann nötig sind – das System verteilt die Ladung möglichst in die Sonnenstunden.",
"<b>Max. Ladestrom</b> begrenzt, wie schnell geladen wird.",
]],
["teil" => 1, "kapitel" => "Energie", "titel" => "Heizstab und Gartenwasser",
"text" => "Auch Heizstab und Wasser haben eigene Steuerungen, erreichbar über ihre Symbole auf der Solarseite.",
"bild" => "bewaesserung.jpg", "form" => "handy",
"punkte" => [
"<b>Heizstab</b>: Automatik heißt, er heizt mit Überschuss; von Hand lässt sich eine Leistung vorgeben.",
"<b>Bewässerung</b>: jede Zone mit Minuten und einem Knopf zum Starten. Darunter steht, wann zuletzt gegossen wurde.",
"Die Bewässerung am Morgen läuft ohnehin automatisch – sie lässt aus, wenn es geregnet hat.",
]],
["teil" => 1, "kapitel" => "Energie", "titel" => "Ertrag und Kosten dieses Jahr",
"text" => "Weiter unten auf der Solarseite rechnet die <b>Statistik</b> zusammen, was die Anlage bisher gebracht hat.",
"bild" => "solar_statistik.jpg", "form" => "handy",
"punkte" => [
"<b>Ertrag der Anlage</b>: gesparte Stromkosten plus Einspeisevergütung.",
"<b>Netzkosten</b>: was trotzdem an Strom gekauft wurde.",
"<b>Autarkie</b>: wie viel des Verbrauchs selbst erzeugt war.",
"Die kleinen Pfeile vergleichen mit demselben Zeitraum im Vorjahr.",
]],
["teil" => 1, "kapitel" => "Energie", "titel" => "Der Tagesverlauf",
"text" => "Ganz unten auf der Solarseite zeigen zwei Diagramme den heutigen Tag: <b>Verbrauch</b> und <b>Erzeugung</b>.",
"bild" => "solar_verlauf.jpg", "form" => "handy",
"punkte" => [
"Die Farben stehen für die einzelnen Verbraucher bzw. Quellen – die Legende steht darunter.",
"Mit den Knöpfen oben am Diagramm blätterst du zu anderen Tagen.",
]],
["teil" => 1, "kapitel" => "Energie", "titel" => "Historie: Monate und Jahre",
"text" => "Die Seite <b>Historie</b> zeigt Verbrauch und Erzeugung über längere Zeiträume – den letzten Monat, die letzten 13 Monate, mehrere Jahre.",
"bild" => "historie.jpg", "form" => "handy",
"punkte" => [
"Mit der Lupe vergrößerst du einen Ausschnitt.",
"Weiter unten steht der <b>Jahresvergleich</b>: dieselben Kennzahlen Jahr neben Jahr.",
]],
["teil" => 1, "kapitel" => "Energie", "titel" => "Jahre vergleichen",
"text" => "Der <b>Jahresvergleich</b> stellt Ertrag, Netzkosten und Autarkie der Jahre nebeneinander.",
"bild" => "jahresvergleich.jpg", "form" => "handy",
"punkte" => [
"<b>Gleicher Zeitraum</b> vergleicht bis zum heutigen Datum – fair, auch mitten im Jahr.",
"<b>Ganze Jahre</b> vergleicht abgeschlossene Jahre.",
"Mit <b>Monatsverlauf zeigen</b> siehst du kleine Kurven je Monat.",
]],
["teil" => 1, "kapitel" => "Haus und Wetter", "titel" => "Heizung und Warmwasser",
"text" => "Die Seite <b>Heizung</b> zeigt den Wasserspeicher mit seinen Temperaturen oben, in der Mitte und unten, darunter den Verlauf.",
"bild" => "heizung.jpg", "form" => "handy",
"punkte" => [
"Oben heiß, unten kühler – das ist normal und gewollt.",
"Weiter unten steht auch der <b>Wasserverbrauch</b>.",
]],
["teil" => 1, "kapitel" => "Haus und Wetter", "titel" => "Wetter",
"text" => "Die Seite <b>Wetter</b> zeigt die Werte der eigenen Wetterstation und die Vorhersage für die nächsten Tage.",
"bild" => "wetter.jpg", "form" => "handy",
"punkte" => [
"Oben: Temperatur, Feuchte, Wind, Böen und Luftdruck – gemessen direkt am Haus.",
"Darunter die Vorhersage mit Temperatur, Regen und Wind.",
]],
["teil" => 1, "kapitel" => "Das Auto", "titel" => "Das Auto",
"text" => "Die Seite <b>Škoda</b> zeigt den Zustand des Autos: Ladestand, Reichweite, ob es verriegelt und angesteckt ist.",
"bild" => "skoda.jpg", "form" => "handy",
"punkte" => [
"Tippe auf den <b>Ladeanschluss</b>, um das Laden zu starten oder zu stoppen.",
"Tippe auf den <b>Innenraum</b>, um die Klimatisierung zu starten.",
"Der Stand oben sagt, wie aktuell die Werte sind – das Auto meldet sich nicht ständig.",
]],
["teil" => 1, "kapitel" => "Das Auto", "titel" => "Ladungen und Akku",
"text" => "Weiter unten stehen die letzten <b>Ladungen</b> mit Menge und Ladestand vorher und nachher, und wie gesund der Akku ist.",
"bild" => "skoda_ladungen.jpg", "form" => "handy"],
["teil" => 1, "kapitel" => "Hilfe", "titel" => "Wenn etwas nicht klappt",
"punkte" => [
"<b>Striche statt Zahlen</b>: Die Werte kommen gleich – kurz warten oder die Seite neu laden. Bleibt es so, meldet das Gerät gerade nichts.",
"<b>Rollo reagiert nicht</b>: Einmal Stopp drücken und erneut versuchen. Bei Frost fahren Jalousien absichtlich nicht.",
"<b>Automatik lief nicht</b>: Eintrag antippen – der Stand sagt, ob sie heute dran war oder auf eine Bedingung wartet.",
"<b>Etwas ist ganz falsch eingestellt</b>: Automatik lieber pausieren als löschen und Bescheid geben.",
],
"tipp" => "Du kannst nichts kaputtmachen: jede Automatik lässt sich pausieren, jedes Gerät auch von Hand bedienen."],
// ============================================== Teil 2: Einrichten
["teil" => 2, "kapitel" => "Einstellungen", "titel" => "Einrichten und Verwalten",
"text" => "Dieser Teil ist für die, die das System pflegen: Preise, Grundriss, Kacheln, Geräte und Automatiken. Alles davon geht ohne Programmieren, unter <b>Einstellungen</b>.",
"bild" => "einst_preise.jpg", "form" => "breit",
"marken" => [
1 => "Die <b>Reiter</b>: Preise, Grundriss, Home-Kacheln, Geräte, Fahrzeug.",
2 => "<b>Preise</b>: trage bei einem neuen Stromvertrag einen neuen Zeitraum ein – die alten bleiben für die Statistik stehen.",
3 => "<b>Speichern</b> nicht vergessen.",
]],
["teil" => 2, "kapitel" => "Grundriss", "titel" => "Etagen und Räume",
"text" => "Im Reiter <b>Grundriss</b> legst du fest, welche Etagen es gibt, welches Bild sie zeigen und wo die Kacheln der Räume sitzen.",
"bild" => "einst_grundriss.jpg", "form" => "breit",
"lage" => [3 => [91, 42.5]],
"marken" => [
1 => "<b>Etage wählen</b> oder mit „+ Etage“ eine neue anlegen.",
2 => "<b>Kacheln ziehen</b>, um sie zu verschieben. Ein Tipp wählt den Raum.",
3 => "Ein <b>Grundrissbild</b> hochladen – ein Foto oder eine Zeichnung der Etage.",
4 => "<b>Neuen Raum</b> anlegen. Einem Raum ohne Kachel gibst du mit „Platzieren“ und einem Klick auf den Plan einen Platz.",
],
"tipp" => "Den Thermostat-Zweig eines Raums schlägt die Liste vor – er sorgt für Heizsymbol und Temperaturregler."],
["teil" => 2, "kapitel" => "Grundriss", "titel" => "Was auf einer Kachel steht",
"text" => "Im Reiter <b>Home-Kacheln</b> wählst du bis zu drei Werte je Kachel.",
"bild" => "einst_kacheln.jpg", "form" => "breit",
"marken" => [
1 => "Eine <b>Kachel aufklappen</b>.",
2 => "Für jeden Wert: <b>Gerät und Messwert</b> wählen, dazu Einheit, Symbol und ob er groß stehen soll.",
3 => "<b>Wert hinzufügen</b> – höchstens drei.",
4 => "<b>Speichern</b>. „Standard“ nimmt deine Auswahl wieder zurück.",
]],
["teil" => 2, "kapitel" => "Geräte", "titel" => "Geräte den Räumen zuordnen",
"text" => "Neue Geräte findet der Suchlauf von selbst. In welchem Raum sie hängen, legst du im Reiter <b>Geräte</b> fest – danach erscheinen sie im Raum.",
"bild" => "einst_geraete.jpg", "form" => "breit",
"marken" => [
1 => "<b>Suchen</b> nach Name, Art oder Raum.",
2 => "Den <b>Raum</b> wählen – gespeichert wird sofort.",
3 => "Ein abgebautes Gerät <b>entfernen</b>. Steckt es noch in einer Automatik, geht das nicht.",
],
"tipp" => "Thermostate verraten ihren Raum selbst – oben erscheint dann „Vorschläge übernehmen“."],
["teil" => 2, "kapitel" => "Automatik anlegen", "titel" => "Eine Automatik – 1. Wenn",
"text" => "Eine neue Automatik legst du mit dem grünen <b>+</b> bei den Automatismen an, eine vorhandene mit „Bearbeiten“. Sie besteht aus drei Teilen: <b>Wenn</b>, <b>Wann</b>, <b>Dann</b>.",
"bild" => "editor_wenn.jpg", "form" => "breit",
"marken" => [
1 => "Ein <b>Name</b>, an dem du sie wiedererkennst.",
2 => "<b>Wenn</b>: die Bedingungen. Alle in einem Kasten müssen zutreffen – eine Uhrzeit, ein Sonnenstand, ein Messwert wie Helligkeit oder Temperatur.",
3 => "Hier siehst du, <b>wie viele gerade zutreffen</b>; neben jeder Bedingung steht der aktuelle Wert.",
4 => "<b>Oder stattdessen</b>: ein zweiter Kasten, der ebenfalls auslöst.",
],
"tipp" => "Ausgelöst wird in dem Moment, in dem die Bedingung erfüllt wird – nicht immer wieder, solange sie erfüllt bleibt."],
["teil" => 2, "kapitel" => "Automatik anlegen", "titel" => "Eine Automatik – 2. Wann",
"text" => "<b>Wann</b> legt fest, an welchen Tagen und zu welchen Zeiten die Automatik überhaupt aktiv ist.",
"bild" => "editor_wann.jpg", "form" => "breit",
"marken" => [
1 => "<b>Vorlagen</b>: täglich, werktags (ohne Feiertage), Wochenende (mit Feiertagen).",
2 => "Oder die <b>Wochentage</b> einzeln wählen.",
3 => "<b>Ferien und Feiertage</b>: nie, egal oder immer.",
4 => "<b>Am Vorabend</b>: die Tage gelten für morgen – „Kinderrollos zu, wenn morgen Schule ist“.",
5 => "Dieser <b>Satz</b> fasst zusammen, was dabei herauskommt.",
],
"punkte" => [
"<b>Nur einmal am Tag</b>: danach ist bis Mitternacht Ruhe – gut für Rollos, die du danach von Hand verstellst.",
"<b>Sperre</b>: Mindestabstand zwischen zwei Auslösungen, gegen Hin und Her bei wechselnder Sonne.",
]],
["teil" => 2, "kapitel" => "Automatik anlegen", "titel" => "Eine Automatik – 3. Dann",
"text" => "<b>Dann</b> sagt, was passieren soll.",
"bild" => "editor_dann.jpg", "form" => "breit",
"marken" => [
1 => "Je Zeile ein <b>Gerät und ein Befehl</b>, etwa „Wohnzimmer Fensterfront: Auf“ oder eine Position mit Neigung.",
2 => "<b>Und noch etwas tun</b>: weitere Geräte in derselben Automatik.",
3 => "<b>Speichern</b> – danach steht die Automatik in der Zeitleiste.",
]],
["teil" => 2, "kapitel" => "Automatik anlegen", "titel" => "Automatiken hintereinander",
"text" => "Eine Automatik kann eine andere starten. Wähle dazu im <b>Wenn</b> das Gerät <b>„Automatiken“</b> und darin die auslösende.",
"punkte" => [
"Beispiel: <b>Wecker</b> um 05:50 → <b>10 Minuten danach</b>, wenn es hell ist, gehen die Rollos auf.",
"In der Zeitleiste verbindet eine <b>gestrichelte Linie</b> die beiden.",
"Wird die erste pausiert, bleibt auch die zweite still.",
"Beim Löschen der ersten wirst du gefragt, was mit der zweiten geschehen soll.",
]],
["teil" => 2, "kapitel" => "Fahrzeug und Protokolle", "titel" => "Zugang zum Auto",
"text" => "Das Auto wird über Schlüssel aus der <b>MyŠkoda-App</b> abgefragt. Sie laufen nach einiger Zeit ab und müssen dann erneuert werden – im Reiter <b>Fahrzeug</b>.",
"bild" => "einst_fahrzeug.jpg", "form" => "breit",
"punkte" => [
"Den neuen Schlüssel in der App erzeugen, hier einfügen, speichern – er wird sofort ausprobiert.",
"Aus Sicherheitsgründen zeigt die Seite nie einen ganzen Schlüssel.",
]],
["teil" => 2, "kapitel" => "Fahrzeug und Protokolle", "titel" => "Protokolle",
"text" => "Unter <b>Protokolle</b> stehen die Aufzeichnungen der Hintergrundprogramme – hilfreich, wenn etwas nicht so läuft wie erwartet.",
"bild" => "protokolle.jpg", "form" => "breit",
"punkte" => [
"Links das <b>Programm</b> wählen, zum Beispiel den Automatik-Runner.",
"Oben nach <b>Text</b> oder nur nach <b>Warnungen</b> filtern.",
"Gelb und rot markierte Zeilen sind Warnungen und Fehler.",
]],
["teil" => 2, "kapitel" => "Geschafft", "titel" => "Das Wichtigste in Kürze",
"punkte" => [
"<b>Home</b> → Kachel antippen → Raum bedienen.",
"<b>Automatismen</b>: Eintrag antippen → pausieren, fortsetzen, bearbeiten.",
"<b>Solar</b>: Stromfluss sehen, Auto, Heizstab und Wasser über ihre Symbole steuern.",
"<b>Einstellungen</b>: Grundriss, Kacheln, Geräte, Preise und Fahrzeug pflegen.",
],
"tipp" => "Diese Einführung findest du jederzeit im Menü unter „Einführung“."],
];
}
$folien = einfuehrungFolien();
$bildOrdner = __DIR__ . "/../assets/img/einfuehrung/";
foreach ($folien as $i => $f) {
if (empty($f["bild"]) || empty($f["marken"])) {
continue;
}
$lage = [];
$json = @file_get_contents($bildOrdner . pathinfo($f["bild"], PATHINFO_FILENAME) . ".json");
$gemessen = $json ? json_decode(ltrim($json, "\xEF\xBB\xBF"), true) : [];
foreach ($f["marken"] as $nr => $text) {
$p = $f["lage"][$nr] ?? null;
if (!$p && isset($gemessen[$nr]) && is_array($gemessen[$nr])) {
$x = $gemessen[$nr]["x"];
$y = $gemessen[$nr]["y"];
// Unsichtbare (0/0) oder ausserhalb des Bildes liegende Elemente
// bekommen keinen Punkt - die Erklärung steht trotzdem daneben.
if (($x > 0 || $y > 0) && $y >= 0 && $y <= 100 && $x >= 0 && $x <= 100) {
$p = [$x, $y];
}
}
$lage[$nr] = $p;
}
$folien[$i]["lage"] = $lage;
}
$kapitel = [];
foreach ($folien as $i => $f) {
if (!isset($kapitel[$f["teil"] . "|" . $f["kapitel"]])) {
$kapitel[$f["teil"] . "|" . $f["kapitel"]] = ["teil" => $f["teil"], "name" => $f["kapitel"], "folie" => $i];
}
}
$teile = [1 => "Bedienen im Alltag", 2 => "Einrichten und Verwalten"];
$version = @filemtime($bildOrdner . "home.jpg") ?: 1;
?>
<main class="app-main">
<div class="app-content-header">
<div class="container-fluid">
<h3 class="mb-0">Einführung</h3>
</div>
</div>
<div class="app-content">
<div class="container-fluid">
<div class="ef" id="einfuehrung" tabindex="-1">
<div class="ef-kopf">
<div class="ef-teile" role="tablist">
<?php foreach ($teile as $nr => $name): ?>
<button type="button" class="ef-teil" data-teil="<?= $nr ?>">
<span class="ef-teil-nr"><?= $nr ?></span><?= htmlspecialchars($name) ?></button>
<?php endforeach; ?>
</div>
<select class="form-select form-select-sm ef-kapitel" aria-label="Kapitel">
<?php foreach ($teile as $nr => $name): ?>
<optgroup label="Teil <?= $nr ?> · <?= htmlspecialchars($name) ?>">
<?php foreach ($kapitel as $k): if ($k["teil"] !== $nr) continue; ?>
<option value="<?= $k["folie"] ?>"><?= htmlspecialchars($k["name"]) ?></option>
<?php endforeach; ?>
</optgroup>
<?php endforeach; ?>
</select>
<span class="ef-zaehler"></span>
</div>
<div class="ef-fortschritt"><span></span></div>
<div class="ef-buehne">
<?php foreach ($folien as $i => $f): $mitBild = !empty($f["bild"]); ?>
<section class="ef-folie<?= $mitBild ? " mit-bild form-" . htmlspecialchars($f["form"] ?? "breit") : " ohne-bild" ?>"
data-nr="<?= $i ?>" data-teil="<?= $f["teil"] ?>" hidden aria-roledescription="Folie">
<?php if ($mitBild): ?>
<figure class="ef-bild">
<div class="ef-rahmen">
<img src="assets/img/einfuehrung/<?= htmlspecialchars($f["bild"]) ?>?v=<?= $version ?>"
alt="<?= htmlspecialchars(strip_tags($f["titel"])) ?>" loading="<?= $i < 2 ? "eager" : "lazy" ?>">
<?php foreach ($f["lage"] ?? [] as $nr => $p): if (!$p) continue; ?>
<span class="ef-marke" data-marke="<?= $nr ?>" style="left:<?= $p[0] ?>%;top:<?= $p[1] ?>%"><?= $nr ?></span>
<?php endforeach; ?>
</div>
</figure>
<?php endif; ?>
<div class="ef-text">
<div class="ef-kapitelname">Teil <?= $f["teil"] ?> · <?= htmlspecialchars($f["kapitel"]) ?></div>
<h2 class="ef-titel"><?= htmlspecialchars($f["titel"]) ?></h2>
<?php if (!empty($f["text"])): ?>
<p class="ef-einleitung"><?= $f["text"] ?></p>
<?php endif; ?>
<?php if (!empty($f["marken"])): ?>
<ol class="ef-marken">
<?php foreach ($f["marken"] as $nr => $text): ?>
<li data-marke="<?= $nr ?>"><span class="ef-marke-nr"><?= $nr ?></span><span><?= $text ?></span></li>
<?php endforeach; ?>
</ol>
<?php endif; ?>
<?php if (!empty($f["punkte"])): ?>
<ul class="ef-punkte">
<?php foreach ($f["punkte"] as $p): ?>
<li><?= $p ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if (!empty($f["tipp"])): ?>
<p class="ef-tipp"><i class="bi bi-lightbulb"></i><span><?= $f["tipp"] ?></span></p>
<?php endif; ?>
</div>
</section>
<?php endforeach; ?>
</div>
<div class="ef-fuss">
<button type="button" class="btn btn-outline-secondary ef-zurueck"><i class="bi bi-arrow-left me-1"></i>Zurück</button>
<div class="ef-punkte-nav" aria-hidden="true"></div>
<button type="button" class="btn btn-primary ef-weiter">Weiter<i class="bi bi-arrow-right ms-1"></i></button>
</div>
</div>
</div>
</div>
</main>
+8
View File
@@ -199,6 +199,14 @@ if (!isset($_GET["floor"]))
<p>Protokolle</p>
</a>
</li>
<!-- Die Einführung für Einsteiger - Folien zum
Durchklicken, restricted/einfuehrung.php. -->
<li class="nav-item">
<a href="?action=einfuehrung" class="nav-link<?php if($_GET["action"]=="einfuehrung"){echo " active";} ?>">
<i class="nav-icon bi bi-mortarboard"></i>
<p>Einführung</p>
</a>
</li>
</ul>
<!--end::Sidebar Menu-->
</nav>
+98
View File
@@ -0,0 +1,98 @@
param(
[string]$Plan = "$PSScriptRoot\plan.json",
[string]$Ausgabe = "$PSScriptRoot\bilder"
)
# Edge ohne Fenster über das DevTools-Protokoll steuern: laden, echt warten,
# optional JavaScript ausführen (klicken, scrollen), Screenshot speichern.
$ErrorActionPreference = "Stop"
New-Item -ItemType Directory -Force $Ausgabe | Out-Null
$edge = "${env:ProgramFiles(x86)}\Microsoft\Edge\Application\msedge.exe"
$profil = Join-Path $env:TEMP ("edge-cdp-" + [guid]::NewGuid())
$port = 9333
$proc = Start-Process $edge -ArgumentList @("--headless=new", "--disable-gpu", "--hide-scrollbars",
"--remote-debugging-port=$port", "--user-data-dir=$profil", "--no-first-run", "about:blank") -PassThru
function Warte-Auf-Port {
for ($i = 0; $i -lt 40; $i++) {
try { return Invoke-RestMethod "http://127.0.0.1:$port/json/list" } catch { Start-Sleep -Milliseconds 250 }
}
throw "Edge antwortet nicht"
}
$script:id = 0
function Sende($ws, $methode, $parameter) {
$script:id++
$nachricht = @{ id = $script:id; method = $methode; params = $parameter } | ConvertTo-Json -Depth 20 -Compress
$bytes = [Text.Encoding]::UTF8.GetBytes($nachricht)
$ws.SendAsync([ArraySegment[byte]]$bytes, [Net.WebSockets.WebSocketMessageType]::Text, $true, [Threading.CancellationToken]::None).Wait()
$puffer = New-Object byte[] (1024 * 1024)
while ($true) {
$ms = New-Object IO.MemoryStream
do {
$teil = $ws.ReceiveAsync([ArraySegment[byte]]$puffer, [Threading.CancellationToken]::None)
$teil.Wait()
$ms.Write($puffer, 0, $teil.Result.Count)
} while (-not $teil.Result.EndOfMessage)
$antwort = [Text.Encoding]::UTF8.GetString($ms.ToArray()) | ConvertFrom-Json
if ($antwort.id -eq $script:id) { return $antwort }
}
}
try {
$ziele = Warte-Auf-Port
$seite = $ziele | Where-Object { $_.type -eq "page" } | Select-Object -First 1
$ws = New-Object Net.WebSockets.ClientWebSocket
$ws.Options.KeepAliveInterval = [TimeSpan]::FromSeconds(20)
$ws.ConnectAsync([Uri]$seite.webSocketDebuggerUrl, [Threading.CancellationToken]::None).Wait()
Sende $ws "Page.enable" @{} | Out-Null
Sende $ws "Runtime.enable" @{} | Out-Null
$schritte = Get-Content $Plan -Raw -Encoding UTF8 | ConvertFrom-Json
$breit = 1280; $hoch = 900; $handy = $false
foreach ($s in $schritte) {
# Ein Schritt mit eigener Adresse legt die Größe fest; Schritte ohne
# Adresse (klicken, scrollen) behalten die des vorigen.
if ($s.url) {
$breit = if ($s.breit) { $s.breit } else { 1280 }
$hoch = if ($s.hoch) { $s.hoch } else { 900 }
$handy = [bool]$s.handy
}
$em = Sende $ws "Emulation.setDeviceMetricsOverride" @{ width = [int]$breit; height = [int]$hoch; deviceScaleFactor = $(if ($handy) { 2 } else { 1 }); mobile = $handy }
if ($em.error) { Write-Output ("Emulation: " + ($em.error | ConvertTo-Json -Compress)) }
if ($s.url) {
Sende $ws "Page.navigate" @{ url = $s.url } | Out-Null
Start-Sleep -Seconds $(if ($s.warte) { $s.warte } else { 6 })
}
# Einstellungsreiter per Klick öffnen - ein Anker in der Adresse greift
# nicht, wenn nur er sich ändert.
if ($s.vorher) {
Sende $ws "Runtime.evaluate" @{ expression = "new bootstrap.Tab(document.getElementById('einstellung-$($s.vorher)-tab')).show()" } | Out-Null
Start-Sleep -Seconds 2
}
if ($s.js) {
$r = Sende $ws "Runtime.evaluate" @{ expression = $s.js; awaitPromise = $true; returnByValue = $true }
if ($r.result.exceptionDetails) { Write-Output ("JS-Fehler in " + $s.datei + ": " + $r.result.exceptionDetails.exception.description) }
if ($s.zeige) { Write-Output ("JS " + $s.datei + ": " + ($r.result.result.value | ConvertTo-Json -Compress)) }
Start-Sleep -Seconds $(if ($s.danach) { $s.danach } else { 2 })
}
if ($s.datei) {
# Lage der markierten Elemente in Prozent des Bildausschnitts - daraus
# setzt die Einführung ihre nummerierten Punkte.
if ($s.marken) {
$sel = $s.marken | ConvertTo-Json -Compress
$ausdruck = "(() => { const m = $sel; const aus = {}; for (const k in m) { const el = document.querySelector(m[k]); if (!el) { aus[k] = null; continue; } const r = el.getBoundingClientRect(); aus[k] = { x: +((r.left + r.width / 2) / innerWidth * 100).toFixed(1), y: +((r.top + r.height / 2) / innerHeight * 100).toFixed(1) }; } return JSON.stringify(aus); })()"
$r = Sende $ws "Runtime.evaluate" @{ expression = $ausdruck; returnByValue = $true }
$name = [IO.Path]::GetFileNameWithoutExtension($s.datei)
[IO.File]::WriteAllText((Join-Path $Ausgabe "$name.json"), $r.result.result.value, [Text.Encoding]::UTF8)
if ($r.result.result.value -match "null") { Write-Output ("Marke fehlt in " + $s.datei + ": " + $r.result.result.value) }
}
$bild = Sende $ws "Page.captureScreenshot" @{ format = "jpeg"; quality = 82 }
[IO.File]::WriteAllBytes((Join-Path $Ausgabe $s.datei), [Convert]::FromBase64String($bild.result.data))
Write-Output ("ok " + $s.datei)
}
}
} finally {
if ($proc -and -not $proc.HasExited) { Stop-Process -Id $proc.Id -Force }
Get-Process msedge -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like "*$profil*" } | Stop-Process -Force -ErrorAction SilentlyContinue
}
+310
View File
@@ -0,0 +1,310 @@
[
{
"url": "http://nas.fritz.box/smart/index.php?action=solar",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 9,
"js": "document.querySelector(\u0027[data-lte-toggle=\"sidebar\"]\u0027).click()",
"danach": 2,
"datei": "menue.jpg",
"marken": {
"1": "a[href=\u0027?action=solar\u0027]",
"2": "a[href=\u0027?action=home\u0026floor=EG\u0027]",
"3": "a[href=\u0027?action=settings\u0027]",
"4": "[data-lte-toggle=\u0027sidebar\u0027]"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=home\u0026floor=EG",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 10,
"js": "window.scrollTo({top:60, behavior:\u0027instant\u0027})",
"danach": 1,
"datei": "home.jpg",
"marken": {
"1": "g#UG.floorBtn circle",
"2": "g[onclick=\u0027openRoomView(8);\u0027] rect",
"3": "g[onclick=\u0027openRoomView(12);\u0027] rect"
}
},
{
"js": "openRoomView(8)",
"danach": 6,
"datei": "raum_beschattung.jpg",
"marken": {
"1": "#roomView .nav-tabs",
"2": "#roomView .raum-geraet button",
"3": "#roomView .raum-geraet input[type=range]"
}
},
{
"js": "document.getElementById(\u0027raumtab-heizung-tab\u0027).click()",
"danach": 2,
"datei": "raum_heizung.jpg",
"marken": {
"1": "#modal-slider",
"2": "#roomView [data-mqtt=\u0027Temp[degC]\u0027]"
}
},
{
"js": "document.getElementById(\u0027raumtab-licht-tab\u0027).click()",
"danach": 2,
"datei": "raum_licht.jpg",
"marken": {
"1": "#roomView .tab-pane.active .raum-geraet"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=home\u0026floor=EG",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 10,
"js": "localStorage.clear(); zlAnsicht(\u0027zeitleiste\u0027); document.getElementById(\u0027zeitleiste\u0027).closest(\u0027.card\u0027).scrollIntoView({block:\u0027start\u0027, behavior:\u0027instant\u0027}); window.scrollBy({top:-70, behavior:\u0027instant\u0027})",
"danach": 3,
"datei": "zeitleiste.jpg",
"marken": {
"1": ".zl-tag",
"2": ".zl-etagen",
"3": ".zl-zahl",
"4": ".zl-jetzt-marke",
"5": ".zl-ding.gelaufen .zl-etikett"
}
},
{
"js": "zeitleisteLaden().then(() =\u003e new Promise(r =\u003e setTimeout(r, 800))).then(() =\u003e { const el = document.querySelector(\u0027.zl-ding[data-id=\"19\"]\u0027); el.scrollIntoView({block:\u0027center\u0027, behavior:\u0027instant\u0027}); el.click(); })",
"danach": 3,
"datei": "zeitleiste_karte.jpg",
"marken": {
"1": ".zl-karte-stand",
"2": ".zl-karte [data-tun=\u0027bearbeiten\u0027]",
"3": ".zl-karte [data-tun=\u0027umschalten\u0027]",
"4": ".zl-karte [data-tun=\u0027loeschen\u0027]"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=solar",
"breit": 1280,
"hoch": 780,
"warte": 12,
"js": "document.querySelectorAll(\u0027.card-title\u0027).forEach(t =\u003e { if (t.textContent.includes(\u0027Forecast\u0027)) { const b = t.closest(\u0027.card\u0027).querySelector(\u0027.card-body\u0027); b.style.position = \u0027relative\u0027; const d = document.createElement(\u0027div\u0027); d.style.cssText = \u0027position:absolute;left:0;right:0;top:0;height:70px;background:#1f2326;z-index:5\u0027; b.appendChild(d); } })",
"danach": 1,
"datei": "solar_fluss.jpg",
"marken": {
"1": "#pvText",
"2": "#gridText",
"3": "#batText",
"4": "#genText",
"5": "#consumerText",
"6": "#heatText",
"7": "#evText",
"8": "#evTextOG",
"9": "#ogText",
"10": "#egText",
"11": "#ugText",
"12": "#waterState"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=solar",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 12,
"js": "document.querySelectorAll(\u0027.card-title\u0027).forEach(t =\u003e { if (t.textContent.includes(\u0027Forecast\u0027)) { const b = t.closest(\u0027.card\u0027).querySelector(\u0027.card-body\u0027); b.style.position = \u0027relative\u0027; const d = document.createElement(\u0027div\u0027); d.style.cssText = \u0027position:absolute;left:0;right:0;top:0;height:70px;background:#1f2326;z-index:5\u0027; b.appendChild(d); } })",
"danach": 2
},
{
"js": "[...document.querySelectorAll(\u0027.card-title\u0027)].find(t =\u003e t.textContent.includes(\u0027Statistik dieses Jahr\u0027)).closest(\u0027.card\u0027).scrollIntoView({block:\u0027start\u0027, behavior:\u0027instant\u0027}); window.scrollBy({top:-70, behavior:\u0027instant\u0027})",
"danach": 3,
"datei": "solar_statistik.jpg"
},
{
"js": "[...document.querySelectorAll(\u0027.card-title\u0027)].find(t =\u003e t.textContent.includes(\u0027Power production\u0027)).closest(\u0027.card\u0027).scrollIntoView({block:\u0027start\u0027, behavior:\u0027instant\u0027}); window.scrollBy({top:-70, behavior:\u0027instant\u0027})",
"danach": 3,
"datei": "solar_verlauf.jpg"
},
{
"js": "window.scrollTo({top:0, behavior:\u0027instant\u0027}); openModal(\u0027CarEG\u0027)",
"danach": 6,
"datei": "wallbox.jpg"
},
{
"js": "modalEV.hide()",
"danach": 2
},
{
"js": "openModal(\u0027heater\u0027)",
"danach": 6,
"datei": "heizstab.jpg"
},
{
"js": "modalEV.hide()",
"danach": 2
},
{
"js": "openModal(\u0027watering\u0027)",
"danach": 6,
"datei": "bewaesserung.jpg"
},
{
"url": "http://nas.fritz.box/smart/index.php?action=history",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 12,
"datei": "historie.jpg"
},
{
"js": "[...document.querySelectorAll(\u0027.card-title\u0027)].find(t =\u003e t.textContent.includes(\u0027Jahresvergleich\u0027)).closest(\u0027.card\u0027).scrollIntoView({block:\u0027start\u0027, behavior:\u0027instant\u0027}); window.scrollBy({top:-70, behavior:\u0027instant\u0027})",
"danach": 3,
"datei": "jahresvergleich.jpg"
},
{
"url": "http://nas.fritz.box/smart/index.php?action=heat",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 12,
"datei": "heizung.jpg"
},
{
"url": "http://nas.fritz.box/smart/index.php?action=weather",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 14,
"js": "(() =\u003e { const k = [...document.querySelectorAll(\u0027.card-title\u0027)].find(t =\u003e t.textContent.includes(\u0027Vorhersage\u0027)).closest(\u0027.card\u0027).querySelector(\u0027.card-body\u0027); k.style.position = \u0027relative\u0027; const d = document.createElement(\u0027div\u0027); d.style.cssText = \u0027position:absolute;left:0;right:0;top:0;height:78px;background:#1f2326;z-index:5\u0027; k.appendChild(d); })()",
"danach": 1,
"datei": "wetter.jpg"
},
{
"url": "http://nas.fritz.box/smart/index.php?action=skoda",
"breit": 390,
"hoch": 844,
"handy": true,
"warte": 14,
"datei": "skoda.jpg"
},
{
"js": "[...document.querySelectorAll(\u0027.card-title\u0027)].find(t =\u003e t.textContent.includes(\u0027Ladungen\u0027)).closest(\u0027.card\u0027).scrollIntoView({block:\u0027start\u0027, behavior:\u0027instant\u0027}); window.scrollBy({top:-70, behavior:\u0027instant\u0027})",
"danach": 3,
"datei": "skoda_ladungen.jpg"
},
{
"url": "http://nas.fritz.box/smart/index.php?action=settings\u0026r=preise",
"breit": 1280,
"hoch": 860,
"warte": 8,
"vorher": "preise",
"datei": "einst_preise.jpg",
"marken": {
"1": "#einstellung-grundriss-tab",
"2": ".preis-karte .preis-neu",
"3": ".preis-karte .preis-speichern"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=settings\u0026r=grundriss",
"breit": 1280,
"hoch": 860,
"warte": 8,
"vorher": "grundriss",
"js": "document.querySelector(\u0027.gr-etage[data-code=\"EG\"]\u0027).click(); setTimeout(() =\u003e { const z = [...document.querySelectorAll(\u0027.gr-raum\u0027)].find(r =\u003e r.textContent.includes(\u0027Wohnzimmer\u0027)); z.querySelector(\u0027.gr-raum-name\u0027).click(); document.getElementById(\u0027grundrissKarte\u0027).closest(\u0027.card\u0027).scrollIntoView({block:\u0027start\u0027, behavior:\u0027instant\u0027}); window.scrollBy({top:-70, behavior:\u0027instant\u0027}); }, 300)",
"danach": 3,
"datei": "einst_grundriss.jpg",
"marken": {
"1": ".gr-etagen",
"2": ".gr-kachel.gewaehlt rect",
"3": ".gr-bild",
"4": ".gr-raum-neu",
"5": "[data-block=\u0027raum\u0027] [data-feld=\u0027thermostat\u0027]"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=settings\u0026r=kacheln",
"breit": 1280,
"hoch": 860,
"warte": 8,
"vorher": "kacheln",
"js": "kachelOeffnen(\u0027EG\u0027,\u0027technik\u0027).then(() =\u003e { const z = document.querySelector(\u0027.kachel-zeile.offen\u0027); z.scrollIntoView({block:\u0027start\u0027, behavior:\u0027instant\u0027}); window.scrollBy({top:-90, behavior:\u0027instant\u0027}); })",
"danach": 3,
"datei": "einst_kacheln.jpg",
"marken": {
"1": ".kachel-zeile.offen .kachel-kopf",
"2": ".kachel-zeile.offen .kachel-wert",
"3": ".kachel-zeile.offen .kachel-plus",
"4": ".kachel-zeile.offen .kachel-speichern"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=settings\u0026r=geraete",
"breit": 1280,
"hoch": 860,
"warte": 9,
"vorher": "geraete",
"datei": "einst_geraete.jpg",
"marken": {
"1": "#geraeteSuche",
"2": "#geraeteListe tbody tr td.raum select",
"3": "#geraeteListe tbody tr .geraet-weg"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=settings\u0026r=fahrzeug",
"breit": 1280,
"hoch": 860,
"warte": 9,
"vorher": "fahrzeug",
"datei": "einst_fahrzeug.jpg",
"js": "(() =\u003e { const w = document.createTreeWalker(document.getElementById(\u0027skodaListe\u0027), NodeFilter.SHOW_TEXT); let n; while ((n = w.nextNode())) n.nodeValue = n.nodeValue.replace(/(endet auf\\s*…?)\\s*\\S{2,}/g, \u0027$1 ••••\u0027); return \u0027ok\u0027; })()",
"danach": 1
},
{
"url": "http://nas.fritz.box/smart/index.php?action=home\u0026floor=EG",
"breit": 1280,
"hoch": 860,
"warte": 9,
"js": "openAutoActionModal(\u0027?action=editor\u0026id=16\u0027)",
"danach": 5,
"datei": "editor_wenn.jpg",
"marken": {
"1": "#autoName",
"2": "#condBlocks",
"3": "#condStand",
"4": "#btnAddGroup"
}
},
{
"js": "document.getElementById(\u0027rahmenKopf\u0027).click(); setTimeout(() =\u003e document.getElementById(\u0027rahmenBody\u0027).scrollIntoView({block:\u0027center\u0027, behavior:\u0027instant\u0027}), 500)",
"danach": 3,
"datei": "editor_wann.jpg",
"marken": {
"1": "#tagVorlagen",
"2": "#weekdayGroup",
"3": "#ferienGroup",
"4": "#nextDay",
"5": "#rahmenSummary"
}
},
{
"js": "document.getElementById(\u0027actionList\u0027).scrollIntoView({block:\u0027center\u0027, behavior:\u0027instant\u0027})",
"danach": 2,
"datei": "editor_dann.jpg",
"marken": {
"1": "#actionList",
"2": "#btnAddActor",
"3": "#modalSaveBtn"
}
},
{
"url": "http://nas.fritz.box/smart/index.php?action=logs",
"breit": 1280,
"hoch": 860,
"warte": 9,
"datei": "protokolle.jpg"
}
]