Monitor long your HVAC system runs per day or month

Hi
Sorry, I am very new to this. Do I copy the

  • platform: template
    sensors:
    hvac_status:
    into the config.yaml?

Thanks for this, BTW!

Does the binary sensor have to exist for the duration of sampling in order to get accurate data? I just created the sensor and template (and my heating system was on this past winter), but I see 0h in the templated entity’s graph. Is that because the binary sensor was just created?

Is there a way to extract the accumulated history in the db prior to the creation of the sensor?

The history stats sensor can only get history data from the template sensor after the template sensor has been created.

Can the history in db be extracted. Technically yes. There is a SQL sensor but you’d have to create right custom query to search the states table of the database and accrue the time were the hvac_action attribute of the climate device contains the desired value. Currently the the history_stats sensor can only query by matching state and not by matching attribute.

The history stats sensor is also limited to the purge time of your database set in configuration.yaml.

recorder:  
  purge_keep_days: 45 

Thanks for your response. Still no luck.

As a simplification, I tried creating history_stats for a binary_sensor (a patio door reed switch, 7 days history), and that is also showing 0h. So I must be doing something else wrong. Here’s the simplified one:

sensor:
  - platform: history_stats
    name: Bedroom patio door seven days
    entity_id: binary_sensor.bedroom_patio_door
    state: "Open"
    type: time
    duration:
      days: 7
    end: "{{ now() }}"

Note that I don’t have recorder purge_keep_days set in my configuration.yaml, but it’s supposed to default to 10 days. (BTW is there a way to query this from the UI? The only reference to the recorder integration I could find in Lovelace was the recorder.purge service I could call).

Argh, never mind. I just realized that the state to be checked has to be “on”, not “Open” (as it appears in the UI). The simplified one is working now. Will try again with the HVAC template sensor.

I’ve had issues with recorder in the past, so I white list everything now.

If I’m understanding what is happening correctly, this is being captured in real time, so my feeling is the original sensor doesn’t need to be white listed in recorder, but that the new history_stats based sensors do?

I tried to do this with nest but I can’t find status sensor within the HA integration. All I am seeing is humidity and temp sensors.
On n’est app however I can see the HVAC run history.

You’re looking for an Attribute attached to the climate.nest entity. In Attributes (You can see these if you go look at it in Developer Tools… States… ) you’ll see a hvac_action: attribute, which is idle when Idle, and similarly cooling or heating.

1 Like

Thanks for the example. I followed it but did it based on power consumption using a Shelly EM since my HVAC doesn’t tell me when it is on.

- platform: template

    sensors:

      hvac_running:

        unique_id: "290323092023"

        value_template: >-

          {% if ( states('sensor.ac_outside_power_total') | float ) < 250 %}

            idle

          {% else %}

            on

          {%- endif %}

        icon_template: >-

          {% if is_state('sensor.hvac_running',"idle") %}

            mdi:power-on

          {% elif is_state('sensor.hvac_running',"on") %}

            mdi:snowflake

          {% else %}

            mdi:octagon

          {% endif %}

  - platform: template

    sensors:

      hvac_status:

        unique_id: "2903230920233"

        value_template: >-

          {% if ( states('sensor.ac_outside_power_total') | float ) < 250 %}

            idle

          {% elif ( states('sensor.ac_outside_power_total') | float ) < 750 %}

            low

          {% elif ( states('sensor.ac_outside_power_total') | float ) < 1500 %}

            medium

          {% else %}

            high

          {%- endif %}

        icon_template: >-

          {% if is_state('sensor.hvac_status',"idle") %}

            mdi:power-on

          {% elif is_state('sensor.hvac_status',"low") %}

            mdi:snowflake

          {% elif is_state('sensor.hvac_status',"medium") %}

            mdi:snowflake

          {% elif is_state('sensor.hvac_status',"high") %}

            mdi:snowflake

          {% else %}

            mdi:octagon

          {% endif %}

  - platform: history_stats

    name: HVAC ON time per hour

    entity_id: sensor.hvac_running

    state: "on"

    type: time

    end: "{{ now() }}"

    duration:

      hours: 2

  - platform: history_stats

    name: HVAC ON duty cycle in 2 hr

    entity_id: sensor.hvac_running

    state: "on"

    type: ratio

    end: "{{ now() }}"

    duration:

      hours: 2

  - platform: history_stats

    name: HVAC ON count per hour

    entity_id: sensor.hvac_running

    state: "on"

    type: count

    end: "{{ now() }}"

    duration:

      hours: 1

  - platform: history_stats

    name: HVAC ON duty cycle yesterday

    entity_id: sensor.hvac_running

    state: "on"

    type: time

    end: "{{ now().replace(hour=0, minute=0, second=0) }}"

    duration:

      hours: 24

  - platform: history_stats

    name: HVAC high minutes yesterday

    entity_id: sensor.hvac_status

    state: "high"

    type: time

    end: "{{ now().replace(hour=0, minute=0, second=0) }}"

    duration:

      hours: 24

