import SwiftUI /// Einrichtung des Neigungsmessers: Einbaulage und Nullpunkt. /// /// Beides wird einmal eingestellt und danach kaum wieder angefasst. In der /// Geräteansicht standen die Knöpfe direkt unter der Libelle – ein Fehlgriff /// beim Ablesen verstellte dort den Nullpunkt. Deshalb liegen sie hier. /// /// Die Reihenfolge ist nicht beliebig: erst muss klar sein, welche Achse des /// Sensors welche des Fahrzeugs ist, sonst wird der Nullpunkt auf die falsche /// Achse gelegt. struct LevelSetupView: View { let device: ConfiguredDevice @Environment(BluetoothManager.self) private var bluetooth @State private var showSensorSetup = false @State private var showResetConfirmation = false private var state: LevelState { bluetooth.levelStates[device.id] ?? LevelState() } /// Immer der aktuelle Stand – `device` veraltet, sobald der Assistent /// eine neue Einbaulage gespeichert hat. @Environment(DeviceStore.self) private var store private var currentDevice: ConfiguredDevice { store.devices.first { $0.id == device.id } ?? device } var body: some View { List { Section { HStack(spacing: 24) { reading("Längs", state.pitch) reading("Quer", state.roll) } .frame(maxWidth: .infinity) .padding(.vertical, 4) } Section { Button { showSensorSetup = true } label: { LabeledContent { Text(currentDevice.sensorOrientation.summary) .font(.caption) } label: { Label("Einbaulage bestimmen", systemImage: "arrow.triangle.2.circlepath") } } .disabled(!state.hasReading) } header: { Text("Schritt 1 – Einbaulage") } footer: { Text("Sitzt der Sensor quer oder verdreht im Fahrzeug, meldet er längs " + "und quer vertauscht. Der Assistent klärt das durch zweimaliges " + "Kippen. Danach den Nullpunkt setzen.") } Section { Button { bluetooth.calibrateLevel(for: device.id) } label: { Label("Aktuelle Lage als eben übernehmen", systemImage: "scope") } .disabled(!state.hasReading) if !state.isKnownUncalibrated { Button(role: .destructive) { showResetConfirmation = true } label: { // Ohne das bliebe das Symbol in der Akzentfarbe, während // die Beschriftung rot ist. Label("Nullpunkt verwerfen", systemImage: "arrow.uturn.backward") .foregroundStyle(.red) } } } header: { Text("Schritt 2 – Nullpunkt") } footer: { Text(calibrationHint) } } .navigationTitle("Neigungsmesser einrichten") .navigationBarTitleDisplayMode(.inline) .sheet(isPresented: $showSensorSetup) { SensorSetupView(device: currentDevice) } .confirmationDialog("Nullpunkt verwerfen?", isPresented: $showResetConfirmation, titleVisibility: .visible) { Button("Verwerfen", role: .destructive) { bluetooth.resetLevelCalibration(for: device.id) } } message: { Text("Die Anzeige zeigt danach wieder die Lage des Sensors.") } } private var calibrationHint: String { if state.isCalibrated, let pitchOffset = state.pitchOffset, let rollOffset = state.rollOffset { return String(format: "Der Nullpunkt liegt bei %.1f° längs und %.1f° quer. " + "Zum Neusetzen das Fahrzeug eben stellen und dann tippen.", pitchOffset, rollOffset) } if state.isKnownUncalibrated { return "Noch kein Nullpunkt gesetzt – die Anzeige zeigt die Lage des " + "Sensors, nicht die des Fahrzeugs. Fahrzeug eben stellen, dann tippen." } // Ältere Firmware gibt die Offsets nicht heraus. return "Zum Setzen das Fahrzeug eben stellen und dann tippen. Ob schon ein " + "Nullpunkt gesetzt wurde, meldet dieses Gerät nicht zurück." } private func reading(_ title: String, _ value: Double?) -> some View { VStack(spacing: 2) { Text(value.map { String(format: "%.1f°", $0) } ?? "–") .font(.title2.weight(.semibold).monospacedDigit()) Text(title) .font(.caption) .foregroundStyle(.secondary) } } }