Commit Graph
7 Commits
Author SHA1 Message Date
BiasFandClaude Opus 5 0e05ca22d7 Komplikation fürs Zifferblatt, und die App heisst jetzt VanControl Pro
Zwei Dinge in einem Commit, weil sie sich dieselben Dateien teilen:
project.pbxproj und WatchRootView.swift tragen beides.

**Komplikation.** Neues Target CamperMonitorComplication, auf watchOS
sind Komplikationen WidgetKit-Widgets. Es wird in die Watch-App
eingebettet (PlugIns) und bietet die vier Zubehör-Familien an. Ein Tipp
öffnet die App direkt auf der Nivellierung statt in der Geräteliste:
Die Komplikation hängt eine Adresse an, die Watch-App führt dafür jetzt
einen Navigationsweg, den sie von aussen setzen kann. Die Adresse steht
in Shared/WatchDeepLink.swift, weil beide Seiten sie brauchen – ein
Tippfehler auf einer Seite bliebe sonst stumm.

Bewusst ohne Messwert: Eine Komplikation läuft in einem eigenen Prozess
und käme an die Werte nur über eine App-Gruppe, und die verlangt eine
bezahlte Mitgliedschaft. Ein Wert, der beim Blick aufs Zifferblatt
Stunden alt sein kann, wäre ohnehin schlimmer als keiner – man glaubt
ihm. Die Komplikation tut deshalb genau eine Sache, und die zuverlässig.

**Rebranding.** Der sichtbare Name ist überall VanControl Pro:
CFBundleDisplayName beider Apple-Targets, Android-Label, die Texte in
beiden Apps, beide READMEs und der Name der APK im Gitea-Ablauf.

Die Kennungen bleiben, wie sie sind – Bundle-IDs, Android-Paket,
Target-, Ordner- und Schemanamen. Eine geänderte Bundle-ID ist für iOS
eine neue App: Geräte, Fahrzeugprofile und Victron-Schlüssel wären weg,
dazu neue Profile und eine neu zu koppelnde Watch-App. Sichtbar ist
davon nichts, also gibt es dafür auch keinen Grund.

Geprüft: alle drei Apple-Targets bauen, die Erweiterung liegt im
Watch-Bundle unter PlugIns, die Anzeigenamen stimmen im gebauten
Bundle, Tests grün. Die neue Bundle-ID der Erweiterung braucht beim
ersten Lauf aus Xcode ein Profil, das Xcode selbst anlegt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 21:10:54 +02:00
BiasFandClaude Opus 5 f0df433421 Kalibrierung und Einbaulage gehören in den Sensor, nicht in die Apps
Die Kalibrier-Nullpunkte lagen schon immer im ESP und wurden dort auch
abgezogen – nur verriet die Firmware sie niemandem. Die Einbaulage lag
dagegen in jeder App einzeln, und damit stand sie bei drei Endgeräten
dreimal da, im Zweifel dreimal verschieden. Sie beschreibt aber den
Einbau und nicht das Telefon.

Zwei neue Charakteristiken:

    ...3426  Nullpunkte, zwei Floats, nur lesen
    ...3428  Einbaulage, acht Byte, lesen und schreiben

        0      Version, 1 = gültig gesetzt, 0 = nie geschrieben
        1      Längsachse: 0 = Pitch des Sensors, 1 = Roll
        2      längs umgekehrt (0/1)
        3      quer umgekehrt (0/1)
        4..7   Verdrehung um die Hochachse, float32, Grad

Das Versionsbyte löst die Umstellung: Steht dort 0, hat nie jemand
geschrieben – dann gilt weiter, was die App örtlich gespeichert hat, und
beim nächsten Bestimmen wandert sie hinauf. Mit älterer Firmware ohne die
Charakteristik passiert schlicht nichts.

Angewandt wird die Lage weiterhin in den Apps. Der Sensor verwahrt sie
nur: Meldete er die Winkel bereits umgerechnet, rechnete jede ältere App
die Korrektur ein zweites Mal ein.

Beim Verbinden liest jede App die Lage und übernimmt sie; nach dem
Einbaulage-Assistenten schreibt sie sie hinauf. Die Uhr braucht das
iPhone dafür nicht mehr – sie holt sie sich selbst. Die Android-App
bekommt dabei auch die Verdrehung, die ihrer Fassung bisher ganz fehlte:
Modell, Erkennung und Persistenz nachgezogen.

