Skip to content

[BUG]: EMS optimization fails after restart - multiple root causes #1006

Description

@lilabov

Describe the issue:

EOS Bug Report – 2026-04-12

EOS Version: 0.3.0.dev2603180781250771
Umgebung: Ubuntu 24, LXC Container, Node-RED als Datenlieferant
Datum: 12. April 2026


Zusammenfassung

Nach einem Neustart des EOS-Services schlägt die Optimierung dauerhaft fehl:

Energy management run canceled. Could not prepare optimisation parameters.

Die Ursache ist ein Zusammenspiel aus 5 Bugs, die einzeln lösbar wären aber zusammen dazu führen dass EOS nach einem Neustart nicht mehr optimiert.


Bug 1: LoadImport ignoriert load0_mean Key stillschweigend

Beschreibung

LoadImport mit key_prefix="load" mappt nur Keys die exakt loadforecast_power_w heißen. Der in der Dokumentation und in älteren Beispielen verwendete Key load0_mean wird stillschweigend ignoriert ohne Fehlermeldung. loadforecast_power_w bleibt leer, EOS kann nicht optimieren.

Code

# loadimport.py
def _update_data(self, force_enable=False):
    if self.config.load.loadimport.import_file_path:
        self.import_from_file(..., key_prefix="load")
        # ← "load0_mean" wird NICHT zu "loadforecast_power_w" gemappt!

Reproduktion

// lastprofil.json mit altem Key-Format
{
    "load0_mean": [789.0, 735.0, 715.0, ...]
}
curl -X POST http://localhost:8503/v1/prediction/update/LoadImport
curl http://localhost:8503/v1/prediction/series?key=loadforecast_power_w
# → 0 Datenpunkte, keine Fehlermeldung

Workaround

// lastprofil.json mit korrektem Key
{
    "loadforecast_power_w": [789.0, 735.0, 715.0, ...]
}

Erwartetes Verhalten

Entweder load0_mean als validen Alias akzeptieren, oder eine klare Fehlermeldung ausgeben wenn der Key nicht gemappt werden kann. Die Dokumentation sollte den korrekten Key loadforecast_power_w klar benennen.


Bug 2: Duplicate Timestamp ValueError bei Messwerten (HTTP 500)

Beschreibung

Node-RED sendet Timestamps in lokaler Zeit (+02:00) aber EOS speichert intern in UTC. Bei jedem PUT für growatt_ark-power-l1-w mit lokalem Timestamp wird ein HTTP 500 zurückgegeben.

Fehlermeldung

ValueError: Duplicate timestamp 2026-04-12 14:00:57+02:00 -> 20260412T120057Z
HTTP 500 Internal Server Error

Reproduktion

# Lokalen Timestamp senden (MESZ = UTC+2)
curl -X PUT "http://localhost:8503/v1/measurement/value?\
key=growatt_ark-power-l1-w&value=1000&datetime=2026-04-12T14:00:00+02:00"
# → 200 OK

Gleichen Zeitpunkt nochmal senden (selbe Sekunde)

curl -X PUT "http://localhost:8503/v1/measurement/value?\
key=growatt_ark-power-l1-w&value=1100&datetime=2026-04-12T14:00:00+02:00"

→ 500 Internal Server Error: Duplicate timestamp

Erwartetes Verhalten

EOS sollte Timestamps vor dem Duplikat-Check normalisieren (zu UTC). Bei einem echten Duplikat sollte HTTP 409 zurückgegeben werden statt 500. Ein 500er bei einem bekannten Fehlerfall ist kein akzeptables Verhalten.


Bug 3: EV SOC nicht gefunden obwohl Datenpunkte in der DB vorhanden

Beschreibung

EOS empfängt EV-SOC Werte (HTTP 200 OK) und speichert sie in der Datenbank. Beim Optimierungszyklus findet geneticparams.prepare() die Werte jedoch nicht und setzt den SOC auf 0. Der except: Block loggt die eigentliche Exception nicht, weshalb die Ursache verborgen bleibt.

Fehlermeldung

No electric vehicle device SoC data (measurement key = 'tumbler_my-soc-factor') available - defaulting to 0.
Can not prepare optimization parameters - will retry. Parameter preparation attempt 1..10
Energy management run canceled. Could not prepare optimisation parameters.

Code

# geneticparams.py Zeile ~502-520
try:
    initial_soc_factor = cls.measurement.key_to_value(
        key=electric_vehicle_config.measurement_key_soc_factor,
        target_datetime=ems.start_datetime,
        time_window=to_duration("48 hours"),
    )
    ...
