163efe1392
Bringt die vollstaendig gebaute DataMetric360-App zusammen (React/Vite, 21 Screens, Audi-Assets, PWA + native Capacitor-Huelle, 90+9 Tests) sowie die profil_lesen()-Haertung gegen fehlende/kaputte Profildatei zusammen. Konfliktaufloesung: - AGENTS.md, INSTALL.md, README.md: main-Fassung war jeweils die chronologisch neuere, uebernommen und um die durch den Merge tatsaechlich erledigten Punkte ergaenzt (profil_lesen()-Haertung, Audit-Reste-Entscheidung). - fahrterkennung.py: toten WLAN-Zweig vom Branch verworfen, Zuendungs- basierte Erkennung von main behalten. - homeassistant/FMM003_MAPPING.md (MQTT/Mosquitto-Ansatz vom 2026-08-11, vor der Umstellung auf flespi) bewusst nicht uebernommen - main nutzt seit 2026-08-12 flespi als alleinigen FMM003-Datenweg. UMSETZUNGSPLAN.md Phase 13 entsprechend als ueberholt markiert, verweist auf AGENTS.md als massgeblich. - REVIEW_main_2026-08-13.md, ha_install.md (add/add): main-Fassung war die spaetere Revision derselben Dokumente, uebernommen. Die von REVIEW_main_2026-08-13.md befuerchtete Merge-Falle (profil_lesen() gibt jetzt None zurueck, main-seitige Aufrufer pruefen das nicht) wurde verifiziert als bereits entschaerft: alle 5 Aufrufstellen im gemergten Stand (backup.py x2, fahrterkennung.py, frontend_veroeffentlichung.py, reifenzaehler.py, tankerkennung.py) sind None-sicher. installationspaket/ nicht Teil dieses Commits (gitignored, wird bei Bedarf neu zusammengestellt).
114 lines
4.5 KiB
Python
114 lines
4.5 KiB
Python
"""Backup-Funktion für Fahrzeugprofil, Fahrten und Tankvorgänge.
|
|
|
|
Manuell über "Backup jetzt erstellen" in den Einstellungen oder automatisch
|
|
je nach einstellungen.backup_intervall im Fahrzeugprofil (aus/täglich/
|
|
wöchentlich/monatlich) - immer um 04:00 Uhr, wenn eingestellt. Ein einzelner
|
|
täglicher Trigger um 04:00 genügt für alle drei Intervalle: wöchentlich läuft
|
|
nur montags, monatlich nur am Monatsersten, täglich jeden Tag - so reicht eine
|
|
einzige Trigger-Registrierung statt drei.
|
|
|
|
Jedes Backup ist ein eigener Zeitstempel-Unterordner mit Kopien der drei
|
|
Bestände (siehe profil.py), keine ZIP-Datei - das würde den vorhandenen
|
|
task.executor(io.open, ...)-Weg (siehe dessen Kopfkommentar in profil.py)
|
|
unnötig verkomplizieren. shutil.copy() ist wie io.open eine echte externe
|
|
Funktion und läuft deshalb über denselben task.executor-Weg.
|
|
|
|
"Backup exportieren"/"importieren" in der Oberfläche laufen dagegen rein
|
|
über den Browser (aktuell geladenes Profil + Fahrten + Tankvorgänge als eine
|
|
JSON-Datei, siehe audi-dashboard-app.js) - audi_dashboard_backup_wiederherstellen()
|
|
hier ist nur das Gegenstück zum Import, das die drei Bestände wieder
|
|
komplett überschreibt.
|
|
"""
|
|
|
|
import datetime
|
|
import json
|
|
import os
|
|
import shutil
|
|
|
|
import entitaeten
|
|
import frontend_veroeffentlichung
|
|
import profil
|
|
|
|
BACKUP_ORDNER = f"{profil.BASIS}/backups"
|
|
# entitaeten.json gehört dazu: Ohne sie wäre nach einer Wiederherstellung die
|
|
# komplette Sensor-Zuordnung aus dem Setup-Menü verloren, und weil sie sonst
|
|
# nirgends abgelegt ist, gäbe es nichts zurückzuholen.
|
|
_DATEIEN = [
|
|
"fahrzeugprofil.json",
|
|
"fahrten.jsonl",
|
|
"tankvorgaenge.jsonl",
|
|
"entitaeten.json",
|
|
]
|
|
|
|
|
|
def _backup_jetzt():
|
|
ziel = f"{BACKUP_ORDNER}/{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
os.makedirs(ziel, exist_ok=True)
|
|
for name in _DATEIEN:
|
|
quelle = f"{profil.BASIS}/{name}"
|
|
if os.path.exists(quelle):
|
|
task.executor(shutil.copy, quelle, f"{ziel}/{name}")
|
|
return ziel
|
|
|
|
|
|
def _letztes_backup_vermerken():
|
|
p = profil.profil_lesen()
|
|
if p is None:
|
|
return
|
|
p.setdefault("einstellungen", {})["letztes_backup"] = datetime.datetime.now().isoformat()
|
|
profil.profil_schreiben(p)
|
|
frontend_veroeffentlichung.profil_veroeffentlichen()
|
|
|
|
|
|
@service
|
|
def audi_dashboard_backup_jetzt():
|
|
"""Erstellt sofort ein Backup. Aufruf als pyscript.audi_dashboard_backup_jetzt."""
|
|
ziel = _backup_jetzt()
|
|
_letztes_backup_vermerken()
|
|
log.info(f"audi_dashboard: Backup erstellt unter {ziel}")
|
|
|
|
|
|
@time_trigger("cron(0 4 * * *)")
|
|
def _automatisches_backup():
|
|
p = profil.profil_lesen()
|
|
if p is None:
|
|
return
|
|
intervall = p.get("einstellungen", {}).get("backup_intervall", "aus")
|
|
heute = datetime.date.today()
|
|
faellig = (
|
|
intervall == "taeglich"
|
|
or (intervall == "woechentlich" and heute.weekday() == 0)
|
|
or (intervall == "monatlich" and heute.day == 1)
|
|
)
|
|
if not faellig:
|
|
return
|
|
ziel = _backup_jetzt()
|
|
_letztes_backup_vermerken()
|
|
log.info(f"audi_dashboard: automatisches Backup ({intervall}) erstellt unter {ziel}")
|
|
|
|
|
|
@service
|
|
def audi_dashboard_backup_wiederherstellen(profil_json=None, fahrten_json=None, tankvorgaenge_json=None, entitaeten_json=None):
|
|
"""Stellt ein über "Backup exportieren" heruntergeladenes Backup wieder
|
|
her - überschreibt Profil, Fahrten, Tankvorgänge und die Sensor-Zuordnung
|
|
komplett. Aufruf als pyscript.audi_dashboard_backup_wiederherstellen.
|
|
|
|
entitaeten_json ist optional: ältere Sicherungen, die vor dem Setup-Menü
|
|
entstanden sind, enthalten den Abschnitt nicht - dann bleibt die aktuelle
|
|
Zuordnung unangetastet, statt sie zu leeren."""
|
|
if profil_json:
|
|
p = json.loads(profil_json) if isinstance(profil_json, str) else profil_json
|
|
profil.profil_schreiben(p)
|
|
if fahrten_json is not None:
|
|
fahrten = json.loads(fahrten_json) if isinstance(fahrten_json, str) else fahrten_json
|
|
profil.fahrten_schreiben(fahrten)
|
|
if tankvorgaenge_json is not None:
|
|
tankvorgaenge = json.loads(tankvorgaenge_json) if isinstance(tankvorgaenge_json, str) else tankvorgaenge_json
|
|
profil.tankvorgaenge_schreiben(tankvorgaenge)
|
|
if entitaeten_json:
|
|
zuordnung = json.loads(entitaeten_json) if isinstance(entitaeten_json, str) else entitaeten_json
|
|
entitaeten.overrides_schreiben(zuordnung)
|
|
entitaeten.overrides_anwenden()
|
|
frontend_veroeffentlichung.alles_veroeffentlichen()
|
|
log.info("audi_dashboard: Backup wiederhergestellt")
|