Files
VanAligneiOS/Shared/Models/SolarState.swift
T
fototeddyandClaude Sonnet 5 6d7b32d622 Add solar charge controller integration and firmware
App:
- New DeviceRole.solar with its own BLE session/protocol/state
  (SolarSession, VanAlignSolarProtocol, SolarState), mirroring the
  leveling sensor but deliberately not wired into the Live Activity.
- BluetoothManager now keeps an in-memory history per metric (voltage,
  current, power) instead of a single primary-metric series; cleared on
  app restart by design, not persisted to disk.
- DeviceDetailView shows a channel picker above the history chart when a
  device has more than one chartable metric.
- AddDeviceView recognizes the solar service UUID during setup.
- DemoData gets a simulated solar device so the integration can be
  checked in the simulator without hardware.

Firmware:
- Add esp32_ble_solar.yaml (Votronic solar charge controller over BLE)
  and simulated variants (esp32_ble_sim.yaml, esp32_ble_solar_sim.yaml)
  for testing without a vehicle.
- esp32_ble.yaml: set flash_size/psram for the ESP32-S3 N16R8 module.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 00:04:20 +02:00

49 lines
1.8 KiB
Swift

import Foundation
/// Zustand des Solarladereglers.
struct SolarState: Equatable, Codable, Sendable {
var batteryVoltage: Double?
var pvVoltage: Double?
var pvCurrent: Double?
var pvPower: Double?
var controllerTemperature: Double?
var isBatteryCharging: Bool?
var isBatteryDischarging: Bool?
var isControllerActive: Bool?
var isCurrentLimited: Bool?
var hasReading: Bool {
batteryVoltage != nil || pvVoltage != nil || pvCurrent != nil || pvPower != nil
}
/// Kurzer Klartext, wie bei den übrigen Geräten als "Zustand" angezeigt.
var stateText: String? {
guard isControllerActive != nil else { return nil }
if isBatteryCharging == true { return "Lädt" }
if isControllerActive == true { return "Aktiv" }
return "Inaktiv"
}
func snapshot(deviceID: UUID, rssi: Int?) -> DeviceSnapshot {
var snapshot = DeviceSnapshot(deviceID: deviceID, timestamp: Date(), rssi: rssi)
snapshot.metrics = [
Metric("pv_power", "Solarleistung", pvPower, unit: "W", precision: 0, primary: true),
Metric("pv_voltage", "PV-Spannung", pvVoltage, unit: "V", precision: 1),
Metric("pv_current", "PV-Strom", pvCurrent, unit: "A", precision: 1),
Metric("battery_voltage", "Batteriespannung", batteryVoltage, unit: "V", precision: 2),
]
if let controllerTemperature {
snapshot.metrics.append(
Metric("controller_temperature", "Reglertemperatur", controllerTemperature,
unit: "°C", precision: 0)
)
}
snapshot.state = stateText
if isCurrentLimited == true {
snapshot.offReasons = ["PV-Strombegrenzung aktiv"]
}
return snapshot
}
}