Apple Watch: Übersicht, Ausrichten mit Vibration, Kühlbox am Handgelenk

Neues Target CamperMonitorWatch, eingebettet in die iPhone-App. Die Uhr
zeigt je Gerät den Hauptwert, die Nivellierung mit Libelle und Keilhöhen,
den Ausrichtungs-Assistenten und stellt die Kühlbox.

Die Uhr funkt nicht selbst, obwohl watchOS das könnte. BMS, Kühlbox und
Neigungsmesser lassen jeweils nur eine Verbindung zu – eine mitlesende Uhr
würde dem iPhone die Verbindung wegnehmen, statt sie zu ergänzen. Und die
Victron-Schlüssel liegen in der Keychain des iPhones; sie ein zweites Mal
auf der Uhr aufzubewahren brächte nichts. Das iPhone bleibt also das
Funkgerät und reicht fertige Messwerte über WatchConnectivity weiter.

Gesendet wird in zwei Takten: ein halber Sekundentakt als Nachricht,
solange die Watch-App im Vordergrund ist und das über eine Frist meldet,
sonst alle zwei Sekunden als Anwendungskontext und nur bei Änderungen.
Läuft die Frist ab, hört das iPhone von selbst wieder auf.

Solange die Uhr hinschaut, hält die App das Funkgerät auch im Hintergrund
am Leben (UIBackgroundModes in Config/CamperMonitor-Info.plist) – beim
Rangieren liegt das iPhone sonst mit dunklem Bildschirm in der Halterung
und die Anzeige am Handgelenk wäre genau dann tot. Die verbundenen Geräte
liefern dabei weiter, die Victron-Werbedaten nicht: ungefiltertes Suchen
lässt iOS im Hintergrund nicht zu.

Der plattformneutrale Modellcode wandert nach Shared/ und wird in beide
Targets übersetzt; LevelState ist dafür aus VanAlignProtocol.swift
herausgelöst. Datensatz und Befehle stehen in Shared/WatchLink und sind
in run-tests.sh abgesichert: Rundlauf, Schutz gegen fremde Formatversion,
Vergleich ohne Zeitstempel und die Auflösung der Zeitangaben.