Hi Guys,
I’d also appreciate if some could tak a look what is not right here.
I’m trying to count time when falownik ( solar PV) is on or working on max output.

sensor definition:

template:
  - binary_sensor:
      - name: "falownik_max_moc"
        state:  >
          {% set wartosc = states('sensor.moc_pv')|float %}        
          {{ (wartosc) >4985 }}

  - binary_sensor:
      - name: falownik
        state: >
          {% set wartosc = states('sensor.moc_pv')|float %}        
          {{ (wartosc) >0 }}

history definition:

  - platform: history_stats
    name: Falownik max
    entity_id: sensor.falownik_max_moc
    state: 'on'
    type: time
    start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
    end: '{{ now() }}' 
      
  - platform: history_stats
    name: Falownik praca
    entity_id: sensor.falownik
    state: 'on'
    type: time
    start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
    end: '{{ now() }}' 

and result… nothing is measured although one state is “on”

entity_id: binary_sensor.falownik_max_moc
entity_id: binary_sensor.falownik
1 Like

Thanks!
:man_facepalming:

Hi,
I try to use this for a netamo thermostat.

But i always have an error. The sensor is unavailable.

If someone could check my code, it will be great :

- platform: template
  sensors:
    test:
      friendly_name: test_test
      icon_template: mdi:radiator
      value_template: "{ states('sensor.duree_de_chauffe' , 'hvac_modes' , 'heat')"
      unit_of_measurement: 'min'

- platform: history_stats
  name: "duree_de_chauffe"
  entity_id: climate.salon
  state: "heat"
  type: time
  # end today at 00:00:00
  end: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
  duration:
    hours: 24

thanks per advance :slight_smile:

The template sensor should create a sensor that reports the current operational state, obtained from the climate entity of the thermostat. In the corrected configuration below that entity_id: will be sensor.test

The hvac_modes attribute of the climate entity only contains a list of supported operation modes, not the current operating state of the thermostat. Check for the existence of the attribute hvac_action and see what changes to when the thermostat calls for heat. Not all thermostat integrations may support hvac_action or properly update the status.

The history_stats sensor looks at sensor.test and measures the time its state is heat.

- platform: template
  sensors:
    test:
      friendly_name: test_test
      icon_template: mdi:radiator
      value_template: "{{ state_attr('climate.salon' , 'hvac_action' )}}"

- platform: history_stats
  name: "duree_de_chauffe"
  entity_id: sensor.test
  state: "heating"
  type: time
  # end today at 00:00:00
  end: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
  duration:
    hours: 24

I’m not sure what time period you are wanting to look at. I think your history_stats sensor will give you yesterday’s runtime, not today’s.

To get the run time starting at midnight today, the interval resetting every midnight…

start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
end: "{{ now() }}"

To get the usage continuously over a rolling 24 hour period, use the below.

   end: "{{ now() }}"
   duration: 
     hours: 24

Thanks a lot for the answer.

I use Netatmo.
If i see attributes :

So, i see the modes : auto, heat, off.
And i see hvac_action, who is heating now.

What i think, is that if i have “heat” in state of history stats, i just have the manual heat demand (manual boost on Netatmo). An no the heating period when the mode is auto.
I say that, because i see for yesterdayt 1,30H and i think that it isn’t all the heating duration of my house in the day.

If i have “heating” in state of the history stats, it doesn’t works. i have a 0 duration.

What do you think about that?

This is the code who works, but not sure i have all the heating duration for yesterday.

- platform: history_stats
  name: "temps_chauffage"
  entity_id: climate.salon
  state: "heat"
  type: time
  # end today at 00:00:00
  end: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
  duration:
    hours: 24

test:
        friendly_name: test_test
        icon_template: mdi:radiator
        value_template: "{{ state_attr('sensor.temps_chauffage' , 'hvac_action' )}}"

EDIT :
Here is my heating report for yesterday :

Like i said, and as you can see, there is few hours in the day with heating. But it is “auto” heating.

And because of my tests, only 1H30 of manual heating, which is the value given by the history-stats

RE-EDIT

When i am on auto mode (schedule), and when there is a heating demand, i see in the attributes :

hvac_action: heating
preset_mode: Schedule

May me is it possible to combine the 2 values?

Like : give me the heatng duration in manual mode (it works) and the heating duration when hvac_action is “heating” and preset_mode is “Schedule”.

But i don’t know how to do that.

What do you think ?

The state of climate.salon will be only be auto,heat or off. It doesn’t tell you when the unit is actually heating. It only tells you if the unit will heat when it gets too cold or if it is completely turned off. It won’t help you find how much energy it using.

