Runner liest nach einem Tahoma-Kommando gleich nach

Bewegt eine Automatik eine Jalousie, stand ihr alter Stand bis zu fuenf
Minuten in actor_states - so lange dauert es bis zur naechsten Runde von
poll_tahoma. Im Raum-Modal sah man dann eine Jalousie, die es so nicht
mehr gab.

Der Versandfaden wartet nach dem Kommando jetzt das Ende der Fahrt ab und
meldet den Stand des Geraets zurueck; geschrieben wird er wie alles andere
im Hauptfaden. Gewartet wird nur auf core:MovingState, ohne Zielwerte -
welche das waeren, weiss an dieser Stelle niemand, und nicht jedes
Kommando loest eine Fahrt aus. Dass der Faden dabei steht, ist gewollt:
das naechste Kommando an dieselbe Jalousie darf ohnehin erst nach der
Fahrt kommen.

Transport.nachlesen() gibt es fuer alle, liefert aber nur bei Tahoma
etwas. MQTT-Geraete melden sich von selbst, HTTP und WLED werden jede
Minute gefragt.

Nebenbei: das Lesen aller Zustaende eines Geraets steckt jetzt in
_zustaende(), die Zuordnung zu den Messwert-Nummern in _zuordnen().
zustaende_lesen() und die Warteschleife der Kugelschreiber-Mechanik
benutzen beide dasselbe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 12:18:20 +02:00
co-authored by Claude Opus 5
parent 2e155ea4a7
commit 797d3dd6d9
2 changed files with 91 additions and 21 deletions
+71 -21
View File
@@ -108,6 +108,18 @@ class Transport:
def senden(self, aktion):
raise NotImplementedError
def nachlesen(self, actor_url):
"""
Was das Geraet direkt nach einem Kommando meldet.
Fuer die meisten Transporte nichts: MQTT-Geraete melden sich von
selbst, HTTP und WLED werden ohnehin jede Minute gefragt. Nur bei
Tahoma lohnt es sich - dort liegen fuenf Minuten zwischen zwei
Abfragen, und eine Jalousie steht so lange mit ihrem alten Stand in
der Tabelle.
"""
return {}
# ---------------------------------------------------------------------------
# MQTT
@@ -446,27 +458,70 @@ class TahomaTransport(Transport):
"""
self.kombigeraete = set(urls)
def _zustaende(self, actor_url):
"""
Alle Zustaende eines Geraets, oder None wenn die Box nicht antwortet.
Ein leeres Dict waere hier falsch: es hiesse "kennt keine Zustaende"
und liesse eine Warteschleife durchlaufen, statt sie warten zu lassen.
"""
try:
antwort = self.requests.get(
self.basis + "/setup/devices/" + quote(actor_url, safe="") + "/states",
headers=self._kopf(), timeout=self.timeout, verify=False)
return {z["name"]: z.get("value") for z in antwort.json()}
except Exception as fehler:
logger.debug("Tahoma %s nicht erreichbar: %s", actor_url, fehler)
return None
def _zuordnen(self, actor_url, zustaende):
"""Die gelesenen Felder den Messwert-Nummern dieses Geraets zuordnen."""
werte = {}
for s in self.states:
if s["actor_url"] == actor_url and s["state_url"] in zustaende:
werte[s["id"]] = str(zustaende[s["state_url"]])
return werte
def zustaende_lesen(self):
if not self.token:
return {}
werte = {}
nach_geraet = {}
for s in self.states:
nach_geraet.setdefault(s["actor_url"], []).append(s)
for geraet, states in nach_geraet.items():
try:
antwort = self.requests.get(
self.basis + "/setup/devices/" + quote(geraet, safe="") + "/states",
headers=self._kopf(), timeout=self.timeout, verify=False)
zustaende = {z["name"]: z.get("value") for z in antwort.json()}
except Exception as fehler:
logger.debug("Tahoma %s nicht erreichbar: %s", geraet, fehler)
for geraet in {s["actor_url"] for s in self.states}:
zustaende = self._zustaende(geraet)
if zustaende is None:
continue
for s in states:
if s["state_url"] in zustaende:
werte[s["id"]] = str(zustaende[s["state_url"]])
werte.update(self._zuordnen(geraet, zustaende))
return werte
def nachlesen(self, actor_url):
"""
Nach einem Kommando abwarten, bis das Geraet steht, und dann seinen
Stand melden - statt ihn bis zur naechsten Runde (poll_tahoma, fuenf
Minuten) alt in der Tabelle stehen zu lassen.
Gewartet wird nur auf core:MovingState, ohne Zielwerte: welche das
waeren, weiss hier niemand, und ein Kommando muss auch keine Fahrt
ausloesen. Der Vorlauf faengt die Traegheit der Box ab, danach gilt
ein "faehrt nicht" als Stillstand.
"""
if not self.token:
return {}
start = time.time()
gestartet = False
zustaende = None
while time.time() - start < self.JALOUSIE_WARTE_SEKUNDEN:
time.sleep(2)
gelesen = self._zustaende(actor_url)
if gelesen is None:
continue
zustaende = gelesen
if zustaende.get("core:MovingState") is True:
gestartet = True
continue
if gestartet or time.time() - start >= self.JALOUSIE_VORLAUF_SEKUNDEN:
break
return self._zuordnen(actor_url, zustaende) if zustaende else {}
def senden(self, aktion):
if not self.token:
raise RuntimeError("Kein Tahoma-Token in der config.ini")
@@ -547,13 +602,8 @@ class TahomaTransport(Transport):
gestartet = False
while time.time() - start < self.JALOUSIE_WARTE_SEKUNDEN:
time.sleep(2)
try:
antwort = self.requests.get(
self.basis + "/setup/devices/" + quote(actor_url, safe="") + "/states",
headers=self._kopf(), timeout=self.timeout, verify=False)
z = {x.get("name"): x.get("value") for x in antwort.json()}
except Exception as fehler:
logger.debug("Zustand nicht lesbar: %s", fehler)
z = self._zustaende(actor_url)
if z is None:
continue
if z.get("core:MovingState") is True:
gestartet = True