EMHASS: An Energy Management for Home Assistant

Ok so I set up an automation that does three things

  1. Charges at p_batt_forecast when its a negative figure
  2. Puts the battery into standby when p_batt_forecast is 0
  3. Puts the battery into ‘Automatic - self consumption’ when above 0 (don’t quite trust it to discharge at p_batt_forecast yet)

Result:


Overynight is charged the battery to just under 50% when there’s a sunny day coming up so not sure why?

Also, the forecast seems to want to charge again in the middle of a sunny day?

I’m on the Ausgrid two way trial tariff that adds an extra 26.58 c/kWh FiT between 14:00 and 20:00 so I don’t want to be charging when this schedule wants to charge. I’ll want to be discharging somewhat but not too much as I want to get over the evening peek which will end at 20:00.


So I want the system to combine the forecast PV and battery capacity and calculate the discharge rate to streatch battery to 20:00 and feed in as much energy during this Ausgrid two way tariff peek. Perhaps I have something misconfigured?

My shell commands:

shell_command:
  dayahead_optim: 'curl -i -H "Content-Type: application/json" -X POST -d ''{}'' http://localhost:5000/action/dayahead-optim'
  publish_data: 'curl -i -H "Content-Type: application/json" -X POST -d ''{}'' http://localhost:5000/action/publish-data'
  post_amber_forecast:
    'curl -i -H ''Content-Type: application/json'' -X POST -d ''{"prod_price_forecast":{{(
    state_attr(''sensor.cecil_st_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)
    }},"load_cost_forecast":{{(
    state_attr(''sensor.cecil_st_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)
    }},"prediction_horizon":33}'' http://localhost:5000/action/dayahead-optim'
  post_emhass_forecast:
    'curl -i -H ''Content-Type: application/json'' -X POST -d ''{"prod_price_forecast":{{(
    state_attr(''sensor.cecil_st_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)
    }},{{states(''sensor.solcast_24hrs_forecast'')}},"load_cost_forecast":{{(
    state_attr(''sensor.cecil_st_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)
    }}}'' http://localhost:5000/action/dayahead-optim'
  post_mpc_optim_solcast:
    'curl -i -H "Content-Type: application/json" -X POST -d ''{"load_cost_forecast":{{(
    ([states(''sensor.cecil_st_general_price'')|float(0)] +
    state_attr(''sensor.cecil_st_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)[:48])
    }}, "prod_price_forecast":{{(
    ([states(''sensor.cecil_st_feed_in_price'')|float(0)] +
    state_attr(''sensor.cecil_st_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)[:48])
    }}, "pv_power_forecast":{{states(''sensor.solcast_24hrs_forecast'')
    }}, "prediction_horizon":48,"soc_init":{{(states(''sensor.sonnenbatterie_84324_state_charge_user'')|float(0))/100
    }},"soc_final":1.0,"def_total_hours":[2,2,2,0]}'' http://localhost:5000/action/naive-mpc-optim'
# def_total_hours [Deferrable_0=PoolPump] [Deferrable_1=DW] [Deferrable_2=WM&D] [Deferrable_3=tesla] 

Automations that call these shell commands are as follows:

  1. calling dayahead_optim at 05:30
  2. calling post_mpc_optim_solcast and then publish_data every 5 min

But I’m not calling post_amber_forecast or post_emhass_forecast at all. Don’t understand if these are required.

Can you show p_grid_forecast for the same period. It should be scheduling exports during the high pricing and shouldn’t charge to much overnight, unless these is a high price in the morning.

Can you also show the table from the web interface in port :5000 as that will show the full picture.


Thanks for looking at this for me.

Mark, david,

Is there a way to set the export price target? I noticed the lowest price was 10c overnight then wanted to charge… but targeting to sell at 11c? is there a way to set the sell target price?

If you look at your load cost and prod price you are not injecting the Amber prices, thus EMHASS isn’t optimising for those price signals.

How are you calling your optimisation?

I’m using the shell commands listed below and calling them with node-red (also below). I plan to convert the shell commands in config.yaml to node-red when I understand them better. I prefer the visual nature of node-red.

