Help with Garage Door Auto Close


#1

1) Give a description of the problem
I have 2 garage doors I would like to auto close with conditions tied to the time of day and the state of the garage lights.

2) What is the expected behaviour?
If the time of day is between Sunset and Sunrise and the garage door is open, I want it to auto close.

If however the garage lights are on I want it to stay open, or remain open after opening until 10 min after the garage lights are turned off.

I have a Smartthings automation that, when the garage door opens (sunset to sunrise) the garage lights come on automatically, then turn off automatically after 10 minutes. The thought here is that the lights would be on when coming home, then turn off if someone forgot.

What I would really like is to include some way of looking at the lights and basing the auto closure of the door to look at, were the lights turned on via touching the switch (perhaps indicating I want to be in the garage for a while) or via the automation, (perhaps indicating I’m just coming or going)

3) What is happening/not happening?
Is this the best way to set this up?

**4) Post a Green Snapshot of the piston![image|45x37]

5) Attach logs after turning logging level to Full

REMOVE BELOW AFTER READING


#2

There are some great examples of this over in this thread.


There is at least one example of this concept in this thread.

(no need to recreate the wheel)


#3

Thanks for the links. I attempted to build in the functions but haven’t quite got it. For the moment I’ve removed Sunset-Sunrise so I can test. At the moment, it appears to evaluate if the lights are on or off with the boolean variables but never progresses past line 46.


#4

Quick answer:

Lines 45-70 should run when any door opens or closes…
Lines 25-43 should only run after the 10 seconds has expired…

Although 10 sec is a bit tight. Can you test using at least 22 seconds please?


Long answer(s):

I have not fully analyzed the entire logic here, but my first thoughts were:

I would drag and drop the large block (lines 45-70) to the very end.
Totally after the original block.

Just keep in mind that each trigger runs thru the entire code, top to bottom.
(IE: when door opens or closes, that large block will attempt to execute)


Let’s use your line 23 as an example:

IF any of Door1 or Door2 stays open for 10 sec

This trigger will execute the entire piston on 6 events:

  • Door1 opens
  • Door1 closes
  • Door2 opens
  • Door2 closes
  • 10 sec after Door1 opens
  • 10 sec after Door2 opens

The first 4 in that list will execute the ELSE block (lines 45-70),
and then continue on with the rest of the piston

The last 2 in that list may execute the THEN block (lines 25-43),
and then continue on with the rest of the piston


Side note:

I realize that you are likely starting with someone else’s pistons, but generally speaking, I typically recommend new members avoiding using any ELSE blocks, until you have 30 pistons under your belt and six months experience. Even with “pros”, they are used VERY sparingly.

For reference, last week I had 514 pistons in my main house, and I bet less than a dozen use ELSE blocks.


One more thought:

Your trigger:
IF any of Door1 or Door2's door stays open for X
is usually written:
IF any of Door1 or Door2's CONTACT stays open for X


Edit… Just noticed another issue…

You have a trigger inside of a trigger. (line 35)

Generally speaking, triggers need to be top level (extreme left), with conditions indented beneath.


… and because I am a good sport… one more for good measure. :grin:

Line 35 mentions “programmatically changes”…
It is important to note that 99% of bulbs do not report this.
(IE: when editing a piston, leave the default “any changes”)

Essentially, we are using variables to keep tabs on “man vs machine”.


#5

Again, thank you.

I had made quite a few changes to the piston prior to reading all your updates, and I have it functional but would like to make a few tweaks to bring it up a level.

For line 35, the ELSE. What should I use in it’s place? I do admit to a bit of grave robbing to get this beast working.

edit The “door” is not available as a contact sensor

The block beginning at 50, how would I tell it to watch at the light switch (switch 6), and if it changes back to off, jump back to if block at 35? I’m not sure which “do” parameter captures that idea.


#6

