The Unofficial SmartThings Blog
Jump to navigation Jump to search


logo courtesy of @chickwebb

These are indexed snippets of revelations from the CoRE (Community's own Rules Engine) developer, @ady624 and the SmartThings community.

For the the main CoRE documentation page, see CoRE [1]
For indexed samples of working CoRE Pistons, submitted by the SmartThings community, see CoREsamples [2]

Now forward, listing most recent post first. --Whippm (talk) 01:38, 31 July 2016 (EDT)

This page is a work in progress and will hopefully serve as a resource to a better formatted, narrative documentation from others. --Whippm (talk) 20:06, 22 July 2016 (EDT)


[RELEASE CANDIDATE] CoRE (Community’s own Rule Engine) [3]
[BETA MILESTONE 2] CoRE (Community’s own Rules Engine) [4]
CoRE - Get peer assistance here with setting up Pistons [5]
[BETA MILESTONE 1] CoRE (Community’s own Rules Engine) [6]
CoRE and Piston Rules Engine, first design steps [7]
GitHub - ady624/CoRE: CoRE - Community's own Rule Engine [8]
@ady624 PayPal.Me [9]
CoRE - Things That Are Smart Wiki [10]
SmartThings Platform Status [11]
SmartThings API (main) [12]
SmartThings Installed Smart App List (health) [13]
SmartThings Local Device List (can run local) [14]


the dashboard shows the evaluation at last piston run, NOT the current evaluation of things. [15]

Fixed some minor issues with condition evaluation display in the dashboard. Introducing "not evaluated": blue means evaluated as true, red means evaluated as false, gray means not evaluated at all 9/16/2016 >>> v0.2.14f.20160916 - Beta M2 -

You need to go to the IDE and enable OAuth for CoRE. Then try the dashboard... [16]

Dashboard improvements - capture piston is now completely self-contained, should work in embedded browser on Android [17]

The countdown timer is only updated every now and then, not continuously synced to ST. It is normal for it to go into past due times because it assumes nothing happened between updates. [18]

Browser Extension

I attempted to fix the dashboard display - the red/blue/gray borders represent the truth evaluation at last piston run - but when using THEN IF, or ELSE IF, etc. some conditions may actually be skipped - they should be gray. I've made sure to update all conditions to null right before starting evaluations, but that was used for condition state change too - so now it looked like the conditions always changed states - because they were going from null to whatever the new evaluation was, thus always "changing". Does that explain the weird stuff? :slight_smile: [19]

Try this chrome extension. Google is now charging for publishing extensions, so I eventually bit the bullet [20]

Experimental web extension for CoRE, tested with desktop browsers Chrome and Firefox 48.

Open your dashboard in a tab, then click on the CoRE icon that should now exist in the browser's top right toolbar. Once it identifies the dashboard, you no longer need that tab open. [21]

Circle Colors - The letter represents the type of piston (piston mode) Blue means its state is currently true, red is false.

In piston view, blue and red show the state the last time the piston was evaluated. It does not reflect the current state of each condition or device. [22]

Green- new, never ran - thus not true and not false. null state [23]

Time Circle - from outside towards inside: s, m ,h [24]

Change Theme - ST UI > Smart Apps > CoRE > Settings Must be on v0.2.13e+ [25]

Blue Dots - it fired recently (in the last 2 min) [26]


Has anyone noticed you can take a snapshot of the piston in the dashboard? It opens an image in a new tab, you can then save that to your Photos and post it here :slight_smile: No more multiple screen snapshots... [27]


Only works with the experimental dashboard for now [28]

v0.2.148.20160902 - Beta M2 - Added instructions for removing dashboard taps. Thank you @dseg for the Tap idea. [29]


Device implementations define commands that CoRE is able to do. What each command does is really dependent on the DTH (device type handler, aka device driver) code. You would need to look into that code to see if there is any difference between on(), off(), enable(), and disable() [30]

CoRE displays all attributes it finds. If it does not know them, it tries to determine their type. If it cannot, it treats them as strings. But it does allow selection of them. Hint: you need expert mode for the Attribute field to be displayed. [31]

Real world devices connect to ST via what's called a Device Type Handler, also known as DTH or sometimes referred to as DH. This DTH is the code that allows communication to the device (commands) or from the device (attributes). Now, the DTH revolves around what ST defined as capabilities (find the list of capabilities here4). Each DTH (and therefore the underlying physical device behind it) has to have one or more capabilities that describe the device. Each capability may bring its own commands (for communication to the device) and attributes (for communication from the device). Sometimes, devices can do more than what the limited capabilities offer. One example is the Hue being able to alert or startLoop (self driven color changing loop, ST doesn't have to constantly order new colors). These two "commands" have no presence in any of the capabilities described by ST, which makes them "custom" commands. Now, CoRE has gone a bit further and pre-defined some of these custom commands so that users have easier access to them. You'll see that "startLoop" for a Hue light is shown as if it were a standard command, but it really is a "custom" command. Now, CoRE can't know of all possible commands of all devices, but the device itself can list the commands it supports. This is how CoRE gains access to the list and it will display the standard (including CoRE pre-defined custom commands) with an empty dot, virtual commands (commands that augment ST, but are not device based, or combined commands such as Toggle and Adjust) with a full dot, and the "custom" commands with a little empty arrow up sign (someone called it a house, I guess that describes it better). This allows you, the user, to use commands that are not understood by the ST ecosystem and that wouldn't be available to SmartApps that abide by the capabilities list (Smart Lights, for instance). One thing to note here, is that many of these "custom" commands take parameters. This is unknown to CoRE and possibly to the user as well, so a little bit of documenting needs to be done before one can use that custom command. CoRE now allows you to add simple parameters to the command, but you'd have to know what parameters (data type and value), their order, and most importantly, their meaning. But at least you have a way to use it :slight_smile:

NOTE: Same goes for "custom" attributes. These are pieces of information that the device itself can provide, but no capability describes yet. Just like with custom commands, you'd have to know what the attribute does and how to interpret its values. So far, CoRE assumes all custom attributes are strings and compares them as such. [32]

If you modify the DTH to have an attribute, I can use that attribute. But how will the attribute change? Every one minute? CoRE can read custom attributes and make conditions on them. It can also execute custom commands (though right now I'm missing the ability to define the parameters - but it's planned for). It cannot however execute a custom command and get the data into a variable. Or use a custom command as a condition. [33]

Door Locks

Yep, $currentEventDeviceIndex will provide the index used. Same goes for button pushes. [34]

The actual unlock code is not provided. What is provided is the index of the code, or slot, if you want. @eibyer is right. [35]

Yep,just tested on my Kwikset, data: { "usedCode" : 1 } Will implement it. [36]


CoRE is now technically able to send HTTP requests to local equipment in the same network as the hub, via HubAction. The "Make web request" task has become smarter, being able to detect local IP requests and redirect them to the hub instead of executing the web request from the cloud. There is a limitation however, local requests are not able to return data, as the request itself is asynchronous - which means the response can be listened for by CoRE, but the data will arrive at a later time, not during the actual request. So far, no data is returned by a local HTTP request. [37]


The piston actually executes in the cloud regardless of the hub being on or off - the problem with the hub being off is most things won't work - device changes won't be reported (hence the piston not running) and commands to devices won't work either. Time and presence should still trigger the piston, but don't expect the device related commands to work - they won't and I don't think they will be "delayed" and execute whenever the hub comes back online. [38]

As per ST's request, CoRE is not able to start a z-wave repair. The ST team wants to first identify any possible side effects of such commands before allowing me to implement this feature. Sorry. There is - apparently - undocumented code that can initiate such a repair but it is not public (I do not have it) - I do not know if it will ever make it to CoRE or not. [39]

Access your Core Engine from the "Hamburger Menu" (3 horizontal lines in the upper right corner) not from the Marketplace. The Marketplace is for installing apps, the Hamburger is for modifying installed apps. [40]

If you find another delayed piston, and if you have any device in the house (motion sensor, button, contact sensor, etc) that triggers the firing of any piston, can you please try tripping that and see if the delayed piston recovers? Also, you may need to pause/resume the often-delayed piston because it needs to subscribe to an event it wasn't subscribing before, so if you haven't changed that piston recently, those recent improvements to CoRE may not be active for that piston. Let me know if that gets the delayed piston going or not please.

When I go to CoRE settings and try to change anything and then hit "Done" or just hit "Done" without changing anything at all I get the red banner that states "Please fill out all required fields" [41]

Known Android issue, most of the times you'll be able to use the back arrow instead of Done. Just make sure to tap Done on the main piston page to save it. Same goes for the parent app. [42]

