EMHASS: An Energy Management for Home Assistant

You might find some inspiration here

1 Like

I have a comparable set-up, Huawei hybrid inverter + 15kWh Huawei storage. With the Huawei integration in HA and creating automations for the EMHASS calculations, it works nicely now. I also have dynamic tariff via Nordpool. Plus I work with pv_forecast from forecast.solar (paid subscription as I have 3 planes of solar panels). Maybe I can be of help for you.

1 Like

I am trying to reduce my processing time, with 5 minute intervals, 288 timesteps takes some time to process.

My concept is to once a day run dayahead optimisation and then every 5 minutes run MPC optimisation with a prediction horizon of say 1 hour (12x 5 minute timesteps), which I understand is how EMHASS is designed.

However, I initially run the initial dayahead optimisation and then run the MPC and it clobbers the dayahead SOC forecasts. Should I run dayahead with say the prefix ā€œdh_ā€ and then MPC with the prefix ā€œmpc_ā€ and then continue to set soc_init and soc_final for each MPC optimisations with reference to the SOC forecasts in the dh_ prefix?

Then I run the battery/ loads automation using the dh_ prefix entities, and display the combined dh_ and mpc_ forecasts for the forward view of what is planned to happen?

Hi,
can someone explain to me the time difference between the Nordpool data and that of EMHASS? EMHASS receives the data from Nordpool via shell command.
The Nordpool data is correct, but EMHASS displays it +1 hour later.
Is this related to winter time? (Berlin time zone)
How can I fix this?

Nordpool

...

- start: '2024-11-01T11:00:00+01:00'
  end: '2024-11-01T12:00:00+01:00'
  value: 0.264
- start: '2024-11-01T12:00:00+01:00'
  end: '2024-11-01T13:00:00+01:00'
  value: 0.261
- start: '2024-11-01T13:00:00+01:00'
  end: '2024-11-01T14:00:00+01:00'
  value: 0.268
...

Shell Command

  trigger_nordpool_forecast: 'curl -i -H "Content-Type: application/json" -X POST -d ''{"load_cost_forecast":{{((state_attr(''sensor.nordpool_price'', ''raw_today'') | map(attribute=''value'') | list  + state_attr(''sensor.nordpool_price'', ''raw_tomorrow'') | map(attribute=''value'') | list))[now().hour:][:24] }},"load_price_forecast":{{((state_attr(''sensor.nordpool_price'', ''raw_today'') | map(attribute=''value'') | list  + state_attr(''sensor.nordpool_price'', ''raw_tomorrow'') | map(attribute=''value'') | list))[now().hour:][:24]}}}'' http://localhost:5000/action/dayahead-optim'

Itā€™s not related to the winter/summer time.
Have you tried to change the EMHASS timestamp rounding?
Try with ā€œfirstā€.

1 Like

Thats it! :slight_smile: I had nearest and first solved it.

If you have nearest and it was associated to the next hour, it means you have 1h resolution and you are running the computation half past. Correct?
I suggest you try to move the computation immediately after the hour, so you have fresh data you can use during that timeslot.

My time resolution is currently 1 hour, as we have hourly electricity prices.
I have triggered the day-ahead optimization via automation at 8 p.m. and am currently setting it manually for testing.
When is the best time for the day-ahead optimization? Do you only run it once a day?

day-ahead is intended to be run once per day and when you run it is a bit irrelevant; ideally at midnight but as it covers 24h you can run it whenever you want.
If you want to perform intermediate optimizations you should go for mpc.
I run mpc only but this is a personal choice.

Yes thatā€™s exactly correct, you need to use custom prefix otherwise the forecasts sensors obtained with day-ahead will be overwritten by those of the MPC run. Then send the ā€œdh_ā€ soc_init and soc_final to each MPC call.

No, you should then manage your automations using the mpc_ prefix entities, which are the forecasts updated with your precise short term forecasts inputs (and even your current/now values).

3 Likes

With historic_days_to_retrieve, how this function affect the model?
If set to 2 days, does it retrieve last 48 power consumption and average it over 48hours to project to next 24h?

What is dayahead optimization then used for?

This approach:

https://emhass.readthedocs.io/en/latest/lpems.html

So actually there can be two different approaches when using MPC:

  • Receiding horizon: in this case the prediction horizon is a fixed value and MPC is a sliding window of always the same size. This is the case that I wont recommend because then like I said before you ā€œlooseā€ the daily optimization notion. For example, for me the total operating hours for deferrable loads are bounded to daily objectives, so a receding approach is not much compatible with this.

  • Shrinking horizon: in this case the prediction horizon is a variable value that shrinks during the day, as depicted in the previous figure. With this approach you keep you daily optimization objectives.

This is just my vision of this, open to discussion, and like Iā€™ve said the receding approach is also a valid one, but for me it make much more sense to use a shrinking approach. Iā€™ve now updated a bit the documentation to reflect this.