I think I have it now. It doesn’t feel very “elegant” around line 50. The scenario I’m trying to achieve is if the garage door opens and the garage light was already on, (perhaps someone is working in there, then do not auto close and turn off the light. That is all working.

Then at line 50 I’m saying if the lights then turn off, the person is done working so close the garage door. That too is working but if I delay the closing too long then the boolean picks up a “false” value and jumps to line 61. I’m trying to head that off with a 40 second delay.

Is there a cleaner way to achieve that?


#7

First off, compliments to the “chef”… Your piston is coming along nicely. :grin:

With regards to line 50’ish… I would try the opposite.

  • The Trigger should be far left (not indented)
  • The Condition should be indented beneath.

Something like:

IF Switch 6 changes to off          <-- Trigger
Then
    IF {GarageLightsWasOn} is true  <-- Condition
    Then
        Close (a wait after is pointless. You cannot prevent another trigger)
    Else
        Wait (TCP set to Never)
        Close door
        I highly recommend rethinking Switch 6 here, 
        since it will start the piston all over at the top if toggled
    END IF
END IF

Normally, we do NOT want this logic:

IF SwitchA changes
    Then Change SwitchA
END IF

Keep in mind, any lightning bolt in the left margin will run thru the entire code at any change.

So, for example, we’d need to add two more events to the bottom of the previous list:

Door1 opens
Door1 closes
Door2 opens
Door2 closes
10 sec after Door1 opens
10 sec after Door2 opens
Switch 6 changes to on 
Switch 6 changes to off

(all 8 events runs thru the entire piston, and will execute anything allowed)


#8

Here is a working* piston, with my thoughts (hopes and dreams)

On the garage doors I have Samsung Multipurpose sensors and Meross wi-fi garage door openers. I wanted to stick with using the Meross in the piston but they occasionally deliver bad position information to Smartthings, so I used the contact sensors to determine door position and just sent the close to the Meross.

IF @ LINE 22: Designed to see if the garage door is open at sunset with the garage light off. If so I presume no one is in the garage and close the door.

IF @ LINE 35: Designed for when driving into a dark garage then entering the home and forgetting to close the door. I’m not saying my wife is the culprit here. I’m just saying she parks in the garage. This block ends by changing the variable to TRUE which leads to.

IF @ 44: This is the 10 minute countdown to auto closure. On line 55 I used the TCP of NEVER. I read about this, played with it in various iterations, it seems like it works, I don’t have a clear grasp of what’s going on, frankly it may not be required. At 62 setting the variable to FALSE prevents the occasional loop back to 44.

IF LINE 68: If someone is in the garage with light on, and the garage is opened, I don’t want the auto close to begin its countdown. That’s where setting the GarageLightNotOn to FALSE comes in. This block will turn the light off and if the doors are open close them too. The variable of FALSE at 75 is preventative. There are occasions where due to internet gremlins I found the piston could jump back to 35 and get in a loop. The 3 seconds is to realize you made a mistake and smack the stop button.

Much thanks to WCmore for the guidance. :smiley:

I’m open to critique, and am pleased it does seem to work* as desired.


#9

I am happy, if you are happy. :grin:


… although I would recommend combining your last two IFs… (lines 44-80)

IF {variable} is true
Then
    Do stuff
Else
    Do other stuff
END IF

Otherwise, your 44-65 IF can influence 66-80 IF.
(essentially executing BOTH blocks)


I also think the WITH’s on line 52 & 72 need TCP set to Never.


… and the indented trigger on line 70 may give you issues…


#10

Yeah, that was a premature celebration.

Anything jump out at you here?


#11

While I skim, what error did you receive?


#12

Now? none.

Previuosly? One of the blocks could run outside sunset-sunrise, the garage lights would get stuck in a loop turning on.


#13

There are other things that stand out, but most I have already mentioned earlier in this thread.

The one I have not mentioned yet is line 42, which I would invert to:
IF Time is NOT between sunrise and sunset

This keeps the time from spanning midnight.


But as mentioned before, don’t forget to keep triggers far left (unindented) with conditions indented beneath.

You did not do this on lines: 42, 46, 56, or 61.

The key is: the triggers (lightning bolts) is what starts the piston all over from the top.


#14

Sorry to be thick, I’ve gone back into the thread to see your comments, I think* I made the changes as well as removing the ELSE.

Inverting the time… NOT between sunset and sunrise puts the time the actions can run in the daytime, as opposed to evening. Is there some other way of expressing that?


#15

I recommended the opposite:
IF Time is NOT between sunrise and sunset
(notice I placed sunrise before sunset, like a normal day)

The statement above will be true from sunset until sunrise the following day.


#16

I did not notice the first time, that makes sense. I should have been able to figure that out. I think I’ve been staring at this too long.


#17

Don’t sweat it… Even on a good day, a “double-negative” can throw the best of us… :grin:

If you are curious, in the eyes of webCoRE, sunrise and sunset are always for that exact date. So, for example, at 11pm on a Monday, sunrise is still showing as 6am Monday morning. Once midnight passes, then sunrise now becomes 6:01am on Tuesday.

So by using the “double-negative”, we are keeping both time events on the same day. This ensures that anything outside that bounds becomes ‘true’.

(Not seen above, but for others following along… If a piston uses TWC for sunrise, then it may not switch over until a little bit past midnight. I have seen it as late as 12:12 am)


Side Note:

I apologize I have not spent more time with this piston of yours. I have a lot on my plate at the moment, and apparently, so does your piston, LOL.

I hope you have (at least) been able to learn a few things along the way…


#18

The last piston I posted, worked, but as I keep exploring WebCore I see how it could be better. I’m posting this because I don’t understand why the timer is canceling. “Canceling condition 54s schedules…”

I could toss in a TCP of never, but there is a scenario where I would want to cancel the timer,

This is just me playing with ideas and not a finished piston.

9/4/2020, 11:54:30 AM +77ms
+0ms ╔Received event [Home].time = 1599245671448 with a delay of -1372ms
+143ms ║RunTime Analysis CS &gt; 19ms &gt; PS &gt; 94ms &gt; PE &gt; 31ms &gt; CE
+146ms ║Runtime (45429 bytes) successfully initialized in 94ms (v0.3.110.20191009) (145ms)
+147ms ║╔Execution stage started
+148ms ║╚Execution stage complete. (1ms)
+149ms ╚Event processed successfully (149ms)
9/4/2020, 11:54:16 AM +861ms
+0ms ╔Received event [Formal Dining Lights].switch = on with a delay of 47ms
+71ms ║RunTime Analysis CS &gt; 17ms &gt; PS &gt; 29ms &gt; PE &gt; 26ms &gt; CE
+73ms ║Runtime (45429 bytes) successfully initialized in 29ms (v0.3.110.20191009) (72ms)
+74ms ║╔Execution stage started
+107ms ║║Comparison (time) 42856939 is_between (time) 1599226680000 .. (time) 1599273720000 = true (8ms)
+108ms ║║Time restriction check passed
+110ms ║║Condition #92 evaluated true (32ms)
+111ms ║║Condition group #70 evaluated true (state did not change) (32ms)
+121ms ║║Cancelling condition #56's schedules...
+122ms ║║Condition #56 evaluated false (9ms)
+123ms ║║Cancelling condition #54's schedules...
+124ms ║║Condition group #54 evaluated false (state changed) (11ms)
+130ms ║║Comparison (enum) on changes_to (string) off = false (0ms)
+131ms ║║Cancelling condition #47's schedules...
+132ms ║║Condition #47 evaluated false (6ms)
+133ms ║║Cancelling condition #90's schedules...
+134ms ║║Condition group #90 evaluated false (state changed) (8ms)
+136ms ║╚Execution stage complete. (62ms)
+137ms ╚Event processed successfully (137ms)
9/4/2020, 11:54:16 AM +256ms
+1ms ╔Received event [Entry Hall Lights].switch = on with a delay of 62ms
+85ms ║RunTime Analysis CS &gt; 17ms &gt; PS &gt; 39ms &gt; PE &gt; 28ms &gt; CE
+87ms ║Runtime (45430 bytes) successfully initialized in 39ms (v0.3.110.20191009) (85ms)
+88ms ║╔Execution stage started
+124ms ║║Comparison (time) 42856349 is_between (time) 1599226680000 .. (time) 1599273720000 = true (8ms)
+126ms ║║Time restriction check passed
+127ms ║║Condition #92 evaluated true (35ms)
+128ms ║║Condition group #70 evaluated true (state did not change) (36ms)
+135ms ║║Comparison (enum) on changes_to (string) on = true (0ms)
+136ms ║║Cancelling condition #56's schedules...
+137ms ║║Condition #56 evaluated true (7ms)
+138ms ║║Cancelling condition #54's schedules...
+139ms ║║Condition group #54 evaluated true (state changed) (9ms)
+144ms ║║Comparison (boolean) false is (boolean) false = true (1ms)
+146ms ║║Condition #85 evaluated true (4ms)
+147ms ║║Condition group #87 evaluated true (state did not change) (5ms)
+149ms ║║Cancelling statement #36's schedules...
+175ms ║║Executed physical command [Formal Dining Lights].on() (21ms)
+176ms ║║Executed [Formal Dining Lights].on (23ms)
+181ms ║║Executed virtual command [Formal Dining Lights].setVariable (3ms)
+184ms ║║Cancelling statement #16's schedules...
+190ms ║║Executed virtual command [Entry Hall Lights].wait (1ms)
+191ms ║║Requesting a wake up for Fri, Sep 4 2020 @ 11:54:31 AM PDT (in 15.0s)
+196ms ║╚Execution stage complete. (108ms)
+197ms ║Setting up scheduled job for Fri, Sep 4 2020 @ 11:54:31 AM PDT (in 14.995s)
+204ms ╚Event processed successfully (204ms)

#19

Let’s take a look at a summary of your logs…

11:54:16 AM +256ms
= Received event [Entry Hall Lights].switch = on
(Condition group #54 evaluated true)

… and 0.6 seconds later:

11:54:16 AM +861ms
= Received event [Entry Hall Lights].switch = on
(Condition group #54 evaluated false)

This makes sense to me, because it only registers as a CHANGE the first time around.
To register another change, it must actually change to something else.


Also note that if you insist on using this logic:

IF GarageDoor's switch changes
    Then Change GarageDoor's switch
END IF

Then you probably want to move the command on line 45 to the very last line in that block.
(IE: turn light on, wait, turn light off, THEN Turn off GarageDoor as the final command)

Remember, each change to GarageDoor will start the piston all over at the top.


Although, you have also applied the same looping logic to the lights…

IF Door's switch changes
    Then Change Light's switch
END IF

IF Light's switch changes
    Then Change Door's switch
END IF

Can you visualize the potential for disaster here?

I cannot, in good conscious, recommend this method.


#20

Thanks.