[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.

Assumptions:

  • You are on a demand tariff (which requires a smart meter)
  • You have working electricity usage sensors
    • sensor.electricty_imported_power_kwh - a sensor providing a running total of power imported in kWh
    • sensor.electricity_exported_power_kwh - a sensor providing a running total of power exported in kWh
      • If you do not get paid to export power, 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 per day.

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(month=next, day=1) %}
        {{ (last.date() - this.date()).days - 1 }}

Now, we need to setup some utility meters to break our power 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 power it no tariffs because I have a flat export rate.

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_imported_demand:
    source: sensor.electricity_imported_power_kwh
    name: Electricity Imported Demand
    cron: 0,30 * * * *
    tariffs:
      - high-demand
      - low-demand
      - no-demand

  electricity_exported_power_daily:
    source: sensor.electricity_exported_power_kwh
    name: Electricity Exported Power 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_power_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_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) }}

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_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) }}


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

package: !include_dir_named packages

Then in packages/energy_costs.yaml

### This package assumes you have an import sensor "sensor.electricty_imported_power_kwh" 
### which provides cumulative electricity imported in kWh 
### and an export sensor sensor.electricty_exported_power_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
    province: NSW ### your state code
    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(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_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 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_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) }}

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_power_daily:
    source: sensor.electricity_imported_power_kwh
    name: Electricity Imported Power Daily
    cycle: daily
    tariffs:
      - peak
      - shoulder
      - off-peak

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

  electricity_exported_power_daily:
    source: sensor.electricity_exported_power_kwh
    name: Electricity Exported Power 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_power_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

3 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