Use Amber Electric Forecast to Optimize what hours are selected for pool pump each day

Yes, you are correct 48 values, so .24 is the median.

Of course it isn’t exact as the 48 values do change over time, both with new values being added to the end and the forecast changing as the estimation is more accurate. I could foresee if lots of low values get added to the end, it could delay until those low prices are realised, similarly if lots of high prices are added to the end, it may bring forward the best hours. I have also seen prices jump within the 30 minute interval, so what started out as the a best hour of the day can change dynamically.

I’m going to switch my pool filter over to this now to see how it runs over the next 24 hrs.

This scaleable best hours approach also has utility for other loads which can be time shifted, like the hot water service (run in best 4 hours of the day), heating/ cooling (run in best 6 hours of the day to maintain stable temp, home battery charging (14 kWh battery with max charge rate of 3.68 kW wants best four hours of the day).

EV charging gets very interesting (75 kWh battery with max charge rate of 11 kW), especially as car isn’t always connected to charger, so it will constantly recalculate when car is plugged in. Could integrate with calendar to see how many km are required to drive the next day and ensure that amount of range is added.

  • best 7 hours of the day when at 0% state of charge (add 400 km range)
  • best 3 hours of the day at 50% state of charge (add 200 km range)
  • best 1 hour of the day at 85% state of charge (add 66 km range)
  • best x hours of the day at y% state of charge where x=(100 -y)/100 * (75/ 11)
1 Like

I think I’m going to implement the automation by adding 0.01 to the value to turn on and leave it natural for turning the gear off. because it is always above or below…wish there was a less than or equal to in the automation triggers and conditions

Have a look at my last example where I set the binary sensor:

amber_general_price_le_median

Which does include less than or equal to, which I use as the condition in the automation to turn on/ off the pump.

1 Like

Reverse use case also applies for battery export to the grid (14 kWh battery max discharge rate 5 kW) select best 3 hours with highest feed in tariff (FIT).

I’m doing something wrong. Have to learn how to add the new template integrations. I converted to legacy, as everything else in my config is legacy. But the check “true/false” of general price >= forecast isn’t working. Is always true

I have setup binary sensors for the key time periods:

  - binary_sensor:
      - name: "Amber Price Below Median"
        state: "{{ states('sensor.amber_general_price')|float(0) 
                    <= (state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|sort).23}}"
      - name: "Amber Cheapest 8 hrs"
        state: "{{ states('sensor.amber_general_price')|float(0) 
                    <= (state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|sort).15}}"
      - name: "Amber Cheapest 4 hrs"
        state: "{{ states('sensor.amber_general_price')|float(0) 
                    <= (state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|sort).7}}"
      - name: "Amber Cheapest hour"
        state: "{{ states('sensor.amber_general_price')|float(0) 
                    <= (state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|sort).1}}"

Then my automations look like this: (note you have to check for on/ off not true)

alias: pool filter - cheapest 8 hrs
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.amber_cheapest_8_hrs
condition: []
action:
  - service: switch.turn_{{trigger.to_state.state}}
    data: {}
    target:
      entity_id: switch.pool_socket_1
  - service: notify.mobile_app_pixel_6
    data:
      message: Cheapest 8 hrs - Pool
mode: single
alias: EV Charging - best hour
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.amber_cheapest_hour
action:
  - service: switch.turn_{{trigger.to_state.state}}
    data: {}
    target:
      entity_id: switch.duka_charger_switch
  - service: notify.mobile_app_pixel_6
    data:
      message: >-
        EV Charging {{trigger.to_state.state}} - Cheapest Hour {{
        states('sensor.amber_general_price')}} $/kWh
mode: single

Lol. Feel stupid…binary sensors :woman_facepalming:

Sorted

Was super cheap mid day when I wrote them in. So didn’t notice till a bit later when I expected change

1 Like

Funny day today, started with medium prices in AM and high prices in PM, but then price kept falling each hour so best hour kept on rolling into the current hour, which is why best hour is much longer than an hour, which was fine for the pool pump, but a bit more expensive (above DMO) than I would like for EV charging.