Geprüft sind der Bau beider Targets samt Einbetten und der Testlauf; auf
echter Hardware noch nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
BiasF
2026-08-31 15:20:16 +02:00
co-authored by Claude Opus 5
parent 32659ff0b9
commit 6612b07fd5
31 changed files with 1904 additions and 101 deletions
+105 -4
View File
@@ -12,6 +12,11 @@ iOS-App, die per Bluetooth LE die Energieanlage im Wohnmobil ausliest:
Ein Victron SmartShunt/BMV wird ebenfalls unterstützt, falls später einer dazukommt.
Dazu gehört eine **Apple-Watch-App**: Übersicht, Nivellierung samt
Ausrichtungs-Assistent mit Vibration und die Steuerung der Kühlbox am
Handgelenk. Sie funkt nicht selbst, sondern hängt am iPhone warum, steht
unter [Apple Watch](#apple-watch).
Die Firmware des Neigungsmessers liegt mit im Projekt, siehe
[firmware/vanalign](firmware/vanalign/README.md).
@@ -25,8 +30,21 @@ Dann in Xcode:
1. Target `CamperMonitor`**Signing & Capabilities** → dein Apple-Team auswählen.
Die Bundle-ID `de.fritob.CamperMonitor` ggf. anpassen, falls sie schon vergeben ist.
Dasselbe beim Target `CamperMonitorWatch`; dessen Bundle-ID muss die des
iPhones mit angehängtem `.watchkitapp` bleiben.
2. iPhone per Kabel anschließen, oben als Ziel wählen, ⌘R.
Das Schema `CamperMonitor` baut die Watch-App mit und bettet sie ein. Dafür
muss die watchOS-Plattform in Xcode installiert sein sonst bricht schon das
Übersetzen des Symbolkatalogs ab:
```bash
xcodebuild -downloadPlatform watchOS
```
Auf die Uhr kommt die App danach von selbst: Sie steckt in der iPhone-App und
taucht in der Watch-App des iPhones unter *Verfügbare Apps* auf.
Wichtig: **Der Simulator hat kein Bluetooth.** Die App startet dort, findet aber
nie ein Gerät. Zum Testen muss sie auf ein echtes iPhone.
@@ -191,6 +209,57 @@ damit exakt: 2,0° Querneigung bei 2,00 m Spurweite ergeben 7,0 cm unter die
tieferstehende Seite. Die Maße stehen im Fahrzeugprofil, erreichbar über das
ⓘ in der Fahrzeugliste.
## Apple Watch
Die Uhr zeigt dasselbe wie das Dashboard, nur auf das eingedampft, wofür man
den Arm hebt:
* **Übersicht** je Gerät der Hauptwert und ein Punkt für den Zustand der
Verbindung. Ganz unten steht, wie alt der Stand ist.
* **Nivellierung** Libelle, beide Winkel, was zu tun ist, und die nötige
Keilhöhe, falls im Fahrzeugprofil Spurweite und Radstand stehen.
* **Ausrichten** derselbe Assistent wie am iPhone: Tendenz, Rat und der
Hinweis auf den flachsten Punkt der letzten anderthalb Minuten. **Steht das
Fahrzeug in der Toleranz, vibriert die Uhr.** Das ist der eigentliche Gewinn
gegenüber dem iPhone beim Rangieren schaut niemand aufs Display, aber die
Vibration am Handgelenk kommt an.
* **Kühlbox** ein/aus, Eco oder Max und die Solltemperatur, über die Krone
gestellt.
### Warum die Uhr nicht selbst funkt
watchOS könnte Bluetooth, und trotzdem geht alles über das iPhone. Zwei Gründe,
beide hart:
* BMS, Kühlbox und Neigungsmesser lassen jeweils nur **eine** Verbindung zu.
Eine mitlesende Uhr würde dem iPhone die Verbindung wegnehmen, statt sie zu
ergänzen.
* Die Victron-Schlüssel liegen in der Keychain des iPhones. Sie auf die Uhr zu
kopieren hiesse, sie ein zweites Mal aufzubewahren, ohne dass der zweite Ort
irgendetwas brächte.
Das iPhone bleibt also das Funkgerät und reicht fertige Messwerte weiter. Was
die Uhr stellt, stellt in Wahrheit das iPhone und angezeigt wird auch dort
nur, was das Gerät zurückmeldet, nicht der Tastendruck.
### Was das für den Betrieb heisst
Auf dem iPhone muss Camper Monitor laufen. Solange die Uhr meldet, dass jemand
hinschaut, hält die App das Funkgerät auch im Hintergrund am Leben sonst wäre
die Anzeige am Handgelenk beim Rangieren tot, denn dann liegt das iPhone mit
dunklem Bildschirm in der Halterung. Die verbundenen Geräte liefern dabei
weiter; die Victron-Werbedaten nicht, weil iOS im Hintergrund kein ungefiltertes
Suchen erlaubt.
Gesendet wird in zwei Takten: ein halber Sekundentakt, solange die Watch-App im
Vordergrund ist, sonst alle zwei Sekunden und nur bei Änderungen. Der schnelle
Takt läuft über eine Frist, die die Uhr regelmässig erneuert schläft sie ein,
hört das iPhone von selbst wieder auf.
Was die Uhr zeigt, ist immer Weitergereichtes. Deshalb steht auf jedem
Bildschirm das Alter des Standes: Eine abgerissene Strecke zum iPhone sähe sonst
genauso aus wie ein Fahrzeug, an dem sich nichts tut.
## Diagnose
Im Alltag zeigt die App nur Messwerte und Bedienelemente. Die technischen
@@ -218,6 +287,10 @@ ohne Bluetooth prüfen lassen. In Xcode unter *Product → Scheme → Edit Schem
Run → Arguments* die Umgebungsvariable `CAMPER_DEMO` auf `1` setzen. Er greift
nur in Debug-Builds.
Die Watch-App kennt denselben Schalter und braucht dann kein iPhone: Im Schema
`CamperMonitorWatch` dieselbe Variable setzen. Die Neigung wandert dort
langsam hin und her, sonst hätte der Ausrichtungs-Assistent nichts zu zeigen.
## Protokolle prüfen
```bash
@@ -235,14 +308,19 @@ firmware/vanalign/ Firmware des Neigungsmessers (ESPHome)
├── esp32_ble.yaml Neigungsmessung und Bluetooth-Schnittstelle
└── experimente/ Nicht für den Betrieb nötig
CamperMonitor/
Shared/ In iPhone- und Watch-App übersetzt
├── Models/
│ ├── Profile.swift Fahrzeug samt Maßen
│ ├── AlignmentAssistant.swift Verlauf, Tendenz und Keilberechnung
│ ├── SensorOrientation.swift Einbaulage und ihre Erkennung
│ ├── ConfiguredDevice.swift Eingerichtetes Gerät, Rolle, Transportart
│ ├── DeviceSnapshot.swift Messwerte in Anzeigeform
│ ├── LevelState.swift Neigung, Toleranz und Klartext dazu
│ └── VictronCodes.swift Klartexte für Zustands-/Fehlercodes
└── WatchLink/
└── WatchLink.swift Datensatz und Befehle zwischen iPhone und Uhr
CamperMonitor/
├── Bluetooth/
│ ├── BluetoothManager.swift Zentraler Scan, Verbindungen, Verlauf
│ ├── VictronAdvertisement.swift Advertisement entschlüsseln und auswerten
@@ -259,6 +337,8 @@ CamperMonitor/
├── Store/
│ ├── DeviceStore.swift Geräteliste, Persistenz
│ └── KeychainStore.swift Victron-Schlüssel
├── Watch/
│ └── PhoneWatchLink.swift Sendet den Stand, nimmt Befehle der Uhr an
└── Views/
├── DashboardView.swift Kachelübersicht
├── DeviceCard.swift Eine Kachel
@@ -271,13 +351,34 @@ CamperMonitor/
├── SettingsView.swift App-Einstellungen
├── SensorSetupView.swift Assistent für die Einbaulage
└── AddDeviceView.swift Scannen und Einrichten
CamperMonitorWatch/ watchOS-App
├── CamperWatchApp.swift Einstieg, meldet dem iPhone das Hinschauen
├── PhoneLink.swift Gegenstelle zum iPhone
├── WatchDemo.swift Erfundene Werte für den Simulator
└── Views/
├── WatchRootView.swift Übersicht und Altersangabe
├── WatchBubble.swift Libelle für den kleinen Bildschirm
├── WatchLevelView.swift Neigung, Keilhöhe, Kalibrierung
├── WatchAlignView.swift Ausrichten mit Vibration
├── WatchDeviceDetailView.swift Alle Werte eines Geräts
└── WatchFridgeControls.swift Kühlbox stellen
Config/
└── CamperMonitor-Info.plist Nur der Hintergrundbetrieb für die Uhr
```
## Bekannte Grenzen
* **Kein Hintergrundbetrieb.** iOS erlaubt das ungefilterte Scannen nach
Advertisements nur im Vordergrund. Die App pausiert, sobald sie in den
Hintergrund geht, und nimmt beim Zurückkommen wieder auf.
* **Im Hintergrund nur die verbundenen Geräte.** iOS erlaubt das ungefilterte
Scannen nach Advertisements nur im Vordergrund; die Victron-Werte stehen im
Hintergrund also still. Verbundene Geräte Neigungsmesser, BMS, Kühlbox
laufen weiter, aber nur solange die Uhr meldet, dass jemand hinschaut. Ohne
Uhr pausiert die App im Hintergrund vollständig und nimmt beim Zurückkommen
wieder auf.
* **Die Uhr braucht das iPhone.** Sie zeigt nur, was von dort kommt. Steckt das
iPhone ausser Reichweite, steht auf der Uhr, wie alt der letzte Stand ist
aber kein neuer.
* **Das Modbus-Registerlayout des neuen Daly-Protokolls variiert zwischen
Firmwareständen.** Das klassische `A5`-Protokoll und das JBD-Protokoll sind
gut dokumentiert; falls dein BMS Modbus spricht und Werte unplausibel