Runner merkt jetzt, wenn eine Bedingung geaendert wurde

Die Signatur, an der er ein Neuladen festmacht, bestand aus Zeilenzahlen
und MAX(changed). Beides bleibt gleich, wenn in der Weboberflaeche nur die
Uhrzeit einer Bedingung verstellt wird: gespeichert wird durch Loeschen
und Neueinfuegen, und automations.changed ruehrt sich nicht, weil sich in
der Zeile selbst nichts aendert. Der Runner lief dann bis zum naechsten
Neustart mit der alten Uhrzeit weiter - ohne dass irgendwo etwas
schieflief, er wusste es schlicht nicht besser.

Jetzt geht der Inhalt mit ein, als Summe der CRC32 je Zeile. Billig genug
fuer die Pruefung alle 30 Sekunden. Neu dabei sind die Aktionsparameter,
die vorher gar nicht drinstanden - eine geaenderte Zielhoehe einer
Jalousie schlug also ebenso wenig durch.

Nachgestellt mit einer Automatik ohne Aktionen: vorher blieb eine reine
Bedingungsaenderung wirkungslos, danach loeste sie zwei Sekunden nach der
Zielminute aus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 19:27:10 +02:00
co-authored by Claude Opus 5
parent 05ce194478
commit df2765b16a
+38 -4
View File
@@ -138,16 +138,50 @@ class Regelwerk:
@staticmethod @staticmethod
def signatur_lesen(db): def signatur_lesen(db):
""" """
Woran der Runner merkt, dass er neu laden muss. `changed` allein Woran der Runner merkt, dass er neu laden muss.
genuegt nicht: eine geloeschte Automatik veraendert den groessten
Zeitstempel nicht. Deshalb zaehlen die Zeilen mit - auch die der `changed` allein genuegt nicht: eine geloeschte Automatik veraendert
Geraetetabellen, damit ein Discovery-Lauf ebenfalls durchschlaegt. den groessten Zeitstempel nicht. Die Zeilen zu zaehlen genuegt aber
auch nicht - die Weboberflaeche speichert eine Automatik, indem sie
deren Bedingungen und Aktionen loescht und gleich wieder einfuegt. Wer
nur die Uhrzeit einer Bedingung verstellt, aendert damit weder die
Zeilenzahl noch `changed`: ON UPDATE stoesst nur an, wenn sich in
automations wirklich eine Spalte aendert, und Name, Stockwerk und
Zeitfenster stehen ja noch genauso da. Der Runner lief dann bis zum
naechsten Neustart mit der alten Uhrzeit weiter, ohne dass irgendwo
etwas schieflief - er wusste es schlicht nicht besser.
Deshalb geht jetzt der Inhalt mit ein, als Summe der CRC32 je Zeile.
Das ist kein Hash mit Sicherheitsanspruch, sondern ein billiger
Fingerabdruck - er wird alle paar Sekunden gebildet und darf nichts
kosten. Zwei Aenderungen, die sich in der Summe gegenseitig aufheben,
sind theoretisch denkbar und praktisch nicht zu erwarten. Die
Zeilenzahl steht trotzdem daneben, damit eine geloeschte und eine neu
angelegte Zeile nicht zufaellig gleich viel ergeben.
Neu dabei sind die Aktionsparameter. Sie standen vorher gar nicht
drin: eine geaenderte Zielhoehe einer Jalousie schlug also ebenso
wenig durch.
Die Zeilenzahlen der Geraetetabellen bleiben, damit ein
Discovery-Lauf weiterhin ein Neuladen ausloest.
""" """
with db.cursor() as c: with db.cursor() as c:
c.execute("""SELECT (SELECT COUNT(*) FROM automations) AS a, c.execute("""SELECT (SELECT COUNT(*) FROM automations) AS a,
(SELECT UNIX_TIMESTAMP(MAX(changed)) FROM automations) AS t, (SELECT UNIX_TIMESTAMP(MAX(changed)) FROM automations) AS t,
(SELECT COUNT(*) FROM automation_conditions) AS b, (SELECT COUNT(*) FROM automation_conditions) AS b,
(SELECT COALESCE(SUM(CRC32(CONCAT_WS(':',
id, automation_id, group_no, position,
state_id, operator, value))), 0)
FROM automation_conditions) AS bs,
(SELECT COUNT(*) FROM automation_actions) AS c, (SELECT COUNT(*) FROM automation_actions) AS c,
(SELECT COALESCE(SUM(CRC32(CONCAT_WS(':',
id, automation_id, position, command_id))), 0)
FROM automation_actions) AS cs,
(SELECT COUNT(*) FROM automation_action_params) AS p,
(SELECT COALESCE(SUM(CRC32(CONCAT_WS(':',
action_id, parameter_id, value))), 0)
FROM automation_action_params) AS ps,
(SELECT COUNT(*) FROM actor_states) AS d, (SELECT COUNT(*) FROM actor_states) AS d,
(SELECT COUNT(*) FROM actor_commands) AS e""") (SELECT COUNT(*) FROM actor_commands) AS e""")
return tuple(sorted(c.fetchone().items())) return tuple(sorted(c.fetchone().items()))