Maintain an array with last values of a sensor

Hello,

I’d like to create an automation which saves the value of a sensor every fix time and maintain last N values in an array, so I can predict future values based on previous day’s ones.

I don’t know if best way of doing it is:

  • With a sql query at the database.
  • With a text file (csv).
  • With an array (I don’t know if it’s possible to maintain an array whose values are stored permanently even HA is restarted).

Does anybody point me to the best way?

Definitely don’t know the best way. Perhaps add input_select to the list?

[edit] also wanted to point out. Why don’t you try each option and report back? The “best” for one person is not often the “best” for another who has different requirements.

1 Like

I’ve done it with `input_text’ helper.

I’ve created an automation triggered every hour. It maintains a first-in first-out buffer in the input_text. It saves the last 24 values of the sensor, dropping oldest one if 24-spaces buffer is full.

Here’s the automation code:

alias: read_sensor_data
description: ''
trigger:
  - platform: time_pattern
    minutes: '0'
condition: []
action:
  - service: input_text.set_value
    target:
      entity_id: input_text.fi_fo_buffer
    data:
      value: >
        {% set array_in=states('input_text.fi_fo_buffer') %}
        {% set array_out=states('sensor.temperature1')|string + ',' + array_in.split(',')[0:23] | join(',') %} 
        {{ array_out }}
mode: single

I have to test it, and then my plan is to create a sensor whose values predict an average of the next N-hours temperature based on present and last day temperatures.

I’ll then switch-on or not accumulator heaters within scheduled times based on the value of this sensor compared with a certain threshold.

1 Like

If you wish, you can reduce the template to this:

    data:
      value: >
        {{ ([states('sensor.temperature1')] + states('input_text.fi_fo_buffer').split(',')[0:23]) | join(',') }} 

It prepends the sensor’s value as a list, thereby saving a step or two.

I assume the values you are storing are just short integers, or floats, and 24 of them, plus commas, won’t exceed 255 characters in length (which is the maximum storage capacity of an entity’s state value).

2 Likes

The values are already stored in the database.

This is my final configuration.

It includes:

  • An automation, which every hour updates the last day’s temperatures.
  • A sensor, which makes an estimation of next hours average temperature based on difference of actual and -24hours temperature and a weighted average of yesterday’s next hours temperatures.

I hope this is helpful for somebody else.

input_text:
  fi_fo_buffer:
    name: "First in - First out buffer"
    min: 0
    max: 255

input_number:
  window_size:
    name: Window size
    icon: mdi:arrow-expand-horizontal
    initial: 6
    min: 1
    max: 24
    step: 1

sensor:
  - platform: template
    sensors:
      estimated_temperature:
        friendly_name: "Estimated temperature based on actual and previous day's values"
        value_template: >
                    {% set data = namespace(values=[], weights=[], products=[]) %}
                    {# average window size #}
                    {% set n = states('input_number.window_size') | int %}
                    {% set len = states('input_text.fi_fo_buffer').split(',') | length %}
                    {# if buffer is full, estimation can be done #}
                    {% if len == 25 %}
                      {% for num in states('input_text.fi_fo_buffer').split(',') %}
                        {% set data.values = data.values + [num|float] -%}
                        {% set cont = loop.index0 %}
                        {# Linear weights #}
                        {% set data.weights = data.weights + [max((1-(1/n)*(len-cont-1)),0)] %}
                        {% set data.products = data.products + [(data.values[cont] * data.weights[cont])|round(3)] %}
                      {% endfor %}
                      {{ ((data.values|first - data.values|last) + (data.products|sum/data.weights|sum)) | round(2) }}
                    {% else %}
                    {# buffer is still not full #}
                      {{'undefined'}}
                    {% endif %}
        attribute_templates:
          number_samples: >
                    {{ states('input_text.fi_fo_buffer').split(',') | length }}

automation:
  - id: update_buffer
    alias: "Update first in - first out buffer"
    description: "Updates first-in first-out buffer every hour."

    trigger:
      # run each hour
      - platform: time_pattern
        minutes: '0'

    condition: []

    action:
      - service: input_text.set_value
        target:
          entity_id: input_text.fi_fo_buffer
        data:
          value: >-
            {{ ([states('sensor.temperature')] +
            states('input_text.fi_fo_buffer').split(',')[0:24]) |
            join(',') }} 
    mode: single
1 Like

Hi,
Is this meant to be input_text.fi_fo_buffer in your example?

1 Like

Yes. I’ve just corrected it.

Thanks.