EMHASS: An Energy Management for Home Assistant

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.

Sorry to bother @davidusb
Okay I’m a bit stuck here, I’ve reduced my def_total_hours to 1 and 0, with the start timestep 0,26 and 26,26. But still getting infeasible.

Am I missing something ? I feel like I’m missing something reaaaaaally basic…

But I did some googling - my power_var_no_loads were negative a few times yesterday - is it related to this? If so - I’ve just fixed the template to be always >= 0

/edit

I can run Day Ahead Optimsation and it will be Optimal but when I run MPC optimisation it goes Infeasible. Need to try again with fresh eyes tomorrow…

2024-06-01 01:54:47,680 - web_server - INFO - Passed runtime parameters: {'soc_init': 0.06, 'prod_price_forecast': [0.15, 0.1, 0.09, 0.09, 0.09, 0.09, 0.08, 0.09, 0.09, 0.12, 0.17, 0.27, 0.32, 0.32, 0.24, 0.31, 0.14, 0.06, 0.05, 0.05, 0.06, 0.04, 0.05, 0.04, 0.04, 0.33, 0.33, 0.35, 0.35, 0.42, 0.52, 0.58, 0.58, 0.58, 0.58, 0.42, 0.4, 0.12, 0.11, 0.09, 0.08, 0.1, 0.09, 0.09, 0.09, 0.08, 0.08, 0.08], 'load_cost_forecast': [0.26, 0.21, 0.19, 0.19, 0.19, 0.19, 0.18, 0.19, 0.19, 0.22, 0.27, 0.39, 0.44, 0.44, 0.35, 0.43, 0.24, 0.18, 0.16, 0.16, 0.18, 0.16, 0.16, 0.16, 0.16, 0.42, 0.42, 0.44, 0.44, 0.51, 0.63, 0.7, 0.7, 0.7, 0.7, 0.52, 0.5, 0.22, 0.21, 0.19, 0.18, 0.21, 0.19, 0.19, 0.19, 0.18, 0.18, 0.18], 'pv_power_forecast': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80.69999999999999, 296, 959.8, 1774.4, 2450.7, 2909.2, 3880, 3920.4, 3698.6, 3252.5, 2844.1, 2508.4, 2321.8999999999996, 2238.9, 2009.9, 1507.3000000000002, 937.4, 381.2, 123.30000000000001, 22.4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'num_def_loads': 2, 'def_total_hours': [4, 2], 'P_deferrable_nom': [3600, 6000], 'def_start_timestep': [0, 0], 'def_end_timestep': [0, 0], 'set_def_constant': [False, True], 'treat_def_as_semi_cont': [True, True]}
2024-06-01 01:54:47,680 - web_server - INFO -  >> Setting input data dict
2024-06-01 01:54:47,680 - web_server - INFO - Setting up needed data
2024-06-01 01:54:47,683 - web_server - INFO - Retrieving weather forecast data using method = list
2024-06-01 01:54:47,683 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2024-06-01 01:54:47,684 - web_server - INFO - Retrieve hass get data method initiated...
2024-06-01 01:54:52,071 - web_server - INFO -  >> Performing dayahead optimization...
2024-06-01 01:54:52,071 - web_server - INFO - Performing day-ahead forecast optimization
2024-06-01 01:54:52,080 - web_server - INFO - Perform optimization for the day-ahead
2024-06-01 01:54:52,086 - web_server - DEBUG - Deferrable load 0: Proposed optimization window: 0 --> 0
2024-06-01 01:54:52,086 - web_server - DEBUG - Deferrable load 0: Validated optimization window: 0 --> 0
2024-06-01 01:54:52,087 - web_server - DEBUG - Deferrable load 1: Proposed optimization window: 0 --> 0
2024-06-01 01:54:52,088 - web_server - DEBUG - Deferrable load 1: Validated optimization window: 0 --> 0
2024-06-01 01:54:52,401 - web_server - INFO - Status: Optimal
2024-06-01 01:54:52,401 - web_server - INFO - Total value of the Cost function = 0.09
2024-06-01 01:55:57,841 - web_server - INFO - Passed runtime parameters: {'soc_init': 0.06, 'prod_price_forecast': [0.15, 0.1, 0.09, 0.09, 0.09, 0.09, 0.08, 0.09, 0.09, 0.12, 0.17, 0.27, 0.32, 0.32, 0.24, 0.31, 0.14, 0.06, 0.05, 0.05, 0.06, 0.04, 0.05, 0.04, 0.04, 0.33, 0.33, 0.35, 0.35, 0.42, 0.52, 0.58, 0.58, 0.58, 0.58, 0.42, 0.4, 0.12, 0.11, 0.09, 0.08, 0.1, 0.09, 0.09, 0.09, 0.08, 0.08, 0.08], 'load_cost_forecast': [0.26, 0.21, 0.19, 0.19, 0.19, 0.19, 0.18, 0.19, 0.19, 0.22, 0.27, 0.39, 0.44, 0.44, 0.35, 0.43, 0.24, 0.18, 0.16, 0.16, 0.18, 0.16, 0.16, 0.16, 0.16, 0.42, 0.42, 0.44, 0.44, 0.51, 0.63, 0.7, 0.7, 0.7, 0.7, 0.52, 0.5, 0.22, 0.21, 0.19, 0.18, 0.21, 0.19, 0.19, 0.19, 0.18, 0.18, 0.18], 'pv_power_forecast': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80.69999999999999, 296, 959.8, 1774.4, 2450.7, 2909.2, 3880, 3920.4, 3698.6, 3252.5, 2844.1, 2508.4, 2321.8999999999996, 2238.9, 2009.9, 1507.3000000000002, 937.4, 381.2, 123.30000000000001, 22.4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'prediction_horizon': 44, 'num_def_loads': 2, 'P_deferrable_nom': [3600, 6000], 'def_start_timestep': [0, 0], 'def_end_timestep': [0, 0], 'soc_final': 0.06, 'treat_def_as_semi_cont': [True, True], 'set_def_constant': [False, True], 'weight_battery_discharge': 0.2, 'weight_battery_charge': 0, 'def_total_hours': [4, 2]}
2024-06-01 01:55:57,842 - web_server - INFO -  >> Setting input data dict
2024-06-01 01:55:57,842 - web_server - INFO - Setting up needed data
2024-06-01 01:55:57,843 - web_server - INFO - Retrieve hass get data method initiated...
2024-06-01 01:55:59,553 - web_server - INFO - Retrieving weather forecast data using method = list
2024-06-01 01:55:59,554 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2024-06-01 01:55:59,554 - web_server - INFO - Retrieve hass get data method initiated...
2024-06-01 01:56:04,183 - web_server - INFO -  >> Performing naive MPC optimization...
2024-06-01 01:56:04,184 - web_server - INFO - Performing naive MPC optimization
2024-06-01 01:56:04,191 - web_server - INFO - Perform an iteration of a naive MPC controller
2024-06-01 01:56:04,197 - web_server - DEBUG - Deferrable load 0: Proposed optimization window: 0 --> 0
2024-06-01 01:56:04,197 - web_server - DEBUG - Deferrable load 0: Validated optimization window: 0 --> 0
2024-06-01 01:56:04,198 - web_server - DEBUG - Deferrable load 1: Proposed optimization window: 0 --> 0
2024-06-01 01:56:04,198 - web_server - DEBUG - Deferrable load 1: Validated optimization window: 0 --> 0
2024-06-01 01:56:04,260 - web_server - INFO - Status: Infeasible
2024-06-01 01:56:04,261 - web_server - INFO - Total value of the Cost function = -2.70

[Edit 2] It’s come good now, it was the SOC_INIT being the same as SOC_FINAL

@Lildan you were right - I should’ve tried this first (but I forgot I could manually adjust the SOC to test). I think the battery was able to gain a bit of charge in the early morning light, so by the time I woke up EMHASS was tuning optimal.

soc_init = 0.06, soc_final = 0.06

2024-06-01 09:48:55,283 - web_server - INFO - Passed runtime parameters: {'soc_init': 0.06, 'prod_price_forecast': [0.09, 0.05, 0.06, 0.06, 0.06, 0.06, 0.07, 0.05, 0.05, 0.33, 0.33, 0.33, 0.34, 0.38, 0.48, 0.58, 0.58, 0.58, 0.51, 0.42, 0.4, 0.11, 0.11, 0.09, 0.09, 0.09, 0.09, 0.11, 0.09, 0.08, 0.08, 0.08, 0.08, 0.08, 0.07, 0.06, 0.06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'load_cost_forecast': [0.2, 0.17, 0.18, 0.18, 0.18, 0.18, 0.19, 0.16, 0.17, 0.42, 0.42, 0.42, 0.43, 0.48, 0.59, 0.7, 0.7, 0.7, 0.62, 0.52, 0.5, 0.22, 0.21, 0.19, 0.19, 0.19, 0.19, 0.21, 0.2, 0.18, 0.18, 0.18, 0.18, 0.18, 0.17, 0.16, 0.16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'pv_power_forecast': [2019.2, 1873.6999999999998, 2618.3999999999996, 3577.3, 3171.1, 2453.6, 2226.5, 2155.6000000000004, 1947.2, 1435, 894.9, 354.9, 110.9, 22.2, 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, 54.4, 244.79999999999998, 830.2, 1720.6, 2523.2000000000003, 3181.6], 'def_total_hours': [1, 1], 'prediction_horizon': 28, 'num_def_loads': 2, 'P_deferrable_nom': [3600, 6000], 'def_start_timestep': [0, 2], 'def_end_timestep': [8, 8], 'soc_final': 0.06, 'treat_def_as_semi_cont': [True, True], 'set_def_constant': [False, True], 'weight_battery_discharge': 0.2, 'weight_battery_charge': 0}
2024-06-01 09:48:55,283 - web_server - INFO -  >> Setting input data dict
2024-06-01 09:48:55,283 - web_server - INFO - Setting up needed data
2024-06-01 09:48:55,285 - web_server - INFO - Retrieve hass get data method initiated...
2024-06-01 09:48:57,151 - web_server - INFO - Retrieving weather forecast data using method = list
2024-06-01 09:48:57,152 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2024-06-01 09:48:57,152 - web_server - INFO - Retrieve hass get data method initiated...
2024-06-01 09:49:01,627 - web_server - INFO -  >> Performing naive MPC optimization...
2024-06-01 09:49:01,627 - web_server - INFO - Performing naive MPC optimization
2024-06-01 09:49:01,633 - web_server - INFO - Perform an iteration of a naive MPC controller
2024-06-01 09:49:01,637 - web_server - DEBUG - Deferrable load 0: Proposed optimization window: 0 --> 8
2024-06-01 09:49:01,637 - web_server - DEBUG - Deferrable load 0: Validated optimization window: 0 --> 8
2024-06-01 09:49:01,638 - web_server - DEBUG - Deferrable load 1: Proposed optimization window: 2 --> 8
2024-06-01 09:49:01,638 - web_server - DEBUG - Deferrable load 1: Validated optimization window: 2 --> 8
2024-06-01 09:49:01,672 - web_server - INFO - Status: Infeasible
2024-06-01 09:49:01,672 - web_server - INFO - Total value of the Cost function = -0.79

soc_init = 0.07, soc_final = 0.06

2024-06-01 09:49:20,795 - web_server - INFO - Passed runtime parameters: {'soc_init': 0.07, 'prod_price_forecast': [0.09, 0.05, 0.06, 0.06, 0.06, 0.06, 0.07, 0.05, 0.05, 0.33, 0.33, 0.33, 0.34, 0.38, 0.48, 0.58, 0.58, 0.58, 0.51, 0.42, 0.4, 0.11, 0.11, 0.09, 0.09, 0.09, 0.09, 0.11, 0.09, 0.08, 0.08, 0.08, 0.08, 0.08, 0.07, 0.06, 0.06, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'load_cost_forecast': [0.2, 0.17, 0.18, 0.18, 0.18, 0.18, 0.19, 0.16, 0.17, 0.42, 0.42, 0.42, 0.43, 0.48, 0.59, 0.7, 0.7, 0.7, 0.62, 0.52, 0.5, 0.22, 0.21, 0.19, 0.19, 0.19, 0.19, 0.21, 0.2, 0.18, 0.18, 0.18, 0.18, 0.18, 0.17, 0.16, 0.16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'pv_power_forecast': [2019.2, 1873.6999999999998, 2618.3999999999996, 3577.3, 3171.1, 2453.6, 2226.5, 2155.6000000000004, 1947.2, 1435, 894.9, 354.9, 110.9, 22.2, 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, 54.4, 244.79999999999998, 830.2, 1720.6, 2523.2000000000003, 3181.6], 'def_total_hours': [1, 1], 'prediction_horizon': 28, 'num_def_loads': 2, 'P_deferrable_nom': [3600, 6000], 'def_start_timestep': [0, 2], 'def_end_timestep': [8, 8], 'soc_final': 0.06, 'treat_def_as_semi_cont': [True, True], 'set_def_constant': [False, True], 'weight_battery_discharge': 0.2, 'weight_battery_charge': 0}
2024-06-01 09:49:20,795 - web_server - INFO -  >> Setting input data dict
2024-06-01 09:49:20,795 - web_server - INFO - Setting up needed data
2024-06-01 09:49:20,797 - web_server - INFO - Retrieve hass get data method initiated...
2024-06-01 09:49:22,566 - web_server - INFO - Retrieving weather forecast data using method = list
2024-06-01 09:49:22,566 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2024-06-01 09:49:22,567 - web_server - INFO - Retrieve hass get data method initiated...
2024-06-01 09:49:26,750 - web_server - INFO -  >> Performing naive MPC optimization...
2024-06-01 09:49:26,750 - web_server - INFO - Performing naive MPC optimization
2024-06-01 09:49:26,756 - web_server - INFO - Perform an iteration of a naive MPC controller
2024-06-01 09:49:26,760 - web_server - DEBUG - Deferrable load 0: Proposed optimization window: 0 --> 8
2024-06-01 09:49:26,760 - web_server - DEBUG - Deferrable load 0: Validated optimization window: 0 --> 8
2024-06-01 09:49:26,761 - web_server - DEBUG - Deferrable load 1: Proposed optimization window: 2 --> 8
2024-06-01 09:49:26,761 - web_server - DEBUG - Deferrable load 1: Validated optimization window: 2 --> 8
2024-06-01 09:49:26,817 - web_server - INFO - Status: Optimal
2024-06-01 09:49:26,818 - web_server - INFO - Total value of the Cost function = -0.80

/Edit
Fixed a few more things and now it’s running much more reliably.
Ensured all my end/start timesteps are greater than or equal to 0 and rounded integer values (they were before, and I experimented with floats).
Ensured the prediction horizon is 5x timestep length (if < 3, default to 48).
To calculate runtime left of a load - I was calculating the current runtime of a load by it’s energy used / power draw, which meant sometimes a 0Wh / 3600W for hot water element, so I just checked if it was 0, leave as 0.
Ensured I wasn’t running MPC more than once a minute by using delay node with 1msg/min.

So far so good. Any other ideas/tips I could try?

Hello community, a new release has been published: v0.10.0

A word of caution with the new release as there is a possible BREAKING CHANGE for some people.

In this new version we have added support for PV curtailment computation. While doing this the nominal PV peak power is needed. The easiest way find this information is by directly using the inverter_model defined in the configuration. As this is needed in the optimization to correctly compute PV curtailment, this parameter need to be properly defined for your installation. Before this chage this parameter was only needed if using the PV forecast method scrapper, but now it is not optional as it is directly used in the optimization.

Use the dedicated webapp to find the correct model for your inverter, if you cannot find your exact brand/model then just pick an inverter with the same nominal power as yours: https://emhass-pvlib-database.streamlit.app/

This release come with a bunch of nice imporovements:

  • Added support for hybrid inverters and PV curtailment computation
  • Implemented a new continual_publish service that avoid the need of setting a special automation for data publish. Thanks to @GeoDerp
  • Implement a deferrable load start penalty functionality. Thanks to @werdnum
    • This feature also implement a def_current_state that can be passed at runtime to let the optimization consider that a deferrable load is currently scheduled or under operation when launching the optimization task

There were also some needed fixes:

  • Fixed forecast methods to treat delta_forecast higher than 1 day
  • Fixed solar.forecast wrong interpolation of nan values
3 Likes

Thank you David,
For my better understanding, how should I interpret the P_PV_curtailment variable?
From the chart below I understand it should be the power (I discharge from the battery) that is not covering the load demand, so it is being pushed to the grid.
Is that correct or is there more? Also, in this case, should I expect to see this being reflected in the P_grid (_neg) variable?
Last question, is it done on purpose not to make the P_PV_curtailment variable available to HA, or is this related to the new publish method?
Thanks

(PS I do not own a hybrid inverter)

A fork of the solcast integration has been created at GitHub - BJReplay/ha-solcast-solar: Solcast Integration for Home Assistant. They’ve applied for it to be added to HACS, however you can add it as a manual repository if you want to whilst that is sorted.

1 Like

I’m not sure if Solcast changes it’s forecast throughout the day. If it doesn’t, we could add a feature request for EMHASS that retains the Solcast data after being run at the start of each day. All preceding calls to Solcast forecaster will reference the saved data and not call the API?

Some minor changes throughout the day, but generally not very material.

The key consideration is the entry tier only allows for 10 API calls per day. Often just a few calls during the day would be sufficient, early morning, midday and afternoon. However EMHASS calls SolCast API during every call, including MPC which I am calling every minute.

It would be great if EMHASS could cache the solcast data, maybe from the day-ahead optimisation and not update via the MPC, but use the cached SolCast data.

1 Like

Thanks for implementing that feature request @davidusb

This is how I’m using def_current_state
"def_current_state": [{{iif(states('sensor.p_deferrable0')|int(0),1,0)}}]

So if prior to mpc-optimize run, the deferrable sensor is greater than 0 (running) then pass TRUE for the next run. You still need to reduce “def_total_hours” as the load runs throughout the day.

2 Likes

Ability to cache Solcast sounds like a good idea. I think it should be definitely a feature request, and maybe something I’ll look into.
I wonder how difficult it would be to tell MPC to use to use the cached Solcast unless it’s like mid-day, in which it will then run another Solcast and cache till afternoon/evening, etc.

2 Likes