d8b12da36d
Das Backend liegt jetzt als custom_components/audi_dashboard/ vor - eine normale Home-Assistant-Integration mit Config-Flow, einer sensor-Plattform und 18 Diensten. Damit ist die App über HACS installierbar; bis das Repo auf GitHub gespiegelt ist (HACS spricht ausschließlich mit GitHub), installiert homeassistant/installationspaket/install.ps1 denselben Ordner ohne HACS. Fünf Installationsschritte entfallen ersatzlos: der pyscript:-Block, der panel_custom:-Block, das Kopieren der Oberfläche nach www/, das langlebige Zugriffstoken (der Verlauf wird direkt über die recorder-API gelesen) und "pip install pypdf" (steht in manifest.json). Das Fahrzeugprofil legt die Integration beim ersten Start aus ihrer Vorlage an. Drei alte Schwächen sind dabei mit erledigt: - Die Nutzlast landet nicht mehr in der Recorder-Datenbank (_unrecorded_attributes - das kann nur eine echte Entität). - Eine laufende Fahrt überlebt einen Neustart (Store statt Arbeitsspeicher); fiel sie während eines Ausfalls ins Ende, schließt nach_neustart_fortsetzen() sie beim letzten aufgezeichneten Zeitpunkt. - Sensor-Zuordnungen wirken sofort - die Zustandsbeobachter werden neu gebunden, der Neustart-Hinweis und der Neustart-Dienst sind weg. Namensvertrag geändert, beide Oberflächen mitgezogen: pyscript.audi_dashboard_x -> sensor.audi_dashboard_x, pyscript.audi_dashboard_y -> audi_dashboard.y. Eine Companion-App vom alten Stand findet nach dem Umstieg nichts mehr und muss neu gebaut werden; das Panel liegt in der Integration und kann nicht driften. Der selbstgebaute Updater entfällt - HACS ist die Update-Mechanik, die Home Assistant kennt. Die Versionierung schrumpft auf eine Quelle: manifest.json. Geprüft am laufenden Testcontainer (Container byteweise identisch mit dem Repo): alle 18 Dienste, Panel, Config-Entry neu laden, Historienimport, echter Shell-Beleg in-process, Neuinstallation im Wegwerf-Container blank mit automatisch nachinstalliertem pypdf. Companion-App: tsc sauber, 112/112 Tests, beide Rauchtests gegen das laufende Backend grün. Belegparser 8/8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
2.5 KiB
Python
65 lines
2.5 KiB
Python
"""Langfristige Aufzeichnung der 12V-Batteriespannung (Mein Audi -> Zustand
|
|
-> Batteriespannung antippen).
|
|
|
|
Eine über die Jahre absinkende Spannung deutet auf eine alternde Batterie hin
|
|
- dafür braucht es einen Verlauf, nicht nur den aktuellen Wert.
|
|
|
|
Ein Eintrag pro Tag ({datum, min, min_ts, max, max_ts}) reicht dafür und hält
|
|
die Datei auch über viele Jahre klein, selbst wenn der Sensor mehrfach pro
|
|
Stunde meldet (aktive Fahrt: Motor an/aus, Ladezustand des Alternators
|
|
wechselt laufend). Die Prüfung läuft deshalb alle 5 Minuten und trägt jede
|
|
Messung in den Tageseintrag ein; die Ablage erweitert dabei nur min/max samt
|
|
dem genauen Zeitstempel der jeweiligen Extremmessung - robust gegen Neustarts
|
|
und schwankende Erreichbarkeit.
|
|
|
|
Der Tagesminimalwert ist die für die Entladung aussagekräftige Größe: er fällt
|
|
typischerweise auf den Moment kurz nach dem Start, bevor der Alternator die
|
|
Ladespannung hochfährt - also am nächsten an die "echte" Ruhespannung heran,
|
|
ohne 2+ Stunden Standzeit abwarten zu müssen. Deshalb verwendet auch die
|
|
Ladezustands-/Gesundheitsauswertung im Frontend (AGM_RUHE_MAX_V) den
|
|
Tagesminimalwert, nicht den Maximalwert.
|
|
|
|
Es wird bewusst NICHT gefiltert, welche Messwerte gespeichert werden - min UND
|
|
max jedes Tages werden roh aufgezeichnet. Die AGM-Ruhespannungs-Grenze wird
|
|
erst beim Anzeigen angewendet: Tage, an denen selbst der Minimalwert über der
|
|
Grenze liegt (Fahrzeug nie im Ruhezustand beobachtet, z. B. bei einer sehr
|
|
langen Fahrt), tauchen dort nicht in der Statistik auf.
|
|
|
|
Ohne zugeordneten BATTERIE_SENSOR passiert hier bewusst nichts, statt
|
|
Platzhalter-Nullwerte aufzuzeichnen.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import datetime
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
from .veroeffentlichung import zustand_oder_none
|
|
|
|
if TYPE_CHECKING:
|
|
from .koordinator import Koordinator
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
async def pruefen(k: Koordinator) -> None:
|
|
sensor = k.zuordnung.werte.BATTERIE_SENSOR
|
|
if not sensor:
|
|
return
|
|
wert = zustand_oder_none(k.hass, sensor)
|
|
if wert is None:
|
|
return
|
|
try:
|
|
spannung = float(wert)
|
|
except (TypeError, ValueError):
|
|
return
|
|
|
|
jetzt = datetime.datetime.now(datetime.UTC)
|
|
heute = datetime.date.today().isoformat()
|
|
await k.ablage.batterieverlauf_tageswert_aktualisieren(
|
|
heute, jetzt.isoformat(), spannung
|
|
)
|
|
await k.batterieverlauf_veroeffentlichen()
|
|
_LOGGER.debug("Batteriespannung erfasst (%s: %s V)", heute, spannung)
|