EMHASS: An Energy Management for Home Assistant

Where to find that specific share folder to find ML forecaster pkl file? My share folder is empty? I use the add-on

Hello. I use a hybrid inverter. How i solved this is setting amp charge to battery to what emhass recommends when negative grid forecast. If else grid forecast is 0 i just det my soc to 30 and let the inverter charge the battery full :blush: it works great and almost follows emhass perfectly.

What im missing in home assistant is not positive grid and negative grid sensor to make this automation even better.

Also have a check if there is positive grid then i set charge amps to what emhass recommends and it will buy from grid if electricity is cheap.

Are you able to share an example of your automations?

Is there a possibility to add the option to extend the prediction horizon from 24h to 48h?

Are the people who control the heating/cooling based on the energy price and can provide me a example?

Would be great if I can control the in house temperature when the energy price is low or expensive.

Here is the details of how I’m doing cooling during the day over summer.

Cooling over summer is easier as the hottest part of the day and the cheapest energy costs are aligned. So I set def_total_hours_hvac to match the number of hours each day that exceed my desired setpoint.

Winter heating overnight is more complex as the demand generally doesn’t sign with the cheapest cost of energy. I’m still within up this concept but with the new end_timestep option I am planning on calculating the number of hours overnight that the temp forecast is below my setpoint and then setting the def_end_timestep_hvac to force the HVAC scheduling before the time the temp forecast crosses my setpoint threshold.

The other method I have used for overnight heating which has been quite effective if your heating demand consistent is is to not bother with scheduling my HVAC with EMHASS and just letting the HVAC power consumption accumulate in the load forecast each day as part of power_load_no_vars. This later approach has the disadvantage that your HVAC consumption isn’t modulated by cost variations throughout the night.

Just upgraded HA to 2024.6.0b0 and EMHASS has started failing for me, proceed with caution:

