EMHASS: An Energy Management for Home Assistant

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

A variable speed pool pump might have this settings.

A normal pool pump wouldn’t.
I have a 15 minute delay.

ok thanks for that Mark.

Another question if I may. When p_batt_forecast is at 0, should the battery be set to standby or self-consumption (Automatic)?

So you would set true below for a car charger to feed variable W into a car depending on what is available and then use the sensor.p_deferableX to vary the charge into the car?

- treat_deferrable_load_as_semi_cont: false # Poolpump
- treat_deferrable_load_as_semi_cont: false # Dishwasher
- treat_deferrable_load_as_semi_cont: false # Washing Machine and Dryer
- treat_deferrable_load_as_semi_cont: true  # Car

Correct.

I have true set for EV charging and HVAC and I then have automations that set the device to the respective power load required.

Advantages of doing either way.

Setting to self consumption means battery can ride the pressure and troughs as other devices come on line or clouds pass over.

Setting to 0 means things like EV charging won’t deplete your battery unless that is the cheaper way to do things.

If you are aiming for high frequency/ accuracy MPC then I would set to 0 and let EMHASS pick up the bumps.