Setup-Menue: Datenverlust verhindern, Vorbelegung korrigieren
Befund 1 (Datenverlust): Solange pyscript.audi_dashboard_entitaeten nicht
veroeffentlicht ist, liefert setupKatalog() eine leere Liste. Das Fenster ging
trotzdem auf - ohne eine Zeile, ohne Meldung - und ein Klick auf Speichern
schrieb {}. Weil overrides_schreiben() die Datei vollstaendig ersetzt, waren
damit alle Zuordnungen weg, sichtbar erst beim naechsten Neustart. Jetzt drei
Riegel: das Setup laesst sich ohne Katalog gar nicht oeffnen, Speichern bricht
bei leerer Zuordnung ab, und die Entitaet wird im Nachlade-Pfad mitgeholt -
der sie bisher als einzige der sechs ausliess.
Befund 3 (gleiche Sensoren): Der Vorschlag haengt nicht vom Index ab, also
bekamen alle vier Positionen einer Liste denselben Sensor. Der
Sicherheitscheck haette danach viermal dieselbe Tuer geprueft und Sicher
abgestellt gemeldet, obwohl drei Tueren nie geprueft wurden. Vergebene IDs
werden jetzt mitgefuehrt und uebersprungen.
Befund 4 (beliebige Sensoren): entitaetScore vergibt fuer einen reinen
Domain-Treffer bereits 3 Punkte - die Schwelle 3 war damit immer erfuellt und
belegte jedes leere Feld mit dem erstbesten Sensor der Domain, in willkuer-
licher Reihenfolge. Schwelle jetzt 5, also mindestens ein Stichworttreffer.
Geprueft mit den echten Funktionen aus der Datei: vier Tuerpositionen
erhalten vier verschiedene Sensoren, ein Bewegungsmelder wird der Zuendung
nicht mehr zugeordnet, ein Sensor mit passendem Namen dagegen schon.
Ausserdem wird die Sensor-Zuordnung jetzt im Browser-Backup mitgesichert und
beim Import wieder eingespielt.
This commit is contained in:
@@ -1966,24 +1966,56 @@ function entitaetKandidaten(feldKey, suchtext, aktuellerWert) {
|
||||
return liste.slice(0, 60);
|
||||
}
|
||||
|
||||
/* Ab diesem Wert gilt ein Vorschlag als belastbar. entitaetScore() vergibt für
|
||||
einen reinen Domain-Treffer bereits 3 Punkte - eine Schwelle von 3 wäre
|
||||
deshalb immer erfüllt und hätte jedes leere Feld mit dem erstbesten Sensor
|
||||
der passenden Domain belegt (Reihenfolge aus Object.keys(HASS.states), also
|
||||
willkürlich). 5 verlangt mindestens einen Stichworttreffer. */
|
||||
const SETUP_VORSCHLAG_SCHWELLE = 5;
|
||||
|
||||
/* Bester Vorschlag für ein Feld, der noch nicht anderweitig vergeben ist.
|
||||
Das "noch nicht vergeben" ist der Punkt: ohne diese Prüfung bekamen alle
|
||||
vier Positionen einer Liste (Türen, Fenster, Schlösser) denselben Sensor,
|
||||
weil der Vorschlag nicht vom Index abhängt. Der Sicherheitscheck hätte
|
||||
danach viermal dieselbe Tür geprüft und "Sicher abgestellt" gemeldet,
|
||||
obwohl drei Türen nie geprüft wurden. */
|
||||
function setupVorschlag(feld, schonVergeben) {
|
||||
for (const kandidat of entitaetKandidaten(feld.key, "", "")) {
|
||||
if (schonVergeben.has(kandidat.id)) continue;
|
||||
if (entitaetScore(feld, kandidat, "") < SETUP_VORSCHLAG_SCHWELLE) return "";
|
||||
return kandidat.id;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function setupOeffnen() {
|
||||
if (!setupBereit()) return;
|
||||
ALLE_ENTITAETEN = alleEntitaeten();
|
||||
setupNurPassend = true;
|
||||
const werte = (ENTITAETEN && ENTITAETEN.werte) || {};
|
||||
const zuordnung = {};
|
||||
/* Bereits gesetzte und bereits vorgeschlagene IDs, damit kein Sensor
|
||||
zweimal landet. Vorbelegte Werte kommen zuerst hinein. */
|
||||
const vergeben = new Set();
|
||||
for (const feld of setupKatalog()) {
|
||||
const vorhandeneWerte = feld.liste ? (werte[feld.key] || []) : [werte[feld.key]];
|
||||
for (const id of vorhandeneWerte) if (id) vergeben.add(id);
|
||||
}
|
||||
for (const feld of setupKatalog()) {
|
||||
if (feld.liste) {
|
||||
zuordnung[feld.key] = feld.positionen.map((_, i) => {
|
||||
const vorhanden = (werte[feld.key] || [])[i];
|
||||
if (vorhanden) return vorhanden;
|
||||
const vorschlag = entitaetKandidaten(feld.key, "", "")[0];
|
||||
return vorschlag && entitaetScore(feld, vorschlag, "") >= 3 ? vorschlag.id : "";
|
||||
const vorschlag = setupVorschlag(feld, vergeben);
|
||||
if (vorschlag) vergeben.add(vorschlag);
|
||||
return vorschlag;
|
||||
});
|
||||
} else {
|
||||
const vorhanden = werte[feld.key];
|
||||
if (vorhanden) { zuordnung[feld.key] = vorhanden; continue; }
|
||||
const vorschlag = entitaetKandidaten(feld.key, "", "")[0];
|
||||
zuordnung[feld.key] = vorschlag && entitaetScore(feld, vorschlag, "") >= 3 ? vorschlag.id : "";
|
||||
const vorschlag = setupVorschlag(feld, vergeben);
|
||||
if (vorschlag) vergeben.add(vorschlag);
|
||||
zuordnung[feld.key] = vorschlag;
|
||||
}
|
||||
}
|
||||
setupZuordnung = zuordnung;
|
||||
@@ -1991,6 +2023,19 @@ function setupOeffnen() {
|
||||
setupSucheOffen = null; setupSuchtext = ""; setupSpeichertLaeuft = false;
|
||||
setupOffen = true; render();
|
||||
}
|
||||
|
||||
/* Das Setup lässt sich erst öffnen, wenn der Katalog wirklich da ist.
|
||||
Ohne diese Prüfung öffnete sich das Fenster leer (setupKatalog() liefert
|
||||
dann []), und ein Klick auf "Speichern" schrieb {} - was entitaeten.json
|
||||
vollständig ersetzt und damit ALLE Zuordnungen löscht. Weil die Werte im
|
||||
laufenden Prozess erhalten bleiben, wäre das erst beim nächsten Neustart
|
||||
aufgefallen. */
|
||||
function setupBereit() {
|
||||
if (setupKatalog().length) return true;
|
||||
hinweis("Setup noch nicht bereit",
|
||||
"Die Liste der Sensor-Rollen wurde noch nicht vom Server geladen. Nach einem Neustart dauert das einen Moment - bitte gleich noch einmal versuchen.");
|
||||
return false;
|
||||
}
|
||||
function setupSchliessen() {
|
||||
setupOffen = false; setupSucheOffen = null; setupZuordnung = null; setupZuordnungStart = null; render();
|
||||
}
|
||||
@@ -2025,6 +2070,13 @@ function setupGeaenderteTriggerFelder() {
|
||||
}
|
||||
|
||||
function setupSpeichernAusfuehren() {
|
||||
// Zweiter Riegel gegen den Datenverlust-Pfad: selbst wenn das Fenster auf
|
||||
// irgendeinem Weg leer aufgeht, wird nichts geschrieben. overrides_schreiben()
|
||||
// ersetzt die Datei vollstaendig - ein leeres Objekt loescht alles.
|
||||
if (!setupZuordnung || !Object.keys(setupZuordnung).length) {
|
||||
hinweis("Nichts zu speichern", "Es ist keine Zuordnung geladen. Bitte das Setup schliessen und neu oeffnen.");
|
||||
return;
|
||||
}
|
||||
setupSpeichertLaeuft = true; render();
|
||||
(async () => {
|
||||
try {
|
||||
@@ -3707,6 +3759,9 @@ function nachladeAnstossen() {
|
||||
NACHGELADEN.tank = finden("pyscript.audi_dashboard_tankvorgaenge") || NACHGELADEN.tank;
|
||||
NACHGELADEN.status = finden("pyscript.audi_dashboard_fahrzeugstatus") || NACHGELADEN.status;
|
||||
NACHGELADEN.batt = finden("pyscript.audi_dashboard_batterieverlauf") || NACHGELADEN.batt;
|
||||
// Ohne diese Zeile bleibt der Setup-Katalog nach einem HA-Neustart leer,
|
||||
// bis zufaellig ein Push kommt - siehe setupBereit().
|
||||
NACHGELADEN.entitaeten = finden("pyscript.audi_dashboard_entitaeten") || NACHGELADEN.entitaeten;
|
||||
} catch (e) {
|
||||
// wird beim naechsten Versuch erneut probiert
|
||||
}
|
||||
@@ -3725,7 +3780,7 @@ async function datenLaden(erzwingen) {
|
||||
// Unabhängig vom geaendert-Block unten gelesen (der trackt nur profil/
|
||||
// fahrten/tank/status/batt) - ändert sich erst nach einem Setup-Speichern,
|
||||
// nicht Teil des normalen Update-Rhythmus.
|
||||
const entitaetenState = HASS.states["pyscript.audi_dashboard_entitaeten"];
|
||||
const entitaetenState = HASS.states["pyscript.audi_dashboard_entitaeten"] || NACHGELADEN.entitaeten;
|
||||
if (entitaetenState && entitaetenState.attributes) ENTITAETEN = entitaetenState.attributes.daten;
|
||||
|
||||
if (belegState && belegState !== letzterBelegState) {
|
||||
|
||||
Reference in New Issue
Block a user