You need to use the state of sensor.test with history_statistics to tell exactly when the unit is running. The states should be.

  • heating – The unit is currently heating and using electricity/gas to heat the air.
  • idle – The unit is currently not heating
  • off – The unit is completely turned off

Try the following configuration… If you turn the heat up, you should see a change in heating duration almost immediately.

- platform: template
  sensors:
    test:
      friendly_name: test_test
      icon_template: mdi:radiator
      value_template: "{{ state_attr('climate.salon' , 'hvac_action' )}}"

- platform: history_stats
  name: "duree_de_chauffe"
  entity_id: sensor.test
  state: "heating"
  type: time
  end: "{{ now() }}"
  duration:
    hours: 24

Thanks a lot Eric. I understand perfecly the way.

But it doesn’t works, i’m sorry.

Even if i stop the heat, and turn on the heat. The sensor’s value stay at 0.

My actually code

     temps_chauffage2:
        friendly_name: temps_chauffage2
        icon_template: mdi:radiator
        value_template: "{{ state_attr('climate.salon', 'hvac_action' )}}"

- platform: history_stats
  name: "temps_chauffage2"
  entity_id: sensor.temps_chauffage2
  state: "heating"
  type: time
  start: "{{ now().replace(hour=0, minute=0, second=0, microsecond=0) }}"
  end: "{{ now() }}"
1 Like

I have the exact same situation. did you manage a fix?

Update: its working for me now with this config

- platform:  history_stats
  name:  "hvac_run_time"
  entity_id:  sensor.hvac_state
  state:  "heating"
  type:  time
  end:  "{{ now() }}"
  duration:
    hours: 24

I am very new to getting HA working in my production home environment.

I am using a Radio Thermostat CT-50, using the information in this helpful thread,

I have added this block in my configuration.yaml

recorder:  
  purge_keep_days: 365
# HVAC On/Off time Tracking
binary_sensor:
  - platform: template
    sensors:
      hvac_heat:
        friendly_name: "HVAC Heat"
        value_template: >-
          {{ is_state_attr('climate.home', 'hvac_action', 'heating') }}
  - platform: template
    sensors:
      hvac_cool:
        friendly_name: "HVAC Cool"
        value_template: >-
          {{ is_state_attr('climate.home', 'hvac_action', 'cooling') }}
# HVAC History Tracking
sensor:
  - platform: history_stats
    name: Daily Heating ON Time
    entity_id: binary_sensor.hvac_heat
    state: 'on'
    type: time
    start: '{{ now().replace(hour=0).replace(minute=0).replace(second=0) }}'
    end: '{{ now() }}'
  - platform: history_stats
    name: Hourly Cooling ON Time
    entity_id: binary_sensor.hvac_cool
    state: 'on'
    type: time
    start: '{{ now().replace(minute=0).replace(second=0) }}'
    end: '{{ now() }}'

This is my state info from the developers tab:

I did not get any errors when I verified the yaml and reloaded…

Now how do I check if the sensor is actually working?
View a graph of the logged data over time?

Thanks for that code. Worked like a charm for me. Though I had to make a slight modification. My system (cool/heat/off) and fan (on/auto/circulate/off) are separate (Honeywell). I have my fan set to circulate which works regardless of the system operation. So when it is off, the fan can still circulate air.
Here’s the updated code to take that into account. (does check for fan twice. As when it’s off, the hvac_action is idle, but fan_action is running. But if it’s heating, the hvac_action is heat/cool, so I don’t want to capture that with a fan_action in the initial if ‘off’ statement)

basement_status:
        friendly_name: "Basement Climate Status"
        value_template: >-
          {%- if is_state('climate.basement', 'off') %}
            {% if is_state_attr('climate.basement', 'fan_action', 'running') %}
              fan
            {% else %}
              off
            {% endif %}
          {% elif is_state_attr('climate.basement', 'hvac_action', 'fan') %}
            fan
          {% elif is_state_attr('climate.basement', 'hvac_action', 'idle') %}
            idle  
          {% elif is_state_attr('climate.basement', 'hvac_action', 'cooling') %}
            cool
          {% elif is_state_attr('climate.basement', 'hvac_action', 'heating') %}
            heat
          {% else %}
            unknown
          {%- endif %}
        icon_template: >-
          {% if is_state('sensor.basement_status',"idle") %}
            mdi:power-on
          {% elif is_state('sensor.basement_status',"cool") %}
            mdi:snowflake
          {% elif is_state('sensor.basement_status',"heat") %}
            mdi:fire
          {% elif is_state('sensor.basement_status',"fan") %}
            mdi:fan
          {% else %}
            mdi:octagon
          {% endif %}