CoRE - Variables 101

Case Study: Incrementing Variables - Counting Up

The following case study will demonstrate creating a variable and adding 1 to it until we reach 10. It includes variable creation, initialization, and incrementing.

Requires:
1 Motion Sensor

Before starting, set for expert mode: Settings->ExpertFeatures turn ON Expert Mode

  1. Add Piston
  2. Once added, scroll to the very bottom of that page and select ‘Local Variables’

  1. Select ‘Initialize a variable’. Create a variable called ‘counter’ and set its value to 0. Press ‘Initialize!’. This is important otherwise the variable will not exist. Should you need to change this number in the future, you will need to change the value and press ‘Initialize!’ once again.

  1. Piston Mode is Simple (If-Then-Else)
  2. Follow below example to set up If statement
  3. For the Then statement, you will create an action/task Set Variable:
    -set the name of the variable to be the name of the local variable we set at the beginning, ‘counter’
    -set variable type to ‘number’
    -set ‘First operand…or variable value…’ to be ‘counter’
    -Press ‘Add operation’ and select ‘+(add)’
    -set ‘Second operand…Value’ to be 1




As you can see, you have multiple operations you can choose from when applying to variables:

Be sure to remember once your counter reaches 10, to continue testing you must reset the local variable within the Piston back to 0.

Knowledge is like manure. It’s meant to be spread around.

1 Like

Can you please tell me how you are getting “Please put the clothes in the dryer! 2 Hours has passed” notification? Are you using Power meter or some other trigger?

I can’t seem to find the logic for that in the screenshots you posted.

I use a total of 3 pistons for laundry.

The “nagger” piston below incorporates the 2 Hours has passed notification. You should be able to follow it. The variables washeron and dryeron are no longer used and have been replaced with virtual switches and a piston to control them. This way I can easily tell if a load is running in the washer or dryer and start a timer when they are running and how long.

Let me know if you need any further explanation.

The Laundry Washing Machine Contact Sensor is not being used as it was tapped to the drum inside the washing machine tucked in a ziplock bag. Worked good but the Power Meter works better. I will eventually re-purpose it.

Here is the Piston to control the Virtual Switches which are incorporated into the piston above.

My wife thought I was crazy trying to get this all working correctly but laundry is no longer an issue. Yep, all the complaining about how much money I was spending on this “nonsense” has equaled to her doing a full 180 and now creating various HA scenarios for me to work on!

CoRE and Variables makes for endless possibilities in SmartThings!

2 Likes

Default System Variables - Types and Examples

So it appears the default system variables will have a $ before their names. But what are their defaults at rest with their types and example? Listed below are only those variables that are non-null at rest. Note $random is a string, not a decimal. I’ve not found a way to cast from one type to the next yet.** Unknown why it was done this way. Putting this information here to be more complete with this thread.

As far as operations per type, it would appear these are the operations:

  • decimal (+,-,*,/)
  • boolean (AND, OR)
  • number (+,-,*,/)
  • string +(concatenate)
  • time (+,-,*,/)

System Variables (… indicates those left out with null values)

$day = number (Example: 17)
$dayOfWeek = number (Example: 5)
$dayOfWeekName = string (Example: Friday)
$hour = number (Example: 1)
$hour24 = number (Example: 13)

$locationMode = string (Example: Away)
$meridian = string (Example: PM)
$meridianWithDots = string (Example: P.M.)
$midnight = time (Example: Fri, Feb 18 @ 12:00 AM HST)
$minute = number (Example: 53)
$month = number (Example: 2)
$nextMidnight = time (Example: Fri, Feb 18 @ 12:00 AM HST)
$nextNoon = time (Example: Fri, Feb 18 @ 12:00 PM HST)

$nextSunset = time (Example: Fri, Feb 18 @ 6:55 AM HST)
$nextSunset = time (Example: Fri, Feb 17 @ 6:25 PM HST)
$noon = time (Example: Fri, Feb 17 2017 @ 6:25 PM HST)
$now = time (Example: Fri, Feb 17 2017 @ 1:51 PM HST)

$random = string (Example: 0.7815571)
$randomColor = string (Example: #AFEEEE)
$randomHue = number (Example: 339)
$randomLevel = number (Example: 76)
$randomSaturation = number (Example: 64)
$seconds = number (Example: 18)
$sunrise = time (Fri, Feb 18 2017 @ 6:55 AM HST)
$sunset = time (Fri, Feb 17 2017 @ 6:25 PM HST)
$time = string (current clock time. Example: 1:44 P.M.)
$time24 = string (current military clock time. Example: 13:44)
$year = number (Example: 2017)

**It does seem we can cast variable types in a limited form from what I see. For example, if you have an action of Door Sensor getting the temperature via ‘Save attribute to variable’, not only can you aggregate its value, but you can ‘Convert to data type’

2 Likes

Further Variable Examples

CoREsamples Variable Examples

1 Like

Case Study - NightlightPro

Demonstrates a handoff of timers between two motion sensors. Some may term this a cascade or a flip. Each motion sensor will change the others timers giving the effect of a cascade from one sensor to the other. This example will demonstrate the use of 2 variables along with switch and case statements.

This coRE was an idea based off of the groovy runin() scheduling functions. After some experiments, I found I could use the same methods inside of CoRE. It stated:

By default, if a method is scheduled to run in the future, and then if another call to runIn() with the same method is made, the last one overwrites the previously scheduled method. This is usually preferable.

Scene: Between midnight and sunrise, turn on bulb for 15 seconds if Motion Sensor 1(MS1) is active. Wait 10 seconds to see if I walked from MS1 to Motion Sensor 2(MS2). If MS2 is active, change the wait time from 15 seconds to 60 seconds. However, if I walk back into MS1, reset timer to 10 seconds. Turn off light after wait timer ends and all motion stops.

Requires:
2 Motion Sensors, Minimum (Switch statement may be extended for more sensors)
1 Bulb, Minimum

Before starting, set for expert mode: Settings->ExpertFeatures turn ON Expert Mode

*Note in my example, I use 3 motion sensors:
Bed Left Toe Motion Sensor
Bed Right Toe Motion Sensor
Bathroom Toe Motion Sensor

You’ll note on the Trigger of the motion sensors, you will be adding the variable @deviceList. This will return the current device activated such as ‘Bedroom Right Toe Motion Sensor’ in this example.

Here is a different view of the code. First, the ‘If True’:

And the ‘If False’:

You’ll then place each of the ‘If True’ and ‘If False’ with ‘between midnight/sunrise’ (my example shows testing of sunrise and sunset)

2 Likes

How do you get the bulb level?

First you will need to save the bulb ‘level’ into a new variable. In this example, the variable is a global variable called @lgtLevel. This simple example prints out the current value of the bulb.

@alanrosesf Thanks for starting this thread! And thanks to everyone that has contributed to it. I’ve still not wrapped my head around how CoRE really works, but I can see where variables will made a big difference in what I can do with CoRE.

So my first question to this thread…

On the Global Variables screen, does the “Initialize variables” option allow me to add my global variables? Or do I have to do that in the pistons as I build them? I guess the term initialize here confuses me.

Thanks!

You can do both. So yes you can create a global variable there or in the piston.

Let me try a quick answer to each

Questions:

  1. When should you use a local variable vs. a global variable?

You use a local variable when its value is not needed outside of that piston. If you need to share data between pistons (set the value in one or more pistons, get the value in a different piston, etc.) then you need to use a global variable.

  1. When a variable is global, does that mean it is available to all Pistons?

Yes, a global variable set by a piston can be read by any other pistons.

  1. When entering the name of a global variable, do I place a @ or $ before it? Is there a difference?

@ signifies a global variable, while $ signifies a system variable - system variables are read-only (you cannot set them) and provide information about the environment. For instance, $time gives you the current time. $sunrise gives you the time of today’s sunrise. $random gives you a random decimal number between 0 and 1, $randomLevel gives you a random level between 0 and 100, $nextSunrise gives you the date and time of the following sunrise (either today - if asked of very early - or tomorrow). For example, when a minimote button is pushed, $currentEventDeviceIndex gives you the button number that was pressed. You can find a list of all the variables, including their current values, if you open any piston in the dashboard and scroll just below the actual piston - the local variables are that piston’s own variables, so they’re relevant for that piston only.

  1. Where do you print the result of a variable? i.e. which screen shows the output of a variable for testing?

There is no print per se, but you can send it in a notification, or to other services by using http requests. Most text fields in CoRE support variables. To use a variable in a text, you need to use curly brackets { } around the variable name. For example Send Notification “This is a random number: {$random}”. You can see the list of all available variables in the dashboard, under any piston you open.

  1. Must you set the name and initialize a global variable under Global Variables on the main page? If not, what is the Global Variables on the main page meant for?

No. That page is there because it quickly became apparent that people were confused about how to create a variable. CoRE was designed in such a way that all you need to do is just use a Set Variable (or other fields that request a variable name) in any piston and provide a name for the variable - that variable would be automatically created. So no, you don’t have to initialize it, but you can if you want to. Or you can change the value manually, I guess that is where that page comes in handy.

Q) Can you create a variable with the same name of another local or global and if so does the most current setting over-ride the last?

Global variables are unique. You can’t have two global variables with the same name, placing a value into an existing variable obviously overrides the old value and saves the new one. Local variables are unique within any given piston. Two pistons can both use the same variable name, though the two variables are distinct, each having its own value. Also, each piston can only see its own variable.

Q) So far, I am familiar with Boolean, Number, String and Time. Have no idea what decimal is used for…

When setting a variable value, you get to choose what kind of data you want to store in it. Boolean can store true or false, Number can store an integer number (no decimals), String can hold any text, including all of the other type’s data (i.e. “false”, “12”), Time is a long number (similar to a unix timestamp) that contains the number of milliseconds since Jan 1st 1970. Current values hover around 1,488,460,769,000 - CoRE knows to format these into a readable date/time when used in a text field. Decimal is able to hold numbers that can or do have decimal places (i.e. 2.34).

Q) I’ve been wondering how “Execute during validation stage” can / should be used.

In order to avoid as many timeouts as possible (SmartApps are only awarded 20s to finish their execution, after which they are forcefully terminated), CoRE implements what I called stages: it has an evaluation stage and an execution stage. Evaluation happens first and it’s where CoRE is comparing conditions, deciding what needs to be done and what not. It queues all the decisions and then saves the list (in case it times out) and moves on to the execution stage, where it actually performs the tasks. The “execute during evaluation stage” will do just what it says. It will force that “Set Variable” to be executed during the evaluation (it will again be performed during the execution stage, later on). The one reason you need this for is if you alter the value of a variable (using a when true/false action) and then further down the piston code you have an IF that uses that variable. IF (Door is open) >> when true Set variable x = 1 THEN IF x == 1 THEN… Had you not enable that “execute during evaluation stage”, the second IF would still see the old value of x, not the 1 you just set. Hope it clarifies things a bit.

NOTE: CoRE (SE) (name is not final) is underway and it will sport a full HTML interface, taking the pain away from editing pistons (pfew). There will be several very important changes in there. For example, in CoRE {name} represents the value of a variable named “name”. In CoRE (SE) the { } signify an expression is included between the brackets. Expressions bring in several benefits, one being the ability to use calculations inside the { }, not just a single variable (i.e. {@test + 3 * (anotherVariable - 1)} ), the ability to use predefined functions (like avg(values), min(values), sum(values), stdev(values), format(format, values), round(decimal, precision), floor(decimal, precision), sqr(decimal), sqrt(decimal), power(decimal, power), etc.) and also the ability to reference device attribute values by using [name:attribute] where name is the name of a device, or the name of a variable referencing a device, i.e. [$currentEventDevice:contact]. This may render variables useless (though I’d still use them here and there). For example,

IF { avg( [Kitchen:temperature], [Living Room:temperature], [Bedroom:temperature] ) } exits range {@minTemp} … {@maxTemp} THEN do something

In other words, the condition is able to calculate the average of the temperatures in the three rooms I selected and compare them to two global variables that I can modify in other pistons. You can also use conversions in your notifications:

Send Notification “The outside temperature is { format(‘%.1f’, [Outside Sensor:temperature]) }°F or { format(‘%.1f’, celsius([Outside Sensor:temperature])) }°C”

This would always format the temperatures with one decimal place (because of the %.1f - see more formats here) Notice how it can even convert the temperature to Celsius on the fly :slight_smile:

Or here’s another one, to know when any two doors are open:

IF { count( [Front Door:contact], [Back Door:contact], [Patio Door:contact], [Sliding Door:contact] ) } is greater than 1 THEN do something

For those who don’t really fancy expressions, you can also do this in the UI:

Also, CoRE (SE) will allow multiple installation instances (there is a hack people are using to install CoRE multiple times) and I am considering even adding super global variables (@@) which will even work across instances of CoRE (SE). He he :smiley:

Enough with the show off for now, sorry, had to do it LOL

8 Likes

As for logic, I’ve always found this page helps me think things through:

Request for List examples

Is it possible to get a list of devices into a variable and then loop over that list? If so, how would you iterate over them?

Sorry, CoRE cannot do that. There are plans for CoRE (SE) to support lists of devices, including iteration over such lists.

Thank you.

Any examples of a while loop?

Sorry, not following, any example for what, CoRE (SE)? CoRE is not able to use a variable as a device. There were people requesting the use of $currentEventDevice to execute commands with it, but that never got materialized. In CoRE (SE) I’m planning on allowing variables to store devices and/or lists of devices, with automatic conversion to string, when needed (in CoRE storing the matching device list would store an enumeration of device names, CoRE (SE) will actually store references to the devices, but when used in a string, it will automatically provide a string enumeration of their names). Also, CoRE (SE) has an open statement-based script structure. You have basic statements like if, for, while, repeat which can be used as building blocks to make complex scripts. The loops are not yet functional.

So sorry for not being specific. I’m curious about non-SE core examples, as lists are not available I was going to bang my head with while loops and switch statements.

What are you trying to achieve?

Now that Lutron Caseta is integrated, I’m trying to account for their rare power failure modes, On 100%. Lutron states the default is to return to the previous state prior to the power failure. In my core script, Power Failure, I attempt to detect lights above 99% and turn them off. I wished to loop over a list of the bulbs to test each of their levels.

I may be able with while loops and switch/case statements to do this.

Let me ask this, if I Save Attribute of Switch with an aggregation of Count I get a number, say 3. If I then loop over the levels of the bulbs and get those, are the ordering guaranteed to be in the same order? Or does this not make sense?

Thinking this through here, I may simply be able to take the MAX of the levels and do an ‘if’ above 99%. That makes more sense than trying to build my own list capability.