[GUIDE] Australian Electricity Demand tariffs (e.g AGL)

Hi all, I am sharing my working configuration for handling demand tariffs which seem fairly unique to Australia and also fairly new to us. I suspect more and more people will find themselves on it over the next few years.

There is a lot to read here, but i’ve also put it all together in one package at the end of this post.

Assumptions:

  • You are on a demand tariff (which requires a smart meter)
  • You have working electricity usage sensors
    • sensor.electricity_imported_energy_kwh - a sensor providing a running total of energy imported in kWh
    • sensor.electricity_exported_energy_kwh - a sensor providing a running total of energy exported in kWh
      • If you do not get paid to export electricity, ignore export sensors and adjust as required.

What is a Demand Tariff?

  • A demand tariff is an additional charge on top of the standard usage rates.
    • My personal plan has a daily supply charge and usage is broken into peak, shoulder and off peak rates depending on the time and day of the week.
    • Demand rates are then extra charges on top, again dependant on the time and day.
    • Demand usage is only considered on work days, so weekends and public holidays are not included (for some of it).
  • Demand charges are a single variable charge applied per calendar month.
  • Demand rates are broken into high and low season rates.
    • Low season rates apply in April, May, September and October.
    • All other months are considered high season.
  • Demand is only applicable during certain peak hours. For this guide I’ll base it off AGLs demand windows which are:
    • High-season Demand - From 2 pm to 8 pm on working weekdays during 1 November to 31 March (inclusive) – the ‘summer months’. From 5 pm to 9 pm on working weekdays during 1 June to 31 August (inclusive) – the ‘winter months’.
    • Low-season Demand - From 2 pm to 8 pm on working weekdays during 1 April to 31 May and 1 September to 31 October (inclusive) – the non-summer and non-winter months

How is the demand tariff calculated?

The TLDR is that the highest 30 minutes of demand usage during the demand windows for the month, in kW, is charged at the applicable demand rate on all days in the month.

What the heck?

It took me a while to get my head around it at first too. Let me explain.

Determine the highest 30 minutes of imported electricity (during a demand period) within the whole month. It might occur on the 1st, 13th or 31st of the month. You generally will have a value in kWh reported by your smart meter or other usage source. Let’s say it is 1.5kWh.

Now we need to convert this to kW. I’ve done the hard maths for you and all you need to do is double it, as in order to use 1.5kW in 30 minutes we must have been importing an average of 3kW during that time. So 3kW.

Multiply by the number of days in the month. 3kW * 31 = 93kW

Multiply by the demand rate. 93 * 0.11726 = $10.91

Got it?

The Config

I will not be sharing the setup of my usage sensors as they will be fairly unique to everyone. I will hopefully use clearly identifiable names for the below configuration so you can edit them to your matching sensors as required.

So let’s make this work in Home Assistant.

There are a couple of extra sensors we need to create in order to make this work. I’ve had these configured for a while and there may be better ways to do it now, but they work. Happy for improvements to be suggested.

binary_sensor:
  - platform: workday
    name: Workday Sensor
    country: AU
    province: NSW
    remove_holidays:
      - "Bank Holiday"
sensor:
  - platform: time_date
    display_options:
      - "date"
template:
  - sensor:
    - name: Days Remaining in Month
      unique_id: days_remaining_in_month
      state: >
        {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set last = this.replace(year=this.year + 1, month=1, day=1) if now().month == 12 else this.replace(month=next, day=1) %}
          {{ (last.date() - this.date()).days - 1 }}

Now, we need to setup some utility meters to break our electricity usage up into the relevant time periods. The first utility meter captures the standard usage broken into peak, shoulder and off peak.

The second meter breaks the has the same source sensor, but looks at usage in 30 minute increments. I’ll explain more later.

The third meter gives us our daily exported kWh. Unlike imported electricity it has no tariffs because I have a flat export rate.

utility_meter:
  electricity_imported_energy_daily:
    source: sensor.electricity_imported_energy_kwh
    name: Electricity Imported Energy Daily
    cycle: daily
    tariffs:
      - peak
      - shoulder
      - off-peak

  electricity_imported_demand:
    source: sensor.electricity_imported_energy_kwh
    name: Electricity Imported Demand
    cron: 0,30 * * * *
    tariffs:
      - high-demand
      - low-demand
      - no-demand

  electricity_exported_energy_daily:
    source: sensor.electricity_exported_energy_kwh
    name: Electricity Exported Energy Daily
    cycle: daily

Automating the tariff changes

Now we create some automations to change the utility meter to the appropriate tariff at the right time.

This one is for normal usage (peak, shoulder, off peak) and changes all of my relevant utility meters at once. The time triggers are relevant to when My plan changes it’s rates. Use as an example.

alias: Set Electricity Tariff
description: ''
trigger:
  - platform: time
    at: '07:00:00'
  - platform: time
    at: '14:00:00'
  - platform: time
    at: '20:00:00'
  - platform: time
    at: '22:00:00'
  - platform: homeassistant
    event: start
condition: []
action:
  - service: select.select_option
    data:
      option: >-
        {% set t = now() %}  {%- if t.hour >=14 and t.hour <20 and
        is_state('binary_sensor.workday_sensor', 'on') %}
          peak
        {%- elif t.hour >= 22 or t.hour < 7 -%}
          off-peak
        {%- else -%}
          shoulder
        {%- endif -%}
    target:
      entity_id:
        - select.electricity_imported_energy_daily
mode: single

This one sets the demand tariff.

Triggers are time based for all times when my demand period could start or end, as well as a homeassistant boot.

I could place the workday sensor as a condition but chose not to in case something went haywire and needed to be corrected on a weekend.

The action is fairly simple, i had to create a template that captured the possible demand windows and output the correct demand tariff name.

The no-demand period is used for all electricity usage that is not subject to demand rates.

alias: Set Electricity Demand Tariff
description: ''
trigger:
  - platform: time
    at: '14:00:00'
  - platform: time
    at: '17:00:00'
  - platform: time
    at: '20:00:00'
  - platform: time
    at: '21:00:00'
  - platform: homeassistant
    event: start
condition: []
action:
  - service: select.select_option
    data:
      option: >-
        {% set t = now() %}   {% if is_state('binary_sensor.workday_sensor', 'on') %}
          {%- if ( t.hour >=14 and t.hour <20 and t.month in [11,12,1,2,3] ) or ( t.hour >=17 and t.hour <21 and t.month in [6,7,8] ) -%}
            high-demand
          {%- elif t.hour >= 14 and t.hour < 20 and t.month in [4,5,9,10] -%}
            low-demand
          {%- else -%}
            no-demand
          {%- endif -%}
        {%- else -%}
          no-demand
        {%- endif -%}
    target:
      entity_id:
        - select.electricity_imported_demand
    enabled: true
mode: single

Defining rates/tariffs

The tariff values could be set up a few different ways; as sensors, input numbers or as values directly in calculations in subsequent template sensors. The latter means a little more editing should the prices change in the future. I went with sensors.

Note: My peak, shoulder and offpeak rates are actually the same, but may not always be, so i gave myself the flexibility of changing them easily in the future by separating them now.

