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>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package de.fritob.campermonitor.protocol
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Welche Rolle ein Gerät im Camper spielt. Bestimmt Symbol, Sortierung und
|
||||
* welche Kennzahl als "Hauptwert" auf der Kachel gross dargestellt wird.
|
||||
*/
|
||||
enum class DeviceRole(val title: String) {
|
||||
CHARGE_BOOSTER("Ladebooster"),
|
||||
SOLAR_CHARGER("Solarladeregler"),
|
||||
BATTERY_MONITOR("Batteriemonitor"),
|
||||
BMS("Batterie / BMS"),
|
||||
FRIDGE("Kühlbox"),
|
||||
LEVELING("Nivellierung");
|
||||
|
||||
/**
|
||||
* Victron-Geräte werden passiv über das Advertisement gelesen, BMS,
|
||||
* Kühlbox und Neigungsmesser brauchen eine echte GATT-Verbindung.
|
||||
*/
|
||||
val transport: DeviceTransport
|
||||
get() = when (this) {
|
||||
BMS, FRIDGE, LEVELING -> DeviceTransport.CONNECT
|
||||
else -> DeviceTransport.ADVERTISEMENT
|
||||
}
|
||||
}
|
||||
|
||||
enum class DeviceTransport {
|
||||
/** Passives Mitlesen der BLE-Werbedaten (Victron Instant Readout). */
|
||||
ADVERTISEMENT,
|
||||
|
||||
/** Verbindungsaufbau, Kommando schreiben, Antwort per Notify lesen. */
|
||||
CONNECT,
|
||||
}
|
||||
|
||||
/**
|
||||
* Ein eingerichtetes Gerät.
|
||||
*
|
||||
* Die Bluetooth-Adresse ist unter Android ein Text („AA:BB:CC:DD:EE:FF") und
|
||||
* nicht wie unter iOS eine UUID – iOS gibt die Hardware-Adresse gar nicht
|
||||
* heraus, Android schon.
|
||||
*/
|
||||
data class ConfiguredDevice(
|
||||
val id: UUID = UUID.randomUUID(),
|
||||
val name: String,
|
||||
val role: DeviceRole,
|
||||
val profileID: UUID,
|
||||
/** MAC-Adresse des Geräts. */
|
||||
val address: String,
|
||||
/** Wie das Gerät sich selbst nennt, für die Wiedererkennung. */
|
||||
val advertisedName: String? = null,
|
||||
val fridgeZoneMode: FridgeZoneMode = FridgeZoneMode.AUTOMATIC,
|
||||
val sensorOrientation: SensorOrientation = SensorOrientation.IDENTITY,
|
||||
)
|
||||
|
||||
/**
|
||||
* Ein Fahrzeug. Jedes Profil hat seinen eigenen Satz Geräte; die App zeigt und
|
||||
* funkt immer nur für das gerade gewählte.
|
||||
*/
|
||||
data class Profile(
|
||||
val id: UUID = UUID.randomUUID(),
|
||||
val name: String,
|
||||
/** Symbolname für die Auswahl im Dashboard. */
|
||||
val symbol: String = "box_truck",
|
||||
/** Spurweite in Metern – für die Berechnung der Auffahrkeile quer. */
|
||||
val trackWidth: Double? = null,
|
||||
/** Radstand in Metern – dasselbe längs. */
|
||||
val wheelbase: Double? = null,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Profil, dem Geräte aus der Zeit vor der Profilverwaltung zugeordnet
|
||||
* werden. Feste Kennung, damit die Zuordnung beim Update erhalten bleibt.
|
||||
*/
|
||||
val DEFAULT_ID: UUID = UUID.fromString("00000000-0000-0000-0000-00000000c001")
|
||||
|
||||
fun initial() = Profile(id = DEFAULT_ID, name = "Mein Camper")
|
||||
|
||||
/** Auswahl für die Profilbearbeitung. */
|
||||
val SYMBOLS = listOf(
|
||||
"box_truck", "pickup", "bus", "double_decker",
|
||||
"car", "car_side", "tent", "sailboat", "lodge", "mountains",
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user