diff --git a/AGENTS.md b/AGENTS.md
index ba25df8..7b2a299 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -2296,6 +2296,47 @@ store, never `configuration.yaml`. A read-only-scoped Gitea token is enough; the
now say install.ps1 is the only way *to install*; the self-update is now the preferred way *to
update* an already-running instance.
+**Two fixes found during the owner's own first live test (2026-08-24, same day).** First attempt
+against the test container ("nothing changed") turned out to be a real user error caught by log
+inspection, not a bug: the check was clicked before the token had actually been saved in the
+Options Flow — `dienste.py`'s log showed exactly why (`Kein Gitea-Token hinterlegt`), confirming the
+error path works. But this surfaced a genuine observability gap: `update_pruefen`/
+`update_installieren` only ever logged on *failure* — a successful check was silent, so "nothing in
+the log" was ambiguous between "not clicked yet" and "worked fine." Fixed by adding an `_LOGGER.info`
+line on the success path of both services. Separately, the owner asked for the panel's idle-state
+text ("Ein Gitea-Zugriffstoken muss zuvor... eingetragen sein") to only appear as part of an actual
+error, not as a permanent reminder cluttering the tile before the first check — removed from the
+idle branch; the same information already surfaces via `integration_update.fehler` when it's
+actually true. companion-app never had this line in the first place, so no parity fix needed there.
+
+### K) vCard import for the workshop contact (built 2026-08-24)
+
+Service → Autohaus (gear icon) now has a "vCard importieren" button next to the existing form
+fields, opening a file picker for a `.vcf` contact exported from a phone/desktop address book. Reads
+`FN`/`ORG` (name, `FN` preferred), `TEL`, `EMAIL`, and `ADR` (the seven-part vCard address —
+PO-Box/extended/street/locality/region/postal-code/country — joined into one readable line, since
+the form's address field is a single string). Deliberately not a full vCard parser: only the four
+fields the Autohaus form already has. Unfolds RFC 6350 §3.2 folded lines (CRLF + one WSP removed
+entirely, not replaced with a space) and un-escapes `\,`/`\;`/`\n`/`\\` before use. Multiple
+`TEL`/`EMAIL`/`ADR` lines: first one wins.
+
+- Panel: `vcardParsen()` in `audi-dashboard-app.js`, wired into `vWerkstatt()`'s new hidden
+ `data-vcard-import` file input via the existing central `change` listener — same
+ parse-then-`hinweis()`-on-error pattern as the existing JSON profile/backup import handlers.
+ Updates `CAR.service.autohaus` fields directly and calls the existing `profilSpeichern()` (saves
+ immediately, matching this field's existing per-keystroke-save behavior).
+ Same isolated-function shape as `entpacken_pruefen_tauschen()` in section J's self-updater: parse
+ logic takes no dependency on `HASS`/global state, so it's directly testable.
+- companion-app: new `src/screens/vcard.ts` (`vcardParsen()`, 7 unit tests in `vcard.test.ts`
+ covering folding, escaping, ORG fallback, first-wins on repeats, and the two "nothing readable"
+ error cases), wired into `Werkstatt()` in `Service.tsx`. Unlike the panel, this only populates the
+ local `entwurf` draft state — the existing explicit "Speichern" button still does the actual save,
+ matching this screen's existing edit-then-confirm pattern rather than the panel's live-save one.
+ Empty fields from the vCard never overwrite non-empty draft values.
+
+Verified: `tsc --noEmit` clean, all 134 companion-app tests pass (127 + 7 new), panel JS re-checked
+with `node --check`. Manifest bumped to `2026.8.24.5`, `npm run ota` rerun (both frontends changed).
+
---
## Working conventions (observed — keep them)
diff --git a/companion-app/src/screens/Service.tsx b/companion-app/src/screens/Service.tsx
index f618171..c37badc 100644
--- a/companion-app/src/screens/Service.tsx
+++ b/companion-app/src/screens/Service.tsx
@@ -8,7 +8,7 @@
* eingestellten Intervallen abweichen.
*/
-import { useState } from "react"
+import { useRef, useState } from "react"
import { ActionButton, Feld, Tile } from "@audi-dash/ui"
@@ -25,6 +25,7 @@ import type { SeitenName } from "../navigation"
import { SymbolInspektion, SymbolOelwechsel } from "../symbole"
import { Leerzustand, NaviKachel, Wertzeile, Werteliste, bestaetigen } from "./bausteine"
import { kalenderDateiLaden } from "./kalender"
+import { vcardParsen } from "./vcard"
export interface ServicebuchEintrag {
datum?: string
@@ -206,6 +207,8 @@ export function Werkstatt() {
const [entwurf, setzeEntwurf] = useState({ ...autohausRoh })
const [laeuft, setzeLaeuft] = useState(false)
const [gespeichert, setzeGespeichert] = useState(false)
+ const [vcardFehler, setzeVcardFehler] = useState(null)
+ const vcardDatei = useRef(null)
if (!fahrzeug) return null
@@ -221,6 +224,33 @@ export function Werkstatt() {
}
}
+ // Befüllt nur den Entwurf, speichert nicht selbst - der Nutzer prüft die
+ // übernommenen Felder wie bei jeder anderen Änderung erst im Formular,
+ // bevor "Speichern" etwas an Home Assistant schickt. Leere Felder aus der
+ // vCard überschreiben vorhandene Werte im Entwurf nicht.
+ const vcardImportieren = (datei: File) => {
+ setzeVcardFehler(null)
+ setzeGespeichert(false)
+ const leser = new FileReader()
+ leser.onload = () => {
+ try {
+ const felder = vcardParsen(String(leser.result))
+ setzeEntwurf((alt) => ({
+ ...alt,
+ ...(felder.name ? { name: felder.name } : {}),
+ ...(felder.adresse ? { adresse: felder.adresse } : {}),
+ ...(felder.tel ? { tel: felder.tel } : {}),
+ ...(felder.mail ? { mail: felder.mail } : {}),
+ }))
+ } catch (fehler) {
+ setzeVcardFehler(
+ fehler instanceof Error ? fehler.message : "Die vCard ließ sich nicht lesen.",
+ )
+ }
+ }
+ leser.readAsText(datei)
+ }
+
const feld = (schluessel: keyof Autohaus, beschriftung: string, letztes = false) => (
{vcardFehler}