fix: Offline-Flapping — erst nach 3 aufeinanderfolgenden Lesefehlern offline

Ein einzelner UDP-Paketverlust (Goodwe) oder TCP-Timeout (Modbus) hat sofort
MQTT-Status "offline" getriggert. Bei 10s Poll-Rate reicht ein Ausreißer.

Fix: _fail_count pro Poll-Loop, OFFLINE_THRESHOLD=3. Erst wenn 3 Reads in Folge
scheitern (≥30s bei 10s Interval) wird offline publiziert. Erfolg resettet
den Zähler auf 0 und stellt online sofort wieder her.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
retr0
2026-05-07 09:58:48 +02:00
parent a9f33c8e9e
commit a361c30f1b
3 changed files with 11 additions and 5 deletions
+2 -1
View File
@@ -97,4 +97,5 @@
| v1.8.20 | 2026-05-05 | Fix: Eigenversorgungskarte bei PV offline, Stromtarif-Einstellungen gehen nicht verloren | | v1.8.20 | 2026-05-05 | Fix: Eigenversorgungskarte bei PV offline, Stromtarif-Einstellungen gehen nicht verloren |
| v1.8.21 | 2026-05-05 | Fix: Finanzen-Tab bleibt nicht bei "Lade..." hängen (fEur/fKwh Scope-Bug) | | v1.8.21 | 2026-05-05 | Fix: Finanzen-Tab bleibt nicht bei "Lade..." hängen (fEur/fKwh Scope-Bug) |
| v1.8.22 | 2026-05-06 | Fix: Stromtarif-Einstellungen bleiben nach Neustart erhalten; Finanzen-Tab Layout | | v1.8.22 | 2026-05-06 | Fix: Stromtarif-Einstellungen bleiben nach Neustart erhalten; Finanzen-Tab Layout |
| v1.8.23 | 2026-05-06 | Fix: Goodwe Netzbezug-Periode zeigt 0 kWh — integrierter grid_power-Zähler statt e_total_imp-Delta | | v1.8.23 | 2026-05-07 | Fix: Goodwe Netzbezug-Periode zeigt 0 kWh — integrierter grid_power-Zähler statt e_total_imp-Delta |
| v1.8.24 | 2026-05-07 | Fix: WR/Wallbox-Offline-Flapping — erst nach 3 aufeinanderfolgenden Lesefehlern offline schalten |
+1 -1
View File
@@ -1,5 +1,5 @@
name: ShineBridge name: ShineBridge
version: "1.8.23" version: "1.8.24"
slug: shinebridge slug: shinebridge
description: Growatt Wechselrichter lokal in Home Assistant — Modbus TCP via ShineLAN-X, MQTT Discovery, Web UI description: Growatt Wechselrichter lokal in Home Assistant — Modbus TCP via ShineLAN-X, MQTT Discovery, Web UI
url: https://gitea.bitfire.work/retr0/shinebridge url: https://gitea.bitfire.work/retr0/shinebridge
+5
View File
@@ -314,6 +314,8 @@ def _poll_loop(inv_cfg: Dict[str, Any], stop: threading.Event):
inv_int_import = hist_data["_int_import"][-1][1] if hist_data.get("_int_import") else 0.0 inv_int_import = hist_data["_int_import"][-1][1] if hist_data.get("_int_import") else 0.0
inv_int_export = hist_data["_int_export"][-1][1] if hist_data.get("_int_export") else 0.0 inv_int_export = hist_data["_int_export"][-1][1] if hist_data.get("_int_export") else 0.0
last_grid_ts = time.time() last_grid_ts = time.time()
_fail_count = 0 # aufeinanderfolgende Lesefehler; erst ab >= 3 → offline
_OFFLINE_THRESHOLD = 3
while not stop.is_set(): while not stop.is_set():
t0 = time.time() t0 = time.time()
@@ -354,6 +356,7 @@ def _poll_loop(inv_cfg: Dict[str, Any], stop: threading.Event):
with State.lock: with State.lock:
d = State.inv_data.setdefault(inv_id, {"poll_count": 0}) d = State.inv_data.setdefault(inv_id, {"poll_count": 0})
if values is not None: if values is not None:
_fail_count = 0
d["values"] = values d["values"] = values
d["last_update"] = time.time() d["last_update"] = time.time()
d["modbus_ok"] = True d["modbus_ok"] = True
@@ -369,6 +372,8 @@ def _poll_loop(inv_cfg: Dict[str, Any], stop: threading.Event):
_publisher.publish_data(pub_values, prefix) _publisher.publish_data(pub_values, prefix)
_publisher.publish_status("online", prefix) _publisher.publish_status("online", prefix)
else: else:
_fail_count += 1
if _fail_count >= _OFFLINE_THRESHOLD:
d["modbus_ok"] = False d["modbus_ok"] = False
if _publisher: if _publisher:
_publisher.publish_status("offline", prefix) _publisher.publish_status("offline", prefix)