Motion Stays Inactive not working as expected


#1

1) Give a description of the problem
I’m trying to ensure that once there is no motion on all three main motion sensors for 15 minutes, my piston will re-trigger. Even though one of the motion sensors had reported motion in ST multiple times in the 15 minute window leading up to the piston running, it still evaluated true

2) What is the expected behaviour?
With my one main if statement i trigger based on time. The idea is that if at this time all of the conditions are met, it will execute. If not, it will skip execution. This part is happening correctly. Read the next section for where its failing

3) What is happening/not happening?
Immediately after the if fails and it proceeds to the OR, the similar evaluation seems to come back true for all the motion sensors and allows the piston to trigger. It does seem to work, however, when it does periodic checks after the specifically assigned time check. I’m not sure what the best way to solve this would be. I presume it has something to do with the “stays” portion of the trigger, should I be wrapping the code after the “or” in the main if within another IF statement to check that the sensors are all currently marked as inactive, as a secondary check?

4) Post a Green Snapshot of the pistonimage

5) Attach logs after turning logging level to Full
8/5/2020, 11:54:59 PM +306ms
+0ms ╔Received event [Home].time = 1596696900000 with a delay of -695ms
+153ms ║RunTime Analysis CS > 29ms > PS > 69ms > PE > 57ms > CE
+156ms ║Runtime (41023 bytes) successfully initialized in 69ms (v0.3.110.20191009) (155ms)
+157ms ║╔Execution stage started
+166ms ║║Comparison (string) :706a8997fa8f4e1047545bcaf6d5b19f: is_not (string) :9c30cedc4761b921e93d6a843f39f41d: = true (2ms)
+167ms ║║Condition #16 evaluated true (5ms)
+169ms ║║Cancelling condition #14’s schedules…
+170ms ║║Condition group #14 evaluated true (state changed) (7ms)
+174ms ║║Comparison (time) 86099477 happens_daily_at (time) 86100000 = true (0ms)
+175ms ║║Time restriction check passed
+176ms ║║Cancelling condition #20’s schedules…
+177ms ║║Condition #20 evaluated true (6ms)
+182ms ║║Cancelling statement #20’s schedules…
+185ms ║║Requesting time schedule wake up at Thu, Aug 6 2020 @ 11:55:00 PM PDT
+244ms ║║Duration 2327579ms for is >= 900000ms threshold = true
+245ms ║║Comparison (enum) inactive was (string) inactive = true (37ms)
+272ms ║║Duration 75183ms for is >= 900000ms threshold = false
+273ms ║║Comparison (enum) inactive was (string) inactive = false (27ms)
+275ms ║║Cancelling condition #3’s schedules…
+276ms ║║Condition #3 evaluated false (89ms)
+277ms ║║Condition group #19 evaluated false (state did not change) (106ms)
+298ms ║║Comparison (enum) inactive stays (string) inactive = true (2ms)
+300ms ║║Comparison (enum) inactive stays (string) inactive = true (1ms)
+303ms ║║Comparison (enum) inactive stays (string) inactive = true (2ms)
+305ms ║║Cancelling condition #15’s schedules…
+305ms ║║Condition #15 evaluated true (27ms)
+306ms ║║Condition group #14 evaluated true (state did not change) (136ms)
+318ms ║║Comparison (time) 86099615 is_between (time) 85800000 … (time) 3540000 = true (7ms)
+319ms ║║Time restriction check passed
+321ms ║║Cancelling condition #22’s schedules…
+322ms ║║Condition #22 evaluated true (13ms)
+323ms ║║Cancelling condition #21’s schedules…
+324ms ║║Condition group #21 evaluated true (state changed) (15ms)
+326ms ║║Cancelling statement #17’s schedules…
+405ms ║║Executed virtual command sendPushNotification (75ms)
+626ms ║║Executed virtual command setLocationMode (218ms)
+629ms ║║Cancelling statement #9’s schedules…
+784ms ║║Executed physical command [Apple TV].off() (148ms)
+785ms ║║Executed [Apple TV].off (151ms)
+793ms ║║Skipped execution of physical command [The TV].off([]) because it would make no change to the device. (4ms)
+794ms ║║Executed [The TV].off (7ms)
+801ms ║╚Execution stage complete. (643ms)
+804ms ║Setting up scheduled job for Thu, Aug 6 2020 @ 12:08:44 AM PDT (in 824.542s), with 1 more job pending
+812ms ╚Event processed successfully (812ms)


#2

I think you piston is working exactly as you have programmed. I’m not sure I understand what you wanted to happen vs not. First, I would get rid of the ‘only when’. It confuses the logic and may not act as you expect. Instead, add that condition to your internal if.

As far as the logic, understand that any time any one of these sensors goes active or inactive this piston will run top to bottom. It will check your first grouped condition and if that fails, coach your or condition. If either of those pass, it will execute your ‘then’. So, it is doing exactly as expected when seeing all the sensors remain inactive for 15 minutes and executing the ‘then’.


#3

I was looking at this earlier. The ‘stays’ comparison was baffling me as it usually sets up a timer and then immediately returns false to continue with the piston. Not sure what is going on there.


#4

It’s not clear since there is only one event in the log. I assume this ‘time’ event is the end of the timer for the ‘stays’ since it came back true.


#5

So, the “problem” is that each of those conditions are triggers - the “time happens daily at 11:55:00 PM”, so it triggers at exactly that time just fine, this is the log i shared.

The 11:55:00PM trigger executes, and at that point the “were inactive for at least 15 minutes” condition (not a trigger) evaluates false, as i expected, since i know there was motion on one of the sensors multiple times just before then. That part is right.

The issue is the second trigger on line 26 is intended to be a “catch all”, so that it evaluates every 15 minutes (as a trigger) that all of the motion sensors have been inactive for 15 minutes. my understanding is that any time any of the motion sensors change state it resets the trigger to check again in 15 minutes. Which is working fine, that matches ‘true’ all the time, but then the next if block at like 28 makes sure it only carries out its actions between 11:50:00PM and 12:59:00 AM. That part works great too.

The part that is NOT working correctly, is when the time based trigger on line 21 fires at precisely 11:55 and that group fails (you can see its red), it proceeds to the next check on line 26 and somehow that doesn’t correctly evaluate that there was recent motion on one of the sensors, returns true and executes even though it should not. In other words, the same logic i use on line 23 that properly realizes there has recently been motion and it should not continue does not come to the same conclusion when i use the trigger condition in a similar logical way on line 26.

What I’ve done for now is add another group into the “or” condition on line 25. It includes both the trigger, but also a duplicate of the same “were inactive for” logic from line 23. So the trigger on line 26 triggers but then does a quick validation to make sure that all the motion sensors are currently inactive.

I’ll see if this does the trick, and its probably some side effect of using a trigger as both a trigger and a “check against right now” that isnt evaluating logic as I would expect it to given the language the rule lays out.

Hopefully that makes sense?


#6

Those two are VERY different lines of code…

  • Line 23 is a condition that checks the past
  • Line 25 is a trigger that will check in the future.

Both lines are treated very differently here in webCoRE.


Also keep in mind that a STAY INACTIVE will continue on with the next line of code, and come back in 15 minutes to see if things are still OK.

(I normally keep STAYS in their own, independent block)


#7

Here is how I would do this:

Every day at 11:55pm
do
   IF Location mode is X
      and
      All sensors were inactive for 15min
   Then
      Turn off Switch 2 & 7
   END IF
END EVERY

IF all Sensors stays inactive for 15 minutes
Then
   IF Location mode is X
      and
      Time is not between 12:59am and 11:50pm
   Then
      Turn off Switch 2 & 7
END IF

#8

OK so thats the problem then. The way it was worded (disregarding for a moment how triggers expect to execute) I was presuming that it would also look back in time and confirm that the things it was checking were true in the current execution.

So in this case would the modification I made to add an extra condition inside a group with the trigger make the most sense? or to split it out into a different if block?

Basically the intended outcome was:

  • Specifically, statically, at 11:55pm check to see whether or not there has not been motion for the past 15 minutes on the specified sensors. If not, shut down the house, etc.
  • Between 11:50 and 12:59, check every 15 minutes (presuming its only going to run of course if there was a reason that the trigger reset its check because of a state change, i am presuming this will be true if at 11:55pm there’s still motion happening, it would at LEAST check again at 12:10) and if it sees that all the sensors have finally calmed and have stayed inactive for 15 minutes, proceed with shutting down the house.

Essentially a “check at a set time and lock up if its already quiet”, or if not, periodically check until 1am to try again.


#9

My last post is my suggestion.
(I posted while you were typing, but it should cover all of your goals)


#10

Relating this to the code block i shared (im curious for efficiency’s sake, here), this would have me use two completely independent if blocks and duplicate the ‘then’ actions, rather than some how utilizing two grouped IF’s and ending up with one set of ‘then’ actions. Is that right?


#11

Correct. We want the STAYS to be separate.
(I also took the liberty of removing the ONLY WHEN, and converting it to an IF… and inverting your time, so it no longer spans midnight)


#12

Got it. The developer in me felt that this was less efficient to duplicate the action blocks, but i guess it completely depends on how webcore expects to behave around those functions.

Thanks, this has been helpful!


#13

Sometimes, if the “action block” is complex, I would put that portion in a separate piston (with no triggers), and then let many pistons CALL that piston, when needed.

If it is only a few commands, then I just duplicate (drag & copy) the tiny block, for simplicity.