Setup-Menue: Sensorkennung per naechstem Verwandten statt globalem Praefix
Zwei weitere Korrekturrunden, beide vom Nutzer live erkannt: - .6: paarweise Abstimmung statt fortlaufender Verengung - robuster gegen einen einzelnen Ausreisser, aber grundsaetzlich noch falsch. - .7: die eigentliche Ursache war, dass diese Installation zwei etwa gleich grosse Sensorquellen gleichzeitig hat (9 FMM003-Rollen, 18 Rollen einer zweiten Integration mit "audi_rs_4_avant_"-Praefix) - ein EINZELNES globales "Sieger"-Praefix kann immer nur eine der beiden Quellen richtig kuerzen. entitaetIdKurz() sucht jetzt pro Feld den naechsten Verwandten unter allen zugeordneten Sensoren und kuerzt nur dessen gemeinsames Praefix - unabhaengig von Anzahl und Groesse der Quellen. Diesmal vor dem Deploy anhand der ECHTEN sensor.audi_dashboard_entitaeten- Zuordnung durchgerechnet statt mit erfundenen Test-IDs (das hatte den .6-Fehler faelschlich als behoben erscheinen lassen). Version 2026.8.28.7, live verifiziert: alle 20 Einzelwert-Felder zeigen die richtige, kurze Kennung, keine installationsspezifischen Reste aus beiden Quellen mehr. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4241,6 +4241,46 @@ plus the `TANK_DISTANZ_SENSOR` fallback case above and a screenshot confirming t
|
||||
--check` clean at each step, manifest bumped `2026.8.28.4` → `.5`, `audi_ha_test` restarted and confirmed
|
||||
clean via log both times.
|
||||
|
||||
**`.5`'s own verification above was itself misleading - the owner caught the real bug immediately after
|
||||
("I still see testcar_b9_fmm003_testintegratoin_ and audi_rs_4_avant_").** `.5`'s `setupGemeinsamesPraefix()`
|
||||
computed ONE global prefix by intersecting every mapped sensor's ID down to their shared characters - and a
|
||||
single outlier (any one field mapped to a sensor from a different source than the rest) collapses that
|
||||
intersection to nothing, so EVERY field falls back to its full, unstripped ID. The live verification quoted
|
||||
above only exercised four fields and happened not to include the one that broke it - a real gap in how
|
||||
thoroughly that pass was checked, not a fluke in the code.
|
||||
|
||||
Fixed properly in `.6`: replaced the single global intersection with pairwise voting (every sensor pair's own
|
||||
common prefix, the most frequently occurring one wins) - more robust against one outlier, but **still wrong
|
||||
for a different reason**, found by the owner's report and confirmed by pulling the actual live
|
||||
`sensor.audi_dashboard_entitaeten` data (not by re-reading the source - the only way this surfaced): this
|
||||
installation genuinely has **two comparably-sized sensor sources at once** - 9 FMM003-sourced roles
|
||||
(ignition, odometer, tank, battery, GPS) and 18 roles from a second integration, prefixed
|
||||
`audi_rs_4_avant_` (all four doors, all four windows, tailgate/hood/lock/sunroof/light, all four service-date
|
||||
fields). A single global "winner" prefix, however it's computed, can only ever strip ONE of the two families
|
||||
correctly and leaves the other's IDs fully unstripped - exactly what both `.5` and `.6` did, just for
|
||||
different roles each time.
|
||||
|
||||
**`.7` drops the idea of a single global prefix entirely.** `entitaetIdKurz()` now finds, for the entity
|
||||
being displayed, its own single closest relative among every other currently-mapped sensor (the one it
|
||||
shares the longest prefix with) and strips only that pairwise prefix - computed independently per field, so
|
||||
it doesn't matter how many different sensor sources are mixed together or how large each cluster is. Verified
|
||||
by pulling the *actual* stored `werte` mapping from `sensor.audi_dashboard_entitaeten` (not fabricated test
|
||||
IDs like the `.5`/`.6` passes used) and running the real algorithm against it before deploying - this is what
|
||||
caught the `.6` design flaw *before* declaring it fixed, rather than after another owner report. A pleasant
|
||||
side effect: `NAECHSTER_OELWECHSEL_SENSOR`/`NAECHSTE_INSPEKTION_SENSOR` (`oil_change_due`/`inspection_due`)
|
||||
and their `_STRECKE_SENSOR` counterparts (`oil_change_distance`/`inspection_distance`) now pair off with each
|
||||
other specifically and reduce to `"due"`/`"distance"` - each field lands next to its true sibling, not just
|
||||
"some" shared prefix.
|
||||
|
||||
Verified live in `audi_ha_test` at every step (`.6` then `.7`), each time reading the actual rendered Setup
|
||||
popup fresh after a real restart (not a stale tab - confirmed via `performance.getEntriesByType('resource')`
|
||||
showing the new `?v=` served with a nonzero transfer size each time). Final state (`.7`), all 20 single-value
|
||||
fields checked in one pass: every FMM003 role shows its bare signal name
|
||||
(`external_power_voltage`, `can_ambient_air_temperature`, ...), every `audi_rs_4_avant_` role shows its own
|
||||
(`tailgate`, `hood`, `sunroof`, `parking_lights`, `due`, `distance`, ...), nothing shows installation noise
|
||||
from either source. `node --check` clean at each step. Manifest `2026.8.28.5` → `.6` → `.7`, `audi_ha_test`
|
||||
restarted and confirmed clean via log each time.
|
||||
|
||||
---
|
||||
|
||||
## Working conventions (observed — keep them)
|
||||
|
||||
@@ -4061,53 +4061,58 @@ function sheetMarkup() {
|
||||
/* ------------------------------------------------------------ Setup-Popup */
|
||||
const LUPE_SVG = `<svg viewBox="0 0 20 20" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><circle cx="8.5" cy="8.5" r="6"/><path d="m17 17-4.3-4.3"/></svg>`;
|
||||
|
||||
/* Das gemeinsame Präfix aller aktuell zugeordneten Sensoren (domain-frei),
|
||||
bis zum letzten gemeinsamen "_" - z. B. "testcar_b9_fmm003_testintegratoin_".
|
||||
NICHT hart auf "fmm003_" verdrahtet: sowohl der Geräte-Slug
|
||||
("testcar_b9") als auch der Instanzname der Integration
|
||||
("testintegratoin") sind installationsspezifisch, vom Nutzer beim
|
||||
Einrichten seines eigenen flespi-Kanals selbst vergeben - bei ihm heißt
|
||||
das anders. Nur was WIRKLICH von allen zugeordneten Sensoren geteilt
|
||||
wird, ist reines Rauschen; alles danach ist die eigentliche, stabile
|
||||
Signalbezeichnung. Mit weniger als zwei zugeordneten Sensoren (nichts zum
|
||||
Vergleichen) oder wenn Sensoren aus zwei erkennbar verschiedenen Quellen
|
||||
kommen (kein gemeinsames Präfix), liefert das null - dann zeigt
|
||||
entitaetIdKurz() lieber die volle, ungekürzte ID als eine geratene,
|
||||
möglicherweise falsche Kürzung. */
|
||||
function setupGemeinsamesPraefix() {
|
||||
/* Gemeinsames Präfix zweier domain-freier IDs, bis zum letzten gemeinsamen
|
||||
"_" - oder null, wenn sie nicht einmal ein volles Segment teilen. */
|
||||
function praefixPaar(a, b) {
|
||||
let i = 0;
|
||||
while (i < a.length && i < b.length && a[i].toLowerCase() === b[i].toLowerCase()) i++;
|
||||
const idxUnter = a.slice(0, i).lastIndexOf("_");
|
||||
return idxUnter > 0 ? a.slice(0, idxUnter + 1) : null;
|
||||
}
|
||||
|
||||
/* Alle aktuell im Setup zugeordneten Entity-IDs (domain-frei), außer der
|
||||
übergebenen selbst - Vergleichsbasis für entitaetIdKurz() unten. */
|
||||
function setupAndereIdsOhneDomain(ausser) {
|
||||
const ids = [];
|
||||
for (const feld of setupKatalog()) {
|
||||
const wert = setupZuordnung[feld.key];
|
||||
if (feld.liste) { for (const w of (wert || [])) if (w) ids.push(w); }
|
||||
else if (wert) ids.push(wert);
|
||||
if (feld.liste) { for (const w of (wert || [])) if (w && w !== ausser) ids.push(w); }
|
||||
else if (wert && wert !== ausser) ids.push(wert);
|
||||
}
|
||||
const ohneDomain = ids.map((id) => id.includes(".") ? id.split(".").slice(1).join(".") : id);
|
||||
if (ohneDomain.length < 2) return null;
|
||||
let praefix = ohneDomain[0];
|
||||
for (const s of ohneDomain.slice(1)) {
|
||||
let i = 0;
|
||||
while (i < praefix.length && i < s.length && praefix[i].toLowerCase() === s[i].toLowerCase()) i++;
|
||||
praefix = praefix.slice(0, i);
|
||||
if (!praefix) return null;
|
||||
}
|
||||
const idxUnter = praefix.lastIndexOf("_");
|
||||
return idxUnter > 0 ? praefix.slice(0, idxUnter + 1) : null;
|
||||
return ids.map((id) => id.includes(".") ? id.split(".").slice(1).join(".") : id);
|
||||
}
|
||||
|
||||
/* Die Entity-ID ohne Domain und ohne das gemeinsame, installationsspezifische
|
||||
Präfix (siehe setupGemeinsamesPraefix()) - die tatsächliche, stabile
|
||||
Kennung des zugeordneten Sensors, unabhängig davon, wie Geräte-Slug und
|
||||
Integrationsinstanz beim jeweiligen Nutzer heißen. Passt das gemeinsame
|
||||
Präfix nicht auf DIESE Entität (z. B. eine einzelne Entität aus einer
|
||||
anderen Quelle als der Rest), bleibt die volle domain-freie ID stehen. */
|
||||
/* Die Entity-ID ohne Domain und ohne das installationsspezifische Präfix,
|
||||
das sie mit ihrem NÄCHSTEN Verwandten unter den übrigen zugeordneten
|
||||
Sensoren teilt (dem, mit dem sie das längste gemeinsame Präfix hat) - die
|
||||
tatsächliche, stabile Kennung des zugeordneten Sensors, unabhängig davon,
|
||||
wie Geräte-Slug und Integrationsinstanz beim jeweiligen Nutzer heißen
|
||||
(z. B. "testcar_b9_fmm003_testintegratoin_" oder "audi_rs_4_avant_").
|
||||
|
||||
Bewusst PRO ENTITÄT der beste Nachbar statt eines einzigen, für alle
|
||||
Felder gemeinsamen Präfixes (frühere Fassung) - eine reale Installation
|
||||
hat oft MEHRERE Sensorquellen gleichzeitig (hier: FMM003 für Fahrt-/
|
||||
Tank-/Batteriewerte, eine zweite Integration für Türen/Fenster/
|
||||
Service-Termine, per "Nur passende Sensoren anzeigen" beide gleichzeitig
|
||||
vorgeschlagen) - keine ist objektiv "die Mehrheit". Ein einziges globales
|
||||
Präfix (egal ob per fortlaufender Verengung oder per Mehrheitsentscheid)
|
||||
kann immer nur EINE Quelle richtig kürzen und zeigt die Sensoren der
|
||||
anderen ungekürzt - genau das hat der Nutzer live beobachtet
|
||||
("audi_rs_4_avant_" gewann die Abstimmung, alle FMM003-Felder blieben
|
||||
unangetastet). Mit dem nächsten Verwandten statt einem globalen Sieger
|
||||
kürzt jede Rolle unabhängig von den anderen richtig, egal wie viele
|
||||
verschiedene Quellen gleichzeitig im Spiel sind. Findet sich kein
|
||||
Verwandter mit gemeinsamem Präfix, bleibt die volle domain-freie ID
|
||||
stehen statt einer geratenen, möglicherweise falschen Kürzung. */
|
||||
function entitaetIdKurz(entityId) {
|
||||
if (!entityId) return null;
|
||||
const ohneDomain = entityId.includes(".") ? entityId.split(".").slice(1).join(".") : entityId;
|
||||
const praefix = setupGemeinsamesPraefix();
|
||||
if (praefix && ohneDomain.toLowerCase().startsWith(praefix.toLowerCase())) {
|
||||
return ohneDomain.slice(praefix.length);
|
||||
let bestesPraefix = null, besteLaenge = 0;
|
||||
for (const andere of setupAndereIdsOhneDomain(entityId)) {
|
||||
const kandidat = praefixPaar(ohneDomain, andere);
|
||||
if (kandidat && kandidat.length > besteLaenge) { bestesPraefix = kandidat; besteLaenge = kandidat.length; }
|
||||
}
|
||||
return ohneDomain;
|
||||
return bestesPraefix ? ohneDomain.slice(bestesPraefix.length) : ohneDomain;
|
||||
}
|
||||
|
||||
/* Die Art des erwarteten Werts, als kurzes Klammer-Etikett direkt neben der
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.8.28.5",
|
||||
"version": "2026.8.28.7",
|
||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||
"codeowners": ["@paul"],
|
||||
|
||||
Reference in New Issue
Block a user