Geprüft: Firmware übersetzt, im erzeugten Code stehen beide
Charakteristiken mit den richtigen Rechten und der Schreib-Handler;
iPhone- und Watch-Ziel bauen; sieben neue Prüfungen in run-tests.sh
nageln das Byte-Format fest, samt Rundlauf und der Bedeutung von
Version 0. Nicht geprüft ist die Android-Fassung: Auf dieser Maschine
liegt nur Java 8, Gradle verlangt mindestens 11.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 20:50:23 +02:00
BiasFandClaude Opus 5 3f37e6f047 Android: Release-Build signieren und veröffentlichen
Der Release-Build lieferte bisher app-release-unsigned.apk - und die
installiert Android nicht, auch nicht per Sideload. Ohne Signatur kann das
System nicht feststellen, ob ein Update vom selben Absender stammt wie die
Erstinstallation; das ist unabhängig davon, woher die Datei kommt.

Der Build signiert jetzt. Liegt Android/keystore.properties vor, mit dem
eigenen Schlüssel; sonst wird aus der Umgebung gelesen, damit ein Bauknecht
sie aus seinen Geheimnissen setzen kann, ohne dass ein Passwort im Verlauf
landet. Fehlt beides, wird mit dem Debug-Schlüssel signiert - der Build
sagt das dann deutlich, denn eine so signierte Release-APK sieht sonst aus
wie eine richtige, lässt sich aber vom nächsten Rechner aus nicht mehr
aktualisieren. Schlüssel und Passwörter sind ausgeschlossen.

Version und Versionszähler lassen sich beim Bauen setzen, damit sie aus
einem Etikett kommen können.

Dazu ein Ablauf für Gitea Actions: ein Etikett hochschieben baut die APK,
legt das Release an, falls es noch nicht besteht, und hängt sie an. Beides
muss gehen - sonst scheitert es je nachdem, ob man das Release vorher von
Hand angelegt hat.

Android/RELEASE.md erklärt beide Wege und sagt auch, warum die APK nicht
ins Repository gehört: Release-Anhänge liegen in Gitea neben dem
Repository. Eine 11-MB-Binärdatei je Version im Verlauf wäre unwiderruflich
- Git vergisst nichts, ein späteres Löschen bringt den Platz nicht zurück.

Geprüft: die signierte APK installiert sich im Emulator, meldet sich mit
der übergebenen Version und trägt keine Debug-Kennzeichnung mehr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 11:49:07 +02:00
BiasFandClaude Opus 5 899240c567 Android: Oberfläche im Emulator durchgeklickt und nachgebessert
Vier Dinge, die erst am laufenden Bild auffielen.

Die dynamischen Systemfarben ab Android 12 sind wieder raus. In dieser App
trägt Farbe Bedeutung - grün heisst eben, orange knapp daneben, rot
schief. Eine vom Hintergrundbild abgeleitete Akzentfarbe stand daneben und
liess die App je nach Telefon anders aussehen als ihr eigenes Logo.

Die Libelle sprengte ihren Rahmen. `aspectRatio` koppelt die Höhe an die
Breite und hebelte damit die Deckelung aus; auf grossen Anzeigen schob sie
sich über die Umschalter. Jetzt eine feste Höhe - der Kreis wird darin
ohnehin passend gezeichnet.

Der Ratschlag im Ausrichtungs-Assistenten blieb auf "Warte auf Messwerte"
stehen, obwohl daneben schon Werte standen. Compose überspringt einen
Aufruf, dessen Argument dieselbe Instanz ist, und der Assistent ist
bewusst kein Compose-Zustand. Sein Stand wird jetzt bei jeder Messung in
ein Wertobjekt gezogen.

Und die Zahlen, die die Ansichten selbst rechnen, standen mit Punkt statt
Komma - `String.format` ohne Sprachangabe nimmt die des Systems. Das
läuft jetzt über eine gemeinsame Stelle, wie schon bei den Messwerten.

Durchgeklickt: Dashboard, Gerätedetails mit Verlauf, Kühlbox-Steuerung,
Libelle, Fahrzeugansicht, Einrichtung des Neigungsmessers,
Ausrichtungs-Assistent, Einbaulage-Assistent und Fahrzeuge. Die
Kühlbox-Anzeige greift dabei auch hier richtig: der Demo-Datensatz trägt
den Fühlerplatzhalter, und die Box wird als einzonig geführt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 11:33:45 +02:00
BiasFandClaude Opus 5 4c16cdd485 Android: Demo-Modus für den Emulator
Der Emulator hat kein Bluetooth. Damit sich die Ansichten trotzdem
ansehen und durchklicken lassen, füttert ein Demo-Modus fertige Werte
ein - dasselbe Vorgehen wie in der iOS-Fassung.

