Need help with a trigger on a sensor attribute within a set time

Hi all,
I have a xiaomi scale sensor.weight sensor that has the following attributes:

state_class: measurement
weight: 78
weight_unit: kg
bmi: 33.39
basal_metabolism: 1964.87
visceral_fat: 30.01
timestamp: '2023-02-02T20:30:08+00:00'
unit_of_measurement: kg
icon: mdi:scale-bathroom
friendly_name: Test Weight

I would like to, whenever the timestamp attribute changes (it changes every time someone steps on the scale), to trigger a copy of that weight to a value called previous_weight. I’d also only like to do this in the morning, say from 8 - 10 so i can check what my weight was the previous morning at the same time.

I tried something like this but it’s not really working as of yet. Also tried with an automation but not sure how to add triggering on a attribute there.

# Test Weight Previous Day
  - trigger:
      - platform: template
        entity_id: sensor.test_weight
    sensor:
      - name: test_weight_pd
        state: >-
         {{ state_attr('sensor.test_weight', 'timestamp') }}

Any thoughts most appreciated!

The basic sensor configuration would be:

  - trigger:
      - platform: state
        entity_id: sensor.test_weight
        attribute: timestamp
    sensor:
      - name: test_weight_pd
        state_class: measurement
        unit_of_measurement: kg
        state: >-
         {{ states('sensor.test_weight') }}

A template sensor alone is not really conducive to doing what you are asking because the sensor will update every time you step on the scale. There’s no way AFAIK , in a template trigger, to differentiate between an update to a specific attribute in a specific time frame. Your likely going to need to employee either an automation or a SQL sensor.

You may want to look into the hass-variables custom integration.

1 Like

Thank you!

I guess there’s different ways i can approach this (doing a history sensor, checking last time updated etc). I will have a think about it.

The following Trigger-based Template Sensor maintains a record of the last 5 weight and BMI measurements in an attribute named history. Each measurement is time-stamped.

template:
  - trigger:
      - platform: state
        entity_id: sensor.test_weight
        attribute: timestamp
    sensor:
      - name: Weight History
        state: "{{ now().timestamp() | timestamp_custom() }}"
        attributes:
          history: >
            {% set current = this.attributes.get('history', []) %}
            {% set new = [{
                "weight": trigger.to_state.attributes.weight,
                "bmi": trigger.to_state.attributes.bmi,
                "time": now().timestamp() | timestamp_custom() }] %}
            {{ (new + current)[:5] }}

If you don’t want to record BMI, just remove this line:

                "bmi": trigger.to_state.attributes.bmi,

If you want to record another parameter like basal_metabolism, just add this line after the one that records weight:

                "basal_metabolism": trigger.to_state.attributes.basal_metabolism,

If you want to record more than the last 5 measurements, perhaps to 7 or more, just change [:5] to [:7] on the last line of the template.

1 Like

That is …amazing. Thank you.

I was just thinking about how i would store the previous state of the weight once the triggers happens.
Where can i read more about this magic history attribute.

Mainly what does:

{% set current = this.attributes.get('history', []) %}

Do and what does

 {{ (new + current)[:5] }}

do.

1 Like

It’s important to keep in mind that the attribute’s name “history” is just something I chose for your application. For another user, who wanted a record of the most recent music that was played, I called the attribute “tracks”.

The template’s first line gets the current value of the sensor’s history attribute and assigns it to a variable named current. If the history attribute doesn’t exist (which is the case when the sensor was just created) it uses an empty list as the default value.

The template’s last line appends the current value of the sensor’s history attribute to a new value (containing the latest measurement of weight, bmi, etc). In other words, it’s growing the list of measurements. The list could grow indefinitely if it were not for the [:5] which ensures only the first 5 items of the list are retained.


FWIW, I have experimented with more sophisticated versions of this type of Trigger-based Template Sensor that support services to purge all of, or a specific value in, the history attribute. Personally, I haven’t found a practical need for these services beyond ‘proof of concept’. All this to say that Trigger-based Template Sensors can be used in creative ways.

1 Like

Since you’ve played around with these… is there any reason you know of why @atv couldn’t change the expression to something like:

{{ iif( 8 <= now().hour < 10, (new + current)[:5], current) }}

to cover their time-span condition?

1 Like

I overlooked to implement the time constraint requirement. :man_facepalming:t3:

Yes, your enhanced version of the last line should do the trick.


FWIW, I would implement it as an Inline If, instead of an Immediate If, only because an iif always computes its if_true and if_false values regardless of the test.

In other words, even if the hour is not between 8 and 10, it’ll report current but still process (new + current)[:5].

I know, I know, it’s non-critical in this situation and only saves a few microseconds of processing time so, yeah, maybe fretting over nothing but there it is. :slightly_smiling_face:

1 Like

Thanks both. I’ll see if i can merge that in there as well.

BTW I should probably open another topic for the following, but i’m trying to use the above construct with a condition value template of:

{{ states('sensor.atv_weight') != 'unknown' or 'unavailable' }}
{% set w=state_attr('sensor.atv_weight_history','history') %} 
{{ w[0].weight if w is iterable and w|count > 0 and w[0].weight is defined }}

{{ states('sensor.{{atv.id}}_weight') < states('w[0].weight') and
state_attr('sensor.{{atv.id}}_weight','timestamp')[8:10] < states('w[0].time')[8:10] }}

For some reason, the state of w[0].time is always unknown. The last line just compares if the date we are comparing against was yesterday (< then today).

You cannot have {{atv.id}} inside your sensors’ entity id.

I think i removed the part where it’s a trigger.id, so when ‘translated’ it becomes sensor.atv_weight.

Yes. Open a separate topic and include the template and where it will be used.

Hi, I’m trying to save a dynamic created input select and then restore it after HA restart, i understand it can be done using “Trigger-based Template Sensor” I did not find any examples similar with what i need is there a way someone here can help me?

I’m generating the lists like this:

service: input_select.set_options
data:
  options: >-
    ({% set ns = namespace(list=[]) %} {% for option in
    state_attr("input_select.type_of_meal_filtered", "options") if option !=
    states("input_text.type_of_meal_random") %} {% set ns.list =
    ns.list+[option] %} {%endfor%} {{ns.list}})
target:
  entity_id: input_select.type_of_meal_filtered
enabled: true

If I can just add the “options” to a sensor attributes and after restart red them and add them back to the input select.

{{ state_attr('input_select.type_of_meal_filtered', 'options')}}

OR
Just replace the input select altogether if “Template Sensor” survives restart and is possible to add/remove attributes then randomly pick from them. I’m only using input select because that is what I can adapt from the code I found.

service: input_text.set_value
data:
  value: >-
    {{ state_attr('input_select.type_of_meal_filtered', 'options') |
    reject('in', states('input_text.type_of_meal_random')) |list |random }}
target:
  entity_id: input_text.type_of_meal_random