c11c492d86
homeassistant/install.sh richtet das Panel samt Backend auf einer Home-Assistant-Instanz ein und verdrahtet dabei die Selbstaktualisierung, so dass danach alles Weitere ueber die Weboberflaeche laeuft: Sensoren zuordnen, nach Updates suchen, einspielen. Gedacht fuer das Add-on "Terminal & SSH" direkt auf der Instanz: bash <(curl -fsSL <ROH-URL>/homeassistant/install.sh) Alternativ von einem Rechner aus mit --ziel gegen ein eingebundenes config-Verzeichnis, oder mit --von gegen eine lokale Arbeitskopie. Was es tut: pyscript installieren (neueste Version von GitHub, falls nicht vorhanden), pyscript/ und www/ einspielen, den Belegleser mitliefern (er liegt in data/, ist aber Code), fehlende Datenbestaende aus der Vorlage anlegen, den Abschnitt in die configuration.yaml eintragen und UPDATE_REPO_URL setzen. Drei Eigenschaften, auf die es dabei ankommt: - Mehrfach ausfuehrbar. Der Abschnitt in der configuration.yaml steht zwischen Markern und wird beim zweiten Lauf ersetzt statt angehaengt. Stehen pyscript oder panel_custom bereits ausserhalb dieses Abschnitts, weist das Skript darauf hin, statt einen doppelten Schluessel zu erzeugen. - Vorhandene Daten bleiben unangetastet. Fahrzeugprofil, Fahrten, Tankvorgaenge und ein bereits hinterlegtes Token werden nie ueberschrieben, nur fehlende Dateien angelegt. - Die configuration.yaml wird vor jeder Aenderung mit Zeitstempel gesichert. Geprueft gegen eine frische Home-Assistant-Instanz im Container: HA startet ohne Konfigurationsfehler, alle neun pyscript-Entitaeten werden veroeffentlicht, das Panel ist als "Mein Audi" mit mdi:car-sports unter /audi-dashboard-panel registriert und rendert im Browser mit fuenf Tabs. Ein zweiter Durchlauf laesst Profil und Token unveraendert und den Konfigurationsabschnitt genau einmal stehen. Ausserdem den blockierenden Verlaufsabruf in fahrtabschluss_logik.py uebernommen: urlopen lief direkt statt ueber task.executor, was Home Assistant seit 2026.8 abbricht - jede Fahrt waere ohne Strecke geblieben. Der Fix lag bisher nur im Branch umsetzung-datametric360; eine frische Installation haette den Fehler sonst mitgebracht. INSTALL.md nennt den schnellen Weg jetzt vor der ausfuehrlichen Anleitung.
294 lines
11 KiB
Bash
Executable File
294 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# DataMetric360 — Installation in Home Assistant
|
|
#
|
|
# Richtet das Panel samt Backend auf einer Home-Assistant-Instanz ein und
|
|
# verdrahtet die Selbstaktualisierung, damit danach alles Weitere über die
|
|
# Weboberfläche läuft: Sensoren zuordnen, nach Updates suchen, einspielen.
|
|
#
|
|
# Wo ausführen:
|
|
# Am einfachsten direkt auf der HA-Instanz, im Add-on "Terminal & SSH":
|
|
# bash <(curl -fsSL <ROH-URL dieser Datei>)
|
|
# Alternativ von einem Rechner aus gegen ein eingebundenes config-Verzeichnis:
|
|
# ./install.sh --ziel /Volumes/config
|
|
#
|
|
# Mehrfach ausführbar: bereits vorhandene Fahrzeugdaten werden nie
|
|
# überschrieben, und der Abschnitt in der configuration.yaml wird beim
|
|
# erneuten Lauf ersetzt statt ein zweites Mal angehängt.
|
|
#
|
|
set -euo pipefail
|
|
|
|
# ------------------------------------------------------------ Voreinstellungen
|
|
|
|
REPO_STANDARD="https://gitea.nothaft.cloud/paul/audi-app.git"
|
|
BRANCH="main"
|
|
ZIEL="/config"
|
|
QUELLE=""
|
|
TOKEN=""
|
|
REPO="$REPO_STANDARD"
|
|
PYSCRIPT_INSTALLIEREN="ja"
|
|
INTERAKTIV="ja"
|
|
|
|
MARKE_START="# >>> DataMetric360 (von install.sh verwaltet) >>>"
|
|
MARKE_ENDE="# <<< DataMetric360 <<<"
|
|
|
|
# ------------------------------------------------------------------- Ausgabe
|
|
|
|
rot() { printf '\033[31m%s\033[0m\n' "$*"; }
|
|
gruen(){ printf '\033[32m%s\033[0m\n' "$*"; }
|
|
grau() { printf '\033[90m%s\033[0m\n' "$*"; }
|
|
schritt(){ printf '\n\033[1m==> %s\033[0m\n' "$*"; }
|
|
fehler(){ rot "FEHLER: $*"; exit 1; }
|
|
|
|
hilfe() {
|
|
cat <<'ENDE'
|
|
DataMetric360 — Installation in Home Assistant
|
|
|
|
--ziel <pfad> config-Verzeichnis von Home Assistant (Standard: /config)
|
|
--von <pfad> lokale Arbeitskopie benutzen statt zu klonen
|
|
--repo <url> Repository zum Klonen und für die Selbstaktualisierung
|
|
--branch <name> Branch (Standard: main)
|
|
--token <wert> langlebiges Zugriffstoken für die Kilometerstand-Historie
|
|
--ohne-pyscript pyscript nicht mitinstallieren (wenn schon vorhanden)
|
|
--still keine Rückfragen stellen (für unbeaufsichtigte Läufe)
|
|
--hilfe diese Übersicht
|
|
|
|
Beispiele
|
|
bash install.sh # auf der HA-Instanz, klont selbst
|
|
bash install.sh --von ~/audi-app # aus einer lokalen Arbeitskopie
|
|
bash install.sh --ziel /Volumes/config # gegen ein eingebundenes Laufwerk
|
|
ENDE
|
|
}
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--ziel) ZIEL="${2:-}"; shift 2 ;;
|
|
--von) QUELLE="${2:-}"; shift 2 ;;
|
|
--repo) REPO="${2:-}"; shift 2 ;;
|
|
--branch) BRANCH="${2:-}"; shift 2 ;;
|
|
--token) TOKEN="${2:-}"; shift 2 ;;
|
|
--ohne-pyscript) PYSCRIPT_INSTALLIEREN="nein"; shift ;;
|
|
--still) INTERAKTIV="nein"; shift ;;
|
|
--hilfe|-h) hilfe; exit 0 ;;
|
|
*) fehler "Unbekannte Option: $1 (--hilfe zeigt alle)" ;;
|
|
esac
|
|
done
|
|
|
|
# --------------------------------------------------------------- Vorprüfungen
|
|
|
|
schritt "Umgebung prüfen"
|
|
|
|
for werkzeug in python3 curl unzip; do
|
|
command -v "$werkzeug" >/dev/null 2>&1 || fehler "$werkzeug wird gebraucht, ist aber nicht vorhanden."
|
|
done
|
|
|
|
[ -d "$ZIEL" ] || fehler "Das Zielverzeichnis $ZIEL gibt es nicht. Mit --ziel den richtigen Pfad angeben."
|
|
[ -w "$ZIEL" ] || fehler "Keine Schreibrechte auf $ZIEL."
|
|
|
|
if [ ! -f "$ZIEL/configuration.yaml" ]; then
|
|
rot "In $ZIEL liegt keine configuration.yaml."
|
|
fehler "Das sieht nicht nach einem Home-Assistant-Verzeichnis aus. Pfad mit --ziel prüfen."
|
|
fi
|
|
grau " Ziel: $ZIEL"
|
|
|
|
# ------------------------------------------------------------ Quelle besorgen
|
|
|
|
AUFRAEUMEN=""
|
|
saeubern() { [ -n "$AUFRAEUMEN" ] && rm -rf "$AUFRAEUMEN" || true; }
|
|
trap saeubern EXIT
|
|
|
|
if [ -n "$QUELLE" ]; then
|
|
schritt "Lokale Arbeitskopie benutzen"
|
|
[ -d "$QUELLE/homeassistant" ] || fehler "In $QUELLE gibt es keinen Ordner homeassistant/."
|
|
HA_QUELLE="$QUELLE/homeassistant"
|
|
grau " $HA_QUELLE"
|
|
else
|
|
schritt "Projekt herunterladen"
|
|
command -v git >/dev/null 2>&1 || fehler "git wird zum Klonen gebraucht (oder --von <pfad> benutzen)."
|
|
AUFRAEUMEN="$(mktemp -d)"
|
|
if ! git clone --depth 1 --branch "$BRANCH" "$REPO" "$AUFRAEUMEN/quelle" >/dev/null 2>&1; then
|
|
rot "Klonen von $REPO fehlgeschlagen."
|
|
grau " Bei einem privaten Repository ein Zugangstoken in die URL aufnehmen, etwa:"
|
|
grau " --repo https://<nutzer>:<token>@gitea.example/paul/audi-app.git"
|
|
exit 1
|
|
fi
|
|
HA_QUELLE="$AUFRAEUMEN/quelle/homeassistant"
|
|
grau " $REPO ($BRANCH)"
|
|
fi
|
|
|
|
[ -d "$HA_QUELLE/pyscript" ] || fehler "Im Projekt fehlt homeassistant/pyscript/."
|
|
|
|
# ------------------------------------------------------------------- pyscript
|
|
|
|
if [ "$PYSCRIPT_INSTALLIEREN" = "ja" ]; then
|
|
if [ -d "$ZIEL/custom_components/pyscript" ]; then
|
|
schritt "pyscript ist bereits vorhanden"
|
|
grau " $ZIEL/custom_components/pyscript"
|
|
else
|
|
schritt "pyscript installieren"
|
|
VERSION="$(curl -fsSL https://api.github.com/repos/custom-components/pyscript/releases/latest \
|
|
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])' 2>/dev/null || true)"
|
|
[ -n "$VERSION" ] || fehler "Konnte die neueste pyscript-Version nicht ermitteln (kein Internet?)."
|
|
TMPPY="$(mktemp -d)"
|
|
curl -fsSL -o "$TMPPY/pyscript.zip" \
|
|
"https://github.com/custom-components/pyscript/archive/refs/tags/${VERSION}.zip"
|
|
unzip -oq "$TMPPY/pyscript.zip" -d "$TMPPY"
|
|
mkdir -p "$ZIEL/custom_components"
|
|
cp -r "$TMPPY/pyscript-${VERSION}/custom_components/pyscript" "$ZIEL/custom_components/"
|
|
rm -rf "$TMPPY"
|
|
gruen " pyscript $VERSION installiert"
|
|
fi
|
|
fi
|
|
|
|
# --------------------------------------------------------- Dateien einspielen
|
|
|
|
schritt "Backend und Oberfläche einspielen"
|
|
|
|
mkdir -p "$ZIEL/pyscript" "$ZIEL/www" "$ZIEL/audi_dashboard"
|
|
|
|
# pyscript und www sind Code - vollständig ersetzen.
|
|
cp -r "$HA_QUELLE/pyscript/." "$ZIEL/pyscript/"
|
|
cp -r "$HA_QUELLE/www/." "$ZIEL/www/"
|
|
grau " pyscript/ und www/ aktualisiert"
|
|
|
|
# Der Belegleser liegt in data/, ist aber Code (belegverarbeitung.py ruft ihn
|
|
# als Unterprozess auf) - deshalb hier mit, anders als die Datenbestände.
|
|
cp "$HA_QUELLE/data/shell_beleg_parser.py" "$ZIEL/audi_dashboard/shell_beleg_parser.py"
|
|
|
|
# Datenbestände: nur anlegen, was fehlt. Vorhandenes nie überschreiben.
|
|
if [ ! -f "$ZIEL/audi_dashboard/fahrzeugprofil.json" ]; then
|
|
cp "$HA_QUELLE/data/fahrzeugprofil.example.json" "$ZIEL/audi_dashboard/fahrzeugprofil.json"
|
|
gruen " Fahrzeugprofil aus der Vorlage angelegt"
|
|
else
|
|
grau " Fahrzeugprofil vorhanden - unangetastet"
|
|
fi
|
|
for datei in fahrten.jsonl tankvorgaenge.jsonl; do
|
|
[ -f "$ZIEL/audi_dashboard/$datei" ] || : > "$ZIEL/audi_dashboard/$datei"
|
|
done
|
|
mkdir -p "$ZIEL/audi_dashboard/belege"
|
|
|
|
# --------------------------------------------------- Selbstaktualisierung
|
|
|
|
schritt "Selbstaktualisierung verdrahten"
|
|
|
|
# UPDATE_REPO_URL im ausgelieferten einstellungen.py setzen, damit
|
|
# "Update suchen" in den Einstellungen der App funktioniert.
|
|
python3 - "$ZIEL/pyscript/modules/einstellungen.py" "$REPO" "$BRANCH" <<'PYENDE'
|
|
import re, sys
|
|
pfad, repo, branch = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
with open(pfad, encoding="utf-8") as f:
|
|
inhalt = f.read()
|
|
inhalt = re.sub(r'^UPDATE_REPO_URL\s*=.*$', f'UPDATE_REPO_URL = "{repo}"', inhalt, count=1, flags=re.M)
|
|
inhalt = re.sub(r'^UPDATE_BRANCH\s*=.*$', f'UPDATE_BRANCH = "{branch}"', inhalt, count=1, flags=re.M)
|
|
with open(pfad, "w", encoding="utf-8") as f:
|
|
f.write(inhalt)
|
|
PYENDE
|
|
grau " Updatequelle: $REPO ($BRANCH)"
|
|
grau " Danach in der App unter Einstellungen → Version → \"Update suchen\""
|
|
|
|
# ---------------------------------------------------------- configuration.yaml
|
|
|
|
schritt "configuration.yaml ergänzen"
|
|
|
|
KONFIG="$ZIEL/configuration.yaml"
|
|
SICHERUNG="$KONFIG.vor-datametric360-$(date +%Y%m%d_%H%M%S)"
|
|
cp "$KONFIG" "$SICHERUNG"
|
|
grau " Sicherung: $(basename "$SICHERUNG")"
|
|
|
|
python3 - "$KONFIG" "$MARKE_START" "$MARKE_ENDE" <<'PYENDE'
|
|
import re, sys
|
|
pfad, start, ende = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
with open(pfad, encoding="utf-8") as f:
|
|
inhalt = f.read()
|
|
|
|
block = f"""{start}
|
|
# Von install.sh eingefügt. Aenderungen innerhalb dieses Abschnitts werden bei
|
|
# der naechsten Installation ersetzt - Eigenes bitte ausserhalb notieren.
|
|
pyscript:
|
|
allow_all_imports: true
|
|
|
|
panel_custom:
|
|
- name: audi-dashboard-panel
|
|
sidebar_title: Mein Audi
|
|
sidebar_icon: mdi:car-sports
|
|
module_url: /local/audi-dashboard-panel.js?v=1
|
|
embed_iframe: false
|
|
trust_external_script: false
|
|
{ende}"""
|
|
|
|
muster = re.compile(re.escape(start) + r".*?" + re.escape(ende), re.S)
|
|
if muster.search(inhalt):
|
|
inhalt = muster.sub(block, inhalt)
|
|
print(" Abschnitt ersetzt")
|
|
else:
|
|
for schluessel in ("pyscript:", "panel_custom:"):
|
|
if re.search(rf"^{schluessel}", inhalt, re.M):
|
|
print(f" ACHTUNG: '{schluessel}' steht bereits ausserhalb des verwalteten Abschnitts.")
|
|
print(" Der Abschnitt wird trotzdem angehaengt - Home Assistant meldet dann")
|
|
print(" einen doppelten Schluessel. Bitte den alten Eintrag von Hand entfernen.")
|
|
if not inhalt.endswith("\n"):
|
|
inhalt += "\n"
|
|
inhalt += "\n" + block + "\n"
|
|
print(" Abschnitt angehaengt")
|
|
|
|
with open(pfad, "w", encoding="utf-8") as f:
|
|
f.write(inhalt)
|
|
PYENDE
|
|
|
|
# ------------------------------------------------------------------- Token
|
|
|
|
schritt "Zugriffstoken für die Kilometerstand-Historie"
|
|
|
|
TOKENDATEI="$ZIEL/audi_dashboard/ha_token.txt"
|
|
if [ -n "$TOKEN" ]; then
|
|
printf '%s' "$TOKEN" > "$TOKENDATEI"
|
|
chmod 600 "$TOKENDATEI" 2>/dev/null || true
|
|
gruen " Token gespeichert"
|
|
elif [ -s "$TOKENDATEI" ]; then
|
|
grau " Vorhandenes Token bleibt unangetastet"
|
|
elif [ "$INTERAKTIV" = "ja" ] && [ -t 0 ]; then
|
|
echo " Der Fahrtabschluss liest den Kilometerstand aus dem Verlauf und braucht dafür"
|
|
echo " ein langlebiges Zugriffstoken (Profil → Sicherheit → Langlebige Zugriffstoken)."
|
|
echo " Leer lassen und später nachtragen ist möglich - Fahrten bleiben dann ohne Strecke."
|
|
printf " Token (Eingabe bleibt verborgen): "
|
|
read -rs EINGABE || EINGABE=""
|
|
echo
|
|
if [ -n "$EINGABE" ]; then
|
|
printf '%s' "$EINGABE" > "$TOKENDATEI"
|
|
chmod 600 "$TOKENDATEI" 2>/dev/null || true
|
|
gruen " Token gespeichert"
|
|
else
|
|
rot " Übersprungen - später in $TOKENDATEI ablegen."
|
|
fi
|
|
else
|
|
rot " Kein Token angegeben. Später in $TOKENDATEI ablegen,"
|
|
rot " sonst bleiben Fahrten ohne Streckenangabe."
|
|
fi
|
|
|
|
# ------------------------------------------------------------------ Abschluss
|
|
|
|
schritt "Fertig"
|
|
cat <<ENDE
|
|
|
|
Noch zu tun, alles in der Weboberfläche:
|
|
|
|
1. Home Assistant neu starten
|
|
Einstellungen → System → oben rechts → Neu starten
|
|
|
|
2. Sensoren zuordnen
|
|
Mein Audi → Einstellungen → Fahrzeug einrichten → Einrichten
|
|
→ "Setup — Sensoren zuordnen"
|
|
Pflicht ist nur die Zündung; alles Weitere ist optional.
|
|
|
|
3. Fahrzeugdaten eintragen
|
|
Im selben Menü unter "Einrichten": Kennzeichen, FIN, Versicherung, …
|
|
|
|
Ab dann läuft alles über die Oberfläche - auch Updates:
|
|
Mein Audi → Einstellungen → Version → "Update suchen"
|
|
|
|
Sicherung der configuration.yaml:
|
|
$SICHERUNG
|
|
|
|
ENDE
|
|
gruen "Installation abgeschlossen."
|