Been looking here for answers but can’t figure out how to express the algorithm in HA I need.
I wish to calculate an average temperature.
There are 5 sensors, “sensor.temp1”, “sensor.temp2”,…
Each sensor has an associated flag that the front end uses to allow the user to enable/disable that sensor: input_boolean.temp1enabled", “input_boolean.temp2enabled”,… using a button card.
I only want to include those sensors that are enabled (e.g. input_boolean.temp1enabled == true) in any calculation.
I want to calculate the avg of all enabled sensors
I want to calculate the max of any enabled sensor.
I know I can use min_max platform but that doesn’t allow me to mode on the enabled/disabled flag.
I’ve got this far:
sensor:
# AVERAGE OF ALL ROOM TEMP SENSORS
- platform: template
sensors:
house_temp_avg:
friendly_name: house_temp_avg
unit_of_measurement: "°C"
device_class: temperature
value_template: >
{{ }}
house_temp_max:
unit_of_measurement: "°C"
device_class: temperature
value_template: >
{{ }}
How might I finish this off please?
I started thinking of storing the sensor and its flag in a tuple and creating a list of tuples to traverse through, adding values to a zero initialised var, in order to calculate avg and of course compare against previous max value to get the max sensor name/value. But I can’t help feeling I’m overcomplicating things?
sensor:
# AVERAGE OF ALL ROOM TEMP SENSORS
- platform: template
sensors:
house_temp_avg:
friendly_name: house_temp_avg
unit_of_measurement: "°C"
device_class: temperature
value_template: >
{% set a = 0 if is_state('input_boolean.temp1enabled', 'off') else states('sensor.temp1')|float %}
{% set b = 0 if is_state('input_boolean.temp2enabled', 'off') else states('sensor.temp2')|float %}
{% set c = 0 if is_state('input_boolean.temp3enabled', 'off') else states('sensor.temp3')|float %}
{% set d = 0 if is_state('input_boolean.temp4enabled', 'off') else states('sensor.temp4')|float %}
{% set e = 0 if is_state('input_boolean.temp5enabled', 'off') else states('sensor.temp5')|float %}
{{ (a + b + c + d + e) / 5 }}
house_temp_max:
unit_of_measurement: "°C"
device_class: temperature
value_template: >
{% set a = 0 if is_state('input_boolean.temp1enabled', 'off') else states('sensor.temp1')|float %}
{% set b = 0 if is_state('input_boolean.temp2enabled', 'off') else states('sensor.temp2')|float %}
{% set c = 0 if is_state('input_boolean.temp3enabled', 'off') else states('sensor.temp3')|float %}
{% set d = 0 if is_state('input_boolean.temp4enabled', 'off') else states('sensor.temp4')|float %}
{% set e = 0 if is_state('input_boolean.temp5enabled', 'off') else states('sensor.temp5')|float %}
{{ [a, b, c, d, e] | max }}
I suspect you could probably do some kind of for loop
Pseudo code (kotlin style)
set f = 0
set x = [a, b, c, d, e]
x.foreach {
if(it != 0) f++
}
Then
{{ (a + b + c + d + e) / f }}
But I can’t remember how to do that in jinja off the top of my head. I know scoping is a problem with for loops, but I’m sure someone will be able to point you in the right direction.
Assuming you don’t have any other input booleans with the name “temp” then:
{% set count = namespace(value=0) %}
{% for state in states.input_boolean -%}
{%- if state.name[0:4] == "temp" -%}
{%- if state.state == "on" -%}
{% set count.value = count.value + 1 %}
{%- endif %}
{%- endif %}
{%- endfor %}
{{ count.value }}
Where count.value will be the number of “on” temp booleans.
The explanation is that it loops all input_booleans and look at the name, if it starts with “temp” then it’s true.
Then it loks at the state, is it on., if true it adds one to the counter.
Thank you; you are generating ideas in my mind:
I am trying to use this but it’s not working, syntax all wrong I think
{% set arr= ( {"temp1":{"enabled":states("input_boolean.temp1enabled"),"temp":states("sensor.temp1")}})
{% for dict_item in arr %}
{% for key, value in dict_item.items() %}
Key: {{key}}
Value: {{value}}
{% endfor %}
{% endfor %}
#-- then somehow calculate the average and max values
Where arr is an array of dicts for each sensor although I’ve just used one sensor temp1 for example purposes. Can it be done?
Not like that I don’t think, I’m not that hot on jinja but I know you can’t generate key value pairs like that - but @Hellis81 's approach to get the count of enabled values is about right
{%- set ns = namespace(entities = []) %}
{%- for i in range(1, 6) %}
{%- set sensor_id = 'sensor.temp' ~ i %}
{%- set boolean_id = 'input_boolean.temp' ~ i ~ 'enabled' %}
{%- if is_state(boolean_id, 'on') %}
{%- set ns.entities = ns.entities + [ expand(sensor_id) ] %}
{%- endif %}
{%- endfor %}
{{ ns.entities | map(attribute='state') | map('float') | sum / ns.entities | count }}
It also could be done like this
{%- set ns = namespace(values = []) %}
{%- for i in range(1, 6) %}
{%- set sensor_id = 'sensor.temp' ~ i %}
{%- set boolean_id = 'input_boolean.temp' ~ i ~ 'enabled' %}
{%- if is_state(boolean_id, 'on') %}
{%- set ns.values = ns.values + [ states(sensor_id) | float ] %}
{%- endif %}
{%- endfor %}
{{ ns.values | sum / ns.values | count }}
It’s basically iterating through the entities, if they are on it adds the value to a list. Then it sums the list up and divides it by the count. Both do the same thing. The first template returns a list of state objects, where you can get any information out of what is enabled. The second only puts the state value into the list as a floating number.
Thanks fellas. Sorry for late reply.
I went with @petro solution with a few adjustments as I grouped the sensors in the yaml config and in the for…loop I iterate over the group sensor names.
Excellent and works !
I also noted the template editor on lovelace Developer Tools, and clicked the links to the jinja designer page and HA extensions; been reading them to understand the code snippets you all provided. map and expand are useful functions👌
@petro , you seem to be an enlightened guru. May I inquire with you please as to if (and how) my config can be improved?
I have this config below.
essentially, I have grouped my sensors as declared below,
I use template code to iterate over the sensors contained within the group
map() is used to extract the entity_ids of each sensor within the group
a filter removes the preceding component/integration domain name to yield just the id part of the entity.
the id is used to construct the input_boolean entity ids and the sensor ids.
if the corresponding input_boolean - for a given sensor name - is ‘on’ then the sensor entity state value is used and the average mean and the max values are calculated.
Is there a better more efficient way of doing this?
Is there a way to use “a template within a template”. What I mean is that value_template code is almost identical for the avg and max sensor values, with the exception of the final lines in each, so is there a way to code up the common code as a single block , call this block inside each of the template blocks, then use the final lines as in:
{{ ns.values | sum / ns.values | count }}
and
{{ ns.values | max }}
Incidentally, this is all working perfectly so thank you