I am starting to play a little with blockly and I am starting with simple rules involving motion sensors and timers. I have a rule that when a motion sensor is activated I turn on a light and after a certain amount of time it is turned off, or if in the meantime the sensor gets triggered again the time is rescheduled. This is working fine with the following blocks:
So I wanted to evolve this rule and instead of a fixed time (in the example 10 seconds) I would like to put my own defined times, for each light in an Number Item so that if I need to change them I do not have to touch the rule anymore, I do it from the UI/Phone app. From my readings and tests I do not see that is possible to insert any block in the time definition of the timer (blue square rectangle in my picture).
Is this correct? Or is there any workaround for this to be done with blockly?
Thank you in advanced for all your help.
You can only add numbers, if you try with an item state, whats a string, that will not work.
What you can try as a workaround storing an items state into a variable and than adding the variable to the block.
Hello @Matze0211 ,
Thank you very much for your fast reply. I almost am certain that I tried before but I did not manage to put the variable in place of the fixed time. Just now I tried it and it works perfectly.
Thanks again for your valuable help.
Out of interest, what does the
cancel timer block do at the end of the timer-block code? I was under the impression that when the timer had finished running its code it will terminate anyway (i.e. self cancel). But maybe I’m missing something?
Hello @hafniumzinc ,
Well that is in fact a good question, because I also wondered if it is necessary. I decided to put it in just in case. Afterwards I looked at the code generated by blockly and before actually performing the cancel timer block the code generated verifies if the timer is not undefined and if not it cancels it. So I guess it does not hurt having it there, but if someone could confirm it would definitely be an improvement to the whole block.
Take it away and see?
Cancelling a timer just removes it from the scheduler’s to-do list, it doesn’t affect the block of code it is supposed to execute. That’s just as well, since we are in the middle of executing it. So technically it’s an error, there is no longer a to-do entry to be removed. The hidden blockly machinery deals with that gracefully, but it’s just a waste of time in this instance.
The cancel block does need to deal with it nicely, because it is possible for us to try and cancel a timer from somewhere else altogether, and it just might happen to be executing at that moment.
And we do not want to forbid putting a cancel block inside a timer - maybe it’s some other timer we want to kill.
Hello @rossko57 ,
Thanks for stepping in. Understood and it makes perfect sense.
Removed and working as expected.
Just a bit of warning for everyone on this thread and future readers.
At some point Blockly is going to have to migrate from using Nashorn as the language it compiles to to JS Scripting. When this happens, attempting to cancel the timer like this will actually generate an error. So not only is it pointless but eventually it will lead to errors.
[Complicated explanation, skip if you don’t care]
GraalVM, the engine upon which JS Scripting is implemented, has a hard “no multithreaded operations” limitation. Operations like
Thread.sleep cause an error.
Here is where it gets a bit unexpectedly weird. As we should all know by now, each rule in OH 3 has its very own thread to run in. What is not as well known is that Timers execute in the same thread as the rule that it created it. And cancelling a timer is potentially a multithreaded operation.
As a result we have the following to potential problems:
if a timer is running when a rule needs to run, or a rule is running when a timer needs to run, it will fail because both will be trying to use the same thread which is not allowed
if a timer tries to cancel itself inside its called lambda, that’s a multithreaded operation.
Hello @rlkoshak ,
Thanks for the detailed explanation and valuable input as always. Just for future viewers and reference here is the final blockly:
Hopefully I misunderstand this, because it would have implications on the timer block with the retriggering options.
I have a rule which monitors the power input to my dishwasher. Inside that rule is a timer which activates if the power input is below a certain threshold. If the rule is triggered again, and the power is above the threshold, then I cancel the timer; if not I leave it run. But does the above mean that when my rule is retriggered, if the timer is already running, it won’t work because the timer is blocking the thread?
It does have implications.
Yes. But keep in mind what it means for a timer to be running. It’s only running when it’s actually executing the code that makes up the function passed to it. In the Blockly context, the blocks between the after and reschedule part of the timer block.
Unless you are making some sort of long running call in the timer body, like a sleep or a call to executeCommandLine with a timeout, that code will only be running for a dozen milliseconds or so. The chances of collision are pretty low for most cases, but not impossible.
The issue is know about and explorations are taking place to see what, if anything can be done about it. For example, one proposed solution is to queue up the timers the same way that the rule triggers are queued up so if the thread is busy, the timer/rule will wait until it’s released. A few milliseconds late is better than crashing with an exception.
OK, so we’re quite rightly choosing words carefully:
- Timer active = the timer-block has been reached, and is counting down.
- Timer running = the timer-block has finished counting down, and is now executing the code within it.
#1 is not affected by this threading issue.
#2 is affected by this threading issue.
Understood - less of an issue than I feared!
With timers there’s no choice.
It definitely is less of an issue but still something that needs to be addressed at some point. I discovered this when I coded up my TimerMgr library where I simply used to just cancel the timer when asked. I had a rule where a timer is set to cancel all the timers in the manager though, and that timer was itself in the manager so it ended up cancelling itself causing an exception. I also ran into this in the test code I created to verify that TimerMgr and other library classes I’ve published work. I had to be very careful to not have timers collide.
I might add a check in TimerMgr to ensure that no two timers are scheduled to go off a the same time. Eventually I’d like to port TimerMgr and the rest to Blockly too, but I think I’ll wait for Blockly to support ECMAScript 2021 so I don’t have to keep two versions of the code around.