Files
audi-app/homeassistant/installationspaket/install.ps1
T
tobias 2ea01ce2ce install.ps1 warnt vor einem veralteten OTA-Bündel
Beim Prüfen, ob die GitHub-Entfernung eine Versionserhöhung brauchte, fiel
eine echte Lücke auf: nichts hielt frontend/app/bundle.json auf demselben
Stand wie manifest.json. Bei der nächsten echten Versionserhöhung ohne
"npm run ota" hätte das zu einem stillen Widerspruch geführt - die
Hinweisleiste hätte "veraltet" gemeldet (liest die Version live), die
Update-Seite "aktuell" (vergleicht gegen das eingefrorene, dann falsche
Bündel) -, ohne dass ein Update über OTA erreichbar gewesen wäre, bis
jemand den Widerspruch bemerkt.

install.ps1 liest jetzt direkt nach der Manifest-Version auch
frontend/app/bundle.json und vergleicht. Bei Abweichung: deutliche Warnung
plus erster Eintrag in der Restliste. Fehlt das Bündel ganz, passiert
nichts - OTA ist optional, das ist der normale Zustand.

Beide Pfade an einem Mock-Zielordner geprüft: passende Versionen melden
"OTA-Bündel passt zur Integration" ohne Restliste-Eintrag; eine testweise
erhöhte Manifest-Version (danach byte-genau zurückgesetzt, gegen HEAD
gegengeprüft) erzeugt die Warnung und landet als Restliste-Punkt 1.

VERSIONIERUNG.md: "Was du tun musst" nennt den OTA-Neubau jetzt als
eigenen nummerierten Schritt, statt ihn ganz auszulassen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 09:32:36 +02:00

251 lines
13 KiB
PowerShell

