Files
audi-app/homeassistant/install.sh
T
Paul Nothaft 2a3c4f2ab5 Zugangswege zum privaten Repository dokumentieren und trennen
Neue Option --update-repo trennt, woher installiert wird, von dem, was als
Updatequelle hinterlegt wird. Damit laesst sich mit vollen Zugangsdaten
installieren, ohne sie abzulegen (--update-repo aus). Enthaelt die hinterlegte
URL Zugangsdaten, weist das Skript darauf hin und maskiert sie in der Ausgabe.

Neuer Abschnitt 1b in ha_install.md beantwortet die drei naheliegenden Fragen
und benennt den Haken dahinter:

- Werkzeuge sind vorhanden. git 2.54, curl, unzip und ssh liegen im
  offiziellen HA-Container bereits vor, an der Instanz nachgesehen.
- Passwort in der URL funktioniert genauso wie ein Token - es ist aber das
  Kontopasswort, im Klartext in /config und in jeder Sicherung, und bei
  Zwei-Faktor-Anmeldung verweigert Gitea es ohnehin.
- Ein vorher im Terminal eingerichteter Anmeldehelfer hilft der
  Selbstaktualisierung NICHT. Sie klont aus dem Home-Assistant-Prozess und
  allein anhand von UPDATE_REPO_URL; _git() setzt keine Umgebung. Unter HA OS
  laeuft das Terminal-Add-on ausserdem in einem eigenen Container, geteilt ist
  nur /config. Fuer die Installation selbst genuegt der Helfer dagegen.

Dazu eine Optionstabelle mit Bewertung, der Hinweis dass auch curl fuer das
Skript selbst Zugang braucht, und was sich nach der Umstellung auf die
Integration daran aendert: der Zugang wandert in den Konfigurationsdialog und
Home Assistants eigene Ablage statt in eine Python-Datei.
2026-08-13 17:21:19 +02:00

319 lines
12 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"
UPDATE_REPO="" # leer = wie --repo
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 (Standard: das Projekt-Repository)
--update-repo <url|aus>
Was als Updatequelle hinterlegt wird. Standard: wie --repo.
"aus" hinterlegt nichts - dann gibt es keine
Selbstaktualisierung, aber auch keine Zugangsdaten auf der
Platte. Siehe ha_install.md, Abschnitt 1b.
--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
bash install.sh --repo https://nutzer:token@gitea.example/paul/audi-app.git
bash install.sh --von . --update-repo aus # lokal einspielen, ohne Zugangsdaten abzulegen
ENDE
}
while [ $# -gt 0 ]; do
case "$1" in
--ziel) ZIEL="${2:-}"; shift 2 ;;
--von) QUELLE="${2:-}"; shift 2 ;;
--repo) REPO="${2:-}"; shift 2 ;;
--update-repo) UPDATE_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"
# Was hinterlegt wird, ist bewusst von der Installationsquelle getrennt: die
# Selbstaktualisierung laeuft spaeter im Home-Assistant-Prozess und klont
# allein anhand dieser URL - Zugangsdaten muessen also darin stehen und landen
# damit im Klartext in einstellungen.py. Wer das nicht will, gibt
# "--update-repo aus" an und aktualisiert weiterhin ueber dieses Skript.
QUELLE_FUER_UPDATE="${UPDATE_REPO:-$REPO}"
if [ "$QUELLE_FUER_UPDATE" = "aus" ]; then
QUELLE_FUER_UPDATE=""
fi
python3 - "$ZIEL/pyscript/modules/einstellungen.py" "$QUELLE_FUER_UPDATE" "$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
if [ -n "$QUELLE_FUER_UPDATE" ]; then
grau " Updatequelle: $(printf '%s' "$QUELLE_FUER_UPDATE" | sed -E 's#://[^@/]+@#://***@#') ($BRANCH)"
grau " Danach in der App unter Einstellungen → Version → \"Update suchen\""
case "$QUELLE_FUER_UPDATE" in
*://*:*@*) rot " Hinweis: Die URL enthaelt Zugangsdaten. Sie stehen im Klartext in"
rot " pyscript/modules/einstellungen.py und damit auch in jeder Sicherung." ;;
esac
else
grau " Keine Updatequelle hinterlegt - Aktualisieren laeuft weiter ueber dieses Skript."
fi
# ---------------------------------------------------------- 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."