An approach to both detailed and group level energy management

My approach to detailed energy management in Home Assistant

I started measuring my home energy use about 3 years ago. In the beginning it was just interesting to get things setup and see my total usage. It gave me some insights, but I found myself wanting a more granular level of detail that would allow me to more quickly identify problem areas and opportunities for improvement.

Detailed energy monitoring with Emporia Gen2 Vue

About 18 months ago I purchased two Emporia Gen2 Vue devices and outfitted 16 circuits in each of my two electrical panels. Being able to report on the total power usage as well as details of 32 selected circuits gives a really nice level of detail.

I purchased the Vue2 devices because of the well thought out design, attractive pricing, and at that time, Emporia was saying local API was on their roadmap… based on many inquiries and Emporia’s forum responses, it doesn’t seem like that will ever happen.

Using Emporia’s cloud services

Until a week ago, I had been using the custom_component integration that was written by magico13. That served me well, but it requires cloud accounts is limited to 1 minute reporting because of load on cloud servers. One minute reporting is still very good, and probably covers 98% of the use cases that anyone would have.

Emporia Gen2 Vue running ESPHome!

Even though everything was working, I always felt a little cheated that I couldn’t access the full capabilities of the device in Home Assistant. Last week I stumbled across this fantastic project where a group reverse engineered the Vue device and provided instructions to flash with ESPHome! I flashed both units later that day, and after a few small hurdles, I had everything working perfectly. I finally have this unit working exactly as I originally anticipated with the ability to see real-time data with updates few seconds.

Home Assistant Energy Dashboard

The Home Assistant developers have done a great job adding in better support for monitoring energy. Nice looking dashboards with improved statistics and reporting capabilities. All the work I’ve done can also take advantage of those built in features. I’d like to see native capabilities to more easily group devices together for energy reporting and show real time power reporting as I’ve done in mine, but I’m guessing it will come at some point. For my HA energy dashboard, I’m only monitoring my device groups, not individual devices since I have that in my custom dashboard. I also have more detailed reporting in Influx/Grafana, but that’s a separate discussion.

General concepts for my approach

I’ll start out by saying that not everyone will agree with everything I do in my approach toward this. (i.e. basing a lot of this on entity_id naming and not aiming for “Perfect” measurements of energy usage) That said, there are always limitations and compromises to consider, and based on a good amount of research, trial, and error, I think this is a pretty good compromise. It definitely gives me a clear and detailed understanding of my usage.

Here are the key points in my configuration:

  • I only store and report data from power and energy sensors that I’ve created with templates. This allows to ensure consistent naming and attributes since I use power details from many different integrations and sensors. This also allows me to create a relationship between the circuit being monitored and the loads that are running on that circuit. Naming is also very important when you want to use things like filters or templates to minimize the amount of manual group management required.
  • Similar to the point above, you should consider excluding unused power and energy sensors from the recorder to minimize data size and performance impact.
  • I keep all of my energy configuration in single package yaml file for easy management. If you aren’t using the package directory approach for managing and organizing at least your complex configurations, you should look into it.

Backend Configuration:

Create an input_number to store your energy cost

You can also reference this entity in the native energy dashboard, so it is dual purpose. Note I do not have variable energy rates, but @guardmedia wrote a nice addition to this thread with a detailed variable rate configuration here.

input_number:
  energy_kwh_cost:
    name: Energy kWh Cost
    icon: mdi:currency-usd
    mode: box
    unit_of_measurement: "USD/kWh"
    min: 0.001
    max: 1

Create a template trigger for all energy sensors

The reason for the trigger template for sensors is to control the number of updates that would occur otherwise. In my case I’m having the templates evaluate and capture energy data from all sensors and groups every 5 seconds. So my trigger is a 5 second pattern. You can change that to your needs, but your database will grow very quickly and performance will suffer if you aren’t careful.

IMPORTANT: If you use a regular template sensor without a trigger for a group of devices, you will likely have issues. Consider a group with 10 devices getting updates every 1 second. That template will re-evaluate every time each one of those devices reports. That’s at least 10+ updates per second for each group sensor which over days and weeks will add up to issues.

