So nested triggers are subject to invalid results of sensor state tests?
Piston reading stale door sensor value when triggered by a timer?
The way I look at it is this:
- A trigger is only true for a brief moment, and then returns to being false.
- A condition can be true for long periods of time.
When you have a precise trigger inside a precise trigger, the odds are against you that they will both be true simultaneously.
This is why my recommendation is to place a precise trigger first, and the conditions below it.
OK. So I just need to add a condition type qualifier to the inner timer test.
Or restructure the trigger based on the door state change to kick things off (not sure there was an appropriate one available, which is why I used the condition then forced subscribe).
(It doesn’t make logical sense to me to trigger starting the piston based on the inactivity timer.)
I would go with restructuring…
IF Contact changes to open
is a great trigger to simply set the piston state.
Then you can use the sample code I posted above (in a new block) to do the real work.
But isn’t this the exact moment that you want stuff done?!?
(after 45 minutes of no activity)
I’m checking for the door being open for 45’ with no activity.
It should be more efficient to wait for the door being opened to start checking for the error condition, since a door opening is a relatively infrequent event as compared to 45’ of inactivity. 45’ minutes of inactivity happens all the time in that area.
I understand. I guess my logic is it is worth 10 ms of processing power for this:
IF Motion's motion stays inactive for 45 min <-- Trigger
Then
IF any of Contact Sensor 4, 1, or 2 is open <-- Condition
Then
Do stuff
END IF
END IF
If all contacts are closed at the 45 min mark, then the piston immediately stops executing.
At the risk of stating the obvious…
I should probably mention that this trigger can not fire more than once for every 46 min.
(the countdown resets each time it sees activity)
That code would “Do stuff” if the sensor was open when the timeout occurred.
What I want is to “Do stuff” if the sensor has been open for the duration of the inactivity time period.
So the trigger itself is somehow an “AND” of the two.
If your motion sensor and contacts are not near each other (meaning possible conflict), then I think you will have to use additional variables to complexify your piston even more.
My original code is close to working correctly. All I need to do is (re) check the current “correct” state of the physical sensor in the inner code section where the decision to send notification exists.
Is there some reason a simple “if contact sensor is open” should not accomplish this?
Thinking outloud here:
IF any Contact stays open for 45 minutes
Then Set variable {contactOpened} = true
Wait 1 sec
END IF
IF Motion stays inactive for 45 minutes
Then Set variable {motionInactive} = true
Wait 1 sec
END IF
IF {motionInactive} = true
and
{contactOpened} = true
Then
Do stuff
Set both variables back to false
END IF
Fair enough. I was really asking more about how the system works than coding style, but I was not being clear. I have always assumed that condition tests check the latest reported sensor data, but the problem I ran into with my piston suggests that is not always the case. And not with triggers as you noted.
I guess I did not realize that the “always subscribe” setting for line 28 (a conditional test) had the side effect of making that a trigger. I thought that subscribe simply ensured the piston was called when any changes to the subscribed things happened. The piston seemed to “miss” the change in state of the subscribed event (i.e. final change to “closed” state of that sensor). So this seems more like a bug to me than a trigger vs condition choice problem.
Thanks for your time trying to help!
You have perfectly described a trigger.
One additional note is that any trigger (or subscribed device) will run thru the entire code (from top to bottom) whenever the device changes in either direction.
For example:
IF Switch changes to on <-- Trigger
Then do "X"
END IF
With Location
Do "Y"
END WITH
“X” will execute only when the switch changes to on
“Y” will execute for both events (switch changing to on or off)
I knew the entire code (or piston) was executed on subscribed events, but I now think I understand a different subtlety. When subsequent top to bottom executions happen, triggers unrelated to the latest triggering event are NOT re-evaluated.
In my example, line 28 is a such a “trigger” (even though it is shown as a condition which subscribes to the event). When line 32’s events (i.e. the timeout) trigger the piston to fire again, line 28 is not re-evaluated even though the piston executes top to bottom.
Did I get that right?
Untested on my end, but from what you have described, it sounds like you may be right.
I never experience that with conditions inside of triggers, but with this inverted logic (apologies) it may do just that.
IE: Line 28 (+) is definitely a trigger, but it may also be seen as a condition…
Time permitting I’ll try to create a simpler example to prove my assertion. If I’m right then this behavior should be better highlighted in documentation. I didn’t really run across it in the “condition vs trigger” threads, although admit I could’ve easily missed it.
If I’m wrong I’ll (hopefully) learn why.
In all fairness, we learn very early with webCoRE to avoid placing a trigger inside of another trigger…
So in a way, that documentation is already in place.
I don’t want to sound discouraging though… I am truly interested in hearing about your test results with this. It’s so rare to see a trigger inside of a trigger work at all, so I am on the edge of my seat… LOL
What I thought was not well documented is incorrect evaluation of statements in the piston.
You are quite correct that avoiding nested triggers is covered. The subtlety that a condition “becomes” a trigger upon subscription over-rides is not explicitly called out. In fact, one of the posts attempting to provide a definition of the trigger ( Conditions and Triggers: The difference? ) implies to me that my first condition is not a trigger since it was in the “condition” set of choices in the drop-down list. If I read carefully an earlier post ( Conditions and Triggers: The difference? ) then I could conclude my condition is considered a trigger (and thus should not be nested).
Now after seeing this execution correctness problems, I can find mention by others of similar issues when trigger statements are “late” in the expression ( Piston not running when condition met )… it’s not clear this is the same case I have but invalid expression execution due to trigger ordering is apparently already known.
So back to my test case… that code & log is below. For this test, I toggled the sensor from open to closed, after the piston had fired. So when the timer expired, the contact was closed, which is confirmed in the trace spit out @+163ms – but that state was ignored by code in line 22 of the piston on that run. I’m guessing the behavior has something to do with the “trigger” nature of the first IF condition, and only the trigger that fires the piston is evaluated on that run.
For my original code issue, I have added a “redundant” condition-only IF test after the timeout condition statement in the inner IF statement, that seems to have solved the problem. I could post that final code but suspect its a point solution of little interest. The more interesting matter (perhaps) is pointing out the behavior I saw for others.
Thanks again for your help - I at least (kinda) understand root cause, and more importantly have a fix.
4/4/2019, 3:18:50 PM +62ms
+1ms ╔Received event [HayekHome].time = 1554416330903 with a delay of -842ms
+150ms ║*** INNER IF TIMEOUT EXPIRED, CURRENT CONTACT STATE IS:
+163ms ║closed
+167ms ╚Event processed successfully (167ms)
4/4/2019, 3:17:01 PM +401ms
+1ms ╔Received event [Virtual contact sensor TEST].contact = closed with a delay of 71ms
+188ms ║*** CONTACT ELSE CONDITION WAS SATISFIED, CURRENT CONTACT STATE IS:
+199ms ║closed
+204ms ║Setting up scheduled job for Thu, Apr 4 2019 @ 3:18:50 PM PDT (in 109.299s)
+217ms ╚Event processed successfully (217ms)
4/4/2019, 3:16:50 PM +702ms
+2ms ╔Received event [Virtual contact sensor TEST].contact = open with a delay of 76ms
+184ms ║*** CONTACT IF CONDITION WAS SATISFIED
+207ms ║Setting up scheduled job for Thu, Apr 4 2019 @ 3:18:50 PM PDT (in 119.995s)
+217ms ╚Event processed successfully (217ms)
Thanks for sharing your test results…
It seems like the code:
IF X stays Y for Z minutes
will continue on with the next line after the duration.
(kind of like “Wait” picking up right where it left off)
Adding “IF contact is open” on the next line can resolve that, as you discovered.
In this test, am I correct in assuming that the Motion block (lines 25-30) does nothing until after the contact opens?
If this is true, then I can think of many uses for these nested triggers. (which are usually avoided)