Die .ipa war nicht installierbar, plus QR-Scanner (2026.9.4.8)

Der Erweiterung fehlte CFBundleVersion - eine Pflichtangabe, ohne die iOS
die Installation der ganzen App verweigert ("kann nicht installiert werden").
Verursacht von der Versionsangleichung vom Vortag: das Skript reichte den
Platzhalter $(CURRENT_PROJECT_VERSION) statt einer Zahl weiter, und das per
Skript angelegte Ziel der Erweiterung kennt diese Build-Einstellung nicht.
An den entpackten .ipa nachgemessen, 2026.9.3.3 gegen 2026.9.4.5.

ios-signieren.sh reicht nur noch echte Zahlen weiter, das Gegenlesen der
fertigen .ipa bricht ab statt zu warnen, und abgelegt wird erst danach -
eine Datei, die die Pruefung nicht besteht, darf nicht in auslieferung/
landen.

Dazu:
- Kopfmarke statt Firmierung des Betreibers in der Tankstellen-Zeile
- dritter Suchbegriff: der Name ohne fuehrende Marke. Gemessen: Nominatim
  findet "Shell, Pascalstr. 8, Ingolstadt" nicht, "Pascalstr. 8, Ingolstadt"
  schon - daran blieb die Belegkarte der realen Instanz leer
- der Tankstellen-Verweis zeigt die Tankstelle, statt eine Route zu rechnen
- Cache-Brecher auf .ipa, Symbolen und manifest.plist
- QR-Scanner fuer den Zugangs-Token bei der Ersteinrichtung (jsQR statt
  GoogleMLKit, damit der Xcode-Lauf kein pod install riskiert)

