Adding a trigger to a sensor

Hello. I am a newby trying to get started. I have understood that there should be an option to add triggers to a sensor so it would be possible to determine when a sensor runs. However, I am unable to do that.
Would appreciate greatly a push to the right direction.

configuration.yaml:

sensor: !include sensors.yaml

sensors.yaml:

- platform: template
  sensors:
    night_charge_scheduler:
      unique_id: carchargerids.00001
      friendly_name: Night Charge Scheduler
      value_template: >-
        {# Variables read from Helpers -#}
        {% set charge_time = states('input_number.CarCharge_time') | int -%}
        {% set night_start = states('input_datetime.CarCharge_Night_Start') | truncate(3, False, '.', 0) | int -%}
        {% set night_end = states('input_datetime.CarCharge_Night_End') | truncate(3, False, '.', 0) | int -%}
        {# Setting price addition -#}
        {% set a = {
          "day_tariff": 0.0521,
          "day_peak_tariff": 0.0802,
          "weekend_peak_tariff": 0.0464,
          "night_tariff": 0.0299,
          "contract_price_addition": 0.0024
                  }
        -%}
        {# Gettting Nordpool prices-#}
        {% if state_attr('sensor.nordpool_kwh_ee_eur_3_10_02', 'tomorrow')[23]| string == 'None'  -%}
          {% set prices = state_attr('sensor.nordpool_kwh_ee_eur_3_10_02', 'today') -%}
        {% else -%}
          {% set prices = state_attr('sensor.nordpool_kwh_ee_eur_3_10_02', 'today') +
                          state_attr('sensor.nordpool_kwh_ee_eur_3_10_02', 'tomorrow') -%}
        {% endif -%}
        {# Adding price additions to get final price vector for today + tomorrow -#}
        {% set final_prices = namespace(values = []) -%}
        {% for price in prices -%}
          {% set x = loop.index0 -%}
          {% if loop.index0 < 24 -%}
            {% set hour = loop.index0 -%}
            {% set day = now().weekday() -%}
          {% elif loop.index0 >= 24 -%}
            {% set hour = loop.index0 - 24 -%}
            {% set day = now().weekday() + 1 -%}
          {% endif -%}
          {% if day < 5 and ((hour >= 7 and hour < 9)
                                or (hour >= 12 and hour < 16)
                                or (hour >= 20 and hour < 22))-%}
            {% set final_prices.values = final_prices.values + 
                                [(price + a.day_tariff + a.contract_price_addition) | round(4) ]  -%}
          {% elif day < 5 and ((hour >= 9 and hour < 12)
                                or (hour >= 16 and hour < 20)) -%}
            {% set final_prices.values = final_prices.values +
                                [(price + a.day_peak_tariff + a.contract_price_addition) | round(4) ]   -%}
          {% elif day >= 5 and hour > 16 and hour < 20 -%}
            {% set final_prices.values = final_prices.values +
                                [(price + a.weekend_peak_tariff + a.contract_price_addition) | round(4) ] -%}
          {% else -%}
            {% set final_prices.values = final_prices.values +
                                [(price + a.night_tariff + a.contract_price_addition) | round(4) ] -%}
        {% endif -%}
        {% endfor -%}
        {#{ final_prices.values }-#}
        {# making a vector of prices/times during "night charge" time window i.e. from night_start to
                                                                                        night_end  -#}
        {% set night_charge_prices = namespace(values = [], time = []) -%}
        {% for price in final_prices.values -%}
          {% if (loop.index0 >= night_start) and (loop.index0 < (night_end + 24)) -%}
              {% set night_charge_prices.values = night_charge_prices.values + [price] -%}
              {% set night_charge_prices.time = night_charge_prices.time + [loop.index0 % 24]-%}
          {% endif -%}
        {% endfor -%}
        {# during "night charging time" turn on only at cheapest hours -#}
        {#{ 'need to turn on the charger (at hour, price)' }-#}
        {% for price in night_charge_prices.values -%}
          {% if (night_charge_prices.values | sort)[charge_time - 1] >= price  -%}
            {{ night_charge_prices.time[loop.index0], night_charge_prices.values[loop.index0] }}
          {% endif -%}
        {% endfor -%}

Question1: where do I add the trigger and how? I do not seem to be able to - get an errorā€¦
Question2: currently I am getting an array as an output that looks crappy (example:
(4, 0.1703) (5, 0.1703) (6, 0.1233) (7, 0.1773)
How can I access these values or assign these as attributes?

Thanks!

If you want to have trigger based sensor, you will need to use the current template sensor formatā€¦ what you have shown in your example is the legacy format which is not compatible with using a trigger.

Thanks! That helps a little. But could you please direct me towards using the latest and greatest format? The thing Iā€™ve done was based on some example I could find.
An example would be greatly appreciated.

Trigger-based Template sensors

The new format does not go in the sensors.yaml folderā€¦ you can place it in configuration.yaml or create a templates.yaml file and add an include statement in configuration.yaml like you have for sensors.yaml:

template: !include templates.yaml

It is important to know that trigger-based sensors do not survive restart. You can include a homeassistant trigger, but the values of the sensor will update upon restart which maybe undesirable if your goal is to set the value based on a specific time.

For Question 2:

By HA convention, states are always strings, so what you have is not an array it is an array-shaped string. Attributes can be other datatypes like arrays and dictionaries. You will need to determine logical/functional ways to break up your template then you can use the this variable for self-referencing. However, you may not be able to use triggers and self-referencing at the same timeā€¦ I vaguely remember this being an issue, but I donā€™t know if it was ever addressed.

1 Like

Thank you for the help - this helped me forward a bit. However, as it often is I get one step forward and 2 back :smiley:

  1. I cannot seem to get the trigger and attributes work at the same time (self referencing seems to be nonfunctional, when there is a trigger defined):

# Trigger without attributes works
- trigger:
    - platform: time_pattern
      # values: '0', '*', '/30' - every 30,  
      hours: '*'
      minutes: '*'
      seconds: '/5'
  sensor:
    # Triggered sensor with attributes does not seem to work
    - name: test_sensor10
      unit_of_measurement: "time"
      state: >-
        {{ now().hour + now().minute + now().second }}

# Attributes and self referencing works without trigger
- sensor:
  - name: test_sensor7
    unit_of_measurement: "time"
    state: >-
      {{ this.attributes.attr0 | default('99'), this.attributes.attr1 | default('99'), this.attributes.attr2 | default('99') }}
    attributes:
      attr0: >
        {% set a = now().hour -%}
        {{ a }}
      attr1: >-
        {{ this.attributes.attr0, now().minute }}
      attr2: "{{ now().second }}"

# If trigger and attributes are combined, the sensor is unavailable and does not work???
- trigger:
    - platform: time_pattern
      # values: '0', '*', '/30' - every 30,  
      hours: '*'
      minutes: '*'
      seconds: '/5'
  sensor:
    # Triggered sensor with attributes does not seem to work
    - name: test_sensor9
      unit_of_measurement: "time"
      state: >-
        {{ this.attributes.hou1 | default('0') + this.attributes.hou2 | default('0') + this.attributes.hou3 | default('0') }}
      attributes:
        hou1: "{{ now().hour }}"
        hou2: "{{ now().minute }}"
        hou3: "{{ now().second }}"

  1. if triggered sensors do not survive restart (and i verified that by testing) then is there any method to achieve this?
  2. by examples and testing how things work I could not understand how I can define a non-string data type for attributes.

Your inputs are really appreciated.

What we used to do before there were trigger-based template sensors is to use an automation to store the values to helpers then compile the desired value with a template sensor.

Outputs will maintain their native datatype unless altered by filters or by being wrapped in appropriate bracket types (), [], {} ( Jinja Literals)

- sensor:
  - name: test_sensor8
    state: >-
      {{ this.attributes.integer | default('99'), this.attributes.array | default('99') }}
    attributes:
      integer: >
        {{ now().hour + now().minute + now().second }}
      string: >
        {{ now().hour | string }}
      array: >
        {{ [ this.attributes.integer, now().minute, now().second ] }}
      dictionary: >
        {{ { 'array1': this.attributes.array, 'array2': ['stuff', 'other stuff', 3] } }}

Thanks - much appreciated. The dictionary option is really great. Can basically store and recall all kinds of stuff. If it would survive a restart and self referencing would work, I would be all set already (only would need to write a bunch of code).

Still I have 2 questions:

  1. if the self referencing does not work - should I post a bug report somewhere or how to handle this? Or am I doing something wrong? Tried it again and sure enough - with trigger the sensor will be unavailable if self referenced.
  2. is there a method to write a value to helpers from a template sensor or do I have to use an automation?

@Didgeridrew Iā€™m also facing some issues when trying to create a trigger based sensor.
I organzied all my template sensors in a sensor.yaml so far and due to your comment in this thread I created a template.yaml which looks like this:

trigger:
- platform: time_pattern
  seconds: "/20"
sensors:
  name: "avail_for_water_2"
  friendly_name: "Verfuegbar fuer Warmwasser"
  device_class: "power"
  unit_of_measurement: "W"
  value_template: >-
    {% set gen = states.sensor.inverter_active_power.state | float(0) %}
    {% set gen = [gen,11000] | min %}
    {% set con = states.sensor.current_consumption.state | float(0) %}
    {% set sol = states.sensor.ac_elwa_2_1_power1_solar.state | float(0) %}
    {% set grid = states.sensor.ac_elwa_2_1_power1_grid.state | float(0) %}
    {% set wat = grid + sol | float(0) %}
    {% set con = iif(con<wat,con+wat,con) %}
    {% set res = "%i" % (gen - con + wat) | float(0) %}
    {{ [float(res),0] | max }}

Within the Developer Tools Templates tab it looks good and also the yaml check throws no errors, but after a yaml reload the following error occurs:

Logger: homeassistant.config
Source: config.py:623
First occurred: 14:32:20 (9 occurrences)
Last logged: 14:59:01

Invalid config for ā€˜templateā€™ at template.yaml, line 10: expected dictionary for dictionary value ā€˜sensors->value_templateā€™, got ā€˜{% set gen = states.sensor.inverter_active_power.state | float(0) %} {% set gen = [gen,11000] | min %} {% set con = states.sensor.current_consumption.state | float(0) %} {% set sol = states.sensor.ac_elwa_2_1_power1_solar.state | float(0) %} {% set grid = states.sensor.ac_elwa_2_1_power1_grid.state | float(0) %} {% set wat = grid + sol | float(0) %} {% set con = iif(con<wat,con+wat,con) %} {% set res = ā€œ%iā€ % (gen - con + wat) | float(0) %} {{ [float(res),0] | max }}ā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 1: ā€˜platformā€™ is an invalid option for ā€˜templateā€™, check: platform Invalid config for ā€˜templateā€™ at template.yaml, line 6: expected dictionary for dictionary value ā€˜sensors->nameā€™, got ā€˜avail_for_water_2ā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 7: expected dictionary for dictionary value ā€˜sensors->friendly_nameā€™, got ā€˜Verfuegbar fuer Warmwasserā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 8: expected dictionary for dictionary value ā€˜sensors->device_classā€™, got ā€˜powerā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 9: expected dictionary for dictionary value ā€˜sensors->unit_of_measurementā€™, got ā€˜Wā€™
Invalid config for ā€˜templateā€™ at template.yaml, line 1: ā€˜templateā€™ is an invalid option for ā€˜templateā€™, check: template
Invalid config for ā€˜templateā€™ at template.yaml, line 5: expected dictionary for dictionary value ā€˜sensors->nameā€™, got ā€˜avail_for_water_2ā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 6: expected dictionary for dictionary value ā€˜sensors->friendly_nameā€™, got ā€˜Verfuegbar fuer Warmwasserā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 7: expected dictionary for dictionary value ā€˜sensors->device_classā€™, got ā€˜powerā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 8: expected dictionary for dictionary value ā€˜sensors->unit_of_measurementā€™, got ā€˜Wā€™ Invalid config for ā€˜templateā€™ at template.yaml, line 9: expected dictionary for dictionary value ā€˜sensors->value_templateā€™, got ā€˜{% set gen = states.sensor.inverter_active_power.state | float(0) %} {% set gen = [gen,11000] | min %} {% set con = states.sensor.current_consumption.state | float(0) %} {% set sol = states.sensor.ac_elwa_2_1_power1_solar.state | float(0) %} {% set grid = states.sensor.ac_elwa_2_1_power1_grid.state | float(0) %} {% set wat = grid + sol | float(0) %} {% set con = iif(con<wat,con+wat,con) %} {% set res = ā€œ%iā€ % (gen - con + wat) | float(0) %} {{ [float(res),0] | max }}ā€™

Any hint what Iā€™m doing wrong?

thx & br

Trigger-based template sensors must follow the current configuration syntax, what you have posted is a trigger pasted to a sensor configured using the legacy syntax.

thx for the fast reply!
do you have an example for me how the new syntax should look like?

thx in advance

still not working

template:
  - trigger:
      - platform: time_pattern
        seconds: "/20"
    sensor:
      - name: "avail_for_water_2"
        friendly_name: "Verfuegbar fuer Warmwasser"
        device_class: "power"
        unit_of_measurement: "W"
        value_template: >-
          {% set gen = states.sensor.inverter_active_power.state | float(0) %}
          {% set gen = [gen,11000] | min %}
          {% set con = states.sensor.current_consumption.state | float(0) %}
          {% set sol = states.sensor.ac_elwa_2_1_power1_solar.state | float(0) %}
          {% set grid = states.sensor.ac_elwa_2_1_power1_grid.state | float(0) %}
          {% set wat = grid + sol | float(0) %}
          {% set con = iif(con<wat,con+wat,con) %}
          {% set res = "%i" % (gen - con + wat) | float(0) %}
          {{ [float(res),0] | max }}

with error:
Logger: homeassistant.config
Source: config.py:623
First occurred: 23:01:10 (7 occurrences)
Last logged: 23:11:08

Invalid config for ā€˜templateā€™ at template.yaml, line 5: required key ā€˜stateā€™ not provided Invalid config for ā€˜templateā€™ at template.yaml, line 6: ā€˜friendly_nameā€™ is an invalid option for ā€˜templateā€™, check: sensor->0->friendly_name Invalid config for ā€˜templateā€™ at template.yaml, line 9: ā€˜value_templateā€™ is an invalid option for ā€˜templateā€™, check: sensor->0->value_template
Invalid config for ā€˜templateā€™ at template.yaml, line 1: ā€˜templateā€™ is an invalid option for ā€˜templateā€™, check: template

Read the linked doc again, then. Modern config has name instead of friendly_name, and state instead of value_template.

The error message calls this out quite clearly.

The clues are in the error message:

You have added this to a separate file. The method you chose to include/merge that file into your configuration.yaml file will determine the necessary syntax. Likely you need to remove the template top level key because you have used template: !include template.yaml. If you used a different method you need to share that information so we can tell you what is needed.

The current syntax does not use value_template, it uses state.

Hi guys, thx for your fast responses!
You were absolutely right and I was simply blind to the obvious missconfiguration.

The only thing Iā€™m still confused about is, why is the old syntax still working within the sensor.yaml I set up a few months ago?

Legacy sensors are still supported, but you cannot use triggers with those.

2 Likes