template:
  - trigger:
      # NOTE - This approach does somewhat impact the accuracy of your data since it is not averaging the data in
      # between the time periods, but it will still end up being relatively close and give you a good data
      # about energy usage
      - platform: time_pattern
        seconds: "/5" #Only pull electric data every 5 seconds to minimize data / performance impact
      - platform: homeassistant
        event: start
      - platform: event
        event_type: "call_service"
        event_data:
          domain: "template"
          service: "reload"
    sensor:
      ################  Electrical Panel #1 Voltage ###############
      - name: electric_home_voltage
        attributes:
          tmp_friendly_name: "Electric Home Voltage"
          <<: &voltage_sensor_force_update
            min_last_updated: "{{ now().minute }}" #Forces reporting every 1 min
        <<: &voltage_sensor_defaults
          device_class: voltage
          unit_of_measurement: "V"
          state_class: measurement
        state: "{{ (states('sensor.electric_p1_phase_a_voltage') | float(0) + states('sensor.electric_p1_phase_b_voltage') | float(0)) | round(1) }}"
      - name: electric_home_l1_voltage
        attributes:
          tmp_friendly_name: "Electric Home Leg 1 Voltage"
          <<: *voltage_sensor_force_update
        <<: *voltage_sensor_defaults
        state: "{{ (states('sensor.electric_p1_phase_a_voltage') | float(0)) | round(1) }}"
      - name: electric_home_l2_voltage
        attributes:
          tmp_friendly_name: "Electric Home Leg 2 Voltage"
          <<: *voltage_sensor_force_update
        <<: *voltage_sensor_defaults
        state: "{{ (states('sensor.electric_p1_phase_b_voltage') | float(0)) | round(1) }}"

      ################  Electrical Whole Home Power ###############
      - name: electric_home_total_power
        attributes:
          tmp_friendly_name: "Electric Home Total Power"
          <<: &power_sensor_force_update
            two_min_update: "{% if now().minute is divisibleby(2) %} {{now().minute}} {% else %} {{now().minute - 1 }} {% endif %}" #Ensure updates occur at least every 2 min for influx reporting
        state: "{{ (states('sensor.p1_00_total_power') | float(0) + states('sensor.p2_00_total_power') | float(0)) | round(1)  }}"
        <<: &power_sensor_defaults
          device_class: power
          unit_of_measurement: "W"
          state_class: measurement

      ################  Electrical Panel #1 Power ###############
      # Note - Subtracting out power in the templates is not perfect.  The circuits get updated every x seconds from Emporia vue, but the change to the virtual circuits happens immediately.
      # Currently handling by simply not allowing the total power of the circuit to go negative which is why max() filter is in use. (i.e. 0 is bigger than negative so max will return that)
      - name: p1_00_total_power
        attributes:
          tmp_friendly_name: "Electric Panel 1 Total Power"
          <<: *power_sensor_force_update
        state: "{{ max( states('sensor.electric_p1_total_power') | float(0) | round(1), 0.0 ) }}"
        <<: *power_sensor_defaults
      - name: p1_01_first_floor_hvac_ahu_1_power
        attributes:
          tmp_friendly_name: "First Floor HVAC AHU 1 Power"
          <<: *power_sensor_force_update
        state: "{{ max( states('sensor.fl1_air_handler_p1_1_power') | float(0) | round(1), 0.0 ) }}"
        <<: *power_sensor_defaults
      - name: p1_02_first_floor_hvac_ahu_2_power
        attributes:
          tmp_friendly_name: "First Floor HVAC AHU 2 Power"
          <<: *power_sensor_force_update
        state: "{{ max( states('sensor.fl1_air_handler_p1_2_power') | float(0) | round(1), 0.0 ) }}"
        <<: *power_sensor_defaults
      - name: p1_03_kitchen_gfci_power
        attributes:
          tmp_friendly_name: "Kitchen GFCI Power"
          <<: *power_sensor_force_update
        state: "{{ max( states('sensor.kitchen_gfci_p1_3_power') | float(0) | round(1), 0.0 ) }}"
        <<: *power_sensor_defaults
      - name: p1_04_office_power
        attributes:
          tmp_friendly_name: "Office Power"
          <<: *power_sensor_force_update
        state: >-
          {% set virtualpower = expand('group.p1_04_v_power') | rejectattr('state', 'in', ['unavailable', 'unknown']) | map(attribute='state') | map('float') | sum | round(2) %}
          {{ max( (states('sensor.office_p1_4_power') | float(0) - virtualpower) | round(1), 0.0 ) }}
        <<: *power_sensor_defaults
      - name: p1_04_v_office_floor_light_power
        attributes:
          tmp_friendly_name: "Office Floor Light Power"
          <<: *power_sensor_force_update
        state: "{{ max( (5 * 8 * state_attr('light.office_floor_light','brightness') | float(0) / 255) | round(1), 0.0 ) }}"
        <<: *power_sensor_defaults
      - name: p1_04_v_office_computer_outlet_power
        attributes:
          tmp_friendly_name: "Office Computer Outlet Power"
          <<: *power_sensor_force_update
        state: "{{ max( states('sensor.office_computer_outlet_power') | float(0) | round(1), 0.0 ) }}"
        <<: *power_sensor_defaults
      - name: p1_05_kitchen_refrigerator_power
        attributes:
          tmp_friendly_name: "Kitchen Refrigerator Power"
          <<: *power_sensor_force_update
        state: "{{ max( states('sensor.kitchen_refrigerator_p1_5_power') | float(0) | round(1), 0.0 ) }}"
        <<: *power_sensor_defaults
        
<<<<<<<<<<<<<<<<<<<<<<<<<<  Truncated  >>>>>>>>>>>>>>>>>>>>>>>>>>>

In the snip of yaml above, it probably looks a little complex, but take a look at each entry and it gets a little easier. A few key points:

  • I am using yaml anchors to simplify, thats why you see the “<<: &” and “<<: *”. It’s like declaring a variable, then allows use of those items throughout your code.
  • The names are structured and specific. In the case of p1_00_total_power, that is the total power being delivered by electric panel 1, while p1_04_office_power is electric panel 1, the 4th monitored circuit.
  • Take note of the next entry which is p1_04_v_office_floor_light_power. The “v” is meant to stand for virtual, which means it will be used for some calculations on that circuit. This naming structure allows easy automated creation of “circuit groups” that make up the known loads on each circuit. By subtracting the known loads from the circuit, you are left with the remaining power so nothing is double counted in reporting totals.
  • The last part of the naming used for each sensor is also very specific. “_power”, “_total_power”, “_group_total_power”, “_energy”, “_group_total_energy”, “_group_daily_total_energy”, “_group_monthly_total_energy”, “_group_daily_total_energy_cost”, “_group_monthly_total_energy_cost”
  • The use of “max( x, 0.0)” in most of the states is an easy way of preventing negative power readings which are typically noise or could also occur briefly due to the way calculations are being done

Calculating the values the power sensors templates

Notice there are several ways to get power usage:

  • Direct from another power sensor:
{{ max( states('sensor.fl1_air_handler_p1_1_power') | float(0) | round(1), 0.0 ) }}
  • Adding sensors together:
{{ (states('sensor.electric_p1_phase_a_voltage') | float(0) + states('sensor.electric_p1_phase_b_voltage') | float(0)) | round(1) }}
  • You don’t need to have true energy monitoring on devices to track power usage. If you can determine whether the device is on or off, and you can estimate is power while on, then just calculate it in a template. Granted it is an estimate, but if can give you a lot more information if you are OK with the compromise. Use the p1_04_v_office_floor_light_power example again. That template is calculating an energy value estimate based on 5 bulbs x 8 watts each x brightness of the light to determine power usage.
{{ (5 * 8 * state_attr('light.office_floor_light','brightness') | float(0) / 255) | round(1) }}
  • Same as above, but when only on / off are known for a 45W device:
{% if is_state('switch.landscape_lighting','on') %}{{ 45| float(0) | round(1)}}{% else %}{{ 0 | float(0) | round(1) }}{% endif %}
  • More complex calculations can also be done using templates and groups. In the case of p1_04_office_power, that is a monitored circuit with multiple loads. The known loads are listed with the “v” designations so they can be automatically placed into groups to be subtracted from that circuit. With this approach, the value of p1_04_office_power ends up only being the remaining power of the circuit after the known loads are removed. Without calculation the p1_04_office_power circuit would be 77.4W, which would be double counting the loads on that circuit that are also being measured.
      - name: p1_04_office_power
        attributes:
          tmp_friendly_name: "Office Power"
          <<: *power_sensor_force_update
        state: >-
          {% set virtualpower = expand('group.p1_04_v_power') | rejectattr('state', 'in', ['unavailable', 'unknown']) | map(attribute='state') | map('float') | sum | round(2) %}
          {{ max( (states('sensor.office_p1_4_power') | float(0) - virtualpower) | round(1), 0.0 ) }}
        <<: *power_sensor_defaults

Creating power sensors for devices in a group

In this example, the template will expand out group.water_heater_group_total_power and add the power usage of all the sensors in that group. The resulting sensor created will be sensor.water_heater_group_total_power. Note the different entities even group name is the same as the sensor name, that is just for easy management. In the example picture, this sensor would just be the “head” entry total for kitchen appliances, the sum of all the values in the group shown.
IMPORTANT: This should be done in a trigger template, otherwise will constantly be recalculating. See warnings above.

      - name: kitchen_appliance_group_total_power
        icon: mdi:fridge-outline
        state: >-
          {% set virtualpower = expand('group.kitchen_appliance_group_total_power') | rejectattr('state', 'in', ['unavailable', 'unknown']) | map(attribute='state') | map('float') | sum | round(2) %}
          {{ max( virtualpower | round(1), 0.0 ) }}
        attributes:
          tmp_friendly_name: "Kitchen Appliance Group Total Power"
          <<: *power_sensor_force_update
        <<: *power_sensor_defaults

Creating groups of power sensors

One option is to just manually create the groups you want to monitor, then use the formulas found above to calculate total power usage of that group of sensors. I wanted to automate that group creation and maintenance based on entity names. It does take a good naming structure for it to work properly, but if you stick to the structure it works well.

automation:
  ################  Create and update power groupings for circuits and devices  ###############
  - alias: "Update Power Groups"
    trigger:
      - platform: homeassistant
        event: start
      - platform: event
        event_type: "call_service"
        event_data:
          domain: "group"
          service: "reload"
    action:
      - service: group.set
        data_template:
          object_id: p1_04_v_power
          entities: >
            {% set ns = namespace(entities=[]) %}
            {% for s in states.sensor if s.object_id.startswith('p1_04_v') and s.object_id.endswith('_power') %}
              {% set ns.entities = ns.entities + [ s.entity_id ] %}
            {% endfor %}
            {{ ns.entities }}
      - service: group.set
        data_template:
          object_id: kitchen_appliance_group_total_power
          entities: >
            {% set ns = namespace(entities=[]) %}
            {% for s in states.sensor if '_kitchen_' in s.object_id and not '_light_' in s.object_id and (s.object_id.startswith('p1_') or s.object_id.startswith('p2_')) and s.object_id.endswith('_power') %}
              {% set ns.entities = ns.entities + [ s.entity_id ] %}
            {% endfor %}
            {{ ns.entities }}

Simplifying the energy calculations

When looking into this, I remember it was really tricky to figure out what I needed to do in Home Assistant to convert from power to energy. It is important to be clear on these two terms, they aren’t directly interchangeable.

Power rate of producing or consuming energy. For home electric normally measured in watts(W) or kilowatts(kW). Note I keep everything in watts for simiplicity

Energy measures the total quantity of “Work” done. For home electric normally measured in kWh (kiloWatt hours).

If you are measuring your power in Watts, you can easily track your energy/kWh by following the patterns in the following 3 sections.

Create sensors for hourly energy calculations

These sensors continuously calculate the kWh over the past hour which allows the utility meter integration to capture the daily and monthly energy consumption from this data.

sensor:
  ################  Total hourly energy calculation  ###############
  - platform: integration
    name: electric_home_total_energy
    source: sensor.electric_home_total_power
    <<: &energy_calculation_defaults
      unit_time: h
      unit_prefix: k
      round: 2
  - platform: integration
    name: kitchen_appliance_group_total_energy
    source: sensor.kitchen_appliance_group_total_power
    <<: *energy_calculation_defaults

Tracking consumption with the utility_meter

These sensors are what will be used for actual reporting of usage per day or per month in kWh

utility_meter:
  ################  Track daily consumption for each grouping  ###############
  electric_home_daily_total_energy:
    source: sensor.electric_home_total_energy
    cycle: daily
  kitchen_appliance_group_daily_total_energy:
    source: sensor.kitchen_appliance_group_total_energy
    cycle: daily
  ################  Track monthly consumption for each grouping  ###############
  electric_home_monthly_total_energy:
    source: sensor.electric_home_total_energy
    cycle: monthly
  kitchen_appliance_group_monthly_total_energy:
    source: sensor.kitchen_appliance_group_total_energy
    cycle: monthly

Calculating Cost

sensor:
  - platform: template
    sensors:
      ################  Calculate daily energy cost for each grouping  ###############
      electric_home_daily_total_energy_cost:
        friendly_name: "Electric Home Daily Total Energy Cost"
        value_template: >-
          {{ max( (states('sensor.electric_home_daily_total_energy') | float(0) * states('input_number.energy_kwh_cost') | float(0)) | round(2), 0.00 ) }}
        <<: &energy_cost_defaults
          unit_of_measurement: "$"
          icon_template: mdi:currency-usd
      kitchen_appliance_group_daily_total_energy_cost:
        friendly_name: "Kitchen Appliance Group Daily Total Energy Cost"
        value_template: >-
          {{ max( (states('sensor.kitchen_appliance_group_daily_total_energy') | float(0) * states('input_number.energy_kwh_cost') | float(0)) | round(2), 0.00 ) }}
        <<: *energy_cost_defaults
      ################  Calculate monthly energy cost for each grouping  ###############
      electric_home_monthly_total_energy_cost:
        friendly_name: "Electric Home Monthly Total Energy Cost"
        value_template: >-
          {{ max( (states('sensor.electric_home_monthly_total_energy') | float(0) * states('input_number.energy_kwh_cost') | float(0)) | round(2), 0.00 ) }}
        <<: *energy_cost_defaults
      kitchen_appliance_group_monthly_total_energy_cost:
        friendly_name: "Kitchen Appliance Group Monthly Total Energy Cost"
        value_template: >-
          {{ max( (states('sensor.kitchen_appliance_group_monthly_total_energy') | float(0) * states('input_number.energy_kwh_cost') | float(0)) | round(2), 0.00 ) }}
        <<: *energy_cost_defaults

Example of all entities created for one energy group type

Bringing it all together in the UI

Last year, around earth day, I decided it would be a good time to update my dashboards with a focus on energy throughout. Each one of my dashboard pages has energy details at the top to keep it visible and top of mind. Here are some examples:

title: "" #########################  Home  #########################
icon: mdi:home
cards:
  - type: vertical-stack
    cards:
      - type: entities
        title: "Home"
        icon: mdi:home
        show_header_toggle: false
        entities:
          - type: section
          - entity: sensor.electric_home_daily_total_energy_cost
            type: custom:multiple-entity-row
            name: Energy
            icon: mdi:flash-outline
            state_header: Today $
            format: precision2
            secondary_info: false
            unit: false
            styles:
              font-weight: bold
              color: green
              width: 34px
            entities:
              - entity: sensor.electric_home_monthly_total_energy
                name: Month kWh
                format: precision0
                unit: false
                styles:
                  width: 34px
              - entity: sensor.electric_home_monthly_total_energy_cost
                name: Month $
                format: precision2
                unit: false
                styles:
                  color: green
                  width: 34px
              - entity: sensor.electric_home_daily_total_energy
                name: Today kWh
                format: precision1
                unit: false
                styles:
                  width: 34px
          - entity: sensor.count_outside_fans_on
            type: custom:multiple-entity-row
            name: "On"
            icon: mdi:lightbulb-group
            state_header: Out Fans
            format: precision0
            secondary_info: false
            unit: false
            entities:
              - entity: sensor.count_inside_lights_on
                name: In Lights
                format: precision0
                unit: false
              - entity: sensor.count_inside_fans_on
                name: In Fans
                format: precision0
                unit: false
              - entity: sensor.count_outside_lights_on
                name: Out Lights
                format: precision0
                unit: false
          - entity: sensor.count_doors_open
            type: custom:multiple-entity-row
            name: "Security"
            icon: mdi:shield
            state_header: ""
            format: precision0
            secondary_info: false
            unit: false
            show_state: false
            entities:
              - entity: sensor.count_garage_doors_open
                name: "Garage Open"
                format: precision0
                unit: false
              - entity: sensor.count_doors_open
                name: "Doors Open"
                format: precision0
                unit: false

title: "" #########################  Lights  #########################
icon: mdi:lightbulb-multiple
cards:
  ## Heating and cooling
  - type: entities
    title: "Lighting"
    icon: mdi:lightbulb-group
    show_header_toggle: false
    entities:
      - type: section
      - entity: sensor.light_group_daily_total_energy_cost
        type: custom:multiple-entity-row
        name: Energy
        icon: mdi:flash-outline
        state_header: Today $
        format: precision2
        secondary_info: false
        unit: false
        styles:
          font-weight: bold
          text-align: right
          color: green
        entities:
          - entity: sensor.light_group_monthly_total_energy
            name: Month kWh
            format: precision1
            unit: false
          - entity: sensor.light_group_monthly_total_energy_cost
            name: Month $
            format: precision2
            unit: false
            styles:
              font-weight: bold
              text-align: right
              color: green
          - entity: sensor.light_group_daily_total_energy
            name: Today kWh
            format: precision1
            unit: false
      - type: custom:fold-entity-row
        head:
          entity: sensor.light_group_total_power
          name: "Power"
          icon: " "
        entities:
          - type: custom:auto-entities
            show_empty: false
            card:
              type: entities
              title: ""
              show_header_toggle: false
            sort:
              method: state
              reverse: true
              numeric: true
            filter:
              include:
                - group: "group.light_group_total_power"
      - type: section
      - entity: sensor.count_outside_fans_on
        type: custom:multiple-entity-row
        name: "On"
        icon: mdi:lightbulb-group
        state_header: Out Fans
        format: precision0
        secondary_info: false
        unit: false
        entities:
          - entity: sensor.count_inside_lights_on
            name: In Lights
            format: precision0
            unit: false
          - entity: sensor.count_inside_fans_on
            name: In Fans
            format: precision0
            unit: false
          - entity: sensor.count_outside_lights_on
            name: Out Lights
            format: precision0
            unit: false

title: "" #########################  Home Tech  Net#########################
icon: "mdi:network"
badges: []
cards:
  - type: entities
    title: "Technology"
    icon: mdi:network
    show_header_toggle: false
    entities:
      - type: section
      - entity: sensor.technology_group_daily_total_energy_cost
        type: custom:multiple-entity-row
        name: Energy
        icon: mdi:flash-outline
        state_header: Today $
        format: precision2
        secondary_info: false
        unit: false
        styles:
          font-weight: bold
          color: green
          width: 34px
        entities:
          - entity: sensor.technology_group_monthly_total_energy
            name: Month kWh
            format: precision1
            unit: false
            styles:
              width: 34px
          - entity: sensor.technology_group_monthly_total_energy_cost
            name: Month $
            format: precision2
            unit: false
            styles:
              color: green
              width: 34px
          - entity: sensor.technology_group_daily_total_energy
            name: Today kWh
            format: precision1
            unit: false
            styles:
              width: 34px
      - type: custom:fold-entity-row
        head:
          entity: sensor.technology_group_total_power
          name: "Power"
          icon: " "
        entities:
          - type: custom:auto-entities
            show_empty: false
            card:
              type: entities
              title: ""
              show_header_toggle: false
            sort:
              method: state
              reverse: true
              numeric: true
            filter:
              include:
                - group: "group.technology_group_total_power"
      - type: custom:fold-entity-row
        head:
          type: section
          label: Configure
        open: false
        entities:
          - entity: input_boolean.network_device_down_notify
          - entity: input_boolean.server_device_down_notify
          - entity: input_boolean.wan_down_notify
          - entity: input_boolean.device_restart_notify

Extra credit

Once you have access to all of your power and energy data, there are many creative things you can use it for.

Laundry

  • See when appliances are running
  • Get notifications when a cycle is finished
  • Count how many times each appliance has run in the past 7 days

Water

  • See how long your water heater is running each day
  • Count how many times per hour your sump pump is running
  • Notify if your sump pump runs x times per hour

Heating and Cooling

  • Calculate the amount of time your heater is running per day
  • For those with heat pumps, count your defrost cycles per day as well as aux heat runtime.

My full electric configuration and main dashboard yaml files

packages\home_electric.yaml

lovelace\home_electric.yaml

43 Likes

Wow! You’ve really gone through an incredible amount of effort to get this set up, and it looks great!

Thank you for sharing!

It’s awesome, thanks for sharing !

I like all the thought that went into making it useful & practical, like grouping measurements of different but related circuits together.
I’ve definitively got some ideas to steal from you ! :slight_smile:

Fantastic work, I really like how you have been able to group sensors both for calculation and dashboard display.

I also like how you are doing estimation for the power of other devices.

You might want to have a look at powercalc, which is great for doing the estimation for not only lights but other devices as well. My HVAC doesn’t have a power monitor, but with powercalc I can assign estimation values for each mode of operation, which now flow through to energy dashboard, it can also handle the setup of energy integration and utility meters for your devices:

2 Likes

Thanks for the feedback and the tip, definitely something I’ll look into.

Thank you so much for writing this up. It is super helpful.

@ccpk1 Wow! This is very impressive! Thanks for sharing!

I’ve got a couple questions for you, I’m very interested in utilizing the energy consumption portion of your efforts on my dashboard across 8 high usage sensors: (Washing Machine, Water Heater, Dryer, AC, etc.).

  1. I haven’t (and probably won’t) convert my Emporia to local ESPHome, so is it possible to plug in the existing entities into the utility meter to track them through the cloud reporting? (I understand this would only get feedback every 60 seconds or so)

  2. If so…I’m still a bit of a hack when it comes to YAML, can I just rip out that section of the code or is it dependent on other variables to function correctly?

  3. Lastly, does the script automatically reset itself every cycle? (Monthy/Day/Today)?

Thanks again! Looking forward to playing with this some more!

@mboarman - You can definitely do all of those things with the cloud integration to Emporia you are using. In fact, I used that integration for at least year before changing to the ESPHome version a few week ago. Agree your wattage data only updates every 60 seconds, but that is granular enough for most things anyway.

As for calculating your energy usage, you can just follow the steps in Simplifying the Energy Calculations section to capture energy usage for anything that is reporting power in watts. I mocked up the example below as if you had sensor.washing_machine_power. (It might have _1min appended if it came from the integration)

sensor:
  ################  Total hourly energy calculation  ###############
  ################  Always shows the past hour of consumption  ###############
  - platform: integration
    name: washing_machine_energy
    source: sensor.washing_machine_power
    unit_time: h
    unit_prefix: k
    round: 2

utility_meter:
  ################  Track daily consumption for each grouping  ###############
 washing_machine_daily_total_energy:
    source: sensor.washing_machine_energy
    cycle: daily
  ################  Track monthly consumption for each grouping  ###############
 washing_machine_monthly_total_energy:
    source: sensor.washing_machine_energy
    cycle: monthly

Lastly, the utility_meter integration automatically takes care of the daily and monthly cycle resets. You can see where they are defined in the example above, but there are several ways to further customize the reset cycles if you want to match them with your utility provider.

Utility Meter - Home Assistant (home-assistant.io)

Good luck

1 Like

@ccpk1 Thanks for clarifying this! I think I have it built out, but I have one additional question: I added the input number yaml to my package:

input_number:
  energy_kwh_cost:
    name: Energy kWh Cost
    icon: mdi:currency-usd
    mode: box
    unit_of_measurement: "USD/kWh"
    min: 0.001
    max: 1

But I don’t understand where I’m supposed to populate the USD/kWh state rate? What am I missing?

Edit: I set my rate: “0.10772” in the developer tab/states. hopefully that will work?

Yes, just editing it in developer tab is fine, that’s what I do. You could also add that input_number to one of your dashboards to edit in the UI, but it rarely changes, so not likely worth it.

Keep in mind, you can edit the native HA energy dashboard settings to reference that entity for your cost information. This way you only have the one source to keep up to date.

1 Like

If you’re using Influx v2 - I would love to see your examples. I have the Emporia V2 but switched over to the Iotawatt as it allowed for easy data retrieval. My Influx/Grafana skills are meager.

Thank you @ccpk1 – this is amazing!

For those that have different rates by time of day, I used the following to setup automatic rate tracking for Toronto Hydro.

Input number is used to define rates for each tariff rate.

input_number:
  energy_tou_rate_onpeak:
    name: Energy kWh Cost On-Peak
    icon: mdi:currency-usd
    mode: box
    unit_of_measurement: "CAD/kWh"
    min: 0.001
    max: 1
    initial: 0.17
  energy_tou_rate_midpeak:
    name: Energy kWh Cost Mid-Peak
    icon: mdi:currency-usd
    mode: box
    unit_of_measurement: "CAD/kWh"
    min: 0.001
    max: 1
    initial: 0.113
  energy_tou_rate_offpeak:
    name: Energy kWh Cost Off-Peak
    icon: mdi:currency-usd
    mode: box
    unit_of_measurement: "CAD/kWh"
    min: 0.001
    max: 1
    initial: 0.082
  energy_tou_rate_average:
    name: Energy kWh Cost Average
    icon: mdi:currency-usd
    mode: box
    unit_of_measurement: "CAD/kWh"
    min: 0.001
    max: 1
    initial: 0.10325

A template is used to define the name of the tariff rate, and another is used to retrieve the current tariff rate. The name is important since it will be used in utility_meter to calculate which rates apply.

The energy_tou_tariff state can be adjusted as needed

template:
  - trigger:
      - platform: time_pattern
        seconds: "/1"
      - platform: homeassistant
        event: start
      - platform: event
        event_type: "call_service"
        event_data:
          domain: "template"
          service: "reload"
    sensor:
      - name: energy_tou_tariff
        unique_id: e09d54cbfd1e4511a66a847f25e246ef
        icon: mdi:chart-bar
        state: >
          {% if now().weekday() >= 0 and now().weekday() < 5 %}
            {% if now().month >= 5 and now().month < 11 %}
              {% if now().hour >= 7 and now().hour < 11 %} onpeak
              {% elif now().hour >= 11 and now().hour < 17 %} midpeak
              {% elif now().hour >= 17 and now().hour < 19 %} onpeak
              {% else %} offpeak
              {% endif %}
            {% else %}
              {% if now().hour >= 7 and now().hour < 11 %} midpeak
              {% elif now().hour >= 11 and now().hour < 17 %} onpeak
              {% elif now().hour >= 17 and now().hour < 19 %} midpeak
              {% else %} offpeak
              {% endif %}
            {% endif %}
          {% else %} offpeak
          {% endif %}
      - name: energy_kwh_cost
        unique_id: 97041eea00d0448caf9e300ee08694a5
        unit_of_measurement: "CAD/kWh"
        icon: mdi:currency-usd
        state: >
          {% if is_state('sensor.energy_tou_tariff', 'onpeak') %} {{ states('input_number.energy_tou_rate_onpeak') | float }}
          {% elif is_state('sensor.energy_tou_tariff', 'midpeak') %} {{ states('input_number.energy_tou_rate_midpeak') | float }}
          {% elif is_state('sensor.energy_tou_tariff', 'offpeak') %} {{ states('input_number.energy_tou_rate_offpeak') | float }}
          {% else %} {{ states('input_number.energy_tou_rate_average') | float }}
          {% endif %}

Utility Meter is setup for daily and monthly cycles, and tariffs are defined. These tariff names must match the template sensor values above for automatic rate select. Once implemented, usage is then split into “buckets”, where the sensor name will now include the tariff name as a suffix.

For example, sensor.electric_home_daily_total_energy would become …

  • sensor.electric_home_daily_total_energy_onpeak for onpeak usage
  • sensor.electric_home_daily_total_energy_midpeak for midpeak usage
  • and sensor.electric_home_daily_total_energy for offpeak usage

Actual tariff names can be changed as needed, just make sure they still match. Note the use of yaml anchor on the first value using <<: &utility_meter_tariff_defaults, and all other utility_meter definitions have <<: *utility_meter_tariff_defaults instead.

utility_meter:
  ################  Track daily consumption for each grouping  ###############
  electric_home_daily_total_energy:
    source: sensor.electric_home_total_energy
    cycle: daily
    <<: &utility_meter_tariff_defaults
      tariffs:
        - offpeak
        - midpeak
        - onpeak
  other_daily_total_energy:
    source: sensor.other_total_energy
    cycle: daily
    <<: *utility_meter_tariff_defaults
# ... repeat as needed ...
  ################  Track monthly consumption for each grouping  ###############
  electric_home_monthly_total_energy:
    source: sensor.electric_home_total_energy
    cycle: monthly
    <<: *utility_meter_tariff_defaults
  other_monthly_total_energy:
    source: sensor.other_total_energy
    cycle: monthly
    <<: *utility_meter_tariff_defaults
# ... repeat as needed ...

Since utility_meter will now output usage into multiple “buckets” by tariff, total usage must then be configured to aggregate each tariff rate.

sensor:
  - platform: template
    sensors:
    ################  Calculate daily energy usage for each grouping  ###############
      electric_home_daily_total_energy_usage:
        friendly_name: "Electric Home Daily Total Energy Usage"
        value_template: "{{ states('sensor.electric_home_daily_total_energy_onpeak') | float(2) + states('sensor.electric_home_daily_total_energy_midpeak') | float(2) + states('sensor.electric_home_daily_total_energy_offpeak') | float(2) }}"
        <<: &energy_usage_defaults
          unit_of_measurement: "kWh"
          icon_template: mdi:gauge
      electric_other_daily_total_energy_usage:
        friendly_name: "Electric Other Daily Total Energy Usage"
        value_template: "{{ states('sensor.other_daily_total_energy_onpeak') | float(2)  + states('sensor.other_daily_total_energy_midpeak') | float(2) + states('sensor.other_daily_total_energy_offpeak') | float(2) }}"
        <<: *energy_usage_defaults
# ... repeat as needed ...
    ################  Calculate monthly energy usage for each grouping  ###############
      electric_home_monthly_total_energy_usage:
        friendly_name: "Electric Home Monthly Total Energy Usage"
        value_template: "{{ states('sensor.electric_home_monthly_total_energy_onpeak') | float(2) + states('sensor.electric_home_monthly_total_energy_midpeak') | float(2) + states('sensor.electric_home_monthly_total_energy_offpeak') | float(2) }}"
        <<: *energy_usage_defaults
      electric_other_monthly_total_energy_usage:
        friendly_name: "Electric Other Monthly Total Energy Usage"
        value_template: "{{ states('sensor.other_monthly_total_energy_onpeak') | float(2)  + states('sensor.other_monthly_total_energy_midpeak') | float(2) + states('sensor.other_monthly_total_energy_offpeak') | float(2) * states('input_number.energy_tou_rate_offpeak') | float(2) }}"
        <<: *energy_usage_defaults
# ... repeat as needed ...

In addition, cost must be calculated and aggregated for each tariff as well.

sensor:
  - platform: template
    sensors:
    ################  Calculate daily energy cost for each grouping  ###############
      electric_home_daily_total_energy_cost:
        friendly_name: "Electric Home Daily Total Energy Cost"
        value_template: >-
          {{ max((states('sensor.electric_home_daily_total_energy_onpeak') | float(2) * states('input_number.energy_tou_rate_onpeak') | float(2))
            + (states('sensor.electric_home_daily_total_energy_midpeak') | float(2) * states('input_number.energy_tou_rate_midpeak') | float(2))
            + (states('sensor.electric_home_daily_total_energy_offpeak') | float(2) * states('input_number.energy_tou_rate_offpeak') | float(2)) | round(2), 0.00) }}
        <<: &energy_cost_defaults
          unit_of_measurement: "$"
          icon_template: mdi:currency-usd
      electric_other_daily_total_energy_cost:
        friendly_name: "Electric Other Daily Total Energy Cost"
        value_template: >-
          {{ max((states('sensor.other_daily_total_energy_onpeak') | float(2) * states('input_number.energy_tou_rate_onpeak') | float(2))
            + (states('sensor.other_daily_total_energy_midpeak') | float(2) * states('input_number.energy_tou_rate_midpeak') | float(2))
            + (states('sensor.other_daily_total_energy_offpeak') | float(2) * states('input_number.energy_tou_rate_offpeak') | float(2)) | round(2), 0.00) }}
        <<: *energy_cost_defaults