214/214 Tests, Panel als Modul geparst, live gegen audi_ha_test geprueft.
This commit is contained in:
2026-09-04 11:51:44 +02:00
parent d819f2e285
commit 63a45d4ee2
20 changed files with 788 additions and 56 deletions
@@ -1 +1 @@
{"version":"2026.9.4.5","sha256":"ffea00a94ac842d8b6b233ebd91a1a97252c4b7b6a952c336ca7a6218eceef1e","bytes":270888,"gebaut":"2026-09-04T07:57:27Z"}
{"version":"2026.9.4.8","sha256":"d6fe041f31480ff67d9b39884e9ef8b5ac5795d8575716d4638a9f557198bc49","bytes":319995,"gebaut":"2026-09-04T09:50:03Z"}
@@ -1236,6 +1236,38 @@ async function adresseAufloesen(adresse) {
Erste Zeile Tankstelle und Strasse, zweite Zeile der Ort (Vorgabe des
Eigentuemers, 04.09.2026). Ohne Komma bleibt alles in der ersten Zeile -
lieber ein langer erster Eintrag als ein erfundener Ort. */
/* Kopfmarken des deutschsprachigen Marktes. WORTGLEICH mit _MARKEN in
shell_beleg_parser.py und MARKEN in companion-app/src/daten/tankstelle.ts -
der Parser erkennt die Marke beim Einlesen, diese Liste erkennt sie
nachtraeglich in dem, was gespeichert ist. Reihenfolge zaehlt:
"TotalEnergies" muss vor "Total" stehen. */
const MARKEN = [
"TotalEnergies", "Total", "Aral", "Shell", "Eni", "Agip", "Esso", "OMV",
"Avia", "Turmöl", "Turmoel", "Orlen", "Tamoil", "Westfalen", "Allguth",
"Classic", "Sprint", "Raiffeisen", "BayWa", "Elan", "HEM", "Star", "JET",
"bft", "BP", "Q1",
];
/* Die Kopfmarke aus dem gespeicherten Stationsnamen - null, wenn keine
bekannte darin steht.
`station_name` traegt je nach Alter des Belegs Verschiedenes: seit dem
16.08.2026 setzt der Parser "Marke, Strasse, Ort" zusammen, aeltere Belege
und jeder ohne erkennbare Marke tragen dort die Firmierung des Betreibers
("A. Zrenner GmbH"). Die ist nach aussen nicht sichtbar und gehoert nicht in
die Anzeige (Vorgabe des Eigentuemers, 04.09.2026).
Wortgrenzen sind Absicht: "Star" darf nicht in "Starnberg" treffen. */
function markeErkennen(name) {
const roh = (name || "").trim();
if (!roh) return null;
for (const marke of MARKEN) {
const muster = new RegExp("\\b" + marke.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\b", "i");
if (muster.test(roh)) return marke;
}
return null;
}
function adresseTeilen(adresse) {
const roh = (adresse || "").trim();
if (!roh) return { strasse: "", ort: "" };
@@ -1244,9 +1276,14 @@ function adresseTeilen(adresse) {
return { strasse: roh.slice(0, komma).trim(), ort: roh.slice(komma + 1).trim() };
}
/* Oben Kopfmarke und Strasse mit Hausnummer, unten Postleitzahl und Ort.
Ohne erkennbare Marke steht oben nur die Strasse - die Firmierung des
Betreibers tritt NICHT an ihre Stelle. Ohne Anschrift bleibt der
gespeicherte Name die einzige Auskunft und wird unveraendert gezeigt. */
function tankstelleZeilen(name, adresse) {
const { strasse, ort } = adresseTeilen(adresse);
const oben = [(name || "").trim(), strasse].filter(Boolean).join(", ");
if (!strasse && !ort) return { oben: (name || "").trim() || "unbekannt", unten: "" };
const oben = [markeErkennen(name) || "", strasse].filter(Boolean).join(", ");
return { oben: oben || "unbekannt", unten: ort };
}
@@ -1255,8 +1292,20 @@ function tankstelleZeilen(name, adresse) {
gemessen: "Pascalstr. 8, 85057 Ingolstadt" findet Nominatim, mit dem
Betreibernamen davor nicht. Der Name ALLEIN ist trotzdem wertvoll
("Shell, SOELDEN" findet es), deshalb der zweite Versuch. */
/* Dritter Versuch: der Name OHNE die fuehrende Marke. Seit dem 16.08.2026
setzt der Belegparser den Namen als "Marke, Strasse, Ort" zusammen; fehlt
darin die Postleitzahl, kippt die Marke die Suche. Am 04.09.2026 gemessen:
"Shell, Pascalstr. 8, Ingolstadt" findet Nominatim NICHT,
"Pascalstr. 8, Ingolstadt" dagegen schon. Genau daran blieb die Karte auf
der realen Instanz leer. */
function tankstelleKandidaten(name, adresse) {
return [(adresse || "").trim(), (name || "").trim()].filter(Boolean);
const n = (name || "").trim();
const marke = markeErkennen(n);
const ohneMarke =
marke && n.toLowerCase().startsWith(marke.toLowerCase())
? n.slice(marke.length).replace(/^[ ,]+/, "").trim()
: "";
return [...new Set([(adresse || "").trim(), n, ohneMarke].filter(Boolean))];
}
/** Der erste Suchbegriff - leer heisst: der Beleg nennt gar nichts. */
@@ -1268,7 +1317,9 @@ function tankstelleSuchtext(name, adresse) {
Apple Karten und Google Maps kennen Betriebsnamen und treffen damit die
Zapfsaeule statt des Nachbargebaeudes. */
function tankstelleZiel(name, adresse) {
return [(name || "").trim(), (adresse || "").trim()].filter(Boolean).join(", ");
const a = (adresse || "").trim();
if (!a) return (name || "").trim();
return [markeErkennen(name) || "", a].filter(Boolean).join(", ");
}
/* Die Kandidaten der Reihe nach nachschlagen, erster Treffer gewinnt. */
@@ -1286,15 +1337,26 @@ function apfelgeraet() {
return /iPhone|iPad|iPod|Macintosh|Mac OS X/i.test(kennung);
}
function kartendienstUrl(name, adresse, pos) {
/* modus "route" berechnet den Weg, "zeigen" setzt nur eine Nadel. Der
Tankstellen-Verweis im Einzelbeleg will das Zweite (Vorgabe des
Eigentuemers, 04.09.2026): erst sehen, wo sie liegt - eine Route erwartet
dort niemand, und sie ist im Kartendienst einen Tipp entfernt. */
function kartendienstUrl(name, adresse, pos, modus = "route") {
const beschriftung = (name || "").trim();
const ziel = pos ? `${pos.lat},${pos.lon}` : tankstelleZiel(name, adresse);
if (!ziel) return null;
if (apfelgeraet()) {
if (modus === "zeigen") {
return pos
? `https://maps.apple.com/?ll=${ziel}&q=${encodeURIComponent(beschriftung || "Ziel")}`
: `https://maps.apple.com/?q=${encodeURIComponent(ziel)}`;
}
const q = pos && beschriftung ? `&q=${encodeURIComponent(beschriftung)}` : "";
return `https://maps.apple.com/?daddr=${encodeURIComponent(ziel)}${q}`;
}
return `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(ziel)}`;
return modus === "zeigen"
? `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(ziel)}`
: `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(ziel)}`;
}
function kartendienstUrlZuPunkt(pos) {
@@ -1308,8 +1370,12 @@ function tankstelleAufKarteZeigen(karte, pos, f) {
if (!karte || !window.L || !pos) return;
karte.setView([pos.lat, pos.lon], 14);
const icon = window.L.divIcon({ className: "fahrzeug-pin-wrap", html: `<div class="fahrzeug-pin">${tankstellenMarkerSVG(32)}</div>`, iconSize: [32, 32], iconAnchor: [15, 31] });
// Dieselben zwei Zeilen wie in der Wertezeile darunter (tankstelleZeilen) -
// die Nadel soll nicht die Firmierung des Betreibers nennen, wenn die Zeile
// die Kopfmarke zeigt.
const zeilen = tankstelleZeilen(f.station_name, f.station_address);
window.L.marker([pos.lat, pos.lon], { icon }).addTo(karte)
.bindPopup((f.station_name || "") + "<br>" + (f.station_address || ""));
.bindPopup(esc(zeilen.oben) + (zeilen.unten ? "<br>" + esc(zeilen.unten) : ""));
// Position merken und den Knopf scharf schalten - er kann erst jetzt etwas
// Sinnvolles tun (bei Adressauflösung kommt das hier verzögert, siehe
// Aufrufstelle in initMap).
@@ -4542,7 +4608,7 @@ function belegKarte(f) {
ein Verweis, der nirgendwohin fuehrt, waere schlechter als keiner. */
function tankstellenZeile(f) {
const { oben, unten } = tankstelleZeilen(f.station_name, f.station_address);
const url = kartendienstUrl(f.station_name, f.station_address);
const url = kartendienstUrl(f.station_name, f.station_address, null, "zeigen");
const inhalt =
`<span class="tankstellenzeile-ort">${esc(oben)}${unten ? `<small>${esc(unten)}</small>` : ""}</span>` +
(url ? `<svg class="chev" viewBox="0 0 6 10" aria-hidden="true"><polyline points="1,1 5,5 1,9"/></svg>` : "");
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.4.5",
"version": "2026.9.4.8",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": [