EMHASS: An Energy Management for Home Assistant

Slowly starting to get things working nicely, Today i decluttered my automation for controlling inverter modes.

I have created a template sensor that decides what “Mode” emhass is in, Chargine, Discharging, Exporting Solar or General (Self consumption)

This means now i dont need to touch my complicated automation for inverter control, only the relativly simple template sensor.

- sensor:
    - unique_id: emhass_mode
      name: "EMHASS Mode"
      state: >-
        {%set bat_power = states('sensor.p_batt_forecast') | float %}
        {%set load = states('sensor.p_load_forecast') | float%}
        {%set grid = states('sensor.p_grid_forecast') | float%}
        {%set solar = states('sensor.p_pv_forecast') | float %}


        {% if bat_power + load < 0 and grid > 0 %}
          charge 
        {% elif bat_power - load > 0 and grid < 0 and solar <= 0%}
          discharge
        {% elif bat_power == 0 and grid < 0 %}
          exporting_solar 
        {%elif bat_power <= 0 and grid > 0%}
          backup 
        {%else %} 
          general 
        {%- endif %}

@davidusb A toggle i would love to see added to EMHASS is no discharging to the grid while the sun is shinning

3 Likes

Oh ok you don’t run post_amber_forecast at all either. Just post_mpc_optim_solcast and publish_data every minute using restful command. ok thanks again

I’ve been pushing Tibber prices from a REST sensor and some templating to EMHASS.
Tibber current day prices start at 00:00 hours. But it looks like EMHASS uses the first value as the current or next hour? depending on when the data is published this seems to create an unwanted offset.

I could push only future prices but won’t that mess with the expected number of values/hours?

Only pushing forecast data at 00:00 hours could solve this I guess. But what happens after a restart and no data is present? Waiting till midnight cannot be the solution in a production like environment, right?

@markpurcell ok you don’t run post_amber_forecast at all. Just post_mpc_optim_solcast and publish_data every minute? thanks again for your help.

What I do is utilise the prediction_horizon variable, either set it to the known minimum (for Amber that is 33) or dynamically calculate by counting the number of price forecasts.

See my example above.

I’ve got the prediction horizon set to 33 at the moment (see below). However, I count 48 elements in each array for each of load_cost_forecast, prod_price_forecast and pv_power_forecast.

Should I change this to 48? What is the effect of this change?

I’m also still running post_amber_optim at 5:50am when I don’t think I need to if I’m going straight to 1 minute MPC optimisations.

Also, SOC init and SOC final can go to 100 and 0 (or 1 to 0 should I say) for sonnen I think as the user batery state is 10% less than real battery capacity, if my understanding of these two data is correct.

post_mpc_optim_solcast:
    "curl -i -H \"Content-Type: application/json\" -X POST -d '{\"load_cost_forecast\":[0.2, 0.19, 0.2, 0.17, 0.17, 0.17, 0.17, 0.17, 0.16, 0.16, 0.16, 0.17, 0.17, 0.21, 0.31, 0.27, 0.2, 0.17, 0.17, 0.16, 0.13, 0.13, 0.12, 0.09, 0.1, 0.11, 0.11, 0.13, 0.39, 0.4, 0.39, 0.39, 0.42, 0.45, 0.48, 0.56, 0.56, 0.56, 0.51, 0.5, 0.28, 0.27, 0.27, 0.23, 0.26, 0.21, 0.21, 0.2], \"prod_price_forecast\":[0.09, 0.09, 0.09, 0.07, 0.07, 0.07, 0.07, 0.07, 0.06, 0.06, 0.06, 0.07, 0.07, 0.11, 0.2, 0.16, 0.1, 0.07, 0.07, 0.06, 0.02, 0.02, 0.0, -0.02, -0.01, -0.0, 0.0, 0.02, 0.3, 0.31, 0.3, 0.3, 0.34, 0.36, 0.38, 0.45, 0.46, 0.46, 0.42, 0.41, 0.17, 0.16, 0.16, 0.13, 0.15, 0.11, 0.11, 0.09], \"pv_power_forecast\":[3244, 3333, 3359, 3304, 3192, 2976, 2712, 2384, 2003, 1497, 926, 227, 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, 99, 666, 1200, 1677, 2107, 2529, 2922, 3166], \"prediction_horizon\":33,\"soc_init\":0.97,\"soc_final\":0.05,\"def_total_hours\":[2,2,2,2]}' http://localhost:5000/action/naive-mpc-optim"
This template listens for the following state changed events:

Entity: sensor.cecil_st_feed_in_forecast
Entity: sensor.cecil_st_feed_in_price
Entity: sensor.cecil_st_general_forecast
Entity: sensor.cecil_st_general_price
Entity: sensor.solcast_24hrs_forecast
Entity: sensor.sonnenbatterie_84324_state_charge_user

