Skip to content

Octopus Agile

Jonas Karlsson edited this page Oct 14, 2025 · 1 revision

Assumes that the Home Assistant Octopus Energy intergration is installed and configured.

There are two options for the template sensor depending on if you also have a sensor set up for the agile price prediction. In both cases you will need to replace the XXXX_XXXX in the entiry names with the values for your electric meter.

If you do have the prediction sensor configured as per here, you can use this template sensor for pricing:

- sensor:
    - name: "Octopus Energy Price Sensor"
      unique_id: octopus_energy_price_sensor
      unit_of_measurement: GBP/kWh
      state: "{{ (states('sensor.octopus_energy_electricity_XXXXXX_XXXXXX_current_rate') | float)  }}"
      attributes:
        prices_today: >
          {% set today = now() %}
          {% set fifteen_minutes = timedelta(minutes=15) %}
          {% set ns = namespace(data=[]) %}

          {% set current_day_rates = state_attr("event.octopus_energy_electricity_XXXX_XXXX_current_day_rates", "rates") %}
          {% set time_key = 'start' %}
          {% set price_key = 'value_inc_vat' %}
          {% for i in current_day_rates | default([], true) %}
            {% set current_day_i_date = as_local(as_datetime(i[time_key])) %}
            {% if current_day_i_date.date() == today.date() %}
              {% set ns.data = ns.data + [dict(time = current_day_i_date.isoformat(), price = i[price_key])] %}
              {% set ns.data = ns.data + [dict(time = (current_day_i_date + fifteen_minutes).isoformat(), price = i[price_key])] %}
            {% endif %}
          {% endfor %}
          {% set last_date_in_current_day = as_datetime(ns.data[-1].time) %}

          {% set next_day_rates = state_attr("event.octopus_energy_electricity_XXXX_XXXX_next_day_rates", "rates") %}
          {% if next_day_rates %}
            {% set next_day_time_key = 'start' %}
            {% set price_key = 'value_inc_vat' %}
            {% for i in next_day_rates | default([], true) if as_local(as_datetime(i[next_day_time_key])).date() == today.date() %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[next_day_time_key])).isoformat(), price = i[price_key])] %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[next_day_time_key]) + fifteen_minutes).isoformat(), price = i[price_key])] %}
            {% endfor %}
          {% else %}
            {% set forecast_data = state_attr('sensor.agile_predict', 'prices') %}
            {% set time_key = 'date_time' %}
            {% set price_key = 'agile_pred' %}
            {% for i in forecast_data | default([], true) %}
              {% set forcast_next_i_date = as_local(as_datetime(i[time_key])) %}
              {% if forcast_next_i_date.date() == today.date() and forcast_next_i_date > last_date_in_current_day %}
                {% set ns.data = ns.data + [dict(time = forcast_next_i_date.isoformat(), price = i[price_key] / 100)] %}
                {% set ns.data = ns.data + [dict(time = (forcast_next_i_date + fifteen_minutes).isoformat(), price = i[price_key] / 100)] %}
              {% endif %}
            {% endfor %}
          {% endif %}

          {{ ns.data }}
        prices_tomorrow: >
          {% set tomorrow = now() + timedelta(days=1) %}
          {% set fifteen_minutes = timedelta(minutes=15) %}
          {% set ns = namespace(data=[]) %}

          {% set next_day_rates = state_attr("event.octopus_energy_electricity_XXXX_XXXX_next_day_rates", "rates") %}
          {% if next_day_rates %}
            {% set time_key = 'start' %}
            {% set price_key = 'value_inc_vat' %}
            {% for i in next_day_rates | default([], true) if as_local(as_datetime(i[time_key])).date() == tomorrow.date() %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[time_key])).isoformat(), price = i[price_key])] %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[time_key]) + fifteen_minutes).isoformat(), price = i[price_key])] %}
            {% endfor %}
          {% else %}
            {% set forecast_data = state_attr('sensor.agile_predict', 'prices') %}
            {% set time_key = 'date_time' %}
            {% set price_key = 'agile_pred' %}
            {% for i in forecast_data | default([], true) if as_local(as_datetime(i[time_key])).date() == tomorrow.date() %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[time_key])).isoformat(), price = i[price_key] / 100)] %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[time_key]) + fifteen_minutes).isoformat(), price = i[price_key] / 100)] %}
            {% endfor %}
          {% endif %}

          {{ ns.data }}

If you don't or only want to use announced pricing then you can use this template sensor:

- sensor:
    - name: "Octopus Energy Price Sensor"
      unique_id: octopus_energy_price_sensor
      unit_of_measurement: GBP/kWh
      state: "{{ (states('sensor.octopus_energy_electricity_XXXXXX_XXXXXX_current_rate') | float)  }}"
      attributes:
        prices_today: >
          {% set today = now() %}
          {% set fifteen_minutes = timedelta(minutes=15) %}
          {% set ns = namespace(data=[]) %}

          {% set current_day_rates = state_attr("event.octopus_energy_electricity_XXXX_XXXX_current_day_rates", "rates") %}
          {% set time_key = 'start' %}
          {% set price_key = 'value_inc_vat' %}
          {% for i in current_day_rates | default([], true) %}
            {% set current_day_i_date = as_local(as_datetime(i[time_key])) %}
            {% if current_day_i_date.date() == today.date() %}
              {% set ns.data = ns.data + [dict(time = current_day_i_date.isoformat(), price = i[price_key])] %}
              {% set ns.data = ns.data + [dict(time = (current_day_i_date + fifteen_minutes).isoformat(), price = i[price_key])] %}
            {% endif %}
          {% endfor %}
          {% set last_date_in_current_day = as_datetime(ns.data[-1].time) %}

          {% set next_day_rates = state_attr("event.octopus_energy_electricity_XXXX_XXXX_next_day_rates", "rates") %}
          {% if next_day_rates %}
            {% set next_day_time_key = 'start' %}
            {% set price_key = 'value_inc_vat' %}
            {% for i in next_day_rates | default([], true) if as_local(as_datetime(i[next_day_time_key])).date() == today.date() %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[next_day_time_key])).isoformat(), price = i[price_key])] %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[next_day_time_key]) + fifteen_minutes).isoformat(), price = i[price_key])] %}
            {% endfor %}
          {% endif %}

          {{ ns.data }}
        prices_tomorrow: >
          {% set tomorrow = now() + timedelta(days=1) %}
          {% set fifteen_minutes = timedelta(minutes=15) %}
          {% set ns = namespace(data=[]) %}

          {% set next_day_rates = state_attr("event.octopus_energy_electricity_XXXX_XXXX_next_day_rates", "rates") %}
          {% if next_day_rates %}
            {% set time_key = 'start' %}
            {% set price_key = 'value_inc_vat' %}
            {% for i in next_day_rates | default([], true) if as_local(as_datetime(i[time_key])).date() == tomorrow.date() %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[time_key])).isoformat(), price = i[price_key])] %}
              {% set ns.data = ns.data + [dict(time = as_local(as_datetime(i[time_key]) + fifteen_minutes).isoformat(), price = i[price_key])] %}
            {% endfor %}
          {% endif %}

          {{ ns.data }}

The template is a little complicated as the agile pricing "day" stops at 22:30 so we need to also look at the "next day" to get the final few elements for prices_today. We also need to duplicate the prices so they are every 15 minutes as the integration gives an error if you only supply values for every 30 minutes.

Clone this wiki locally