Skip to content

VWEUDA: fix TypeError crash in update_result_from_payload() blocking all SoC updates - #3800

Merged
benderl merged 1 commit into
openWB:masterfrom
seaspotter:fix_vweuda
Aug 14, 2026
Merged

VWEUDA: fix TypeError crash in update_result_from_payload() blocking all SoC updates#3800
benderl merged 1 commit into
openWB:masterfrom
seaspotter:fix_vweuda

Conversation

@seaspotter

Copy link
Copy Markdown
Collaborator

Problem

Im vweuda-Modul (VW EU-Data-Act-Integration) kann update_result_from_payload()
in libeuda.py mit einem TypeError abstürzen, sobald der zuvor gecachte
Odometer-Wert None ist und ein neuer, gültiger Payload einen Odometer-Wert
als String liefert:

if _valid and result['odometer'] is not None:
    if vin in euda.result and result['odometer'] < euda.result[vin]['odometer']:
        ...

result['odometer'] ist ein str (z. B. "15255"), euda.result[vin]['odometer']
kann None sein (z. B. wenn der allererste erfolgreich verarbeitete Payload
selbst keinen Odometer-Wert enthielt). str < None wirft in Python 3 einen
TypeError.

Da diese Exception vor der Zeile euda.result[vin] = result auftritt, wird
das komplette Update verworfen – inklusive eines eigentlich gültigen, neueren
SOC-Werts und Zeitstempels. Da der gecachte Odometer-Wert dadurch nie auf
einen nicht-None-Wert aktualisiert wird, tritt der Fehler bei jedem
weiteren Zyklus erneut auf. Das Modul bleibt dauerhaft auf dem allerersten
erfolgreich geladenen Datenstand eingefroren – SOC, Reichweite, Kilometerstand
und Zeitstempel aktualisieren sich nie wieder, bis der Prozess neu gestartet
wird (was den Fehler nur temporär "verdeckt", siehe unten).

Sekundäreffekt: Rate-Limiting durch das VW-Portal

Da die Exception vor dem geplanten
await asyncio.sleep(CYCLE_INTERVAL) (600 s) auftritt und im äußeren
except Exception der Schleife landet, gibt es an dieser Stelle keine Pause.
Der Thread ruft die VW-API dadurch im Sekundentakt erneut auf, was zu
HTTP 429 (Too Many Requests) führt und den Datenabruf zusätzlich blockiert.

Belege aus einem produktiven SoC-Log

Reproduziert auf einer laufenden openWB-Installation (Fahrzeug: VW ID.3,
Modul vweuda). Auszug aus dem Log:

2026-08-13 22:18:13,209 - INFO:soc_bt_ev2 - latest result=
{
    "soc": "71.0",
    "range": "378",
    "soc_timestamp": 1786644301.0,
    "soc_timestamp_str": "2026-08-13T18:05:01Z",
    "odometer": "15255"
}
2026-08-13 22:18:13,218 - INFO:soc_bt_ev2 - cache result=
{
    "soc": "50.0",
    "range": null,
    "soc_timestamp": 1786459560.0,
    "soc_timestamp_str": "2026-08-11T14:46:00Z",
    "odometer": null
}
2026-08-13 22:18:13,225 - ERROR:soc_bt_ev2 - thread loop failed 0, exception='<' not supported between instances of 'str' and 'NoneType'
Traceback (most recent call last):
  File ".../vweuda/libeuda.py", line 920, in async_eudaThread
    self.update_result_from_payload(_data, _name)
  File ".../vweuda/libeuda.py", line 817, in update_result_from_payload
    if vin in euda.result and result['odometer'] < euda.result[vin]['odometer']:
TypeError: '<' not supported between instances of 'str' and 'NoneType'

Dieser Ablauf wiederholte sich im vorliegenden Log kontinuierlich zwischen
16:44 Uhr und 22:18 Uhr (>5,5 Stunden), jeweils im Minuten- bis
Sekundentakt gegen Ende. get_status() lieferte in diesem gesamten
Zeitraum konstant denselben, eingefrorenen Stand vom 2026-08-11, 14:46 Uhr:

get_status: soc=50.0, range=246, ts=1786459560.0,
            ts_str=2026-08-11T14:46:00Z, odometer=15040

Kurz danach im Log auch der Rate-Limit-Effekt:

thread APIError Could not download newest dataset:
Download 20260813201138_WVWxxxxx.zip -> HTTP 429

Parallel dazu wurden über die VW-Portal-Weboberfläche ("Mein Datenpaket")
mehrere ZIPs aus demselben Zeitraum heruntergeladen und manuell geprüft:
Die enthaltenen JSON-Payloads lieferten durchgehend einen validen,
aktuelleren SOC (71 %) und Odometer (15255 km) – die Daten kamen also
tatsächlich frisch vom Portal an, wurden aber durch den beschriebenen Bug
nie in euda.result übernommen.

Fix

update_result_from_payload() verwirft das Update nicht mehr pauschal,
wenn kein direkter Vergleich möglich ist:

  • Ist der neue Odometer-Wert None, wird der zuletzt bekannte Wert
    übernommen (statt das gesamte Update zu verwerfen).
  • Ist der gecachte Wert None, wird kein Vergleich versucht, der neue Wert
    wird direkt übernommen.
  • Der eigentliche Zahlenvergleich läuft über float(...), um String/Typ-
    Mismatches abzufangen; ein verbleibender TypeError/ValueError wird
    geloggt statt die Schleife abstürzen zu lassen.

Damit werden SOC und Zeitstempel in jedem Fall aktualisiert, sobald sie
selbst gültig und neuer sind – unabhängig davon, ob für den Odometer in
diesem Zyklus ein verwertbarer Wert vorliegt.

Refactor odometer handling to preserve last known value if no new reading is available.
@seaspotter
seaspotter requested a review from rleidner August 13, 2026 20:41
@rleidner

Copy link
Copy Markdown
Collaborator

Vielen Dank fürs Finden und Fixen. Sieht gut aus.

@rleidner rleidner left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Danke fürs Finden und Fixen. Sieht gut aus.

@benderl
benderl merged commit 4cfd004 into openWB:master Aug 14, 2026
1 check passed
@seaspotter
seaspotter deleted the fix_vweuda branch August 14, 2026 06:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants