Skip to content

Fix Tesla/Powerwall data handling and improve robustness of control loop - #3232

Merged
LKuemmel merged 16 commits into
openWB:masterfrom
NicoP-codeing:master
Sep 28, 2026
Merged

LKuemmel merged 16 commits into
openWB:masterfrom
NicoP-codeing:master

Conversation

@NicoP-codeing

Copy link
Copy Markdown
Contributor

see also OpenWB Forum entry: https://forum.openwb.de/viewtopic.php?t=11634

Problem
Tesla/Powerwall sometimes provides unusable or inconsistent per-phase energy values.
/api/status is not critical for the control loop, but currently can still affect it.
Missing current values from Tesla/Neurio result in incomplete data, causing load management to fail.
Critical HTTP errors currently do not stop the Tesla cycle, which can lead to cascading issues.

Changes
Restored aggregate-based import/export counters (site-level data)
Per-phase energy is now used for diagnostic purposes only
Reduced /api/status usage and marked it as non-critical
Added fallback calculation for currents and power factor from P/Q/U
Implemented fail-fast behavior for critical requests
Added additional diagnostics and session logging

Motivation / Background
The main goal of this change is to restore reliable load management by using per-phase values for current-related calculations and diagnostics while keeping consistent aggregate site import/export counters.

Additional improvements were made to increase robustness and observability of the Tesla/Powerwall integration, especially in cases of incomplete data or intermittent API issues.

Testing
Tested with a real Tesla Powerwall setup
Verified that load management works reliably again
Checked that import/export counters are consistent and plausible
Simulated missing current values → fallback calculation works
Simulated API timeouts → control loop continues without cascading failures

…d gateway diagnostics

- add structured HTTP error handling in PowerwallHttpClient
  - distinguish transport, HTTP and JSON parsing failures
  - support fail_fast=False for non-critical requests
  - track cycle_failed and cookie_renewed state across one polling cycle

- add fail-fast handling in device.py
  - stop remaining Tesla component updates once a critical Powerwall request fails
  - log native TCP connection state diagnostics via /proc/net/tcp(/tcp6)
  - emit periodic session / connection health logs
  - keep cookie reuse, but mark startup + reauth cycles explicitly

- reduce /api/status traffic in counter.py
  - fetch firmware only after startup or after a new auth cookie was negotiated
  - treat /api/status as non-critical so it cannot fail the whole cycle

- keep import/export counters based on aggregate["site"]["energy_imported/exported"]
  - do not use summed per-phase energy counters as meter source
  - keep /api/meters/site for phase details only
  - add derived fallback currents and signed power factors from P/Q/U when Tesla/Neurio currents are 0
  - expose additional diagnostics such as frequency / serial number
  - add throttled debug logging to compare aggregate counters with per-phase sums
  - restore aggregate-based site counters so load management works correctly again

@LKuemmel LKuemmel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Leider bläht dein PR den Code ganz schon auf. Von 154 Zeilen für die drei Python-Dateien auf 565.
Einige Punkte sind zentral gelöst, andere sind für den Entwickler des Moduls interressant, müssen aber nicht dauerhaft im Code enthalten sein.
Wichtig ist, dass der Code wartbar und verständlich bleibt. Gibt es konkrete Probleme mit der Powerwall, die du mit deinem PR lösen willst?

Comment on lines +167 to +176
# If all API currents are 0 -> use calculated currents
if all(self._nearly_zero(i) for i in api_currents):
currents = calculated_currents
log.debug(
"Tesla/Neurio phase currents missing (all 0). Calculated currents from P/Q and U."
)
else:
currents = api_currents
# PF still useful even if currents exist
log.debug("Using phase currents from Tesla/Neurio API.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wenn es Modelle gibt, die keine Ströme liefern, dann dürfen diese im CounterState nicht gesetzt werden. Dann werden sie automatisch gesetzt.

def _nearly_zero(x: float, eps: float = 1e-9) -> bool:
return abs(x) < eps

def _calc_currents_and_pf_from_pqu(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Diese Berechnung erfolgt zentral im CounterState, wenn keine Ströme gesetzt sind, aber Phasenspannungen und Phasenleistungen vorhanden sind. Die Berechnung soll nicht in jedem Modul erfolgen.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Im CounterState sind jedoch nur die Wirkleistungen vorhanden. Reale Ströme können dann nicht berechnet werden. Das sollte in einem anderen PR grundlegend überarbeitet werden.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Danke für den Hinweis.

Dann würde ich für diesen PR die lokale Stromberechnung im Tesla-Counter beibehalten. In meinem Setup liefert Tesla/Neurio für die Phase currents nur 0-Werte, und ohne diese Ableitung funktioniert das Lastmanagement nicht zuverlässig.

Den generischen Ansatz zur Berechnung realer currents im CounterState würde ich nicht in diesem PR lösen. Das wäre aus meiner Sicht ein separates Thema, wie von dir vorgeschlagen.

Ich reduziere den PR daher weiter auf:

reduzierte /api/status-Nutzung
schlankes fail-fast
Tesla-spezifische Stromableitung nur dort, wo die API keine brauchbaren currents liefert

Ist das in eurem Interesse?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ich habe das bei mir umgesetzt und teste die Änderungen über Ostern. Anschließend würde ich den PR aktualisieren...

self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))

@staticmethod
def _safe_float(val, default: float = 0.0) -> float:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Default Werte werden zentral im CounterState gesetzt, damit sie konsistent sind.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bitte die Methode entfernen. Wenn Werte in einem Zyklus fehlen, wird eine Exception geworfen.
Es wird inkonsistent und der Code in jedem Modul sehr umfangreich, wenn überall default-Werte gesetzt werden.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Erledigt. _safe_float() und die lokalen Default-Werte habe ich entfernt. Fehlende bzw. nicht konvertierbare Werte laufen jetzt über den bestehenden Exception-/Fallback-Pfad.

Zusätzlich wird HTTPError im Counter nicht mehr abgefangen, damit HTTP-/Kommunikationsfehler durch das zentrale Error-Handling verarbeitet werden.

Comment thread packages/modules/devices/tesla/tesla/counter.py Outdated
…andling

- remove session / connection diagnostics and analysis-only helpers
- remove /proc-based connection stats and periodic session logging
- keep fail-fast handling for the Tesla update cycle
- keep cookie reuse and 401/403 reauthentication flow
- keep support for non-critical requests via fail_fast=False
- reduce implementation closer to upstream while preserving Powerwall-specific fixes
@NicoP-codeing

Copy link
Copy Markdown
Contributor Author

Hallo LKuemmel,

für die Entwicklung und Optimierung der Schnittstelle hatte ich einige Debug und Testfunktionen implementiert. Die habe ich nun wieder entfernt (siehe meine PR-Aktualisierung 2d479e0).
Was ist damit beheben möchte: Tesla scheint irgendwann damit aufgehört zu haben, über die lokale API gültige Stromwerte bereitzustellen. Euer Modul wurde leider nie an diese Änderung angepasst.

@NicoP-codeing

Copy link
Copy Markdown
Contributor Author

Meine Tests über das Osterwochenende sind erfolgreich verlaufen. Es wäre nett, wenn ihr nochmals einen Review durchführt.

self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config))

@staticmethod
def _safe_float(val, default: float = 0.0) -> float:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bitte die Methode entfernen. Wenn Werte in einem Zyklus fehlen, wird eine Exception geworfen.
Es wird inkonsistent und der Code in jedem Modul sehr umfangreich, wenn überall default-Werte gesetzt werden.

Comment on lines +80 to +94
need_status = False
if getattr(client, "cookie_renewed", False):
need_status = True
elif not getattr(self.store, "firmware", ""):
need_status = True

if need_status:
try:
status = client.get_json("/api/status", fail_fast=False)
self.store.firmware = status.get("version", "")
log.debug("Firmware: %s", self.store.firmware)
except Exception:
# /api/status is non-critical and must not trigger fail-fast
pass

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ist es nicht sinnvoller, die Firmware-Version in der initializer-Methode der device.py aufzurufen?
Die Firmware-Version wird ohnehin nur im Log aufgerufen und hat keinen weiteren Nutzen.

@NicoP-codeing NicoP-codeing Apr 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ja, das ist aus meiner Sicht sinnvoller. Die Firmware-Version ist nicht regelungsrelevant und sollte daher nicht im regulären Update-Zyklus abgefragt werden.

Ich nehme die /api/status-Abfrage aus dem Counter-Update heraus und verschiebe sie in die initializer-Methode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Danke für den Hinweis. Ich nehme die lokale Default-Wert-Behandlung an dieser Stelle wieder heraus, damit fehlende oder unvollständige Werte konsistent über den bestehenden Exception-/Fallback-Pfad behandelt werden.

Mein Ziel war hier vor allem, auf inkonsistente Tesla/Neurio-Antworten robuster zu reagieren. Ich richte mich im PR aber nach der zentralen openWB-Logik und reduziere die lokale Sonderbehandlung wieder.

Comment on lines +63 to +69
def _ensure_initialized():
nonlocal http_client, session
if session is None or http_client is None:
initializer()
if session is None or http_client is None:
raise Exception("Powerwall device initializer did not create session/http_client")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bitte entfernen, wird zentral gelöst.
Wenn bei einer Exception der initializer aufgerufen werden soll, in Z. 113 den Parameter error_handler mit initializer initialisieren.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Danke für den Hinweis. Ich entferne die lokale Initialisierungs-Hilfslogik wieder, damit das Modul näher an der zentralen openWB-Struktur bleibt.

Falls eine Reinitialisierung bei Fehlern weiterhin nötig ist, binde ich das wie vorgeschlagen über den error_handler mit initializer an, statt es lokal im Modul zu behandeln.

Comment on lines +19 to +53
# Fail-fast marker for the current polling cycle
self.cycle_failed = False

# Marker: set when a new auth cookie was negotiated (startup or reauth)
self.cookie_renewed = False

def reset_cycle(self):
"""Reset per update-cycle flags."""
self.cycle_failed = False
self.cookie_renewed = False

def mark_cookie_renewed(self):
"""Mark that cookies have been freshly negotiated (start or reauth)."""
self.cookie_renewed = True

def get_json(self, relative_url: str, *, fail_fast: bool = True):
"""
:param fail_fast:
True -> errors mark this cycle as failed (device aborts remaining components)
False -> errors do NOT mark cycle_failed (for non-critical endpoints like /api/status)
"""
url = self.__base_url + relative_url
return self.__session.get(url, cookies=self.cookies, verify=False, timeout=5).json()

try:
response = self.__session.get(
url,
cookies=self.cookies,
verify=False,
timeout=5,
)
return response.json()
except (HTTPError, Timeout, ConnectionError, SSLError, RequestException, ValueError):
if fail_fast:
self.cycle_failed = True
raise No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bitte die Änderungen entfernen, diese blähen den Code stark auf, ohne einen wirklichen Mehrwert zu bieten.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Verstanden. Ich reduziere die Änderungen an dieser Stelle wieder deutlich.

Mein Ziel war hier, konkrete Probleme im Tesla-/Powerwall-Modul zu adressieren. Für den PR lasse ich aber nur die Teile stehen, die dafür funktional wirklich notwendig sind, und nehme die übrigen Erweiterungen wieder heraus.

…allback

- remove analysis-only diagnostics and session tracking from device.py
- remove connection stats and other debug-only helpers
- simplify http_client.py to a minimal request wrapper
- keep cookie reuse and 401/403 reauthentication flow in device.py
- use fail-fast handling in device.py via SingleComponentUpdateContext(..., reraise=True)
- move Tesla-specific robustness back into the counter module only
- keep aggregate["site"]["energy_imported/exported"] as sole source for EVU counters
- keep local current and power factor calculation when Neurio/Tesla returns only 0 currents
- remove /api/status handling from counter.py
- fix formatting / lint issues (newline at EOF, spaces instead of tabs)
return default
return float(val)
except (TypeError, ValueError):
return default

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Die Methode und die Aufrufe kann entfallen, das ist zentral umgesetzt.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Danke, ich entferne _safe_float() und verlasse mich auf die zentrale Behandlung. Mein ursprünglicher Gedanke war, unvollständige Tesla-Antworten robuster abzufangen.

Comment on lines +27 to +34
try:
# For Tesla/Powerwall we want fail-fast behaviour:
# if one critical component update fails (especially EVU / house transition point),
# abort the remaining Tesla component updates in this cycle.
with SingleComponentUpdateContext(component.fault_state, reraise=True):
component.update(client, aggregate)
except Exception:
break

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
try:
# For Tesla/Powerwall we want fail-fast behaviour:
# if one critical component update fails (especially EVU / house transition point),
# abort the remaining Tesla component updates in this cycle.
with SingleComponentUpdateContext(component.fault_state, reraise=True):
component.update(client, aggregate)
except Exception:
break
with SingleComponentUpdateContext(component.fault_state, reraise=True):
component.update(client, aggregate)

Das bricht mit der bestehenden Logik. Wenn eine Komponente nicht auslesbar ist, müssen alle anderen trotzdem ausgelesen werden, sonst kommen sie nicht in den Fehlerzustand oder wenn sie Werte liefern, braucht die Regelung diese.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ich verstehe den Einwand zum break im Komponentenloop. Ich würde das Verhalten gern präzisieren:

Bei Parsing-/Datenfehlern innerhalb einer einzelnen Komponente soll kein fail-fast erfolgen; dann soll die jeweilige Komponente über den bestehenden Fehlerpfad fehlschlagen und die übrigen Komponenten sollen weiter verarbeitet werden.

Fail-fast soll nur bei Gateway-/Transportfehlern greifen, also wenn die Powerwall/Gateway-Kommunikation selbst fehlschlägt. In diesem Fall hängen alle Tesla-Komponenten an derselben nicht erreichbaren Quelle, weitere Abfragen im selben Zyklus erzeugen nur zusätzliche Timeouts und verzögern die Regelschleife.

Ich würde die Änderung entsprechend anpassen, sodass nur Verbindungs-/Gatewayfehler den Tesla-Zyklus abbrechen, nicht aber komponentenspezifische Parsing- oder Datenfehler. Ist das in eurem Interesse?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bitte kein modulspezifisches Error-Handling implementieren. Das Error-Handling erfolgt zentral. Bei der Vielzahl der Module ist das sonst nicht wartbar.
Wenn die Powerwall nicht erreichbar ist, wird __update_components abgebrochen und die Modul-Updates nicht mehr aufgerufen.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Danke für die Klarstellung. Ich habe das modulspezifische Fail-Fast jetzt vollständig entfernt.