Now that tomorrow mornings prices have all rolled into the window, everything has been switched off, which is as desired (not using above DMO prices), and is waiting to restart for the low price windows tomorrow morning.

image

# Amber forecasts in 30 minute intervals
[0.22, 0.29, 0.33, 0.33, 0.33, 0.33, 0.33, 0.26, 0.23, 0.24, 0.24, 0.24, 0.22, 0.22, 0.21, 0.23, 0.22, 0.22, 0.22, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.22, 0.23, 0.24, 0.24, 0.21, 0.2, 0.2, 0.19, 0.2, 0.18, 0.18, 0.18, 0.18, 0.17, 0.17, 0.17, 0.18, 0.17, 0.18, 0.18, 0.19, 0.22, 0.22]

# Sort into asending values
[0.17, 0.17, 0.17, 0.17, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.19, 0.19, 0.2, 0.2, 0.2, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.22, 0.22, 0.22, 0.22, 0.22, 0.22, 0.22, 0.22, 0.22, 0.23, 0.23, 0.23, 0.24, 0.24, 0.24, 0.24, 0.24, 0.26, 0.29, 0.33, 0.33, 0.33, 0.33, 0.33]

#Grab 23th attribute from list which is the median value
0.21


amber_median_forecast: 0.21
amber_8hr: 0.2
amber_4hr: 0.18
amber_hour: 0.17

amber_general_price: 0.23

Maybe put a static high limit value in as well? What is the price you aren’t willing to pay to charge the EV and will walk or ride a bike instead?

Got the binary sensors working. Really need to split my config. Is getting unruly….2 weeks after installing.

I’ve created some headaches…as I’ve now got a mix of legacy and the new template formats. Still learning how it all works with indentation and sections…on top of all the parameters :joy:

So dilemma now is making the count Variable at the end that finds the amount of time needed to be dynamic.if possible…maybe what i’m thinking doesn’t work.

so in stead of a static .2 (i.e.1 hour) or .6 (i.e.3 hours)…it is dynamic (X)

  - binary_sensor:
      - name: "Amber Cheapest X Hours Trigger"
        state: "{{ states('sensor.durst_home_general_price')|float(0) <= (state_attr('sensor.durst_home_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|sort)X}}"