But I’m only calling:

  1. dayahead_optim at 05:30.
  2. post_mpc_optim_solcast and publish_data every 5 min.

I’m not calling post_amber_forecast or post_emhass_forecast at all. This must be the issue? How often are these two shell commands meant to be called?

shell_command:
  dayahead_optim: 'curl -i -H "Content-Type: application/json" -X POST -d ''{}'' http://localhost:5000/action/dayahead-optim'
  publish_data: 'curl -i -H "Content-Type: application/json" -X POST -d ''{}'' http://localhost:5000/action/publish-data'
  post_amber_forecast:
    'curl -i -H ''Content-Type: application/json'' -X POST -d ''{"prod_price_forecast":{{(
    state_attr(''sensor.cecil_st_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)
    }},"load_cost_forecast":{{(
    state_attr(''sensor.cecil_st_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)
    }},"prediction_horizon":33}'' http://localhost:5000/action/dayahead-optim'
  post_emhass_forecast:
    'curl -i -H ''Content-Type: application/json'' -X POST -d ''{"prod_price_forecast":{{(
    state_attr(''sensor.cecil_st_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)
    }},{{states(''sensor.solcast_24hrs_forecast'')}},"load_cost_forecast":{{(
    state_attr(''sensor.cecil_st_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)
    }}}'' http://localhost:5000/action/dayahead-optim'
  post_mpc_optim_solcast:
    'curl -i -H "Content-Type: application/json" -X POST -d ''{"load_cost_forecast":{{(
    ([states(''sensor.cecil_st_general_price'')|float(0)] +
    state_attr(''sensor.cecil_st_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)[:48])
    }}, "prod_price_forecast":{{(
    ([states(''sensor.cecil_st_feed_in_price'')|float(0)] +
    state_attr(''sensor.cecil_st_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)[:48])
    }}, "pv_power_forecast":{{states(''sensor.solcast_24hrs_forecast'')
    }}, "prediction_horizon":48,"soc_init":{{(states(''sensor.sonnenbatterie_84324_state_charge_user'')|float(0))/100
    }},"soc_final":1.0,"def_total_hours":[2,2,2,0]}'' http://localhost:5000/action/naive-mpc-optim'
# def_total_hours [Deferrable_0=PoolPump] [Deferrable_1=DW] [Deferrable_2=WM&D] [Deferrable_3=tesla] 

Node-red flows:

They are set up like this one:

Again thanks

Ah ha,

Yes please call post_amber_forcast instead of dayahead_optim.

You should be able to call at any time, not have to wait until 0530.

ok, fantastic. Thanks
I’ll replace dayahead_optim with post_amber_forecast to run at 5:30 but also run it now as well. I assume it only needs to run once a day?

It will run ok if you update prices once a day and I would start there.

The challenge is Amber prices change every 5 minutes so if you only run the update once a day then the prices will be out of date very quickly, which means EMHASS will be making decisions on our of date data.

This is where the more complex MPC optimisation is better as it is designed to run at high frequency. SmartShift by comparison runs their lpc optimisation every five minutes.

But certainly start with the once a day to get your model in the ball park and ensure your battery and other things are working as expected.

Ok thanks for that.
Now I have the following flow:


When p_batt_forecast changes vaule it passes through this switch (Ignore the orange time window, its set to 24 hours now):

This passes the value of p_batt_forecast to one of three combination POST or PUT commands.

  1. When the p_batt_forecast is < 0 a charge is initiated at this value by
    a) Setting the backup buffer to 0%
    b) Putting the battery in manual mode 2 seconds later
    c) POSTing a charge comman to the positive value (Math.abs(msg.payload)) of p_batt_forecast 5 seconds later:

  2. When the p_batt_forecast is == 0 the battery is put in standby by PUTing a 100% backup buffer configuration:

  3. When the p_batt_forecast is > 0 a discharge is initiated at this value:

Be interesting to see how it works.

1 Like

If I’m running post_mpc_optim every 5 mins and then publish_data am I not also using MPC and should see the better result?

Seems to be a bit all over the place.

