diff --git a/ajax/room.php b/ajax/room.php index 3ce2064..20b9b75 100644 --- a/ajax/room.php +++ b/ajax/room.php @@ -46,6 +46,38 @@ function raumAntwort($data, $code = 200) exit; } +/** + * Die Antwort abschicken, aber weiterarbeiten. + * + * Nach einem Fahrbefehl muss noch jemand abwarten, wo die Jalousie stehen + * bleibt, und das aufschreiben - sonst steht in actor_states bis zum + * naechsten Poll des Runners der alte Wert. Das dem Browser zu ueberlassen + * ging schief: wer das Modal gleich nach dem Fahrbefehl zumachte, nahm den + * einzigen Zuschauer mit. + * + * Dieselbe Bauart wie in ajax/tahoma.php: Content-Length setzen, ausliefern, + * die Verbindung schliessen, weiterlaufen. Ohne FPM bleibt nur, die Puffer zu + * leeren - dann hat der Browser seine Daten zwar, wartet aber trotzdem. + */ +function raumAntwortUndWeiter($data) +{ + http_response_code(200); + header("Content-Type: application/json; charset=utf-8"); + $text = json_encode($data, JSON_UNESCAPED_UNICODE); + header("Content-Length: " . strlen($text)); + echo $text; + // Der Aufrufer ist weg, das Skript soll trotzdem zu Ende kommen. + ignore_user_abort(true); + if (function_exists("fastcgi_finish_request")) { + fastcgi_finish_request(); + return; + } + while (ob_get_level() > 0) { + ob_end_flush(); + } + flush(); +} + // --- Schaltende Aufrufe --------------------------------------------------- if ($_SERVER["REQUEST_METHOD"] === "POST") { $body = json_decode(file_get_contents("php://input"), true); @@ -54,10 +86,20 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { } try { if (($_GET["action"] ?? "") === "command") { - $text = executeCommand(intval($body["command_id"] ?? 0), + $id = intval($body["command_id"] ?? 0); + $text = executeCommand($id, is_array($body["params"] ?? null) ? $body["params"] : [], !empty($body["probe"])); - raumAntwort(["ok" => $text]); + // Bei einem Tahoma-Geraet ist mit dem Absenden noch nichts + // festgehalten: die Box meldet die neue Stellung erst, wenn die + // Fahrt zu Ende ist. Also Antwort raus und in Ruhe nachsehen. + $url = empty($body["probe"]) ? tahomaUrlZuKommando($id) : null; + if ($url === null) { + raumAntwort(["ok" => $text]); + } + raumAntwortUndWeiter(["ok" => $text]); + tahomaNachlesen($url); + exit; } if (($_GET["action"] ?? "") === "temp") { // Bewusst derselbe Weg wie im alten Thermostat-Modal: das Topic diff --git a/restricted/commands.php b/restricted/commands.php index e578a73..71cf54c 100644 --- a/restricted/commands.php +++ b/restricted/commands.php @@ -139,6 +139,21 @@ function executeCommand($commandId, $werte, $probe = false) throw new RuntimeException("Für " . $cmd["actor_name"] . " gibt es keinen Weg zum Gerät."); } +/** + * Die Tahoma-Geraete-URL hinter einem Kommando, null wenn es keine ist. + * Damit der Aufrufer nach dem Schalten weiss, ob sich ein Nachlesen lohnt. + */ +function tahomaUrlZuKommando($commandId) +{ + $cmd = loadCommand($commandId); + $pin = strval($GLOBALS["tahoma_PIN"] ?? ""); + if (!$cmd || $pin === "" + || strpos(strval($cmd["actor_url"]), "://" . $pin . "/") === false) { + return null; + } + return $cmd["actor_url"]; +} + /** Werte in der Reihenfolge der Parameter, fehlende als leer. */ function parameterWerte($cmd, $werte) { @@ -445,6 +460,49 @@ function warteAufJalousie($deviceUrl, $neigungZiel, $schliessungZiel = null) return false; } +/** + * Nach einem Kommando abwarten, bis das Geraet steht, und seinen Stand in + * actor_states festhalten. + * + * Gedacht fuer den Aufruf *nach* fastcgi_finish_request(): der Browser hat + * seine Antwort dann laengst, und ob jemand noch zusieht, spielt keine + * Rolle mehr. Genau daran scheiterte die Verfolgung im Modal - wer es + * direkt nach dem Fahrbefehl zumachte, liess niemanden zurueck, der den + * Endstand haette aufschreiben koennen. Bis zum naechsten Poll des Runners + * stand dann der alte Wert in der Tabelle, fuenf Minuten lang. + * + * Gewartet wird nur auf core:MovingState, ohne Zielwerte - welche das + * waeren, weiss hier niemand, und nicht jedes Kommando loest eine Fahrt + * aus. Der Vorlauf faengt die Traegheit der Box ab: kurz nach dem Kommando + * steht dort noch "faehrt nicht". + * + * Anders als die Warteschleife der Kugelschreiber-Mechanik laeuft das hier + * erst, wenn das ganze Manoever durch ist - beide Schwenks sind geschickt, + * die Fahrt, auf die gewartet wird, ist die letzte. + */ +function tahomaNachlesen($deviceUrl) +{ + $start = time(); + $gestartet = false; + $zustaende = null; + while (time() - $start < JALOUSIE_WARTE_SEKUNDEN) { + sleep(2); + $gelesen = tahomaZustaende($deviceUrl); + if ($gelesen === null) { + continue; + } + $zustaende = $gelesen; + if (($zustaende["core:MovingState"] ?? null) === true) { + $gestartet = true; + continue; + } + if ($gestartet || time() - $start >= JALOUSIE_VORLAUF_SEKUNDEN) { + break; + } + } + return $zustaende === null ? 0 : tahomaZustaendeSchreiben($deviceUrl, $zustaende); +} + /** * Die Geraete-URL eines Aktors, null wenn es ihn nicht gibt oder er nicht an * der Tahoma-Box haengt.