Whenever my entrance door is opened, I want to send a 30 seconds video to my email.
In order to accomplish that, I have a batch file that that checks what is the latest video file my camera captured, waits for 30 seconds (for the recording to end) and email me that file.
In order to execute that batch file, I have created a PHP page on my computer’s local web server, that simply executes the file.
Having read the issue about 5 threads, and the recommendation to use a timer, whenever the entrance door is opened, I execute the following:
Reading many of your other posts and recommendations, about making sure rules run fast, not use sendHttp*Request, etc, and since the batch file is running for ~40 seconds (waiting for the 30 seconds video to finish recording), I am using the following to call the PHP file:
I understand this means that the rule does not wait for the HTTP call, as the call is done in a different thread.
1. Does the long wait for the call (40 seconds) can still cause any issues (slowness, high CPU, etc.)?
I read in many posts you wrote, that there is a limit of 5 rule threads that can run at the same time. 2. Is there a limit on the number of “createTimer” threads that can run at the same time?
Since I don’t really care what is the response the HTTP call gets (the PHP file executes the batch file that sends the video) - 3. would you recommend modifying the HTTP call, so it will not wait that long?
Do you see a problem reducing the HTTP timeout to 1 sec, thus making the HTTP call and receiving the error after 1 second, but still the PHP would run my batch file?
Well, using the 1mS createTimer here is pointless really.
You’re only firing off an HTTP request, whether that triggers a script or makes a bank payment makes no difference to openHAB. It’s only going to wait as long as the timeout to see if anything comes back.
Have you tried 0 out of interest? Not clear from docs if it might work.
Re Timers; you can have as many as you wish awaiting their moment, but I think only two executing their code. Hence the advice not to hog there.
I’d be inclined to do the small work to suppress an expected error message - wood/trees for the next issue.
As rossko57 indicates, this only moves the problem. There are two thread pools, one for Rules that has 5 by default and another for Timers that has 2 by default. You are likely to have fewer Timer actually executing at the same time than Rules so this might be better. Or it could be worse if you have lots of timers. NOTE that some bindings use threads out of the Timer thread pool too like Astro. cron triggered Rules use these threads too.
Yes, 2. Note that they are only using a thread when they are actively running. When they are awaiting their trigger time they do not consume and threads.
The best solution would be if you can modify the php script to not block until the batch file finishes. If the php script can kick off the script and immediately return and do all that extra work in the background it would be far better over all.
Using the HTTP binding like rossko57 recommends indeed would probably be a better approach, though there would be no way to ever suppress the error from the logs short of configuring the logger to not log any errors from the HTTP binding I think.
This will still allow me to see if there is an error whenever the called server is not responding.
Would you two still recommend the HTTP binding approach also now, when the PHP automatically provides a response?
Please note that even with the HTTP binding I will still have to use the timers, since the same rule makes several HTTP calls, which should be in a specific order along 15 seconds.
This is the full picture:
rule EntranceDoor Opened
when
Item iEntranceDoorSensor changed to OPEN
then
createTimer(now.plusMillis(1), [ |
sendHttpGetRequest(". . . . .", 1000) // Move camera to face door
])
createTimer(now.plusSeconds(1), [ |
sendHttpGetRequest(". . . . .", 1000) // Start Video recording
])
createTimer(now.plusSeconds(1), [ |
sendHttpGetRequest(". . . . .", 1000) // Run PHP file that checks what is the latest file NOW but sends it in 30 seconds
])
createTimer(now.plusSeconds(15), [ |
sendHttpGetRequest(". . . . .", 1000) // Stop Video recording
])
end
My personal Rule of thumb is if I can have fewer lines of code in Rules it’s better. I’m not sure it saves you much in this case. I wouldn’t change the working Rule.
However I have some observations.
I’d get rid of the first timer (now.plusMillis(1)) and just call the HTTP command.
The next two timers go off at basically the same time, so why not combine them and just use one Timer that calls both?
For me to understand & learn:
Wouldn’t this mean that if it takes the camera 1000ms to respond to the move request, it will make the rule wait for a whole second?
If my understanding is correct, why would you prefer the rule to wait for 1 second over using a timer thread? Because there are only 2 timer threads and 5 rule threads?
You are correct.
The reason for this is that the “Start Video recording” and “Stop video recording” are at the same part of the code, while the “Run PHP file” is in a later part of the code, which calls the PHP file with a parameter, based on an “if” clause.
Based on reading your Design Pattern: How to Structure a Rule, I assume I should build the HTTP string in the “if” clause, and only then create a single timer with the 2 HTTP calls:
One “static” to Start Video recording
Second “dynamic” (which was built in the “if” clause) to run the PHP file.
Am I correct?
EDIT:
While waiting for your answer, I tried modifying the rule, but I receive an error in VS.
The error is:
Cannot refer to the non-final variable httpPHPStr inside a lambda expression
The modified rule:
rule EntranceDoor Opened
when
Item iEntranceDoorSensor changed to OPEN
then
createTimer(now.plusMillis(1), [ |
sendHttpGetRequest(". . . . .", 1000) // Move camera to face door
])
var httpPHPStr = "http://192.168.1.111/SendMailOpenHAB.php?id=3" // Parents at home
if (ParentsNotAtHome) {
if (KidsAtHome) {
httpPHPStr = "http://192.168.1.111/SendMailOpenHAB.php?id=1" // Kids Home Alone
} else {
httpPHPStr = "http://192.168.1.111/SendMailOpenHAB.php?id=2" // Home is Empty
}
}
createTimer(now.plusSeconds(1), [ |
sendHttpGetRequest(". . . . .", 1000) // Start Video recording
sendHttpGetRequest(httpPHPStr, 1000) // Run PHP file that checks what is the latest file NOW but sends it in 30 seconds
])
createTimer(now.plusSeconds(15), [ |
sendHttpGetRequest(". . . . .", 1000) // Stop Video recording
])
end
I assume this is because the httpPHPStr variable is not defined within the createTimer.
How should I tackle this?
Or does it mean I cannot use your DP in this case?
Rick can give you the “why” about lambdas and context, but the overall effect is that you cannot pass vars into timer lambdas - the code between . This is the “non-final” thing it’s whinging about.
You can pass vals - but have to observe the limitation that you cannot modify a val after creation, it’s like a constant.
Unrelated consideration; do you want to evaluate conditions at the time you create the timer, or at the moment it executes? Doesn’t matter a lot when it’s just a few seconds, but it can do, and then you’d need to structure as required.
This should work, note use of val and var
// only one place to set URL, easier to maintain
val httpPHPStr = "http://192.168.1.111/SendMailOpenHAB.php"
createTimer(now.plusSeconds(1), [ |
sendHttpGetRequest(". . . . .", 1000) // Start Video recording
var httpPHPStrParam = httpPHPStr + "?id=3" // Parents at home
if (ParentsNotAtHome) {
if (KidsAtHome) {
httpPHPStrParam = httpPHPStr + "?id=1" // Kids Home Alone
} else {
httpPHPStrParam = httpPHPStr + "?id=2" // Home is Empty
}
}
sendHttpGetRequest(httpPHPStrParam, 1000) // Run PHP file that checks what is the latest file NOW but sends it in 30 seconds
])
I’m a bit confused with the overall task - if you have control of Start and Stop recording, why not do the grab-and-post part after recording has stopped?
Thank you for the thorough explanation - I understand the difference between var and val, and now also know that from outside a lambda you can also pass vals, and you can only use vars that are defined within the lambda.
I wold like to evaluate at the time the timer is created. Thanks.
I have 1 more question:
Is it allowed to use createTimer within a createTimer? Is there any risk involved (apart of what you already explained me, that only 2 timer threads can be executed at the same time)?
If, for example, I would like to send a Telegram message with a photo, a few seconds after the door was opened, can I use this (note the sendTelegramPhoto inside:
// only one place to set URL, easier to maintain
val httpPHPStr = "http://192.168.1.111/SendMailOpenHAB.php"
createTimer(now.plusSeconds(1), [ |
sendHttpGetRequest(". . . . .", 1000) // Start Video recording
var httpPHPStrParam = httpPHPStr + "?id=3" // Parents at home
if (ParentsNotAtHome) {
if (KidsAtHome) {
httpPHPStrParam = httpPHPStr + "?id=1" // Kids Home Alone
} else {
httpPHPStrParam = httpPHPStr + "?id=2" // Home is Empty
createTimer(now.plusSeconds(3), [ |
sendTelegramPhoto("bot1", "http://192.168.1.222/snapshot.cgi", "Door Opened when Kids home alone")
])
}
}
sendHttpGetRequest(httpPHPStrParam, 1000) // Run PHP file that checks what is the latest file NOW but sends it in 30 seconds
])
Is there a better approach to this?
This is a bit complicated - I hope I’ll manage to explain it with my lousy English.
My camera starts and stops recording based on different events (for example, motion detected, size of video file, etc).
In order for the video file to be played correctly after being sent, it must be sent in full, i.e. after the recording of that file is stopped.
Therefor, whenever the door is opened I want to start recording (and make sure a new video file is created).
Then, I want to check what is the name of that last video file, wait for 15 seconds, finish recording (thus “closing” the file) and only then sending it.
I am already using the expire binding for a dozen of “virtual” switch items that operate as timers (for example: change air condition temperature after some time, etc.)
Do you recommend creating a couple of virtual items that will server as timers with the expire binding, and using a couple of rules, execute the relevant activities?
Yes… exactly!
I have just been checking out some forum posts on use of the expire binding and there is info in the forums but I don’t find a clear easy example of how I mostly use it… so
I usually create a switch item as follows
Switch BathDayXpr { expire="3m,command=OFF" }
So, the switch changes to OFF after 3 minutes. If it is sent any command except OFF, it re-triggers the timer. So if it is sent ON from a motion sensor or something, the 3 minutes starts over.
then I use a rule as such
rule "My timer ran out"
when
BathDayXpr changes to OFF
then
// do whatever you want here
MyBathroomLight.sendCommand (OFF)
end
What may be useful for you is not worrying about running out of timer threads or creating new timers or whatever, just set up an unbound expire item as shown then do whatever when it is ready
Yes and you are creating a lot of timers in this a rule which increases the likelihood that you will have two or more that need to run at the same time.
There is also no evidence presented to indicate that you are usually running low on rule threads.
This sounds like a good approach.
I believe this is one of those cases where VSCode complains but the rule will actually run just fine.
The following code works.
var Timer timer = null
rule "Test string"
when
Item Test received command
then
logInfo("Test", "Test changed to \""+Test.state.toString+"\"")
var count = 0
timer = createTimer(now, [ |
logInfo("test", "Looping timer with " + count)
if(count < 10) {
count = count + 1
timer.reschedule(now.plusMillis(500))
}
else {
timer = null
}
])
end
I tested it not to long ago. As you can see, count is a var referenced inside the lambda. The trick though is that the changes to count exist only inside the lambda.
Actually you can with create timer. It doesn’t make sense to with a lambda passed to forEach, for example, because the changes made to the var only exist inside the lambda.
This has been reported as an error by the vscode forever but I wonder if it’s always been allowed despite the warning.
All that being said, rossko57’s version is a bit cleaner over all.
So taking into consideration the advantages and disadvantages, how would you implement if it was for yourself?
A. Using the rossko57’s version (which does uses timers)
or B. leaving the sendHttp* calls in the rule, and taking the risk that the rule could hang up to 3 seconds (and another rule with expire binding that will be executed 15 seconds later and could hang for 1 second a well)?
You are right.
When I originally built my rules, I placed all my sendHTTP* calls in a createTimer, because I read somewhere a recommendation to refrain from having it in a rule.
I was not aware that there is also a limit on the number of timer threads running together.
When I check the number of threads using htop, is it normal that this number fluctuates, with an overall growing trend?
On June 13th at 10:45 it was 287
On June 13th at 15:12 it was 301
On June 13th at 16:48 it was 291
On June 13th at 23:33 it was 296
On June 14th at 08:35 it was 298
On June 14th at 23:03 it was 294
On June 15th at 23:48 it was 316
On June 16th at 21:46 (now) it is 310
What should I pay attention to in my rules, to make sure I don’t cause this number to grow?
Also, is it normal to see on the htop list many threads with:
The answer depends on information I don’t have. The better solution depends on what the rest of your rules and timers are doing. If you are unlikely to have lots of other rules running when this rule runs then I’d the up the Rule thread. If you are but you are unlikely to have more than one other Timer, Cron triggered rule, or Astro triggered rule go off at the same time then Timers would be a better choice. If you are likely to have rules and timers going off at the same time, I’d move these long running http calls to an external script that can execute without consuming OH resources.
Also note that as you update and modify your rules the better solution may change.
That shows you all of the OH threads. But in this discussion all that matters is the timer threads and the rules threads. By default you only get two timer threads and five rules threads.
What can cause the number of threads to increase?
Do the statistics I provided above reflect a normal status or not?
Do these threads get “released” after some time?
The thread gets released when the rule is done executing
I’m sure Rich can explain it better but there are 5 rules threads. Every running rule consumes one thread while it is actually running. Most rules only run for a few fractions of a second so long as they are not overly complex. Example: motion sensor signal ON, rule runs, turns light on… done, maybe a few milliseconds. So 5 should be plenty, in case 2 or 3 things happen all at once. But if you have rules that run for a long period of time, for what ever reason, that is one less thread in the pool of available threads for other rules that want to run
So… what causes threads to run for a long time? Lots of stuff, but most is common sense. Long thread sleeps are the worst offenders. Long running loops, calls to services that don’t reply quickly, whatever