Files
audi-app/testumgebung/einrichten.mjs
T
Paul Nothaft 982bb04c1a Phase 1: reproduzierbare HA-Testumgebung als Skript
Die urspruengliche Wegwerf-Testinstanz war verloren, weil sie nur als
Anleitung existierte. testumgebung/aufsetzen.sh baut sie jetzt vollstaendig
neu auf: Container, Nutzer, langlebiges Token, pyscript als Custom Component,
Projektdateien und nachgebildete Fahrzeugsensoren als Template-Entitaeten,
die ueber Helfer zur Laufzeit steuerbar sind.

Verifiziert: pyscript veroeffentlicht alle acht Entitaeten, Fahrzeugsensoren
liefern Werte, beide Smoke-Tests gruen.
2026-08-11 10:44:50 +02:00

103 lines
3.4 KiB
JavaScript

// Legt in einer frischen Home-Assistant-Instanz einen Testnutzer an und gibt ein
// langlebiges Zugriffstoken auf der Standardausgabe aus. Meldungen gehen auf die
// Fehlerausgabe, damit das aufrufende Skript nur das Token einliest.
// Aufruf: node einrichten.mjs http://localhost:18123
const BASIS = process.argv[2] ?? "http://localhost:18123"
const CLIENT_ID = BASIS + "/"
const BENUTZER = "test"
const PASSWORT = "testtest123"
const melden = (text) => process.stderr.write(` ${text}\n`)
async function jsonPost(pfad, koerper, token) {
const kopf = { "Content-Type": "application/json" }
if (token) kopf.Authorization = `Bearer ${token}`
const antwort = await fetch(BASIS + pfad, {
method: "POST",
headers: kopf,
body: JSON.stringify(koerper),
})
const text = await antwort.text()
if (!antwort.ok) throw new Error(`${pfad} -> ${antwort.status}: ${text.slice(0, 200)}`)
return text ? JSON.parse(text) : {}
}
// 1. Onboarding-Schritt "user"
const { auth_code: authCode } = await jsonPost("/api/onboarding/users", {
client_id: CLIENT_ID,
name: "Test Nutzer",
username: BENUTZER,
password: PASSWORT,
language: "de",
})
melden("Nutzer angelegt")
// 2. Auth-Code gegen ein kurzlebiges Zugriffstoken tauschen
const tokenAntwort = await fetch(BASIS + "/auth/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "authorization_code",
code: authCode,
client_id: CLIENT_ID,
}).toString(),
})
const token = await tokenAntwort.json()
if (!token.access_token) throw new Error("kein access_token: " + JSON.stringify(token))
melden("Zugriffstoken erhalten")
// 3. Restliche Onboarding-Schritte abschliessen (Fehler sind unkritisch)
for (const [pfad, koerper] of [
["/api/onboarding/core_config", {}],
["/api/onboarding/analytics", {}],
["/api/onboarding/integration", { client_id: CLIENT_ID, redirect_uri: CLIENT_ID }],
]) {
try {
await jsonPost(pfad, koerper, token.access_token)
} catch {
// Schritt bereits erledigt oder in dieser Version nicht vorhanden
}
}
// 4. Langlebiges Token ueber die WebSocket-API erzeugen
const wsUrl = BASIS.replace(/^http/, "ws") + "/api/websocket"
const llat = await new Promise((aufloesen, ablehnen) => {
const ws = new WebSocket(wsUrl)
const zeitgeber = setTimeout(() => {
ablehnen(new Error("Zeitueberschreitung beim Erzeugen des Tokens"))
ws.close()
}, 20000)
ws.onmessage = (nachricht) => {
const daten = JSON.parse(nachricht.data)
if (daten.type === "auth_required") {
ws.send(JSON.stringify({ type: "auth", access_token: token.access_token }))
} else if (daten.type === "auth_ok") {
ws.send(
JSON.stringify({
id: 1,
type: "auth/long_lived_access_token",
client_name: `DataMetric360 Test ${Date.now()}`,
lifespan: 365,
}),
)
} else if (daten.type === "result") {
clearTimeout(zeitgeber)
if (daten.success) aufloesen(daten.result)
else ablehnen(new Error(JSON.stringify(daten.error)))
ws.close()
} else if (daten.type === "auth_invalid") {
clearTimeout(zeitgeber)
ablehnen(new Error("auth_invalid"))
ws.close()
}
}
ws.onerror = () => {
clearTimeout(zeitgeber)
ablehnen(new Error("WebSocket-Verbindung fehlgeschlagen"))
}
})
melden("langlebiges Token erzeugt")
process.stdout.write(llat)