Die Uhr funkt den Neigungsmesser jetzt selbst an, statt die Werte vom
iPhone weiterreichen zu lassen. Bei diesem einen Gerät geht das, und nur
bei ihm: Es bewirbt seinen Dienst, ist also ohne Einrichtung auffindbar,
und es ist unverschlüsselt – es gibt keinen Schlüssel, der auf der Uhr
ein zweites Mal lagern müsste. Damit steht die Nivellierung am Handgelenk
ohne geöffnetes iPhone, und genau dafür hebt man beim Rangieren den Arm.
Batterie, Solar und Kühlbox bleiben beim Weg über das iPhone: Sie lassen
nur eine Verbindung zu, und die Victron-Schlüssel liegen in dessen
Keychain. Die Einbaulage des Sensors reist einmal mit dem Datensatz
herüber und bleibt auf der Uhr gespeichert – ohne sie stünden längs und
quer je nach Einbau vertauscht.
LevelSession und VanAlignProtocol wandern nach Shared/Bluetooth; beide
sind reines CoreBluetooth und laufen auf watchOS unverändert. Die
Fahrzeugansicht gibt es jetzt auch auf der Uhr, umschaltbar zur Libelle;
Überhöhung und Farbregeln stehen in Shared/VehicleTilt.swift, damit
iPhone und Uhr nicht auseinanderlaufen.
Kalibrieren fällt auf der Uhr weg, samt Befehl. Das gehört einmalig ans
iPhone mit ebenem Fahrzeug; ein Knopf dafür am Handgelenk wäre vor allem
eine Gelegenheit, die Nullage aus Versehen zu verstellen.
Zwei Fehler dabei behoben:
* Beim Start meldete die Uhr dem iPhone nie, dass jemand hinschaut –
onChange(of: scenePhase) feuert beim ersten Erscheinen nicht. Der
schnelle Sendetakt blieb aus, bis die App einmal im Hintergrund war.
* Ein einmal direkt gemessener Wert hatte für immer Vorrang. Blieb der
Sensor stehen, ohne die Verbindung zu trennen, klebte die Anzeige
daran. Jetzt gilt er drei Sekunden als frisch, danach übernimmt der
Stand des iPhones – mit Alter und Grund daneben.
Ausserdem WATCHOS_DEPLOYMENT_TARGET auf 10.0 (Xcode hatte 11.6 gesetzt)
und die Bluetooth-Begründung für das Watch-Target.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Der Vollbildschirm hing an einer `Section`. SwiftUI zeigt Blätter und
Vollbildschirme dort nicht zuverlässig an - genau der Fehler, an dem
schon der Bestätigungsdialog der Kühlbox scheiterte. Dass es "irgendwie
nicht immer" auftrat, passt dazu. Er hängt jetzt am Knopf selbst. Alle
übrigen Blätter und Dialoge der App sitzen bereits an einer Liste oder
einem Stapel; nur diese Stelle nicht.
Dazu die zweite Hälfte: der Assistent war freigegeben, sobald irgendwann
einmal ein Messwert angekommen war. Nach einem Verbindungsabbruch blieb
der letzte Stand stehen, der Knopf also offen - und der Assistent zeigte
Zahlen, die längst nicht mehr galten. Er verlangt jetzt eine stehende
Verbindung, und der Abschnitt sagt, warum er sonst grau ist.
Reisst die Verbindung während des Rangierens ab, steht das jetzt oben im
Assistenten. Vorher stand die Anzeige einfach still, was von einer
hängenden App nicht zu unterscheiden war.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kalibrierung und Einbaulage standen direkt unter der Libelle. Wer im
Fahrzeug die Anzeige abliest und dabei scrollt, verstellt so leicht den
Nullpunkt - und merkt es nicht, weil die Anzeige danach schlicht null
zeigt. Beides liegt jetzt hinter "Neigungsmesser einrichten".
In der Geräteansicht bleibt, was im Betrieb gebraucht wird: die Libelle,
die Werte und der Ausrichtungs-Assistent. Die Einrichtungszeile nennt den
Stand, damit man nicht erst hineinschauen muss - fehlt der Nullpunkt,
steht das dort.
Die Namen sagen jetzt, was geschieht. "Auf aktuelle Lage kalibrieren"
klang nach einer Messung, gemeint ist das Festlegen des Nullpunkts: jetzt
"Aktuelle Lage als eben übernehmen", das Zurücksetzen entsprechend
"Nullpunkt verwerfen". Aus dem Hauptwort "Einbaulage" wird "Einbaulage
bestimmen".
Die beiden Schritte stehen in der Reihenfolge, in der sie gehören: erst
die Einbaulage, sonst legt der Nullpunkt sich auf die falsche Achse.
Beide Knöpfe bleiben stumm, solange keine Werte ankommen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Oben in der Geräteansicht stand, was zur Diagnose taugt, aber im Alltag
nichts sagt.
Der Empfangspegel ist raus - er hilft beim Aufspüren eines Geräts, sonst
nie - und erscheint jetzt nur noch mit eingeblendeter Diagnose. Ebenso das
Alter der Werte: solange sie frisch sind, sagt es nichts, was der
Verbindungspunkt nicht schon zeigt. Bleiben sie stehen, ist es die
eigentliche Nachricht und wird gezeigt.
Der Victron-Schlüssel stand mit Eingabefeld und Anleitung ganz oben,
obwohl er einmal eingetragen und danach nie wieder angefasst wird. Er hat
jetzt eine eigene Seite, erreichbar aus den Einstellungen - dort, wo auch
die Bluetooth-ID steht. In der Übersicht bleibt nur eine Zeile mit dem
Stand: hinterlegt, fehlt oder passt nicht.
Fehlt der Schlüssel wirklich oder gehört er zu einem anderen Gerät, steht
weiter ganz oben ein Hinweis - dann ist er ja das Einzige, was zu tun ist.
Kommen Werte an, passt er offensichtlich, und es wird nichts gemeldet.
Der Vergleich der ersten Schlüsselbytes ist aus der Diagnose auf die
Schlüsselseite gewandert. Dort wird er gebraucht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dein Befund grenzt es ein: die Solltemperatur lässt sich stellen, also
kommen Schreibvorgänge an. Es scheitert nur am Einstellungsblock.
Das mehrfache Piepen war meine Schuld. Beantwortet die Box die Anmeldung
nicht, habe ich sie vor jedem Stellbefehl erneut angemeldet - und die Box
quittiert jede Anmeldung mit einem Ton. Jetzt geschieht das höchstens
einmal je Verbindung.
Weiter komme ich nicht ohne die Antwort deiner Box. Ihre Länge entscheidet
darüber, wie lang der Stellbefehl sein muss, und sie steht jetzt als
"Statusdaten der Box" in der Diagnose. Dazu ein Knopf, der alles zusammen
in die Zwischenablage legt - Merkmalsbaum, Weg, Schreibart, letzten Befehl
und letzte Antwort. Das abzutippen wäre zuviel verlangt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vier Anläufe ohne Erfolg heisst: die Annahme, der Schreibvorgang komme an,
trägt nicht mehr. Also wird sie geprüft, statt weiter darauf zu bauen.
Drei Dinge am Schreibweg waren wirklich falsch.
Die Reihenfolge der Kandidaten war nicht festgelegt. Bietet ein Merkmal
beide Schreibarten an, sind beide Einträge gleichwertig - und `sorted`
sichert für gleichwertige Einträge keine Reihenfolge zu. Ob mit oder ohne
Bestätigung geschrieben wurde, entschied damit der Zufall, von Start zu
Start womöglich verschieden. Jetzt bleibt die Fundreihenfolge erhalten.
Ein Schreibvorgang ohne Bestätigung wird von iOS stillschweigend verworfen,
wenn der Sendepuffer voll ist. Das wurde nirgends beachtet. Jetzt wartet,
was nicht sofort rausgeht, auf die Bereitschaftsmeldung.
Schreibfehler waren unsichtbar: für Schreibvorgänge mit Bestätigung gab es
gar keine Rückmeldung. Jetzt werden sie gezählt und Fehler gemeldet, beides
steht in der Diagnose.
Dazu bessert die Sitzung selbst nach. Nach einem Stellbefehl wird geprüft,
ob sich die Einstellungen der Box überhaupt geändert haben - an den
Einstellungsbytes, nicht an Temperatur und Spannung, die ohnehin schwanken.
Bleibt alles gleich, wird einmal mit der anderen Schreibart nachgesetzt.
Nimmt die Box nur eine der beiden an, findet die App das jetzt allein.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diesmal gegen die Referenzimplementierung geprüft statt geraten. Rahmen,
Längenbyte, Prüfsumme, Anmelde- und Abfragepaket stimmen dort byteweise
mit unseren überein. Ein Unterschied blieb: die Länge des Einstellungs-
blocks.
Ich hatte sie an die Zonenerkennung gehängt. Sendet eine Box den langen
Datensatz, füllt den zweiten Block aber mit Nullen, galt sie als einzonig
und bekam den kurzen Befehl - während sie den langen erwartet. Die Box
verwirft ihn dann wortlos, und Ein- und Ausschalten blieb wirkungslos.
Die Referenz hängt die Länge an die Antwort der Box, nicht an eine
Deutung davon. Jetzt auch hier. Für die Anzeige bleibt die Erkennung
unverändert: ein leerer zweiter Block wird weiterhin nicht als zweite
Zone gezeigt.
Zweitens die Anmeldung. Abfragen beantwortet die Box teils auch
unangemeldet, Stellbefehle nicht. Ob die Anmeldung je bestätigt wurde,
wurde bisher nicht verfolgt. Jetzt schon: fehlt die Bestätigung, wird vor
dem Stellbefehl noch einmal angemeldet. In der Diagnose steht der Stand
als eigene Zeile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Fehler aus dem letzten Stand, beide von mir eingebaut.
Die Kühlbox liess sich nicht mehr bedienen. Die Wartesperre gegen den
Verbindungssturm hat nicht unterschieden, warum eine Verbindung endete:
ein Abbruch einer Verbindung, die stand, wurde genauso lange gesperrt wie
ein Fehlschlag. Im Fahrzeug ist so ein Abbruch der Normalfall, und die Box
war danach bis zu einer Minute nicht erreichbar. Ein Tippen ging in dieser
Zeit lautlos verloren, weil es ohne Sitzung ins Leere fiel.
Jetzt zählt die Dauer: was mindestens drei Sekunden stand, gilt als
zustandegekommen und wird nach zwei Sekunden wieder aufgenommen. Nur was
sofort wieder abbricht, zählt als Fehlschlag und wird gestaffelt gesperrt -
gegen das Wechselspiel, wenn eine andere App das Gerät belegt.
Dazu gehen Stellbefehle nicht mehr verloren. Fehlt die Verbindung, wird der
Befehl aufgehoben, die Sperre für dieses Gerät aufgehoben und sofort ein
Versuch angestossen; sobald der Kanal steht, geht er raus. Ein Befehl, der
älter als eine halbe Minute ist, verfällt - ein verspätetes Ausschalten
würde nur überraschen. Die Ansicht sagt es, statt den Schalter stumm
zurückspringen zu lassen.
Zweitens der Assistent für die Einbaulage. Er hat die zweite Bewegung von
der gekippten Lage aus gemessen statt von der Ruhelage. Damit steckte in
jeder zweiten Messung das Zurückkippen aus der ersten, und es schlugen
immer beide Achsen aus. Die Ruhelage ist jetzt der Bezug für beide
Bewegungen, und ein eigener Schritt dazwischen nimmt sie neu auf.
Auch die Auswertung war zu streng: sie hat jede Bewegung für sich
beurteilt und abgelehnt, sobald die andere Achse mitlief. Von Hand gekippt
läuft sie aber immer mit. Beurteilt werden jetzt beide Bewegungen
gemeinsam - es gewinnt die Zuordnung, die zusammen am meisten erklärt.
Zweimal um dieselbe Achse gekippt wird weiterhin erkannt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Dinge.
Die Kühlbox piepte dauernd und die Bedienung wurde zäh. Ursache war ein
Fehler in der Verbindungslogik: nach einem fehlgeschlagenen Versuch wurde
der Eintrag freigegeben, und das nächste Advertisement löste sofort den
nächsten aus - bei laufendem Scan bis zu einmal je Sekunde. Geräte
quittieren jeden Versuch, Kühlboxen mit einem Piepton. Nebenher wechselte
der Verbindungszustand im selben Takt, was die Oberfläche in eine
Dauerneuzeichnung trieb.
Ein Versuch wird jetzt für eine Weile gesperrt, mit wachsendem Abstand von
fünf bis sechzig Sekunden. Auch nach einem Trennen durch die Gegenseite
wird nicht sofort neu angeklopft - trennt ein Gerät von sich aus, etwa
weil eine andere App verbunden ist, entstünde sonst ein Wechselspiel aus
Verbinden und Trennen. Beim Umkonfigurieren werden die Sperren
zurückgesetzt, damit ein neu eingerichtetes Gerät sofort drankommt.
Dazu entlastet: bei jeder Antwort wurden alle fünf Protokollparser
durchprobiert, auch wenn längst feststand, welches Protokoll gilt. Steht
der Dialekt, läuft nur noch dieser.
Zweitens der Neigungsmesser: je nach Einbaulage meldet er längs und quer
vertauscht oder mit falschem Vorzeichen. Statt die Lage aus einer Liste
raten zu lassen, misst der neue Assistent sie - zweimal kippen, einmal um
jede Achse, und aus der Reaktion ergibt sich die Zuordnung. Schräges oder
zu schwaches Kippen wird erkannt und gemeldet, statt eine zufällige
Zuordnung zu liefern. Die Sitzung führt die Rohwerte des Sensors weiter
mit, weil der Assistent sie unumgerechnet braucht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Geräteansichten zeigten dauerhaft die technischen Angaben: erkanntes
Protokoll, Bluetooth-Merkmale samt vollständigem Merkmalsbaum, gesendete
Befehle und die Rohdaten der letzten Antwort. Das war zum Einrichten der
Geräte nötig - für den Alltag ist es nur Ballast zwischen den Messwerten.
Sie sind jetzt ausgeblendet und über Einstellungen → Diagnose anzeigen
wieder einzublenden. Die Einstellungen sind neu und sitzen im
Fahrzeugmenü des Dashboards.
Eine Ausnahme: Meldet ein Gerät einen Fehler oder fehlt ein
Victron-Schlüssel, erscheinen die Angaben unabhängig von der Einstellung.
Genau dann sind sie das Einzige, was weiterhilft - und niemand denkt in
dem Moment daran, sie erst irgendwo einzuschalten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neben der Libelle gibt es jetzt eine zweite Darstellung: die Neigung am
Fahrzeug selbst, mit den Zeichnungen aus dem Ursprungsprojekt. Umschaltbar
und über Starts hinweg gemerkt.
Seitenansicht für längs, Heckansicht für quer. Die Heckansicht ist bewusst
gewählt, nicht die Frontansicht: sie teilt die Blickrichtung des Fahrers,
links im Bild ist also links am Fahrzeug. Von vorne betrachtet wäre es
seitenverkehrt und damit beim Ausrichten irreführend.
Die Zeichnungen sind weiss gefüllt mit dunklen Linien. Als Schablone
eingefärbt wurde daraus eine massive Fläche, weil alles Deckende zur
Tintfarbe wird - Fenster, Räder und Konturen verschwanden. Tools/
make-vehicle-art übersetzt deshalb die Helligkeit in Deckkraft: dunkle
Linien voll deckend, helle Karosserie nur angedeutet. Eingefärbt bleibt
die Zeichnung erhalten und passt sich Hell- wie Dunkeldarstellung an.
Die Neigung wird dreifach überhöht gezeigt, sonst wären zwei Grad am
Fahrzeug kaum auszumachen. Das steht als Hinweis darunter, zusammen mit
der Klarstellung, dass die Gradzahlen echt sind - sonst nähme man den
Bildwinkel für den tatsächlichen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Begleitet das Einparken: verfolgt die Neigung über die Zeit, meldet ob es
besser oder schlechter wird, und erinnert an den flachsten Punkt - "vor 4
Sekunden stand das Fahrzeug 0,8 Grad flacher". Bei Erreichen der Toleranz
gibt es eine Vibration, das Display bleibt währenddessen wach.
Bewusst ohne Positionsbestimmung. Aus einem MEMS-Sensor lässt sich keine
brauchbare Strecke ableiten: der Fehler wächst beim zweifachen Integrieren
quadratisch mit der Zeit, und im Schritttempo gehen die tatsächlichen
Beschleunigungen im Rauschen unter. Gebraucht wird sie auch nicht - beim
Einparken lautet die Frage nie "wo stehe ich", sondern "wird es besser".
Das steckt vollständig im zeitlichen Verlauf der Neigung, ohne jede
Annahme über das Gelände. Die vorhandene Verlaufsaufzeichnung war mit
einem Punkt alle fünf Sekunden zu grob; der Assistent führt einen eigenen
Ringpuffer, der nur läuft solange die Ansicht offen ist.
Sind Spurweite und Radstand hinterlegt, kommt die nötige Höhe der
Auffahrkeile dazu. Das ist reine Geometrie und damit exakt. Die Masse
stehen im Fahrzeugprofil.
Dabei fiel auf, dass sich vorhandene Fahrzeuge praktisch nicht bearbeiten
liessen: das ging nur über eine Wischgeste, die niemand findet - erst
recht nicht, wenn dort jetzt die Masse einzutragen sind. Die Zeile hat nun
einen sichtbaren Knopf dafür, wie bei den WLAN-Einstellungen: antippen
wählt aus, das Zeichen daneben öffnet die Bearbeitung.
Ausserdem übernimmt der Assistent den anliegenden Messwert beim Öffnen.
Sonst stand dort bis zur nächsten Messung "Warte auf Messwerte", obwohl
längst welche vorlagen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die überarbeitete Firmware liess sich prüfen und fehlerfrei übersetzen,
blieb auf dem Gerät aber stumm - keine Werte, keine Ausgabe über die
serielle Schnittstelle. Der Umbau war zu gross für das, was sich ohne
Gerätezugriff absichern lässt: config und compile sagen nichts über das
Laufzeitverhalten.
Die Firmware setzt jetzt wieder auf dem bewährten Stand 1.0 auf und ändert
daran nur zweierlei: die Kalibrierung über Bluetooth funktioniert (die
Charakteristik war beschreibbar, hatte aber keine Aktion hinterlegt und
tat nichts), und der ESP-NOW-Rest ist entfernt, der bei jedem Messwert ein
"hallo" an eine feste MAC schickte. Kein notify, kein aktives Setzen der
Werte, kein Zugriff auf den BLE-Server aus dem Boot-Ablauf heraus.
In der App fällt damit ein Denkfehler auf: fehlende Kalibrier-Offsets
galten als "nicht kalibriert" statt als "unbekannt". Bei Firmware, die
sie nicht meldet, stünde dauerhaft eine Warnung, die sich nicht abstellen
lässt. Gewarnt wird jetzt nur, wenn das Gerät ausdrücklich Nulloffsets
meldet. Der Rücksetz-Knopf hing an derselben Bedingung und wäre sonst
unerreichbar gewesen.
Das Abfragen im Takt war bereits als Rückfallebene eingebaut und trägt
diesen Aufbau ohne Änderung.
Ausserdem nimmt die .gitignore jetzt virtuelle Python-Umgebungen aus. Eine
solche war versehentlich mitversioniert worden - über 14000 Dateien, die
maschinenabhängig sind und im Repo nichts zu suchen haben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Das ESPHome-Projekt VanAlign Pro misst über einen MPU6050 die Längs- und
Querneigung des Fahrzeugs und stellt sie über Bluetooth bereit. Damit
lässt sich der Camper beim Parken ausrichten.
Der Neigungsmesser bewirbt seinen Dienst, wird beim Einrichten also
sicher erkannt und die Geräteart vorbelegt. Anders als bei den übrigen
verbundenen Geräten gibt es hier kein Rahmenprotokoll: jede Messgrösse
liegt in einer eigenen Charakteristik als Float. LevelSession ist deshalb
eine eigene, deutlich einfachere Sitzungsart neben BMSSession - ohne
Protokollerkennung und ohne Kandidatensuche.
Bevorzugt werden die Werte abonniert; bietet das Gerät das nicht an, wird
im Takt abgefragt. Damit läuft die App auch mit der alten Firmware 1.0,
die nur Lesen kennt.
Die Detailansicht zeigt eine Libelle: die Blase wandert dorthin, wo das
Fahrzeug höher steht. Grün bis 0,5 Grad, orange bis zwei, darüber rot -
die Akzentfarbe der App ist selbst grün, "schief" hätte sonst genauso
ausgesehen wie "eben". Die Ringe sind ein echter Massstab (Toleranz und
zwei Grad), sonst sagt die Blasenlage nichts über die verbleibende
Abweichung. Dazu die Ansage in Worten, welche Seite höher steht.
Die Winkel werden fest little-endian gelesen. Die Web-Oberfläche des
Ursprungsprojekts probiert zusätzlich die umgekehrte Reihenfolge, falls
die erste unplausibel wirkt. Das ist nicht nur unnötig, sondern
schädlich: ein vertauschter Float von 4,25 Grad liest sich als etwa 0,0
und wirkt damit völlig plausibel - der Fehler bliebe unbemerkt. Ein Test
hält das fest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Umbenennen war zwar möglich, aber praktisch unauffindbar: das Eingabe-
feld trug nur einen Platzhalter, und übernommen wurde erst über einen
zusätzlichen Knopf. Jetzt steht "Name" als Beschriftung davor, der Knopf
ist weg, gesichert wird beim Abschluss der Eingabe und beim Verlassen der
Ansicht. Der Funkname steht darunter als "Gefunden als", damit das Gerät
weiter zuzuordnen ist.
Beim Einrichten wird nicht mehr der Funkname vorgeschlagen - Namen wie
"WTaEaAA25342229" taugen nicht als Anzeigename -, sondern die Art des
Geräts, sofern der Funkname kryptisch wirkt.
Die Detailansicht arbeitete auf einer Momentaufnahme des Geräts. Nach dem
Umbenennen oder nach dem Umstellen der Kühlzonen zeigte sie deshalb
weiter die alten Werte; sie liest den Stand jetzt aus dem Speicher.
Gegen die träge Oberfläche: CoreBluetooth meldet jedes Advertisement
einzeln und auf dem Hauptthread, und gescannt wird mit Duplikaten über
alle Geräte in Reichweite. Victron sendet mehrmals je Sekunde, dazu
kommt alles andere in Funkreichweite. Ausgewertet wird jetzt höchstens
einmal je Sekunde und Gerät, die Geräteliste beim Einrichten alle zwei
Sekunden, die Rohdaten der Diagnose alle drei. Ein Advertisement mehr
ändert die Anzeige ohnehin nicht, kostet aber Entschlüsselung und eine
Neuzeichnung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ausschalten blieb wirkungslos. Zwei Ursachen, beide behoben.
Die Länge des Einstellungsblocks hing an der Länge des empfangenen
Datensatzes statt an der Zonenzahl. Einzonen-Boxen, die den langen
Datensatz trotzdem senden, bekamen so einen 25-Byte-Block für zwei Zonen
und nahmen ihn offenbar nicht an. Jetzt entscheidet die erkannte
Zonenzahl: eine Zone ergibt 20 Byte und passt damit ohne Aufteilen in
einen Schreibvorgang.
Der Bestätigungsdialog beim Ausschalten hing an einer Section, und dort
zeigt SwiftUI ihn nicht zuverlässig an - der Schalter tat dann gar
nichts. Ein und Aus laufen jetzt beide direkt über denselben Weg, ohne
Rückfrage.
Die Diagnose zeigt zusätzlich den zuletzt abgeschickten Stellbefehl mit
Zeitstempel, damit sich unterscheiden lässt, ob ein Befehl das Gerät
nicht erreicht oder von ihm verworfen wird.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Fehler am Gerät gefunden.
Einzonen-Boxen als Doppelzone erkannt: Manche senden den langen
Datensatz einer Zweizonen-Box mit, füllen den zweiten Block aber mit
Nullen. Die Nutzlastlänge allein taugt deshalb nicht als Kriterium. Als
zweizonig gilt jetzt nur, wo dieser Block überhaupt Inhalt hat, und in
den Geräteeinstellungen lässt sich die Erkennung auf "Eine Zone" oder
"Zwei Zonen" festnageln.
Einschalten reagierte nicht: Ein/Aus und Betriebsart schicken den
kompletten Einstellungsblock, der mit dem langen Datensatz auf 31 Byte
kommt. Ohne ausgehandelte MTU nimmt BLE aber nur 20 Nutzbytes je
Schreibvorgang an, der Rest wurde stillschweigend verworfen. Die
Solltemperatur ging als kurzes Paket durch, alles Längere nicht. Lange
Pakete werden jetzt anhand von maximumWriteValueLength aufgeteilt und
versetzt geschrieben; die Statusabfrage danach wartet entsprechend
länger. Aus dem kurzen Datensatz ist der Befehl übrigens genau 20 Byte
groß und hätte gerade noch gepasst - deshalb trat es nur an dieser Box
auf.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die IceCube-Boxen von Plug-in Festivals sind umgelabelte Alpicool-Boxen –
der Hersteller verweist selbst auf die App "Alpicool T-Series". Bestätigt
am Gerät: die Charakteristiken 00001235 und 00001236 tauchen im GATT-Baum
auf. Dasselbe Protokoll sprechen BrassMonkey und Ocean Comfort.
AlpicoolProtocol baut und liest die Rahmen (FE FE, Länge, Kommando,
Daten, Bytesummen-Prüfsumme) und wertet die Statusantwort aus: Ist- und
Solltemperatur je Zone, Betriebsart, Kompressorstatus, Bedienfeldsperre,
Bordspannung und Batterieanzeige. Zweizonen-Boxen werden an der
Nutzlastlänge erkannt. Auf Stellbefehle antwortet die Box mit Echo und
Status in einer einzigen Benachrichtigung, weshalb der Rahmenleser
weitersucht statt nach dem ersten Treffer abzubrechen; das Echo läuft
mangels Statuslänge ins Leere.
Als bisher einziges Gerät ist die Box auch stellbar: Ein/Aus, Eco/Max,
Solltemperatur je Zone und Bedienfeldsperre. Nach jedem Stellbefehl wird
der Zustand neu abgefragt, sodass die Schalter dem Gerät folgen und nicht
der Eingabe. Ausschalten fragt einmal nach, weil dabei die Kühlung
stoppt. Ein/Aus und Betriebsart brauchen den kompletten
Einstellungsblock: er wird aus dem letzten Status neu aufgebaut und nur
im betroffenen Byte geändert, sonst überschriebe die Box eigene
Einstellungen mit Nullen. Ohne bekannten Status entsteht gar kein
Stellbefehl.
Die Namensprüfung in der Geräteliste hiess bisher looksLikeDaly und
hätte Kühlboxen aus der Vorauswahl geworfen; sie deckt jetzt alle
unterstützten Geräte ab und belegt die Art beim Einrichten vor.
Protokoll und Feldbelegung nach Gruni22/alpicool_ha_ble. Die festen
Anmelde- und Abfragepakete der Referenz sind byteweise abgesichert und
belegen zugleich, dass die allgemeine Prüfsummenregel auch für sie gilt.
139 Prüfungen laufen durch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
WattCycle spricht weder Daly noch JBD, sondern ein eigenes Modbus-artiges
Protokoll – und verlangt vor der ersten Abfrage eine Freischaltung: der
Text "HiLink" muss auf die Charakteristik FFFA geschrieben werden, sonst
bleibt der Akku auf alles stumm. Genau daran scheiterte die Erkennung;
die Charakteristik war im GATT-Baum sichtbar, wurde aber nur als weiterer
Schreibkandidat behandelt.
Neu ist WattCycleProtocol mit Rahmenbau (1E … 0D für Anfragen, 7E … 0D für
Antworten), Prüfsummen und der Auswertung des Messwert-Datensatzes
0x008C. Der ist selbstbeschreibend: Zellenanzahl, Zellspannungen,
Fühleranzahl, MOSFET- und Platinentemperatur, Zellfühler, dann Strom,
Spannung, Kapazitäten, Zyklen und Ladezustand. Der Strom hat ein eigenes
Format, bei dem Bit 15 das Vorzeichen und Bit 14 die Nachkommastelle
angibt. Aus Datenpunkt 0x0092 kommen Modell, Hersteller und Seriennummer,
die einmalig gelesen und in der Detailansicht gezeigt werden.
BMSSession kennt die Freischaltung jetzt als Teil eines Kandidaten: liegt
im selben Dienst eine FFFA-Charakteristik, wird nach dem bestätigten Abo
kurz gewartet, freigeschaltet, nochmal gewartet und dann erst abgefragt.
Für die anderen Protokolle ist das unschädlich.
Protokoll und Feldbelegung stammen aus frabnet/esphome-wattcycle-ble. Die
dortige Tabellen-Prüfsumme ist gegen den klassischen Modbus-CRC
nachgerechnet (identisch über 3063 Testfälle), sodass die vorhandene
CRC-Funktion genügt und die 512 Byte Tabellen entfallen. Die
Anfragerahmen sind byteweise abgesichert, die Auswertung an einem
vollständigen Datensatz – 107 Prüfungen laufen durch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Suche nach dem richtigen Verbindungsweg blieb auf dem ersten
Kandidaten stehen: gesendet wurde erst, nachdem iOS das Abonnieren der
Notify-Charakteristik bestätigt hat. Bestätigt ein Modul das nie – bei
der WattCycle-Batterie auf FFF1 der Fall –, wurde weder je eine Anfrage
geschickt noch zum nächsten Kandidaten gewechselt. Die Diagnose zeigte
dauerhaft "1 von 16" und "0 Anfragen / 0 Byte".
Die Aktivierung eines Kandidaten hat jetzt ein Zeitlimit von vier
Sekunden. Läuft es ab, geht es zum nächsten; ist es der einzige Weg,
wird trotzdem gesendet – manche Module antworten auch ohne bestätigtes
Abonnement. Ein Token verhindert, dass verspätete Rückmeldungen eines
bereits verworfenen Kandidaten den aktuellen durcheinanderbringen.
Damit sich Fortschritt überhaupt beobachten lässt, wird die Diagnose
jetzt bei jeder gesendeten Anfrage aktualisiert statt nur am Ende einer
Runde, und zeigt zusätzlich Verbindungszustand, ob der Empfang abonniert
ist, und wann zuletzt gesendet wurde. Die Suche ist enger getaktet, damit
alle Wege in gut zwei Minuten durch sind.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die WattCycle-Batterie meldete sich am FFF0-Dienst, nahm aber keine
Kommandos an. Grund: dort ist FFF1 die erste beschreibbare
Charakteristik, sie sieht schreibbar aus und bleibt trotzdem stumm –
Kommandos gehören auf FFF2.
Statt die richtige Charakteristik zu raten, stellt BMSSession jetzt alle
Paare aus schreibbarer und benachrichtigender Charakteristik zusammen,
jeweils mit und ohne Schreibbestätigung, und arbeitet sie ab, bis eines
antwortet. Bekannte Paare (FFF2/FFF1, FF02/FF01, Nordic UART) kommen
zuerst dran; ein Kandidat, der sich nicht abonnieren lässt, wird sofort
übersprungen. Auf jedem Weg werden weiterhin Daly klassisch, Daly Modbus
und JBD angefragt.
Die Diagnose zeigt dazu den vollständigen GATT-Baum des Geräts, den
gerade versuchten Weg samt Position in der Kandidatenliste sowie Zähler
für gesendete Anfragen und empfangene Bytes. Damit lässt sich
unterscheiden, ob das BMS die Kommandos gar nicht annimmt oder ein
unbekanntes Protokoll spricht.
Nebenbei: "caravan" existiert nicht als SF-Symbol und ließ SwiftUI
stillschweigend auf Text zurückfallen – in den Demodaten ersetzt.
README auf Profile und WattCycle/JBD nachgezogen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>