But it is possible to not using the dayahead at all?
I have a timestep of 30min and doing a MPC optimisation every 5min.
Iā€™m using a shrinking prediction horizon based on the (half) hour of the day, on the length of my sensor.pv_power_forecast_list and the possibility of a longer endstep for one of my defferable loads

    - name: Prediction horizon
      unique_id: 39144142-06b5-4c2a-897a-d24b0c8fd2e4
      unit_of_measurement: 1/2u
      availability: >
        {{ state_attr('sensor.pv_power_forecast_list', 'list') | length | is_number and 
            states("sensor.wasmachien_end_timesteps") | is_number and 
            states("sensor.droogkast_end_timesteps") | is_number and 
            states("sensor.afwasmachien_end_timesteps") | is_number }}
      state: >
        {% macro max(X, Y) -%} {{X|int(0) if X|int(0) > Y|int(0) else Y|int(0) }} {%- endmacro %}
        {% macro min(X, Y) -%} {{X|int(0) if X|int(0) < Y|int(0) else Y|int(0) }} {%- endmacro %}
        {% set hour = now().hour %}
        {% set minutes = now().minute %}
        {% if state_attr('sensor.pv_power_forecast_list', 'list') is iterable %}
          {% set solar = state_attr('sensor.pv_power_forecast_list', 'list') | length %}
        {% else %}
          {% set solar = 48 %}
        {% endif %}
        {% set endstep = max(states('sensor.wasmachien_end_timesteps'), states('sensor.droogkast_end_timesteps')) %}
        {% set endstep = max(endstep, states('sensor.afwasmachien_end_timesteps')) %}

        {% if hour == 7 %}
          {{ min(48, max(endstep, min(solar, 34))) if minutes < 30 else min(48, max(endstep, min(solar, 33))) }}
        {% elif hour == 8 %}
          {{ min(48, max(endstep, min(solar, 32))) if minutes < 30 else min(48, max(endstep, min(solar, 31))) }}
        {% elif hour == 9 %}
          {{ min(48, max(endstep, min(solar, 30))) if minutes < 30 else min(48, max(endstep, min(solar, 29))) }}
        {% elif hour == 10 %}
          {{ min(48, max(endstep, min(solar, 28))) if minutes < 30 else min(48, max(endstep, min(solar, 27))) }}
        {% elif hour == 11 %}
          {{ min(48, max(endstep, min(solar, 26))) if minutes < 30 else min(48, max(endstep, min(solar, 25))) }}
        {% elif hour == 12 %}
          {{ min(48, max(endstep, min(solar, 24))) if minutes < 30 else min(48, max(endstep, min(solar, 23))) }}
        {% elif hour == 13 %}
          {{ min(48, max(endstep, min(solar, 22))) if minutes < 30 else min(48, max(endstep, min(solar, 21))) }}
        {% elif hour == 14 %}
          {{ min(48, max(endstep, min(solar, 20))) if minutes < 30 else min(48, max(endstep, min(solar, 19))) }}
        {% elif hour == 15 %}
          {{ min(48, max(endstep, min(solar, 18))) if minutes < 30 else min(48, max(endstep, min(solar, 17))) }}
        {% elif hour == 16 %}
          {{ min(48, max(endstep, min(solar, 16))) if minutes < 30 else min(48, max(endstep, min(solar, 15))) }}
        {% elif hour == 17 %}
          {{ min(48, max(endstep, min(solar, 14))) if minutes < 30 else min(48, max(endstep, min(solar, 13))) }}
        {% elif hour == 18 %}
          {{ min(48, max(endstep, min(solar, 12))) if minutes < 30 else min(48, max(endstep, min(solar, 11))) }}
        {% elif hour == 19 %}
          {{ min(48, max(endstep, min(solar, 10))) if minutes < 30 else min(48, max(endstep, min(solar, 9))) }}
        {% else %}
          {{ min(48, max(endstep, min(solar, 36))) }}
        {% endif %}

SchermĀ­afbeelding 2024-11-04 om 10.35.17

If you donā€™t have a battery and you donā€™t care about daily objectives for your deferrable loads then yes, itā€™s ok for me to just use MPC.
Otherwise the combined day-ahead and MPC should be the most optimal solution.

Iā€™m planning to buy a battery, so can you show an example how to combine those 2 optimizations?

I have a battery (two actually but thatā€™s a long story :') ) and I use MPC receding horizon only. With this approach I think Iā€™m focusing on the short-mid term and less on the long term.
Iā€™m not really interested in cycling my batteries 30%-30% (for example) over the day.
I personally prefer to charge them as much as possible and then buy (over the night) only when the next hours are not covered by my storage.
Thatā€™s why out of the MPC computation I only take when PV = 0 and battery should charge (or should be idle - in general at night, when itā€™s the same directly using power from the grid as using it to charge the battery); for the rest they are managed by the internal logic.

I have to say at the moment I do not have deferrable loads to control and it would be interesting to have something like 48h forecast (data availability is the problem here I think). Under this scenario my considerations about day-ahead approach for my needs might change.

1 Like

Following up on my own post. It was indeed a time zone issue where I had accidentally typed quotation marks around the environment variable in the docker compose file. Dā€™oh.

Yesterday I upgraded and restarted Home Assistant, somehow crippling EMHASS by not providing enough history for the configured sensors anymore.

I wanted to investigate, but it seems the HA API doesnā€™t return anything for specific ranges in history.

Thereā€™s an issue for HA Core, but unfortunately thereā€™s little progress.

Wouldnā€™t it be good to have this issue tracked in the EMHASS github as well?

I think it should not, unless itā€™s a problem generated by EMHASS.