"""Audi Dashboard - Fahrtenbuch, Tankstatistik und Fahrzeugzustand als eigene Home-Assistant-Integration. Diese Datei ist der Lebenszyklus: einrichten, entladen, neu laden. Die Fach- logik liegt in den Modulen daneben, der laufende Zustand im Koordinator. WAS DIE INTEGRATION MITBRINGT, WAS FRÜHER VON HAND EINGERICHTET WERDEN MUSSTE ----------------------------------------------------------------------------- Die pyscript-Fassung brauchte fünf getrennte Handgriffe in der Installation: einen `pyscript:`-Block mit allow_all_imports und hass_is_global, einen `panel_custom:`-Block, das Kopieren der Oberflächen-Dateien nach /config/www/, ein langlebiges Zugriffstoken in einer Textdatei und ein `pip install pypdf` im Container. Jeder davon konnte vergessen werden, und mehrere scheiterten still. Alle fünf sind hier weg: - Das Panel meldet die Integration selbst an (kein YAML, kein Neustart). - Die Oberflächen-Dateien liegen in frontend/ und werden von hier unter einem eigenen URL-Pfad ausgeliefert (kein Kopieren nach www/). - Den Verlauf liest die Integration direkt über die recorder-API (kein Token, siehe verlauf.py). - pypdf steht über manifest.json bereit (kein pip-Aufruf, siehe belege.py). - Eingerichtet wird über die Oberfläche (config_flow), nicht über YAML. Was NICHT hierher gehört und deshalb weiterhin von Hand in die configuration.yaml kommt: der `recorder:`-Block mit der verlängerten Aufbewahrung (recorder_snippet.yaml). Der betrifft die ganze Instanz und ihre Datenbankgröße - das ist eine Entscheidung des Betreibers, keine, die eine einzelne Integration für ihn treffen darf. """ from __future__ import annotations import logging import os from homeassistant.components.http import StaticPathConfig from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.components import frontend, panel_custom from homeassistant.loader import async_get_integration from . import dienste from .const import ( DOMAIN, PANEL_ICON, PANEL_KOMPONENTE, PANEL_TITEL, PANEL_URL, STATIK_ORDNER, STATIK_URL, ) from .koordinator import Koordinator _LOGGER = logging.getLogger(__name__) PLATTFORMEN = [Platform.SENSOR] type AudiConfigEntry = ConfigEntry[Koordinator] async def async_setup_entry(hass: HomeAssistant, entry: AudiConfigEntry) -> bool: integration = await async_get_integration(hass, DOMAIN) version = str(integration.version) koordinator = Koordinator(hass, entry, version) entry.runtime_data = koordinator await _statik_anmelden(hass) await koordinator.starten() await hass.config_entries.async_forward_entry_setups(entry, PLATTFORMEN) await _panel_anmelden(hass, version) dienste.registrieren(hass, koordinator) # Ändert sich die Einrichtung, wird die Integration neu geladen statt # halb angepasst - das ist die einzige Variante, bei der nichts von der # alten Einrichtung übrig bleiben kann. entry.async_on_unload(entry.add_update_listener(_neu_laden)) _LOGGER.info("Audi Dashboard %s eingerichtet", version) return True async def async_unload_entry(hass: HomeAssistant, entry: AudiConfigEntry) -> bool: geladen = await hass.config_entries.async_unload_platforms(entry, PLATTFORMEN) if not geladen: return False dienste.abmelden(hass) frontend.async_remove_panel(hass, PANEL_URL) await entry.runtime_data.beenden() return True async def _neu_laden(hass: HomeAssistant, entry: AudiConfigEntry) -> None: await hass.config_entries.async_reload(entry.entry_id) async def _statik_anmelden(hass: HomeAssistant) -> None: """Liefert frontend/ unter STATIK_URL aus. Nur einmal je Home-Assistant-Lauf: aiohttp-Routen lassen sich nicht wieder entfernen, ein zweiter Versuch nach einem Neu-Laden der Integration würde scheitern. Das ist unkritisch - der Pfad zeigt auf einen Ordner, dessen Inhalt sich beim Neu-Laden nicht ändert. cache_headers=False ist Absicht: die Dateien werden von install.ps1 ersetzt, und ein Browser, der eine alte Fassung 31 Tage lang aus dem Cache bedient (die HA-Vorgabe für statische Pfade), wäre genau die stille Veraltung, die das Versions-Verfahren verhindern soll.""" if hass.data.get(DOMAIN, {}).get("statik"): return ordner = os.path.join(os.path.dirname(__file__), STATIK_ORDNER) await hass.http.async_register_static_paths( [StaticPathConfig(STATIK_URL, ordner, False)] ) hass.data.setdefault(DOMAIN, {})["statik"] = True async def _panel_anmelden(hass: HomeAssistant, version: str) -> None: """Meldet den Sidebar-Eintrag an. Die Version hängt als Query-Parameter an der Modul-URL: sie ändert sich genau dann, wenn eine neue Fassung installiert wurde, und ist damit der passende Cache-Brecher für den Lade-Stub.""" await panel_custom.async_register_panel( hass, frontend_url_path=PANEL_URL, webcomponent_name=PANEL_KOMPONENTE, sidebar_title=PANEL_TITEL, sidebar_icon=PANEL_ICON, module_url=f"{STATIK_URL}/audi-dashboard-panel.js?v={version}", embed_iframe=False, require_admin=False, config={"version": version, "statik": STATIK_URL}, )