forked from fritob/Camper-Monitor
Die Geräteansichten zeigten dauerhaft die technischen Angaben: erkanntes Protokoll, Bluetooth-Merkmale samt vollständigem Merkmalsbaum, gesendete Befehle und die Rohdaten der letzten Antwort. Das war zum Einrichten der Geräte nötig - für den Alltag ist es nur Ballast zwischen den Messwerten. Sie sind jetzt ausgeblendet und über Einstellungen → Diagnose anzeigen wieder einzublenden. Die Einstellungen sind neu und sitzen im Fahrzeugmenü des Dashboards. Eine Ausnahme: Meldet ein Gerät einen Fehler oder fehlt ein Victron-Schlüssel, erscheinen die Angaben unabhängig von der Einstellung. Genau dann sind sie das Einzige, was weiterhilft - und niemand denkt in dem Moment daran, sie erst irgendwo einzuschalten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
117 lines
4.2 KiB
Swift
117 lines
4.2 KiB
Swift
import SwiftUI
|
|
|
|
struct DashboardView: View {
|
|
@Environment(DeviceStore.self) private var store
|
|
@Environment(BluetoothManager.self) private var bluetooth
|
|
|
|
@State private var isAddingDevice = false
|
|
@State private var isManagingProfiles = false
|
|
@State private var isShowingSettings = false
|
|
|
|
private let columns = [GridItem(.adaptive(minimum: 300), spacing: 16)]
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ScrollView {
|
|
if !bluetooth.isBluetoothReady {
|
|
statusBanner
|
|
}
|
|
|
|
if store.activeDevices.isEmpty {
|
|
emptyState
|
|
} else {
|
|
LazyVGrid(columns: columns, spacing: 16) {
|
|
ForEach(store.activeDevices) { device in
|
|
NavigationLink {
|
|
DeviceDetailView(device: device)
|
|
} label: {
|
|
DeviceCard(
|
|
device: device,
|
|
snapshot: bluetooth.snapshots[device.id],
|
|
linkState: bluetooth.linkStates[device.id] ?? .searching
|
|
)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.padding(.horizontal)
|
|
.padding(.top, 8)
|
|
}
|
|
}
|
|
.background(Color(.systemGroupedBackground))
|
|
.navigationTitle(store.activeProfile?.name ?? "Camper")
|
|
.toolbar {
|
|
ToolbarItem(placement: .topBarLeading) {
|
|
profileMenu
|
|
}
|
|
ToolbarItem(placement: .primaryAction) {
|
|
Button("Gerät hinzufügen", systemImage: "plus") {
|
|
isAddingDevice = true
|
|
}
|
|
}
|
|
}
|
|
.sheet(isPresented: $isAddingDevice) {
|
|
AddDeviceView()
|
|
}
|
|
.sheet(isPresented: $isManagingProfiles) {
|
|
ProfilesView()
|
|
}
|
|
.sheet(isPresented: $isShowingSettings) {
|
|
SettingsView()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Umschalter zwischen den Fahrzeugen.
|
|
private var profileMenu: some View {
|
|
Menu {
|
|
Picker("Fahrzeug", selection: Binding(
|
|
get: { store.activeProfileID },
|
|
set: { id in
|
|
guard let profile = store.profiles.first(where: { $0.id == id }) else { return }
|
|
store.selectProfile(profile)
|
|
bluetooth.refreshConfiguration()
|
|
}
|
|
)) {
|
|
ForEach(store.profiles) { profile in
|
|
Label(profile.name, systemImage: profile.symbol).tag(profile.id)
|
|
}
|
|
}
|
|
Divider()
|
|
Button("Fahrzeuge verwalten…", systemImage: "car.2") {
|
|
isManagingProfiles = true
|
|
}
|
|
Button("Einstellungen…", systemImage: "gearshape") {
|
|
isShowingSettings = true
|
|
}
|
|
} label: {
|
|
Label(store.activeProfile?.name ?? "Fahrzeug",
|
|
systemImage: store.activeProfile?.symbol ?? "box.truck")
|
|
.labelStyle(.iconOnly)
|
|
}
|
|
}
|
|
|
|
private var statusBanner: some View {
|
|
Label(bluetooth.bluetoothStatusText, systemImage: "exclamationmark.triangle.fill")
|
|
.font(.callout)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding()
|
|
.background(.orange.opacity(0.15), in: .rect(cornerRadius: 12))
|
|
.padding(.horizontal)
|
|
.padding(.top, 8)
|
|
}
|
|
|
|
private var emptyState: some View {
|
|
ContentUnavailableView {
|
|
Label("Noch keine Geräte", systemImage: "antenna.radiowaves.left.and.right")
|
|
} description: {
|
|
Text("Füge \(store.activeProfile.map { "„\($0.name)“" } ?? "diesem Fahrzeug") "
|
|
+ "den Ladebooster, den Solarladeregler und das BMS hinzu.")
|
|
} actions: {
|
|
Button("Gerät suchen") { isAddingDevice = true }
|
|
.buttonStyle(.borderedProminent)
|
|
}
|
|
.padding(.top, 60)
|
|
}
|
|
}
|