EMHASS: An Energy Management for Home Assistant

Thanks, this is very cool, you mention the next 30 minutes ala Amber, just checking it supports 5 minutes ala LocalVolts?

Do you have EMHASS recalculate the “plan” every x minutes then, so it can take into account energy usage that changes the energy required to achieve the plan?

Can the “plan” be as simple as “add 6kw charge to the battery over the next 8 hours, while I sleep, as cheaply as possible” (it cant be perfect as the price is hard to predict but it could use NEM historical to maybe decide to go full bore between 2am and 4am unless there is a issue that causes a price spike)?

Sorry for all the questions, no doubt a lot of this has been covered somewhere in the 2520 posts here.

30 minutes because in general we have forecast data (PV production, energy prices, energy production prices, …) with a 30 minutes resolution. Using this EMHASS will build a forecast for the next hours (in general 24) with such resolution. If you have 5 minutes resolution data you can have a forecast aligned with that but all the data you feed to EMHASS need the same resolution.

EMHASS computation creates some sensors (see previous post) containing the trajectory you need to follow to match your goals. Each time you “publish” these sensors you are pushing to HA the value corresponding to this specific instant (or time delta: 5 minutes or 30 minutes or whatever). As I mentioned above you could even run EMHASS at high frequency to have fresh computation using almost instant values/creating almost instant forecasts.

This means you can’t just tell “add 6kwh over the next hours” but what will do is, according to your publish schedule/EMHASS elaboration”, use the p batt sensor, soc sensor - EMHASS generated sensors in general - to have your battery/grid import-export/… match that exact value and in this way replicate the overall EMHASS forecast.

1 Like

No problem,

It might be simplest if you load an instance of EMHASS locally so you can experiment.

Yes you can setup 5 minute intervals to match localvolts, or even AEMO 5 minute pricing.

I recalculate my optimisation plan every minute and the plan for the next 24 hours looks like this, so you can see the optimal charging and discharging schedule. You don’t say charge x amount in the next 8 hours, it actually determines the best time to charge and discharge over the next 24 hours to maximise your profit. You could always reduce this to the optimal over the next 8 hours, but 24 hours gives you a full solar cycle.

In the first chart you could see the initial values for now just before 8pm.

Here are the forecast sensors which my system is currently producing:
image

You can see the battery forecast is for 5,143 W which means the optimal solution is for my battery to discharge at this rate now, at the end of the period my battery SOC will be 30.65% it is currently 38%, so the automation command I send to my battery is to export at 5,143 W, then in the next minute it will recalculate, and then at 8pm you can see it wants to export at 13 kW as the price is going to be the highest then.

1 Like

Thanks, does EMHASS manage selling during spikes (or do I use HASS for that), this evening in NSW there were two massive 5 minute spikes, if my SoC was above 30% I would want to sell into that 5 minutes at full power?

EMHASS is very good at selling during spikes, it it sees a spike in the future it will charge your battery before the spike.

If it sees a spike in the now forecast it will start exporting after the next optimisation, which you would want to run on a high frequency, or even re-run an optimisation when a spike commences.

Depending on your battery you could be exporting within 10-30 seconds of an unexpected spike commencing. It will sell below 30% depending on what you set as your soc_min.

1 Like

Yes I run every 60 seconds. You can run at whatever frequency your CPU can handle I guess.

If you are with localvolts and don’t have any forecast you might want to put some effort into creating your own forecast.

There is a machine learning forecast function in EMHASS that I think was introduced to provides a more efficient way to forecast the power load consumption but perhps could be used to forecast FiT and ST if you have enough history to feed into it.

I think AEMO offers an API and downloadable datasets for more detailed analysis, which include forecast data for spot prices, demand, and other factors that influence feed-in and supply tariffs.

Rather than containing or restricting how EMHASS controls your battery it is easier to let it have it’s way. But you have full control over the data you feed into it and full control over what you do with the resultant output so you can ben it whichever way you want.

My plan for tomorrow (with the data I have at hand in this 60 second window) is:

I can switch between using p batt forecast (which is the green line in the lefthand chart) and the soc batt forecast (which is the orange line in the right graph). I can use the value of sensor.p_batt_forecast as input in a REST API POST as is directly to the internal inverter in the sonnen battery to command it to charge of discharge in accordance with the value of this sensor.

Or I can use the sensot.soc_batt_forecast and the actual state of charge of the battery to calculate how much power in Watts I need to feed into or take out of the battery to achieve the value of the sensor. Just a simple template in this case:

{%- set third_row = state_attr('sensor.soc_batt_forecast', 'battery_scheduled_soc')[0] -%}
{%- set soc_value = third_row['soc_batt_forecast']|float(0) -%}
{%- set raw_power = ((states('sensor.sonnenbatterie_84324_state_charge_user')|float(0) - soc_value) / 100 * 9300 / 0.5 / 0.9)|round(0) -%}
{%- set limited_power = min(max(raw_power, -3300), 3300) -%}
{{ limited_power }}

It’s all described in that document.

1 Like

Yes, you can see the spike this evening at about 6pm for about 30 minutes. Only went to $4 or so. But you can also see EMHASS decided to discharge the battery from 26% to 16%. I was using the SoC method so it was calculating what power rate to discharge the battery at to achieve a lower SoC and looks like it came up with 2000W. My battery is only capable of 3300W. It may have done better if I was using the p batt forecast value instead. It wasn’t charged up much due to the dismal weather and no forecast increase in FiT worth going to the expence of charging off the grid today.

1 Like

Here’s p batt and soc batt for same time period:

1 Like

EMHASS calculates very large p_batt, settings dictate 5000 W max, yet I get almost 19000 W and the calculation becomes infeasible, during the hour it changes into optimal, just wondering why this happens?

I find something similar if I specify an end_step constraint, are you using that constraint?

P_batt can have max 5kW charge and 5kW discharge, I added these numbers in the EHMASS config.

I encountered a problem when using EMHASS, which is that if I click the button "Show unused optional configuration options " and then click “save” when modifying the configuration, EMHASS will not start and can only be used again after uninstalling and reinstalling.

Hi all,

I have some problem. Since begin this month I changed my energy contract to dynamic, so I changed my configuration to that. But now I have a lot of times that my car isn’t charged in the morning, only something like 50-60%
someone know how to solve this problem?
This is my config:

MPC:

{
  "pv_power_forecast": {{
    ([states('sensor.solar_panel_production_w')|int(0)] +
    state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedForecast')|selectattr('period_start','gt',utcnow()) | map(attribute='pv_estimate')|map('multiply',1000)|map('int')|list +
    state_attr('sensor.solcast_pv_forecast_forecast_tomorrow', 'detailedForecast')|selectattr('period_start','gt',utcnow()) | map(attribute='pv_estimate')|map('multiply',1000)|map('int')|list
    )| tojson
  }},
  "load_power_forecast": {{
          ([states('sensor.power_load_no_var_loads')|int] +
          (states('input_text.fi_fo_buffer').split(', ')|map('multiply',1000)|map('int')|list)[1:]
          )| tojson
  }},
  "prediction_horizon": {{
    (24 if now().hour <= 15 else 48) - now().hour
  }},
  "load_cost_forecast": {{
    ((state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_today') |map(attribute='value') | list + state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_tomorrow') |map(attribute='value')|list)[now().hour:][:48]| tojson )
  }},
  "prod_price_forecast": {{
    ((state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_today') |map(attribute='value') | list + state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_tomorrow') |map(attribute='value')|list)[now().hour:][:48]| tojson )
  }},
  "num_def_loads": 1,
  "def_total_hours": [
    {% set hours = (5 - states('sensor.volvo_battery_charge_level')|float(0) / 20) * (states('sensor.laadpaal_status_2')|float(0))|int(0) %} {{- hours -}}
    ],
  "P_deferrable_nom": [6400],
  "treat_def_as_semi_cont": [0],
  "set_def_constant": [0],
  "def_start_timestep": {{ [states('sensor.def_0_start_timestep')|int(0)] }},
  "def_end_timestep": {{ [states('sensor.def_0_end_timestep')|int(0)] }},
  "soc_init": {{ (states('sensor.solaredge_b1_state_of_energy')|int(0))/100 }},
  "soc_final": 0.99,
  "alpha": 0.75,
  "beta": 0.25 
}

