vCard-Import fürs Autohaus, Selbst-Update-Logging und UI-Feinschliff
- Service -> Autohaus -> Zahnrad hat jetzt einen "vCard importieren"-Knopf (öffnet den Dateiauswahldialog für .vcf). Liest FN/ORG, TEL, EMAIL, ADR und befüllt das Formular - in beiden Frontends, jeweils passend zum bestehenden Speicherverhalten (Panel speichert sofort, companion-app erst über den vorhandenen "Speichern"-Knopf). - update_pruefen/update_installieren loggten bisher nur bei einem Fehler - ein erfolgreicher Aufruf war im Log nicht von "noch nicht ausgeführt" zu unterscheiden. Jetzt auch eine Erfolgsmeldung. - Panel: der feste Hinweis auf den fehlenden Gitea-Token stand dauerhaft in der Integration-Update-Kachel, auch wenn ein Token längst hinterlegt war. Erscheint jetzt nur noch als Teil einer echten Fehlermeldung. Details in AGENTS.md, Abschnitte J (Nachtrag) und K. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -228,6 +228,10 @@ def registrieren(hass: HomeAssistant, k: Koordinator) -> None:
|
||||
jetzt = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
try:
|
||||
ergebnis = await aktualisierung.version_pruefen(hass, token, k.version)
|
||||
_LOGGER.info(
|
||||
"Update-Prüfung: Gitea hat Version %s (installiert: %s, verfügbar: %s)",
|
||||
ergebnis["version"], k.version, ergebnis["verfuegbar"],
|
||||
)
|
||||
k.letzte_update_pruefung = {
|
||||
"verfuegbar": ergebnis["verfuegbar"],
|
||||
"version": ergebnis["version"],
|
||||
@@ -255,6 +259,10 @@ def registrieren(hass: HomeAssistant, k: Koordinator) -> None:
|
||||
jetzt = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
try:
|
||||
ergebnis = await aktualisierung.update_installieren(hass, token)
|
||||
_LOGGER.info(
|
||||
"Update installiert: Version %s - Home Assistant muss neu gestartet werden.",
|
||||
ergebnis["version"],
|
||||
)
|
||||
k.letzte_update_pruefung = {
|
||||
"verfuegbar": False,
|
||||
"version": ergebnis["version"],
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"2026.8.24.4","sha256":"cbce1baa785a3577fc65168b9f8b2094ecd844aadc2ac36f99d6be8ccb4cb8c1","bytes":231567,"gebaut":"2026-08-24T09:39:59Z"}
|
||||
{"version":"2026.8.24.5","sha256":"378a1cbc7f5daa3d8c5d74734a4c09f30557b243a8ce4d720240bf911fbeff76","bytes":232096,"gebaut":"2026-08-24T10:29:33Z"}
|
||||
Binary file not shown.
@@ -1969,6 +1969,44 @@ function termine() {
|
||||
];
|
||||
}
|
||||
|
||||
/* Liest Name, Adresse, Telefon und E-Mail aus einer vCard (.vcf) - deckt
|
||||
vCard 3.0/4.0 so weit ab, wie ein typischer Werkstatt-Kontakt aus dem
|
||||
Adressbuch (iOS/Android/Outlook) sie befüllt. Kein vollständiger Parser,
|
||||
nur die vier Felder, die das Autohaus-Formular kennt.
|
||||
|
||||
Gefaltete Zeilen (Fortsetzung mit führendem Leerzeichen/Tab, RFC 6350
|
||||
§3.2) werden vor dem zeilenweisen Parsen entfaltet; ADR setzt sich aus
|
||||
PO-Box, Zusatz, Straße, Ort, Region, PLZ, Land zusammen (in dieser
|
||||
Reihenfolge, mit Semikolon getrennt) - hier zu einer lesbaren
|
||||
Ein-Zeilen-Adresse zusammengefasst, weil das Formularfeld nur eine
|
||||
Zeile kennt. Mehrfache TEL/EMAIL/ADR-Zeilen: die erste gewinnt. */
|
||||
function vcardParsen(text) {
|
||||
const entfaltet = String(text).replace(/\r\n/g, "\n").replace(/\n[ \t]/g, "");
|
||||
const zeilen = entfaltet.split("\n").map((z) => z.trim()).filter(Boolean);
|
||||
const entwerten = (s) => s.replace(/\\n/gi, "\n").replace(/\\,/g, ",").replace(/\\;/g, ";").replace(/\\\\/g, "\\");
|
||||
let name = null, org = null, tel = null, mail = null, adresse = null;
|
||||
for (const zeile of zeilen) {
|
||||
const doppelpunkt = zeile.indexOf(":");
|
||||
if (doppelpunkt < 0) continue;
|
||||
const eigenschaft = zeile.slice(0, doppelpunkt).split(";")[0].toUpperCase();
|
||||
const wert = zeile.slice(doppelpunkt + 1);
|
||||
if (eigenschaft === "FN" && !name) name = entwerten(wert);
|
||||
else if (eigenschaft === "ORG" && !org) org = entwerten(wert.split(";")[0]);
|
||||
else if (eigenschaft === "TEL" && !tel) tel = entwerten(wert);
|
||||
else if (eigenschaft === "EMAIL" && !mail) mail = entwerten(wert);
|
||||
else if (eigenschaft === "ADR" && !adresse) {
|
||||
const teile = wert.split(";").map(entwerten);
|
||||
const strasse = [teile[1], teile[2]].filter(Boolean).join(" ");
|
||||
const ort = [teile[5], teile[3]].filter(Boolean).join(" ");
|
||||
adresse = [strasse, ort, teile[6]].filter(Boolean).join(", ") || null;
|
||||
}
|
||||
}
|
||||
if (!name && !org && !tel && !mail && !adresse) {
|
||||
throw new Error("Keine lesbaren Kontaktfelder gefunden - ist das eine .vcf-Datei?");
|
||||
}
|
||||
return { name: name || org || null, adresse, tel, mail };
|
||||
}
|
||||
|
||||
function vWerkstatt() {
|
||||
const a = CAR.service.autohaus;
|
||||
return `
|
||||
@@ -1978,6 +2016,8 @@ function vWerkstatt() {
|
||||
<div class="feld"><label>Telefon</label><input type="tel" value="${esc(a.tel)}" data-ah="tel"></div>
|
||||
<div class="feld" style="border-bottom:0"><label>E-Mail</label>
|
||||
<input type="email" value="${esc(a.mail || "")}" data-ah="mail" placeholder="noch offen"></div>
|
||||
<label class="aktion" style="display:block;text-align:center;margin-top:16px;cursor:pointer">
|
||||
vCard importieren<input type="file" accept=".vcf,text/vcard" data-vcard-import hidden></label>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -2405,9 +2445,7 @@ function vEinst() {
|
||||
<button class="aktion" style="margin-top:14px" data-update="installieren">Update installieren</button>`;
|
||||
}
|
||||
return `<span style="display:block;margin-top:8px;color:var(--fg2);font-size:13.5px;line-height:1.6">
|
||||
${u ? `Integration ist aktuell${u.geprueft_am ? ` (geprüft ${dedat(new Date(u.geprueft_am))})` : ""}.` : "Noch nicht geprüft."}
|
||||
Ein Gitea-Zugriffstoken muss zuvor unter Einstellungen -> Geräte & Dienste -> Audi
|
||||
Dashboard -> Konfigurieren eingetragen sein.</span>
|
||||
${u ? `Integration ist aktuell${u.geprueft_am ? ` (geprüft ${dedat(new Date(u.geprueft_am))})` : ""}.` : "Noch nicht geprüft."}</span>
|
||||
<button class="aktion" style="margin-top:14px" data-update="pruefen">Auf Update prüfen</button>`;
|
||||
})()}
|
||||
${INTEGRATION_UPDATE && INTEGRATION_UPDATE.fehler
|
||||
@@ -4612,6 +4650,23 @@ function ereignisseVerdrahten() {
|
||||
leser.readAsText(datei);
|
||||
return;
|
||||
}
|
||||
if (e.target.dataset.vcardImport !== undefined) {
|
||||
const datei = e.target.files[0]; if (!datei) return;
|
||||
const leser = new FileReader();
|
||||
leser.onload = () => {
|
||||
try {
|
||||
const felder = vcardParsen(leser.result);
|
||||
if (felder.name) CAR.service.autohaus.name = felder.name;
|
||||
if (felder.adresse) CAR.service.autohaus.adresse = felder.adresse;
|
||||
if (felder.tel) CAR.service.autohaus.tel = felder.tel;
|
||||
if (felder.mail) CAR.service.autohaus.mail = felder.mail;
|
||||
profilSpeichern(); render();
|
||||
} catch (err) { hinweis("vCard konnte nicht gelesen werden", err.message); }
|
||||
};
|
||||
leser.readAsText(datei);
|
||||
e.target.value = "";
|
||||
return;
|
||||
}
|
||||
if (e.target.dataset.bildupload !== undefined) {
|
||||
const datei = e.target.files[0]; if (!datei) return;
|
||||
const dateiname = e.target.dataset.bildupload;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.8.24.4",
|
||||
"version": "2026.8.24.5",
|
||||
"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