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>
266 lines
8.3 KiB
YAML
266 lines
8.3 KiB
YAML
# VanAlign Solar - Votronic Solarladeregler über BLE (SIMULATION)
|
|
#
|
|
# Kopie von esp32_ble_solar.yaml für den Fall, dass gerade kein Votronic-
|
|
# Regler zum Anschliessen vorhanden ist. Die `platform: votronic`-Sensoren
|
|
# sowie UART/external_components wurden entfernt und durch Template-Sensoren
|
|
# ersetzt, die einen plausiblen Tagesverlauf simulieren: PV-Spannung/-Strom
|
|
# folgen einem "Sonnenstand" (ein gedachter, ca. 6 Minuten langer Tag), die
|
|
# Batteriespannung schwankt gemütlich mit, und die Statusflags/Bitmasken
|
|
# leiten sich aus diesen Werten ab. Die BLE-Charakteristiken funktionieren
|
|
# dadurch exakt wie im Original - nur eben ohne angeschlossene Hardware.
|
|
#
|
|
# Name und Friendly Name sind bewusst auf "-sim" abgeändert, damit dieses
|
|
# Gerät im BLE-Umfeld nicht mit einem echten VanAlign-Solar-Gerät kollidiert.
|
|
#
|
|
# Für Debug-Zwecke läuft WLAN mit und die Werte werden zusätzlich auf dem
|
|
# eingebauten Webserver (Port 80) angezeigt; WLAN-Zugangsdaten liegen in
|
|
# secrets.yaml (lokal anzulegen, ist per .gitignore ausgeschlossen). Alle
|
|
# simulierten Werte aktualisieren sich höchstens jede Sekunde, damit sich
|
|
# Änderungen beim Debuggen zügig zeigen.
|
|
#
|
|
# Sobald wieder ein echter Votronic-Regler verfügbar ist, einfach
|
|
# esp32_ble_solar.yaml weiterverwenden - diese Datei ist nur zum Testen der
|
|
# App/BLE-Anbindung.
|
|
|
|
esphome:
|
|
name: vanalign-solar-sim
|
|
friendly_name: "VanAlign Solar (Sim)"
|
|
|
|
esp32:
|
|
board: esp32-s3-devkitc-1
|
|
flash_size: 16MB
|
|
framework:
|
|
type: esp-idf
|
|
|
|
# N16R8: 16 MB Flash + 8 MB PSRAM, beim S3 als Octal-PSRAM angebunden.
|
|
psram:
|
|
mode: octal
|
|
speed: 80MHz
|
|
|
|
logger:
|
|
level: WARN
|
|
|
|
wifi:
|
|
ssid: !secret wifi_ssid
|
|
password: !secret wifi_password
|
|
|
|
web_server:
|
|
port: 80
|
|
|
|
sensor:
|
|
# Simulierter "Sonnenstand": 0 nachts, sanfter Buckel tagsüber, Periode
|
|
# ca. 6 Minuten - reicht zum Beobachten eines vollen Auf/Ab in der App.
|
|
- platform: template
|
|
name: "Sonnenstand (Sim)"
|
|
id: sun_level
|
|
internal: true
|
|
update_interval: 1s
|
|
lambda: |-
|
|
float t = millis() / 1000.0f;
|
|
return std::max(0.0f, sinf(t / 180.0f));
|
|
|
|
- platform: template
|
|
name: "Batteriespannung (Sim)"
|
|
id: battery_voltage
|
|
unit_of_measurement: "V"
|
|
accuracy_decimals: 2
|
|
update_interval: 1s
|
|
lambda: |-
|
|
float t = millis() / 1000.0f;
|
|
return 12.6f + 0.8f * id(sun_level).state + 0.05f * sin(t / 20.0f);
|
|
|
|
- platform: template
|
|
name: "PV Spannung (Sim)"
|
|
id: pv_voltage
|
|
unit_of_measurement: "V"
|
|
accuracy_decimals: 2
|
|
update_interval: 1s
|
|
lambda: |-
|
|
float t = millis() / 1000.0f;
|
|
return id(sun_level).state > 0.01f ? (19.0f + 1.0f * sin(t / 25.0f)) : 0.0f;
|
|
|
|
- platform: template
|
|
name: "PV Strom (Sim)"
|
|
id: pv_current
|
|
unit_of_measurement: "A"
|
|
accuracy_decimals: 2
|
|
update_interval: 1s
|
|
lambda: |-
|
|
float t = millis() / 1000.0f;
|
|
return id(sun_level).state * (6.0f + 1.5f * sin(t / 17.0f));
|
|
|
|
- platform: template
|
|
name: "PV Leistung (Sim)"
|
|
id: pv_power
|
|
unit_of_measurement: "W"
|
|
accuracy_decimals: 1
|
|
update_interval: 1s
|
|
lambda: |-
|
|
return id(pv_voltage).state * id(pv_current).state;
|
|
|
|
- platform: template
|
|
name: "Reglertemperatur (Sim)"
|
|
id: pv_temperature
|
|
unit_of_measurement: "°C"
|
|
accuracy_decimals: 1
|
|
update_interval: 1s
|
|
lambda: |-
|
|
float t = millis() / 1000.0f;
|
|
return 22.0f + 10.0f * id(sun_level).state + 1.0f * sin(t / 11.0f);
|
|
|
|
- platform: template
|
|
name: "PV Modus-ID (Sim)"
|
|
id: pv_mode_id
|
|
update_interval: 1s
|
|
lambda: |-
|
|
return 3.0f; // fester simulierter Modus (MPPT)
|
|
|
|
- platform: template
|
|
name: "PV Batteriestatus (Bitmaske, Sim)"
|
|
id: pv_battery_status_bitmask
|
|
update_interval: 1s
|
|
lambda: |-
|
|
return id(sun_level).state > 0.01f ? 1.0f : 0.0f;
|
|
|
|
- platform: template
|
|
name: "PV Reglerstatus (Bitmaske, Sim)"
|
|
id: pv_controller_status_bitmask
|
|
update_interval: 1s
|
|
lambda: |-
|
|
return id(sun_level).state > 0.01f ? 2.0f : 0.0f;
|
|
|
|
binary_sensor:
|
|
- platform: template
|
|
name: "Batterie lädt (Sim)"
|
|
id: battery_charging
|
|
lambda: |-
|
|
return id(pv_current).state > 0.2f;
|
|
|
|
- platform: template
|
|
name: "Batterie entlädt (Sim)"
|
|
id: battery_discharging
|
|
lambda: |-
|
|
return id(pv_current).state <= 0.2f;
|
|
|
|
- platform: template
|
|
name: "PV Regler aktiv (Sim)"
|
|
id: pv_active
|
|
lambda: |-
|
|
return id(sun_level).state > 0.01f;
|
|
|
|
- platform: template
|
|
name: "PV Strombegrenzung (Sim)"
|
|
id: pv_current_reduction
|
|
lambda: |-
|
|
float t = millis() / 1000.0f;
|
|
return id(pv_active).state && sin(t / 60.0f) > 0.9f;
|
|
|
|
- platform: template
|
|
name: "PV AES aktiv (Sim)"
|
|
id: pv_aes_active
|
|
lambda: |-
|
|
return id(battery_voltage).state > 13.3f;
|
|
|
|
text_sensor:
|
|
- platform: template
|
|
name: "Firmware Version"
|
|
id: firmware_version
|
|
icon: mdi:tag
|
|
lambda: |-
|
|
return {"v1.0.0-sim"};
|
|
|
|
esp32_ble_server:
|
|
services:
|
|
- uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a001
|
|
advertise: true
|
|
characteristics:
|
|
- id: battery_voltage_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a101
|
|
description: "Batteriespannung"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(sizeof(float));
|
|
float val = id(battery_voltage).state;
|
|
memcpy(v.data(), &val, sizeof(float));
|
|
return v;
|
|
- id: pv_voltage_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a102
|
|
description: "PV Spannung"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(sizeof(float));
|
|
float val = id(pv_voltage).state;
|
|
memcpy(v.data(), &val, sizeof(float));
|
|
return v;
|
|
- id: pv_current_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a103
|
|
description: "PV Strom"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(sizeof(float));
|
|
float val = id(pv_current).state;
|
|
memcpy(v.data(), &val, sizeof(float));
|
|
return v;
|
|
- id: pv_power_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a104
|
|
description: "PV Leistung"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(sizeof(float));
|
|
float val = id(pv_power).state;
|
|
memcpy(v.data(), &val, sizeof(float));
|
|
return v;
|
|
- id: pv_temperature_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a105
|
|
description: "Reglertemperatur"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(sizeof(float));
|
|
float val = id(pv_temperature).state;
|
|
memcpy(v.data(), &val, sizeof(float));
|
|
return v;
|
|
# Bit0 Batterie lädt, Bit1 Batterie entlädt, Bit2 PV-Regler aktiv,
|
|
# Bit3 PV-Strombegrenzung, Bit4 AES aktiv.
|
|
- id: status_flags_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a106
|
|
description: "Statusflags"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(1, 0);
|
|
uint8_t flags = 0;
|
|
if (id(battery_charging).state) flags |= (1 << 0);
|
|
if (id(battery_discharging).state) flags |= (1 << 1);
|
|
if (id(pv_active).state) flags |= (1 << 2);
|
|
if (id(pv_current_reduction).state) flags |= (1 << 3);
|
|
if (id(pv_aes_active).state) flags |= (1 << 4);
|
|
v[0] = flags;
|
|
return v;
|
|
- id: pv_mode_id_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a107
|
|
description: "PV Modus-ID"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(1, 0);
|
|
v[0] = (uint8_t) id(pv_mode_id).state;
|
|
return v;
|
|
- id: pv_battery_status_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a108
|
|
description: "PV Batteriestatus (Bitmaske)"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(1, 0);
|
|
v[0] = (uint8_t) id(pv_battery_status_bitmask).state;
|
|
return v;
|
|
- id: pv_controller_status_ble
|
|
uuid: 05c9a349-2b8e-4b1d-9c9d-c247e9a6a109
|
|
description: "PV Reglerstatus (Bitmaske)"
|
|
read: true
|
|
value: !lambda |-
|
|
std::vector<unsigned char> v(1, 0);
|
|
v[0] = (uint8_t) id(pv_controller_status_bitmask).state;
|
|
return v;
|
|
|
|
button:
|
|
- platform: restart
|
|
name: "ESP Restart"
|