IMPORTANT ANNOUNCEMENT FOR CoRE PRE-BETA ADOPTERS: If you installed CoRE early and have any pistons created prior to v0.0.085, you may experience a problem with rebuilding a dead piston. Some "when true" individual actions may be regenerated as "when false". If you are affected, just go to the affected individual action and scroll down to the state on which the action should run, select true and save the piston. The problem will not occur again, even if you rebuild the piston again at a later time. Thank you [43]

Can you guys please open issues in github for everything that seems to not work? It is much easier for me to track issues that way and I won't be missing anyone. I mean, bring them here but don't forget to open issues in github too. The post thread is much harder to keep up with... [44]

The proper way to access the smart app is by tapping Settings in the right top (the three lines) and going to SmartApps > CoRE. [45]


You don't need to enable the debug messages. If both trace and debug are selected, then the logs will most likely be flooded, leading to log lines being dropped. I recommend leaving the debug messages off. Just enable debugging overall [46]

Trace Logs - Each piston has an Enable debugging option on their main page. Enable that and then open the Live Logging in the IDE. [47]

PISTONS = IF(events)THEN(actions)

Pistons subscribe to events. An event is usually generated when a device attribute changes value (though it does not always necessarily change the value - you can get multiple temperature events for the same device and the same value). Pistons only evaluate the condition set(s) whenever such an event occurs that the piston is listening for. To determine which events the piston is looking for, it takes all the unique device/attribute pairs from the list of used devices. If you say "presence is active" you will get events for present as well as not present. The difference between a condition (empty circle) and a trigger (full circle) is that a condition simply checks a state: door is open. When you combine multiple device events into a single piston, it may happen that another condition makes the piston evaluates. Door is open is true as long as the door is currently open. If you were to use a trigger, changes to open, then that trigger would be true only when the door actually opened. A second evaluation while the door is still open would result in a false - the door did not just change to open, it is open, but it did not just open. So triggers add a sense of momentary action to the mix. Also, if you mix triggers with conditions, the piston will only subscribe to the triggered device/attributes. That is, if you say : door is open and presence changes to present, you'll only subscribe to presence events (either present or not present). The door won't be subscribed to because any door event would ALWAYS return a false overall evaluation (the trigger will always be false on events not generated by the device it listens to). Another aspect is that "door is open and presence is present" will be true (and therefore execute actions) when either the door opens while you are present, or when you come home while the door is open. The "door is open and presence changes to present" will only execute your actions when you arrive home and the door was already open. It won't run if the door opens while you're home... [48]

On a different note, no one seems to have noticed the Paused pistons count under CoRE's Runtime Statistics is always 0. Hard coded to 0, really [49]


Each action starts its timeline at the same time [50]

v0.1.139.20160810 - Beta M1 - Implemented "Wait for common time" - waits for sunrise, sunset, noon or midnight on specified days of the week and "Wait for custom time" - waits for custom defined time on specified days of the week

NOTE: There may be an issue when Wait for common/custom time is used after a Wait for state, because the Wait for common/custom time is calculated at the time of execution, with no knowledge of when the state will change in the future. I don't recommend mixing the two. [51]

If you have a very complex situation, you can always use individual actions applied to a series of OR'd conditions or condition groups: [52]

You can rerun the same piston. If the piston uses triggers (full dots), you may encounter strange results from executing it. But if you only use conditions, you can simply add an action to Control location and add a task for "Follow up with piston" and choose the very same piston, enter time to wait before following up.

EDIT: you can always have piston restricted on alarm mode, happening every 5 minutes with a date & time trigger (happens at) [53]

you can add three tasks to the same THEN statement and restrict each one to a certain mode. Go to each action and look below for Restrictions. That simplifies your IF statements a lot since you'll be taking the mode out of them and applying it to the actions themselves [54]

Event Condition

An Event driven SmartApp (ie, subscription to Device Attribute Events) is much less likely to fail than a Scheduled SmartApp in our experience.

But I understand it's a bother to rewrite existing functionality.

I'm posting this though as a recommended approach for other folks who are considering data collection... ie, don't rely on Event History: SmartThings may arbitrarily truncate it at anytime. [55]

The custom commands have a little house profile icon in front of their names - you can add parameters there. [56]

Conditions have an empty circle, triggers have a full circle. If you're only using conditions, they all act as if they're all triggers (each condition has an equivalent trigger, like is <=> changes to) Once you mix them with triggers, they take on a secondary role of "restrictions". They won't generate events, they'll just limit events generated by triggers. [57]