except:  # ← fängt ALLE Exceptions, loggt NICHTS!
    initial_soc_percentage = None

if initial_soc_percentage is None:
logger.info(f"No electric vehicle device SoC data ... defaulting to 0.")
initial_soc_percentage = 0

← EOS setzt SOC=0 aber bricht NICHT hier ab!

Warum bricht EOS ab?

Der Abbruch passiert nicht wegen initial_soc_percentage = 0 sondern weil eine andere Exception im gleichen try-Block beim Zusammenbauen der oparams geworfen wird (vermutlich durch fehlende Lastdaten – Bug 1). Da aber except: alles fängt und nichts loggt, erscheint nur die irreführende SOC-Meldung.

Erwartetes Verhalten

  1. except: durch except Exception as e: logger.error(...) ersetzen
  2. defaulting to 0 sollte kein Abbruchgrund sein – EOS sollte mit SOC=0 weiteroptimieren
  3. Die eigentliche Ursache des Abbruchs sollte klar geloggt werden

Bug 4: import_json überschreibt import_file_path ohne Warnung

Beschreibung

Wenn sowohl import_file_path als auch import_json in der Config gesetzt sind, werden beide ausgeführt wobei import_json die Daten von import_file_path überschreibt. Nach einem Neustart bleibt import_json mit alten Daten in der Config erhalten (aus vorherigen API-Calls) und überschreibt den frischen Datei-Import.

Code

# loadimport.py
def _update_data(self, force_enable=False):
    if self.config.load.loadimport.import_file_path:
        self.import_from_file(...)       # wird zuerst ausgeführt
    if self.config.load.loadimport.import_json:
        self.import_from_json(...)       # überschreibt Datei-Import!

Reproduktion

# 1. import_file_path setzen via API
curl -X PUT http://localhost:8503/v1/config \
  -d '{"load": {"loadimport": {"import_file_path": "/path/to/file.json"}}}'

2. EOS neu starten

systemctl restart eos

3. Config prüfen - import_json ist noch vorhanden mit alten Daten!

curl http://localhost:8503/v1/config | jq .load.loadimport

→ import_json: "{...alte Daten...}" ← überschreibt import_file_path!

Erwartetes Verhalten

import_file_path und import_json sollten mutex sein. Wenn beide gesetzt sind:

  • Eine Warnung ausgeben
  • Klar definieren welche Quelle Vorrang hat (z.B. import_json hat Vorrang)
  • Oder nur eine der beiden Optionen zulassen

Bug 5: PUT /v1/config Änderungen überleben Neustart nicht vollständig

Beschreibung

Änderungen via PUT /v1/config werden in der laufenden Instanz übernommen aber nach einem Neustart lädt EOS wieder Teile der alten Config. Insbesondere loadimport.import_file_path und optimization.genetic Parameter gehen verloren.

Reproduktion

# Optimization Parameter ändern
curl -X PUT http://localhost:8503/v1/config \
  -d '{"optimization": {"genetic": {"individuals": 150, "generations": 200}}}'

Prüfen - Änderung ist aktiv

curl http://localhost:8503/v1/config | jq .optimization.genetic

→ individuals: 150 ✅

EOS neu starten

systemctl restart eos

Nach Neustart - alte Werte wieder!

curl http://localhost:8503/v1/config | jq .optimization.genetic

→ individuals: 300 ❌

Erwartetes Verhalten

PUT /v1/config sollte Änderungen persistent in EOS.config.json speichern, oder die API-Dokumentation sollte klar darauf hinweisen dass Änderungen nicht persistent sind und stattdessen die Config-Datei direkt bearbeitet werden muss.


Empfohlene Fixes (Priorität)

Priorität Bug Aufwand
🔴 Kritisch Bug 3: Bare except: ohne Logging Klein – Exception loggen
🔴 Kritisch Bug 1: load0_mean wird ignoriert Klein – Alias oder Fehlermeldung
🟠 Hoch Bug 2: HTTP 500 bei Duplicate Timestamp Mittel – Normalisierung
🟠 Hoch Bug 5: API-Config nicht persistent Mittel – Config-Datei schreiben
🟡 Mittel Bug 4: import_json vs import_file_path Klein – Mutex oder Warnung

Reproduceable code example:

See above

Error message:

See above

Version information:

EOS Version: 0.3.0.dev2603180781250771

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions