Custom Component: Generic Variable Entities

Overview: The var integration

Hey all. I’ve created a custom component called var for generic variable entities. This component is basically a slightly more flexible template sensor. Variable entity attributes can be set manually from a service or dynamically through the use of templates and/or SQL queries. All of a variable’s attributes (including dynamic template attributes) can be set using the var.set service. Variables can be displayed in Lovelace in all of the same places that sensors are displayed. Each variable entity contains one value which can be of any type (a tuple, for example).

Variables can be updated based on events, including the state changed events of other entities.

I’ve found this component to be a bit more convenient than template sensors which require separate backing data.

I should also note that there is another custom component for variables that is floating around, “hass-variables” by rogro82. Their component has different aims than this one, and there were a few things I wanted to do differently which is why I made this one.

Let me know what you think!

Features

Current Features

  • Create variable entities by adding them under var: in configuration.yaml
  • Set the value of one or more variables using the var.set service - values can be computed using data_template
  • Set any other variable attributes using the var.set service (e.g., icon, entity_picture) - these can be computed using data_template as well!
  • Specify a value_template and a variable will update dynamically using that template
  • Specify attribute templates (e.g., icon_template, entity_picture_template) and those attributes will update dynamically
  • Display variables in Lovelace UI in the same way as other sensor components

Advanced Features

  • Update a variable whenever the state of one or more specified entities changes (tracked_entity_id)
  • Update a variable whenever one or more specified events fire (tracked_event_type)
  • Update templates (e.g., value_template, friendly_name_template) dynamically using var.set
  • Update the value of a variable using an SQL query (useful for history statistics)
  • Use the results of an SQL query in a template

Why?

  • It was tedious to create a corresponding separate template sensor for each entity in the UI.
  • I wanted a single general-purpose component, with a generic name, that could be used to store, update, and display values using templates.
  • I didn’t like using named UI components to store first-class data (e.g. input_text ).
  • I wanted to be able to work with data directly from the home assistant database (especially custom events) without having to create and flip-flop between a bunch of different entities.
  • I wanted a custom component that I could extend with more features in the future.

Useful links

4 Likes

Good to have variable concept in HA. I don’t see the difference with the rogro82 custom component. Can you explain?

I’m not intimately familiar with rogro82’s component, but I do know that it has an unconventional way to tack on additional attributes to the entity using a JSON structure. It doesn’t handle live updates via templates, and it doesn’t support setting multiple variables at once to my knowledge.

I think the examples on the github page should help clarify.

can the two components (yours and rogro82’s) operate alongside each other? Will they interfere with each other in some way?

They have the same component name (“variable”). That’s the only thing stopping them from running together.

I like the dynamic variable. This is not available in rogro82 implementation.
@finity not sure as the two custom components declare the same domain ‘variable’
@snarkysnark perhaps you can name your custom components ‘dynamic_variable’ or something like that to allow the two custom components to cohexist?8

Looking more closely at rogro82’s component, it does appear to support a value template through the “value_template” key, and other templates through the “attributes_template”. However, as you mention @oncleben31, it doesn’t update these values dynamically.

I don’t like it’s use of JSON to pass attributes, though which seems to be the other main difference between these components. I’m guessing the JSON is required in order to be able to pass ad-hoc named attributes, but I didn’t see a use for those over multiple variables.

I’m happy to change the name of this component. That seems like a good idea.

I’m open to suggestions for a new name. “dynamic_variable” is sensible, but a bit too verbose for how often the entities are referenced in the yaml and jinja.

I might have to think on this a bit. :thinking:

It’s not as elegant but you could use “dyn_var” for yours. It’ definitely more succinct. :slightly_smiling_face:

And it’s not very different than the naming conventions of many other languages that declare variables (“var”).

I’ve changed the domain to var which I think is a good compromise.

1 Like

I’ve been contemplating where to take this next.

I’d like for the variable entities to be able to assume values from a db query like the SQL sensor component. In particular, I’d like to query events from the Home Assistant db and use that query as the variable state. For example, I’d like to be able to specify something like, “This variable is the number of ‘door_open’ events in the trailing 3 days” or “this variable is the average of all ‘light_events’ whose ‘event_data.brightness’ is greater than zero”.

A few things I’m working through:

  • is worth incorporating the sql sensor code to achieve this (shouldn’t be difficult)?
  • should the config params be an SQL query or a more limited interface?
  • how to write an SQL query to filter events from the db based on event_data? I can do it for event_type AND time_fired, but it’s not clear to me how to write a query that filters on event_data which is a dict stored as the SQL TEXT type.
1 Like

Well, I’ve incorporated the SQL sensor functionality into the var integration, but I can’t seem to get the variable to update after the recorder component writes the event to the database. I’m not sure if this strict ordering is possible without modifying home assistant. I’m looking into it…

The ugly alternative is to add an option to update the variable state based on some time period.

Update1: It looks like the recorder component is a singleton instance that I can access through the hass instance. I might be able to do a non-blocking wait on the recorder until it’s done updating the database after an event fires, but I’m entirely unsure if it will work.

Update2: I’ve tried to synchronize with the recorder by adding the following await statements to an event listener callback. The idea is that the variable should update itself after an event fired, but only once the event has been written to the database by the recorder.

# Calling this in the event listener callback doesn't seem to work...
await self.hass.async_block_till_done()
await self.hass.async_add_job(self.hass.data[recorder.DATA_INSTANCE].block_till_done)

However, this doesn’t work - the event is written to the database, but the variable doesn’t update. I’m not sure what else to do. I’m close, but something isn’t quite right.

Work-in-progess branch that’s currently failing:

Code references for usage of await and block_till_done:

Side note, this is the error I get:

RuntimeError: cannot reuse already awaited coroutine

I can see many uses for this. Thanks for the effort put forth to give us a new tool.

Could you possibly convert this for HACS input? That will make updating much faster for us.

Could you possibly convert this for HACS input? That will make updating much faster for us.

@toofewacres: Sure. Actually, from what I can tell, this integration is already compatible with HACS. Let me know if this isn’t the case, and I’ll be happy to fix it.

In the mean time, I have a couple of bug fixes to push, and this database sync problem to solve.

@toofewacres, I submitted a pull request to add this integration to HACS. I’ll let you know when it has been added. :+1:

Great, It’s already there in HACS. Thanks.

I’ve added Releases to the GitHub repo, so those should show up in HACs now, too.

Release v0.2.0 is available today and it adds tracked events and SQL query support!

1 Like

This is an awesome component, especially given the ability to auto-update from another entity. I finished migrating everything from input_* to var.* (and all Tasker calls!).

Now, I was hoping to use this as a way to track my last-x contact closures.

  outer_front_door_log:
    friendly_name: "Outer front door log"
    value_template: >-
      {# Only track opens #}
      {% if states.binary_sensor.outer_front_door.state == 'on' %}
        {# If the variable is empty, don't use a delimiter #}
        {% if is_state('var.outer_front_door_log', '') %}
          {% set delimiter= '' %}
        {% else %}
          {% set delimiter= '|' %}
        {% endif %}
        {{ states('var.outer_front_door_log') }}{{delimiter}}Front Door: {{ as_timestamp(states.binary_sensor.outer_front_door.attributes.timestamp) | timestamp_custom('%a, %m-%d, %I:%M %p') }}
      {% else %}
        {# return our current value if we did not just open #}
        {{ states('var.outer_front_door_log') }}
      {% endif %}
    tracked_entity_id:
      - binary_sensor.outer_front_door

I’m realizing that it fires just after start-up as the tracked_entity_id is restored with its previous value.

This would be easy to prevent in a automation that could be enabled in the homeassistant_start automation, but that’s not making use if this cool new stuff.

From peeking above, it appears you might be investing a logbook solution, too. Maybe I should just chill?
(I hunted around - if there’s a way to detect HA is just starting, I’m not finding it.)

Keep up the good work! :+1:

I’ve noticed this issue myself, but I’m not yet sure how to handle it.

I think the behavior that we want is that a variable should restore its state on startup (if restore is not set to false), but the variable should not update itself on startup (unless it tracks the homeassistant_start event).

Like you mentioned, tracked entities seem to emit a state change event when their state is restored on startup. So, I need to find a way to identify and ignore those startup state change events. I’m not exactly sure how to do that, but I’ll do some thinking.

Sorry, I’m not sure what you’re referring to regarding the logbook. Variables do show up in the logbook if you configure it to track the var domain.

Ah, no worries. I guess I got excited reading this:

:star_struck: