NEWS
Mehrere (separate) Timeouts in einer Statemachine
-
Hallo,
kann mir jemand sagen, warum der 1. Timeout in meiner Statemachine wie erwartet funktioniert und die nachfolgenden 2 (in anderen States) wie nicht existent durchrauschen
Anstatt den Status von 2 auf 3 erst nach 5 Min zu ändern, geschieht dies direkt nach der Änderung von Status 1 auf 2

ICh habe bereits die Namen so geändert, daß sich das erste Zeichen unterscheidet (half auch nichts)
und habe den "Stop Timeout" in den Timeout selber verschoben (war zuvor im nächsten State)
Auch wenn ich mir den JS Code zum Blockly anschaue, kann ich keinen Grund erkennen, weshalb die weiteren Timeouts NICHT mehr funktionieren - der 1. aber schon (und auch wiederholt - als nach erneutem Start der Ablaufsteuerung)Hier der JS-Code zu dieser Funktion
async function EinfahrtLicht_StatusHandle() {
// Status
// 0 = Aus
// 1 = Start / Einschalten
// 2 = Stufe 1
// 3 = Stufe 2
// 4 = Ausschalten
if (einfahrtLicht_status == 0) {
// Idle Status / nichts machen
} else if (einfahrtLicht_status == 1) {
setState('alexa2.0.Smart-Home-Devices.c9a608f4-03de-4952-8125-afec97fdb14a.powerState' /* powerState /, true);
setState('alexa2.0.Smart-Home-Devices.c9a608f4-03de-4952-8125-afec97fdb14a.brightness' / brightness /, 100);
my_1timeout = setTimeout(async () => {
my_1timeout = null;
await EinfahrtLicht_StatusNext();
(() => { if (my_1timeout) { clearTimeout(my_1timeout); my_1timeout = null; }})();
}, 300000);
} else if (einfahrtLicht_status == 2) {
setState('alexa2.0.Smart-Home-Devices.c9a608f4-03de-4952-8125-afec97fdb14a.brightness' / brightness /, 20);
my_2timeout = setTimeout(async () => {
my_2timeout = null;
await EinfahrtLicht_StatusNext();
(() => { if (my_2timeout) { clearTimeout(my_2timeout); my_2timeout = null; }})();
}, 300000);
} else if (einfahrtLicht_status == 3) {
setState('alexa2.0.Smart-Home-Devices.c9a608f4-03de-4952-8125-afec97fdb14a.brightness' / brightness /, 0);
my_3timeout = setTimeout(async () => {
my_3timeout = null;
await EinfahrtLicht_StatusNext();
(() => { if (my_3timeout) { clearTimeout(my_3timeout); my_3timeout = null; }})();
}, 2000);
} else {
setState('alexa2.0.Smart-Home-Devices.c9a608f4-03de-4952-8125-afec97fdb14a.powerState' / powerState /, false);
setState('alexa2.0.Smart-Home-Devices.c9a608f4-03de-4952-8125-afec97fdb14a.brightness' / brightness */, 100);
await EinfahrtLicht_StatusNext();
}
}--
Liebe Grüße
HaJoLu069 -
@hajolu069 Meine Befürchtung ist jetzt, daß Blockly NICHT mit mehreren "Sonst Falls" Fällen umgehen kann und max. 1 solchen "Sonst Falls" richtig behandeln kann, der Rest läuft unter "Sonst"
muß schauen, woher ich ein "Switch" bekomme und wie ich es zu nutzen habe - vermute es ist das "Prüfe xxx"
nur wie ich dort die einzelnen Prüfungen angeben muß, muß ich jetzt erstmal herausfinden.Sobald ich mehr weiß, melde ich mich wieder un löse hier alles auf.
--
Liebe Grüße
HaJoLu069 -
@hajolu069 sagte: Blockly NICHT mit mehreren "Sonst Falls" Fällen umgehen kann
Der Javascript-Code zeigt, dass Blockly es kann.
Was macht die Funktion EinfahrtLicht_StatusNext?
Wodurch wird die gezeigte Funktion aufgerufen? -
@hajolu069 sagte: woher ich ein "Switch" bekomme
Versuche es mal so:

Blockly setzt die Timer-Variable bei Ablauf automatisch auf null.
-
@paul53 Danke - das ist ein sehr guter Tipp
denn meine Vermutung ist und bleibt, daß anscheinend nicht mehr als 1 Timeout innerhalb einer Funktion wirklich funktioniertdenn bisher haben wir Timeouts kaum in unserem Scripten im Einsatz
Daa Blockly die Timer-Variable bei Ablauf automatisch auf Null setzt, davon war ich ausgegangen - mein erster Ansatz war 3 Timer (in jedem Status einer) OHNE ein Stop
Deine andere Fragen beantworte ich Dir gleich.
-
@hajolu069 sagte in Mehrere (separate) Timeouts in einer Statemachine:
denn meine Vermutung ist und bleibt, daß anscheinend nicht mehr als 1 Timeout innerhalb einer Funktion wirklich funktioniert
Das ist eindeutig falsch.. Ich habe Skripte mit mehreren Timeouts in Funktionen im Einsatz.
A.
-
@paul53 Die StatusNext macht nichts anderes als "bei Änderung" die lokale Variable und anschließend die globale Variable (0_userdata.0-Bereich) auf den angeforderten Wert zu ändern
UND damit ist auch die 2. Frage beantwortet, denn die gezeigte Funktion wird durch den Trigger bei Änderung von der globalen Variable aufgerufenmeine Debug-Informationen zeigen auch, daß die Ablaufsteuerung / Statemachine sauber funktioniert - bis auf die "Timeouts"
Daher werde ich Deinen Ansatz ausprobieren und dann Rückmeldung geben.
Ist denn irgendwo schon bekannt geworden, daß ein abgelaufener Timer in einem Script einen 2. Timer nicht mehr zulässt ?
Man muß es ja nur wissen, um "Workarounds" zu entwickeln - mein Ansatz wäre dies dann auch über eine "globale Variable" zu lösen, die man auf "true" setzt, um einen Timeout aufzuziehen (ähnlich Deiner Lösung - nur eben in einem Trigger, der nur bei Änderung auf True Aktionen auslöst und als erstes sich selber auf False ändert - die Dauer kann ja dann ruhig weiterhin lokal bleiben, weil globale Zugriffe deutlich mehr Zeit und Speicher kosten)Dieser "Workaround mit globaler Variable" hätte den Charme, daß man an beliebigen Stellen im Code einen neuen Timeout aufziehen kann - wenn man dann unterschiedliches "Handling" durch den Timeout benötigt, muß man dieses auch wieder über eine "Handle"-Funktion abarbeiten und die Stati (oder für jene, die Namen brauchen auch namentliche Stati) in diesem Handle dann ausführen, während man zuvor (vor Setzen des Timeouts) eine lokale Variable beschreibt, die den "Status" (also die später auszuführenden Schritte im "Handle") beinhaltet.
Klingt vielleicht kompliziert, ist es aber nicht wirklich.
-
@hajolu069 sagte in Mehrere (separate) Timeouts in einer Statemachine:
ist es aber nicht wirklich.
hat aber einige Pitfalls.
ggf. wird der Wert in den Datenpunkt erst/schon geschrieben, wenn die nächste Stufe abgefragt wird. -
@asgothian Das ist schön für Dich - sagt mir nur, daß es NICHT GRUNDSÄTZLICH ein Problem ist, sondern nur bei mir - hilft mir aber bei meiner Fehlersuche (denn die States werden sauber (und auch jeweils nur EINMALIG durchlaufen - und damit sauber gewechselt und ausgeführt) rein gar nichts, weil Fakt ist, bei mir wird NUR der 1. Timeout ordentlich ausgeführt und die weiteren ignoriert und der dort enthaltene Code SOFORT (OHNE Wartezeit) ausgeführt.
@Asgothian bist Du Dir denn auch sicher, daß Deine multiplen Timeouts so sind, daß 1 Timeout bereits abgelaufen ist, ehe die anderen gestartet werden sollen - das ist nämlich bei mir in der Programmierung der Fall.
Wenn Du Deine Timeouts vielleicht startest EHE auch nur einer davon abgelaufen ist, könnte es sein, daß dieser "Fall" richtig gut funktioniert - nur im Falle von einem "abgelaufenen Timeout" macht Blockly Fehler beim Starten von weiteren Timeouts und sie gelten dann alle als bereits abgelaufen (oder werde gar nicht erst gestartet, sondern "ignoriert").
Dein Fall wäre z.B. der Fall, wenn Du mit "geschachtelten" Timeouts arbeitest.Und vielleicht ist das ja auch die Ursache für MEIN Problem, daß Blockly so programmiert wurde, daß bei mehreren Timerouts, davon ausgegangen wurde, daß diese in sich verschachtelt sind und der 1. Timeout als "Master" für die anderen Timeouts angesehen wird und diese dann automatisch beendet, wenn er endet (nur so eine Vermutung eines Programmierers mit 25 Jahren Programmier-Erfahrung in div. Sprachen [angefangen bei Assembler über C bis hin zu C#, aber auch diverse Script-Sprachen - auch WebAssembly-Erfahrung ist dabei und jahrelanges embeded Programmieren]).
-
@hajolu069 sagte in Mehrere (separate) Timeouts in einer Statemachine:
daß diese in sich verschachtelt sind und der 1. Timeout als "Master" für die anderen Timeouts angesehen wird und diese dann automatisch beendet, wenn er endet
falsche Vermutung!
dann startet er erst due inneren Timeouts -
@hajolu069 Wer ruft denn EinfahrtLicht_StatusHandle auf?
Initial durch einen Datenpunkt-Trigger das Wecken aus Idle, und das Weiterschalten dann über EinfahrtLicht_StatusNext? -
@hajolu069 sagte in Mehrere (separate) Timeouts in einer Statemachine:
@Asgothian bist Du Dir denn auch sicher, daß Deine multiplen Timeouts so sind, daß 1 Timeout bereits abgelaufen ist, ehe die anderen gestartet werden sollen - das ist nämlich bei mir in der Programmierung der Fall.
Wenn Du Deine Timeouts vielleicht startest EHE auch nur einer davon abgelaufen ist, könnte es sein, daß dieser "Fall" richtig gut funktioniert - nur im Falle von einem "abgelaufenen Timeout" macht Blockly Fehler beim Starten von weiteren Timeouts und sie gelten dann alle als bereits abgelaufen (oder werde gar nicht erst gestartet, sondern "ignoriert").
Dein Fall wäre z.B. der Fall, wenn Du mit "geschachtelten" Timeouts arbeitest.In kurz, ja. Allerdings bin ich nicht zu Hause und kann / will das Skript nicht posten. Deswegen hab ich Deinen Ansatz vereinfacht und mit Log-Ausgaben nachgebaut.
Skript:
Log:
2025-09-28 13:09:50.706 - info: javascript.0 (615) script.js.Skript_1: registered 0 subscriptions, 0 schedules, 0 messages, 0 logs and 0 file subscriptions 2025-09-28 13:09:51.707 - info: javascript.0 (615) script.js.Skript_1: debug from first timeout: 2 2025-09-28 13:09:52.209 - info: javascript.0 (615) script.js.Skript_1: debug from second timeout: 3 2025-09-28 13:09:57.211 - info: javascript.0 (615) script.js.Skript_1: debug from third timeout: 4 2025-09-28 13:09:57.222 - info: javascript.0 (615) script.js.Skript_1: debug from last timeout: 5Die Timestamps im Log zeigen deutlich das alles so ist wie es soll
- Der erste Log-EIntrag kommt 1001 ms nach Start
- der nächste dann 502 ms später
- der dritte dann 5002 ms danach
- und der letzte 11 ms später - so wie die Timeouts auch gesetzt sind.
Dein Problem muss liegen bei
- der Verschachteln von 2 Funktionen (
EinfahrtLicht_statusHandleundEinfahrtLicht_statusNext) - der globalen Variable
einfahrtlicht_status
In dem Umfeld passiert etwas das du uns nicht gezeigt hast welches für den von Dir beschriebenen Effekt verantwortlich ist.
A.
-
@hajolu069 sagte: die lokale Variable und anschließend die globale Variable (0_userdata.0-Bereich) auf den angeforderten Wert zu ändern
UND damit ist auch die 2. Frage beantwortet, denn die gezeigte Funktion wird durch den Trigger bei Änderung von der globalen Variable aufgerufenBegriffserklärung: Die Variable einfahrtLicht_status ist eine globale Variable im Skript und "0_userdata.0-Bereich" ist ein Datenpunkt.
Anstelle des rekursiven Aufrufs der Funktion setzt du den Datenpunkt "0_userdata.0-Bereich" auf den geänderten Wert, der anschließend triggert, um die Funktion aufzurufen. Etwa so:

Der Trigger auf "ist größer als letztes" verhindert den Trigger auf den Wert 0.
EDIT: Als Funktionsparameter ist einfahrtLicht_status eine lokale Variable innerhalb der Funktion:

-
@martinp Ja genau - der "lokale / interne" Status wird beim Ändern auch immer auf einem "Datenpunkt" abgespeichert und DESSEN Änderung ruft dann den Handle auf - wie man so Statemachinen eben baut mit Blockly.
Idle - ist der Initiale Status
und Idle wird automatisch immer dann eingenommen, wenn man über den letzten Status hinaus wechseln wollte - von daher ist StatusNext ein- Speichere neuen Status (aktueller + 1) temporär ab
- prüfe ob temporärer Wert > als maxStatus ist, falls "ja", setze temporären Wert auf 0
- schreibe temporären Wert auf lokale Variable und anschließend auf Datenpunkt-Kopie, damit der Trigger wieder ausgelöst wird
ach ja - und wenn man die LichtSteuerung aufstarten will (Wallbox wurde eingeschaltet oder Lichtschranke hat ausgelöst - wobei eine zeitliche Überprüfung auf Sonnenaufgang-Start und Sonnenuntergang-Ende erfolgt, damit nur bei "Dunkelheit" das Aufstarten aktiviert wird), dann wird der Status auf 1 gesetzt, damit die Ablauf-Steuerung / Statemachine angestoßen wird und ihre Stati bis wieder "Idle" automatisch durchläuft.
-
@hajolu069 sagte: schreibe temporären Wert auf lokale Variable und anschließend auf Datenpunkt-Kopie, damit der Trigger wieder ausgelöst wird
Vorschlag:
