Skip to content

Expression with Entities and Attributes

One of the most useful things that the expressions used for global variables and rule-based (local) variables can do is gather data from an entity and operate on it. This page will give you a basic foundation for doing those kinds of expressions, and give you two versions of perhaps the most commonly-used problems they can solve.

Before diving in, you'll want to familiarize yourself with the expression language. You may want to print those pages so you can refer to them while we're working these examples.

Let's start with the simplest building block, which by itself isn't very useful, but is a necessary foundational concept: getting the entity data as an object. All of the examples here will be expressions that you can enter into a global expression (on the Expressions page) and evaluate to see the results.

getEntity( "ctrl>id" )

This simple expression will return the located entity as an object, or null if not matching entity could be found. In order to make this work, you need to change ctrl>id to the canonical ID of one of the entities in your system. A list of entities is published by the Entities page, in a column of its own to the right of the Entity Name column. Notice that these canonical IDs have two parts, separated by a greater-than (>) character. The first part is the controller ID, and the second part is the entity ID. This two-part form unambiguously identifies an entity even if, for example, you have multiple Vera or Hubitat controllers, or multiple Home Assistant instances. It tells which controller and which entity. So again, to make this example work for you, you'll need to replace the example text ctrl>id with a canonical ID from your Entities list. I recommend you choose a switch or dimmer (filter the Entities list by choosing power_switch from the capability filter selector), so you can follow the progress of my example here exactly.

Underneath your expression you'll see the Last value label and (hopefully) the word object in parentheses, followed by a bunch of data (probably so much data that it's been shortened and left with an ellipsis (...) at the end to tell you there was more. You can hover over the value to see the full value string. Before continuing, make sure you've selected a valid object and that your result so far is not null. If it's null, your canonical ID isn't valid.

We need to navigate within this object to get to the data we want. To do that, we dereference, which is a programming term that means selecting what part of the object we want to look at. An entity object is made up of properties, and one of the properties of an entity is its list of attributes. These attributes are where the various values related to the device/entity are stored (you may want to brush up on terminology by rereading the Concepts and Terminology page again). In the Reactor expression language, we use the same dereferencing operator as C++, Java, JavaScript, C#, Python, and a number of other languages; it's almost a standard — the dot (.). So if we want to get to the attributes of our selected entity, we add a dot and the name of the property, which is attributes, like this:

getEntity( "ctrl>id" ).attributes

Now if you evaluate this expression you will still see that the result is an object, but it's different data. We have just drilled down one level into the entity's data (the object).

The attributes of an entity are stored filed by capability. Since we're looking at a switch, it will probably have the power_switch capability, because this is pretty much the defining capability of a switch: the ability to turn on and off. There may be other capability names visible to you in the Last value display, but we're just going to focus on power_switch by again using that dot operator to get down into it:

getEntity( "ctrl>id" ).attributes.power_switch

After evaluating this expression, you again see an object result, and these results are the attributes defined by the power_switch capability. There aren't many, as you can see. In fact, you probably only see state as the only key. A key is a name used for dereferencing in an object, like the subscript of an array. You used attributes and power_switch in the expression so far, and those are also keys. It's just a generic term for a name. We can take the last step to get the current on/off state of the switch dereferencing to the state key:

getEntity( "ctrl>id" ).attributes.power_switch.state

At this point, your result should be boolean type, value true or false, indicating the current state of your selected switch (true = switch on). This form of expression is the most commonly-used building block for accessing data in an entity, and from here, you can do many other things.

Using Entity Attributes in Expressions

Let's step away from power_switch for a moment and look at a temperature sensor, and for this example, we'll assume the power sensor reports its value in degrees Kelvin, and we want to convert it to degree Celsius. We would first write the expression to get the value from the sensor, and we know that our temperature sensor has the temperature_sensor capability that publishes a value attribute (remember, you can see the capabilities and attributes for any entity in the Entities list; there's also documentation for the most commonly-used capabilities, how they are used, what their attributes mean, and what actions they support).

getEntity( "ctrl>id" ).attributes.temperature_sensor.value

OK. So far we have the value the way the hub gives it to us, which we believe is Kelvin. To convert to Celsius, we just need to do a (very) little math, which we can do in the same expression:

getEntity( "ctrl>id" ).attributes.temperature_sensor.value + 273.15

Now we have the fetch of the temperature and the conversion to Celsius all in one expression. Easy. So your expression can be much more than a fetch. There's no requirement that it contain only the fetch of a value, you can make it as complex as you can tolerate.

Handling Multiple Entities

Sometimes it's useful to either find all the entities having an attribute with a certain value, or go through a defined list of entities looking for a certain attribute's value. We'll do an example of each, starting with this: find all the entities that are battery-powered and have 25% battery or less (i.e. they're low).

The expression language has a function called matchEntities() that can be used to create a list of entities (an array) that match some given criteria. Since we're going to take this step by step, let's use a new expression for each step, so make a new expression called battery_powered and put in this expression for it:

matchEntities( { capability: 'battery_power' } )

Here, we're just telling matchEntities() that we only have one criterion: pick entities that have the battery_power capability. If you evaluate this expression, and you have at least one battery-powered device, you'll get a list of matching canonical IDs. If you see (array:0) [] as the Last value, you either don't have any battery-powered devices, or you misspelled something.

Now that we have an array of canonical IDs, how do we navigate through it to find only the ones that are 25% battery or less? The expression language has an iteration statement, which is programming buzzwords for a statement that touches each element of our array. The statement will go through all of the elements of our battery-powered device list/array and test them to see if they match our condition. We will set up our condition so that only those devices that have less than 25% battery will result (in a new array). Call this expression low_battery:

each id in battery_powered: getEntity( id ).attributes.battery_power.level <= 0.25 ? id : null

OK. This has a little expression stunt-work that bears some explanation. If we read the expression from left to right, the each id in battery_powered tells it how to loop and over what: we're going to go through the array of IDs of battery-powered devices, and for each one, we're going to put its canonical ID (the value in the array) into a local variable id. In the second part of the expression, we first fetch the entity data using getEntity(), then dereference into the attributes to ultimately get the battery power level last reported for the device. The comparison operator then gives us a true result if it is, or false otherwise. Now, what's the ? and : part about? That's a ternary operator. If you program in C/C++/variants it will be familiar to you. If it's not familiar, it's basically a short-cut if...then...else... in this case, if the comparison is true, the id of the entity will result from this subexpression, and if the comparison is false, null will be returned by this subexpression. We could have written this in a more verbose form, which some people prefer, here shown with spacing added for clarity (the expression language doesn't care about the spacing):

each id in battery_powered:
    if getEntity( id ).attributes.battery_power.level <= 0.25
        then id
        else null

The each statement will then look at that result, and if it's null, it will ignore it; but if it's not null (that is, it's an entity ID of a device having <= 25% battery), it will put it into a new array. When all of the values in battery_powered have been examined by each, it will return a new array containing the non-null results. When you run this expressions, you may get an empty array result, and that's because you have no low-battery devices at the moment. You can try changing the 0.25 to something silly like 0.90 (90%) to see if it picks up some devices that have good, but not perfect, batteries, just to convince yourself this works.

Before we go on to the next example, know that you can write this as one expression. We did this as two expressions to make it a little easier to see the two parts working, but you can write it as one like this:

each id in matchEntities( { capability: 'battery_power' } ):
    getEntity( id ).attributes.battery_power.level <= 0.25 ? id : null

Notice also I've split this expression across two lines, just to make it easier to read. Spacing and newlines aren't significant to the expression language.

Now the second example: working from a fixed list of devices. Let's say I have a handful of door sensors that cover the perimeter of my home, and I want to make a list of all perimeter sensors that are open. First, we make the list as an array by building an expression using array definition syntax. We'll call this expression perimeter_doors:

[ "vera>device_210", "hass>door_sensor_kitchen", "hubitat>23" ]

When you evaluate this expression, you get pretty much the same thing you typed in. We're just making a list of devices. Now let's do an each that goes through them and finds the open ones, which are denoted by the door_sensor capability state attributes being false. We'll make a new expression called open_perimeter_doors:

each doorid in perimeter_doors: getEntity( doorid ).attributes.door_sensor.state ? null : doorid

A little tricky here, maybe... it looks a lot like our previous example, but I've done two things, one trivial and one important. First the trivial: I changed the name of our loop variable from id to doorid, just to show you that it doesn't have to named id, you can call it anything you want, as long as you use that name consistently on both sides of the colon (:). The second thing is less trivial: notice in my result expression, the positions of null and doorid are reversed from the way they were used in the previous example. Why? Well, because when door_sensor.state is true, the door is closed/secure, and we don't care about that, we want to list open doors, so in this case, we will answer null when the door is secure, and answer doorid when it's not.

In the same way as the previous example, we could combine this into a single expression, but in this case, you may not want to. Having the list of devices living on its own makes it easy to edit in future, so if you add/change/remove devices, the editing is easier and you're less likely to break the more complicated expression.

Before we move on to another topic, let's embellish these two examples: let's say you want to send yourself an email with the names of the low-battery devices or open perimeter doors. Our expressions have given us a list of IDs, but how do we get names? Really, it's just more of the same:

each doorid in open_perimeter_doors: getEntity( doorid ).name

Short and sweet, here's what this does: for each ID in our list of open_perimeter_doors, get the entity, and from that, get its name, which is another property of the entity object. There's no condition here, it's just going through every ID listed and giving back a name, so the result of this statement is a new array containing the names of all the open doors. To put that into a message, it's probably more useful to have it as a string with which you can use substitition in a Notify action, so we'll just tweak this expression using the join() function, which converts the elements of an array into a string joined by a given separator:

    join( each doorid in open_perimeter_doors: getEntity( doorid ).name, ", " )

Now we have a string suitable for emailing. We've used comma followed by a space as our separator to make it neat and human-readable (i.e. so it's "a, b, c" rather than "a,b,c").

And of course, if we want to do everything (except make the list of entities) in one expression, it could be:

join( each doorid in perimeter_doors: do
           e = getEntity( doorid ),
           e.attributes.door_sensor.state ? null :
    , ", " )

This version both filters the perimeter doors by those that are open and gets the name, so the result is a list of names, again joined with comma-space. Notice we have to use a do...done block to contain the multiple statements of the each body, and we use a temporary variable e in the each body to store the entity object data so we don't have use getEntity() twice (once for the sensor state and once for the name), which is inefficient.

Going Deeper: Attribute Metadata

Metadata, generally, is data about data. For example, we started our examples looking at the power_switch.state attribute, which we know to be a boolean value with true representing on and false representing off. The type (boolean) is metadata: it's information about the value itself (what type it is). The fact that it can be true or false is also metadata (its possible range of valid values).

Attributes in Reactor have metadata that is accessible via the attribute_meta property in entity objects returned by getEntity(). You can use this property name (key) to navigate down to a specific element of metadata about one of the entity's attributes. Probably one of the most useful is last_modified, which tells you when the value of the attribute last changed.

Going back to our power_switch.state example, it should therefore be clear that you can not only get the state of the switch by fetching getEntity().attributes.power_switch.state, but you can also get the time it took that state by fetching getEntity().attribute_meta.power_switch.state.last_modified. That makes it pretty easy to figure out if the switch is on, and for how long:

e=getEntity( "ctrl>id" ),
e.attributes.power_switch.state ? ( time() - e.attribute_meta.power_switch.state.last_modified ) : 0

The above is a compound expression (in this case two expressions that execute as one unit). The first fetches the entity object for the switch and stores it in a local variable called e, because we're going to need to look at the object twice in the next expression (i.e. we avoid using getEntity() twice for the same entity). The second dereferences through e to get the attribute value and metadata last-modified time for power_switch.state. It uses the ternary operator (?...:...) so that, if the switch is on, it returns the amount of time the switch has been on (units are milliseconds), or just 0 if the switch is off. The result of a compound expression is always the result of the last expression.

The following are the currently defined metadata keys for all attributes:

  • last_modified — the time the current value was set to the attribute (a Unix Epoch time in milliseconds);
  • previous_value — the previous value the attribute had;
  • previous_last_modified — the time at which the previous value was originally assigned (Epoch time, ms).

There are other metadata keys possible. You can see the metadata for an attribute by hovering over the attribute in the detail panel of an entity in the Entities list. Commonly, you'll see the data type of the attribute if it's defined, as well as value ranges and other restrictions that may apply to the attribute's value1. In future, additional metadata may be provided as utility demands.

Let's clarify how previous_last_modified is updated using an example: if a switch is turned on at 8:15am, the value of its power_switch.state attribute will be set to true, and the last_modified metadata value will be set to 8:15am.

Turn ON at 8:15am
power_switch.state = true
last_modified = 08:15
previous_value = false
previous_last_modified = ...

Five minutes later at 8:20am, the switch is turned off, so before the attribute power_switch.state is set to false, Reactor sets the previous_value metadata value to the current attribute value (i.e. previous_value is set to true), and sets previous_last_modified to the current value of last_modified. Then the attribute value of power_switch.state is set false and last_modified is set to 8:20am. In other words, the attribute value and its last_modified time are preserved in previous_ metadata before the new values are set.

Turn ON at 8:15am                  >>>     Turn OFF at 8:20am
--------------------------------           --------------------------------
power_switch.state = true  ---->----+      power_switch.state = false
last_modified = 08:15  ------>----+  \     last_modified = 08:20
previous_value = false             \  +--> previous_value = true
previous_last_modified = ...        +----> previous_last_modified = 08:15

The purpose of handling th previous_ metadata this way is to make it easy to tell both (a) what the current value of the attribute is and how long it has been at that value, and (b) what the previous value of the attribute was and how long it was that value. Using the example above, one can tell how long the light was on (i.e. was in its previous state, because it's now off) by subtracting previous_last_modified from last_modified (8:20 - 8:15 = 5 minutes). One can tell how long it has been at its current value by subtracting last_modified from time() (the current time, whatever it may then be).

Updated: 2023-Oct-20

  1. The restrictions noted in Reactor's definition of an attribute are advisory only and are not enforced in the UI. Reactor will allow you to enter/assign a value that is inconsistent with the declared type or out of the declared range. This is intentional, to provide maximum flexibility in these values, as the hubs we work with often have inconsistencies in their data models that could not be represented if we enforced these rules strictly. However, you may note, in actions for example, that the UI highlights an entered value in yellow to indicate that it is not consistent with declared restrictions, but this is only a warning and the (re)action can be saved with the "non-compliant" value.