This looks pretty good to me.

  • Until 8pm your battery exports to the grid for 32¢/ kWh. Which is a high feeding rate so exporting is the correct action.

  • After 8pm it runs your house from the grid @ 18¢/ kWh, which is cheaper than the earlier exports earned. So correct.

  • At 10am is starts charging your battery from the grid @ 4¢/ kWh which is the cheapest time of the day to charge and is cheaper than the high value (27¢/ kWh) of your solar later in the day. So grid charging is correct.

  • At 2pm your AusGrid high feed in rate (27+ ¢/ kWh) starts so your battery exports to the grid as well as exporting your solar, which is correct as you get the highest feeding of the day.

I agree it does seem counter intuitive that you don’t save your battery to run your house overnight, but these are the insights you get with EMHASS. You are financially better off (and more environmental benefits) by fully exporting your battery to zero @32¢/ kWh before 8pm as if you saved an extra kWh to run your house after 8pm you would only save 18¢/ kWh.

EMHASS does have other options than optimising for profit, the self consumption will generate less savings, but will maximise your battery for the household.

1 Like

ok great. I’ll try not to sit in front of the dashboard and watch the needle flick back and forward. Thanks

2 Likes

Hi,
I am having an issue where I am unable to get my solcast data into EMHASS. I have 3xstrings and have successfully set up sensors as per the instructions. I have combined the sensors and can see the data is getting into home assistant correctly when use the states lookup in developer tools. I have the shell command

dayahead_optim: "curl -i -H \"Content-Type:application/json\" -X POST -d '{\"pv_power_forecast\":{{states('sensor.solcast_24hrs_forecast')}}}' http://localhost:5000/action/dayahead-optim"

In this command the sensor.solcast_24hrs_forecast has the correct pv forecast data. However EMHASS appears to list the PV production as if for the defaults (16 panels, 1 string, primo inverter etc). Is there a specific line of code I need in my config.yaml to use solcast data instead? Cheers

Edit: When I put that shell command through the template editor I get the correct data but for some reason its nots getting into emhass / overwriting p_pv_forecast

What is the best setting for history days?

Can you provide the output from the template editor here so we can see?

@davidusb it might be useful to provide as debug output the dictionary keys that EMHASS receives as this is a common issue.

2 is a good default value.

Thanks for your response Mark.
This is what is in my configuration.yaml file. All sensors seem to be correct when I view them in