Using a trigger will only provide one device at a time in your list, because only the device that triggerred the evaluation actually dropped below 30% at that time.

Dropped below is different from is below. This is because Dropped below involves transition. It senses the transition of a value past a threshold. So you won't get all devices in your list, because only one "transitions" past the 30% threshold at any given time.

Use a condition instead. IF [devices].battery is below 30% ..... this will still happen every time a battery level changes, but all devices below 30% would match true. But be advised, every time a battery level changes, you'll get an SMS, if any is below 30%. So if you have a chatty device that keeps reporting battery changes (should not happen in real life as they are supposed to conserve their battery, not waste it on reporting that battery is going down) you'll definitely see a lot of SMSs coming your way. You can fix this with a time trigger and the condition on battery. This way you can make the time trigger happen daily at whatever time you want it, so you only get up to one SMS a day... [58]

As long as you don't have any "prerequisite" conditions to the presence becoming present, you can safely use conditions. If, however, you have other conditions that need to be "passed" at the time presence changes, and not the other way (i.e. someone arrives and mode is home, but not if mode becomes home and someone was already present - sense the difference?) - then you need to use a trigger. When mixing triggers and conditions, only devices involved in the trigger can make things happen. When using conditions only, any device involved in those conditions is "eligible" to reevaluate the piston and do things. [59]

Conditions are independent of each other, non-contextual, if I may. "before 5am" literally means hour is 0, 1, 2, 3 or 4. Has no clue you'd used an after 7pm prior. Which is why you have Between. Besides that, AND is not the correct condition. Between 7pm and 5am is equivalent to after 7pm OR before 5am. Not AND... [60]

Logic Gates

However, all AND operands are evaluated without using conditional optimizations, whereas THEN IF uses optimizations.

Say you have conditions A true, B false, and C true.

A AND B AND C = false A THEN IF B THEN IF C = false

The difference is that when using AND, all A, B, and C get evaluated, so if C has individual actions on true, they will run. When using THEN IF, only A and B will get evaluated, C will be skipped as the overall result is now known to be false. So if you had individual actions on C, they won't run, because C never gets evaluated. [61]

Well, here's the trick: if motion is detected and the light is off, it turns it on and sets a variable to true (meaning I - the piston - turned the light on). When motion stops, if the variable is true (meaning I - the piston - turned it on), then it schedules an action to turn it off later. When the light was turned on manually or by any other means other than the piston, the light will already be on when motion is detected, so nothing will happen. That's the whole piston logic, in plain English :wink: [62]

Well, think about it, for a simple piston that turns true and executes the THEN branch, if the state changes, guess where it goes! To false, of course. And if the evalution is false, the ELSE would then run, no? :wink: when a piston state changes, it cancels all tasks marked to be canceled on piston state change and then it will execute whatever actions are programmed for that new state... [63]

The BUT IF is independent of the IF, so you can use it all conditions, the triggers in the IF won't interfere with it - it's essentially two pistons sharing the same state [64]

Else-If will only look at conditions2 if the overall result of conditions1 is false. Then-If does the opposite, it only looks at conditions2 if the overall result of conditions1 is true. The final ELSE statement is executed if the Else-If conditions2 is false, or if either of the Then-If conditions1 or conditions2 is false. [65]

It is intentional. Else-If and Then-If do not support triggers in their secondary IFs. The Else-If is only evaluated if the primary IF is false. Try a latching piston, it allows two independent condition sets. Or enable expert mode and then use individual actions, for more advanced cases. [66]

Your IF and ELSE IF cover all possible options, it's either any is active or all inactive. Your IF - ELSE IF will always match one of the two, never giving a chance to the ELSE to run. Consider moving the ELSE action under the IF THEN action group. Just add it to the IF THEN, along with the other Turn on action. It will only run during selected modes anyway. [67]


(Mode is Home OR Mode is Evening) can be replaced with Mode is one of [Home, Evening] [68]


If you choose anything other than custom time the offset field will reappear. Is that set to 60 by any chance? Custom time is not supposed to have an offset, but I have a feeling it still respects it. Bug. [69]

The stay will become true at the set time after the light turned on. If that time doesn't happen to be within your time window.... you miss it. [70]

Odd Week Scheduling - use time trigger happens at custom time (or any predefined time for that matter), then instead of every day, choose every number of weeks, choose 2 and select the day of the week Wednesday. Voila! [71]

