How TCP is set to NEVER - when WAIT is not working in your piston



Hi everyone…
I was going to add this to TUTORIALS but one of the admins can change it if they like it.

One of the most asked question I’ve seen is WHY WAIT IS NOT WORKING??
It could be many different reason but most new users are not aware TCP settings. (I wasn’t:)))))

I’m creating this very simple step by step instruction so at least I can refer users to this post instead of explaining it over and over.

1 - Your piston will run top to bottom in one go and won’t stop.
2 - If you want piston to HOLD and COUNT certain amount of seconds, minutes or even hours, you need to change your TCP settings. (TCP = Task Cancellation Policy)

Here is a very simple piston.
If motion sensor gets activated, lights turn on and wait 10 minutes then turn off…
But look at the first picture, this piston WON’T do what I want…
Because TCP is not SET to NEVER…

It will come to WAIT (line 24) and won’t stop.

Here is what you need to do… Click on WITH (red arrow) line 20.
You’ll see a new setup window.
Clcik on the settings wheel…

Now you’ll see a new settings window…
Find the TCP (Shown below)

Now click on it and choose NEVER CANCEL TASK…This is how it’ll look

And finally SAVE this and when you go back to your piston, you’ll see a circled letter N (stands for NEVER)

Now you are good to go…
Run your piston.
Once motion is detected, lights will turn ON and you’ll see a count down, counting from X minutes to turn the lights OFF…

Motion lights not turning off
Lights and garage door piston not working
Newbie---Piston Help
Power Outlet Piston Design Help
Only trigger piston when called via POST request
Piston will not work at sunset
Need help with contact sensor and simulated contact sensor
Executing Action after 10 minutes
New to webcore please critique my first piston
Rain Sensor False alarm

any suggestions and/or corrections, please jump in…


Great start! This is good info to have/share. The specifics that I would like clarification on is why the piston used as an example would not complete. It is my understanding that because the trigger is “changed to” active which starts the chain. Once it waits those 10 minutes, the device no longer “changed to” but “is” active and that’s why it cancels the subsequent action?

Lastly a more concise clarification on when or why to use wait might be helpful as well. For me I have very small wait times (600-800 milliseconds) in some pistons to allow ST to be able to process all the commands. For example when adjusting multiple bulbs I would set color, set level, wait, turn on. It might have been my previous inexperience with WC but in the past I felt that just sending large waves of commands to ST clogged it up and by adding some wait times it assured a more precise execution.


Yes exactly… if a trigger or a condition or both is/are no longer TRUE in your piston, the piston will stop…

Even with conditions it’s the same thing:

Lets say you want to use a condition but you want something happens X amount of time after condition is no longer TRUE… You can use wait.

IF the bedroom lamp IS not ON (condition)
WAIT 5 minutes
And do this do that

And this is not in the original post but lets say you have a WAIT 10 minutes in your piston BUT if certain things change, you want to stop the WAIT, then you can use the command CANCEL ALL PENDING TASKS…
Now piston will stop the count down.


This will be very helpful to new users… Thanks for taking the time to compose this.

Pro Tip:

Here is how you can change the TCP in only two clicks:

  • Click on the empty space next to the WITH. (shown highlighted in magenta)
  • Change TCP in the top right. (shown highlighted in yellow)



Oh dang!!! @WCmore I didn’t know that…Thank youuuu

And yes this post meant for new - brand new - users only:))))


Not quite. A wait over five seconds is implemented by scheduling a new execution and exiting. If, during that ten minutes, the motion sensor changes state the piston will be fired and executed from the top. The ‘changes to’ condition will no longer be true as the previous value will now be ‘on’. At this point the scheduled wake-up will be cancelled (you see log messages about cancelling schedules all the time).

If the motion stays active the piston will wake-up after ten minutes, execute, fast forward to after the wait and turn the lights on. It does not check the conditions after the wait.

If the wait was only 5 seconds or less (roughly) and the motion changed, the new execution would be held at a semaphore and wait up to ten seconds for the current piston execution to finish (in my limited experience it is always ten seconds but it is supposed to be up to ten). That means the light would turn off.

If the piston were more complex and didn’t reach the parent condition of the ‘wait’ when executed the wake-up would again not be cancelled. It is only the immediate parent that matters.

Setting the TCP to Never prevents the scheduled wait being cancelled if the ‘wait’ must happen. It can be applied on the ‘if’, the ‘with’ or the ‘do’ (in ‘do wait’). If you think about it hard enough you could come up with different outcomes.

Need help with simple piston which turns light off after a fixed interval of time

I think this is an excellent topic. I was very confused about all of this when I started using webcore. Some aspects are still confusing…such as…

Is there any good documentation of this material anywhere? I would be happy to write it up and add it to the wiki if not. However, with the imminent changes to webcore just around the corner, would it be fruitful to do so now?


I thought about the same but mean while new users are keep coming, so I started the topic.
But most will be useless soon I guess…


