forked from fritob/Camper-Monitor
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>
67 lines
2.4 KiB
Swift
67 lines
2.4 KiB
Swift
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("Ä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)
|
||
}
|
||
}
|