and fed by a time estimation sensor…multiplied by 0.2 and rounded to a single decimal (assuming less than 4.5 hours and 2 decimals if more

  - sensor:
    - name: "Pool Estimate Heat Time"
      unit_of_measurement: "Hours"
      state: >
          {% set pooltemprise = states('sensor.last_pool_temp_average_rise_rate') | float(none) %}
          {% set poolsettemp = states('sensor.pool1_heaters0_settemp') | float(none) %}
          {% set poolcurrenttemp = states('sensor.pool1_temperature') | float(none) %}
          {{ (pooltemprise * ((poolsettemp) - (poolcurrenttemp)))*0.2 | round(1) }}  

not sure why but rounding doesn’t work in that?

could X in the trigger, be the output of that sensor?

No dilemmas, just opportunities.

Need to get my pool heat pump fixed, so I can play along.

Here is a dynamic solution for EV charging based on % battery. (note we have to change the magic . into [] )

For my current state of charge of 75%, I require the two hours charging, which requires an amber price of 0.16, which are all tomorrow morning. The charging duration will change dynamically with battery soc and the amber cost will change based on updated electricity forecast pricing and required hours charging.

When battery is empty (0%) it will select best 16 x 30 minute blocks for charging from the amber forecast.
When battery is 93% it will select best 1x 30 minute block for charging from the amber forecast.

# Amber forecasts in 30 minute intervals
{{ state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list}}

# Sort into asending values
{{ state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|sort}}

#Number of periods
{{ state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|wordcount / 2}}

battery_state_of_charge: {{states('sensor.duka_battery_sensor')}}

    ev_required_charging_30mins:
      value_template : "{{(((100-states('sensor.duka_battery_sensor')|int) / 100 * 8*2)|int)}}"

    amber_charging_hours_price: {{ (state_attr('sensor.amber_general_forecast', 'forecasts') | 
                                   map(attribute='per_kwh')|list|sort)[states('ev_charging_30mins')|int] }}

1 Like

Edit: fixed…int or flat needs to be inside bracket

OK…closer…lol

getting this in developer
UndefinedError: ‘list object’ has no attribute ‘0.1’

final sensor is

{{ (state_attr('sensor.amber_general_forecast', 'forecasts') | map(attribute='per_kwh')|list|sort)[states('sensor.pool_estimate_heat_time_corrected')] |float }}

to get from time as a decimal place I had two intermediate sensors to convert it…probably a simpler way

    - name: "Pool Estimate Heat Time Converted"
      unit_of_measurement: "30 Min Segments"
      state: >
          {% set pooltemprise = states('sensor.last_pool_temp_average_rise_rate') | float(none) %}
          {% set poolsettemp = states('sensor.pool1_heaters0_settemp') | float(none) %}
          {% set poolcurrenttemp = states('sensor.pool1_temperature') | float(none) %}
          {{ (((poolsettemp) - (poolcurrenttemp)) / pooltemprise) | round() * 60/30 | round(1) }}         
    - name: "Pool Estimate Heat Time Corrected"
      state: "{{ (states('sensor.pool_estimate_heat_time_converted'))| round()*0.1 }}" 

Just had a mini spike ($0.40) followed by lowest price of the day ($0.16). AC, pool and EV all performed as expected switching on an off.

  • Pool running in cheapest 8 hrs
  • EV charging in cheapest hour
  • AC switching off for peak and back on for cheap

1 Like

Show you the variability we get here in SA

FYI the counting starts at 0.0…not 0.1

so if the variable is 0.1 it is actually the first 1hour and 0.2 is an hour and a half

do you have a variable speed pump?

Fixed speed circulating pump for Filter/ Chlorinator.

Variable speed circulating pump for Heater, but no remote control over speed. Just starts at high speed and then steps down to steady state.

Got screwed today. Forcast low was 0.8 for 5 hours. Never got below 0.9 and forecast never adjusted till it skyrocketed to 0.43.

Would have been happy topping the heat up for $0.10. Actual price just fell short consistently. Have to watch a bit more see if this is common and add an offset/buffer

Ouch.

Yes, if the forecast is never realised, then it won’t turn on, because it can see something better to wait for into the future. Agree it is worth monitoring.

Did it run during 0.43 because you have a minimum run hrs per day and you ran out of runway?

I would of thought it would of kicked in 2-3 hrs before the 0.43, stopped during the peak, and then picked up after the peak had past as it can see far enough into the future to wait for the good prices.

I have my filter pump running on straight best 8hrs of the day, which sees pretty constant pricing.

My EV is running on a sliding best hours based of % charge. At a low % it will consume almost any price below DMO, which then tapers off as % gets higher, e.g. above 72% it will only charge on best 30mins of the day. For my daily drive anything above 30-40% is a bonus and thus the motivation is cheap storage.

You could break your 5 hrs heating runtime like that:

  • runtime less than 2 hrs - use best 8 hr window (most important)
  • runtime 2-4 hrs - use best 5 hr window (import)
  • runtime 4-5 hrs - use best 2 hr window (less important)
  • runtime over 5 hr - stop

I’d also use energy as a proxy for runtime as it measures actual work performed, sometimes my switch can be on, but the pump may not be running because it is in the wrong mode.

Calculations are simple for my 1 kW pump - 1 hr runtime = 1 kWh energy, my 30 kW heat pump has a nominal 5 kW input - 1 hr runtime = 5 kWh energy.

I don’t have the heater set up with mandatory run time in a day…heating I see as a luxury…the filter will need to be on, or pool will go off…need to get my head around how to logically implement it.

it didn’t turn on…because it started to see good value the next day at 10am onwards…so just skipped the day…might just set up an additional trigger of <=0.1 because regardless of it going to 0.2 later…it is still good value…guess it will need to be some kind of ‘or’ statement.