A lot needs to be covered of course. My topic is only for simple WAIT and do this do that questions…


Sorry for paraphrasing below, but with regards to the default TCP, this is worth repeating…

In the past, a trigger such as:
would only be true for a few seconds…

Just now, I tested a full 8 minutes, and it was still true and continued to process the next line of code (as long as the switch did not change during the WAIT). I am running an 8 hour test now.

This is great news, and means that a lot of blocks will no longer need TCP fiddled with… :+1:
(perhaps ixnay on motion sensors though, since they change so often)


I have not tested this logic with contact sensors changing to X yet…

Color Cycling is Freezing

Yes, I have seen a lot of comments to that effect, and a previous one about how it has changed. I’m a relative newcomer to webCoRE compared to many on here and I’ve never encountered that behaviour. I’ve never known if the behaviour has actually changed or if users just believed that was how it worked and so had been making perfectly correct changes to TCP for the wrong reasons, along with some unnecessary but harmless ones.

In the code, triggers are simply comparisons involving a new value and a cached value. They can only evaluate as true if the piston is currently executing because of a change to the attribute in that trigger. If the piston hits a ‘wait’ that is long enough for it to exit and wake up again (say five seconds or more) the trigger can no longer evaluate as true.

As the trigger can only evaluate to true for the short period the piston executes, it could be considered that it is only true for a few seconds. However it is rather an artificial concept as it is only true or false when it is evaluated.

A companion to ‘triggers are only true for a few seconds’ is the idea that pistons get to the end of a wait and then go back and check the parent conditions. It is perfectly reasonable to think that is how TCP might work and generally harmless. You can see from the logs that it isn’t what is really happening though.


I just want to confirm… even after eight WAITS (60 min each), the piston was still going strong using this trigger:

IF Device's switch changes to on


Essentially, even with TCP set to default, it continues to work until the switch changes.


So, I’m still trying to understand this better. I assisted someone recently with code that I frequently see here…

If Temperature_Sensor_1's temperature is above 90 degrees
    Wait 10 Minutes
    Turn on Light_One.

I understand that it would be better to use Rises Above 90 degrees. But would the IF statement result change (or be re-evaluated) as the cycle time of the temperature sensor changes and the temperature changes from say 90 degrees to 92 degrees? And if so, would the Wait be cancelled and therefore not execute the Turn on?

Similarly, would the following be a good reason to use TCP set to Never? Or is there a better way to code this?

Turn Light_One On    
While Motion_Sensor_1 motion stays active
        Wait 5 minutes
      END while
Turn Light_One Off


If the piston is fired during the wait, and the execution gets to the condition, and it evaluates it and the result has changed, then yes the wait could be cancelled. Obviously if the temp was 90 there wouldn’t be a wait to cancel, but if it was 92 and then 90 there would.

The question is whether the piston is fired during the wait. If that is the whole piston then it will do if the temperature changes. If is part of a bigger piston then it depends. There being a wait doesn’t change the usual behaviour.

‘Stays active’ takes a time argument and does the equivalent of a wait so it isn’t really a valid piston.

The point of the discussion is that ‘TCP Never’ is often being used correctly, but for the wrong reasons, or is being used needlessly, but generally harmlessly. Sometimes it is being recommended when it is just wrong.


However it should be emphasised that it will stop if you hit the ‘Test’ button in the meantime, or execute the piston in another way. The key is that the parent condition has to be evaluated. Once it is the trigger will not evaluate to true as the device’s switch hasn’t just changed to on and TCP kicks in.


This is not entirely true… Take a look at this simple piston, with two triggers:


In this test, I turned on Switch 2, counted 8 seconds, then turned on Switch 1.
Both WAITS stayed true, as seen here:

Side Note:

I did the exact same test again, but this time I turned on Switch 1 first…

The results were very different. Switch 1 never completed after the wait.


Honestly, I tend to only use single triggers in each piston, so this doesn’t affect me much… but a lot of people use multiple triggers in the same piston. It is important to realize that (if TCP is set to default), the ORDER of the triggers plays a part.

In other words, in the code:

  • If the 1st trigger is below the 2nd trigger, they both work as expected…
  • If the 1st trigger is above the 2nd trigger, the 1st trigger cancels out.

(another) motion lights control question

Very interesting. I doubt that was by design. But it does explain some of the “wait” problems that I have seen.


In the second example above, I suspect it cancels because once the piston reaches the code for the 2nd trigger, it does not return back to the top to finish the 1st trigger

Honestly though, it is the first example that really surprised me!


For those following along, this is a great example of why it is so tough to accurately summarize “best practices” here in the forum or on the Wiki…

There are very few “blanket statements” that can be made without also listing a handful of exceptions…

Those who know me, know that I rarely use the words “Always” or “Never”… This is because nearly everything we have learned, may only be true some of the time, and in certain situations…


I guess, from a philosophical point of view, real life is this way as well…