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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user