EMHASS: An Energy Management for Home Assistant

For some context, ‘normal’ prod_price is around 0.05 $/ kWh.

We are in the midst of a price ‘spike’ and EMHASS doesn’t want to sell now for 17.497 $/ kWh because it wants to wait for later when the forecast is to sell for $18.073 and $17.699. Mathematically it is correct.

However, these are forecasts and subject to change. So how can I weight EMHASS to act on almost high enough prices to sell now, rather than waiting for a future forecast?

I’ve been having the same issue (except with 30 min blocks), and it’s not exactly what you’ve asked but here’s my latest approach.

I’m not sure which advanced amber price you’ve been using, but my approach has been to to use the advanced_low for the next 6 blocks, then go for the advanced_predicted for there onwards.
Tonight during the spike it seemed to have discharged slightly earlier than it would have if I was just using advanced_predicted. I’m sure i’ve left some money on the table, but it was much better than my previous approach of using a single price.

But i’m with you, it’d be great to have a “prioritise/weight selling sooner than later” option somehow.

 "prod_price_forecast":
        {{ ([states("sensor.amber_feed_in_price")|float(0)]
        + (state_attr("sensor.amber_feed_in_forecast", "forecasts")|map(attribute="advanced_price_low")|list)[:5]
        + (state_attr("sensor.amber_feed_in_forecast", "forecasts")|map(attribute="advanced_price_predicted")|list)[5:])[:48]
        }},
1 Like

Creating the values for “load cost forecat” from Entsoe data as follows:
“load_cost_forecast”:{{((state_attr(‘sensor.entso_energy_average_electricity_price_today’,
‘prices’) | map(attribute=‘price’) | list))[now().hour:][:24] }}

With dynamic pricing of energy, some companies define the cost for returned energy as a value which is a fixed value less than the “load cost” price.
So:
I want to create the list for “prod_price_forecast” in the same way, but subtracting a fixed value from the “load_cost_forecast” list.
I can not find a way to accomplish this.
Note: This could also be a configuration item!!

It has been discussed:

interesting to see you’re getting ready for 5 min windows. at the moment, are you slicing some of the 30 mins blocks in 6 smaller blocks?
if so do you have the code on how you’re cutting up the different inputs to the MPC?

My plan for Amber is to run day-ahead for the full 24 hour schedule at 30 minute resolution and then run MPC on the 5 minute intervals data for the next 60-120 minutes.

The charts above are from a LocalVolts account (that I’m helping setup EMHASS) that has 288x 5 minute intervals for the full 24 hours, but as you have noticed the LV forecasts are just the 30 minute forecast repeated 6 times. LV doesn’t have the Amber Advanced Price Forecast and that really makes a difference to the level of trust you can put into the forecast. Last night as you saw from my chart, it would say a $17+ spike in the next 5 minute forecast, but the spike didn’t come for over an hour.

Optimisations for 5 minutes is going to be challenging with EMHASS. Say you get the confirmed price after 20-30 seconds. On a Home Assistant green (under powered) it can take up to a minute to process 288x 5 minute intervals so you don’t start charging or discharging until 1.30 into a 5 minute interval. Obviously more processing CPU would help, but I’m also thinking of just processing with MPC the next 6-12 x 5 minute intervals would also greatly reduced optimisation time, with little effect on outcomes.

yeah, i hear what you’re saying about the race to compute a result in a 5 min window

Thanks! That was helpfull.

I currently have an issue where the EMHASS add-on starts, but refuses to keep running.
It simply stops running, the (debug) logs are not helpful:

INFO - web_server - Launching the emhass webserver at: http://0.0.0.0:5000
INFO - web_server - Home Assistant data fetch will be performed using url: http://supervisor/core/api
INFO - web_server - The data path is: /share
INFO - web_server - The logging is: DEBUG
INFO - web_server - Using core emhass version: 0.11.2

It’s not triggered by a call to the REST API, (but those fail ofc.)
I already deleted all saved files from the add-on folder. To no avail.
I think it fails to aqcuire some data via the supervisor-api. But I don’t know why that happens.

Unsure what made the difference, but I was able to clear the box in the Web UI and was able to perform a perfect optimization again, followed by a Dayahead and MPC again.

passing max power from grid as a runtime parameter for day-ahead and mpc?

Here in Belgium you get punished with a capacity tariff which is the highest 15min average power drawn from grid in a month. (something like every 1kw over 2,5kw costst you 40€/year on top of your energy costs). When running the optimizer, day ahead or mpc it is important you stay under a certain preset limit. As my ambiton might change over time I have a input_number slider for that in home assistant (next to reading the actual value from the p1 meter).
Is there a way to pass the " maximum_power_from_grid" configuration setting as a runtime parameter? Or use a template in the Configuration like in the screenshot below perhaps?

I do not see it listed in the docs.

You are on the right track with the input slider, but it doesn’t go in your configuration file, you need to put it into your REST command or shell script when you can the optimisation.

Blockquote
you need to put it into your REST command or shell script when you can the optimisation

I know, I use a templated rest api call to configure my deferable loads from the home assistant UI, but since the " maximum_power_from_grid" parameter is’t listed in the " Passing other data at runtime section of the doc’s I didn’t think it was possible. That is why I asked

1 Like

I think this is like a demand tariff we get on the Ausgrid grid (NSW Australia).

From Novermber to March and June to August we are charge an extra tariff calculated by measurung the highest 30-minute period of consumption between 15:00 to 21:00.

What some of us do is increase the supply price atificially to $1 so that the MPC calculation avoids charging or running deferrable loads during that window.

This is how I do it:

"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))
    | tojson 
  }},
  {%- set current_month = now().month %}
  {%- if (3 <= current_month <= 8) or (11 <= current_month <= 12) %}
  "load_cost_forecast": {%- set current_time = now() %}
  {%- set demand_tariff_start = "15:00:00" %}
  {%- set demand_tariff_end = "21:00:00" %}

  {%- set start_time = current_time.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(hours=(demand_tariff_start.split(":")[0]|int), minutes=(demand_tariff_start.split(":")[1]|int)) %}
  {%- set end_time = current_time.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(hours=(demand_tariff_end.split(":")[0]|int), minutes=(demand_tariff_end.split(":")[1]|int)) %}

  {%- if end_time <= start_time %}
    {%- set end_time = end_time + timedelta(days=1) %}
  {%- endif %}

  {%- set values = ([states("sensor.cecil_st_general_price")|float(0)] + state_attr("sensor.cecil_st_general_forecast", "forecasts")|map(attribute="per_kwh")|list) %}
  {%- set ns = namespace(x=[]) %}

  {%- for i in range(values|length) %}
    {%- set future_time = current_time + timedelta(minutes=i * 30) %}
    {%- if start_time <= future_time < end_time and values[i] < 1 %}
      {%- set ns.x = ns.x + [1.0] %}
    {%- else %}
      {%- set ns.x = ns.x + [values[i]] %}
    {%- endif %}
  {%- endfor %}

  {{- ns.x | tojson }},

  {%- else %}

I think this is a new feature with the 0.11.x releases that you can now include any parameter on the command line, which is really useful, but I don’t think it is actually documented.

@GeoDerp can you confirm?

1 Like

Ah that might be a way to bend the power curve to a more realistic shape. After 90% charge start to reduce battery_charge_power_max down to match what the battery will actuall charge at. In my case after 90% is starts to drop from 3300W until it get to 95 where it will only charge at 850W. After 95 it will only periodically charge on and off at about 850W.

I have a question about day-ahead optimization followed by 5 min MPC intervals with shrinking prediction 30min horizon.

I run the day-ahead optimization at 0:01 every night, that works fine. Then MPC takes over. Initially I have a prediction horizon of 48 (30 min intervals), which declines over time (every 30 min), however when the prediction horizon reaches 5, EMHASS will generate an error because of too low prediction horizon.

Probably I implemented it wrongly?

@rcruikshank thanks for the tip but I can’t use a time/tariff based hack. Capacity tariff fees here are 24/7. Even when energy prices drop below zero because of renewable overproduction you are still punished for consuming to much :roll_eyes:. This was set up to battle grid congestion in neighborhoods to stop everyone from consuming max when prices are low. So 2 opposing price triggers at work at the same time, makes it an interesting optimization challenge here in the Flemish part of Belgium (the French speaking part is not on board with dynamic pricing yet)

1 Like

I just tried and can confirm the paramter can indeed be set at runtime. This opens up the possibility to create a proper UI interface in HA to set al the runtime parameter.

2024-11-20 10:50:06,461 - web_server - INFO - Passed runtime parameters: {'load_cost_forecast': [0.20455020000000002, 0.1943848, 0.18929680000000002, 0.1837742, 0.1824174, 0.19351559999999998, 0.2097442, 0.2321632, 0.24872039999999998, 0.24557220000000002, 0.22237939999999998, 0.2117582, 0.2008932, 0.20099920000000002, 0.21006219999999998, 0.23128340000000003, 0.2389472, 0.2479572, 0.24864619999999998, 0.2467064, 0.241417, 0.2300644, 0.2218282, 0.2069882, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 'prod_price_forecast': [0.08814999999999999, 0.07856, 0.07376, 0.06855, 0.06727, 0.07774, 0.09305, 0.1142, 0.12982, 0.12685000000000002, 0.10497, 0.09494999999999999, 0.08470000000000001, 0.0848, 0.09335, 0.11337, 0.1206, 0.1291, 0.12975, 0.12792, 0.12293, 0.11222, 0.10445, 0.09045, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 'num_def_loads': 1, 'P_deferrable_nom': [400], 'def_total_hours': [1], 'treat_def_as_semi_cont': [True], 'set_def_constant': [True], 'maximum_power_from_grid': [3000]}

This is how my rest command looks like now:

  emhass_rest_daily_sec_api:
    url: "http://localhost:5000/action/dayahead-optim"
    method: post
    content_type: "application/json"
    payload: >
      {% set prices_afname = state_attr('sensor.sec_current_contract', 'prices_30_afname')['prices_next24h'] %}
      {% set prices_injectie = state_attr('sensor.sec_current_contract', 'prices_30_injectie')['prices_next24h'] %}
      {
        "load_cost_forecast": [
          {% for price in prices_afname %}
            {{ price['price'] }}{{ "," if not loop.last else "" }}
          {% endfor %}
        ],
        "prod_price_forecast": [
          {% for price in prices_injectie %}
            {{ price['price'] }}{{ "," if not loop.last else "" }}
          {% endfor %}
        ],
        "num_def_loads": 1,
        "P_deferrable_nom": [{{states('input_number.sec_pac_def_load_1_power') | int }}],
        "def_total_hours": [{{states('input_number.sec_pac_def_load_1_duur') | int }}],
        "treat_def_as_semi_cont": [{{ 'true' if is_state('input_boolean.continu', 'on') else 'false' }}],
        "set_def_constant": [{{ 'true' if is_state('input_boolean.fixed', 'on') else 'false' }}],
        "maximum_power_from_grid": [{{states('input_number.sec_pac_doel_limiet_capaciteitspiek') | int }}]
      }
    headers:
      Content-Type: application/json

2 Likes

Another possibility for enhanced user interface is the use of variables: and fields: in scripts and automations, which has been highlighted here:

This has the distinct advantage of not having to reboot HA after making changes to the a REST payload:

rest_command:
  dayahead_optim_rest:
    url: http://localhost:5000/action/dayahead-optim
    method: POST
    content_type: "application/json"
    timeout: 120
    payload: "{{ payload }}"
  mpc_servicecall_rest:
    url: http://localhost:5000/action/naive-mpc-optim
    method: POST
    content_type: "application/json"
    timeout: 120
    payload: "{{ payload }}"
alias: EMHASS Payload Script
sequence:
  - stop: end
    response_variable: payload
    enabled: false
  - action: rest_command.mpc_servicecall_rest
    metadata: {}
    data:
      payload: |
        {{ payload}}
  - action: rest_command.publish_data
    metadata: {}
    data: {}
variables:
  payload: |-
    {
      "prod_price_forecast": {{
        ([states('sensor.amber_feed_in_price')|float(0)] +
        (state_attr('sensor.amber_feed_in_forecast', 'forecasts')|map(attribute='per_kwh')|list)) 
      }},
      "load_cost_forecast": {{
        ([states('sensor.amber_general_price')|float(0)] + 
        state_attr('sensor.amber_general_forecast', 'forecasts') |map(attribute='per_kwh')|list) 
        | tojson 
      }},
      "prediction_horizon": {{
          (state_attr('sensor.amber_feed_in_forecast', 'forecasts')|map(attribute='per_kwh')|list|length)+1
      }},
      "alpha": 1,
      "beta": 0,
      "num_def_loads": 6,
      "def_total_hours": {{[states('sensor.def_total_hours_pool_filter')|int(0),
                              states('sensor.def_total_hours_pool_heatpump')|int(0),
                              states('sensor.def_total_hours_ev')|int(0),
                              states('sensor.def_total_hours_hvac')|int(0),
                              states('sensor.def_total_hours_hws')|int(0),
                              states('sensor.def_total_hours_ev2')|int(0)]}},
      "def_end_timestep":[0, 0, 0, 0, 0, 0],
      "P_deferrable_nom":  [300,
                             6333, 
                            {{ states('sensor.p_nom_ev')}}, 
                           {{ states('input_number.p_nom_hvac')}}, 
                            600, 
                            {{ states('sensor.p_nom_ev2')}}],
      "treat_def_as_semi_cont": [1, 1, 0, 0, 1, 0],
      "set_def_constant": [0, 0, 0, 0, 0, 0],
      "def_start_penalty": [1, 1, 1, 1, 1, 1],
      "weight_battery_charge": 0.2,
      "soc_init": {{ max(0,states('sensor.energy_site_percentage_charged')|int(0))/100 }}
    }
mode: single
fields: {}

1 Like

Having the whole payload as a template makes sense indeed.
Templating the different parameters like I did doesn’t require a HA yaml reload/reboot either when the values change (I have all the variables and rest commands in a yaml package) . But when you want to add additional parameters on the fly payload: “{{ payload }}” is indeed the better option. Trying to find some time to work on this. Would be nice to have a emhass.yaml package file to combine everything emhass related. Combine that with a lovelace UI yaml dashboard file (sliders, settings, trigger buttons, apexcharts, etc) and we get close to a userfriendly HA emhass integration.

3 Likes