chargers in overview
This commit is contained in:
@@ -108,6 +108,10 @@
|
|||||||
"battery.onboarding.title" = "Add your first battery";
|
"battery.onboarding.title" = "Add your first battery";
|
||||||
"battery.onboarding.subtitle" = "Track your bank's capacity and chemistry to keep runtime expectations in check.";
|
"battery.onboarding.subtitle" = "Track your bank's capacity and chemistry to keep runtime expectations in check.";
|
||||||
"battery.bank.badge.voltage" = "Voltage";
|
"battery.bank.badge.voltage" = "Voltage";
|
||||||
|
"overview.chargers.header.title" = "Charger Overview";
|
||||||
|
"overview.chargers.empty.title" = "No chargers configured yet";
|
||||||
|
"overview.chargers.empty.subtitle" = "Add shore power, DC-DC, or solar chargers to understand your charging capacity.";
|
||||||
|
"overview.chargers.empty.create" = "Add Charger";
|
||||||
"battery.bank.badge.capacity" = "Capacity";
|
"battery.bank.badge.capacity" = "Capacity";
|
||||||
"battery.bank.badge.energy" = "Energy";
|
"battery.bank.badge.energy" = "Energy";
|
||||||
"battery.bank.banner.voltage" = "Voltage mismatch detected";
|
"battery.bank.banner.voltage" = "Voltage mismatch detected";
|
||||||
|
|||||||
@@ -282,11 +282,14 @@ struct LoadsView: View {
|
|||||||
system: system,
|
system: system,
|
||||||
loads: savedLoads,
|
loads: savedLoads,
|
||||||
batteries: savedBatteries,
|
batteries: savedBatteries,
|
||||||
|
chargers: savedChargers,
|
||||||
onSelectLoads: { selectedComponentTab = .components },
|
onSelectLoads: { selectedComponentTab = .components },
|
||||||
onSelectBatteries: { selectedComponentTab = .batteries },
|
onSelectBatteries: { selectedComponentTab = .batteries },
|
||||||
|
onSelectChargers: { selectedComponentTab = .chargers },
|
||||||
onCreateLoad: { createNewLoad() },
|
onCreateLoad: { createNewLoad() },
|
||||||
onBrowseLibrary: { showingComponentLibrary = true },
|
onBrowseLibrary: { showingComponentLibrary = true },
|
||||||
onCreateBattery: { startBatteryConfiguration() }
|
onCreateBattery: { startBatteryConfiguration() },
|
||||||
|
onCreateCharger: { startChargerConfiguration() }
|
||||||
)
|
)
|
||||||
.accessibilityIdentifier("system-overview")
|
.accessibilityIdentifier("system-overview")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ struct SystemOverviewView: View {
|
|||||||
let system: ElectricalSystem
|
let system: ElectricalSystem
|
||||||
let loads: [SavedLoad]
|
let loads: [SavedLoad]
|
||||||
let batteries: [SavedBattery]
|
let batteries: [SavedBattery]
|
||||||
|
let chargers: [SavedCharger]
|
||||||
let onSelectLoads: () -> Void
|
let onSelectLoads: () -> Void
|
||||||
let onSelectBatteries: () -> Void
|
let onSelectBatteries: () -> Void
|
||||||
|
let onSelectChargers: () -> Void
|
||||||
let onCreateLoad: () -> Void
|
let onCreateLoad: () -> Void
|
||||||
let onBrowseLibrary: () -> Void
|
let onBrowseLibrary: () -> Void
|
||||||
let onCreateBattery: () -> Void
|
let onCreateBattery: () -> Void
|
||||||
|
let onCreateCharger: () -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@@ -19,6 +22,7 @@ struct SystemOverviewView: View {
|
|||||||
systemCard
|
systemCard
|
||||||
loadsCard
|
loadsCard
|
||||||
batteriesCard
|
batteriesCard
|
||||||
|
chargersCard
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
.padding(.vertical, 20)
|
.padding(.vertical, 20)
|
||||||
@@ -70,6 +74,12 @@ struct SystemOverviewView: View {
|
|||||||
value: "\(batteries.count)",
|
value: "\(batteries.count)",
|
||||||
tint: .green
|
tint: .green
|
||||||
)
|
)
|
||||||
|
summaryMetric(
|
||||||
|
icon: "bolt.fill",
|
||||||
|
label: chargerCountLabel,
|
||||||
|
value: "\(chargers.count)",
|
||||||
|
tint: .orange
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
@@ -85,6 +95,12 @@ struct SystemOverviewView: View {
|
|||||||
value: "\(batteries.count)",
|
value: "\(batteries.count)",
|
||||||
tint: .green
|
tint: .green
|
||||||
)
|
)
|
||||||
|
summaryMetric(
|
||||||
|
icon: "bolt.fill",
|
||||||
|
label: chargerCountLabel,
|
||||||
|
value: "\(chargers.count)",
|
||||||
|
tint: .orange
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,6 +355,94 @@ struct SystemOverviewView: View {
|
|||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var chargersCard: some View {
|
||||||
|
Button(action: onSelectChargers) {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text(chargerSummaryTitle)
|
||||||
|
.font(.headline.weight(.semibold))
|
||||||
|
|
||||||
|
if chargers.isEmpty {
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
Text(chargerEmptyTitle)
|
||||||
|
.font(.subheadline.weight(.semibold))
|
||||||
|
Text(chargerEmptySubtitle)
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(action: onCreateCharger) {
|
||||||
|
Label(chargerEmptyCreateAction, systemImage: "plus")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
.controlSize(.large)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ViewThatFits(in: .horizontal) {
|
||||||
|
HStack(spacing: 16) {
|
||||||
|
summaryMetric(
|
||||||
|
icon: "bolt.fill",
|
||||||
|
label: chargerCountLabel,
|
||||||
|
value: "\(chargers.count)",
|
||||||
|
tint: .orange
|
||||||
|
)
|
||||||
|
summaryMetric(
|
||||||
|
icon: "powerplug",
|
||||||
|
label: chargerOutputLabel,
|
||||||
|
value: formattedChargerOutput(representativeChargerOutput),
|
||||||
|
tint: .indigo
|
||||||
|
)
|
||||||
|
summaryMetric(
|
||||||
|
icon: "gauge.medium",
|
||||||
|
label: chargerCurrentLabel,
|
||||||
|
value: formattedCurrent(totalChargerCurrent),
|
||||||
|
tint: .blue
|
||||||
|
)
|
||||||
|
summaryMetric(
|
||||||
|
icon: "bolt.circle",
|
||||||
|
label: chargerPowerLabel,
|
||||||
|
value: formattedPower(totalChargerPower),
|
||||||
|
tint: .pink
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
summaryMetric(
|
||||||
|
icon: "bolt.fill",
|
||||||
|
label: chargerCountLabel,
|
||||||
|
value: "\(chargers.count)",
|
||||||
|
tint: .orange
|
||||||
|
)
|
||||||
|
summaryMetric(
|
||||||
|
icon: "powerplug",
|
||||||
|
label: chargerOutputLabel,
|
||||||
|
value: formattedChargerOutput(representativeChargerOutput),
|
||||||
|
tint: .indigo
|
||||||
|
)
|
||||||
|
summaryMetric(
|
||||||
|
icon: "gauge.medium",
|
||||||
|
label: chargerCurrentLabel,
|
||||||
|
value: formattedCurrent(totalChargerCurrent),
|
||||||
|
tint: .blue
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.padding(.vertical, 18)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 20, style: .continuous)
|
||||||
|
.fill(Color(.systemBackground))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
|
||||||
private var loadStatus: LoadConfigurationStatus? {
|
private var loadStatus: LoadConfigurationStatus? {
|
||||||
guard !loads.isEmpty else { return nil }
|
guard !loads.isEmpty else { return nil }
|
||||||
let incomplete = loads.filter { load in
|
let incomplete = loads.filter { load in
|
||||||
@@ -374,6 +478,25 @@ struct SystemOverviewView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var totalChargerCurrent: Double {
|
||||||
|
chargers.reduce(0) { result, charger in
|
||||||
|
result + max(0, charger.maxCurrentAmps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var totalChargerPower: Double {
|
||||||
|
chargers.reduce(0) { result, charger in
|
||||||
|
result + max(0, charger.effectivePowerWatts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var representativeChargerOutput: Double? {
|
||||||
|
let outputs = chargers.map { $0.outputVoltage }.filter { $0 > 0 }
|
||||||
|
guard !outputs.isEmpty else { return nil }
|
||||||
|
let total = outputs.reduce(0, +)
|
||||||
|
return total / Double(outputs.count)
|
||||||
|
}
|
||||||
|
|
||||||
private var batteryWarning: BatteryWarning? {
|
private var batteryWarning: BatteryWarning? {
|
||||||
guard batteries.count > 1 else { return nil }
|
guard batteries.count > 1 else { return nil }
|
||||||
|
|
||||||
@@ -488,6 +611,11 @@ struct SystemOverviewView: View {
|
|||||||
return "\(numberString) W"
|
return "\(numberString) W"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func formattedChargerOutput(_ value: Double?) -> String {
|
||||||
|
guard let value, value > 0 else { return "—" }
|
||||||
|
return formattedValue(value, unit: "V")
|
||||||
|
}
|
||||||
|
|
||||||
private func formattedValue(_ value: Double, unit: String) -> String {
|
private func formattedValue(_ value: Double, unit: String) -> String {
|
||||||
let numberString = Self.numberFormatter.string(from: NSNumber(value: value)) ?? String(format: "%.1f", value)
|
let numberString = Self.numberFormatter.string(from: NSNumber(value: value)) ?? String(format: "%.1f", value)
|
||||||
return "\(numberString) \(unit)"
|
return "\(numberString) \(unit)"
|
||||||
@@ -659,6 +787,78 @@ struct SystemOverviewView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var chargerSummaryTitle: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"overview.chargers.header.title",
|
||||||
|
bundle: .main,
|
||||||
|
value: "Charger Overview",
|
||||||
|
comment: "Title for the chargers summary section"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chargerCountLabel: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"chargers.summary.metric.count",
|
||||||
|
bundle: .main,
|
||||||
|
value: "Chargers",
|
||||||
|
comment: "Label for number of chargers metric"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chargerOutputLabel: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"chargers.summary.metric.output",
|
||||||
|
bundle: .main,
|
||||||
|
value: "Output Voltage",
|
||||||
|
comment: "Label for representative output voltage metric"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chargerCurrentLabel: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"chargers.summary.metric.current",
|
||||||
|
bundle: .main,
|
||||||
|
value: "Charge Rate",
|
||||||
|
comment: "Label for total charger current metric"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chargerPowerLabel: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"chargers.summary.metric.power",
|
||||||
|
bundle: .main,
|
||||||
|
value: "Charge Power",
|
||||||
|
comment: "Label for total charger power metric"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chargerEmptyTitle: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"overview.chargers.empty.title",
|
||||||
|
bundle: .main,
|
||||||
|
value: "No chargers configured yet",
|
||||||
|
comment: "Title shown when no chargers are configured"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chargerEmptySubtitle: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"overview.chargers.empty.subtitle",
|
||||||
|
bundle: .main,
|
||||||
|
value: "Add shore power, DC-DC, or solar chargers to understand your charging capacity.",
|
||||||
|
comment: "Subtitle shown when no chargers are configured"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var chargerEmptyCreateAction: String {
|
||||||
|
NSLocalizedString(
|
||||||
|
"overview.chargers.empty.create",
|
||||||
|
bundle: .main,
|
||||||
|
value: "Add Charger",
|
||||||
|
comment: "Button title to create a charger from the overview"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private var systemOverviewTitle: String {
|
private var systemOverviewTitle: String {
|
||||||
NSLocalizedString(
|
NSLocalizedString(
|
||||||
"overview.system.header.title",
|
"overview.system.header.title",
|
||||||
@@ -802,15 +1002,41 @@ struct SystemOverviewView: View {
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let chargers: [SavedCharger] = [
|
||||||
|
SavedCharger(
|
||||||
|
name: "Shore Power",
|
||||||
|
inputVoltage: 230,
|
||||||
|
outputVoltage: 14.4,
|
||||||
|
maxCurrentAmps: 40,
|
||||||
|
maxPowerWatts: 580,
|
||||||
|
iconName: "powerplug",
|
||||||
|
colorName: "orange",
|
||||||
|
system: system
|
||||||
|
),
|
||||||
|
SavedCharger(
|
||||||
|
name: "DC-DC Charger",
|
||||||
|
inputVoltage: 12.8,
|
||||||
|
outputVoltage: 14.2,
|
||||||
|
maxCurrentAmps: 30,
|
||||||
|
maxPowerWatts: 0,
|
||||||
|
iconName: "bolt.circle",
|
||||||
|
colorName: "blue",
|
||||||
|
system: system
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
SystemOverviewView(
|
SystemOverviewView(
|
||||||
system: system,
|
system: system,
|
||||||
loads: loads,
|
loads: loads,
|
||||||
batteries: batteries,
|
batteries: batteries,
|
||||||
|
chargers: chargers,
|
||||||
onSelectLoads: {},
|
onSelectLoads: {},
|
||||||
onSelectBatteries: {},
|
onSelectBatteries: {},
|
||||||
|
onSelectChargers: {},
|
||||||
onCreateLoad: {},
|
onCreateLoad: {},
|
||||||
onBrowseLibrary: {},
|
onBrowseLibrary: {},
|
||||||
onCreateBattery: {}
|
onCreateBattery: {},
|
||||||
|
onCreateCharger: {}
|
||||||
)
|
)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@@ -827,11 +1053,14 @@ struct SystemOverviewView: View {
|
|||||||
system: system,
|
system: system,
|
||||||
loads: [],
|
loads: [],
|
||||||
batteries: [],
|
batteries: [],
|
||||||
|
chargers: [],
|
||||||
onSelectLoads: {},
|
onSelectLoads: {},
|
||||||
onSelectBatteries: {},
|
onSelectBatteries: {},
|
||||||
|
onSelectChargers: {},
|
||||||
onCreateLoad: {},
|
onCreateLoad: {},
|
||||||
onBrowseLibrary: {},
|
onBrowseLibrary: {},
|
||||||
onCreateBattery: {}
|
onCreateBattery: {},
|
||||||
|
onCreateCharger: {}
|
||||||
)
|
)
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,6 +176,10 @@
|
|||||||
"battery.onboarding.title" = "Füge deine erste Batterie hinzu";
|
"battery.onboarding.title" = "Füge deine erste Batterie hinzu";
|
||||||
"battery.onboarding.subtitle" = "Behalte Kapazität und Chemie deiner Batterien im Blick, um die Laufzeit im Griff zu behalten.";
|
"battery.onboarding.subtitle" = "Behalte Kapazität und Chemie deiner Batterien im Blick, um die Laufzeit im Griff zu behalten.";
|
||||||
"battery.bank.badge.voltage" = "Spannung";
|
"battery.bank.badge.voltage" = "Spannung";
|
||||||
|
"overview.chargers.header.title" = "Ladegeräte";
|
||||||
|
"overview.chargers.empty.title" = "Noch keine Ladegeräte konfiguriert";
|
||||||
|
"overview.chargers.empty.subtitle" = "Füge Landstrom-, DC-DC- oder Solarladegeräte hinzu, um deine Ladeleistung zu verstehen.";
|
||||||
|
"overview.chargers.empty.create" = "Ladegerät hinzufügen";
|
||||||
"battery.bank.badge.capacity" = "Kapazität";
|
"battery.bank.badge.capacity" = "Kapazität";
|
||||||
"battery.bank.badge.energy" = "Energie";
|
"battery.bank.badge.energy" = "Energie";
|
||||||
"battery.bank.banner.voltage" = "Spannungsabweichung erkannt";
|
"battery.bank.banner.voltage" = "Spannungsabweichung erkannt";
|
||||||
|
|||||||
@@ -175,6 +175,10 @@
|
|||||||
"battery.onboarding.title" = "Añade tu primera batería";
|
"battery.onboarding.title" = "Añade tu primera batería";
|
||||||
"battery.onboarding.subtitle" = "Controla la capacidad y la química del banco para mantener tus tiempos de autonomía bajo control.";
|
"battery.onboarding.subtitle" = "Controla la capacidad y la química del banco para mantener tus tiempos de autonomía bajo control.";
|
||||||
"battery.bank.badge.voltage" = "Voltaje";
|
"battery.bank.badge.voltage" = "Voltaje";
|
||||||
|
"overview.chargers.header.title" = "Resumen de cargadores";
|
||||||
|
"overview.chargers.empty.title" = "Aún no hay cargadores configurados";
|
||||||
|
"overview.chargers.empty.subtitle" = "Añade cargadores de toma de puerto, DC-DC o solares para conocer tu capacidad de carga.";
|
||||||
|
"overview.chargers.empty.create" = "Agregar cargador";
|
||||||
"battery.bank.badge.capacity" = "Capacidad";
|
"battery.bank.badge.capacity" = "Capacidad";
|
||||||
"battery.bank.badge.energy" = "Energía";
|
"battery.bank.badge.energy" = "Energía";
|
||||||
"battery.bank.banner.voltage" = "Se detectó un desajuste de voltaje";
|
"battery.bank.banner.voltage" = "Se detectó un desajuste de voltaje";
|
||||||
|
|||||||
@@ -175,6 +175,10 @@
|
|||||||
"battery.onboarding.title" = "Ajoutez votre première batterie";
|
"battery.onboarding.title" = "Ajoutez votre première batterie";
|
||||||
"battery.onboarding.subtitle" = "Suivez la capacité et la chimie de votre banc pour mieux maîtriser l'autonomie.";
|
"battery.onboarding.subtitle" = "Suivez la capacité et la chimie de votre banc pour mieux maîtriser l'autonomie.";
|
||||||
"battery.bank.badge.voltage" = "Tension";
|
"battery.bank.badge.voltage" = "Tension";
|
||||||
|
"overview.chargers.header.title" = "Vue d’ensemble des chargeurs";
|
||||||
|
"overview.chargers.empty.title" = "Aucun chargeur configuré pour l’instant";
|
||||||
|
"overview.chargers.empty.subtitle" = "Ajoutez des chargeurs secteur, DC-DC ou solaires pour comprendre votre capacité de charge.";
|
||||||
|
"overview.chargers.empty.create" = "Ajouter un chargeur";
|
||||||
"battery.bank.badge.capacity" = "Capacité";
|
"battery.bank.badge.capacity" = "Capacité";
|
||||||
"battery.bank.badge.energy" = "Énergie";
|
"battery.bank.badge.energy" = "Énergie";
|
||||||
"battery.bank.banner.voltage" = "Écart de tension détecté";
|
"battery.bank.banner.voltage" = "Écart de tension détecté";
|
||||||
|
|||||||
@@ -175,6 +175,10 @@
|
|||||||
"battery.onboarding.title" = "Voeg je eerste accu toe";
|
"battery.onboarding.title" = "Voeg je eerste accu toe";
|
||||||
"battery.onboarding.subtitle" = "Houd capaciteit en chemie van de accubank in de gaten om de gebruiksduur te beheersen.";
|
"battery.onboarding.subtitle" = "Houd capaciteit en chemie van de accubank in de gaten om de gebruiksduur te beheersen.";
|
||||||
"battery.bank.badge.voltage" = "Spanning";
|
"battery.bank.badge.voltage" = "Spanning";
|
||||||
|
"overview.chargers.header.title" = "Overzicht van laders";
|
||||||
|
"overview.chargers.empty.title" = "Nog geen laders geconfigureerd";
|
||||||
|
"overview.chargers.empty.subtitle" = "Voeg walstroom-, DC-DC- of zonneladers toe om je laadvermogen te begrijpen.";
|
||||||
|
"overview.chargers.empty.create" = "Lader toevoegen";
|
||||||
"battery.bank.badge.capacity" = "Capaciteit";
|
"battery.bank.badge.capacity" = "Capaciteit";
|
||||||
"battery.bank.badge.energy" = "Energie";
|
"battery.bank.badge.energy" = "Energie";
|
||||||
"battery.bank.banner.voltage" = "Spanningsafwijking gedetecteerd";
|
"battery.bank.banner.voltage" = "Spanningsafwijking gedetecteerd";
|
||||||
|
|||||||
Reference in New Issue
Block a user