template:
  - sensor:
      - name: Electricity Import Rate High Demand
        unique_id: electricity_import_rate_high_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.23452"

      - name: Electricity Import Rate Low Demand
        unique_id: electricity_import_rate_low_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.11726"

      - name: Electricity Import Rate Peak
        unique_id: electricity_import_rate_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.13156"

      - name: Electricity Import Rate Shoulder
        unique_id: electricity_import_rate_shoulder
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.13156"

      - name: Electricity Import Rate Off Peak
        unique_id: electricity_import_rate_off_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.13156"

      - name: Electricity Export Rate
        unique_id: electricity_export_rate
        icon: mdi:cash-plus
        unit_of_measurement: $/kWh
        state: "0.05000"

      - name: Electricity Supply Charge
        unique_id: electricity_supply_charge
        icon: mdi:cash-plus
        unit_of_measurement: $/day
        state: "1.03235"

Storing the Max demand usage.

To store the current 30 minute usage I created an input number. Ensure you set the max to an appropriately high number. Better to be higher than not high enough.

input_number:
  electricity_demand_max:
    name: Electricity Demand Max Value
    # initial: 0 # don't use initial as it will reset upon restart
    min: 0
    max: 20
    step: 0.001

This input number is set using an automation, but before I show that I need to point out that this will only give us enough data for calculating the current month. If we want to be able to track the costs over a whole year we need to store all 12 different demand values. I chose to use a utility meter for this over 12 different input values.

Utility meters don’t take input numbers as sources so I made a template sensor to duplicate the input number above.

template:
  - sensor:
      - name: "Electricity Demand Max"
        unique_id: electricity_demand_max_sensor
        unit_of_measurement: kWh
        state: >
          {{ states('input_number.electricity_demand_max') |float(0) }}

Now that sensor can be the source for our utility meter.

utility_meter:
  electricity_demand_max_monthly:
    source: sensor.electricity_demand_max
    name: Electricity Demand Max Monthly
    cycle: yearly
    tariffs:
      - 1
      - 2
      - 3
      - 4
      - 5
      - 6
      - 7
      - 8
      - 9
      - 10
      - 11
      - 12

Automations for storing the Max Demand Usage

This automation updates the input number whenever the current 30 minute usage exceeds the stored maximum. Effectively increases in sync with the demand whenever the demand usage is peaking.

alias: Store max demand usage
description: Calculate and store max 30 minute demand in input_value to survive a reboot
trigger:
  - platform: state
    entity_id:
      - sensor.electricity_imported_demand_high_demand
      - sensor.electricity_imported_demand_low_demand
condition:
  - condition: template
    value_template: >-
      {{ (trigger.to_state.state | float(0)) >
      (states('input_number.electricity_demand_max') | float()) }}
action:
  - service: input_number.set_value
    data:
      value: '{{ trigger.to_state.state | float() | round(3) }}'
    target:
      entity_id:
        - input_number.electricity_demand_max
mode: single

I use this automation to change the utility meter to the current month and reset the recorded max at the start of each month. The max value for the previous month survives this reset within the utility meter.


alias: Set Monthly max demand and reset Stored Max Value
description: ''
trigger:
  - platform: time
    at: '00:00:00'
  - platform: homeassistant
    event: start
condition:
  - condition: template
    value_template: '{{ now().day == 1 }}'
action:
  - service: select.select_option
    data:
      option: '{{ now().month }}'
    target:
      entity_id:
        - select.electricity_demand_max_monthly
  - service: input_number.set_value
    data:
      value: 0
    target:
      entity_id:
        - input_number.electricity_demand_max
mode: single

Calculating the Totals

Finally we pull it all together into a template sensor that gives us the actual costs.

You have two options below. One that just shows the import costs (good for the energy dashboard) and one for the total cost (in my case that includes solar feed in )

template:
  - sensor
      - name: Total Daily Import Cost
        icon: mdi:currency-usd
        state_class: total_increasing
        device_class: monetary
        unit_of_measurement: $
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_energy_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_energy_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_energy_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {{ (supply + offpeak + shoulder + peak) | round(2) }}

      - name: "Electricity Total Cost Daily"
        unique_id: electricity_total_cost_daily
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_energy_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_energy_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_energy_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {% set feedintariff = states('sensor.electricity_exported_energy_daily') | float(0) * states('sensor.electricity_export_rate') | float(0) %}
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {% set demand = states('input_number.electricity_demand_max') | float (0) * 2 * demandrate %}
          {{ (supply + offpeak + shoulder + peak - feedintariff) | round(2) }}

On the energy dashboard you can use the first sensor to give you the total import cost

image

Here are a couple more sensors that you might be interested in. A sensor that just calculates the additional cost due to the demand. Also one for the total electricity over the whole year using the 12 max values stored in the utility meter we made earlier.

You’ll need to scroll right to see the full year calculation.

Note that in order to use the yearly calculation you will need to create additional utility meters for the import and export usage that cycle yearly.

template:
  - sensor:
      - name: "Electricity Demand Monthly Cost"
        unique_id: electricity_demand_monthly_cost
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {{states('input_number.electricity_demand_max') | float (0) * 2 * demandrate * ( now().day + states('sensor.days_remaining_in_month') | int(0) ) }}

     - name: "Electricity Total Cost Yearly"
        unique_id: electricity_total_cost_yearly
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) * ( states('sensor.days_passed_in_year') | int(0) + 1 ) %}
          {% set offpeak = states('sensor.electricity_imported_energy_yearly_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_energy_yearly_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_energy_yearly_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {% set feedintariff = states('sensor.electricity_exported_energy_yearly') | float(0) * states('sensor.electricity_export_rate') | float(0) %}
          {% set highdemandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% set lowdemandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% set demand = ( states('sensor.electricity_demand_max_monthly_1')| float (0) * 31 + states('sensor.electricity_demand_max_monthly_2') | float (0) * 28 + states('sensor.electricity_demand_max_monthly_3') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_6') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_7') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_8') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_11') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_12') | float (0) * 31 ) * 2 * highdemandrate + ( states('sensor.electricity_demand_max_monthly_4') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_5') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_9') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_10') | float (0) * 31 ) * 2 * lowdemandrate %}
          {{ (supply + offpeak + shoulder + peak + demand - feedintariff) | round(2) }}

What else do I do with the data

I have a conditional card that shows up whenever we are in the demand windows. It is a graph with two entities, the current max demand usage for the month and the current 30 minute usage. Gives me a live picture of how close I am to busting the current max demand.

Here you can see I went just over during tonight’s demand window. This demonstrates how the maximum demand usage for the month is updated and stored using the automations above. It went from 1.11 to 1.2 during the third 30 minute window and now the max stays at 1.2 until exceeded again, or reset at the end of the month.

image

I have several gauge cards which give me a snap shot of the usage for the current month and year.

The first gauge is the highest 30 minute demand usage for the month. The second gauge is how much that usage has cost me. The third and fourth gauges are the total monthly and yearly cost so far including demand.

image

I have a graph just for the last 10 daily charges so i can pick up on any trends relatively quickly. These do not include demand (though easily could if you wanted to).

image

Complete Package Template

I have all of the above code included as a single file package in home assistant.

in configuration.yaml

homeassistant:
  packages: !include_dir_named packages

Then in [root HA config directory]/packages/energy_costs.yaml

### This package assumes you have an import sensor "sensor.electricity_imported_energy_kwh" 
### which provides cumulative electricity imported in kWh 
### and an export sensor sensor.electricity_exported_energy_kwh 
### which provides cumulative electricity exported in kWh. 
### If you use other sensor names, adjust everything below as required to match. 

binary_sensor:
  - platform: workday
    name: Workday Sensor
    country: AU
    ### your state code
    province: NSW 
    remove_holidays:
      - "Bank Holiday"

sensor:
  - platform: time_date
    display_options:
      - "date"

template:
  - sensor:
    - name: Days Remaining in Month
      unique_id: days_remaining_in_month
      state: >
          {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set last = this.replace(year=this.year + 1, month=1, day=1) if now().month == 12 else this.replace(month=next, day=1) %}
          {{ (last.date() - this.date()).days - 1 }}

      - name: Electricity Import Rate High Demand
        unique_id: electricity_import_rate_high_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.23452"
        ### edit to your high demand import rate

      - name: Electricity Import Rate Low Demand
        unique_id: electricity_import_rate_low_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.11726"
        ### edit to your low demand import rate

      - name: Electricity Import Rate Peak
        unique_id: electricity_import_rate_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.13156"
        ### edit to your peak import rate

      - name: Electricity Import Rate Shoulder
        unique_id: electricity_import_rate_shoulder
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.13156"
        ### edit to your shoulder import rate

      - name: Electricity Import Rate Off Peak
        unique_id: electricity_import_rate_off_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: "0.13156"
        ### edit to your off peak import rate

      - name: Electricity Export Rate
        unique_id: electricity_export_rate
        icon: mdi:cash-plus
        unit_of_measurement: $/kWh
        state: "0.05000"
        ### edit to your solar feed-in-tariff /export rate

      - name: Electricity Supply Charge
        unique_id: electricity_supply_charge
        icon: mdi:cash-plus
        unit_of_measurement: $/day
        state: "1.03235"
        ### edit to your daily supply charge

      - name: "Electricity Demand Max"
        unique_id: electricity_demand_max_sensor
        unit_of_measurement: kWh
        state: >
          {{ states('input_number.electricity_demand_max') |float(0) }}

      - name: Total Daily Import Cost
        icon: mdi:currency-usd
        state_class: total_increasing
        device_class: monetary
        unit_of_measurement: $
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_energy_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_energy_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_energy_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {{ (supply + offpeak + shoulder + peak) | round(2) }}

      - name: "Electricity Demand Monthly Cost"
        unique_id: electricity_demand_monthly_cost
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {{states('input_number.electricity_demand_max') | float (0) * 2 * demandrate * ( now().day + states('sensor.days_remaining_in_month') | int(0) ) }}

      - name: "Electricity Total Cost Daily"
        unique_id: electricity_total_cost_daily
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_energy_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_energy_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_energy_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {% set feedintariff = states('sensor.electricity_exported_energy_daily') | float(0) * states('sensor.electricity_export_rate') | float(0) %}
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {% set demand = states('input_number.electricity_demand_max') | float (0) * 2 * demandrate %}
          {{ (supply + offpeak + shoulder + peak - feedintariff) | round(2) }}

input_number:
  electricity_demand_max:
    name: Electricity Demand Max Value
    # initial: 0 # don't use initial as it will reset upon restart
    min: 0
    max: 20
    step: 0.001

utility_meter:
  electricity_imported_energy_daily:
    source: sensor.electricity_imported_energy_kwh
    name: Electricity Imported Energy Daily
    cycle: daily
    tariffs:
      - peak
      - shoulder
      - off-peak

  electricity_imported_demand:
    source: sensor.electricity_imported_energy_kwh
    name: Electricity Imported Demand
    cron: 0,30 * * * *
    tariffs:
      - high-demand
      - low-demand
      - no-demand

  electricity_exported_energy_daily:
    source: sensor.electricity_exported_energy_kwh
    name: Electricity Exported Energy Daily
    cycle: daily

  electricity_demand_max_monthly:
    source: sensor.electricity_demand_max
    name: Electricity Demand Max Monthly
    cycle: yearly
    tariffs:
      - 1
      - 2
      - 3
      - 4
      - 5
      - 6
      - 7
      - 8
      - 9
      - 10
      - 11
      - 12

automation:
### adjust the time triggers below to match when your standard electricity tariff changes (peak, shoulder, offpeak). Adjust the template in the action block as required to match your plan.
  - alias: Set Electricity Tariff
    description: ''
    trigger:
      - platform: time
        at: '07:00:00'
      - platform: time
        at: '14:00:00'
      - platform: time
        at: '20:00:00'
      - platform: time
        at: '22:00:00'
      - platform: homeassistant
        event: start
    condition: []
    action:
      - service: select.select_option
        data:
          option: >-
            {% set t = now() %}  {%- if t.hour >=14 and t.hour <20 and
            is_state('binary_sensor.workday_sensor', 'on') %}
              peak
            {%- elif t.hour >= 22 or t.hour < 7 -%}
              off-peak
            {%- else -%}
              shoulder
            {%- endif -%}
        target:
          entity_id:
            - select.electricity_imported_energy_daily
    mode: single
 
### adjust the time triggers below to match when your demand electricity tariff changes. Adjust the template in the action block as required to match your plan.
  - alias: Set Electricity Demand Tariff
    description: ''
    trigger:
      - platform: time
        at: '14:00:00'
      - platform: time
        at: '17:00:00'
      - platform: time
        at: '20:00:00'
      - platform: time
        at: '21:00:00'
      - platform: homeassistant
        event: start
    condition: []
    action:
      - service: select.select_option
        data:
          option: >-
            {% set t = now() %}   {% if is_state('binary_sensor.workday_sensor',
            'on') %}
              {%- if ( t.hour >=14 and t.hour <20 and t.month in [11,12,1,2,3] ) or ( t.hour >=17 and t.hour <21 and t.month in [6,7,8] ) -%}
                high-demand
              {%- elif t.hour >= 14 and t.hour < 20 and t.month in [4,5,9,10] -%}
                low-demand
              {%- else -%}
                no-demand
              {%- endif -%}
            {%- else -%}
              no-demand
            {%- endif -%}
        target:
          entity_id:
            - select.electricity_imported_demand
        enabled: true
    mode: single
    
  - alias: Store max demand usage
    description: Calculate and store max 30 minute demand in input_value to survive a reboot
    trigger:
      - platform: state
        entity_id:
          - sensor.electricity_imported_demand_high_demand
          - sensor.electricity_imported_demand_low_demand
    condition:
      - condition: template
        value_template: >-
          {{ (trigger.to_state.state | float(0)) >
          (states('input_number.electricity_demand_max') | float()) }}
    action:
      - service: input_number.set_value
        data:
          value: '{{ trigger.to_state.state | float() | round(3) }}'
        target:
          entity_id:
            - input_number.electricity_demand_max
    mode: single
    
  - alias: Set Monthly max demand and reset Stored Max Value
    description: ''
    trigger:
      - platform: time
        at: '00:00:00'
      - platform: homeassistant
        event: start
    condition:
      - condition: template
        value_template: '{{ now().day == 1 }}'
    action:
      - service: select.select_option
        data:
          option: '{{ now().month }}'
        target:
          entity_id:
            - select.electricity_demand_max_monthly
      - service: input_number.set_value
        data:
          value: 0
        target:
          entity_id:
            - input_number.electricity_demand_max
    mode: single
16 Likes

Hi Sgt Batten, for NOOB to HA, is there any chance you might be able to either detail into which yaml file each of the above pieces of code should be placed? Or else share a copy of the different yaml files you have? TIA Justin

I wasn’t sure how best to present it to be honest and the reason for that is because the answer to your question is “it doesn’t actually matter”.

In my personal case I have two files, energy.yaml and energy_costs.yaml which are included as packages. Almost everything above is from my energy_costs file. The one caveat I have is that in order to present the information more clearly above I made sure every code block had the domain at the start i.e template or utility meter. When in reality you cannot have these more than once in a single package. If you were to copy everything from the post above (except the automations, i’ll get to that) and put it in a single file, you would need to group all the items of the same type like follows.

In individual files these are fine:

template:
  - sensor:
      - name: "Electricity Demand Max"
        unique_id: electricity_demand_max_sensor
        unit_of_measurement: kWh
        state: >
          {{ states('input_number.electricity_demand_max') |float(0) }}
template:
  - sensor:
    - name: Days Remaining in Month
      unique_id: days_remaining_in_month
      state: >
        {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
        {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
        {% set last = this.replace(month=next, day=1) %}
        {{ (last.date() - this.date()).days - 1 }}

But in my energy_costs.yaml file i would have to have them under the one domain like this:

template:
  - sensor:
      - name: "Electricity Demand Max"
        unique_id: electricity_demand_max_sensor
        unit_of_measurement: kWh
        state: >
          {{ states('input_number.electricity_demand_max') |float(0) }}

    - name: Days Remaining in Month
      unique_id: days_remaining_in_month
      state: >
        {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
        {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
        {% set last = this.replace(month=next, day=1) %}
        {{ (last.date() - this.date()).days - 1 }}

Hope that makes sense. Happy to help further.

Edi: oh and the automations i created in the UI so you could paste those into an automation you are editing in “yaml mode”

OK looks like the energy_costs.yaml should contain:

binary_sensor:
  - platform: workday
    name: Workday Sensor
    country: AU
    province: NSW

sensor:
  - platform: time_date
    display_options:
      - "date"

utility_meter:
  electricity_imported_power_daily:
    source: sensor.electricity_imported_power_kwh
    name: Electricity Imported Power Daily
    cycle: daily
    tariffs:
      - peak
      - shoulder
      - off-peak

  electricity_demand_max_monthly:
    source: sensor.electricity_demand_max
    name: Electricity Demand Max Monthly
    cycle: yearly
    tariffs:
      - 1
      - 2
      - 3
      - 4
      - 5
      - 6
      - 7
      - 8
      - 9
      - 10
      - 11
      - 12

  electricity_imported_demand:
    source: sensor.electricity_imported_power_kwh
    name: Electricity Imported Demand
    cron: 0,30 * * * *
    tariffs:
      - high-demand
      - low-demand
      - no-demand


template:
  - sensor:
      - name: "Electricity Demand Max"
        unique_id: electricity_demand_max_sensor
        unit_of_measurement: kWh
        state: >
          {{ states('input_number.electricity_demand_max') |float(0) }}

      - name: Days Remaining in Month
        unique_id: days_remaining_in_month
        state: >
          {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set last = this.replace(month=next, day=1) %}
          {{ (last.date() - this.date()).days - 1 }}

      - name: Total Daily Import Cost
        icon: mdi:currency-usd
        state_class: total_increasing
        device_class: monetary
        unit_of_measurement: $
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_power_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_power_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_power_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {{ (supply + offpeak + shoulder + peak) | round(2) }}

      - name: "Electricity Total Cost Daily"
        unique_id: electricity_total_cost_daily
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_power_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_power_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_power_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {% set feedintariff = states('sensor.electricity_exported_power_daily') | float(0) * states('sensor.electricity_export_rate') | float(0) %}
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {% set demand = states('input_number.electricity_demand_max') | float (0) * 2 * demandrate %}
          {{ (supply + offpeak + shoulder + peak - feedintariff) | round(2) }}


      - name: "Electricity Demand Monthly Cost"
        unique_id: electricity_demand_monthly_cost
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {{states('input_number.electricity_demand_max') | float (0) * 2 * demandrate * ( now().day + states('sensor.days_remaining_in_month') | int(0) ) }}


      - name: "Electricity Total Cost Yearly"
        unique_id: electricity_total_cost_yearly
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) * ( states('sensor.days_passed_in_year') | int(0) + 1 ) %}
          {% set offpeak = states('sensor.electricity_imported_power_yearly_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_power_yearly_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_power_yearly_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {% set feedintariff = states('sensor.electricity_exported_power_yearly') | float(0) * states('sensor.electricity_export_rate') | float(0) %}
          {% set highdemandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% set lowdemandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% set demand = ( states('sensor.electricity_demand_max_monthly_1')| float (0) * 31 + states('sensor.electricity_demand_max_monthly_2') | float (0) * 28 + states('sensor.electricity_demand_max_monthly_3') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_6') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_7') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_8') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_11') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_12') | float (0) * 31 ) * 2 * highdemandrate + ( states('sensor.electricity_demand_max_monthly_4') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_5') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_9') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_10') | float (0) * 31 ) * 2 * lowdemandrate %} 
          {{ (supply + offpeak + shoulder + peak + demand - feedintariff) | round(2) }}


      - name: Electricity Import Rate High Demand
        unique_id: electricity_import_rate_high_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.23452

      - name: Electricity Import Rate Low Demand
        unique_id: electricity_import_rate_low_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.11726

      - name: Electricity Import Rate Peak
        unique_id: electricity_import_rate_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.13156

      - name: Electricity Import Rate Shoulder
        unique_id: electricity_import_rate_shoulder
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.13156

      - name: Electricity Import Rate Off Peak
        unique_id: electricity_import_rate_off_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.13156

      - name: Electricity Export Rate
        unique_id: electricity_export_rate
        icon: mdi:cash-plus
        unit_of_measurement: $/kWh
        state: 0.05000

      - name: Electricity Supply Charge
        unique_id: electricity_supply_charge
        icon: mdi:cash-plus
        unit_of_measurement: $/day
        state: 1.03235

input_number:
  electricity_demand_max:
    name: Electricity Demand Max Value
    # initial: 0 # don't use initial as it will reset upon restart
    min: 0
    max: 20
    step: 0.001

The energy.yaml still trying to work it out. Also do the ‘automations’ go into the energy.yaml or in the default automations.yaml ?

Yep, at a glance looks like you understood my explanation above.

Do you understand what I mean by packages?
I have these yaml files in a folder called packages which is then included in configuration.yaml using

package: !include_dir_named packages

As I said I use the UI for automations which means they exist in automations.yaml.
You could add them in energy_costs.yaml also if given a proper automations domain before the first one.

I would add them using the UI. Copy one from above. Go to settings > automations and create new blank automation. Click the three dots top right and select edit in yaml. Paste the yaml there.

Regarding energy.yaml. this will be unique to everyone because it depends how you provide HA with electricity usage information.

For all the sensors above I tried to use generic names so that you might be able to understand what the equivalent sensor might be for you. Or maybe you need to make some by combining other sensors.

Yes I’ve got that /config/packages previously setup, from adding SolarAnalytics (below ‘S/A’) into the system.

Just having fun working out what the sensors/entities in my setup are for:

- select.electricity_solar_panel_production_daily	(S/A = sensor.sa_todays_energy_generated_total
- select.electricity_house_consumption_daily	    (S/A = sensor.sa_todays_energy_consumed_total)
- select.electricity_imported_power_daily	        (S/A = sensor.sa_todays_energy_imported)
- select.electricity_solar_panel_production_monthly
- select.electricity_house_consumption_monthly
- select.electricity_imported_power_monthly
- select.electricity_solar_panel_production_yearly
- select.electricity_house_consumption_yearly
- select.electricity_imported_power_yearly

Apart from SolarAnalytics, I also have IoTaWatt installed, that gives me:

Exported back to the Grid
sensor.grid_export_wh
sensor.grid_export_wh_accumulated

Imported from the Grid
sensor.grid_import_wh
sensor.grid_import_wh_accumulated

Solar Produced
sensor.solar_wh
sensor.solar_wh_accumulated

Power Consumed (grid imported and solar consumed locally)
sensor.consume_wh
sensor.consume_wh_accumulated

Solar Consumed Locally
sensor.cons_self_wh
sensor.cons_self_wh_accumulated

From all the above, nothing stands out and being applicable for using as the monthly or yearly feeds?
Can I ask what your using to pull in your data feeds from, I’m wondering if you really need access into the inverter (or its central data logger if it has one for multiple inverters) as I assume they’d track monthly and yearly data?

That list of selects has more than absolutely necesary. The monthly and yearly things are just duplicate utility Meters that cycle at those rates, monthly or yearly.

When it comes down to it all you need are sensors for import and export in kWh. So you would need to divide your Wh sensors by 1000 in a new template sensor.

Then you step through this post and create utility meters that have the necessary tariffs and cycles for use in the calculations in the OP.

OK this is a daft question… but could you confirm the format of the lines that anyone needs to change, to reflect their setup (i.e. their data feeds). I’m feeling really daft asking :frowning:

My understanding is it’s the lines starting with :

- select.electricity

that end up residing in the automations.yaml file, or is it somewhere else? Just confused with your last reply, that seems to say these lines shouldn’t be touched.

I’m not sure what the difference is between your _wh and _wh_accumulated sensors.

For comparison though I have a sensor called sensor.electricity_imported_power_kwh and sensor.electricity_exported_power_kwh which are the total lifetime import and export reported by my inverter.

If you were to make two template sensors from your own sensors posted above converting them from WH to kWh you would be at an identical position.

The 4th code block in my OP is the first utility meter. You can see it takes the sensor.electricity_imported_power_kwh as it’s source and cycles daily.

When you create a utility Meyer with tariffs it automatically makes several sensors. On my example from the OP it creates
sensor.electricity_imported_power_daily_off_peak
sensor.electricity_imported_power_daily_peak
sensor.electricity_imported_power_daily_shoulder

And also select.electricity_imported_power_daily
This select entity is the one that is used to tell the utility meter which tariff it should record usage against.

Maybe I have confused things in the OP by referring to so many select entities when the rest of the post only ever creates one of those.

OK so you have an integration for your inverter, that creates its own sensors called.

sensor.electricity_imported_power_kwh
sensor.electricity_exported_power_kwh

Just checking through and you have defined ''sensor.electricity_imported_power_kwh" as a source in the utility meter, in these two sections.

utility_meter:
  electricity_imported_power_daily:
    source: sensor.electricity_imported_power_kwh  #Comment: Replace this sensor with your own sensor that provides kWh your importing from the grid
    name: Electricity Imported Power Daily
    cycle: daily
    tariffs:
      - peak
      - shoulder
      - off-peak

and

  electricity_imported_demand:
    source: sensor.electricity_imported_power_kwh  #Comment: Replace this sensor with your own sensor that provides kWh your importing from the grid
    name: Electricity Imported Demand
    cron: 0,30 * * * *
    tariffs:
      - high-demand
      - low-demand
      - no-demand

but I note that there’s nowhere that you have ‘sensor.electricity_exported_power_kwh’ used as a source. Should this be defined, or is it missing (from above code windows) ?

Whilst the code windows in the OP are great, working through explaining what they are doing. It would probably be easiest, when posting to the community, to then have a code window that contains the entire code for each file in your add-on.
The within that window, next to your code that everyone should change the data source, to specify their own local version of this source, just add “#Comment - Replace this with …”.

Whilst using the /config/packages directory and placing your code there, but then adding the automation ‘via the web GUI’ (that then stores that code in /config/automations.yaml file), you could wrap this into a /config/packages/energy_costs.yaml file with:

binary_sensor:
  - platform: workday
    name: Weekday Sensor
    country: AU
    province: NSW

sensor:
  - platform: time_date
    display_options:
      - "date"

utility_meter:
  electricity_imported_power_daily:
    source: sensor.electricity_imported_power_kwh  #Comment: Replace this sensor with your own sensor that provides kWh your importing from the grid
    name: Electricity Imported Power Daily
    cycle: daily
    tariffs:
      - peak
      - shoulder
      - off-peak

  electricity_demand_max_monthly:
    source: sensor.electricity_demand_max
    name: Electricity Demand Max Monthly
    cycle: yearly
    tariffs:
      - 1
      - 2
      - 3
      - 4
      - 5
      - 6
      - 7
      - 8
      - 9
      - 10
      - 11
      - 12

  electricity_imported_demand:
    source: sensor.electricity_imported_power_kwh  #Comment: Replace this sensor with your own sensor that provides kWh your importing from the grid
    name: Electricity Imported Demand
    cron: 0,30 * * * *
    tariffs:
      - high-demand
      - low-demand
      - no-demand

template:
  - sensor:
      - name: "Electricity Demand Max"
        unique_id: electricity_demand_max_sensor
        unit_of_measurement: kWh
        state: >
          {{ states('input_number.electricity_demand_max') |float(0) }}

      - name: Days Remaining in Month
        unique_id: days_remaining_in_month
        state: >
          {% set this = now().replace(hour=0).replace(minute=0).replace(second=0).replace(microsecond=0) %}
          {% set next = this.month + 1 if this.month + 1 <= 12 else 1 %}
          {% set last = this.replace(month=next, day=1) %}
          {{ (last.date() - this.date()).days - 1 }}

      - name: Total Daily Import Cost
        icon: mdi:currency-usd
        state_class: total_increasing
        device_class: monetary
        unit_of_measurement: $
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_power_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_power_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_power_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {{ (supply + offpeak + shoulder + peak) | round(2) }}

      - name: "Electricity Total Cost Daily"
        unique_id: electricity_total_cost_daily
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) %}
          {% set offpeak = states('sensor.electricity_imported_power_daily_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_power_daily_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_power_daily_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {% set feedintariff = states('sensor.electricity_exported_power_daily') | float(0) * states('sensor.electricity_export_rate') | float(0) %}
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {% set demand = states('input_number.electricity_demand_max') | float (0) * 2 * demandrate %}
          {{ (supply + offpeak + shoulder + peak - feedintariff) | round(2) }}


      - name: "Electricity Demand Monthly Cost"
        unique_id: electricity_demand_monthly_cost
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set t = now() %}
          {% if t.month in [4,5,9,10] %}
            {% set demandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% else %}
            {% set demandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% endif %}
          {{states('input_number.electricity_demand_max') | float (0) * 2 * demandrate * ( now().day + states('sensor.days_remaining_in_month') | int(0) ) }}


      - name: "Electricity Total Cost Yearly"
        unique_id: electricity_total_cost_yearly
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.electricity_supply_charge') | float(0) * ( states('sensor.days_passed_in_year') | int(0) + 1 ) %}
          {% set offpeak = states('sensor.electricity_imported_power_yearly_off_peak') | float(0) * states('sensor.electricity_import_rate_off_peak') | float(0) %}
          {% set shoulder = states('sensor.electricity_imported_power_yearly_shoulder') | float(0) * states('sensor.electricity_import_rate_shoulder') | float(0) %}
          {% set peak = states('sensor.electricity_imported_power_yearly_peak') | float(0) * states('sensor.electricity_import_rate_peak') | float(0) %}
          {% set feedintariff = states('sensor.electricity_exported_power_yearly') | float(0) * states('sensor.electricity_export_rate') | float(0) %}
          {% set highdemandrate = states('sensor.electricity_import_rate_high_demand') | float(0) %}
          {% set lowdemandrate = states('sensor.electricity_import_rate_low_demand') | float(0) %}
          {% set demand = ( states('sensor.electricity_demand_max_monthly_1')| float (0) * 31 + states('sensor.electricity_demand_max_monthly_2') | float (0) * 28 + states('sensor.electricity_demand_max_monthly_3') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_6') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_7') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_8') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_11') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_12') | float (0) * 31 ) * 2 * highdemandrate + ( states('sensor.electricity_demand_max_monthly_4') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_5') | float (0) * 31 + states('sensor.electricity_demand_max_monthly_9') | float (0) * 30 + states('sensor.electricity_demand_max_monthly_10') | float (0) * 31 ) * 2 * lowdemandrate %} 
          {{ (supply + offpeak + shoulder + peak + demand - feedintariff) | round(2) }}


      - name: Electricity Import Rate High Demand
        unique_id: electricity_import_rate_high_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.15400

      - name: Electricity Import Rate Low Demand
        unique_id: electricity_import_rate_low_demand
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.15400

      - name: Electricity Import Rate Peak
        unique_id: electricity_import_rate_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.23100

      - name: Electricity Import Rate Shoulder
        unique_id: electricity_import_rate_shoulder
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.18700

      - name: Electricity Import Rate Off Peak
        unique_id: electricity_import_rate_off_peak
        icon: mdi:cash-minus
        unit_of_measurement: $/kWh
        state: 0.13200

      - name: Electricity Export Rate
        unique_id: electricity_export_rate
        icon: mdi:cash-plus
        unit_of_measurement: $/kWh
        state: 0.06000

      - name: Electricity Supply Charge
        unique_id: electricity_supply_charge
        icon: mdi:cash-plus
        unit_of_measurement: $/day
        state: 1.4300

input_number:
  electricity_demand_max:
    name: Electricity Demand Max Value
    # initial: 0 # don't use initial as it will reset upon restart
    min: 0
    max: 20
    step: 0.001

and the /config/automations.yaml file, with:

alias: Set Electricity Tariff
description: 'Set the daily peak, shoulder and off-peak periods'
trigger:
  - platform: time
    at: '07:00:00'
  - platform: time
    at: '17:00:00'
  - platform: time
    at: '20:00:00'
  - platform: time
    at: '22:00:00'
  - platform: homeassistant
    event: start
condition: []
action:
  - service: select.select_option
    data:
      option: >-
        {% set t = now() %}  {%- if t.hour >=17 and t.hour <20 and
        is_state('binary_sensor.workday_sensor', 'on') %}
          peak
        {%- elif t.hour >= 22 or t.hour < 7 -%}
          off-peak
        {%- else -%}
          shoulder
        {%- endif -%}
    target:
      entity_id:
        - select.electricity_solar_panel_production_daily
        - select.electricity_house_consumption_daily
        - select.electricity_imported_power_daily
        - select.electricity_solar_panel_production_monthly
        - select.electricity_house_consumption_monthly
        - select.electricity_imported_power_monthly
        - select.electricity_solar_panel_production_yearly
        - select.electricity_house_consumption_yearly
        - select.electricity_imported_power_yearly
mode: single

# ----------------------------------------------------------

alias: Set Electricity Demand Tariff
description: 'Set the Peak or Off Peak DEMAND months of the year'
trigger:
  - platform: time
    at: '14:00:00'
  - platform: time
    at: '17:00:00'
  - platform: time
    at: '20:00:00'
  - platform: time
    at: '21:00:00'
  - platform: homeassistant
    event: start
condition: []
action:
  - service: select.select_option
    data:
      option: >-
        {% set t = now() %}   {% if is_state('binary_sensor.workday_sensor',
        'on') %}
          {%- if ( t.hour >=17 and t.hour <20 and t.month in [11,12,1,2,3] ) or ( t.hour >=17 and t.hour <20 and t.month in [6,7,8] ) -%}
            high-demand
          {%- elif t.hour >= 17 and t.hour < 20 and t.month in [4,5,9,10] -%}
            low-demand
          {%- else -%}
            no-demand
          {%- endif -%}
        {%- else -%}
          no-demand
        {%- endif -%}
    target:
      entity_id:
        - select.electricity_imported_demand
    enabled: true
mode: single

# ----------------------------------------------------------

alias: Store max demand usage
description: Calculate and store max 30 minute demand in input_value to survive a reboot
trigger:
  - platform: state
    entity_id:
      - sensor.electricity_imported_demand_high_demand
      - sensor.electricity_imported_demand_low_demand
condition:
  - condition: template
    value_template: >-
      {{ (trigger.to_state.state | float(0)) >
      (states('input_number.electricity_demand_max') | float()) }}
action:
  - service: input_number.set_value
    data:
      value: '{{ trigger.to_state.state | float() | round(3) }}'
    target:
      entity_id:
        - input_number.electricity_demand_max
mode: single

# ----------------------------------------------------------

alias: Set Monthly max demand and reset Stored Max Value
description: ''
trigger:
  - platform: time
    at: '00:00:00'
  - platform: homeassistant
    event: start
condition:
  - condition: template
    value_template: '{{ now().day == 1 }}'
action:
  - service: select.select_option
    data:
      option: '{{ now().month }}'
    target:
      entity_id:
        - select.electricity_demand_max_monthly
  - service: input_number.set_value
    data:
      value: 0
    target:
      entity_id:
        - input_number.electricity_demand_max
mode: single

# ----------------------------------------------------------
Note: The billing periods / costs above are slightly modified from the original code, to fit into the Demand plan Essential Energy offers us, that is one demand type charge all year round and peak is only 5-8pm weekdays / shoulder 7am-5pm and 8-10pm.

This guide intended to focus on the demand config so yes I had neglected to mention the export value initially.

You will need a utility meter with a daily cycle for the daily cost calculation. A monthly cycle for the monthly calculation etc. No tariffs as you probably have a flat export rate.

Edit: I have added to the OP and also included a complete energy_cost.yaml example at the end of the OP.

I discovered today that in NSW it is a bank holiday however this is not a public holiday, therefore the workday sensor was incorrectly ignoring demand usage this evening. I have corrected this in the OP using a remove_holidays property.

For anyone else curious, here is a link to the current holidays repo on github: Python Holidays