Oh, ok so the array size can change from Amber? I didn’t know that. I expect its safer to use 33 and ignore the tail end of the data as its calculating the forecast every minute anyway.

Amber/ AEMO publishes the 24 hour Forecast after 12:30 each day so then you have 48x30 minute forecasts.

They continue to update this until 0330, when the forecasts stop, so between 0330 and 1230 you have incrementally less than 48 forecasts. At 1200 you have the smallest number of forecasts 1200-0330 the next day which is 33x30 minute forecasts.

1 Like

This looks pretty good, what results do you get for the optimisation?

I’m getting errors in the log file.

2023-07-12 17:42:00,052 - web_server - INFO - Setting up needed data
2023-07-12 17:42:00,054 - web_server - INFO - Retrieve hass get data method initiated...
2023-07-12 17:42:00,667 - web_server - INFO - Retrieving weather forecast data using method = list
2023-07-12 17:42:00,668 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2023-07-12 17:42:00,669 - web_server - INFO - Retrieve hass get data method initiated...
2023-07-12 17:42:01,933 - web_server - INFO -  >> Performing naive MPC optimization...
2023-07-12 17:42:01,933 - web_server - INFO - Performing naive MPC optimization
2023-07-12 17:42:01,942 - web_server - INFO - Perform an iteration of a naive MPC controller
2023-07-12 17:42:01,943 - web_server - ERROR - Exception on /action/naive-mpc-optim [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 2190, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1486, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.9/dist-packages/flask/app.py", line 1469, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "/usr/local/lib/python3.9/dist-packages/emhass/web_server.py", line 199, in action_call
    opt_res = naive_mpc_optim(input_data_dict, app.logger)
  File "/usr/local/lib/python3.9/dist-packages/emhass/command_line.py", line 275, in naive_mpc_optim
    opt_res_naive_mpc = input_data_dict['opt'].perform_naive_mpc_optim(
  File "/usr/local/lib/python3.9/dist-packages/emhass/optimization.py", line 573, in perform_naive_mpc_optim
    self.opt_res = self.perform_optimization(df_input_data, P_PV.values.ravel(), P_load.values.ravel(),
  File "/usr/local/lib/python3.9/dist-packages/emhass/optimization.py", line 156, in perform_optimization
    if self.optim_conf['treat_def_as_semi_cont'][k]:
IndexError: list index out of range
2023-07-12 17:42:01,976 - web_server - INFO - Setting up needed data
2023-07-12 17:42:01,978 - web_server - INFO -  >> Publishing data...
2023-07-12 17:42:01,978 - web_server - INFO - Publishing data to HASS instance
2023-07-12 17:42:01,996 - web_server - INFO - Successfully posted to sensor.p_pv_forecast = 922
2023-07-12 17:42:02,007 - web_server - INFO - Successfully posted to sensor.p_load_forecast = 1085.43
2023-07-12 17:42:02,016 - web_server - INFO - Successfully posted to sensor.p_deferrable0 = 0.0
2023-07-12 17:42:02,026 - web_server - INFO - Successfully posted to sensor.p_deferrable1 = 0.0
2023-07-12 17:42:02,037 - web_server - INFO - Successfully posted to sensor.p_deferrable2 = 0.0
2023-07-12 17:42:02,046 - web_server - INFO - Successfully posted to sensor.p_deferrable3 = 0.0
2023-07-12 17:42:02,054 - web_server - INFO - Successfully posted to sensor.p_batt_forecast = 163.43
2023-07-12 17:42:02,063 - web_server - INFO - Successfully posted to sensor.soc_batt_forecast = 68.83
2023-07-12 17:42:02,072 - web_server - INFO - Successfully posted to sensor.p_grid_forecast = 0.0
2023-07-12 17:42:02,084 - web_server - INFO - Successfully posted to sensor.total_cost_fun_value = -2.22
2023-07-12 17:42:02,096 - web_server - INFO - Successfully posted to sensor.unit_load_cost = 0.19
2023-07-12 17:42:02,106 - web_server - INFO - Successfully posted to sensor.unit_prod_price = 0.09

This seems to be causing some loss of data.

Not sure what to do to correct the error?

do you have the correct number of devices setup for this variable?

Oops! I removed all the deferrable loads except one. Simplify the setup for the time being. But missed the ‘number_of_deferrable_loads’ setting. Still has 4. Thanks again?!

By the way if I change the ‘list_peak_hours_periods_start_hours’ and end, does that restrict the operation of the deferrable load to that time period?

Also the one deferrable load left is the pool pump. I assume that ‘treat_deferrable_load_as_semi_cont: true’ is appropriate? The pump is simple one speed 1500w motor.

number_of_deferrable_loads: 4
list_nominal_power_of_deferrable_loads:
  - nominal_power_of_deferrable_loads: 1500
list_operating_hours_of_each_deferrable_load:
  - operating_hours_of_each_deferrable_load: 2
list_peak_hours_periods_start_hours:
  - peak_hours_periods_start_hours: "02:54"
list_peak_hours_periods_end_hours:
  - peak_hours_periods_end_hours: "15:24"
list_treat_deferrable_load_as_semi_cont:
  - treat_deferrable_load_as_semi_cont: true
number_of_deferrable_loads: 1
list_nominal_power_of_deferrable_loads:
  - nominal_power_of_deferrable_loads: 1500
list_operating_hours_of_each_deferrable_load:
  - operating_hours_of_each_deferrable_load: 2
list_peak_hours_periods_start_hours:
  - peak_hours_periods_start_hours: "02:54"
list_peak_hours_periods_end_hours:
  - peak_hours_periods_end_hours: "15:24"
list_treat_deferrable_load_as_semi_cont:
  - treat_deferrable_load_as_semi_cont: true

Still doing strange things like discharging at 1am and I can’t see any amber data after midnight. Just stuck on FiT and price from before midnight

Better logs now though:

s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
services-up: info: copying legacy longrun emhass (no readiness notification)
s6-rc: info: service legacy-services successfully started
2023-07-12 18:08:00,599 - web_server - INFO - Launching the emhass webserver at: http://0.0.0.0:5000
2023-07-12 18:08:00,599 - web_server - INFO - Home Assistant data fetch will be performed using url: http://supervisor/core/api
2023-07-12 18:08:00,599 - web_server - INFO - The data path is: /share
2023-07-12 18:08:00,601 - web_server - INFO - Using core emhass version: 0.4.13
waitress   INFO  Serving on http://0.0.0.0:5000
2023-07-12 18:09:00,064 - web_server - INFO - Setting up needed data
2023-07-12 18:09:00,120 - web_server - INFO - Retrieve hass get data method initiated...
2023-07-12 18:09:00,734 - web_server - INFO - Retrieving weather forecast data using method = list
2023-07-12 18:09:00,735 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2023-07-12 18:09:00,736 - web_server - INFO - Retrieve hass get data method initiated...
2023-07-12 18:09:01,919 - web_server - INFO -  >> Performing naive MPC optimization...
2023-07-12 18:09:01,919 - web_server - INFO - Performing naive MPC optimization
2023-07-12 18:09:01,932 - web_server - INFO - Perform an iteration of a naive MPC controller
2023-07-12 18:09:02,012 - web_server - INFO - Status: Optimal
2023-07-12 18:09:02,013 - web_server - INFO - Total value of the Cost function = -0.11
2023-07-12 18:09:02,446 - web_server - INFO - Setting up needed data
2023-07-12 18:09:02,448 - web_server - INFO -  >> Publishing data...
2023-07-12 18:09:02,449 - web_server - INFO - Publishing data to HASS instance
2023-07-12 18:09:02,477 - web_server - INFO - Successfully posted to sensor.p_pv_forecast = 0
2023-07-12 18:09:02,490 - web_server - INFO - Successfully posted to sensor.p_load_forecast = 1078.94
2023-07-12 18:09:02,504 - web_server - INFO - Successfully posted to sensor.p_deferrable0 = 0.0
2023-07-12 18:09:02,516 - web_server - INFO - Successfully posted to sensor.p_batt_forecast = 1078.94
2023-07-12 18:09:02,533 - web_server - INFO - Successfully posted to sensor.soc_batt_forecast = 45.32
2023-07-12 18:09:02,546 - web_server - INFO - Successfully posted to sensor.p_grid_forecast = 0.0
2023-07-12 18:09:02,556 - web_server - INFO - Successfully posted to sensor.total_cost_fun_value = -0.11
2023-07-12 18:09:02,566 - web_server - INFO - Successfully posted to sensor.unit_load_cost = 0.2
2023-07-12 18:09:02,577 - web_server - INFO - Successfully posted to sensor.unit_prod_price = 0.09

At 1am your sell price is very high at 20¢/ kWh so it makes sense for it to discharge.

Can you include both loads and prices on the graphic as that tells the whole story.

The table has prices after midnight so I suspect there is an issue with your apex card.

1 Like

As you are injecting Amber prices these settings are not used in your optimisation.

For a single speed pool pump you want this to be true.

1 Like

I had to delete and recreate the Amber API token and reconfigure the amber entry? Just stopped working.

Looks ok.

Charges battery on low prices at 2am.
Discharges battery for morning peak at 7am
Starts pool pump at 10am

I suspect 2pm is beyond your 33 forecasts which is why pool pump and battery appear stuck, but that should resolve as the time gets closer.

Yes running perfectly now.

1 Like