Nur in Debug-Bauten und nur, wenn beim Start das Extra gesetzt ist:

  adb shell am start -n de.fritob.campermonitor/.MainActivity --ez demo true

Im Demo-Modus funkt nichts und es wird nichts abgelegt; die echten Geräte
und Fahrzeuge bleiben unberührt. Der Kühlbox-Zustand kommt dabei aus
einem echten Einzonen-Datensatz, wie ihn die Box im Fahrzeug schickt -
inklusive des Fühlerplatzhalters, damit die Anzeige auch im Demo zeigt,
was sie im Fahrzeug zeigen würde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 10:45:49 +02:00
BiasFandClaude Opus 5 fcca37bbd0 Android: Oberfläche
Alle Ansichten der iOS-Fassung in Jetpack Compose: Dashboard mit Kacheln,
Gerätedetails samt Messwerten, Verlauf, Zellspannungen und Diagnose,
Kühlbox-Steuerung, Nivellierung mit Libelle und Fahrzeugansicht,
Ausrichtungs-Assistent, die beiden Einrichtungsassistenten, Fahrzeuge,
Gerät hinzufügen, Schlüsseleingabe und Einstellungen.

Mitgenommen ist auch, was wir uns in der Oberfläche erarbeitet haben. Die
Geräteliste beim Einrichten sortiert nach Fundreihenfolge, nicht nach
Signalstärke - danach zu sortieren macht sie unbenutzbar, weil sie
sekündlich springt. Empfangspegel und Alter der Werte erscheinen nur mit
eingeblendeter Diagnose. Der Victron-Schlüssel liegt auf einer eigenen
Seite und meldet sich nur, wenn er fehlt oder nicht passt. Einbaulage und
Nullpunkt liegen eine Ebene tiefer, damit ein Fehlgriff beim Ablesen nicht
den Nullpunkt verstellt. Der Ausrichtungs-Assistent verlangt eine stehende
Verbindung und sagt es, wenn sie abreisst.

Drei Dinge sind auf Android anders:

Die Berechtigungsabfrage ist eigenständige Arbeit. Bis Android 11 lief ein
BLE-Scan über die Standortfreigabe, seither gibt es eigene
Bluetooth-Rechte; beide Wege werden bedient, und die App erklärt vorher,
wozu sie fragt.

Die Fahrzeugauswahl sitzt als Titel mit Aufklappen in der Leiste statt als
runder Knopf in der Ecke - so ist es hier üblich.

Die Symbole sind Material-Entsprechungen der SF-Symbole. Ausgewählt ist
jeweils das, was dieselbe Sache meint, nicht das ähnlichst aussehende.

Der Verlauf ist selbst gezeichnet; für eine Linie mit Fläche lohnt keine
Diagrammbibliothek.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 10:43:32 +02:00
BiasFandClaude Opus 5 866fce9711 Android: Bluetooth-Schicht und Ablage
Damit steht der erste Teil vollständig: Protokoll, Funk und Ablage
übersetzen sich, die 50 Prüfungen laufen.

Die Funkschicht folgt der iOS-Fassung samt allem, was wir uns dort erst
erarbeiten mussten. Ein eigener Thread für alles, was Bluetooth anfasst;
Android ruft Scan- und GATT-Rückmeldungen auf Binder-Threads auf. Die
Wartesperre nach Fehlschlägen, die zwischen einem Abbruch einer stehenden
Verbindung und einem echten Fehlschlag unterscheidet. Stellbefehle, die
aufgehoben statt verworfen werden, wenn die Kühlbox gerade weg ist. Die
Zwanzig-Byte-Grenze beim Schreiben an die Kühlbox. Die stabile Reihenfolge
der Kandidaten, damit nicht der Zufall die Schreibart bestimmt.

Vier Dinge sind auf Android anders und im Quelltext vermerkt:

Die Herstellerdaten kommen ohne die zwei Bytes der Company-ID - die steht
im Schlüssel der Tabelle. Sie werden wieder davorgesetzt, damit derselbe
Rahmen ankommt wie unter iOS und dieselben Prüfungen gelten.

Notify muss über einen Deskriptor eingeschaltet werden; das Einschalten auf
unserer Seite allein genügt nicht.

Geräte werden über ihre MAC-Adresse angesprochen statt über eine
systemvergebene Kennung - iOS gibt die Adresse gar nicht heraus.

Die Victron-Schlüssel liegen in app-privater Ablage statt im
Schlüsselbund. Das schützt gegen andere Apps, ist aber nicht
hardwaregestützt. Ohne zusätzliche Bibliothek gibt es unter Android kein
gleichwertiges Gegenstück.

Als Nächstes die Oberfläche.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 10:28:53 +02:00