299 lines
8.9 KiB
Markdown
299 lines
8.9 KiB
Markdown
# Fix: SOC springt auf 65535% beim Modus-Wechsel
|
|
|
|
## Problem
|
|
Wenn der ESS-Modus von INTERNAL auf REMOTE wechselt, zeigt `sensor.esssoc` kurzzeitig 65535% (0xFFFF = ungültiger Wert).
|
|
|
|
Die Automation "Batterie Optimierung: Stopp bei Max-SOC" triggert bei SOC > 99% und beendet das Laden sofort wieder.
|
|
|
|
## Lösung 1: Debounce + Plausibilitäts-Check (EMPFOHLEN)
|
|
|
|
Ersetze die Automation in `battery_optimizer_automations.yaml`:
|
|
|
|
```yaml
|
|
# Automatisierung 8: Laden stoppen wenn SOC erreicht (FIXED)
|
|
alias: "Batterie Optimierung: Stopp bei Max-SOC"
|
|
description: "Beendet manuelles Laden wenn maximaler SOC erreicht (mit Spike-Protection)"
|
|
trigger:
|
|
- platform: numeric_state
|
|
entity_id: sensor.esssoc
|
|
above: 99
|
|
condition:
|
|
# 1. Manual Control muss aktiv sein
|
|
- condition: state
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
state: "on"
|
|
# 2. SOC muss plausibel sein (nicht über 105%)
|
|
- condition: numeric_state
|
|
entity_id: sensor.esssoc
|
|
below: 105
|
|
# 3. SOC muss für mindestens 30 Sekunden über 99% sein (Debounce)
|
|
- condition: template
|
|
value_template: >
|
|
{% set soc = states('sensor.esssoc') | float(0) %}
|
|
{% set last_changed = as_timestamp(states.sensor.esssoc.last_changed) %}
|
|
{% set now = as_timestamp(now()) %}
|
|
{% set duration = now - last_changed %}
|
|
{{ soc > 99 and soc < 105 and duration > 30 }}
|
|
action:
|
|
- service: input_boolean.turn_off
|
|
target:
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
# ESS-Modus wird durch manuelle_speicherbeladung_deaktivieren gesetzt
|
|
- service: notify.persistent_notification
|
|
data:
|
|
title: "Batterie-Optimierung"
|
|
message: "Manuelles Laden beendet - SOC {{ states('sensor.esssoc') }}% erreicht"
|
|
mode: single
|
|
```
|
|
|
|
**Was das macht:**
|
|
1. **Plausibilitäts-Check**: SOC muss zwischen 99% und 105% liegen
|
|
2. **Debounce**: SOC muss für mindestens 30 Sekunden über 99% sein
|
|
3. **Spike-Protection**: 65535% wird ignoriert (liegt über 105%)
|
|
|
|
## Lösung 2: Nur Debounce (Einfacher)
|
|
|
|
```yaml
|
|
alias: "Batterie Optimierung: Stopp bei Max-SOC"
|
|
description: "Beendet manuelles Laden wenn maximaler SOC erreicht"
|
|
trigger:
|
|
- platform: numeric_state
|
|
entity_id: sensor.esssoc
|
|
above: 99
|
|
for:
|
|
seconds: 30 # Warte 30 Sekunden bevor getriggert wird
|
|
condition:
|
|
- condition: state
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
state: "on"
|
|
# Plausibilitäts-Check
|
|
- condition: numeric_state
|
|
entity_id: sensor.esssoc
|
|
below: 105
|
|
action:
|
|
- service: input_boolean.turn_off
|
|
target:
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
- service: notify.persistent_notification
|
|
data:
|
|
title: "Batterie-Optimierung"
|
|
message: "Manuelles Laden beendet - SOC {{ states('sensor.esssoc') }}% erreicht"
|
|
mode: single
|
|
```
|
|
|
|
**Vorteil**: Einfacher, verwendet Home Assistant's eingebaute `for:` Funktion
|
|
|
|
## Lösung 3: Initial Delay nach Manual Control Aktivierung
|
|
|
|
Ignoriere SOC-Änderungen in den ersten 60 Sekunden nach Aktivierung:
|
|
|
|
```yaml
|
|
alias: "Batterie Optimierung: Stopp bei Max-SOC"
|
|
description: "Beendet manuelles Laden wenn maximaler SOC erreicht"
|
|
trigger:
|
|
- platform: numeric_state
|
|
entity_id: sensor.esssoc
|
|
above: 99
|
|
condition:
|
|
- condition: state
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
state: "on"
|
|
# Plausibilitäts-Check
|
|
- condition: numeric_state
|
|
entity_id: sensor.esssoc
|
|
below: 105
|
|
# Manual Control muss mindestens 60 Sekunden aktiv sein
|
|
- condition: template
|
|
value_template: >
|
|
{% set last_changed = as_timestamp(states.input_boolean.goodwe_manual_control.last_changed) %}
|
|
{% set now = as_timestamp(now()) %}
|
|
{% set duration = now - last_changed %}
|
|
{{ duration > 60 }}
|
|
action:
|
|
- service: input_boolean.turn_off
|
|
target:
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
- service: notify.persistent_notification
|
|
data:
|
|
title: "Batterie-Optimierung"
|
|
message: "Manuelles Laden beendet - SOC {{ states('sensor.esssoc') }}% erreicht"
|
|
mode: single
|
|
```
|
|
|
|
**Vorteil**: Ignoriert alle Spikes in der ersten Minute nach Modus-Wechsel
|
|
|
|
## Empfehlung: Kombination (Lösung 4)
|
|
|
|
Die robusteste Lösung kombiniert alle Ansätze:
|
|
|
|
```yaml
|
|
alias: "Batterie Optimierung: Stopp bei Max-SOC"
|
|
description: "Beendet manuelles Laden wenn maximaler SOC erreicht (robust gegen Spikes)"
|
|
trigger:
|
|
- platform: numeric_state
|
|
entity_id: sensor.esssoc
|
|
above: 99
|
|
for:
|
|
seconds: 30 # Debounce: 30 Sekunden warten
|
|
condition:
|
|
# 1. Manual Control aktiv
|
|
- condition: state
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
state: "on"
|
|
|
|
# 2. Plausibilitäts-Check: SOC zwischen 99% und 105%
|
|
- condition: numeric_state
|
|
entity_id: sensor.esssoc
|
|
above: 99
|
|
- condition: numeric_state
|
|
entity_id: sensor.esssoc
|
|
below: 105
|
|
|
|
# 3. Manual Control muss mindestens 60 Sekunden aktiv sein
|
|
- condition: template
|
|
value_template: >
|
|
{% set last_changed = as_timestamp(states.input_boolean.goodwe_manual_control.last_changed) %}
|
|
{% set now = as_timestamp(now()) %}
|
|
{% set duration = now - last_changed %}
|
|
{{ duration > 60 }}
|
|
|
|
# 4. Sensor muss verfügbar sein
|
|
- condition: template
|
|
value_template: >
|
|
{{ states('sensor.esssoc') not in ['unavailable', 'unknown', 'none'] }}
|
|
|
|
action:
|
|
- service: input_boolean.turn_off
|
|
target:
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
|
|
- service: notify.persistent_notification
|
|
data:
|
|
title: "Batterie-Optimierung"
|
|
message: "Manuelles Laden beendet - SOC {{ states('sensor.esssoc') }}% erreicht"
|
|
|
|
# Logging für Debugging
|
|
- service: system_log.write
|
|
data:
|
|
message: "Battery charging stopped at SOC {{ states('sensor.esssoc') }}%"
|
|
level: info
|
|
|
|
mode: single
|
|
```
|
|
|
|
**Schutz-Mechanismen:**
|
|
1. ✅ **30 Sekunden Debounce**: Warte 30s nachdem SOC > 99%
|
|
2. ✅ **Plausibilitäts-Check**: SOC muss < 105% sein
|
|
3. ✅ **Initial Delay**: Manual Control muss mindestens 60s aktiv sein
|
|
4. ✅ **Availability Check**: Sensor muss verfügbar sein
|
|
|
|
## Installation
|
|
|
|
**Option A: Via Home Assistant UI** (Empfohlen für schnellen Test)
|
|
1. Gehe zu Einstellungen → Automationen & Szenen
|
|
2. Suche "Batterie Optimierung: Stopp bei Max-SOC"
|
|
3. Bearbeite die Automation
|
|
4. Ersetze den Inhalt mit einer der obigen Lösungen
|
|
5. Speichern
|
|
|
|
**Option B: Via YAML**
|
|
1. Öffne deine `automations.yaml` oder die entsprechende Datei
|
|
2. Finde die Automation (ID oder Alias)
|
|
3. Ersetze sie mit der neuen Version
|
|
4. Home Assistant neu laden oder Automationen neu laden
|
|
|
|
## Testing
|
|
|
|
### Test 1: Manuelles Laden ohne Spike-Problem
|
|
```yaml
|
|
# Developer Tools → Services
|
|
service: input_boolean.turn_on
|
|
target:
|
|
entity_id: input_boolean.goodwe_manual_control
|
|
```
|
|
|
|
Warte 2 Minuten und prüfe:
|
|
- Bleibt Manual Control aktiv?
|
|
- Gibt es SOC-Spikes in den Logs?
|
|
|
|
### Test 2: Simulation eines Spikes
|
|
```yaml
|
|
# Developer Tools → States
|
|
# Suche sensor.esssoc und ändere temporär den Wert auf 65535
|
|
# (nur möglich wenn Sensor-Typ es erlaubt)
|
|
```
|
|
|
|
### Test 3: Echtes Stoppen bei 100%
|
|
Warte bis Batterie wirklich bei 100% ist und prüfe ob das Laden dann korrekt gestoppt wird.
|
|
|
|
## Alternative: Sensor-Filter
|
|
|
|
Wenn das Problem häufiger auftritt, kannst du auch einen gefilterten Sensor erstellen:
|
|
|
|
```yaml
|
|
# In configuration.yaml
|
|
sensor:
|
|
- platform: filter
|
|
name: "ESS SOC Filtered"
|
|
entity_id: sensor.esssoc
|
|
filters:
|
|
# Entferne ungültige Werte
|
|
- filter: outlier
|
|
window_size: 4
|
|
radius: 10.0
|
|
# Entferne extreme Spikes
|
|
- filter: range
|
|
lower_bound: 0
|
|
upper_bound: 100
|
|
# Glättung
|
|
- filter: lowpass
|
|
time_constant: 10
|
|
```
|
|
|
|
Dann verwende `sensor.ess_soc_filtered` in allen Automationen statt `sensor.esssoc`.
|
|
|
|
## Monitoring
|
|
|
|
Füge eine Notification hinzu wenn ungültige Werte erkannt werden:
|
|
|
|
```yaml
|
|
alias: "Debug: SOC Spike Detector"
|
|
description: "Warnt bei ungültigen SOC-Werten"
|
|
trigger:
|
|
- platform: numeric_state
|
|
entity_id: sensor.esssoc
|
|
above: 105
|
|
action:
|
|
- service: notify.persistent_notification
|
|
data:
|
|
title: "SOC Spike erkannt!"
|
|
message: "SOC zeigt {{ states('sensor.esssoc') }}% - wahrscheinlich ungültiger Wert"
|
|
- service: system_log.write
|
|
data:
|
|
message: "SOC spike detected: {{ states('sensor.esssoc') }}% at {{ now() }}"
|
|
level: warning
|
|
mode: queued
|
|
```
|
|
|
|
## Nächste Schritte
|
|
|
|
1. **JETZT**: Implementiere Lösung 4 (kombinierter Ansatz)
|
|
2. **TESTE**: Aktiviere heute Abend manuelles Laden und beobachte
|
|
3. **MONITOR**: Installiere die Spike Detector Automation
|
|
4. **LANGFRISTIG**: Erwäge einen gefilterten Sensor für mehr Robustheit
|
|
|
|
## Zusätzliche Absicherungen
|
|
|
|
Füge auch in der `execute_charging_schedule` Funktion einen Check ein:
|
|
|
|
```python
|
|
# In battery_charging_optimizer.py, Zeile 67
|
|
current_soc = float(state.get('sensor.esssoc') or 50)
|
|
|
|
# Plausibilitäts-Check hinzufügen:
|
|
if current_soc > 105 or current_soc < 0:
|
|
log.warning(f"⚠ Ungültiger SOC-Wert erkannt: {current_soc}%. Verwende letzten gültigen Wert.")
|
|
# Verwende einen Fallback-Wert oder den letzten gültigen Wert
|
|
current_soc = 50 # Oder aus einem gespeicherten State laden
|
|
```
|