The general rule I used is that the thresholds are at second 00 because that's when the event is expected to happen. So the beginning of any interval is inclusive of that minute, while the end is exclusive.

Time is between 7:31am and 8:47pm means from 7:31:00 am through 8:46:59 pm inclusive. 8:47:00pm would be outside the range.

Time is between 11pm and 5:12am means from 11:00:00pm to 5:11:59am next morning

Time is after is the exact equivalent of time is between threshold and midnight. Same rules as above, time is after 6:17pm means 6:17:00pm through 11:59:59pm inclusive.

Similarly, time is before 7:42pm means between 12:00:00am and 7:41:59pm inclusive.

Note that midnight has a special meaning. When used at the beginning of the range, it means >= 12:00:00am but when used at the end of the range it means <= 11:59:59pm

Time is between midnight and 5am means 12:00:00am through 4:59:59am inclusive Time is between 5am and midnight means 5:00:00am through 11:59:59pm inclusive.

Another way you can look at it is the first parameter in a range uses >= whereas the second/last parameter in a range uses < [72]

Time Calculations- store the $currentEventDate into a time variable of your choice, say eventDate. Later (on a different piston run), set a time variable, say ms = $now - eventDate

You now have the number of milliseconds between the two times stored into ms. You can then set number variable minutes = ms / 60000 or seconds = ms / 1000 [73]

Event Trigger

You need to either use all triggers (full dots) or all conditions (empty circles). When mixed, only triggers actually run the piston, the conditions will only restrict the runs. When using all conditions, they all act like triggers, pretty much.

So if you want all those devices to trigger the ligths, either use changes for all of them, or use is for all of them. For the garage door, you can also use is one of [open, closed] <<< that will always be true and run your piston when the door opens or closes.

I would personally go with triggers... acceleration changes to active OR motion changes to active OR garage door changes [74]

Else-If and Then-If do not support triggers in their secondary IFs. The Else-If is only evaluated if the primary IF is false. Try a latching piston, it allows two independent condition sets. Or enable expert mode and then use individual actions, for more advanced cases. [75]

When mixing triggers and conditions, the piston only subscribes to the devices involved in the triggers, so yeah, it will check the state of condition related devices at those times, but won't listen for their change. So if you say door is open (condition) and presence changes to present (trigger), the piston will only subscribe to presence changes. It will execute whenever presence changes, whether to present or not present. At those times, it checks the state of the door, but won't execute when the door state changes. [76]


Current evaluation of triggers is always false as the triggers watch for certain events happening, not certain states. For example, "door changes to open" may be false even when the door is open - it is only true for a very short moment when the door actually opens. I'd say ignore that current evaluation [77]

Changes to one of allows multiple selections and is only true when the change happens from a non-selected value to any of the selected value. Similar to "enters range", but list of values instead of range. [78]


Both triggers and conditions subscribe to attribute changes - this means when motion becomes inactive, that trigger would be false, along with the one above the OR - this will cause the ELSE to execute. So you just need to use a trigger for motion as well. [79]

Expert Mode

If Expert Mode is not enabled, then you'll need to make sure you select the right capability. If Expert Mode is on, you get an Attribute field while building the condition, so regardless of which capability you used, you have access to all options. But the default pre-selected Attribute will differ from one capability to another. [80]

When you enable expert mode a lot of "hidden" features are revealed. Like the attribute field in the Capability condition, the "Individual Actions" in each condition/trigger/group, flow control actions, etc. lots of things all over the place. [81]


Any action, either device or location, tap on Add task and them scroll down to find Send push notification. When controlling devices, the device specific commands are listed first. Then virtual commands follow (non device-implemented commands) with full circles. You have a lot of options in there, push, sms, notification (shows up in the ST app), contacts, etc. [82]

The push sends it to your phone, the other one just saves it in the Notifications page of your ST app [83]