sensor: 
    - platform: rest
      name: "Solcast Forecast huis"
      json_attributes:
        - forecasts
      resource: https://api.solcast.com.au/rooftop_sites/9XX/forecasts?format=json&api_key=XXXXX&hours=24
      method: GET
      value_template: "{{ (value_json.forecasts[0].pv_estimate)|round(2) }}"
      unit_of_measurement: "kW"
      device_class: power
      scan_interval: 86400
      force_update: true
    
    - platform: template
      sensors:
        solcast_24hrs_forecast_huis:
          value_template: >-
            {%- set power = state_attr('sensor.solcast_forecast_huis', 'forecasts') | map(attribute='pv_estimate') | list %}
            {%- set values_all = namespace(all=[]) %}
            {% for i in range(power | length) %}
              {%- set v = (power[i] | float |multiply(1000) ) | int(0) %}
              {%- set values_all.all = values_all.all + [ v ] %}
            {%- endfor %} {{ (values_all.all)[:48] }}


    - platform: rest
      name: "Solcast Forecast garage"
      json_attributes:
        - forecasts
      resource: https://api.solcast.com.au/rooftop_sites/XXX/forecasts?format=json&api_key=XXX&hours=24
      method: GET
      value_template: "{{ (value_json.forecasts[0].pv_estimate)|round(2) }}"
      unit_of_measurement: "kW"
      device_class: power
      scan_interval: 86400
      force_update: true
    
    - platform: template  
      sensors:  
        solcast_24hrs_forecast_garage:
          value_template: >-
            {%- set power = state_attr('sensor.solcast_forecast_garage', 'forecasts') | map(attribute='pv_estimate') | list %}
            {%- set values_all = namespace(all=[]) %}
            {% for i in range(power | length) %}
              {%- set v = (power[i] | float |multiply(1000) ) | int(0) %}
              {%- set values_all.all = values_all.all + [ v ] %}
            {%- endfor %} {{ (values_all.all)[:48] }}

    - platform: rest
      name: "Solcast Forecast third"
      json_attributes:
        - forecasts
      resource: https://api.solcast.com.au/rooftop_sites/XX/forecasts?format=json&api_key=XXXX&hours=24
      method: GET
      value_template: "{{ (value_json.forecasts[0].pv_estimate)|round(2) }}"
      unit_of_measurement: "kW"
      device_class: power
      scan_interval: 86400
      force_update: true
    
    - platform: template  
      sensors:  
        solcast_24hrs_forecast_third:
          value_template: >-
            {%- set power = state_attr('sensor.solcast_forecast_third', 'forecasts') | map(attribute='pv_estimate') | list %}
            {%- set values_all = namespace(all=[]) %}
            {% for i in range(power | length) %}
              {%- set v = (power[i] | float |multiply(1000) ) | int(0) %}
              {%- set values_all.all = values_all.all + [ v ] %}
            {%- endfor %} {{ (values_all.all)[:48] }}

    - platform: template
      sensors:
        solcast_24hrs_forecast:
          value_template: >-
            {% set a = states("sensor.solcast_24hrs_forecast_garage")[1:-1].split(',') | map('int') | list %}
            {% set b = states("sensor.solcast_24hrs_forecast_huis")[1:-1].split(',') | map('int') | list %}
            {% set c = states("sensor.solcast_24hrs_forecast_third")[1:-1].split(',') | map('int') | list %}
            {% set ns = namespace(items = []) %}
            {% for i in range(a | length) %}
              {% set ns.items = ns.items + [ a[i]  + b[i]  + c[i]    ] %}
            {% endfor %}
            {{ ns.items }}

template:
  - sensor:
      - name: power_load_no_var_loads
        device_class: power
        unit_of_measurement: W
        state: >-
            {{states('sensor.solarnet_power_load')|float(0) * -1
            }}

shell_command:
  dayahead_optim: "curl -i -H \"Content-Type:application/json\" -X POST -d '{\"pv_power_forecast\":{{states('sensor.solcast_24hrs_forecast')}}}' http://localhost:5000/action/dayahead-optim"
  publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://localhost:5000/action/publish-data"

When I put the solcast_24hrs_forecast template into editor I get the output (no idea why there are so many empty rows):

sensor:
- platform: template
      sensors:
        solcast_24hrs_forecast:
          value_template: >-
            
            
            
            
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
              
            
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 1647, 2971, 4452, 5722, 7221, 8554, 9640, 10503, 11160, 11511, 11640, 11457, 11105, 10422, 9565, 8405, 7050, 5437, 3520, 1501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
This template listens for the following state changed events:

Entity: sensor.solcast_24hrs_forecast_garage
Entity: sensor.solcast_24hrs_forecast_huis
Entity: sensor.solcast_24hrs_forecast_third

When I put the shell command through I get

shell_command:
  dayahead_optim: "curl -i -H \"Content-Type:application/json\" -X POST -d '{\"pv_power_forecast\":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 186, 1647, 2971, 4452, 5722, 7221, 8554, 9640, 10503, 11160, 11511, 11640, 11457, 11105, 10422, 9565, 8405, 7050, 5437, 3520, 1501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}' http://localhost:5000/action/dayahead-optim"
  publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://localhost:5000/action/publish-data"
This template listens for the following state changed events:

Entity: sensor.solcast_24hrs_forecast

The values in this final shell command are as they should be but this is my forecast in EMHASS with a max PV forecast of 855W:

Cheers
Tom

The system seems to be using the pool pump to balance grid in/out. Its been switching the pool pump on and off all morning. Is this controlled by treat_deferrable_load_as_semi_cont?

The definition in the manual is not clear:

treat_def_as_semi_cont: Define if we should treat each deferrable load as a semi-continuous variable. Semicontinuous
variables are variables that must take a value between their minimum and maximum or zero. For
example:
– True
– True