OK. Let’s start with the basics. Pistons are actually Groovy SmartApps. Like any SmartApp they will subscribe to device events and/or schedule timer events and then sit around waiting for events to happen. So how does a piston know which events to subscribe to? The answer is that it looks through the code to see what events could possibly affect it, which mostly means looking at the various conditions/triggers.
Now you will have noticed that some conditions are just called conditions, and other ones are classed as triggers. If you look at them you will notice that those classed as triggers are associated with things that have recently happened: for example a switch has changed to on; or a temperature has risen above a certain amount. Those just classed as conditions are more about the state of something: a switch is off; or the temperature is greater than a certain amount.
The piston, by default, figures that by using triggers you are explicitly telling it when you want it to be executed. So it will only subscribe to or schedule the events that affect how triggers will be evaluated. If there aren’t any triggers it will subscribe to or schedule the events that affect the conditions instead. This is sometimes described as promoting them to triggers but I’ve never found that helpful. You can actually override the subscription mechanism for each condition/trigger.
When the piston executes it effectively behaves like a script executing from the top down. Triggers and conditions are now just comparisons that evaluate to true or false.
So let’s look in some more detail.
Devicename switch changes to on is a trigger which means by default the piston will subscribe to events from Devicename’s switch attribute. When it comes to evaluation the effective behaviour can be thought of as follows:
- The piston checks if the current event device is
Devicename and the current event attribute is switch. If they aren’t then the result is false as activity on the Devicename switch isn’t the reason the piston is running. It hasn’t changed in the sense that the piston uses the word.
- The next check is if the current event value is
on. If it isn’t then the result is again false as the switch can’t have changed to on.
- The next check is what the current event value was the last time this trigger was evaluated and the event was for
Devicename switch. If it wasn’t on (i.e. it was off), then the switch has changed to on and the result is true, otherwise it is false.
Note the two gotchas here. Firsly the result of the trigger is based on a comparison between the events as seen when the trigger is evaluated so it is important that the trigger is evaluated every time the piston is executed in response to a Devicename switch event. If it isn’t then changes can’t be determined reliably. Secondly, only the event matters. It doesn’t matter if the switch is actually now off, if the event says it is on it is on. So delayed events can confuse things.
Devicename switch is on is a condition so the Devicename switch attribute is only subscribed to if there aren’t any triggers or the defaults are overridden. When evaluated this is basically what happens:
- The piston checks if the current event is for the
Devicename switch attribute. If it is then the current event value is used as the switch status. If it is on the condition is true. If it is off then it is false.
- Otherwise the device object on SmartThings is queried for the current value of the
switch. If it is on the condition is true. If it is off then it is false.
Note the gotcha here. If the piston was fired because of a Devicename switch event then that is what counts. It doesn’t matter if the piston instance has been running for hours and the switch has actually toggled eleventy billion times, it is still handling an event that says the switch is on and that is what counts.
Devicename switch was on for at least one hour (I may have got the wording slightly wrong) is a condition. As long as the piston wasn’t fired by a Devicename switch event what it means is that the Devicename switch has been set to on continuously for at least one hour and it still is on. It is determined by looking back through the device event history on SmartThings.
If the piston is handling a Devicename switch event then the very last event in the history is ignored, so it would mean it was on continuously for an hour prior to the event the the piston is currently handling. There is a gotcha here if events get so delayed that the event isn’t actually the last one in the history.
Devicename switch stays on for one hour is classed a trigger but is a bit quirky. When evaluated it does two things which can be thought of like this:
- It evaluates the equivalent of
Devicename switch is on and if that is true then does the equivalent of:
then
wait one hour
<the contents of the then block>
endif
- As well as the above, the trigger always returns
false and just continues on with any else block and the rest of the piston. That can catch you out if you aren’t expecting it.
So what happens then? Well if the piston fires during the next hour and the stays is evaluated again and the switch is now off the ‘wait’ will be ‘cancelled’. The switch hasn’t stayed on for an hour. Otherwise the switch has stayed on for an hour and the piston fires and continues with the then block.
As with other triggers, there is a gotcha if the switch turns off and the piston doesn’t reach the stays trigger when it executes.