Day Ahead:

{
  "pv_power_forecast": {{
    ([states('sensor.solar_panel_production_w')|int(0)] + state_attr('sensor.solcast_pv_forecast_forecast_today', 'detailedForecast')|selectattr('period_start','gt',utcnow()) | map(attribute='pv_estimate')|map('multiply',1000)|map('int')|list + state_attr('sensor.solcast_pv_forecast_forecast_tomorrow', 'detailedForecast')|selectattr('period_start','gt',utcnow()) | map(attribute='pv_estimate')|map('multiply',1000)|map('int')|list)[:48]| tojson
  }},
  "load_cost_forecast": {{
    ((state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_today') |map(attribute='value') | list + state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_tomorrow') |map(attribute='value')|list)[now().hour:][:48]| tojson )
  }},
  "prod_price_forecast": {{
    ((state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_today') |map(attribute='value') | list + state_attr('sensor.nordpool_kwh_be_eur_3_10_0', 'raw_tomorrow') |map(attribute='value')|list)[now().hour:][:48]| tojson )
  }},
    "prediction_horizon": {{
     (24 if now().hour <= 15 else 48) - now().hour
  }}, 
}

def_0_start_timestep:

{% set ts = (((today_at(states('input_datetime.emhass_p_deferrable_0_start_time'))|as_timestamp - now()|as_timestamp)+900)/1800) | round(0) |  int(0) %}
      {% if states('input_datetime.emhass_p_deferrable_0_start_time') > states('input_datetime.emhass_p_deferrable_0_end_time') %}
        {% if now() < today_at(states('input_datetime.emhass_p_deferrable_0_end_time')) %}
          {% set ts = ts - 48 %}
        {% endif %}
      {% endif %} 
      {{ ts * is_state('input_boolean.emhass_p_deferrable_0_timestep','on') }}

def_0_end_timestep:

{% set ts = (((today_at(states('input_datetime.emhass_p_deferrable_0_end_time'))|as_timestamp - now()|as_timestamp) +900)/1800) | round(0) | int(0) %}
      {% if states('input_datetime.emhass_p_deferrable_0_start_time') > states('input_datetime.emhass_p_deferrable_0_end_time') %}
        {% if now() > today_at(states('input_datetime.emhass_p_deferrable_0_end_time')) %}
          {% set ts = ts + 48 %}
        {% endif %}
      {% endif %} 
      {{ ts * is_state('input_boolean.emhass_p_deferrable_0_timestep','on') }}

input_datetime.emhass_p_deferrable_0_start_time: is set on 18:00
input_datetime.emhass_p_deferrable_0_end_time: is set on 06:00

Hi! I’m facing issue with ML Forecast Tune.

This is my Rest command

  ml_forecast_model_tune:
    url: http://192.168.1.35:5001/action/forecast-model-tune
    method: POST
    content_type: 'application/json'
    payload: >-
      {
        "days_to_retrieve": 20,
        "model_type": "load_forecast",
        "var_model": "sensor.ss_load_power",
        "sklearn_model": "KNeighborsRegressor",
        "num_lags": 48,
        "split_date_delta": "48h",
        "perform_backtest": "True"
      }

I have 48 num_lags there but for some reason it outputs only 24 lags which makes my MPC command fail. If i do Model Fit again it fixes that.

Logs:

2024-08-27 21:25:13,462 - web_server - INFO - Passed runtime parameters: {'days_to_retrieve': 20, 'model_type': 'load_forecast', 'var_model': 'sensor.ss_load_power', 'sklearn_model': 'KNeighborsRegressor', 'num_lags': 48, 'split_date_delta': '48h', 'perform_backtest': 'True'}
2024-08-27 21:25:13,462 - web_server - INFO -  >> Setting input data dict
2024-08-27 21:25:13,463 - web_server - INFO - Setting up needed data
2024-08-27 21:25:13,474 - web_server - INFO - Retrieve hass get data method initiated...
2024-08-27 21:25:32,841 - web_server - INFO -  >> Performing a machine learning forecast model tune...
2024-08-27 21:25:32,843 - web_server - INFO - Bayesian hyperparameter optimization with backtesting

  0%|          | 0/10 [00:00<?, ?it/s]
Best trial: 0. Best value: 1.53433:   0%|          | 0/10 [00:00<?, ?it/s]
Best trial: 0. Best value: 1.53433:  10%|█         | 1/10 [00:00<00:01,  6.34it/s]
Best trial: 0. Best value: 1.53433:  20%|██        | 2/10 [00:00<00:00, 12.67it/s]
Best trial: 0. Best value: 1.53433:  20%|██        | 2/10 [00:00<00:00, 12.67it/s]
Best trial: 0. Best value: 1.53433:  30%|███       | 3/10 [00:00<00:00, 12.67it/s]
Best trial: 0. Best value: 1.53433:  40%|████      | 4/10 [00:00<00:00, 14.03it/s]
Best trial: 0. Best value: 1.53433:  40%|████      | 4/10 [00:00<00:00, 14.03it/s]
Best trial: 0. Best value: 1.53433:  50%|█████     | 5/10 [00:00<00:00, 14.03it/s]
Best trial: 0. Best value: 1.53433:  60%|██████    | 6/10 [00:00<00:00, 15.07it/s]
Best trial: 0. Best value: 1.53433:  60%|██████    | 6/10 [00:00<00:00, 15.07it/s]
Best trial: 0. Best value: 1.53433:  70%|███████   | 7/10 [00:00<00:00, 15.07it/s]
Best trial: 0. Best value: 1.53433:  80%|████████  | 8/10 [00:00<00:00, 15.27it/s]
Best trial: 0. Best value: 1.53433:  80%|████████  | 8/10 [00:00<00:00, 15.27it/s]
Best trial: 0. Best value: 1.53433:  90%|█████████ | 9/10 [00:00<00:00, 15.27it/s]
Best trial: 0. Best value: 1.53433: 100%|██████████| 10/10 [00:00<00:00, 15.17it/s]
Best trial: 0. Best value: 1.53433: 100%|██████████| 10/10 [00:00<00:00, 14.87it/s]
`Forecaster` refitted using the best-found lags and parameters, and the whole data set: 
  Lags: [ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] 
  Parameters: {'n_neighbors': 15, 'leaf_size': 26, 'weights': 'distance'}
  Backtesting metric: 1.5343300123407944
2024-08-27 21:25:33,556 - web_server - INFO - Elapsed time: 0.7120146751403809
2024-08-27 21:25:33,605 - web_server - INFO - R2 score for optimized prediction in train period: -1.5343300123407944
2024-08-27 21:25:33,609 - web_server - INFO - R2 score for optimized prediction in test period: -1.3719114544688713
2024-08-27 21:25:33,609 - web_server - INFO - Number of optimal lags obtained: 24

I have disabled the daily model fit for nor, but if I recall correctly, it used to work few months back.

Did you ever solve this? I’m getting the same error today, was working fine fine yesterday, and I didn’t change anything in between.

Problem solved seemingly after I set historic_days_to_retrieve to zero and back to 2. Or there was coincidentally something amiss in the historic sensor data from 48h before I tried that.


Has anyanyone has this were consumption and forcast doesnt meet? It doesnt update the emhass sensor to be my sensor value.

Currently its 4.5kw forcast but im consuming 7.4kw. Been like this for over an hour

This does happen often, check your logs and ensure your alpha and beta values are correct.

The load forecast also doesn’t include any deferrable loads that maybe activated at this time as well.

What is the value of your power_no_var_loads sensor?

I just came across this thread and I am full of admiration for the commitment to this project.
I am going to read it, there is a lot of it so I will ask a short question:
has anyone tried to control a Stiebel Eltron heater with this add-on, seven heating stages via modbus depending on the excess energy from PV?

https://www.stiebel-eltron.ch/de/home/produkte-loesungen/warmwasser/klein-_wand-_undstandspeicher/zubehoer/asko-pv-e-e/pv-e-e-3-5.html

I use EMHASS to control a basic Stiebel Electron to turn on the SG Ready signal line to increase my set point to 65°C.

With modbus control you would do a lot more.