2024-05-30 07:18:00,259 - web_server - INFO - Passed runtime parameters: {'load_cost_forecast': [0.24, 0.27, 0.15, 0.13, 0.12, 0.11, 0.11, 0.1, 0.08, 0.06, 0.05, 0.05, 0.05, 0.1, 0.11, 0.12, 0.12, 0.13, 0.29, 0.34, 0.35, 0.35, 0.35, 0.35, 0.33, 0.32, 0.32, 0.29, 0.17, 0.15, 0.17, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.14, 0.14, 0.14, 0.14], 'prod_price_forecast': [0.15, 0.18, 0.06, 0.04, 0.04, 0.04, 0.03, 0.02, 0, -0.01, -0.02, -0.02, -0.02, 0.03, 0.04, 0.04, 0.04, 0.05, 0.06, 0.12, 0.12, 0.12, 0.12, 0.12, 0.11, 0.1, 0.1, 0.07, 0.08, 0.07, 0.08, 0.06, 0.07, 0.07, 0.06, 0.06, 0.06, 0.06, 0.06, 0.05, 0.05, 0.05], 'pv_power_forecast': [841], 'load_power_forecast': [1091, 1500, 1600, 1800, 2100, 1200, 1800, 3100, 700, 900, 800, 1000, 300, 1300, 600, 300, 400, 300, 1100, 1300, 1100, 1100, 1000, 600, 500, 400, 500, 1100, 400, 600, 300, 300, 400, 300, 400, 400, 400, 300, 300, 300, 300, 400, 300, 300, 700, 700, 800, 1100, 1700, 1500, 1600, 1800, 2100, 1200, 1800, 3100, 700, 900, 800, 1000, 300, 1300, 600, 300, 400, 300, 1100, 1300, 1100, 1100, 1000, 600, 500, 400, 500, 1100, 400, 600, 300, 300, 400, 300, 400, 400, 400, 300, 300, 300, 300, 400, 300, 300, 700, 700, 800, 1100], 'prediction_horizon': 42, 'alpha': 1, 'beta': 0, 'num_def_loads': 6, 'def_total_hours': [0, 2, 1, 2, 6, 1], 'def_end_timestep': [0, 0, 17, 0, 0, 17], 'P_deferrable_nom': [1232, 6333, 11520, 2500, 600, 11520], 'treat_def_as_semi_cont': [1, 1, 0, 0, 1, 0], 'set_def_constant': [0, 0, 0, 0, 0, 0], 'soc_init': 0.31, 'soc_final': 0}
2024-05-30 07:18:00,259 - web_server - INFO -  >> Setting input data dict
2024-05-30 07:18:00,259 - web_server - INFO - Setting up needed data
2024-05-30 07:18:00,262 - web_server - ERROR - ERROR: The passed data is either not a list or the length is not correct, length should be 42
2024-05-30 07:18:00,262 - web_server - ERROR - Passed type is <class 'list'> and length is 1
2024-05-30 07:18:00,266 - web_server - INFO - Retrieve hass get data method initiated...
2024-05-30 07:18:00,940 - web_server - INFO - Retrieving weather forecast data using method = scrapper
2024-05-30 07:18:03,511 - web_server - ERROR - Exception on /action/naive-mpc-optim [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 1473, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 882, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 880, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/flask/app.py", line 865, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/emhass/web_server.py", line 109, in action_call
    input_data_dict = set_input_data_dict(emhass_conf, costfun,
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/emhass/command_line.py", line 142, in set_input_data_dict
    df_weather = fcst.get_weather_forecast(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/emhass/forecast.py", line 213, in get_weather_forecast
    raw_data.set_index(forecast_dates_scrap, inplace=True)
  File "/usr/local/lib/python3.11/dist-packages/pandas/core/frame.py", line 5910, in set_index
    raise ValueError(
ValueError: Length mismatch: Expected 24 rows, received array of length 48

It seems like a problem with your data list. The passed length is 1 but expected 42

V
Thanks,

I am utilising the SolCast integration to deliver pv_power_forecast’: [841], which is external to EMHASS and appears broken with the HA beta.

Confirmed upstream SolCast issue, which is also likely to effect those using the EMHASS native SolCast polling:

Update: SolCast is backup and so is my EMHASS

Hi team,

Question:

If a optim state is unfeasible - does that mean the data generated in the webpage useless? Can it still be used to control battery/deferrable loads?

For example:

It’s a cloudy/rainy day but we still need the HWS system to run for at least 4-5hours. The state is unfeasible. However it still would be good for EMHASS to run the HWS during the cheapest forecasted times.

Should I let the system run in an unfeasible state or change the costfun option from profit to ____?

/edit - The unfeasible state ignores the semi-continous nature of the load (3.6kW for HWS), so how should I approach this problem?

Thanks!

Just because it is cloudy shouldn’t automatically result in an infeasible solution as EMHASS should schedule additional consumption from the grid to cover this usage.

You can see here deferrable0 and deferrable1 still being scheduled (2x Hot Water Systems) even though the solar production is insufficient to cover. Do you have other constraints?

I don’t allow Home Assistant to make any decisions based on my optimisations if the state = unfeasible, as the plans I’ve observed are wildly beyond what was possible (i.e. charge rates beyond what my system can handle etc). Instead I have a default set of automations that I apply based on what’s worked before I had EMHASS.
I’ve found that most of the times where i get an unfeasible outcome is if my battery SOC at the time of running the optimisation is below the battery_minimum_state_of_charge setting. Once I stopped the battery getting below that then my future optimisations were Optimal

I’ve also only get “unfeasible” when SoC is lower than minimum SoC. However, I’ve noticed EMHASS immediately charges the battery until the minimum SoC and then it is no longer “infeasible”

1 Like

For the life of me I have not been able to get my config working, I have started with just one load and every result is not feasible due to the window.

i am happy for my loads to be open ended outside of my peak window.

logging_level: DEBUG
data_path: default
costfun: profit
sensor_power_photovoltaics: sensor.solar_energy_v2
sensor_power_load_no_var_loads: sensor.home_load_power
set_total_pv_sell: false
set_nocharge_from_grid: false
set_nodischarge_to_grid: false
maximum_power_from_grid: 35000
maximum_power_to_grid: 15000
number_of_deferrable_loads: 1
list_nominal_power_of_deferrable_loads:
  - nominal_power_of_deferrable_loads: 1800
list_operating_hours_of_each_deferrable_load:
  - operating_hours_of_each_deferrable_load: 6
list_start_timesteps_of_each_deferrable_load:
  - start_timesteps_of_each_deferrable_load: 0
list_end_timesteps_of_each_deferrable_load:
  - end_timesteps_of_each_deferrable_load: 0
list_peak_hours_periods_start_hours:
  - peak_hours_periods_start_hours: "16:54"
list_peak_hours_periods_end_hours:
  - peak_hours_periods_end_hours: "20:54"
list_treat_deferrable_load_as_semi_cont:
  - treat_deferrable_load_as_semi_cont: true
list_set_deferrable_load_single_constant:
  - set_deferrable_load_single_constant: false
load_peak_hours_cost: 0
load_offpeak_hours_cost: 0
photovoltaic_production_sell_price: 0
list_pv_module_model: []
list_pv_inverter_model: []
list_surface_tilt: []
list_surface_azimuth: []
list_modules_per_string: []
list_strings_per_inverter: []
set_use_battery: true
battery_nominal_energy_capacity: 13500
list_priority_of_deferrable_loads:
  - priority_of_deferrable_loads: 1
optimization_time_step: 15
battery_maximum_state_of_charge: 1
battery_target_state_of_charge: 0.5
battery_discharge_efficiency: 0.9
battery_charge_efficiency: 0.9
battery_charge_power_max: 5000
battery_discharge_power_max: 5000
set_battery_dynamic: true
battery_dynamic_max: 0.7
battery_dynamic_min: -0.7
battery_minimum_state_of_charge: 0.2
solcast_rooftop_id: XXXX
solcast_api_key: XXXX
production_price_forecast_method: constant
load_forecast_method: naive
weather_forecast_method: solcast
prediction_horizon: 24
method_ts_round: first

doing the following MPC call for Amber prices

    curl -i -H "Content-Type: application/json" -X POST -d '{
      "load_cost_forecast": {{ (
        [states('sensor.general_price')|float(0)] +
        state_attr('sensor.general_forecast', 'forecasts')|map(attribute='per_kwh')|list
      )|list }},
      "prod_price_forecast": {{ (
        [states('sensor.feed_in_price')|float(0)] +
        state_attr('sensor.feed_in_forecast', 'forecasts')|map(attribute='per_kwh')|list
      )|list }},
      "pv_power_forecast": {{ (state_attr('sensor.solcast_24hrs_forecast', 'forecasts') or "0,0,0,0").split(',') | map('int') | list }},
      "prediction_horizon": 24,
      "soc_init": {{ (states('sensor.powerwall_charge')|float(0)) / 100 }},
      "soc_final": 0.20,
      "def_total_hours": [6]  # Adjust to match the configuration
    }' http://localhost:5000/action/naive-mpc-optim

The Logs

2024-05-31 15:53:56,878 - web_server - INFO - Passed runtime parameters: {‘load_cost_forecast’: [0.15, 0.19, 0.22, 0.43, 0.37, 0.69, 0.68, 0.73, 0.73, 0.68, 0.71, 0.8, 0.75, 0.5, 0.57, 0.48, 0.36, 0.36, 0.36, 0.36, 0.31, 0.27, 0.44, 0.39, 0.36, 0.32, 0.27, 0.26, 0.26, 0.26, 0.29, 0.3, 0.36, 0.59, 0.82, 0.46, 0.4, 0.23, 0.22, 0.13, 0.12, 0.14, 0.09, 0.09, 0.09, 0.09, 0.09, 0.14], ‘prod_price_forecast’: [0.05, 0.09, 0.11, 0.26, 0.2, 0.28, 0.27, 0.32, 0.32, 0.27, 0.3, 0.39, 0.34, 0.32, 0.38, 0.3, 0.19, 0.19, 0.19, 0.19, 0.15, 0.11, 0.26, 0.22, 0.19, 0.15, 0.11, 0.1, 0.1, 0.1, 0.13, 0.14, 0.19, 0.4, 0.61, 0.28, 0.23, 0.08, 0.06, 0.03, 0.03, 0.04, -0.0, -0.0, -0.0, -0.0, -0.0, 0.05], ‘pv_power_forecast’: [0.9197, 0.3969, 0.7298, 0.5452, 0.2939, 0.0066, 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.0492, 1.6617, 3.1556, 4.4012, 5.5342, 6.3928, 7.0159, 7.6574, 7.9615, 8.0619, 7.7789, 7.1398, 6.4503, 5.6678, 4.9008, 4.0576], ‘prediction_horizon’: 24, ‘soc_init’: 1.0, ‘soc_final’: 0.2, ‘def_total_hours’: [24], ‘def_load_start’: [0], ‘def_load_end’: [96]}
2024-05-31 15:53:56,878 - web_server - INFO - >> Setting input data dict
2024-05-31 15:53:56,878 - web_server - INFO - Setting up needed data
2024-05-31 15:53:56,882 - web_server - INFO - Retrieve hass get data method initiated…
2024-05-31 15:53:57,349 - web_server - INFO - Retrieving weather forecast data using method = list
2024-05-31 15:53:57,350 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2024-05-31 15:53:57,350 - web_server - INFO - Retrieve hass get data method initiated…
2024-05-31 15:53:57,666 - web_server - INFO - >> Performing naive MPC optimization…
2024-05-31 15:53:57,666 - web_server - INFO - Performing naive MPC optimization
2024-05-31 15:53:57,674 - web_server - INFO - Perform an iteration of a naive MPC controller
2024-05-31 15:53:57,677 - web_server - DEBUG - Deferrable load 0: Proposed optimization window: 0 → 80
2024-05-31 15:53:57,677 - web_server - WARNING - Deferrable load 0 : Available timeframe is shorter than the specified number of hours to operate. Optimization will fail.
2024-05-31 15:53:57,678 - web_server - DEBUG - Deferrable load 0: Validated optimization window: 0 → 24
2024-05-31 15:53:57,686 - web_server - WARNING - Solver default unknown, using default
Welcome to the CBC MILP Solver
Version: 2.10.3
Build Date: Dec 15 2019

command line - /usr/local/lib/python3.11/dist-packages/pulp/solverdir/cbc/linux/64/cbc /tmp/7769826f003d4c8e82c8756113a057a2-pulp.mps -max -timeMode elapsed -branch -printingOptions all -solution /tmp/7769826f003d4c8e82c8756113a057a2-pulp.sol (default strategy 1)
At line 2 NAME MODEL
At line 3 ROWS
At line 316 COLUMNS
At line 2277 RHS
At line 2589 BOUNDS
At line 2830 ENDATA
Problem MODEL has 311 rows, 192 columns and 1768 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Problem is infeasible - 0.00 seconds
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.00

2024-05-31 15:53:57,697 - web_server - INFO - Status: Infeasible
2024-05-31 15:53:57,697 - web_server - INFO - Total value of the Cost function = -27.34

Thanks for the reminder. I do recall my solution was working OK previously.

Then I started to fine tune it a little bit here and there, reducing the def_total_hours by how long the loads have been running for, and then adjusting def_start_timestep.

So it was looking odd why when forecasted over a full day I was getting unfeasible.

Anynway - I’ll start again with as minimal constraints as possible.

But in summary I think I saw earlier in the thread:

If optim_state is unfeasible - don’t use EMHASS forecasting
and I should
re-calculate constraints - to ensure def_total_hours fit within def_start_timestep & def_end_timestep

I just want to post some example templates as a reference:

Use case: we have a irregular life, so not every monday we will have the same consumption profile. To fix this, I made a few template load forecasts for weekdays, weekend and one for when we are on holiday.

I made a template helper for each profile with an array of 24 values representing the load for every hour.

a template sensors, which changes this array to the current hour

      - name: "EMHASS Load weekend"
        unique_id: emhass_load_weekend
        state: >-
              {% set current_hour = now().strftime('%H') | int %}
              {% set data = states('sensor.emhass_load_weekend_raw', 'data') | from_json %}
              {% set values = data.data %}
              {% set values_to_remove = current_hour - 1 %}
              {% if values_to_remove > 0 %}
                {% set values = values[values_to_remove:] %}
              {% endif %}
              {{ values }}   

and finally every 5 minutes an MPC call: (amended with a load forecast for the next day)

        "load_power_forecast": {{ (states('sensor.emhass_load_weekend') | from_json) + (states('sensor.emhass_load_next_day') | from_json) }},

For example when the alarm system is armed, the load forecast goes to a profile with minimum load

If def_total_hours > def_end_timestep you will get Infeasible, which makes it tricky to do with sliding windows and reducing def_total_hours to match.

That’s a shame.
If neeed, solcast is available natively within EMHASS.

Yes I only recently realised I can check the status through optim_status. I’ll use this as a pre-check whether to use EMHASS or not.