Files
Camper-Monitor/CamperMonitor/Views/FridgeControls.swift
T
BiasFandClaude Opus 5 270c701ad6 Kühlbox wieder bedienbar, Einbaulage richtig messen
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>
2026-08-30 23:43:57 +02:00

80 lines
3.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
/// Bedienelemente einer Alpicool-Kühlbox.
///
/// Alle Schalter folgen dem Gerät, nicht der Vermutung: nach jedem Stellbefehl
/// fragt die Sitzung den Zustand neu ab, und die Ansicht zeigt, was zurückkam.
struct FridgeControls: View {
let device: ConfiguredDevice
let state: AlpicoolState
@Environment(BluetoothManager.self) private var bluetooth
var body: some View {
Section {
// Ohne Rückfrage: ein Bestätigungsdialog an einer Section wird von
// SwiftUI nicht zuverlässig angezeigt, und der Schalter blieb dann
// wirkungslos. Der Weg ist jetzt für Ein und Aus derselbe.
Toggle("Eingeschaltet", isOn: Binding(
get: { state.isPoweredOn },
set: { bluetooth.setFridgePower($0, for: device.id) }
))
Picker("Betriebsart", selection: Binding(
get: { state.isEco },
set: { bluetooth.setFridgeEco($0, for: device.id) }
)) {
Text("Max").tag(false)
Text("Eco").tag(true)
}
.pickerStyle(.segmented)
.disabled(!state.isPoweredOn)
targetStepper(zone: .left,
title: state.isDualZone ? "Soll links" : "Solltemperatur",
value: state.leftTarget)
if state.isDualZone {
targetStepper(zone: .right, title: "Soll rechts", value: state.rightTarget)
}
Toggle("Bedienfeld gesperrt", isOn: Binding(
get: { state.isLocked },
set: { bluetooth.setFridgeLock($0, for: device.id) }
))
} header: {
Text("Steuerung")
} footer: {
Text(footerText)
}
}
/// Ob die Box gerade erreichbar ist. Ist sie es nicht, wird ein Befehl
/// aufgehoben statt verworfen das gehört gesagt, sonst sieht es aus, als
/// hätte das Tippen nichts bewirkt.
private var isLinked: Bool { bluetooth.linkStates[device.id] == .live }
private var footerText: String {
guard isLinked else {
return "Die Box ist gerade nicht verbunden. Die Änderung wird "
+ "gemerkt und geht raus, sobald sie wieder erreichbar ist."
}
return "Änderungen gehen direkt an die Box. Der angezeigte Stand kommt "
+ "aus ihrer Antwort, nicht aus der Eingabe."
}
private func targetStepper(zone: AlpicoolState.Zone, title: String, value: Int?) -> some View {
let range = state.targetRange
return Stepper(value: Binding(
get: { value ?? range.lowerBound },
set: { bluetooth.setFridgeTarget($0, zone: zone, for: device.id) }
), in: range) {
LabeledContent(title) {
Text(value.map { "\($0) \(state.unitSymbol)" } ?? "")
.monospacedDigit()
}
}
.disabled(!state.isPoweredOn || value == nil)
}
}