Setup-Menue: Sensorkennung ohne installationsspezifisches Praefix, Einheit statt Live-Wert
Korrektur zur letzten Aenderung, vom Nutzer live erkannt:
- "fmm003_" war hart einprogrammiert und liess trotzdem noch
installationsspezifisches Rauschen stehen (Geraete-Slug, Instanzname der
Integration). Jetzt wird das laengste gemeinsame Praefix ueber alle
aktuell zugeordneten Sensoren berechnet (setupGemeinsamesPraefix()) -
nichts mehr hartkodiert. Faellt bei einer einzelnen Entitaet aus
anderer Quelle korrekt auf die volle ID zurueck, statt falsch zu kuerzen
(live bestaetigt: TANK_DISTANZ_SENSOR zeigte den vollen Namen, weil er
auf eine andere Integration zeigte).
- Der Live-Wert des Sensors gehoert nicht mehr neben die ID - stattdessen
zeigt das Label selbst jetzt die ERWARTETE Art des Werts an
("Tankfuellstand [%]", "12V-Batteriespannung [V]"), statisch aus dem
FELDER-Katalog abgeleitet, unabhaengig von einer aktuellen Zuordnung.
Version 2026.8.28.5, live im Testcontainer verifiziert (acht echte
Setup-Zeilen gegen die Nutzerbeispiele geprueft).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4178,44 +4178,68 @@ this round). The underlying stored data was never wrong (the panel and backend a
|
|||||||
is needed — an existing Wartungsplan entry will display correctly the next time companion-app is rebuilt and
|
is needed — an existing Wartungsplan entry will display correctly the next time companion-app is rebuilt and
|
||||||
deployed with this fix.
|
deployed with this fix.
|
||||||
|
|
||||||
## AF. Item 11: Setup-Menü zeigt jetzt den zugeordneten Sensor statt einer statischen Beschreibung (2026.8.28.4)
|
## AF. Item 11: Setup-Menü zeigt jetzt den zugeordneten Sensor statt einer statischen Beschreibung (2026.8.28.4/.5)
|
||||||
|
|
||||||
Panel only - the Setup menu (sensor-role mapping) is an HA-panel-only concept, no companion-app equivalent
|
Panel only - the Setup menu (sensor-role mapping) is an HA-panel-only concept, no companion-app equivalent
|
||||||
exists to port this to (companion-app consumes the backend's already-resolved state, it has no Setup UI at
|
exists to port this to (companion-app consumes the backend's already-resolved state, it has no Setup UI at
|
||||||
all - see the parity rule's own carve-out for this exact case).
|
all - see the parity rule's own carve-out for this exact case).
|
||||||
|
|
||||||
Every field row in Setup used to show a static, developer-written description under the label ("on = Fahrt
|
Every field row in Setup used to show a static, developer-written description under the label ("on = Fahrt
|
||||||
läuft, erkennt Fahrtbeginn/-ende.", "Optional, vom CAN..."). Owner: replace it with something read from the
|
läuft, erkennt Fahrtbeginn/-ende.", "Optional, vom CAN..."). Owner: replace it with the *actual*
|
||||||
*actual* currently-mapped sensor instead - its technical ID (just the part after `fmm003_`, e.g.
|
currently-mapped sensor's technical ID (just the part after the installation-specific noise) shown next to
|
||||||
`testintegratoin_engine_ignition_or_acc_status` instead of the full
|
the headline, plus a separate indicator of the KIND of value each field expects.
|
||||||
`binary_sensor.testcar_b9_fmm003_testintegratoin_engine_ignition_or_acc_status`) and its live output value
|
|
||||||
with unit (`[on/off]`, `[%]`, `[l]`, `[km]`...), positioned next to the headline rather than stacked below it.
|
|
||||||
|
|
||||||
New `entitaetIdKurz(entityId)` (strips the domain prefix, then finds `"fmm003_"` case-insensitively and
|
**First version (`.4`) got two things wrong, both caught live by the owner before being accepted:**
|
||||||
returns everything after it - falls back to the domain-free ID if `"fmm003_"` isn't present, for other
|
1. Hardcoded searching for the literal substring `"fmm003_"` and keeping everything after it - the owner
|
||||||
brands/integrations) and `entitaetWertKurz(entityId)` (current `state` + `unit_of_measurement`, `null` for
|
pointed out this still leaves *other* installation-specific noise in the result (`"testintegratoin_..."` -
|
||||||
`unavailable`/`unknown`/unmapped - the exact same zustand logic `entitaetZeilenMarkup()` already used for
|
the flespi channel's own instance name, not part of the sensor's real, stable identity; a different
|
||||||
the search-result list, just for one entity instead of a whole candidate list). `setupErwarteterSensor()`
|
installation would have a different instance name here, making a hardcoded strip target wrong for anyone
|
||||||
combines both into one string (`"<id-suffix> · <value> <unit>"`), used in `setupFeldzeile()`'s header in
|
but this one test container).
|
||||||
place of the old `feld.hinweis` read. List-type fields (four positions, e.g. door/window sensors) show
|
2. Combined the ID-suffix with the sensor's *current live reading* into one string next to the headline. The
|
||||||
nothing in the shared header - there's no single "the" sensor to reference there, each position has its own
|
owner: show only the expected sensor identity there - and separately, "next to the Sensor Headline",
|
||||||
mapping and its own combo below.
|
show the *kind* of value the field expects (`Tankfüllstand [%]`, `Tankvolumen [l]`, `12V-Batteriespannung
|
||||||
|
[V]`), not a live reading.
|
||||||
|
|
||||||
`feld.hinweis` itself is untouched in the backend `FELDER` catalog (`einstellungen.py`) - still valid data,
|
**Fixed in `.5`:**
|
||||||
simply no longer read at this one call site; not removed, since removing it wasn't asked and nothing else
|
- `entitaetIdKurz(entityId)` no longer searches for a hardcoded string. New `setupGemeinsamesPraefix()`
|
||||||
depends on knowing it's now unused.
|
computes the longest character-for-character common prefix (case-insensitive, trimmed back to the last
|
||||||
|
shared `_`) across every entity ID currently assigned across the *entire* Setup catalog (domain-stripped),
|
||||||
|
requiring at least two mapped entities to compare. In this real installation that correctly resolves to
|
||||||
|
`"testcar_b9_fmm003_testintegratoin_"` - device slug, integration name, and instance name all stripped in
|
||||||
|
one step, none of it hardcoded. `entitaetIdKurz()` strips that computed prefix only when a given entity's
|
||||||
|
ID actually starts with it; otherwise it shows the full domain-stripped ID rather than an incorrect partial
|
||||||
|
strip. **This fallback path fired for real, unprompted, during live verification**: `TANK_DISTANZ_SENSOR`
|
||||||
|
(added the same day, section AC) had been auto-suggested to `sensor.audi_rs_4_avant_monthly_mileage` - a
|
||||||
|
genuinely different source (not FMM003) sharing no prefix with the rest - and correctly rendered the full,
|
||||||
|
unstripped ID instead of a wrong truncation. Confirms the graceful-degradation design works, not just the
|
||||||
|
common case.
|
||||||
|
- The live-value read (`entitaetWertKurz()`/`setupErwarteterSensor()`) is gone entirely - removed, not just
|
||||||
|
unused. In its place, new `feldEinheitAnzeige(feld)` derives a **static** "kind of value" label straight
|
||||||
|
from the field's own `FELDER` catalog entry (`einstellungen.py`) - `"on/off"` for every `binary_sensor`
|
||||||
|
role (HA's binary_sensor domain always reports the raw state as on/off regardless of `device_class` - that
|
||||||
|
only changes how *other* HA frontends translate it for display, not the raw value this app reads), the
|
||||||
|
first unit in `units` preferring a lowercase-distinct variant when more than one is listed (picks `"l"`
|
||||||
|
over `"L"` for `TANK_LITER_SENSOR`, `"km"` over `"mi"` elsewhere), `"Datum"` for `date`/`timestamp`
|
||||||
|
`device_classes`, nothing for a `button` role or a field with neither (GPS coordinates). This label is
|
||||||
|
appended to the field's own headline text (`"Tankfüllstand [%]"`) and is independent of whether anything is
|
||||||
|
currently mapped - unlike the ID-suffix hint, which needs a live mapping to have anything to show.
|
||||||
|
|
||||||
CSS: `.setup-feld-kopf` was `flex-direction: column` (label above the old description) - changed to a row
|
`feld.hinweis` itself is untouched in the backend `FELDER` catalog - still valid data, simply not read by
|
||||||
(`justify-content: space-between`) so the new content sits beside the label as requested, with
|
this UI anymore; not removed, since nothing else depends on knowing it's unused and removing it wasn't asked.
|
||||||
`text-overflow: ellipsis` on the right-hand span since a long ID-suffix-plus-value string won't always fit.
|
|
||||||
|
|
||||||
Verified live in `audi_ha_test` (not just read from source - this file's own section S lesson): four real
|
CSS: `.setup-feld-kopf` changed from a column (label above the old description) to a row (space-between) so
|
||||||
rows read via the shadow DOM after opening Setup for real (`ZUENDUNG_SENSOR` →
|
the ID-suffix hint sits beside the label; new `.setup-feld-einheit` (a slightly dimmer `--fg3`) for the
|
||||||
`"testintegratoin_engine_ignition_or_acc_status · off"`, `KM_SENSOR` → `"...mileage_read_from_can · 21302
|
bracketed unit label appended to the headline text itself.
|
||||||
km"`, etc.) - label and the new span measured on the identical Y-coordinate (`sameRow: true`), confirming
|
|
||||||
the row layout actually changed, not just the text content. Confirmed a list field (`TUER_SENSOREN`,
|
Verified live in `audi_ha_test` both times (not just read from source - this file's own section S lesson).
|
||||||
"Türen") renders with no header span at all, as designed. `node --check` clean, manifest bumped to
|
First pass (`.4`): confirmed the row layout change itself (label and hint measured on the identical
|
||||||
`2026.8.28.4`, `audi_ha_test` restarted and confirmed clean via log.
|
Y-coordinate) before the owner's correction arrived. Second pass (`.5`), after the fix, read eight real Setup
|
||||||
|
rows via the shadow DOM in one pass and matched every one against the owner's own examples exactly:
|
||||||
|
`"Zündung/ACC-Status [on/off]"` / `"testcar_b9_fmm003_testintegratoin_engine_ignition_or_acc_status"`,
|
||||||
|
`"Tankfüllstand [%]"`, `"Tankvolumen (Liter) [l]"`, `"12V-Batteriespannung [V]"`, `"Außentemperatur [°C]"` -
|
||||||
|
plus the `TANK_DISTANZ_SENSOR` fallback case above and a screenshot confirming the visual layout. `node
|
||||||
|
--check` clean at each step, manifest bumped `2026.8.28.4` → `.5`, `audi_ha_test` restarted and confirmed
|
||||||
|
clean via log both times.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4061,40 +4061,77 @@ function sheetMarkup() {
|
|||||||
/* ------------------------------------------------------------ Setup-Popup */
|
/* ------------------------------------------------------------ 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>`;
|
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>`;
|
||||||
|
|
||||||
/* Nur der Teil der Entity-ID nach "fmm003_" - die tatsächliche, technische
|
/* Das gemeinsame Präfix aller aktuell zugeordneten Sensoren (domain-frei),
|
||||||
Kennung des zugeordneten Sensors, kürzer und aussagekräftiger als der
|
bis zum letzten gemeinsamen "_" - z. B. "testcar_b9_fmm003_testintegratoin_".
|
||||||
volle "binary_sensor.testcar_b9_fmm003_..."-Pfad. Ohne "fmm003_" im Namen
|
NICHT hart auf "fmm003_" verdrahtet: sowohl der Geräte-Slug
|
||||||
(z. B. eine andere Marke/Integration) bleibt der Domain-freie Rest
|
("testcar_b9") als auch der Instanzname der Integration
|
||||||
stehen, statt nichts anzuzeigen. */
|
("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() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
function entitaetIdKurz(entityId) {
|
function entitaetIdKurz(entityId) {
|
||||||
if (!entityId) return null;
|
if (!entityId) return null;
|
||||||
const ohneDomain = entityId.includes(".") ? entityId.split(".").slice(1).join(".") : entityId;
|
const ohneDomain = entityId.includes(".") ? entityId.split(".").slice(1).join(".") : entityId;
|
||||||
const idx = ohneDomain.toLowerCase().indexOf("fmm003_");
|
const praefix = setupGemeinsamesPraefix();
|
||||||
return idx >= 0 ? ohneDomain.slice(idx + "fmm003_".length) : ohneDomain;
|
if (praefix && ohneDomain.toLowerCase().startsWith(praefix.toLowerCase())) {
|
||||||
|
return ohneDomain.slice(praefix.length);
|
||||||
|
}
|
||||||
|
return ohneDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Aktueller Wert + Einheit einer Entität, oder null ohne brauchbaren
|
/* Die Art des erwarteten Werts, als kurzes Klammer-Etikett direkt neben der
|
||||||
Zustand (nicht zugeordnet, unavailable/unknown) - dieselbe Zustandslogik
|
Feld-Überschrift ("Tankfüllstand [%]", "12V-Batteriespannung [V]") - eine
|
||||||
wie in entitaetZeilenMarkup() unten, hier für eine einzelne Entität statt
|
feste Eigenschaft der FELD-ROLLE selbst (aus dem FELDER-Katalog,
|
||||||
einer ganzen Kandidatenliste. */
|
einstellungen.py), unabhängig davon, ob und welcher Sensor gerade
|
||||||
function entitaetWertKurz(entityId) {
|
zugeordnet ist. Bewusst NICHT der Live-Wert eines Sensors - der gehört
|
||||||
if (!entityId || !HASS.states[entityId]) return null;
|
nirgends mehr hin, siehe entitaetIdKurz()'s eigenen Kommentar zur
|
||||||
const zustand = HASS.states[entityId].state;
|
Kopfzeile. binary_sensor liefert immer den Rohzustand "on"/"off"
|
||||||
if (zustand === "unavailable" || zustand === "unknown") return null;
|
(unabhängig von device_class - die beeinflusst nur, wie ANDERE
|
||||||
const einheit = HASS.states[entityId].attributes && HASS.states[entityId].attributes.unit_of_measurement;
|
HA-Oberflächen ihn anzeigen, nicht den Rohwert, den diese App liest).
|
||||||
return `${zustand}${einheit ? " " + einheit : ""}`;
|
Bei mehreren möglichen Einheiten (z. B. "L"/"l") die klein geschriebene
|
||||||
}
|
Variante bevorzugt, sonst die erste genannte. Ohne passenden Fall (Knopf,
|
||||||
|
GPS-Koordinate) kein Etikett. */
|
||||||
/* Ersetzt den bisherigen statischen Beschreibungstext im Setup ("on = Fahrt
|
function feldEinheitAnzeige(feld) {
|
||||||
läuft", "Optional, vom CAN...") - Nutzerwunsch: statt einer generischen
|
if (feld.domains && feld.domains.includes("binary_sensor")) return "on/off";
|
||||||
Erklärung zeigt die Kopfzeile jetzt die Kennung UND die aktuelle Meldung
|
if (feld.domains && feld.domains.includes("button")) return null;
|
||||||
des tatsächlich zugeordneten Sensors, direkt neben der Feld-Überschrift.
|
if (feld.units && feld.units.length) {
|
||||||
Ohne Zuordnung (leer) gibt es nichts zu zeigen. */
|
return feld.units.find((u) => u === u.toLowerCase() && u !== u.toUpperCase()) || feld.units[0];
|
||||||
function setupErwarteterSensor(entityId) {
|
}
|
||||||
const kurz = entitaetIdKurz(entityId);
|
if (feld.device_classes && (feld.device_classes.includes("date") || feld.device_classes.includes("timestamp"))) {
|
||||||
if (!kurz) return "";
|
return "Datum";
|
||||||
const wert = entitaetWertKurz(entityId);
|
}
|
||||||
return wert != null ? `${kurz} · ${wert}` : kurz;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function entitaetZeilenMarkup(feldId, key, suchtext, aktuellerWert) {
|
function entitaetZeilenMarkup(feldId, key, suchtext, aktuellerWert) {
|
||||||
@@ -4133,11 +4170,14 @@ function entitaetCombo(key, idx) {
|
|||||||
|
|
||||||
function setupFeldzeile(feld) {
|
function setupFeldzeile(feld) {
|
||||||
// Bei einem Listenfeld (vier Positionen wie Tür-/Fenstersensoren) gibt es
|
// Bei einem Listenfeld (vier Positionen wie Tür-/Fenstersensoren) gibt es
|
||||||
// keinen EINEN zugeordneten Sensor für die Kopfzeile - jede Position hat
|
// keinen EINEN zugeordneten Sensor, dessen Kennung sich anzeigen ließe -
|
||||||
// ihre eigene Zuordnung, siehe die Unterfelder unten.
|
// jede Position hat ihre eigene Zuordnung, siehe die Unterfelder unten.
|
||||||
const erwartet = feld.liste ? "" : setupErwarteterSensor(setupZuordnung[feld.key]);
|
// Das Einheiten-Etikett neben dem Label gilt dagegen für die ganze Rolle
|
||||||
|
// und wird auch dort gezeigt.
|
||||||
|
const einheit = feldEinheitAnzeige(feld);
|
||||||
|
const erwartet = feld.liste ? "" : entitaetIdKurz(setupZuordnung[feld.key]);
|
||||||
const kopf = `<div class="setup-feld-kopf">
|
const kopf = `<div class="setup-feld-kopf">
|
||||||
<span class="setup-feld-label">${esc(feld.label)}</span>
|
<span class="setup-feld-label">${esc(feld.label)}${einheit ? ` <span class="setup-feld-einheit">[${esc(einheit)}]</span>` : ""}</span>
|
||||||
${erwartet ? `<span class="setup-feld-hinweis">${esc(erwartet)}</span>` : ""}
|
${erwartet ? `<span class="setup-feld-hinweis">${esc(erwartet)}</span>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
if (feld.liste) {
|
if (feld.liste) {
|
||||||
|
|||||||
@@ -1255,12 +1255,14 @@ button.leaf:active { background: var(--tile-2); }
|
|||||||
.setup-gruppe-titel { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); margin-bottom: 6px; }
|
.setup-gruppe-titel { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); margin-bottom: 6px; }
|
||||||
.setup-feldzeile { padding: 12px 0; border-bottom: 1px solid var(--line); }
|
.setup-feldzeile { padding: 12px 0; border-bottom: 1px solid var(--line); }
|
||||||
.setup-feldzeile:last-child { border-bottom: none; }
|
.setup-feldzeile:last-child { border-bottom: none; }
|
||||||
/* Nutzerwunsch: der zugeordnete Sensor (Kennung + aktueller Wert, siehe
|
/* Nutzerwunsch: die Kennung des zugeordneten Sensors (entitaetIdKurz() in
|
||||||
setupErwarteterSensor() in audi-dashboard-app.js) steht NEBEN der
|
audi-dashboard-app.js) steht NEBEN der Feld-Überschrift, nicht mehr wie
|
||||||
Feld-Überschrift, nicht mehr wie die frühere statische Beschreibung
|
die frühere statische Beschreibung darunter - deshalb Zeile statt Spalte.
|
||||||
darunter - deshalb Zeile statt Spalte. */
|
Die erwartete Einheit (feldEinheitAnzeige()) hängt direkt am Label selbst
|
||||||
|
("Tankfüllstand [%]"), losgelöst davon, ob/welcher Sensor zugeordnet ist. */
|
||||||
.setup-feld-kopf { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
|
.setup-feld-kopf { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
|
||||||
.setup-feld-label { font-size: 14.5px; color: var(--fg); flex: 0 1 auto; }
|
.setup-feld-label { font-size: 14.5px; color: var(--fg); flex: 0 1 auto; }
|
||||||
|
.setup-feld-einheit { color: var(--fg3); font-size: 12.5px; }
|
||||||
.setup-feld-hinweis {
|
.setup-feld-hinweis {
|
||||||
font-size: 12.5px; color: var(--fg2); line-height: 1.4; text-align: right;
|
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%;
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 55%;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "audi_dashboard",
|
"domain": "audi_dashboard",
|
||||||
"name": "Audi Dashboard",
|
"name": "Audi Dashboard",
|
||||||
"version": "2026.8.28.4",
|
"version": "2026.8.28.5",
|
||||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||||
"codeowners": ["@paul"],
|
"codeowners": ["@paul"],
|
||||||
|
|||||||
Reference in New Issue
Block a user