<#
.SYNOPSIS
Installiert "Mein Audi" (DataMetric360) als Home-Assistant-Integration -
der Weg ohne HACS.
.BESCHREIBUNG
Kopiert genau einen Ordner:
custom_components\audi_dashboard\ -> <Ziel>\custom_components\
Mehr ist nicht nötig. Alles, was früher an dieser Stelle von Hand gemacht
werden musste, macht die Integration inzwischen selbst: sie meldet ihr Panel
an, liefert ihre Oberflächen-Dateien selbst aus, legt beim ersten Start ein
Fahrzeugprofil aus der Vorlage an und bringt pypdf über ihre manifest.json
mit, das Home Assistant beim ersten Laden nachinstalliert.
WOFÜR ES DIESES SKRIPT ÜBERHAUPT GIBT
--------------------------------------
HACS scheidet für dieses Repository aus, und zwar endgültig: laut eigener
Dokumentation (hacs.xyz/docs/faq/private_repositories) kann HACS
"grundsätzlich nicht" mit privaten GitHub-Repositories arbeiten - ohne
Ausnahme für Tokens oder verbundene Konten. Das Repository enthält die
Audi-Hausschrift und die Typenschilder, die nur für diese eine private
Installation lizenziert sind, und öffentlich machen ist deshalb keine
Option. Ein GitHub-Spiegel wurde deshalb 2026-08-24 wieder aufgegeben -
entwickelt wird ausschließlich auf der eigenen Gitea-Instanz.
Dieses Skript ist damit nicht die Rückfallebene, sondern der einzige Weg.
SICHERHEIT GEGENÜBER DER BESTEHENDEN HA-INSTALLATION
----------------------------------------------------
Die riskanteste Stelle der früheren Fassung gibt es nicht mehr: sie musste
die configuration.yaml ergänzen (pyscript:, panel_custom:), und eine kaputte
configuration.yaml ist der eine Weg, auf dem ein Installer Home Assistant am
Starten hindern kann. Diese Fassung fasst die Datei überhaupt nicht mehr an.
Was bleibt, ist nachprüfbar:
- Geschrieben wird ausschließlich in <Ziel>\custom_components\audi_dashboard.
Kein anderer Pfad wird angefasst - nicht .storage\, nicht
configuration.yaml, nicht automations.yaml, nicht www\, nicht
audi_dashboard\ (die Fahrzeugdaten).
- Entfernt wird nur der eigene Ordner, und nur, wenn darin eine
manifest.json mit "domain": "audi_dashboard" liegt. Ein fremder Ordner
unter diesem Namen führt zum Abbruch, nicht zum Löschen.
- Es startet Home Assistant nicht neu und greift nicht in den laufenden
Betrieb ein.
- -Pruefen zeigt den kompletten Ablauf, ohne irgendetwas zu schreiben.
Im Zweifel damit anfangen.
Warum der Zielordner vor dem Kopieren geleert wird (und nicht nur
überschrieben): bleibt eine Datei aus einer älteren Fassung liegen, die es
im neuen Stand nicht mehr gibt, lädt Home Assistant sie trotzdem mit. Das
ist genau der Fehler, den man hinterher am schwersten findet. HACS macht es
aus demselben Grund so.
Die Fahrzeugdaten liegen woanders (<Ziel>\audi_dashboard\) und werden von
diesem Skript nie berührt - ein Update kann sie deshalb gar nicht treffen.
.PARAMETER Ziel
config-Verzeichnis der HA-Instanz, z. B. "\\homeassistant.local\config"
(Samba-Add-on) oder ein lokal gemounteter Pfad. Ohne Angabe sucht das
Skript die üblichen Samba-Namen selbst ab.
.PARAMETER Pruefen
Nur anzeigen, was passieren würde - es wird nichts geschrieben.
.BEISPIEL
.\install.ps1
.\install.ps1 -Ziel "\\192.168.1.50\config"
.\install.ps1 -Pruefen
#>
param(
[string]$Ziel,
[switch]$Pruefen
)
$ErrorActionPreference = "Stop"
$hier = $PSScriptRoot
function Schritt($text) { Write-Host "`n$text" -ForegroundColor Cyan }
function Gut($text) { Write-Host " [ok] $text" -ForegroundColor Green }
function Info($text) { Write-Host " [info] $text" -ForegroundColor DarkGray }
function Warnung($text) { Write-Host " [!] $text" -ForegroundColor Yellow }
$restliste = New-Object System.Collections.ArrayList
Write-Host ""
Write-Host " Mein Audi - Installation" -ForegroundColor White
Write-Host " ========================" -ForegroundColor White
if ($Pruefen) { Warnung "Pruefmodus - es wird nichts geschrieben." }
# ------------------------------------------------------------ Quelle finden
# Zwei Lagen, damit sowohl der Aufruf aus dem Projektordner heraus als auch
# ein separat weitergegebenes Paket funktioniert.
$quellKandidaten = @(
(Join-Path $hier "custom_components\audi_dashboard"),
(Join-Path $hier "..\..\custom_components\audi_dashboard")
)
$quelle = $null
foreach ($k in $quellKandidaten) {
if (Test-Path (Join-Path $k "manifest.json")) { $quelle = (Resolve-Path $k).Path; break }
}
if (-not $quelle) {
throw "custom_components\audi_dashboard nicht gefunden - dieses Skript gehört in den Projektordner (oder das Paket daneben)."
}
$version = (Get-Content (Join-Path $quelle "manifest.json") -Raw | ConvertFrom-Json).version
Gut "Quelle: $quelle (Version $version)"
# ------------------------------------------------ OTA-Bündel auf Stand prüfen
# Das Bündel für die iOS-App (frontend\app\bundle.json, gebaut von
# companion-app\scripts\ota-paket.ps1) trägt seine eigene, zum Bauzeitpunkt
# eingefrorene Versionsnummer. Ändert sich manifest.json's Version, ohne dass
# jemand "npm run ota" erneut laufen lässt, bekommt die App zwei
# widersprüchliche Signale gleichzeitig: die Hinweisleiste sagt "du bist
# veraltet" (vergleicht gegen die live gelesene manifest-Version), die
# Update-Seite sagt "du bist aktuell" (vergleicht gegen das eingefrorene, nun
# falsche Bündel) - und es gibt keinen Weg, das Update zu bekommen, bis jemand
# den Widerspruch bemerkt. Das ist kein Zustand, den ein Installer schweigend
# durchwinken sollte - hier ist der eine Ort, an dem jede Auslieferung
# durchläuft, also der einzige verlässliche Punkt für diese Prüfung.
#
# Ein fehlendes Bündel ist dagegen kein Fehler: OTA ist eine optionale
# Zusatzfunktion, nicht jede Installation liefert eins mit.
$buendelInfo = Join-Path $quelle "frontend\app\bundle.json"
if (Test-Path $buendelInfo) {
$buendelVersion = (Get-Content $buendelInfo -Raw | ConvertFrom-Json).version
if ($buendelVersion -ne $version) {
Warnung "OTA-Bündel (frontend\app\bundle.json) steht auf Version $buendelVersion, die Integration auf $version."
Write-Host " Die App-Update-Funktion würde bis zur Behebung falsche Ergebnisse zeigen." -ForegroundColor Yellow
[void]$restliste.Add("OTA-Bündel ist veraltet (Version $buendelVersion statt $version): in companion-app\ 'npm run ota' ausführen, dann dieses Skript erneut laufen lassen.")
} else {
Info "OTA-Bündel passt zur Integration (Version $buendelVersion)"
}
}
# ---------------------------------------------------------------- Ziel finden
if (-not $Ziel) {
Schritt "Suche Home Assistant ..."
# Die Reihenfolge ist Absicht: der kurze Name antwortet in den meisten
# Heimnetzen schneller als der .local-Name über mDNS.
$kandidaten = @(
"\\homeassistant\config",
"\\homeassistant.local\config",
"\\hassio\config"
)
foreach ($k in $kandidaten) {
Info "probiere $k"
if (Test-Path $k) { $Ziel = $k; break }
}
if (-not $Ziel) {
Write-Host ""
Warnung "Keine HA-Instanz automatisch gefunden."
Write-Host " Bitte den config-Pfad angeben, z. B. \\192.168.1.50\config"
Write-Host " (Samba-Share-Add-on in HA muss laufen und verbunden sein.)"
Write-Host ""
$Ziel = Read-Host " config-Pfad"
if (-not $Ziel) { throw "Kein Ziel angegeben - Abbruch." }
}
}
if (-not (Test-Path $Ziel)) {
throw "Ziel nicht erreichbar: $Ziel - Samba-Share verbunden? Pfad korrekt?"
}
# Ein config-Verzeichnis ohne configuration.yaml ist mit hoher Wahrscheinlichkeit
# der falsche Ordner - lieber hier abbrechen als Dateien irgendwohin streuen.
if (-not (Test-Path (Join-Path $Ziel "configuration.yaml"))) {
throw "In $Ziel liegt keine configuration.yaml - ist das wirklich das config-Verzeichnis von HA?"
}
Gut "Ziel: $Ziel"
# ------------------------------------------------------- Zielordner prüfen
$zielOrdner = Join-Path $Ziel "custom_components\audi_dashboard"
$vorhandeneVersion = $null
if (Test-Path $zielOrdner) {
$zielManifest = Join-Path $zielOrdner "manifest.json"
if (-not (Test-Path $zielManifest)) {
throw "In $zielOrdner liegt keine manifest.json. Das ist nicht unser Ordner - Abbruch, es wird nichts angefasst."
}
$vorhandenesManifest = Get-Content $zielManifest -Raw | ConvertFrom-Json
if ($vorhandenesManifest.domain -ne "audi_dashboard") {
throw "$zielOrdner gehört zur Integration '$($vorhandenesManifest.domain)', nicht zu 'audi_dashboard' - Abbruch."
}
$vorhandeneVersion = $vorhandenesManifest.version
Info "bereits installiert: Version $vorhandeneVersion"
}
# ----------------------------------------------------------------- Kopieren
Schritt "Integration kopieren"
if ($Pruefen) {
if ($vorhandeneVersion) { Info "würde ersetzen: $zielOrdner (Version $vorhandeneVersion -> $version)" }
else { Info "würde anlegen: $zielOrdner (Version $version)" }
Info "würde sonst nichts anfassen - keine configuration.yaml, keine Fahrzeugdaten"
} else {
New-Item -ItemType Directory -Force -Path (Join-Path $Ziel "custom_components") | Out-Null
if (Test-Path $zielOrdner) {
# Die Prüfung oben hat bestätigt, dass das unser Ordner ist.
Remove-Item $zielOrdner -Recurse -Force
}
Copy-Item $quelle $zielOrdner -Recurse -Force
# Übersetzte Reste einer früheren Fassung gehören nicht ins Ziel.
Get-ChildItem $zielOrdner -Recurse -Directory -Filter "__pycache__" |
ForEach-Object { Remove-Item $_.FullName -Recurse -Force }
# Rücklesen: liegt im Ziel wirklich das, was gemeint war?
$kontrolle = Join-Path $zielOrdner "manifest.json"
if (-not (Test-Path $kontrolle)) {
throw "Nach dem Kopieren fehlt $kontrolle - die Installation ist unvollständig."
}
$geschrieben = (Get-Content $kontrolle -Raw | ConvertFrom-Json).version
if ($geschrieben -ne $version) {
throw "Nach dem Kopieren steht Version '$geschrieben' statt '$version' im Ziel - die Installation ist unvollständig."
}
$anzahl = (Get-ChildItem $zielOrdner -Recurse -File).Count
Gut "custom_components\audi_dashboard (Version $version, $anzahl Dateien)"
}
# --------------------------------------------------------------- Restliste
if (-not $vorhandeneVersion) {
[void]$restliste.Add("Home Assistant neu starten: Einstellungen -> System -> Neu starten. (Beim ersten Start ladt HA die Abhaengigkeit pypdf nach - das kann eine Minute dauern.)")
[void]$restliste.Add("Integration hinzufuegen: Einstellungen -> Geraete & Dienste -> Integration hinzufuegen -> 'Audi Dashboard'. Danach steht 'Mein Audi' in der Seitenleiste.")
[void]$restliste.Add("In der App: Einstellungen -> Fahrzeug einrichten -> Setup - Sensoren zuordnen. Im Auslieferstand ist KEIN Sensor vorbelegt; zwingend fuer die Fahrterkennung ist der Zuendungs-/ACC-Sensor (binary_sensor, meist vom Teltonika FMM003). Ein Neustart ist dafuer nicht mehr noetig - Aenderungen wirken sofort.")
} else {
[void]$restliste.Add("Home Assistant neu starten: Einstellungen -> System -> Neu starten. (Oder kuerzer: Einstellungen -> Geraete & Dienste -> Audi Dashboard -> Neu laden.)")
}
# Bewusst nicht automatisch eingefügt: der recorder-Block ist eine Entscheidung
# über den Plattenplatz der Instanz (ein Jahr Fahrzeugverlauf sind grob 1-1,5 GB,
# siehe Kopf von recorder_snippet.yaml), und viele Instanzen haben bereits einen
# eigenen recorder:-Block mit anderen Einstellungen. Zusammenführen ist Handarbeit.
[void]$restliste.Add("ZEITKRITISCH - Datenaufbewahrung: den Block aus recorder_snippet.yaml in die configuration.yaml uebernehmen. Home Assistant loescht Sensor-Verlaeufe sonst nach 10 Tagen; was weg ist, laesst sich auch mit 'Daten importieren aus Home Assistant' nicht mehr nachtragen. Je frueher der Block drin ist, desto mehr Vergangenheit bleibt erhalten. VORHER Einstellungen -> System -> Speicher pruefen: ein Jahr Verlauf braucht grob 1-1,5 GB. Bei wenig freiem Platz (SD-Karte, kleine eMMC) mit purge_keep_days: 90 anfangen - Details im Kopf der Datei.")
[void]$restliste.Add("Optional, sobald die Sensoren zugeordnet sind: Einstellungen -> Einrichten -> 'Daten importieren aus Home Assistant' holt Fahrten, Tankvorgaenge und Spannungswerte aus dem bereits aufgezeichneten HA-Verlauf nach.")
Write-Host ""
if ($Pruefen) {
Write-Host " Pruefmodus beendet - es wurde nichts geschrieben." -ForegroundColor Yellow
} else {
Write-Host " Die Integration ist installiert." -ForegroundColor Green
}
Write-Host ""
Write-Host " Das fehlt noch:" -ForegroundColor White
$nr = 1
foreach ($punkt in $restliste) {
Write-Host " $nr. $punkt"
$nr++
}
Write-Host ""
Write-Host " Fuer spaetere Updates reicht derselbe Aufruf noch einmal." -ForegroundColor White
Write-Host " Die Fahrzeugdaten unter audi_dashboard\ bleiben dabei unangetastet."
Write-Host ""