WattCycle-Akkus unterstützen

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>
This commit is contained in:
BiasF
2026-08-30 13:00:16 +02:00
co-authored by Claude Opus 5
parent fc73f730a7
commit 091243a1d8
7 changed files with 482 additions and 14 deletions
@@ -55,6 +55,14 @@ struct DeviceSnapshot: Identifiable, Sendable {
var cellVoltages: [Double] = []
/// Temperaturfühler in °C (nur BMS).
var temperatures: [Double] = []
/// Feste Angaben des Geräts, etwa Modell oder Seriennummer.
var info: [InfoItem] = []
struct InfoItem: Identifiable, Hashable, Sendable {
let label: String
let value: String
var id: String { label }
}
var primaryMetric: Metric? {
metrics.first(where: \.isPrimary) ?? metrics.first