Jalousien: Neigung ueber 30 % nur mit Umweg ueber 0 %

Die Aussenjalousien haben eine Kugelschreiber-Mechanik: beim Herunterfahren
stehen die Lamellen bei etwa 30 %. In Richtung 0 % laesst sich von jeder
Stellung aus direkt neigen; darueber hinaus rastet die Mechanik nur um, wenn
sie vorher einmal auf 0 % war. Ein "Neigung 80 %" ohne diesen Umweg bleibt
wirkungslos.

Beide Versender wissen das jetzt: restricted/commands.php fuer die
Bedienung im Raum-Modal, transports.py fuer den Runner. Bei setOrientation
mit einem Ziel ueber 30 % geht erst eine 0 raus, dann wird gewartet, bis die
Box diese 0 auch meldet, und erst danach der eigentliche Wert. Der Umweg
gehoert dorthin und nicht in die Bedienung - sonst muesste ihn jeder kennen,
der eine Jalousie anspricht, auch beim Anlegen einer Automatik.

Gewartet wird auf core:SlateOrientationState und nicht auf
core:MovingState: bei Neigungsfahrten meldet die Box waehrend der ganzen
Bewegung "faehrt nicht". Genau darauf war ich vorher hereingefallen.

Zwei Messungen aus der Erprobung stecken in den Konstanten: von 100 % auf
0 % braucht eine Jalousie gut fuenfzehn Sekunden, deshalb die Grenze von
25 s. Im Versuch ging ein "Neigung 100 %" von 79 % aus nach 14 s durch -
echtes Warten, nicht die Notbremse.

Dabei ein Fehler, den ausgerechnet das Ziel 0 verdeckt haette: die
PHP-Leseroutine liefert null, wenn die Abfrage fehlschlaegt, und
intval(null) ist 0. Die Warteschleife hielt einen fehlgeschlagenen
Lesevorgang damit fuer "Ziel erreicht" und brach sofort ab - der Umweg fand
also gar nicht statt, es sah nur so aus. In der Python-Fassung ist
dieselbe Stelle jetzt ebenfalls ausdruecklich gegen ein fehlendes Feld
abgesichert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-31 19:18:55 +02:00
co-authored by Claude Opus 5
parent 98a1da6cd5
commit 1b9bbc9857
2 changed files with 171 additions and 24 deletions
+106 -19
View File
@@ -26,6 +26,26 @@ require_once(__DIR__ . "/../ajax/phpMQTT.php");
const MQTT_BROKER = "localhost";
const MQTT_PORT = 1883;
/**
* Bis zu dieser Neigung fahren die Aussenjalousien direkt.
*
* Sie haben eine Kugelschreiber-Mechanik: beim Herunterfahren stehen die
* Lamellen bei etwa 30 %. In Richtung 0 % laesst sich von jeder Stellung aus
* direkt neigen; ueber 30 % hinaus muss die Mechanik erst einmal auf 0 %
* zurueck, sonst rastet sie nicht um. Ein "Neigung 80 %" ohne diesen Umweg
* bleibt wirkungslos.
*
* Dasselbe steht in restricted/autoActions/transports.py - beide Versender
* brauchen es, und beide muessen es wissen.
*/
const NEIGUNG_DIREKT_MAX = 30;
/**
* So lange wird hoechstens auf das Ende einer Neigungsfahrt gewartet.
* Gemessen braucht eine Jalousie von 100 % auf 0 % gut fuenfzehn Sekunden.
*/
const NEIGUNG_WARTE_SEKUNDEN = 25;
/** Verbindung zur Geraetedatenbank. */
function commandDb()
{
@@ -210,30 +230,18 @@ function sendeHttp($cmd, $werte, $probe)
return $cmd["actor_name"] . ": " . $cmd["command_name"];
}
/**
* Tahoma: exec/apply an die Box - dieselbe Form wie in ajax/tahoma.php, nur
* ohne dessen Sonderbehandlung fuer "faehrt gerade".
*/
function sendeTahoma($cmd, $werte, $probe)
/** Ein Kommando an die Tahoma-Box schicken. */
function tahomaKommando($deviceUrl, $name, $parameter)
{
$parameter = [];
foreach (parameterWerte($cmd, $werte) as $p) {
// Die Box erwartet Zahlen als Zahlen, Text als Text.
$parameter[] = is_numeric($p["wert"]) ? $p["wert"] + 0 : $p["wert"];
}
$rumpf = [
"label" => "Raumbedienung",
"actions" => [[
"deviceURL" => $cmd["actor_url"],
"commands" => [["name" => $cmd["command_url"], "parameters" => $parameter]],
"deviceURL" => $deviceUrl,
"commands" => [["name" => $name, "parameters" => $parameter]],
]],
];
$ziel = "https://gateway-" . $GLOBALS["tahoma_PIN"]
. ":8443/enduser-mobile-web/1/enduserAPI/exec/apply";
if ($probe) {
return "Tahoma: " . json_encode($rumpf, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
$ch = curl_init($ziel);
$ch = curl_init("https://gateway-" . $GLOBALS["tahoma_PIN"]
. ":8443/enduser-mobile-web/1/enduserAPI/exec/apply");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json",
"Authorization: Bearer " . $GLOBALS["tahoma_token"]]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($rumpf));
@@ -247,5 +255,84 @@ function sendeTahoma($cmd, $werte, $probe)
if ($code >= 400 || $code === 0) {
throw new RuntimeException("Tahoma antwortete mit " . $code . ": " . substr(strval($antwort), 0, 120));
}
return $cmd["actor_name"] . ": " . $cmd["command_name"];
}
/** Einen Zustandswert eines Tahoma-Geraets lesen, null wenn unbekannt. */
function tahomaZustand($deviceUrl, $feld)
{
$ch = curl_init("https://gateway-" . $GLOBALS["tahoma_PIN"]
. ":8443/enduser-mobile-web/1/enduserAPI/setup/devices/"
. rawurlencode($deviceUrl) . "/states");
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $GLOBALS["tahoma_token"]]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$antwort = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!is_array($antwort)) {
return null;
}
foreach ($antwort as $zustand) {
if (($zustand["name"] ?? "") === $feld) {
return $zustand["value"];
}
}
return null;
}
/**
* Wartet, bis die Neigung den Zielwert erreicht hat.
*
* Auf core:MovingState ist bei Neigungsfahrten kein Verlass - gemessen hat
* die Box waehrend einer solchen Fahrt kein einziges Mal "faehrt" gemeldet.
* Der Zustandswert selbst ist die verlaessliche Auskunft.
*/
function warteAufNeigung($deviceUrl, $ziel)
{
for ($i = 0; $i < NEIGUNG_WARTE_SEKUNDEN; $i++) {
sleep(1);
$wert = tahomaZustand($deviceUrl, "core:SlateOrientationState");
// null heisst "nicht gelesen". intval() daraus waere 0 - und damit
// ausgerechnet beim Ziel 0 ein falsches Erfolgssignal, das die
// Warteschleife sofort beendet.
if ($wert !== null && intval($wert) === $ziel) {
return true;
}
}
return false;
}
/**
* Tahoma: exec/apply an die Box - dieselbe Form wie in ajax/tahoma.php, nur
* ohne dessen Sonderbehandlung fuer "faehrt gerade".
*/
function sendeTahoma($cmd, $werte, $probe)
{
$parameter = [];
foreach (parameterWerte($cmd, $werte) as $p) {
// Die Box erwartet Zahlen als Zahlen, Text als Text.
$parameter[] = is_numeric($p["wert"]) ? $p["wert"] + 0 : $p["wert"];
}
// Kugelschreiber-Mechanik: ueber NEIGUNG_DIREKT_MAX hinaus rastet die
// Mechanik nur um, wenn sie vorher einmal auf 0 % war. Der Umweg gehoert
// hierher und nicht in die Bedienung - sonst muesste ihn jeder kennen,
// der eine Jalousie anspricht.
$umweg = ($cmd["command_url"] === "setOrientation"
&& count($parameter) === 1
&& $parameter[0] > NEIGUNG_DIREKT_MAX);
if ($probe) {
$text = "Tahoma: " . $cmd["command_url"] . " " . json_encode($parameter);
return $umweg ? $text . " (mit Umweg über Neigung 0)" : $text;
}
if ($umweg) {
tahomaKommando($cmd["actor_url"], "setOrientation", [0]);
warteAufNeigung($cmd["actor_url"], 0);
}
tahomaKommando($cmd["actor_url"], $cmd["command_url"], $parameter);
return $cmd["actor_name"] . ": " . $cmd["command_name"]
. ($umweg ? " (über 0 % geneigt)" : "");
}