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
+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"
}
]