Sorry just getting back to revisting this, and getting it going.

Great stuff ! Have started afresh and going from this /config/packages/energy_cost.yaml, and editing for NSW with additional 7am-9am peak hours and (currently) no demand fee (easiest to just use cost for these is $0.00, allowing their activation in future by just updating the price).

I note that you mention you also have an energy.yaml file, any chance you can post that, to allow modification and re-use?

Any chance you’ve already developed a tarrif tracker for gas and water in Oz ? :grinning:

Yep, good thinking. it’ll work for either.

I’m not going to post it but instead link you to where i sourced it from which I think is better, as it’s been updated and improved since i did mine. Solaredge Modbus Configuration for Single Inverter and Battery - Share your Projects! - Home Assistant Community (home-assistant.io)
My cost sensors and everything started with the work from the previous version of that post, i then split out my cost file as making it work with demand tariffs added a fair few lines and I just liked organising costs into it’s own package.

Yes, kinda.

Water is easy, although i have not figured out my personal daily supply charge yet so it’s only showing me usage on my dashboard for now. the cost is there tough.

template:
  - sensor:
      - name: Water Consumption
        unique_id: water_consumption
        state: "{{ states('counter.water_counter') | float(0) * 0.5 }}"
        unit_of_measurement: "L"
        icon: mdi:water-pump
        device_class: water
        state_class: total_increasing

      - name: Water Import Rate
        unique_id: water_import_rate
        icon: mdi:cash-minus
        unit_of_measurement: "AUD/L"
        state: "0.00239"
      - name: Water Supply Charge
        unique_id: water_supply_charge
        icon: mdi:cash-minus
        unit_of_measurement: "AUD/L"
        state: "0"

      - name: "Water Total Cost Daily"
        unique_id: water_total_cost_daily
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.water_supply_charge') | float(0) %}
          {% set usage = states('sensor.water_consumption_daily') | float(0) * states('sensor.water_import_rate') | float(0) %}
          {{ (supply + usage) | round(2) }}
      - name: "Water Total Cost Monthly"
        unique_id: water_total_cost_monthly
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.water_supply_charge') | float(0) * now().day %}
          {% set usage = states('sensor.water_consumption_monthly') | float(0) * states('sensor.water_import_rate') | float(0) %}
          {{ (supply + usage) | round(2) }}
      - name: "Water Total Cost Yearly"
        unique_id: water_total_cost_yearly
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.water_supply_charge') | float(0) * ( states('sensor.days_passed_in_year') | int(0) + 1 ) %}
          {% set usage = states('sensor.water_consumption_yearly') | float(0) * states('sensor.water_import_rate') | float(0) %}
          {{ (supply + usage) | round(2) }}

utility_meter:
  water_consumption_daily:
    source: sensor.water_consumption
    name: Water Consumption Daily
    cycle: daily
  water_consumption_monthly:
    source: sensor.water_consumption
    name: Water Consumption Monthly
    cycle: monthly
  water_consumption_yearly:
    source: sensor.water_consumption
    name: Water Consumption Yearly
    cycle: yearly

Currently my gas.yaml is identical to that. Gas for me is billed at 3 rates, first xxxxMJ, next yyyyMJ and the rest zzzzMJ, but the bill doesnt make sense because my total usage last bill was less than xxxx yet i was charged some gas at all three rates. Once i work that out i’ll implement the tiers. For now i just use the same as above with one extra conversion for converting between m3 and MJ.

template:
  - sensor:
      - name: Gas Consumption M3
        unique_id: gas_consumption_m3
        state: "{{ states('counter.gas_counter') | float(0) * 0.01 }}"
        unit_of_measurement: "m³"
        icon: mdi:fire
        device_class: gas
        state_class: total_increasing
      - name: Gas Consumption MJ
        unique_id: gas_consumption_mj
        state: "{{ states('sensor.gas_consumption_m3') | float(0) * 37 }}"
        unit_of_measurement: "MJ"
        icon: mdi:fire
        device_class: gas
        state_class: total_increasing

      - name: Gas Import Rate MJ
        unique_id: gas_import_rate_mj
        icon: mdi:cash-minus
        unit_of_measurement: "AUD/MJ"
        state: "0.031218"
      - name: Gas Import Rate M3
        unique_id: gas_import_rate_m3
        icon: mdi:cash-minus
        unit_of_measurement: "AUD/m³"
        state: "{{ states('sensor.gas_import_rate_mj') | float(0) * 37 }}"
      - name: Gas Supply Charge
        unique_id: gas_supply_charge
        icon: mdi:cash-minus
        unit_of_measurement: "AUD"
        state: "0.65142"

      - name: "Gas Total Cost Daily"
        unique_id: gas_total_cost_daily
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.gas_supply_charge') | float(0) %}
          {% set usage = states('sensor.gas_consumption_daily') | float(0) * states('sensor.gas_import_rate_m3') | float(0) %}
          {{ (supply + usage) | round(2) }}
      - name: "Gas Total Cost Monthly"
        unique_id: gas_total_cost_monthly
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.gas_supply_charge') | float(0) * now().day %}
          {% set usage = states('sensor.gas_consumption_monthly') | float(0) * states('sensor.gas_import_rate_m3') | float(0) %}
          {{ (supply + usage) | round(2) }}
      - name: "Gas Total Cost Yearly"
        unique_id: gas_total_cost_yearly
        icon: mdi:currency-usd
        unit_of_measurement: $
        state_class: total
        device_class: monetary
        state: >
          {% set supply = states('sensor.gas_supply_charge') | float(0) * ( states('sensor.days_passed_in_year') | int(0) + 1 ) %}
          {% set usage = states('sensor.gas_consumption_yearly') | float(0) * states('sensor.gas_import_rate_m3') | float(0) %}
          {{ (supply + usage) | round(2) }}

utility_meter:
  gas_consumption_daily:
    source: sensor.gas_consumption_m3
    name: Gas Consumption Daily
    cycle: daily
  gas_consumption_monthly:
    source: sensor.gas_consumption_m3
    name: Gas Consumption Monthly
    cycle: monthly
  gas_consumption_yearly:
    source: sensor.gas_consumption_m3
    name: Gas Consumption Yearly
    cycle: yearly

My council just charges an annual fee based upon the size (diameter) of the connection. For me this is 20mm connection for $177 per annum, that they then just charge 25% of per quarterly bill. Assuming your probably the same with your council.

Would it be simplest to work on using an extrapolated daily rate for the water_supply_charge ?
i.e. $177 / 4 = $44.25. Then $44.25 / 90 = $0.4916666666666667 per day (i.e. 360 days p.a. , this works out more accurate than $177 / 365).

Even though HA Energy doesn’t allow for a “sewerage meter” LoL, if we want to show the water bill more accurately aligning to the quarterly bill, we can factor in the daily sewerage cost ?
Instead of adding a sensor for sewerage_supply_charge and then having to update the formulas to add this into the total, this could be simply added by adding this into the water_supply_charge daily rate.

For example my council is $790 p.a. for sewerage, divide this by 365 = $2.164383561643836 per day.
Adding $2.164383561643836 (sewer) and $0.4916666666666667 (water) supply charges gives an extrapolated daily fees of $2.656050228310503 that will provide a supply charge within a few $ each quarter. ?


Looking at the gas and referencing a bill:

Gas

It looks like whilst the gas meter gets a pulse for every 10L (or 0.01m3) they then multiply this by a “correction factor” to account for the changes in volume causes by temperature, before it is then multipled by the “heating value” (that seems to vary according to the supplier you have… 38.3 seems to be the scientific rate though) to convert it to MJ.

To add in the correction factor, thinking would an addition sensor be required, instead of adding the correction to gas_consumption_m3? Having gas_consumption_m3 and then another sensor for gas_consumption_corrected_m3 would then mean you have a sensor tracking the actual gas meter reading to match up to the paper bill, whilst then having another sensor that has the corrected m3 that can then be used in calculating the HA daily/weekly gas bill?

This would mean your code could be updated with:

      - name: Gas Consumption M3
        unique_id: gas_consumption_m3
        state: "{{ states('counter.gas_counter') | float(0) * 0.01 }}"
        unit_of_measurement: "m³"
        icon: mdi:fire
        device_class: gas
        state_class: total_increasing

      - name: Gas Consumption M3 (Corrected)
        unique_id: gas_consumption_m3_corrected
        state: "{{ states('sensor.gas_consumption_m3') | float(0) * 0.9775 }}"   # Change 0.9775 to your retailers CORRECTION FACTOR
        unit_of_measurement: "m³"
        icon: mdi:fire
        device_class: gas
        state_class: total_increasing

      - name: Gas Consumption MJ
        unique_id: gas_consumption_mj
        state: "{{ states('sensor.gas_consumption_m3_corrected') | float(0) * 38.3 }}"
        unit_of_measurement: "MJ"
        icon: mdi:fire
        device_class: gas
        state_class: total_increasing

I assume this would also then mean adding to the utility_meter(s) to have:

utility_meter:
  gas_consumption_daily:
    source: sensor.gas_consumption_m3
    name: Gas Consumption Daily
    cycle: daily
  gas_consumption_daily_corrected:
    source: sensor.gas_consumption_m3_corrected
    name: Gas Consumption Daily (Corrected)
    cycle: daily
  gas_consumption_monthly:
    source: sensor.gas_consumption_m3
    name: Gas Consumption Monthly
    cycle: monthly
  gas_consumption_monthly_corrected:
    source: sensor.gas_consumption_m3_corrected
    name: Gas Consumption Monthly (Corrected)
    cycle: monthly
  gas_consumption_yearly:
    source: sensor.gas_consumption_m3
    name: Gas Consumption Yearly
    cycle: yearly
  gas_consumption_yearly_corrected:
    source: sensor.gas_consumption_m3_corrected
    name: Gas Consumption Yearly (Corrected)
    cycle: yearly

Not sure if this is correct, trying to follow the code, it appears that you convert the m3 into MJ (since thats what retailers bill upon) in gas_consumption_mj then you define the baseline (i.e. 0-1905mj rate) in gas_import_rate_mj next I’d assume is to multiple the mj consumed by the cost per mj. This appears to be the intent in what you’ve called gas_import_rate_m3 ?

        state: "{{ states('sensor.gas_import_rate_mj') | float(0) * 37 }}"

But looking at the state/formula it appears to be saying to take the gas_consumption_mj by the retailers heating value, and not by the number of mj you’ve consume? If this is supposed to be per mj $ x mj consumed, shouldn’t it be:

        state: "{{ states('sensor.gas_import_rate_mj') | float(0) * gas_consumption_mj }}"

All this might be OK for the rolling daily amount, but as you mention there’s the mj usage tiers they charge at, and this appears to not be based upon the usage per day, but instead the usage for the 90-92 billing period. With the total mj used over that period, then being broken down into tier 1 (0-1905mj with Origin), tier 2 (1905-252,558mj with Origin) and then “Remaining” (i.e. 252,558mj+).

NFI for the code to do this, but I would assume this means whilst the daily mj/$ bill showing might be OK for tracking purposes. To get near accurate for the monthly, it means adding up x months prior mj consumed and then trying to break it down into 0-1905 and 1905-252558 etc tiers and their associated cost, then total them up.

Still that isn’t going to be very accurate, as your then just taking the last x months mj, as the basis for these calculations, with these then covering multiple billing periods. Does HA include any way to define billing periods within this code, so you could work out the days you get billed for each quarter (i.e. for example for my 3rd quarter bill above… define 10Aug to 9Nov that should roughly be the same every year) so you could then setup sensors for all four quarters with state/formulas that include the from/to dates to count?

Re the three different charge rates you have, what are the they? Looking at Origin and they have 0-1905mj (everyone will exceed that), then 1905-252,558mj that equates to consuming ~6,595m3 (or around $5,227 in a quarter) so I doubt any “Home users” would hit that 3rd billing tier…?

Correct up til this point. I googled a conversion and the first result was 37, but i see on my bill also 38.3 so good pickup.

My conversion factor is less than 1 so yes it will be individual. I’d be more inclined to make it it’s own sensor but same result.

No, i’m only converting the usage rate from MJ to M^3. This gives you a sensor that can be used on the energy dashboard as an entity with the current price. This however doesn’t account for supply charges so the total costs sensors incorporate the m^3 import rate above, plus the supply charges.

Now my rates are different to yours.


I have an annual rate for the first 7560, next 7560, next 18000 and even higher tiers.
According to the fine print its divided by 365 and multiplied by the days in the billing period and not carried forward.

The monthly total costs will have to be tiered in the same way, it goes up by the highest amount until the cap for that tier, then increases by less for the next tier etc etc.

For the daily calcs, i’m just going to consider the highest tier which will give me a consistent look at usage rather than actual cost.

Hi Sgt Batten,

Could you confirm that “counter.water_counter” is the ID of a water sensor in your ESPHome yaml for the water meter sensor? Just trying to work out what/how your pulling the water data and updating to work with my setup. The sensor section of my esphone yaml is:

sensor:
  - platform: pulse_meter
    name: "Water Flow Rate"
    id: "water_flow_rate"
    pin:
      number: 5
      inverted: true
      mode:
        input: true
        pullup: true
    internal_filter_mode: PULSE
    unit_of_measurement: "L/min"
    icon: "mdi:pump"
    internal_filter: 100ms
    timeout: 1min
    total:
      name: 'Water Consumption'
      id: water_consumption
      accuracy_decimals: 2
      unit_of_measurement: "L"
      state_class: "total_increasing"
      device_class: water
      icon: "mdi:water"
      filters:
      - multiply: 0.5

No it is a counter helper made in the HA UI. I have an automation that increments it 1 number each time my aqara door sensor (reed switch) turns on (twice per Lin my case, hence the * 0.5)

I don’t have a flow rate sensor figured out yet but will likely just use the built in derivative sensor in HA.

The yaml for the ESP8266 above with the ‘pulse_meter’, produces a flow rate. Sends into HA the following type data, giving 0.5L increases (per spin of the meter dial) that are captured also with a reed switch.

‘Water Flow Rate’: Sending state 13.44387 L/min with 2 decimals of accuracy
‘Water Consumption’: Sending state 88.50000 L with 2 decimals of accuracy

if your hammering the water, it will keep updating as it increases flow, turn off the tap and it will wait 59sec and then update back to 0.00 L/sec.

Any chance you post your configs ona Github repository, much easier to reverse engineer and customise when seeing the full picture?