Setup-Menü: Sensorkennung per Mehrheitsentscheid statt nächstem Nachbarn; Ellipsis entfernt; Filter/Tanken-Fixes
- entitaetIdKurz(): Präfix per Stimmenmehrheit über alle zugeordneten Sensoren statt des einzelnen nächsten Nachbarn - verhindert Über-Kürzung durch zufällig geteilte Namensteile zwischen verwandten Feldern (can_fuel_volume->fuel_volume, oil_change_due->due waren falsch). - .setup-feld-hinweis: Ellipsis-Kürzung entfernt, voller Sensorname sichtbar. - entitaetKandidaten(): "Nur passende Sensoren anzeigen" filtert jetzt auch nach device_class für Datum/Timestamp-Felder, nicht nur nach Domain/Einheit. - tankFelder(): Kilometerstand/Getankte Liter in .mitEinheit gewrappt (wie alle anderen km/l-Felder im Rest der App). - distanzVorschlag(): negative Vorschläge (fehlerhafte km-Basis eines vorherigen Tankvorgangs) werden nicht mehr angezeigt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4272,6 +4272,49 @@ and their `_STRECKE_SENSOR` counterparts (`oil_change_distance`/`inspection_dist
|
||||
other specifically and reduce to `"due"`/`"distance"` - each field lands next to its true sibling, not just
|
||||
"some" shared prefix.
|
||||
|
||||
**Follow-up (`.9`), two more owner-reported items on the same session:**
|
||||
1. **"Nur passende Sensoren anzeigen" only hard-filtered by domain/unit, not by the "Datum" output shown
|
||||
next to a field's headline** (`feldEinheitAnzeige()`). Fields with `device_classes: ["date",
|
||||
"timestamp"]` and no `units` (the two Ölwechsel/Inspektion date roles) fell through both the domain
|
||||
check (only restricts to `sensor` domain, which is nearly every entity) and the unit check (empty
|
||||
`units` array skips it entirely) - so with the filter ON, every `sensor.*` entity in the whole instance
|
||||
was offered, not just date/timestamp ones. `entitaetKandidaten()` now mirrors `feldEinheitAnzeige()`'s
|
||||
own decision exactly: when a field has no `units` but its `device_classes` includes `date`/`timestamp`,
|
||||
a candidate must itself have `device_class` `date` or `timestamp` to pass the hard filter. Verified live
|
||||
by reading the actual rendered candidate list for "Nächster Ölwechsel (Datum)" via the shadow DOM and
|
||||
cross-checking each of the 14 results' real `device_class` against `hass.states` - all 14 were genuine
|
||||
`date`/`timestamp` sensors, zero false positives.
|
||||
2. **`Neuer Tankvorgang` (`tankFelder()`): "Kilometerstand" and "Getankte Liter" were the only two fields
|
||||
in the form without the `.mitEinheit` wrapper** every other km/€/l field in the app already uses (see
|
||||
e.g. the Wartungsplan "Kilometerstand"/"Kosten" fields, `vSbuch()`) - so they rendered at the browser's
|
||||
default, much wider intrinsic input size instead of the app's usual fixed compact width, and without a
|
||||
unit suffix. Wrapped both in `.mitEinheit` (`km`/`l`), matching the existing convention instead of
|
||||
introducing new CSS.
|
||||
3. **Real bug: "Gefahrene Distanz" showed `-98` when opening a new Tankvorgang.** `distanzVorschlag()`
|
||||
subtracts the last recorded fill's `odometer_km` from the live odometer with no floor - live-inspected
|
||||
the actual data in `audi_ha_test` and found the most recent stored fill (`f-c7b0e818451e`, `source:
|
||||
"auto"`, 2026-08-27T17:37) carries `odometer_km: 21400`, while the vehicle's current live odometer
|
||||
(`sensor.audi_dashboard_fahrzeugstatus`'s `km`) reads `21302` - lower than that fill's own recorded
|
||||
basis, so the subtraction went negative (`21302 - 21400 = -98`, exactly the reported number). Left the
|
||||
underlying record itself untouched - unclear whether it's a genuine real-vehicle detection or leftover
|
||||
test-data noise, and either way it's the owner's own trip/tank history, not something to delete
|
||||
unilaterally. Fixed the display symptom instead: `distanzVorschlag()` now returns `""` (no suggestion)
|
||||
rather than a negative number - a negative "distance driven" is never meaningful regardless of why the
|
||||
underlying data disagrees. **Worth the owner's own look**: that stray fill's `odometer_km` (21400)
|
||||
exceeding the current live reading (21302) suggests either a bad detection or a since-corrected odometer
|
||||
somewhere in this test container's history - if it recurs on the real instance, worth checking there too.
|
||||
4. Also handled the owner's report "there is still the Text with the round collums in the Sensor Page"
|
||||
(referring to the `.7`/`.8` ellipsis fix) - re-verified directly against the live served CSS and by
|
||||
scanning every text node and placeholder in the rendered Setup popup's DOM: the ellipsis truncation is
|
||||
genuinely gone server-side, nothing found. Most likely a stale cached browser tab on the owner's side
|
||||
(this project has hit that exact class of issue repeatedly - see section N) rather than a remaining code
|
||||
defect; flagged to the owner rather than guessed around further.
|
||||
|
||||
Verified: `node --check` clean, manifest bumped to `2026.8.28.9`, `audi_ha_test` restarted and confirmed
|
||||
clean via log; all three fixes re-verified live in the browser (Tanken form screenshot showing `0` instead
|
||||
of `-98` and the shrunk `Kilometerstand`/`Getankte Liter` fields; Setup candidate-list device_class check
|
||||
above).
|
||||
|
||||
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
|
||||
@@ -4281,6 +4324,30 @@ fields checked in one pass: every FMM003 role shows its bare signal name
|
||||
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.
|
||||
|
||||
**`.7`'s "due"/"distance" reduction was itself wrong - owner caught it (`.8`): "Tankvolumen only shows
|
||||
'fuel_volume' but the Sensor is 'can_fuel_volume'... nächster Ölwechsel is 'due' but shall be
|
||||
'oil_change_due'."** `.7`'s per-entity nearest-single-relative algorithm still over-stripped: two fields from
|
||||
the SAME sensor source can themselves share a namepart that has nothing to do with the installation prefix
|
||||
(`oil_change_due`/`oil_change_distance` share `"oil_change_"`; `can_fuel_volume` shares `"can_"` with another
|
||||
CAN-sourced sensor) - the nearest relative for a given entity is exactly the one most likely to share extra,
|
||||
misleading structure with it, so picking the single nearest relative's prefix was the wrong metric all along,
|
||||
not just an edge case. Replaced with a majority vote: every OTHER mapped entity casts one "vote" for whatever
|
||||
prefix it shares with the entity being displayed (via the existing `praefixPaar()`), and the prefix with the
|
||||
most votes wins (ties favor the shorter/safer one). The true installation prefix wins because an entity
|
||||
shares it with nearly every other sensor from the same source (many votes); an incidental shared namepart
|
||||
like `"oil_change_"` is shared with only the one sibling field (one vote) - so the majority is structurally
|
||||
biased toward the correct, install-level prefix regardless of how many simultaneous sensor sources exist.
|
||||
Also removed the CSS ellipsis truncation on the sensor-identity hint (`.setup-feld-hinweis` in
|
||||
`audi-dashboard.css` had `white-space:nowrap;overflow:hidden;text-overflow:ellipsis`) per the owner's
|
||||
explicit "remove the round collum (...)" - full identifiers like `can_fuel_volume` now show in full
|
||||
(wrapping within the existing `max-width:55%` instead of truncating). Verified live in `audi_ha_test` at
|
||||
`.8`: every field re-checked against the real Setup data - `can_fuel_volume`, `oil_change_due`/
|
||||
`oil_change_distance`, `inspection_due`/`inspection_distance` all now show their full, correct suffix; the
|
||||
FMM003 cluster (`external_power_voltage`, `can_ambient_air_temperature`, ...) and the `audi_rs_4_avant_`
|
||||
cluster (`total_vehicle_mileage_read_from_can`, `front_left_door`, ...) both still strip correctly - the
|
||||
majority-vote fix didn't regress the two-simultaneous-sources case `.7` was built to solve. `node --check`
|
||||
clean, manifest bumped to `2026.8.28.8`, `audi_ha_test` restarted and confirmed clean via log.
|
||||
|
||||
---
|
||||
|
||||
## Working conventions (observed — keep them)
|
||||
|
||||
@@ -2759,7 +2759,15 @@ function entitaetKandidaten(feldKey, suchtext, aktuellerWert) {
|
||||
if (e.id === aktuellerWert) return true;
|
||||
if (setupNurPassend) {
|
||||
if (feld.domains && feld.domains.length && !feld.domains.includes(e.domain)) return false;
|
||||
if (feld.units && feld.units.length && !feld.units.includes(e.unit)) return false;
|
||||
if (feld.units && feld.units.length) {
|
||||
if (!feld.units.includes(e.unit)) return false;
|
||||
} else if (feld.device_classes && (feld.device_classes.includes("date") || feld.device_classes.includes("timestamp"))) {
|
||||
// Kein Einheiten-Feld, aber "Datum" ist der angezeigte Output
|
||||
// (feldEinheitAnzeige()) - dieselbe Bedingung hier gespiegelt, damit
|
||||
// "Nur passende Sensoren anzeigen" wirklich nur Sensoren mit
|
||||
// passendem Output zeigt, nicht jeden beliebigen sensor.*.
|
||||
if (e.deviceClass !== "date" && e.deviceClass !== "timestamp") return false;
|
||||
}
|
||||
}
|
||||
if (text && !(e.id.toLowerCase().includes(text) || e.name.toLowerCase().includes(text))) return false;
|
||||
return true;
|
||||
@@ -3517,7 +3525,13 @@ function distanzVorschlag(aktuellerKm) {
|
||||
if (aktuellerKm === "" || aktuellerKm == null || !FILLS.length) return "";
|
||||
const letzte = FILLS.reduce((a, b) => (new Date(b.ts) > new Date(a.ts) ? b : a));
|
||||
if (letzte.odometer_km == null) return "";
|
||||
return Math.round((aktuellerKm - letzte.odometer_km) * 10) / 10;
|
||||
const distanz = Math.round((aktuellerKm - letzte.odometer_km) * 10) / 10;
|
||||
// Ein negativer Wert ist nie eine gefahrene Distanz - kann bei einer
|
||||
// fehlerhaft/verzögert erfassten Kilometerstand-Basis des letzten
|
||||
// Tankvorgangs vorkommen (z. B. ein "auto" erkannter Vorgang, dessen
|
||||
// km-Basis über einen späteren, korrigierten/niedrigeren Kilometerstand
|
||||
// hinausragt). Lieber kein Vorschlag als ein irreführender negativer.
|
||||
return distanz < 0 ? "" : distanz;
|
||||
}
|
||||
|
||||
function tankFelder(praefix, f) {
|
||||
@@ -3541,12 +3555,14 @@ function tankFelder(praefix, f) {
|
||||
return `
|
||||
<div class="feld"><label>Datum</label><input type="date" id="${praefix}Datum" value="${datum}"></div>
|
||||
<div class="feld"><label>Uhrzeit</label><input type="time" id="${praefix}Zeit" value="${zeit}"></div>
|
||||
<div class="feld"><label>Kilometerstand</label><input type="number" id="${praefix}Km" value="${km}"></div>
|
||||
<div class="feld"><label>Kilometerstand</label>
|
||||
<span class="mitEinheit"><input type="number" id="${praefix}Km" value="${km}"><span>km</span></span></div>
|
||||
<div class="feld"><label>Gefahrene Distanz</label>
|
||||
<span class="mitEinheit"><input type="number" step="1" id="${praefix}Distanz" value="${distanz}" placeholder="0"><span>km</span></span></div>
|
||||
<div class="feld"><label>Kraftstoff</label>
|
||||
<select id="${praefix}Kraftstoff">${KRAFTSTOFFSORTEN.map((k) => `<option ${k === kraftstoff ? "selected" : ""}>${k}</option>`).join("")}</select></div>
|
||||
<div class="feld"><label>Getankte Liter</label><input type="number" step="0.01" id="${praefix}Liter" data-tankpreisfeld="${praefix}" value="${liter}" placeholder="0,00"></div>
|
||||
<div class="feld"><label>Getankte Liter</label>
|
||||
<span class="mitEinheit"><input type="number" step="0.01" id="${praefix}Liter" data-tankpreisfeld="${praefix}" value="${liter}" placeholder="0,00"><span>l</span></span></div>
|
||||
<div class="feld"><label>Kosten (real, nach Rabatt)</label>
|
||||
<span class="mitEinheit"><input type="number" step="0.01" id="${praefix}Kosten" data-tankpreisfeld="${praefix}" value="${kosten}" placeholder="0,00"><span>€</span></span></div>
|
||||
${CAR.smartdeal.aktiv ? `<div class="feld"><label>SmartDeal-Ersparnis</label>
|
||||
@@ -4082,35 +4098,47 @@ function setupAndereIdsOhneDomain(ausser) {
|
||||
return ids.map((id) => id.includes(".") ? id.split(".").slice(1).join(".") : id);
|
||||
}
|
||||
|
||||
/* 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_").
|
||||
/* Die Entity-ID ohne Domain und ohne das installationsspezifische Präfix -
|
||||
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. */
|
||||
Das Präfix ist NICHT das mit dem einzelnen nächsten Verwandten geteilte
|
||||
(frühere Fassung) - das kürzte in der Praxis zu viel: zwei Felder
|
||||
derselben Sensorquelle können selbst wieder ein gemeinsames Namens-
|
||||
Teilstück haben, das nichts mit der Installation zu tun hat (z. B. teilen
|
||||
sich "oil_change_due" und "oil_change_distance" das Wortstück
|
||||
"oil_change_", "can_fuel_volume" teilt sich "can_" mit einem anderen
|
||||
CAN-Sensor) - genau das hat der Nutzer live beobachtet: "can_fuel_volume"
|
||||
wurde zu bloß "fuel_volume", "oil_change_due" zu bloß "due". Stattdessen
|
||||
zählt jedes Präfix, das eine Entität mit irgendeiner ANDEREN teilt, als
|
||||
eine Stimme; das Präfix mit den MEISTEN Stimmen gewinnt. Das echte
|
||||
Installationspräfix teilt sich die Entität mit fast allen anderen
|
||||
Sensoren ihrer Quelle (viele Stimmen), ein zufällig geteiltes
|
||||
Namens-Teilstück nur mit dem einen verwandten Feld (eine Stimme) - die
|
||||
Mehrheit entscheidet also richtig, ohne einen einzelnen "nächsten"
|
||||
Nachbarn zu bevorzugen. Bei Gleichstand gewinnt das kürzere Präfix
|
||||
(vorsichtiger - weniger Kürzung im Zweifel). Mehrere gleichzeitige
|
||||
Sensorquellen (z. B. FMM003 und eine zweite, "audi_rs_4_avant_"-
|
||||
präfixte Integration) bleiben dabei weiterhin unabhängig voneinander
|
||||
korrekt gekürzt, da die Stimmen pro Entität und nicht global gezählt
|
||||
werden. 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;
|
||||
let bestesPraefix = null, besteLaenge = 0;
|
||||
const stimmen = new Map();
|
||||
for (const andere of setupAndereIdsOhneDomain(entityId)) {
|
||||
const kandidat = praefixPaar(ohneDomain, andere);
|
||||
if (kandidat && kandidat.length > besteLaenge) { bestesPraefix = kandidat; besteLaenge = kandidat.length; }
|
||||
if (!kandidat) continue;
|
||||
stimmen.set(kandidat, (stimmen.get(kandidat) || 0) + 1);
|
||||
}
|
||||
let bestesPraefix = null, besteStimmen = 0;
|
||||
for (const [kandidat, anzahl] of stimmen) {
|
||||
const besser = anzahl > besteStimmen || (anzahl === besteStimmen && bestesPraefix && kandidat.length < bestesPraefix.length);
|
||||
if (besser) { bestesPraefix = kandidat; besteStimmen = anzahl; }
|
||||
}
|
||||
return bestesPraefix ? ohneDomain.slice(bestesPraefix.length) : ohneDomain;
|
||||
}
|
||||
|
||||
@@ -1265,7 +1265,7 @@ button.leaf:active { background: var(--tile-2); }
|
||||
.setup-feld-einheit { color: var(--fg3); font-size: 12.5px; }
|
||||
.setup-feld-hinweis {
|
||||
font-size: 12.5px; color: var(--fg2); line-height: 1.4; text-align: right;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 55%;
|
||||
max-width: 55%;
|
||||
}
|
||||
.setup-unterfelder { display: flex; flex-direction: column; gap: 12px; }
|
||||
.setup-unterfeld { display: flex; flex-direction: column; gap: 4px; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.8.28.7",
|
||||
"version": "2026.8.28.9",
|
||||
"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