Eine Neuschreibung ist kein Wechsel - Wartezeit startet nicht mehr neu
Home Assistant schreibt denselben Zustandswert auch neu, wenn sich nur ein Attribut geruehrt hat. Am 04.09.2026 gezaehlt: von 22 Zustandszeilen der Zuendung waren nur 6 echte Wechsel, die uebrigen 16 Neuschreibungen - und alle 6 Wechsel lagen ausserhalb der Fahrten. Das Zuendungssignal ist unterwegs stabil. Der Beobachter unterschied das nicht. Fiel eine Neuschreibung von "aus" in die laufende Wartezeit, brach Zweig 2 die wartende Bestaetigung ab und begann die 15 Minuten von vorn. Am 04.09. ist es nicht passiert - die Neuschreibungen lagen hinter dem Fenster -, aber das Loch war da. _signalwechsel() bekommt jetzt mit, ob es ein echter Wechsel war. Eine Neuschreibung, waehrend fuer diese Fahrt schon eine Bestaetigung wartet, laesst sie stehen. Bewusst NICHT als Filter ganz oben: _spaetes_aus() verlaesst sich darauf, ein Zuendungs-Aus auch als off -> off zu sehen - daran haengt das Nachtragen vorlaeufiger Enden. Die Pruefung sitzt nur in Zweig 2, und ein eigener Test belegt, dass der andere Weg weiter offen ist. 21 Tests (von 18), Gegenprobe mit ausgeschalteter Pruefung scheitert wie erwartet an einem. py_compile sauber, audi_ha_test auf 2026.9.4.25 ohne Traceback. AGENTS.md haelt ausserdem fest: die Beschreibung des Beenden-Knopfes war zu weit gefasst (die Korrektur zieht nur nach hinten, nie nach vorn), und der flespi-Cache laesst sich ueber DELETE auf die Einstellung zum Neulesen zwingen - mit dem Haken, dass dabei ausstehende Werte verloren gingen.
This commit is contained in:
@@ -337,6 +337,74 @@ class GepufferterStapel(Basis):
|
||||
self.assertEqual(k.beendet, [])
|
||||
self.assertEqual(k.fahrt_start_ts, beginn)
|
||||
|
||||
|
||||
class Neuschreibung(Basis):
|
||||
"""Home Assistant schreibt denselben Wert auch neu.
|
||||
|
||||
Am 04.09.2026 gezaehlt: von 22 Zustandszeilen der Zuendung waren nur 6
|
||||
echte Wechsel. Die uebrigen 16 waren Neuschreibungen - der Wert selbst
|
||||
hatte sich nicht geruehrt.
|
||||
|
||||
Der Beobachter unterscheidet das nicht, er reicht neu und alt durch. Faellt
|
||||
so eine Neuschreibung von "aus" in die laufende Wartezeit, begann die
|
||||
Wartezeit von vorn - und bei genuegend vielen nie ein Ende.
|
||||
"""
|
||||
|
||||
async def test_neuschreibung_verlaengert_die_wartezeit_nicht(self):
|
||||
k = FakeKoordinator()
|
||||
await f.zuendung_geaendert(k, "off", "on", AUS)
|
||||
wartet = k.ende_wartet
|
||||
self.assertIsNotNone(wartet, "nach dem Aus wartet eine Bestaetigung")
|
||||
|
||||
# Dieselbe Meldung noch einmal, ohne Wertwechsel.
|
||||
await f.zuendung_geaendert(k, "off", "off", AUS + datetime.timedelta(seconds=30))
|
||||
self.assertIs(
|
||||
k.ende_wartet, wartet, "es wartet weiterhin dieselbe Bestaetigung"
|
||||
)
|
||||
|
||||
await k.ausklingen()
|
||||
self.assertEqual(len(k.beendet), 1, "und sie kommt zum Zug")
|
||||
self.assertEqual(
|
||||
k.beendet[0][1],
|
||||
AUS - datetime.timedelta(seconds=f.ZUENDUNG_NACHLAUF_S),
|
||||
"mit dem Ende aus dem ERSTEN Aus, nicht dem der Neuschreibung",
|
||||
)
|
||||
|
||||
async def test_echtes_zweites_aus_zaehlt_weiterhin(self):
|
||||
"""Die Gegenprobe: ein Wechsel darf die Bestaetigung sehr wohl ersetzen."""
|
||||
k = FakeKoordinator()
|
||||
await f.zuendung_geaendert(k, "off", "on", AUS)
|
||||
erstes = k.ende_wartet
|
||||
await f.zuendung_geaendert(k, "on", "off", AUS + datetime.timedelta(seconds=30))
|
||||
await f.zuendung_geaendert(k, "off", "on", AUS + datetime.timedelta(seconds=60))
|
||||
self.assertIsNotNone(k.ende_wartet)
|
||||
self.assertNotEqual(
|
||||
k.ende_wartet, erstes, "ein echtes zweites Aus setzt ein neues Ende"
|
||||
)
|
||||
|
||||
async def test_neuschreibung_ohne_laufende_fahrt_geht_weiter_durch(self):
|
||||
"""_spaetes_aus() braucht auch ein off -> off.
|
||||
|
||||
Ein zweites, spaeter gepuffertes Aus traegt das Ende einer von Hand
|
||||
beendeten Fahrt nach. Ein Filter ganz oben haette genau das
|
||||
verschluckt - deshalb sitzt die Pruefung nur in Zweig 2.
|
||||
"""
|
||||
gesehen = []
|
||||
|
||||
async def spaetes_aus(k, ereigniszeit, jetzt):
|
||||
gesehen.append(ereigniszeit)
|
||||
|
||||
echt = f._spaetes_aus
|
||||
f._spaetes_aus = spaetes_aus
|
||||
try:
|
||||
k = FakeKoordinator(start_ts=None)
|
||||
await f.zuendung_geaendert(k, "off", "off", AUS)
|
||||
self.assertEqual(
|
||||
len(gesehen), 1, "ohne laufende Fahrt muss das Aus ankommen"
|
||||
)
|
||||
finally:
|
||||
f._spaetes_aus = echt
|
||||
|
||||
class Einstellung(unittest.IsolatedAsyncioTestCase):
|
||||
"""_pausenzeit_s selbst - hier ungepatcht."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user