__update_components() liest zunächst zentral /api/meters/aggregates. Ist die Powerwall nicht erreichbar, schlägt bereits dieser Aufruf fehl und die einzelnen Komponenten-Updates werden nicht mehr aufgerufen.

Im Komponentenloop verwende ich wieder den normalen SingleComponentUpdateContext ohne reraise=True, sodass Fehler einzelner Komponenten über die bestehende zentrale Logik behandelt werden und die übrigen Komponenten weiterhin aktualisiert werden können.

timeout=5,
)
log.debug("Authentication endpoint send cookies %s", str(response.cookies))
response.raise_for_status()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
response.raise_for_status()

Wird über einen Hook aufgerufen.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Werde ich entfernen!

<div class="alert alert-success">
Anmeldung erfolgreich!<br>
Die erhaltenen Token wurden gespeichert. Du kannst diese Seite jetzt schließen.
Die erhaltenen Token wurden gespeichert. Sie können diese Seite jetzt schließen.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Im UI wird Du als Ansprache verwendet.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Werde ich ändern!

- use central openWB component error handling
- remove Tesla-specific fail-fast handling
- rely on the shared HTTP response hook
- remove obsolete local API compatibility warning
- remove _safe_float and module-specific defaults
- propagate HTTP errors to central error handling
- keep compatibility fallback for missing phase data
- calculate phase currents from P/Q/U when Tesla reports zero currents
@NicoP-codeing

Copy link
Copy Markdown
Contributor Author

Hallo @LKuemmel,
ich habe die letzten Review-Hinweise umgesetzt und die Änderungen gepusht:

  • modulspezifisches Fail-Fast entfernt; Verwendung der zentralen openWB-Fehlerbehandlung
  • reraise=True und break im Komponentenloop entfernt
  • _safe_float() und lokale Default-Werte aus dem Counter entfernt
  • HTTP-Fehler werden im Counter nicht mehr lokal abgefangen
  • explizites raise_for_status() entfernt
  • Tesla/Neurio-spezifische Berechnung der Phasenströme aus P/Q/U bleibt erhalten, wenn die API für alle Phasen 0 A liefert

Die Änderungen habe ich anschließend nochmals an meiner Powerwall 2 / Backup Gateway 2 getestet.
Könnt ihr bitte nochmals einen Review durchführen?

@LKuemmel LKuemmel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bitte noch die Flake8 Meldungen beseitigen, dann kann der PR so rein.

Comment thread packages/modules/devices/tesla/tesla/device.py Outdated
@LKuemmel
LKuemmel merged commit 165b22a into openWB:master Sep 28, 2026
1 check passed
AlexanderHa98 pushed a commit to AlexanderHa98/core that referenced this pull request Sep 29, 2026
…oop (openWB#3232)

* tesla/powerwall: implement load management, add fail-fast handling and gateway diagnostics

- add structured HTTP error handling in PowerwallHttpClient
  - distinguish transport, HTTP and JSON parsing failures
  - support fail_fast=False for non-critical requests
  - track cycle_failed and cookie_renewed state across one polling cycle

- add fail-fast handling in device.py
  - stop remaining Tesla component updates once a critical Powerwall request fails
  - log native TCP connection state diagnostics via /proc/net/tcp(/tcp6)
  - emit periodic session / connection health logs
  - keep cookie reuse, but mark startup + reauth cycles explicitly

- reduce /api/status traffic in counter.py
  - fetch firmware only after startup or after a new auth cookie was negotiated
  - treat /api/status as non-critical so it cannot fail the whole cycle

- keep import/export counters based on aggregate["site"]["energy_imported/exported"]
  - do not use summed per-phase energy counters as meter source
  - keep /api/meters/site for phase details only
  - add derived fallback currents and signed power factors from P/Q/U when Tesla/Neurio currents are 0
  - expose additional diagnostics such as frequency / serial number
  - add throttled debug logging to compare aggregate counters with per-phase sums
  - restore aggregate-based site counters so load management works correctly again

* tesla/powerwall: simplify device/http client and keep cookie reauth handling

- remove session / connection diagnostics and analysis-only helpers
- remove /proc-based connection stats and periodic session logging
- keep fail-fast handling for the Tesla update cycle
- keep cookie reuse and 401/403 reauthentication flow
- keep support for non-critical requests via fail_fast=False
- reduce implementation closer to upstream while preserving Powerwall-specific fixes

* tesla/powerwall: simplify device/http client and keep local current fallback

- remove analysis-only diagnostics and session tracking from device.py
- remove connection stats and other debug-only helpers
- simplify http_client.py to a minimal request wrapper
- keep cookie reuse and 401/403 reauthentication flow in device.py
- use fail-fast handling in device.py via SingleComponentUpdateContext(..., reraise=True)
- move Tesla-specific robustness back into the counter module only
- keep aggregate["site"]["energy_imported/exported"] as sole source for EVU counters
- keep local current and power factor calculation when Neurio/Tesla returns only 0 currents
- remove /api/status handling from counter.py
- fix formatting / lint issues (newline at EOF, spaces instead of tabs)

* Fix Tesla error handling and compatibility note

- use central openWB component error handling
- remove Tesla-specific fail-fast handling
- rely on the shared HTTP response hook
- remove obsolete local API compatibility warning

* se standard error handling in Tesla counter

- remove _safe_float and module-specific defaults
- propagate HTTP errors to central error handling
- keep compatibility fallback for missing phase data
- calculate phase currents from P/Q/U when Tesla reports zero currents

* Fix Tesla module lint errors

* Adjust Tesla API compatibility note
LKuemmel added a commit that referenced this pull request Sep 30, 2026
* dimm kit: improve error handling, handle deleted output devices (#3868)

* dimm kit: improve error handling, handle deleted output devices

* review, complete logging

* Apply batched suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fixes

* add pytest

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* PV-Reset: NO_CHARGING_ALLOWED blockiert den robustness-Reset nicht mehr (#3968)

Nach PR #3966 blockierte ein eingesteckter, aber unterhalb der Einschaltschwelle wartender Ladepunkt (Zustand NO_CHARGING_ALLOWED) weiterhin dauerhaft den
Reset von reserved_surplus/released_surplus - das ist aber der mit Abstand häufigste, langlebigste Zustand eines PV-ladenden Fahrzeugs. Jede Reservierung
wird jedoch bereits beim Übergang in diesen Zustand freigegeben (siehe zB Counter.reset_switch_on_off()), sodass er nie mit einer noch offenen Reservierung erreicht wird - er gehörte also nie in die Blockierliste.

Betroffen war zB ein Fall, in dem eine verwaiste reserved_surplus-Reservierung aus einem unterbrochenen Vorgang nie mehr zurückgesetzt wurde, wodurch die
Einschaltschwelle dauerhaft nicht mehr erreicht werden konnte (PV-Laden startet gar nicht mehr) - siehe Diskussion.

Ersetzt die 5 Einzelvergleiche + Chargepoint/Consumer-Fallunterscheidung + plug_state-Sonderfall durch eine einzige Zustandsliste der tatsächlich
reservierungshaltenden Zustände (PERFORMING_PHASE_SWITCH, PHASE_SWITCH_DELAY, SWITCH_OFF_DELAY, SWITCH_ON_DELAY), verifiziert gegen jede +=/-=-Stelle in
counter.py/ev.py/chargepoint.py.

references #3908 (reply in thread)

* fix: external_openwb chargepoint crashes when fault_state topic is missing (#3970)

get_values() indexed received_topics[fault_state] directly, including
inside its own KeyError handler - if the paired internal chargepoint
hadn't published fault_state yet (startup, reconnect), the fallback
crashed on the exact same missing key instead of handling it.

* consumer: fix hierarchy, default usage (#3975)

* consumer: fix hierarchy, default usage

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: external_openwb occasionally missed retained MQTT topics under load (#3976)

start_finite_loop()'s fixed 1s collection window left little margin for
connection/handshake overhead when the paired internal chargepoint was
busy (eg mid-charge), occasionally causing get_values() to miss a
retained topic despite retain=True. Made the timeout configurable
(default unchanged for the other 17 call sites) and doubled it for
external_openwb specifically, which has to reliably collect ~20 topics
over a real network hop rather than one or two local values.

* Systembericht Verbraucher (#3956)

* add consumers; reorganize structure

* improve

* fix: Ladepunkte nutzen die Abschaltschwelle der Verbraucher (#3984)

calc_switch_off_threshold() liest fuer alle Lasten surplus.consumer.switch_off_threshold. surplus.vehicle.switch_off_threshold ist konfigurierbar, wird migriert und in der Debug-Ausgabe angezeigt, aber im Regelalgorithmus nirgends gelesen.

Die Migration verschiebt den alten pv_charging/switch_off_threshold nach surplus/vehicle/switch_off_threshold. Damit regeln bestehende Anlagen seit dem Update mit dem neu angelegten Consumer-Default 0 statt mit dem konfigurierten Wert.

* removing meter-only consumers from load management priority (#3998)

* removing meter-only consumers from load management priority

* review

* extend for self_controlled

* Potential fix for pull request finding 'Preserve pre-existing empty groups during migration'

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* build UI (#3999)

* Update: Hausverbrauch Zähler (#3991)

* EVU auf Auto entspricht Ja

* fix

* avm: fix consumer switch_on/off using unresolved ain, fix bare return in session lookup (#3987)

* build UI (#4000)

* Feature negative dc charging (#3795)

* Add Bidi to instant-charging

* Update Koala-UI min/max DC-Sollleistung

* fix

* Build Themes

* avm consumer: invertierte Session-Refresh-Bedingung beheben (#4001)

ensure_valid_session_id() erneuerte die Session nur, wenn
check_valid_session_id() False war - die Funktion liefert aber "ist
ungültig" zurück, nicht "ist gültig". Dadurch wurde nie erneuert,
sobald session_id einmal None oder abgelaufen war: alle Anfragen
liefen dauerhaft mit sid=None und schlugen mit HTTP 403 fehl.

Umbenannt zu session_id_expired(), passend zum tatsächlichen
Rückgabewert.

* Peak Filter: bei Rückwärtssprüngen auf Wert größer/gleich Höchststand warten (#4007)

* Peak Filter: bei Rückwärtssprüngen auf Wert größer/gleich Höchststand warten

* review

* build UI (#4008)

* Consumer: Add Daikin, EGO and EMS-ESP (#3969)

* Add Daikin, EGO and EMS-ESP consumer modules

* daikin: Air2Air-Fixes aus Review (Lena, PR #3969) + neues Altherma-Modul

Air2Air: Name korrigiert ("Daikin Air2Air (HomeHub EKRHH)"), METER_ONLY entfernt
(keine Leistungsmessung verfügbar).

Neu: daikin_homehub_altherma - eigenes Modul für Daikin Altherma, da eigene
Modbus-Register (EKRHH Installer Reference Guide 4PDE744838, Kap. 9, separat von
Air2Air/Kap. 10). Register gegen das offizielle Handbuch geprüft:
- switch_on/off: Reg 56 (Smart-Grid-Modus), analog zu Air2Airs Reg 1000
- set_power_limit: Reg 58 (Allgemeine Leistungsbegrenzung, gilt laut Handbuch
  unabhängig vom Smart-Grid-Modus, anders als Reg 57)
- update: Input-Reg 51 (Stromverbrauch Wärmepumpe) - Altherma kann im Gegensatz
  zu Air2Air tatsächlich Leistung melden, daher METER_ONLY hier sinnvoll

* Consumer: Add Mitsubishi and Gree AC modules (#3973)

* Add Mitsubishi and Gree AC consumer modules

* Mitsubishi/Gree: METER_ONLY entfernt, da keine Leistungsmessung

Beide Module haben keine update()-Funktion und liefern somit keine
Leistungsmessung; ConsumerUsage.METER_ONLY war daher nicht zutreffend.

* Gree: tcid im Bind-Request ergänzt (Review Lena)

cid muss beim Binden konstant "app" sein, tcid identifiziert das
Zielgerät per MAC - bestätigt gegen tomikaa87/gree-remote und
cmroche/greeclimate.

* build UI (#4010)

* Fixed Hours Tariff: migrate fixed-hours weekdays to Python weekday format (#4009)

* Hierarchie: Entferne mit Verbraucher verknüpften Zähler (#4011)

* Hierarchie: Entferne mit Verbraucher verknüpften Zähler

* flake8

* Speicher-Vorrang bei Erholung von Mindest- bis Maximal-SoC (#3986)

* bat_all: restore Speicher-Vorrang beim Erholen von Mindest-SoC bis Maximal-SoC

* bat_all: Speicherleistung nicht ans Fahrzeug weitergeben, wenn Entladung nicht erlaubt ist

* build UI (#4025)

* Add ACLs consumer and load management topics (#3964)

* Add acls

* Add load management prio to status

* fix colors theme: leftover pv_charging topic paths from PR #3812 topic migration (#3946)

* Build Themes

* Consumer: Add Kermi, LG Therma V, Luxtronik (#3971)

* Add Kermi, LG Therma V and Luxtronik consumer modules

* kermi, luxtronik: Skalierungsfaktoren korrigiert (Review #3971)

Kermi Reg 301 (PV-Überschuss): war /10, muss *10 sein.
Luxtronik Reg 10301 (el. Leistung): war *10, muss *100 sein.
Reg 108 (Kermi) war bereits korrekt.

* Sonnenbatterie: add OperationMode Optimization (#3769)

* add Mode Optimization

* check for valid OperatingMode

* Fix load management single phase netting and Bidi discharge currents (#4012)

* Lastmanagement: eigenen Ladestrom bei ein-/zweiphasigem Laden nicht mehr als Fremdlast zählen

_set_current_left() entschied per min(element_current) < 0, ob der Strom
eines Ladepunkts als Verbrauch oder Einspeisung zählt. Bei ein-/zweiphasigem
Laden reicht dafür schon minimales negatives Messrauschen auf einer
ungenutzten Phase - der eigene Ladestrom wurde dann nirgends vom
Zähler-Rohwert abgezogen, das Lastmanagement hielt die eigene Ladung für
Fremdlast (falsche "Maximalstromstärke erreicht"/Schieflast-Meldungen,
siehe Discussion #3908). _set_power_left() hat exakt dasselbe Problem
nicht, da dort schon die Gesamtleistung statt einzelner Phasenströme
geprüft wird - jetzt angeglichen.

* Bidi-Entladung: eigene Hardware-Stromgrenze des Ladepunkts durchsetzen

check_min_max_current() rief check_cp_max_current() (setzt dc_max_current
des Ladepunkt-Templates durch) nur im Lade-Zweig auf, nicht beim Entladen
(BIDI_CHARGING, required_current < 0) - die vom Fahrzeug gemeldete
max_discharge_power wurde beachtet, die eigene Hardwaregrenze des
Ladepunkts nicht. Bug in #3795, check_cp_max_current() behandelt negative
Werte bereits korrekt (Vorzeichen wird erhalten).

* build UI (#4026)

* Improve Monthly/Yearly Log (#3829)

* Improve Log

* Move generate_totals-Logger in logger.py

* Fix

* Fix Circular Import

* Update .gitignore

* fix

* Ensure ramdisk folder exists

* retrigger Copilot

* Add Copilot suggestions

* fix

* Fix Tesla/Powerwall data handling and improve robustness of control loop (#3232)

* tesla/powerwall: implement load management, add fail-fast handling and gateway diagnostics

- add structured HTTP error handling in PowerwallHttpClient
  - distinguish transport, HTTP and JSON parsing failures
  - support fail_fast=False for non-critical requests
  - track cycle_failed and cookie_renewed state across one polling cycle

- add fail-fast handling in device.py
  - stop remaining Tesla component updates once a critical Powerwall request fails
  - log native TCP connection state diagnostics via /proc/net/tcp(/tcp6)
  - emit periodic session / connection health logs
  - keep cookie reuse, but mark startup + reauth cycles explicitly

- reduce /api/status traffic in counter.py
  - fetch firmware only after startup or after a new auth cookie was negotiated
  - treat /api/status as non-critical so it cannot fail the whole cycle

- keep import/export counters based on aggregate["site"]["energy_imported/exported"]
  - do not use summed per-phase energy counters as meter source
  - keep /api/meters/site for phase details only
  - add derived fallback currents and signed power factors from P/Q/U when Tesla/Neurio currents are 0
  - expose additional diagnostics such as frequency / serial number
  - add throttled debug logging to compare aggregate counters with per-phase sums
  - restore aggregate-based site counters so load management works correctly again

* tesla/powerwall: simplify device/http client and keep cookie reauth handling

- remove session / connection diagnostics and analysis-only helpers
- remove /proc-based connection stats and periodic session logging
- keep fail-fast handling for the Tesla update cycle
- keep cookie reuse and 401/403 reauthentication flow
- keep support for non-critical requests via fail_fast=False
- reduce implementation closer to upstream while preserving Powerwall-specific fixes

* tesla/powerwall: simplify device/http client and keep local current fallback

- remove analysis-only diagnostics and session tracking from device.py
- remove connection stats and other debug-only helpers
- simplify http_client.py to a minimal request wrapper
- keep cookie reuse and 401/403 reauthentication flow in device.py
- use fail-fast handling in device.py via SingleComponentUpdateContext(..., reraise=True)
- move Tesla-specific robustness back into the counter module only
- keep aggregate["site"]["energy_imported/exported"] as sole source for EVU counters
- keep local current and power factor calculation when Neurio/Tesla returns only 0 currents
- remove /api/status handling from counter.py
- fix formatting / lint issues (newline at EOF, spaces instead of tabs)

* Fix Tesla error handling and compatibility note

- use central openWB component error handling
- remove Tesla-specific fail-fast handling
- rely on the shared HTTP response hook
- remove obsolete local API compatibility warning

* se standard error handling in Tesla counter

- remove _safe_float and module-specific defaults
- propagate HTTP errors to central error handling
- keep compatibility fallback for missing phase data
- calculate phase currents from P/Q/U when Tesla reports zero currents

* Fix Tesla module lint errors

* Adjust Tesla API compatibility note

* Error status table (#3943)

* add error and warning icons to chargepoint table

* Align warning icons - remove extra warning column

* No tooltip for mobile - ellipsis title only if truncated

* Build Themes

* add consumer temperature readings to consumer card (#3974)

* add consumer temperature readings to consumer card

* check if temperature data is present

* use getValueObject for formatting

* Build Themes

* Consumer: Add M-TEC, Weishaupt, Xtherma (#3972)

* Add M-TEC, Weishaupt and Xtherma consumer modules

* M-TEC: Reg 707 durch evcc-Quelle bestätigt, Kommentar aktualisiert

Reg 707 (elektrische Leistungsaufnahme) ist in evcc's mtec.yaml als
holding register mit scale 1 gelistet, identisch zu unserer Implementierung.

* Weishaupt: Reg 33126 entfernt (nicht verifizierbar), Xtherma: SG-Ready on/off ergänzt

Weishaupt: Reg 33126 (el. Leistungsaufnahme) ist weder in der offiziellen
Datenpunktliste (83807301) noch in einer ausgereiften Community-Implementierung
(OStrama/weishaupt_modbus) zu finden - update()/METER_ONLY entfernt,
SollwertPV (Reg 40002, offiziell dokumentiert) bleibt unverändert.

Xtherma: Reg 60 (SG-Ready-Modus) ergänzt switch_on/switch_off
(SUSPENDABLE_ONOFF), eigener 30min-Throttle laut Protokoll.

* Fix charge cost calc (#4022)

* fix chargelog energy source calculation

* Add Test

* fix circular error

---------

Co-authored-by: LKuemmel <76958050+LKuemmel@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: SeaSpotter <florian.horch@gmail.com>
Co-authored-by: ndrsnhs <156670705+ndrsnhs@users.noreply.github.com>
Co-authored-by: s0170071 <33855380+s0170071@users.noreply.github.com>
Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: BrettS <168732306+Brett-S-OWB@users.noreply.github.com>
Co-authored-by: NicoP-codeing <nico.prenzel@pn-systeme.de>
matmoers pushed a commit to matmoers/openWB-core that referenced this pull request Oct 13, 2026
…llen

Powerwall 3 / Gateway 3 stellen /api/meters/site nicht mehr bereit und
antworten mit HTTP 404. Bis einschließlich 2.2.3 hat der Zähler bei
dieser Abfrage HTTPError abgefangen und auf die Gesamtwerte aus
/api/meters/aggregates zurückgegriffen. Beim Umbau in openWB#3232 ist
HTTPError aus der except-Klausel entfallen. Der 404, den der
raise_for_status()-Hook der Session auslöst, setzt dadurch die gesamte
Zähler-Komponente auf Fehler, obwohl Leistung, Bezug und Einspeisung in
aggregates verfügbar sind.

Ein 404 auf /api/meters/site wird jetzt als "Endpunkt nicht vorhanden"
behandelt: Es werden die Gesamtwerte aus aggregates verwendet, und der
Endpunkt wird für die Lebensdauer der Komponente nicht erneut
abgefragt. Andere HTTP-Fehler (z. B. 500 oder 401/403, die device.py
für die erneute Anmeldung nutzt) werden weiterhin ausgelöst.
matmoers pushed a commit to matmoers/openWB-core that referenced this pull request Oct 13, 2026
…llen

Eine Powerwall 3 mit Backup Gateway 2 stellt /api/meters/site nicht
mehr bereit und antwortet mit HTTP 404. Bis einschließlich 2.2.3 hat der
Zähler bei dieser Abfrage HTTPError abgefangen und auf die Gesamtwerte
aus /api/meters/aggregates zurückgegriffen. Beim Umbau in openWB#3232 ist
HTTPError aus der except-Klausel entfallen. Der 404, den der
raise_for_status()-Hook der Session auslöst, setzt dadurch die gesamte
Zähler-Komponente auf Fehler, obwohl Leistung, Bezug und Einspeisung in
aggregates verfügbar sind.

Ein 404 auf /api/meters/site wird jetzt als "Endpunkt nicht vorhanden"
behandelt: Es werden die Gesamtwerte aus aggregates verwendet, und der
Endpunkt wird für die Lebensdauer der Komponente nicht erneut
abgefragt. Andere HTTP-Fehler (z. B. 500 oder 401/403, die device.py
für die erneute Anmeldung nutzt) werden weiterhin ausgelöst.
LKuemmel pushed a commit that referenced this pull request Oct 14, 2026
…llen (#4105)

Eine Powerwall 3 mit Backup Gateway 2 stellt /api/meters/site nicht
mehr bereit und antwortet mit HTTP 404. Bis einschließlich 2.2.3 hat der
Zähler bei dieser Abfrage HTTPError abgefangen und auf die Gesamtwerte
aus /api/meters/aggregates zurückgegriffen. Beim Umbau in #3232 ist
HTTPError aus der except-Klausel entfallen. Der 404, den der
raise_for_status()-Hook der Session auslöst, setzt dadurch die gesamte
Zähler-Komponente auf Fehler, obwohl Leistung, Bezug und Einspeisung in
aggregates verfügbar sind.

Ein 404 auf /api/meters/site wird jetzt als "Endpunkt nicht vorhanden"
behandelt: Es werden die Gesamtwerte aus aggregates verwendet, und der
Endpunkt wird für die Lebensdauer der Komponente nicht erneut
abgefragt. Andere HTTP-Fehler (z. B. 500 oder 401/403, die device.py
für die erneute Anmeldung nutzt) werden weiterhin ausgelöst.

Co-authored-by: matmoers <ploetman@gmx.de>
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.

4 participants