#!/usr/bin/env python3 """Nur eine NEUE Messung wird aufgezeichnet - und mit ihrer eigenen Zeit. Warum es das gibt (06.09.2026): zustand_oder_none() liefert den zuletzt bekannten Zustand unabhängig von seinem Alter, und pruefen() stempelte ihn mit "jetzt". Ein schweigender Dongle "misst" damit alle fünf Minuten weiter seinen letzten Wert. Am Bestand des Testcontainers nachgemessen, nicht vermutet: 2026-08-16 Tageseintrag vorhanden (13.024 V @ 08:37:43) Spannungsmeldungen an diesem Tag: 0 Abstand zur nächsten echten Meldung: 885 Minuten Sieben von 19 Einträgen standen mehr als fünf Minuten von jeder echten Meldung entfernt; die unstrittigen Fahrzeugtage dagegen exakt bei 0 s. Geprüft wird deshalb: dass eine veraltete Messung gar nicht mehr erfasst wird, dass eine frische ihren eigenen Zeitstempel behält (Gerätezeit, wenn zuordenbar - so wie der Rückblick es tut), und dass der Tag aus der Messung stammt und nicht aus der Uhr des Servers. Aufruf: python3 tests/batterie/test_messzeitpunkt.py (braucht das homeassistant-Paket, weil die Integration es importiert - also z. B. im Test-Container) """ import datetime import os import sys import unittest _HIER = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(os.path.dirname(_HIER))) from custom_components.audi_dashboard import batterie as b # noqa: E402 JETZT = datetime.datetime(2026, 8, 16, 8, 37, 43, tzinfo=datetime.UTC) class Zustand: """Das Wenige, das messzeitpunkt() von einem HA-State braucht.""" def __init__(self, last_updated, state=""): self.last_updated = last_updated self.state = state def vor(sekunden): return JETZT - datetime.timedelta(seconds=sekunden) class NurNeueMessungen(unittest.TestCase): def test_frische_messung_wird_erfasst(self): z = Zustand(vor(12)) self.assertEqual(b.messzeitpunkt(z, None, JETZT), vor(12)) def test_veraltete_messung_wird_nicht_erfasst(self): """Der Fall 16.08.2026: 885 Minuten seit der letzten echten Meldung.""" z = Zustand(vor(885 * 60)) self.assertIsNone(b.messzeitpunkt(z, None, JETZT)) def test_genau_am_fenster_zaehlt_noch(self): z = Zustand(vor(b.MESSUNG_MAX_ALTER_S)) self.assertIsNotNone(b.messzeitpunkt(z, None, JETZT)) def test_eine_sekunde_darueber_nicht_mehr(self): z = Zustand(vor(b.MESSUNG_MAX_ALTER_S + 1)) self.assertIsNone(b.messzeitpunkt(z, None, JETZT)) def test_fenster_deckt_mindestens_einen_takt_ab(self): """Sonst ginge eine Messung verloren, die kurz nach einem Takt eintrifft.""" self.assertGreaterEqual(b.MESSUNG_MAX_ALTER_S, 300) def test_ohne_zustand_nichts(self): self.assertIsNone(b.messzeitpunkt(None, None, JETZT)) class GeraetezeitStattAnkunftszeit(unittest.TestCase): def test_zuordenbare_geraetezeit_gewinnt(self): ankunft = vor(30) geraet = vor(200) mz = Zustand(ankunft, str(geraet.timestamp())) self.assertEqual(b.messzeitpunkt(Zustand(ankunft), mz, JETZT), geraet) def test_zu_weit_entfernte_meldezeit_zaehlt_nicht(self): """Sie gehört dann zu einem anderen Datensatz - siehe ZUORDNUNG_MAX_S.""" ankunft = vor(30) mz = Zustand(vor(90), str(vor(200).timestamp())) self.assertEqual(b.messzeitpunkt(Zustand(ankunft), mz, JETZT), ankunft) def test_unplausible_geraetezeit_zaehlt_nicht(self): """Eine falsch gestellte Geräteuhr darf die Messung nicht mitreißen.""" ankunft = vor(30) zukunft = JETZT + datetime.timedelta(days=3) mz = Zustand(ankunft, str(zukunft.timestamp())) self.assertEqual(b.messzeitpunkt(Zustand(ankunft), mz, JETZT), ankunft) def test_unlesbare_meldezeit_zaehlt_nicht(self): ankunft = vor(30) mz = Zustand(ankunft, "unavailable") self.assertEqual(b.messzeitpunkt(Zustand(ankunft), mz, JETZT), ankunft) class NachEinemNeustart(unittest.TestCase): """Am 06.09.2026 im laufenden Testcontainer aufgefallen, nicht am Quelltext. Nach dem Neustart um 21:12:54 stand die neue Frischeprüfung im Container - und der erste Takt um 21:17:54 warnte trotzdem über den seit 15:30 stehenden 0-V-Wert. Der Grund: Home Assistant setzt beim Hochfahren jede Entität neu, last_updated steht danach auf der Neustartzeit. Gemessen: Spannung last_updated 21:12:54 Wert 0 Meldezeit last_updated 21:12:54 Wert 1788706007 -> 14:46:47 Die Ankunftszeit log also, die Gerätezeit sagte die Wahrheit: 395 Minuten. """ def test_frische_ankunft_aber_alte_geraetezeit_zaehlt_nicht(self): ankunft = JETZT # der Neustart hat sie gerade gesetzt geraet = JETZT - datetime.timedelta(minutes=395) mz = Zustand(ankunft, str(geraet.timestamp())) self.assertIsNone(b.messzeitpunkt(Zustand(ankunft), mz, JETZT)) def test_ohne_meldezeit_bleibt_die_luecke_und_das_ist_dokumentiert(self): """Ohne MELDEZEIT_SENSOR gibt es nur die Ankunftszeit - dann geht nach einem Neustart einmalig ein veralteter Wert durch. Der Test hält fest, dass das bekannt ist und nicht aus Versehen passiert.""" self.assertIsNotNone(b.messzeitpunkt(Zustand(JETZT), None, JETZT)) class DerTagKommtAusDerMessung(unittest.TestCase): def test_messung_kurz_vor_mitternacht_bleibt_im_richtigen_tag(self): """Um 00:03 verarbeitet, um 23:58 gemessen - der Eintrag gehört zum Vortag.""" jetzt = datetime.datetime(2026, 9, 7, 0, 3, 0, tzinfo=datetime.UTC) gemessen = datetime.datetime(2026, 9, 6, 23, 58, 0, tzinfo=datetime.UTC) ergebnis = b.messzeitpunkt(Zustand(gemessen), None, jetzt) self.assertEqual(ergebnis.date().isoformat(), "2026-09-06") self.assertNotEqual(ergebnis.date().isoformat(), jetzt.date().isoformat()) if __name__ == "__main__": unittest.main(verbosity=2)