# ... repeat as needed ...
    ################  Calculate monthly energy cost for each grouping  ###############
      electric_home_monthly_total_energy_cost:
        friendly_name: "Electric Home Monthly Total Energy Cost"
        value_template: >-
          {{ max((states('sensor.electric_home_monthly_total_energy_onpeak') | float(2) * states('input_number.energy_tou_rate_onpeak') | float(2))
            + (states('sensor.electric_home_monthly_total_energy_midpeak') | float(2) * states('input_number.energy_tou_rate_midpeak') | float(2))
            + (states('sensor.electric_home_monthly_total_energy_offpeak') | float(2) * states('input_number.energy_tou_rate_offpeak') | float(2)) | round(2), 0.00) }}
        <<: *energy_cost_defaults
      electric_other_monthly_total_energy_cost:
        friendly_name: "Electric Other Monthly Total Energy Cost"
        value_template: >-
          {{ max((states('sensor.other_monthly_total_energy_onpeak') | float(2) * states('input_number.energy_tou_rate_onpeak') | float(2))
            + (states('sensor.other_monthly_total_energy_midpeak') | float(2) * states('input_number.energy_tou_rate_midpeak') | float(2))
            + (states('sensor.other_monthly_total_energy_offpeak') | float(2) * states('input_number.energy_tou_rate_offpeak') | float(2)) | round(2), 0.00) }}
        <<: *energy_cost_defaults
# ... repeat as needed ...

Finally, an automation is configured to set the tariff rate for each utility_meter. We use an automated group to simplify rate automation.

automation:
  ################  Create and update power groupings for circuits and devices  ###############
  - alias: "Update Utility Groups"
    trigger:
      - platform: homeassistant
        event: start
      - platform: event
        event_type: "call_service"
        event_data:
          domain: "group"
          service: "reload"
    action:
      - service: group.set
        data_template:
          object_id: utility_meters
          entities: >
            {% set ns = namespace(entities=[]) %}
            {% for s in states.utility_meter if s.object_id.endswith('_daily_total_energy') or s.object_id.endswith('_monthly_total_energy') %}
              {% set ns.entities = ns.entities + [ s.entity_id ] %}
            {% endfor %}
            {{ ns.entities }}
  ################  Update power tariffs for all circuits  ###############
  - alias: "Update Power Tariffs"
    trigger:
      - platform: homeassistant
        event: start
      - platform: event
        event_type: "call_service"
        event_data:
          domain: "group"
          service: "reload"
      - platform: state
        entity_id: sensor.energy_tou_tariff
      - platform: time_pattern
        seconds: "/5"
    action:
      - service: utility_meter.select_tariff
        data:
          entity_id: group.utility_meters
          tariff: '{{ states("sensor.energy_tou_tariff") }}'

Edited: simplified yaml using anchors and automatic group creation

3 Likes

@guardmedia - Thanks for the detailed addition, I’m sure it will help several people out. I updated my original post with a link to your work so it can easily be found.

I’ll try to post some details about my Influx and Grafana in the next few weeks when I get a little free time.

1 Like

Thanks @ccpk1 for sharing this project. I would like to create a light version of that.
I would like to calculate my total energycost for each month.

In general I want an overview for each month

  • used electricity from the grid in peak period x electricity cost per kWh that month
  • used electricity from the grid in low period x electricity cost per kWh that month
  • used amount of natural gas x cost that month per m3 that month
  • injected electricity in peak periode x electricity cost per kWh that month
  • injected electricity in low periode x electricity cost per kWh that month
    Calculate the YTD total cost in order to pay a correct advance to my energy supplier.

One problem I not that familiar with yaml programming as you do. Nor with the creation of custom sensor :smiley:
So I you feel comfortable to help me a bit, thanks in advanced!!

@ccpk1 I’ve been using a variation of your work in my dashboard and absolutely love the way it’s formatted and delivered! I only find myself wishing for one small additional…and to be honest, I don’t even know if it’s possible!

I would like to add a fourth column that shows the previous months Kwh consumption for each sensor so I could see how I’m tracking against it. Is there a way to capture the last day of the month data from the month sensor and store it to display in an additional column?! Thoughts?

Thanks for this! This has really encouraged me to do something similar. I just purchased and flashed an Emporia and working on getting everything set up.

Are you able to share how you track if your washing machine is on and how many times it has ran?

Thanks!

Edit:
Nevermind. Figured out a way! Thanks again!

@ccpk1

Thank you for the information.

How do you reset the data each month? Is there any automation?

This looks awesome, thanks for sharing.
I’m new to the energy consumption side of homeassistant. Hoping to make sense of the out of the box functionality before I delve in to yaml.
Could anyone point me to an energy dashboard idiot’s guide? Beyond adding individual devices (I’ve only got a couple) I can’t figure out how to get any of the data in to a nice fancy dashboard.
Thanks,
matt

@ccpk1 would you mind sharing your esphome yaml config files for the VUE2 devices themselves?

I have 3 vue devices, but they are all under 1 main power supply. 2 vue devices needed in the main panel due to the quantity of circuits, and another vue in a subpanel that is feed by the main panel.

So I’m trying to decide on a naming structure for the system that can be adapted to your naming methods.