Simulate is not able to alter current states so as far as presence goes, it may not help much. You can however add a virtual button or minimote and add a backdoor condition into the (presence is away OR minimote button #1 is pushed) that will allow you to run that side of the piston with a push of a virtual minimote button - use the ST app to push the button [ ]

The simulate will actually run an evaluation. The recovery only looks for past due time events - if it finds any, then it evaluates. If not, it continues by looking for past due tasks and executes them. Then it just preps the next time schedule. That's all [84]


Since you're using "execute on state change only" you need to design the piston to actually flip states once in a while. The ELSE IF is an OR in disguise. If any of the two is true, the piston is true. When both are false, the state flips to false. It seems like you never reach that false state? Try changing the mode to Latching instead of Else If. [85]

For the cancel on piston state change to work, you have to design the piston in such a way that it turns false when you don't want it to run - i.e. have a condition for the mode, so that when the mode changes, the action gets cancelled. If you just added that option, it will cancel all tasks when the mode actually changes - your mode did not change, you just changed the piston settings... the piston state only changes when devices that it subscribes to change. That explains why it still shows true. [86]

The piston state. That's where all different kind of pistons come into play. The state is calculated differently depending on the piston type. For the latching piston:

State changes to TRUE when state is FALSE and IF evaluates TRUE. State changes to FALSE when state is TRUE and BUT IF evaluates TRUE

In essence, if both IF and BUT IF are true, the piston state flips (changes to TRUE if it was FALSE and vice-versa), if only one of the two condition sets is true, then the piston becomes the state associated with that one, if none of the two is true, the piston state does NOT change.

With this information in hand, you can plan for state changes.

If you are using measurements that change in time, every measurement change will result in an evaluation. Assuming that the state is currently TRUE, and the IF is true and the BUT IF is false, the state remains TRUE, the THEN actions execute, except those that have "Only execute on piston state change". Those only run ONCE, when the piston state becomes true.

The TOS (Task Override Scope) does something else. If you have multiple actions controlling the same device (one in the IF's THEN, one in the BUT IF's THEN for example), you can choose to "override" all other pre-existing scheduled (pending) tasks for the involved devices, or you can just add to the schedule (by using TOS = None). If the IF THEN says Light turn on in 10 minutes and 2 minutes later, the BUT IF says light turn off, then this will happen:

TOS: None >>> light turns off, 8 minutes later, light turns on TOS: Action >>> light turns off, 8 minutes later, light turns on (the two tasks were added by two distinct/different actions) TOS: Local >>> light turns off - the turn on is canceled TOS: Global >>> same as Local, but across all pistons (if any other piston was planning on doing something with that light, it won't anymore) - the Global is not yet implemented, acts just like Local

To give you an example on when TOS Action is helpful: You have an action that is executed on motion becoming inactive: Wait 10 minutes, Turn light off. Motion becomes inactive, light is scheduled for turn off in 10 minutes. Motion becomes active again, and inactive again after 5 minutes. At this point, there is a scheduled turn off in the remaining 5 minutes. If TOS is Action (or above), that turn off in 5 minutes is canceled and replaced with a brand new one that will run in 10 minutes. Effectively resetting the timer. Use Action if you don't want to interfere with commands from other actions. The action would then only override it's own (Action scope) tasks. When using Local, an action asking a device to do a task overrides all tasks for the same device done by any action within the Local scope - the piston itself.

The Cancel on state change is different from TOS because it does NOT require another action to run to cancel it. The piston itself will cancel it if the piston state changes BEFORE the task is due. So this is - in a way - self contained, does not require another action. The TOS will not do anything unless the same or another action runs AGAIN and has the proper scope. [87]

They don't mutually exclude each other.

TCP is a way to cancel an already existing task OEPSC is a way to prevent a new task from being scheduled TOS is a way to cancel an already existing task WHEN a new task is added [88]

Piston restrictions prevent a piston from evaluating. An action restriction prevents the action from being scheduled.

The piston has three stages: Evaluation (when comparisons are made). Scheduling (when actions are decided and put into a queue). Execution (when actions queued during Scheduling are actually executed). Piston restrictions prevent all three stages. Action restrictions apply to the Scheduling stage. [89]

The easy way: enable cancel on piston state change for the turn off action. The more flexible way: Any action will (by default) override its own future scheduled tasks. What you want will happen automatically. Behavior is defined in the Task Override Scope for the action. If you use the scope Local, when motion is detected, the action that normally turns the light on will remove all turn off scheduled tasks forvthe specified devices, so you'll get exactly what you want. [90]

Each action has advanced options. There are two actually: Task Override Scope (None, Action, Local, or Global) Task Cancellation Policy (None, Cancel on piston state change) There is a short explanation on the override scope there... It prevents different actions fighting over the same device. Action means that when an action is executed, anything pending that has been previously scheduled by the SAME action is cancelled. Local refers to any action within the same piston. If action #1 schedules front light off in 5 minutes and action #2 turns it off now and has the local scope, action #1 is then cancelled... [91]

The "Execute on piston change only" is useful when you have more conditions, all working towards the piston state being true or false. If you have three conditions with OR between them, if anyone is true, your piston is true. The difference is that if you enable that, you'll only execute the action once, when the piston changes from false to true. Any subsequent evaluation that keeps the piston true won't run the action. In your case, you're okay, there are only two cases, open and closed, and the piston follows them. [92]

In the latest version, there is an option at the bottom of restriction to prevent actions that have already been scheduled to execute during times when the piston is restricted. By default, if an action has been scheduled (during unrestricted runs) then it WILL execute when its time come. [93]


Open it up and you will see Add a CoRE piston. When the new piston opens you will see piston Mode. Click on it and the drop down will give you the piston type options. [94]

Latching is different. State becomes true only when it is already false and the main IF is true. It turns false when it is already true and the BUT IF condition is true. [95]

Link to Piston Types Chart [96]

Simple piston: IF (conditions) THEN (actions) ELSE (actions) Else-If piston: IF (conditions) THEN (actions) ELSE IF (conditions) ELSE (actions) [97]

Else-If Piston This is a dual piston, because it supports two conditional sets. It allows for one conditional set to allow a second conditional set to be evaluated only if the first one was false.

Format: IF (conditions1) THEN (actions1) ELSE IF (conditions2) THEN (actions2) ELSE (actions3)

State: The state of the Else-If piston is deemed TRUE if either (conditions1) or (conditions2) evaluates as true, or FALSE otherwise


The (actions1) are executed if (conditions1) is true, (actions2) are executed if (conditions1) is false and (conditions2) is true, (actions3) are executed if both (conditions1) and (conditions2) are false [ ]

Follow-Up pistons are just like the Simple pistons. The only differences are:

  • does not support triggers
  • does not subscribe to anything

They can be initiated from other pistons via the Follow up with piston task. This task can be delayed just like any other using the Wait task in front of it, so you can technically say "IF (this) Then (that) Else (wait 5 min, follow up with piston). Even better, you can follow up with the piston over and over... the follow up piston can follow up with another piston, even itself. Combine that with a global variable counter and you can have a complex solution where something initiates the counter, does things and follows up with a piston that can follow up with itself x times until either your expectations are met, or the counter hits 0, hence giving up... I guess, unlimited possibilities [98]


Save attr to variable is a task, an action that happens upon the evaluation of a piston. Global variables are "pockets" in which tasks can store information for later use. [99]

Store the $currentEventDate into a time variable of your choice, say eventDate. Later (on a different piston run), set a time variable, say ms = $now - eventDate

You now have the number of milliseconds between the two times stored into ms. You can then set number variable minutes = ms / 60000 or seconds = ms / 1000 [100]

Save attribute to variable will allow you to use several sensors and get the average temperature. [101]

The $currentEventDevice represents the device that triggered the event that fired the piston. Stays is initially triggered by a device, and then it sets a timer for the requested time. Once that time elapsed, it gets a time event that triggers it to re-evaluate. That is no longer a physical device, hence the lack of a name. What you see instead is the location id. Use the stays trigger to save the list of matching devices into a variable, then use that variable instead. [102]

Save attribute to variable will allow you to use several sensors and get the average temperature. [103]

It currently applies floor when converting floats to integers:

if (value.isFloat()) return (int) Math.floor(value.toFloat()) [104]

Global vars use @ Also, $ variables are read only, you cannot set anything to them. [105]

Use any name for the "Save matching device list to variable" and then use that name between { } in the SMS message. When saving the info to a variable you can use @ to make that variable global. This means other pistons will have access to it as well. You don't have to, unless you need that info in other pistons. You won't be able to set any variable starting with $ as those are reserved system variables - they change on their own [106]

Go to local variables and look at the $currentEventXXXXXX ones. You can use {$currentEventValue} to show the temperature. [107]


Oh and also, recover and rebuild are two different things. Recover kicks piston started should they completely halt due to missed events, rebuild is supposed to do the actual fixing of corrupted pistons. [108]

For the time being:

The play button in the SmartApps page kicks all piston's butt Stage 1 and 2 recovery also kick all piston's butt Fast recovery is now working - each piston tells CoRE when it's supposed to run next, also giving CoRE a chance to check on all others. Once CoRE determines a piston was scheduled to run more than one minute ago, it gives it a nudge via a CoRE Recovery [app id goes here] event. [109]

it's the recovery that kicks it running every 3 hours, so that's 8 per day, plus second stage, once every 2 days. That is about 19. Plus one when you probably saved it. Running does not mean it does anything, it just attempts to execute any pending (overdue) tasks. No harm there. You can disable the recovery kicks in the main CoRE app under Settings. [110]

The first sign of such a corruption is an empty dashboard. CoRE is able to revive them, but as of today, this process is manual (once this process is deemed worthy, CoRE may automatically rebuild pistons when it needs to). You can find the rebuild option under Advanced Options in each piston's main page. If you are affected, the easiest way to determine which pistons are faulty is to open the Live logging in the IDE and then attempt to view the dashboard. Each affected piston will output an error about a null object. Visit each of them in the CoRE UI and tap on the Rebuild this CoRE piston under the Advanced Options section. Then repeat the process until the dashboard works again. Sorry for the trouble, I will get them to automatically rebuild in the near future, should such a rare event happen again. [111]


Why not set the mode from CoRE directly and rely on Routines? I have a few routines but don't use them anymore... Sounds a bit convoluted to have a smartapp ask another smartapp do something the first could have done in the first place. :wink: but then that's just me? [112]



There is something that is called a Do piston (change its mode in the main piston page). Then use the askAlexa CoRE Macro to run that piston. The Do pistons have no IFs.

The alternative is to create a simple piston using the Ask Alexa capability. You will have the chance to run something like IF (ask alexa macro) executed THEN (actions). Then you don't have to use an Ask Alexa CoRE Macro, but any macro at all, even one that does nothing. [113]

Hue Colors

Just to clarify a few things. The hue in ST is an unnatural percent, 0-100. The hue in the real world is an angle, 0-360 degrees. CoRE is expecting degrees, so that you can pick numbers from your favorite rgb>hsl converter. What you see in the logs there may actually be %, not °. If the color does not match, try applying a x3.6 factor to it. I asked at some point and the general choice was to use degrees, rather than %. [114]

If you are using specific hue, saturation and level, hue is an angle between 0-360, saturation and level are percentages, between 0-100. [115]

The hue that shows in the attribute should be 0-100. Multiply it by 3.6 when using it in CoRE [116]

RGB is a pair of three colors, Red, Green and Blue. Each one has a value between 0 and 255, where 0 means off and 255 full on. Each number is then encoded into two digit hexadecimal representation from 00 (decimal 0) to FF (decimal 255) and simply concatenated with the prefix #.

Red for example is R=255, G=0, B=0, or in hexadecimal format FF, 00, 00. In RGB that is #FF0000 [117]

Yes, setColor is in fact equivalent to setHue + setSaturation + setLevel. All three in one go. [118]

Colors always change hue/saturation/level. The "Deep" colors have lower levels. This is by design. [119]

The Fade to level can do ramping over seconds, minutes or even hours. The up/down is automatic, you just provide the target and the time to take. It will automatically determine whether it needs to raamp up or down. It uses software emulation, meaning the piston runs every so often to readjust the level. [120]


You can also send data to IFTTT via the Maker channel, a method supported by CoRE. Then find a service connected to IFTTT that can record that data... [121]

When making the request, CoRE allows you to select variables to send. Save data into variables and select them. In IFTTT define them as ingredients, then make sure you pass them to the action. [122]


Make the Sonos play some music. Go to the IDE and go to the Devices tab. Select the Sonos and look under the current state section for the attributes Sonos publishes. Make note of whichever is relevant to the playing state of Sonos. Then stop the Sonos and refresh that page (may need to refresh the sonos in the ST app too?) - see if the status changed. Make note of the status that represents the stopped state, minding lower and upper cases - Groovy - the underlying language of ST - is as case sensitive as it gets. Then in the CoRE piston, make a condition for that attribute matching the exact status - you may need to enable Expert Mode to be able to select the correct attribute (an Attribute field will show up). [123]


WARNING: As mentioned before, this SmartApp is in a BETA development stage and may be quirky. It may also change rapidly and drastically, involving a full reset of the app in order to upgrade. [124]

Updating the app code does not lose data. Pistons will survive [125]

I don't really think changes will be made to the extent where all pistons would have to be rebuilt. But changes where all the pistons need to be revisited, yes. Last one was when I removed the "Enabled" option from the piston UI so that I can control it from the dashboard, which provides a much faster way to enable/disable several pistons at a time. [126]