Trigger based template sensor to store global variables

This was posted earlier here. I’ve added some improvements, mostly to avoid errors, and also added some additional stuff suggested by @erkr in this post

It can happen you want to store some data to use it in another automation, or make sure it’s still available after a restart of HA. You can use helpers for that (eg and input_text entity) but these are limited in which types you can store, and they can be easily edited in the GUI.

The configuration below creates a trigger based templates sensor which can be used to store data. As trigger based template sensors restore their state and attributes after a restart, the data will survive restarts.

You can also store any type of data in this sensor, it can be a string, decimal number, integer but also a list or dictionary.

Note: there is a limitation how much data you can store in an attribute. If it becomes too big, it will not be stored in the database, and can therefor not be retrieved. If you really want to store a lot of data, you can maybe create a separate sensor for that.

Sensor configuration

# this configuration can be placed in configuration.yaml
template:
  - trigger:
      - platform: event
        event_type: set_variable
      - platform: event
        event_type: remove_variable
      - platform: event
        event_type: clear_variables
    action:
      - if: "{{ this.attributes.get('log_events', true) }}"
        then:
          - service: logbook.log
            data:
              name: "{{ trigger.event.event_type }}:"
              message: >
                {{ trigger.event.data.key | default('-') }}
                {%- if trigger.event.event_type == 'set_variable' -%}
                  : {{ trigger.event.data.value | default('not defined!')}}.
                {%- endif -%}
    sensor:
      - unique_id: 4a4c8e53-9e68-4198-9cc5-b336e228ea4d
        name: Variables
        state: Variables
        attributes:
          default_timestamp: true
          log_events: false
          variables: >
            {% set current = this.attributes.get('variables', {}) %}
            {% if trigger.event.event_type == 'set_variable' 
              and trigger.event.data.value is defined
              and trigger.event.data.key is defined
            %}
              {% if trigger.event.data.get('set_timestamp', this.attributes.get('default_timestamp', false)) %}
                {% set value  = trigger.event.data.value %}
                {% set value = value.isoformat() if value is datetime else value %}
                {% set new = {trigger.event.data.key: {'value': value, 'timestamp': now().isoformat()}} %}
              {% else %}
                {% set new = {trigger.event.data.key: trigger.event.data.value} %}
              {% endif %}
              {{ dict(current, **new) }}
            {% elif trigger.event.event_type == 'remove_variable' 
              and trigger.event.data.key is defined
            %}
              {{ dict(current.items() | rejectattr('0', 'eq', trigger.event.data.key)) }}
            {% elif trigger.event.event_type == 'clear_variables' %}
              {{ {} }}
            {% else %}
              {{ current }}
            {% endif %}

You can set the value for the set_timestamp attribute to true if you want to add a timestamp for the event by default, this can be overridden when you actually store a variable.

If you set log_events to true the events on which the sensor was triggered will be logged in the logbook.

How to add a variable

The sensor uses event triggers to store the data, you can send a custom event as an automation or script action, or you can use developer tools > events to send it.

# example for an action in an automation
action:
  - event: set_variable
    event_data: 
      key: some_test
      value: Some Value
      set_timestamp: false

Event data parameters

parameter type required explanation
key string yes the name of the variable
value string yes the value which you want to store this kan be any kind of value. datetime objects will be stored as isoformat string
set_timestamp boolean no Use this paramter to set the default timestamp setting used in the sensor

Exemple of the attributes of the sensor after adding some variables

default_timestamp: true
log_events: false
variables:
  test_without_timestamp: What time is it?
  test_with_timestamp:
    value: I know the time!
    timestamp: '2022-10-02T20:19:41.713241+02:00'
  some_list:
    - item 1
    - item 2
  a_mapping:
    fruit: banana
    veggies: broccoli
friendly_name: Variables

How to retreive a variable.

To retreive a variable, you’ll need to use a template. The template differs for variables stored with or without a timestamp.

With timestamp

replace variable_key with the actual key used when adding the variable

{{ state_attr('sensor.variables', 'variables')['variable_key'].value }}

Without timestamp

replace variable_key with the actual key used when adding the variable

{{ state_attr('sensor.variables', 'variables')['variable_key'] }}

How to remove variables

You can send an event similar as when you added a variable.

action: # example for an action in an automation
  - event: remove_variable
    event_data: 
      key: some_test

You can remove all stored variables in one go by sending the clear_variables event without further event data.


19 Likes

Glad you posted this, I’ve been using this successfully for some time now.

FYI there’s some funky indentation going on around line 50ish, around the if/then and logbook service that prevents an easy copy/paste into the config yaml.

1 Like

Thanks, should be fixed now

Have been using this the past couple of weeks, and it was spamming an error. Just figured out what it was complaining about.
You have this:

stating not required, so I didn’t put the set_timestamp key in at all. If I put the key in and say false, the error goes away. No key, it spams. Still works for what I needed either way, though.

I can probably resolve that :slight_smile: let me check that when I’m back from holiday

1 Like

If I add it to the configuration I get to see this:

What does the config checker in developer tools > YAML say about this? If should be fine unless you’re on a very old version of HA

This is almost exactly what I am looking for. I say almost because I want to be able to include many items in the event data.

Has anyone managed to convert it so this possible? I have been trying for a couple of hours and I think I have come close but it seems that the python data types are tripping me up .

Hi!

Really love this template and have big plans for it. So I’ve gotten myself into a mess and began wondering “how can I safeguard myself against (yet) non-existing variables”?

I wanted to fire an event that works on a variable retroactively, so it could not exist yet. This worked:

event: set_variable
event_data:
  key: heater_start
  value: "{{ state_attr('sensor.variables', 'variables')['heater_start'] | default(0) + 1}}"
  set_timestamp: true

However, I also needed a timestamp, so .value was required. This wouldn’t work:

event: set_variable
event_data:
  key: heater_start
  value: "{{ state_attr('sensor.variables', 'variables')['heater_start'].value | default(0) + 1}}"
  set_timestamp: true

So I’m leaving my solution in case any other unfortunate soul gets stuck:

event: set_variable
event_data:
  key: heater_start
  value: "{{ (state_attr('sensor.variables', 'variables') | default({})).get('heater_start', {}).get('value', 0) + 1 }}"
  set_timestamp: true

This is just a bunch of safeguards for every step of getting the value I need. I use it then to send a notification (will probably pack it into a variable, the event might be slower than the notification):

action: notify.mobile_app_rmx2155
metadata: {}
data:
  title: "[INFO] Heater on"
  message: >-
    The heater was turned on {{ (state_attr('sensor.variables', 'variables') | default({})).get('heater_start', {}).get('value', 0) }} times today.

Edit: please use or {} instead of | default({}). I have noticed it doesn’t work properly when sensor.variables doesn’t exist (when HA is starting for example), and it can begin screaming with ERRORs in the logs. Updated retrieval:

czas_pracy: |
{{ (state_attr('sensor.variables', 'variables') or {}).get('czas_pracy_total', 0)
  if (state_attr('sensor.variables', 'variables') or {}).get('czas_pracy_total', {}) is not mapping
  else (state_attr('sensor.variables', 'variables') or {}).get('czas_pracy_total', {}).get('value', 0)
  | float }}

(don’t forget “” when firing an event)
This is an example for a float variable. I have tested that it returns the actual value or 0 without errors in these cases:

  1. When HA starts (no errors in logs)
  2. When the Variables sensor doesn’t exist for some reason
  3. When your variable has a timestamp
  4. When your variable doesn’t have a timestamp
2 Likes

This looks very helpful!

Where do you add it in the UI though? I don’t see anything relevant in the “Automations & Scenes” dash, nor the “Devices & Services” dash.

@jacobmovingfwd Trigger based template sensors can only be created using YAML

1 Like

I have noticed that if a variable’s value is false, trying to change it to 0 doesn’t work. Vice versa is also true. If I change it to true first, then it updates correctly to the integer.

Is that intended? Granted, I have not yet encountered any issue with treating false as 0.

I’ll have a look at that soon

1 Like

Hi, me again :sweat_smile: I have noticed that firing set_variable events in my automations causes some errors. With the blocks responsible for firing these events disabled, there are no issues, so I’m pretty sure something must be off with the global Variables.

The variables are set fine, and the automations run fine, so it took me quite some time to notice that in the traces. It looks like it always appears at the end:

I have also noticed this log after running:

Template variable error: 'this' is undefined when rendering '{{ this.attributes.get('log_events', true) }}'

Relevant excerpt of my automation:

<stuff>
action:
  - event: set_variable
    event_data:
      key: stan_grzalki
      value: true
      set_timestamp: false
    enabled: true
<other actions>
mode: single

My variables look like this:

default_timestamp: true
log_events: true
variables:
  grzalka_pv_start: 0
  stan_grzalki: false
  czas_pracy_total: 0
  test_variable:
    value: 100
    timestamp: "2024-10-10T22:57:16.239481+02:00"
friendly_name: Variables

Errors only appear when the event action is enabled. It doesn’t matter whether set_timestamp is true or false. Do you know what might be wrong?

The error is referring to a template condition in your automation (0/if/condition/0)

Found what the issue is, I was referring to this in the action part, but this can’t be used there.

I can reproduce your issue on 0 not changing when set to false, but I think this is a HA error. I tried to remove the variable and adding it back, so it really uses the new value for sure, but it looks like HA doesn’t seem to see it as a change, so it won’t update the entity. It also doesn’t update the last_updated timestamp if you try to do this. Not sure if this is indented behavior.

Assuming you are on version 2024.10 of HA, you can try this version. I moved some checks from the template to define the new variables attribute to the condition section added in that HA version. This should remove the warning about the undefined this variable.

- trigger:
    - platform: event
      event_type: set_variable
    - platform: event
      event_type: remove_variable
    - platform: event
      event_type: clear_variables
  condition:
    - condition: template
      value_template: >
        {{
            (
              trigger.event.event_type == 'set_variable'
              and trigger.event.data is defined
              and trigger.event.data.key is defined
              and trigger.event.data.value is defined
            )
            or
            (
              trigger.event.event_type == 'remove_variable'
              and trigger.event.data is defined
              and trigger.event.data.key is defined
            )
            or 
            trigger.event.event_type == 'clear_variables'
        }}
  action:
    - if: "{{ trigger.event.data.get('log', state_attr('sensor.variables', 'log_events')) }}"
      then:
        - service: logbook.log
          data:
            name: "{{ trigger.event.event_type }}:"
            message: >
              {{ trigger.event.data.key | default('All variables removed') }}
              {%- if trigger.event.event_type == 'set_variable' -%}
                : {{ trigger.event.data.value }}.
              {%- endif -%}
  sensor:
    - unique_id: 4a4c8e53-9e68-4198-9cc5-b336e228ea4d
      name: Variables
      state: Variables
      attributes:
        default_timestamp: false
        log_events: false
        variables: >
          {% set others = dict(this.attributes.get('variables', {}).items() | rejectattr('0', 'eq', trigger.event.data.key)) %}
          {% if trigger.event.event_type == 'set_variable'
              and trigger.event.data.get('set_timestamp', this.attributes.get('default_timestamp', false))
          %}
            {% set value  = trigger.event.data.value %}
            {% set value = value.isoformat() if value is datetime else value %}
            {% set new = {trigger.event.data.key: {'value': value, 'timestamp': now().isoformat()}} %}
            {{ dict(others, **new) }}
          {% elif trigger.event.event_type == 'set_variable' %}
            {% set new = {trigger.event.data.key: trigger.event.data.value} %}
            {{ dict(others, **new) }}
          {% elif trigger.event.event_type == 'remove_variable' %}
            {{ others }}
          {% elif trigger.event.event_type == 'clear_variables' %}
            {}
          {% endif %}
2 Likes

Thanks, it worked like a charm! After the HA update, the Studio Code Server editor screams at me that some values are not allowed, but the YAML checker from the developer settings passes. No errors in automation traces or logs.
Oh, and the logbook works properly now. It actually shows what was changed to what, it’s so beautiful! I know I’ve had log_events set to true, but I thought it was only to inform that something happened.
:+1:

Hi
Thanks for this @TheFes as I have come from fibaro home center/lua and was missing the global variables.
I have a question , if I may.
Is this type of change/solution suited to a script or an automation or does it really matter…

Thanks again for your solution
rgds…

Automtions are basically scripts with an additional trigger and condition section. So this will both work in automations and scripts.
Also in trigger action part of trigger based template sensors.

thanks very much