Heating COP calculation

Make a statistics sensor that sums Q and a separate one for W over the same timeperiod. Make sure to increase your samples to a large value so the timeperiod is hit. Then make a template sensor dividing the 2.

Thank you for responding.

Make sure to increase your samples to a large value so the timeperiod is hit

What does that mean in laymans terms? Could you help me with a code sample? Os at least which State Characteristic, max_age, sampling_size and precision to use?

The integration uses number of samples or time period for a calculation. If the configured number of samples is smaller than the time period, no additional samples will be added to the calculation. If the number of samples is greater than the time period, the time period is honored and the extra samples are ignored.

max age is the time period.
sampling_size is the number of samples.
precision is the number of sig figs.

state characteristic is whatever calc you want.

So for example if i set the time period 5 minutes and sampling size for example 100 or 1000 it should be enough? And probably “change” would be the right state characteristics in my case?

Edit: If both statistic sensors have same settings, they will be in sync?

it’s number of samples. I.e. how many updates you get over your timespan. If you get 10 updates in 5 minutes, make sure your number of samples is set higher than 10. If you don’t know, set it to a large amount.

I would think that

They should be on the same time period if they have the same max_age.

I made two statistics helper sensors:

  - platform: statistics
    name: "Heating Energy COP Helper"
    entity_id: sensor.total_heating_energy
    state_characteristic: change
    max_age:
      minutes: 5
    sampling_size: 100
    precision: 5
  - platform: statistics
    name: "Heat Pump Electric Energy COP Helper"
    entity_id: sensor.heat_pump_energy
    state_characteristic: change
    max_age:
      minutes: 5
    sampling_size: 100
    precision: 5

And COP calcluation template sensor:

  - platform: template
    sensors:
      heating_cop_current:
        friendly_name: 'Current Heating COP'
        value_template: "{{ (states('sensor.heating_energy_cop_helper')|float /
                             states('sensor.heat_pump_electric_energy_cop_helper')|float)|round(2) }}"
        unit_of_measurement: "CoP"
        availability_template: >-
          {{
            is_number(states('sensor.heating_energy_cop_helper')) and
            is_number(states('sensor.heat_pump_electric_energy_cop_helper')) 
          }}

result:



Some moments COP is under 1 which it cannot be and helpers have 0 value which makes divinding impossible.

I’ll let it run for a while and see what it the result, but maybe there is a better state characteristics for this case? Maybe sum_differences_nonnegative which will rule out the negatives?

Or longer max age, so there will be more readings to take account. My hunch is that heat pumo and shelly might not send the data in a same frequency.

Or maybe do 5 minute average sensor for COP…

EDIT:
For some reason Heatinf Energy will go to minus value. This is what the heat pump is sending:

So this approach did not work

Well you gotta see why it went unavailable, and you aren’t rejecting W <= 0 values. That all has to be done by you in your template that calculates the COP.

It will go unavailable, when heat is not produced, because statistic sensor does not get any value updates.

So to reject values lower or 0 I have to add this to the template?

  - platform: template
    sensors:
      heating_cop_current:
        friendly_name: 'Current Heating COP'
        value_template: "{{ (states('sensor.heating_energy_cop_helper')|float /
                             states('sensor.heat_pump_electric_energy_cop_helper')|float)|round(2) }}"
        unit_of_measurement: "CoP"
        availability_template: >-
          {{
            is_number(states('sensor.heating_energy_cop_helper')) and
            is_number(states('sensor.heat_pump_electric_energy_cop_helper')) 
          }}

Probably to the abailability template part?

        availability_template: >-
          {{
            is_number(states('sensor.heating_energy_cop_helper')) and
            is_number(states('sensor.heat_pump_electric_energy_cop_helper')) 
          }}

Edit:

It should be like this?

{{ 
  states('sensor.heating_energy_cop_helper')|float(0) > 0
  and
  states('sensor.heat_pump_electric_energy_cop_helper')|float(0) > 0
}}

I’d add the logic to the availability template.

Did that. Now Heat Meter statistic sensor is “unknown” status quite frequently. Electricity meter however is showing ok values.


@ThomDietrich , I saw you commented in this topid with a similar issue: Help with statistics sensor: calculate increase in the last 48 hours - #10 by BratislaveBoy

Maybe tou have any ideas what to do?

You don’t want to filter out zero. Just less than zero. So use >= instead of >

Sensors from Heat Pump are also showing ok values:


I sum them up:

  - platform: template
    sensors:
      total_heating_energy:
        friendly_name: 'Total Heating Energy'
        value_template: "{{ (states('sensor.nibe_heat_meter_heat_cpr_total_system_2')|float +
                             states('sensor.nibe_heat_meter_hw_cpr_total_system_2')|float)|round(3) }}"
        unit_of_measurement: "kWh"
        availability_template: >-
          {{
            is_number(states('sensor.nibe_heat_meter_heat_cpr_total_system_2')) and
            is_number(states('sensor.nibe_heat_meter_hw_cpr_total_system_2')) 
          }}

And then use statistics sensors from them and heat pump energy meter:

  - platform: statistics
    name: "Heating Energy COP Helper"
    entity_id: sensor.total_heating_energy
    state_characteristic: change
    max_age:
      minutes: 5
    sampling_size: 100
    precision: 5
  - platform: statistics
    name: "Heat Pump Electric Energy COP Helper"
    entity_id: sensor.heat_pump_energy
    state_characteristic: change
    max_age:
      minutes: 5
    sampling_size: 100
    precision: 5

And then divide the result:

  - platform: template
    sensors:
      heating_cop_current:
        friendly_name: 'Current Heating COP'
        value_template: "{{ (states('sensor.heating_energy_cop_helper')|float /
                             states('sensor.heat_pump_electric_energy_cop_helper')|float)|round(2) }}"
        unit_of_measurement: "CoP"
        availability_template: >-
          {{ states('sensor.heating_energy_cop_helper')|float(0) > 0 and 
          states('sensor.heat_pump_electric_energy_cop_helper')|float(0) > 0
          }}

my hunch is that statistic senseor is not properly configured. Either the max time or state characteristics

changed that now

Keep in mind that your denominator can’t be zero ever. So that should remain >

You could also put in an if statement that outputs zero when the denominator is zero too.

So i came up with this code:

  - platform: template
    sensors:
      heating_cop_current:
        friendly_name: 'Current Heating COP'
        value_template: "{% set q = states('sensor.heating_energy_cop_helper') |float|round(5) %}
                         {% set w = states('sensor.heat_pump_electric_energy_cop_helper') |float|round(5) %}
                         {% if w <= 0 or q < 0 %}
                          0
                         {% else %}
                         {{ (states('sensor.heating_energy_cop_helper')|float /
                             states('sensor.heat_pump_electric_energy_cop_helper')|float)|round(2) }}
                         {% endif %}"
        unit_of_measurement: "CoP"
        availability_template: >-
          {{
            is_number(states('sensor.nibe_heat_meter_heat_cpr_total_system_2')) and
            is_number(states('sensor.nibe_heat_meter_hw_cpr_total_system_2')) 
          }}

I left the availability_template to “is_number” as the if statement now handles the zero and minus values where needed.

I’m i on the right path?

Edit: Now when i could take 5 minutes (or 1 hour) average and not include 0 values, i guess i would get quite clean results. Maybe make a statistics sensor out of the current COP one?

Seems like it. First, use multiline notation instead of quotes. Second there’s no reason to round mid calculation, that just adds error. Round at the end. Third, use the variables you created. Lastly, it’s typically easier to read when you look for ‘positive things’ in your if statement.

        value_template: >
          {% set q = states('sensor.heating_energy_cop_helper') | float %}
          {% set w = states('sensor.heat_pump_electric_energy_cop_helper') | float %}
          {% if q >= 0 and w > 0 %}
            {{ (q / w) | round(2) }}
          {% else %}
            0
          {% endif %}

Thanks, your code is much nicer and I get it what I did differently. Coding is not my everyday thing and thank you very much for helping me.

just one question. What is the difference between > and >- in the notation?

It doesn’t matter for HA in template sensors, use either one. It only matters when formatting text, which is removed by template sensors, so the functions do nothing. That’s why I don’t bother to use the -.

1 Like

Hi,
thanks for this code! I have tried to implement it on my side.

The only downfall is that the data I get from the sensor sensor.heat_pump_electric_energy_cop_helper
is only generated once a day, at the end of that day.

So I am basically able to check the COP from the previous day.

This is what I’ve done now:

#COP Helper sensors
- platform: statistics
  name: "Heating Energy COP Helper"
  entity_id: sensor.shelly_pro3em_total_active_energy
  state_characteristic: change
  max_age:
    hours: 24
  sampling_size: 100
  precision: 2
- platform: statistics
  name: "Heat Pump Electric Energy COP Helper"
  entity_id: sensor.heat_pump_dailyheatingenergyproduced
  state_characteristic: change
  max_age:
    hours: 24
  sampling_size: 100
  precision: 2
    
#Current COP
- platform: template
  sensors:
    heating_cop_current:
     friendly_name: 'Current Heating COP'
     value_template: >
       {% set q = states('sensor.heat_pump_electric_energy_cop_helper') | float %} 
       {% set w = states('sensor.heating_energy_cop_helper') | float %}
       {% if q >= 0 and w > 0 %}
       {{ (q / w) | round(2) }}
       {% else %}
         0
       {% endif %}

I do get a COP of 1,89 because the sensors look back 24 hours, so I get the value from the day before last day as well. So can I change max_age to ‘the previous day’ ?