EMHASS: An Energy Management for Home Assistant

Thank You very much! That was the case. Somehow didn’t catch that from manual.

1 Like

You can set up as 2 deferable loads, the emahss will recognise this, but you will need to setup up automations from this to actually command the heatpump to turn it on.

Within automations set a condition to operate priority with the conditions you want. So i.e heatpump hot water has priority over the pool or whatever the load. Then emhass will re calculate the loads and operate accordingly.

I remember reading somewere you had some log data or soemrhing to record logs outside of emhass and it was picking up errors emhass wasnt writting.

I have multiple watchdogs now that are continuously revooting and checking values so it doesnt fail. So far no errors but node red has something clocking its program and not writting errors. I can not find it for the life of me

I just write content of RESTful errors (POST return code 201 and PUT return code 200), and what was sent to cause the error, into a text file with a time stamp so I can review them later. I do this for POSTs and PUTs to my battery and also the POST to EMHASS.

This allows me to review exacty what was sent to EMHASS when it recorded an error in it’s log file or when the sensor.p_batt_forecast change sent to the battery fails for some reason.

These log files are created in the ‘\hassio\share’ directory where the Node-Red ‘Write File’ node can create files.

Thanks for your response.
I know that I can setup more than one deferrable load but haven`t found a way to tell emhass to plan the timeframe of all loads (or just some loads) sequentially. Most of the times it plans both loads (hot water and heating) at the same time but this is not possible since the heat pump is only capable of doing one at a time.

For some reason pv_power_forecast drops to zero by time now?

Even through POST data seems correct:

{
“prod_price_forecast”: [-0.05, -0.06, -0.07, -0.07, -0.08, -0.08, -0.07, -0.07, 0.22, 0.24, 0.26, 0.3, 0.33, 0.31, 0.31, 0.33, 0.34, 0.36, 0.41, 0.42, 0.16, 0.16, 0.14, 0.13, 0.09, 0.07, 0.08, 0.09, 0.09, 0.11, 0.09, 0.09, 0.07, 0.07, 0.07, 0.07, 0.07, 0.09],
“load_cost_forecast”: [0.06, 0.05, 0.03, 0.03, 0.03, 0.03, 0.03, 0.04, 0.3, 0.32, 0.35, 0.39, 0.42, 0.39, 0.4, 0.42, 0.43, 0.45, 0.5, 0.51, 0.27, 0.27, 0.25, 0.23, 0.19, 0.17, 0.18, 0.19, 0.19, 0.21, 0.19, 0.19, 0.17, 0.17, 0.17, 0.17, 0.17, 0.19],
“pv_power_forecast”: [3421, 3698, 3865, 4058, 4176, 4228, 4212, 4079, 3941, 3766, 3494, 3180, 2780, 2351, 1862, 1345, 789, 319, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 80, 289, 586, 937, 1264, 1532, 1760, 1941, 2008, 1986, 2009, 2069, 2100, 2113, 2146, 2146, 2084, 1902, 1662, 1405, 1172, 972, 800, 567, 305, 86, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0],
“prediction_horizon”: 38,
“alpha”: 0,
“beta”: 0,
“num_def_loads”: 2,
“def_total_hours”: [3,4],
“P_deferrable_nom”: [1300, 7360],
“treat_def_as_semi_cont”: [1, 0],
“set_def_constant”: [0, 0],
“soc_init”: 0.14,
“soc_final”: 0.12
}

p_load_forecast does the same:

{
“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
}},
“load_cost_forecast”: {{
([states(‘sensor.cecil_st_general_price’)|float(0)] +
state_attr(‘sensor.cecil_st_general_forecast’, ‘forecasts’) |map(attribute=‘per_kwh’)|list)
| tojson
}},
“pv_power_forecast”: {{
([states(‘sensor.sonnenbatterie_84324_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
}},
“prediction_horizon”: {{
min(48, (state_attr(‘sensor.cecil_st_feed_in_forecast’, ‘forecasts’)|map(attribute=‘per_kwh’)|list|length)+1)
}},
“alpha”: 0,
“beta”: 0,
“num_def_loads”: 2,
“def_total_hours”: [
{%- if is_state(‘sensor.openweathermap_forecast_condition’, [‘rainy’, ‘cloudy’]) -%}
0
{%- elif is_state(‘sensor.season’, ‘winter’) -%}
2
{%- elif is_state(‘sensor.season’, ‘summer’) -%}
4
{%- else -%}
3
{%- endif -%},
{%- if is_state(‘device_tracker.robsiphone’, [‘home’]) -%}
{%- if is_state(‘cover.ynot_charger_door’, [‘open’]) -%}
{{ ((90-(states(‘sensor.ynot_battery’)|int(0)))/30*3)|int(0) }}
{%- else -%}
0
{%- endif -%}
{%- else -%}
0
{%- endif -%}
],
“P_deferrable_nom”: [1300, 7360],
“treat_def_as_semi_cont”: [1, 0],
“set_def_constant”: [0, 0],
“soc_init”: {{ (states(‘sensor.sonnenbatterie_84324_state_charge_user’)|int(0))/100 }},
“soc_final”: 0.12
})

Alpha and Beta both = 0 ? :face_with_raised_eyebrow:
That’s the problem I guess.

Ah ok. Thanks.

1 Like

So you have 1 deferrable load that controlls 2 items?

No, right now I have setup two deferrable loads, deferrable0 for hot water and deferrable1 for heating.

But as you can see, these loads are planned to run at the same time. This is not possible in my setup, since the heat pump is only capable of doing either /or.

Maybe just have one deferrable load and schedule it for the combined number of hours you want to operate your device.

Then in your activation automation you can monitor and allocate the operating hours for the different modes.

Thanks for your hint. I was thiniking about this as well but honestly, why am I using emhass? To optimize the energy consumption and profit.
Combining hot water and heating into one deferrable load with e.g. 9 hours of runtime will not be ideal in regard to optimization.

I´m wondering no one else is having the same problem!? No one using emhass in conjunction with a heat pump for heating and hot water?

EMHASS will help you select the optimal 9 hours of the day to run your device, you can use your automation to determine which mode is suitable at any one time.

I have a heat pump hot water system and a heat pump air conditioner but they are different devices so I can schedule them independently. Combined systems like yours make a lot of sense but I haven’t come across too many in the field.

Do you actually want your device to run for 9 hours, or are you trying to find some other optimisation? Can you describe an alternative optimisation?

I am. But I can’t separate it from the baseline consumption. Actually I don’t want as I need to keep heating and hot water running all the time so for me they are part of the baseline. I set the heating/water time slots in the pump itself (heating works in combination with Netatmo smart radiator valves, so it is activated only when needed).
What I could do is to treat as deferrable the hot water overheating, but this is done through the same pump and again I can’t clearly identify the load for this. So I just have an automation that switches it on when I have PV overproduction and a corresponding grid push.

Anyway I agree with Mark: one device, one deferrable load.

I’m struggling with my shell commands. When I execute in HA developer tools “post_entsoe_forcast”, then I look ad the error below.

EMHASS
Current version: 0.4.2

Core 2023.11.3
Supervisor 2023.11.3
Operating System 11.1
Frontend 20231030.2

shell_command:
  dayahead_optim: "curl -i -H 'Content-Type:application/json' -X POST -d '{}' http://localhost:5000/action/dayahead-optim"
  publish_data: "curl -i -H 'Content-Type:application/json' -X POST -d '{}' http://localhost:5000/action/publish-data"
  post_entsoe_forcast: "curl -i -H 'Content-Type:application/json' -X POST -d '{\"set_def_constant\":[true, true], \"def_total_hours\": [5, 8], \"load_cost_forecast\":{{(state_attr('sensor.dynamische_prijzen_average_electricity_price_today', 'prices_today')|map(attribute='price')|list)}}, \"prod_price_forecast\":{{(state_attr('sensor.dynamische_prijzen_average_electricity_price_today', 'prices_today')|map(attribute='price')|list)}}}' http://localhost:5000/action/dayahead-optim"

stdout: "HTTP/1.1 400 BAD REQUEST\r\nContent-Length: 167\r\nContent-Type: text/html; charset=utf-8\r\nDate: Thu, 23 Nov 2023 09:35:52 GMT\r\nServer: waitress\r\n\r\n<!doctype html>\n<html lang=en>\n<title>400 Bad Request</title>\n<h1>Bad Request</h1>\n<p>The browser (or proxy) sent a request that this server could not understand.</p>"
stderr: "% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n\r  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r100   703  100   167  100   536  37160   116k --:--:-- --:--:-- --:--:--  171k"
returncode: 0

This is the result from that template:

dayahead_optim: "curl -i -H 'Content-Type:application/json' -X POST -d '{}' http://localhost:5000/action/dayahead-optim"
  publish_data: "curl -i -H 'Content-Type:application/json' -X POST -d '{}' http://localhost:5000/action/publish-data"
  post_entsoe_forcast: "curl -i -H 'Content-Type:application/json' -X POST -d '{\"set_def_constant\":[true, true], \"def_total_hours\": [5, 8], \"load_cost_forecast\":[0.07861, 0.05572, 0.04976, 0.03646, 0.02442, 0.03893, 0.09922, 0.13846, 0.16916, 0.16928, 0.144, 0.10648, 0.08575, 0.10272, 0.11495, 0.11858, 0.13564, 0.18848, 0.16444, 0.15742, 0.11616, 0.11253, 0.11108, 0.07299], \"prod_price_forecast\":[0.07861, 0.05572, 0.04976, 0.03646, 0.02442, 0.03893, 0.09922, 0.13846, 0.16916, 0.16928, 0.144, 0.10648, 0.08575, 0.10272, 0.11495, 0.11858, 0.13564, 0.18848, 0.16444, 0.15742, 0.11616, 0.11253, 0.11108, 0.07299]}' http://localhost:5000/action/dayahead-optim"

Hi Friends,

I’m from Poland (in Europe;)
I have EMHASS installed as docker.

  1. I need help implementing the prices applicable in my region in EMHASS. My currency is PLN.
    I get prices in HA as follows:
rest:
  - scan_interval: 180
    resource: https://spot.56k.guru/api/v2/hass?currency=PLN&area=PL&multiplier=1&extra=0.2&factor=1.23&decimals=2
    sensor:
      - name: "Price RCE Now"
        unique_id: "rce_price_now"
        value_template: "{{ value_json.now }}"
        unit_of_measurement: "PLN/kWh"
        device_class: "monetary"
        json_attributes:
          - data
          - avg
          - min
          - max
          - avg_tomorrow
          - min_tomorrow
          - max_tomorrow
          - avg_yesterday
          - min_yesterday
          - max_yesterday

How to use sensor.rce_price_now in EMHASS?

  1. On the EMHASS dashboard there are buttons:
    Perfect Optimization - works OK
    Day-ahead Optimization - works OK
    Publish Optimization Results - works OK

Unfortunately, the buttons:
ML forecast model fit
ML forecast model predict
ML forecast model tune
return an error:


EMHASS  | 2023-11-23 21:01:45,762 - web_server - INFO - Setting up needed data
EMHASS  | 2023-11-23 21:01:45,775 - web_server - INFO - Retrieve hass get data method initiated...
EMHASS  | 2023-11-23 21:01:45,987 - web_server - ERROR - The retrieved JSON is empty, check that correct day or variable names are passed
EMHASS  | 2023-11-23 21:01:45,988 - web_server - ERROR - Either the names of the passed variables are not correct or days_to_retrieve is larger than the recorded history of your sensor (check your recorder settings)
EMHASS  | 2023-11-23 21:01:45,989 - web_server - ERROR - Exception on /action/forecast-model-fit [POST]
EMHASS  | Traceback (most recent call last):
EMHASS  |   File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1455, in wsgi_app
EMHASS  |     response = self.full_dispatch_request()
EMHASS  |   File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 869, in full_dispatch_request
EMHASS  |     rv = self.handle_user_exception(e)
EMHASS  |   File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 867, in full_dispatch_request
EMHASS  |     rv = self.dispatch_request()
EMHASS  |   File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 852, in dispatch_request
EMHASS  |     return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
EMHASS  |   File "src/emhass/web_server.py", line 179, in action_call
EMHASS  |     input_data_dict = set_input_data_dict(config_path, str(data_path), costfun,
EMHASS  |   File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/command_line.py", line 146, in set_input_data_dict
EMHASS  |     rh.get_data(days_list, var_list)
EMHASS  |   File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/retrieve_hass.py", line 147, in get_data
EMHASS  |     self.df_final = pd.concat([self.df_final, df_day], axis=0)
EMHASS  | UnboundLocalError: local variable 'df_day' referenced before assignment

My config_emhass.yaml is:

retrieve_hass_conf:
  - freq: 30 # The time step to resample retrieved data from hass in minutes
  - days_to_retrieve: 3 # We will retrieve data from now and up to days_to_retrieve days
  - var_PV: sensor.aisolar_pv_power_now # Photovoltaic produced power sensor in Watts
  - var_load: sensor.aisolar_load_totalpower # Household power consumption sensor in Watts (deferrable loads should be substracted)
  - load_negative: False # Set to True if the retrived load variable is negative by convention
  - set_zero_min: True # A special treatment for a minimum value saturation to zero. Values below zero are replaced by nans
  - var_replace_zero: # A list of retrived variables that we would want  to replace nans with zeros
    - sensor.aisolar_pv_power_now
  - var_interp: # A list of retrived variables that we would want to interpolate nan values using linear interpolation
    - sensor.aisolar_pv_power_now
    - sensor.aisolar_load_totalpower
  - method_ts_round: nearest # Set the method for timestamp rounding, options are: first, last and nearest
  - costfun: self-consumption


optim_conf:
  - set_use_battery: True # consider a battery storage
  - delta_forecast: 1 # days
  - num_def_loads: 2
  - P_deferrable_nom: # Watts
    - 3000.0
    - 750.0
  - def_total_hours: # hours
    - 5
    - 8
  - treat_def_as_semi_cont: # treat this variable as semi continuous
    - True
    - True
  - set_def_constant: # set as a constant fixed value variable with just one startup for each 24h
    - False
    - False
  - weather_forecast_method: scrapper # options are 'scrapper' and 'csv'
  - load_forecast_method: naive # options are 'csv' to load a custom load forecast from a CSV file or 'naive' for a persistance model
  - load_cost_forecast_method: hp_hc_periods # options are 'hp_hc_periods' for peak and non-peak hours contracts and 'csv' to load custom cost from CSV file
  - list_hp_periods: # list of different tariff periods (only needed if load_cost_forecast_method='hp_hc_periods')
    - period_hp_1:
      - start: '06:00'
      - end: '17:00'
    - period_hp_2:
      - start: '09:00'
      - end: '23:59'
  - load_cost_hp: 0.1907 # peak hours load cost in ?.?/kWh (only needed if load_cost_forecast_method='hp_hc_periods')
  - load_cost_hc: 0.1419 # non-peak hours load cost in ?.?/kWh (only needed if load_cost_forecast_method='hp_hc_periods')
  - prod_price_forecast_method: constant # options are 'constant' for constant fixed value or 'csv' to load custom price forecast from a CSV file
  - prod_sell_price: 0.065 # power production selling price in ?.?/kWh (only needed if prod_price_forecast_method='constant')
  - set_total_pv_sell: True # consider that all PV power is injected to the grid (self-consumption with total sell)
#  - lp_solver: PULP_CBC_CMD # set the name of the linear programming solver that will be used
  - lp_solver: COIN_CMD # set the name of the linear programming solver that will be used
  - lp_solver_path: /usr/bin/cbc # set the path to the LP solver
  - set_nocharge_from_grid: True # avoid battery charging from the grid
  - set_nodischarge_to_grid: False # avoid battery discharging to the grid
  - set_battery_dynamic: True # add a constraint to limit the dynamic of the battery power in power per time unit
  - battery_dynamic_max: 0.9 # maximum dynamic positive power variation in percentage of battery maximum power
  - battery_dynamic_min: -0.9 # minimum dynamic negative power variation in percentage of battery maximum power

plant_conf:
  - P_grid_max: 7000 # The maximum power that can be supplied by the utility grid in Watts
  - module_model: # The PV module model
    - CSUN_Eurasia_Energy_Systems_Industry_and_Trade_CSUN295_60M
  - inverter_model: # The PV inverter model
    - Fronius_International_GmbH__Fronius_Primo_5_0_1_208_240__240V_
  - surface_tilt: # The tilt angle of your solar panels
    - 30
  - surface_azimuth: # The azimuth angle of your PV installation
    - 90
    - 270
  - modules_per_string: # The number of modules per string
    - 8
  - strings_per_inverter: # The number of used strings per inverter
    - 2
  - Pd_max: 10000 # If your system has a battery (set_use_battery=True), the maximum discharge power in Watts
  - Pc_max: 10000 # If your system has a battery (set_use_battery=True), the maximum charge power in Watts
  - eta_disch: 0.95 # If your system has a battery (set_use_battery=True), the discharge efficiency
  - eta_ch: 0.95 # If your system has a battery (set_use_battery=True), the charge efficiency
  - Enom: 14500 # If your system has a battery (set_use_battery=True), the total capacity of the battery stack in Wh
  - SOCmin: 0.3 # If your system has a battery (set_use_battery=True), the minimun allowable battery state of charge
  - SOCmax: 0.9 # If your system has a battery (set_use_battery=True), the minimun allowable battery state of charge
  - SOCtarget: 0.6 # If your system has a battery (set_use_battery=True), the desired battery state of charge at the end of each optimization cycle

My docker-compose.yml is:

version: "3.7"

services:
  emhass:
    image: davidusb/emhass-docker-standalone:latest
    container_name: EMHASS
    environment:
      - LOCAL_COSTFUN=profit
    ports:
      - 5000:5000
    volumes:
      - ./config_emhass.yaml:/app/config_emhass.yaml
      - ./secrets_emhass.yaml:/app/secrets_emhass.yaml
    restart: unless-stopped

Ufffff… It’s a long post :wink: Can you help?

You should pass the list of price values to the shell/rest command, according to your horizon.

I suggest you to create a shell/rest command instead of relying on the buttons.

You can also test your commands in the terminal if you installed the add-on.

I personally prefer to use the REST commands in my automations as I can pass the different elements as variables to compose the final command that will be executed by EMHASS. And I also find it easier with single/double quotes (always a pain) and escape characters.

Below an example of my REST commands:

# EMHASS
emhass_publish_data:
  url: http://localhost:5000/action/publish-data
  method: POST
  content_type: 'application/json'
  payload: '{}'

emhass_dayahead_optim:
  url: http://localhost:5000/action/dayahead-optim
  method: POST
  timeout: 30
  content_type: 'application/json'
  payload: '{ "pv_power_forecast": {{ pv_power_forecast }}, "delta_forecast": {{ delta_forecast }}, "num_def_loads": {{ num_def_loads }}, "P_deferrable_nom": {{ P_deferrable_nom }}, "def_total_hours": {{ def_total_hours }}, "treat_def_as_semi_cont": {{ treat_def_as_semi_cont }}, "set_def_constant": {{ set_def_constant }}, "load_cost_forecast": {{ load_cost_forecast }}, "prod_price_forecast": {{ prod_price_forecast }} }'

emhass_naive_mpc_optim:
  url: http://localhost:5000/action/naive-mpc-optim
  method: POST
  timeout: 30
  content_type: 'application/json'
  #model-type is ignored unless you use mlforecaster in the config file
  payload: >-
    { "prediction_horizon": {{ prediction_horizon }}, "pv_power_forecast": {{ pv_power_forecast }}, "num_def_loads": {{ num_def_loads }}, "P_deferrable_nom": {{ P_deferrable_nom }}, "def_total_hours": {{ def_total_hours }}, "treat_def_as_semi_cont": {{ treat_def_as_semi_cont }}, "set_def_constant": {{ set_def_constant }}, "soc_init": {{ soc_init }}, "soc_final": {{ soc_final }}, "load_cost_forecast": {{ load_cost_forecast }}, "prod_price_forecast": {{ prod_price_forecast }}, "alpha": {{ alpha }}, "beta": {{ beta }}, "model_type": {{ '"' ~ model_type ~ '"' }} }

emhass_forecast_model_fit:
  url: http://localhost:5000/action/forecast-model-fit
  method: POST
  timeout: 30
  content_type: 'application/json'
  payload: >-
    { "days_to_retrieve": {{ days_to_retrieve }}, "model_type": {{ '"' ~ model_type ~ '"' }}, "var_model": {{ '"' ~ var_model ~ '"' }}, "sklearn_model": {{ '"' ~ sklearn_model ~ '"' }}, "num_lags": {{ num_lags }}, "split_date_delta": {{ '"' ~ split_date_delta ~ '"' }}, "perform_backtest": {{ '"' ~ perform_backtest ~ '"' }} }

emhass_forecast_model_tune:
  url: http://localhost:5000/action/forecast-model-tune
  method: POST
  timeout: 30
  content_type: 'application/json'
  payload: >-
    { "days_to_retrieve": {{ days_to_retrieve }}, "model_type": {{ '"' ~ model_type ~ '"' }}, "var_model": {{ '"' ~ var_model ~ '"' }}, "sklearn_model": {{ '"' ~ sklearn_model ~ '"' }}, "num_lags": {{ num_lags }}, "split_date_delta": {{ '"' ~ split_date_delta ~ '"' }}, "perform_backtest": {{ '"' ~ perform_backtest ~ '"' }}  }
  
emhass_forecast_model_predict:
  url: http://localhost:5000/action/forecast-model-predict
  method: POST
  timeout: 30
  content_type: 'application/json'
  payload: >-
    { "days_to_retrieve": {{ days_to_retrieve }}, "model_type": {{ '"' ~ model_type ~ '"' }}, "var_model": {{ '"' ~ var_model ~ '"' }}, "model_predict_publish": {{ '"' ~ model_predict_publish ~ '"' }}, "model_predict_entity_id": {{ '"' ~ model_predict_entity_id ~ '"' }}, "model_predict_unit_of_measurement": {{ '"' ~ model_predict_unit_of_measurement ~ '"' }}, "model_predict_friendly_name": {{ '"' ~ model_predict_friendly_name ~ '"' }} }

emhass_dayahead_optim_scraper:
  url: http://localhost:5000/action/dayahead-optim
  method: POST
  timeout: 30
  content_type: 'application/json'
  payload: '{ "delta_forecast": {{ delta_forecast }}, "num_def_loads": {{ num_def_loads }}, "P_deferrable_nom": {{ P_deferrable_nom }}, "def_total_hours": {{ def_total_hours }}, "treat_def_as_semi_cont": {{ treat_def_as_semi_cont }}, "set_def_constant": {{ set_def_constant }}, "load_cost_forecast": {{ load_cost_forecast }}, "prod_price_forecast": {{ prod_price_forecast }} }'

emhass_naive_mpc_optim_scraper:
  url: http://localhost:5000/action/naive-mpc-optim
  method: POST
  timeout: 30
  content_type: 'application/json'
  #model-type is ignored unless you use mlforecaster in the config file
  payload: >-
    { "prediction_horizon": {{ prediction_horizon }}, "num_def_loads": {{ num_def_loads }}, "P_deferrable_nom": {{ P_deferrable_nom }}, "def_total_hours": {{ def_total_hours }}, "treat_def_as_semi_cont": {{ treat_def_as_semi_cont }}, "set_def_constant": {{ set_def_constant }}, "soc_init": {{ soc_init }}, "soc_final": {{ soc_final }}, "load_cost_forecast": {{ load_cost_forecast }}, "prod_price_forecast": {{ prod_price_forecast }}, "alpha": {{ alpha }}, "beta": {{ beta }}, "model_type": {{ '"' ~ model_type ~ '"' }} }

this is a dayhaead optim; try to explicitly pass to EMHASS all the accepted values when you run this specific command (some could be omitted, but in this way you are sure you are missing nothing).
This is the full payload for it:

payload: '{ "pv_power_forecast": {{ pv_power_forecast }}, "delta_forecast": {{ delta_forecast }}, "num_def_loads": {{ num_def_loads }}, "P_deferrable_nom": {{ P_deferrable_nom }}, "def_total_hours": {{ def_total_hours }}, "treat_def_as_semi_cont": {{ treat_def_as_semi_cont }}, "set_def_constant": {{ set_def_constant }}, "load_cost_forecast": {{ load_cost_forecast }}, "prod_price_forecast": {{ prod_price_forecast }} }'

And pay attention to single/double quotes and escape; they are always a pain.
I suggest you compose the command and test it using the terminal addon, once you get it working from there you can build your shell/REST command working on something you know is working.

EDIT
Of course if you don’t have the PV prediction, this is something you can’t pass. If you omit it EMHASS will switch to scraping mode. Refer to my previous post for some specific examples.

Very interesting project and great work done, but pretty steep learning curve with EMHASS.

My intial goal is to get it up and running and then figure out what i can do with it and then ultimate goal is to optimize my heat pump and EV charger.

I have Solar Panels and Nordpool electicity hourly prices and taking account of these want to optimise heating and ev charging. To make it more complex, taking account of electricity tariffs and fact that I sell electricity without VAT (20%) and buy with VAT. But these i will figure out later. Also i’m located in Estonia, and my current solar production is 0, because of snow and fact that there is almost no sun for the next two months.

I’m trying to do the inital set it up, but after 4 hours i’ms stuck.

I run emhass in docker. My docker-compose.yaml:

version: "3.6"
services:
  emhass:
    container_name: emhass
    image: davidusb/emhass-docker-standalone
    restart: unless-stopped
    ports:
      - "5001:5000"
    environment:
      - LOCAL_COSTFUN=profit
    volumes:
      - /home/arva/docker/emhass/config_emhass.yaml:/app/config_emhass.yaml
      - /home/arva/docker/emhass/secrets_emhass.yaml:/app/secrets_emhass.yaml
      - /home/arva/docker/emhass/data:/app/data

My emhass config file:

# Configuration file for EMHASS

retrieve_hass_conf:
  - freq: 60 # The time step to resample retrieved data from hass in minutes
  - days_to_retrieve: 2 # We will retrieve data from now and up to days_to_retrieve days
  - var_PV: 'sensor.inverter_active_power' # Photovoltaic produced power sensor in Watts
  - var_load: 'sensor.power_load_no_var_loads' # Household power consumption sensor in Watts (deferrable loads should be substracted)
  - load_negative: False # Set to True if the retrived load variable is negative by convention
  - set_zero_min: True # A special treatment for a minimum value saturation to zero. Values below zero are replaced by nans
  - var_replace_zero: # A list of retrived variables that we would want  to replace nans with zeros
    - 'sensor.inverter_active_power'
  - var_interp: # A list of retrived variables that we would want to interpolate nan values using linear interpolation
    - 'sensor.inverter_active_power'
    - 'sensor.power_load_no_var_load'
  - method_ts_round: 'nearest' # Set the method for timestamp rounding, options are: first, last and nearest

optim_conf:
  - set_use_battery: False # consider a battery storage
  - delta_forecast: 1 # days
  - num_def_loads: 2
  - P_deferrable_nom: # Watts
    - 2700.0
    - 4000.0
  - def_total_hours: # hours
    - 4
    - 10
  - treat_def_as_semi_cont: # treat this variable as semi continuous 
    - True
    - True
  - set_def_constant: # set as a constant fixed value variable with just one startup for each 24h
    - False
    - False
  - weather_forecast_method: 'scrapper' # options are 'scrapper' and 'csv'
  - load_forecast_method: 'naive' # options are 'csv' to load a custom load forecast from a CSV file or 'naive' for a persistance model
  - load_cost_forecast_method: 'hp_hc_periods' # options are 'hp_hc_periods' for peak and non-peak hours contracts and 'csv' to load custom cost from CSV file 
  - list_hp_periods: # list of different tariff periods (only needed if load_cost_forecast_method='hp_hc_periods')
    - period_hp_1:
      - start: '02:54'
      - end: '15:24'
    - period_hp_2:
      - start: '17:24'
      - end: '20:24'
  - load_cost_hp: 0.1907 # peak hours load cost in €/kWh (only needed if load_cost_forecast_method='hp_hc_periods')
  - load_cost_hc: 0.1419 # non-peak hours load cost in €/kWh (only needed if load_cost_forecast_method='hp_hc_periods')
  - prod_price_forecast_method: 'constant' # options are 'constant' for constant fixed value or 'csv' to load custom price forecast from a CSV file
  - prod_sell_price: 0.065 # power production selling price in €/kWh (only needed if prod_price_forecast_method='constant')
  - set_total_pv_sell: False # consider that all PV power is injected to the grid (self-consumption with total sell)
  - lp_solver: 'PULP_CBC_CMD' # set the name of the linear programming solver that will be used
  - lp_solver_path: 'empty' # set the path to the LP solver
  - set_nocharge_from_grid: False # avoid battery charging from the grid
  - set_nodischarge_to_grid: True # avoid battery discharging to the grid
  - set_battery_dynamic: False # add a constraint to limit the dynamic of the battery power in power per time unit
  - battery_dynamic_max: 0.9 # maximum dynamic positive power variation in percentage of battery maximum power
  - battery_dynamic_min: -0.9 # minimum dynamic negative power variation in percentage of battery maximum power

plant_conf:
  - P_grid_max: 10000 # The maximum power that can be supplied by the utility grid in Watts
  - module_model: # The PV module model
    - 'CSUN_Eurasia_Energy_Systems_Industry_and_Trade_CSUN295_60M'
  - inverter_model: # The PV inverter model
    - 'Fronius_International_GmbH__Fronius_Primo_5_0_1_208_240__240V_'
  - surface_tilt: # The tilt angle of your solar panels
    - 30
  - surface_azimuth: # The azimuth angle of your PV installation
    - 205
  - modules_per_string: # The number of modules per string
    - 16 
  - strings_per_inverter: # The number of used strings per inverter
    - 1
  - Pd_max: 1000 # If your system has a battery (set_use_battery=True), the maximum discharge power in Watts
  - Pc_max: 1000 # If your system has a battery (set_use_battery=True), the maximum charge power in Watts
  - eta_disch: 0.95 # If your system has a battery (set_use_battery=True), the discharge efficiency
  - eta_ch: 0.95 # If your system has a battery (set_use_battery=True), the charge efficiency
  - Enom: 5000 # If your system has a battery (set_use_battery=True), the total capacity of the battery stack in Wh
  - SOCmin: 0.3 # If your system has a battery (set_use_battery=True), the minimun allowable battery state of charge
  - SOCmax: 0.9 # If your system has a battery (set_use_battery=True), the minimun allowable battery state of charge
  - SOCtarget: 0.6 # If your system has a battery (set_use_battery=True), the desired battery state of charge at the end of each optimization cycle

Shell commands

shell_command:
  dayahead_optim: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://192.168.1.35:5001/action/dayahead-optim"
  publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://192.168.1.35:5001/action/publish-data"
  trigger_nordpool_forecast: "curl -i -H \"Content-Type: application/json\" -X POST -d '{\"load_cost_forecast\":{{((state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 'raw_today') | map(attribute='value') | list  + state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 'raw_tomorrow') | map(attribute='value') | list))[now().hour:][:24] }},\"prod_price_forecast\":{{((state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 'raw_today') | map(attribute='value') | list  + state_attr('sensor.nordpool_kwh_ee_eur_3_10_0', 'raw_tomorrow') | map(attribute='value') | list))[now().hour:][:24]}}}' http://192.168.1.35:5001/action/dayahead-optim"

HA sensors:

  - platform: template
    sensors:
      power_load_no_var_loads:
        friendly_name: "Power Load No Var Loads"
        unit_of_measurement: "W" 
        value_template: >-
          {{ (states('sensor.total_main_power') | float - 
              states('sensor.heat_pump_power') | float - 
              states('sensor.ev_charger_power_3') | float) | round(2) }}
        availability_template: >-
          {{
            is_number(states('sensor.total_main_power')) and
            is_number(states('sensor.heat_pump_power')) and
            is_number(states('sensor.ev_charger_power_3'))
          }}
  - platform: rest
    name: "Solcast Forecast East"
    json_attributes:
      - forecasts
    resource: https://api.solcast.com.au/rooftop_sites/xxxxxx/forecasts?format=json&api_key=xxxxxx&hours=24
    method: GET
    value_template: "{{ (value_json.forecasts[0].pv_estimate)|round(2) }}"
    unit_of_measurement: "kW"
    device_class: power
    scan_interval: 86400
    force_update: true

  - platform: rest
    name: "Solcast Forecast West"
    json_attributes:
      - forecasts
    resource: https://api.solcast.com.au/rooftop_sites/xxxxxx/forecasts?format=json&api_key=xxxxxxxx&hours=24
    method: GET
    value_template: "{{ (value_json.forecasts[0].pv_estimate)|round(2) }}"
    unit_of_measurement: "kW"
    device_class: power
    scan_interval: 86400
    force_update: true

  - platform: template
    sensors:
      solcast_24hrs_forecast_east:
        friendly_name: "Solcast 24hrs Forecast East"
        unit_of_measurement: 'W'
        value_template: >-
          {%- set power = state_attr('sensor.solcast_forecast_east', 'forecasts') | map(attribute='pv_estimate') | list %}
          {%- set values_all = namespace(all=[]) %}
          {% for i in range(power | length) %}
            {%- set v = (power[i] | float | multiply(1000) ) | int(0) %}
            {%- set values_all.all = values_all.all + [ v ] %}
          {%- endfor %} {{ (values_all.all)[:48] }}

      solcast_24hrs_forecast_west:
        friendly_name: "Solcast 24hrs Forecast West"
        unit_of_measurement: 'W'
        value_template: >-
          {%- set power = state_attr('sensor.solcast_forecast_west', 'forecasts') | map(attribute='pv_estimate') | list %}
          {%- set values_all = namespace(all=[]) %}
          {% for i in range(power | length) %}
            {%- set v = (power[i] | float | multiply(1000) ) | int(0) %}
            {%- set values_all.all = values_all.all + [ v ] %}
          {%- endfor %} {{ (values_all.all)[:48] }}

  - platform: template
    sensors:
      solcast_24hrs_forecast_combined:
        friendly_name: "Solcast 24hrs Forecast Combined"
        unit_of_measurement: 'W'
        value_template: >-
          {% set east = states("sensor.solcast_24hrs_forecast_east")[1:-1].split(',') | map('int') | list %}
          {% set west = states("sensor.solcast_24hrs_forecast_west")[1:-1].split(',') | map('int') | list %}
          {% set ns = namespace(items = []) %}
          {% for i in range(east | length) %}
            {% set ns.items = ns.items + [ east[i] + west[i] ] %}
          {% endfor %}
          {{ ns.items }}

Automations.yaml

- alias: EMHASS day-ahead optimization
  trigger:
    platform: time
    at: 05:30:00
  action:
  - service: shell_command.dayahead_optim
  id: 65cad7fa9ac04cbe8cd034f4335bd5a8
- alias: EMHASS publish data
  trigger:
  - minutes: /5
    platform: time_pattern
  action:
  - service: shell_command.publish_data
  id: 10f4028c36ff4ddabf64ceffd4c485a7

Logs after running EMHASS:

2023-11-27 15:02:07,589 - web_server - INFO - Launching the emhass webserver at: http://0.0.0.0:5000
2023-11-27 15:02:07,590 - web_server - INFO - Home Assistant data fetch will be performed using url: https://0qoww9tsaz9i5shxuci013tyqlk1qdsq.ui.nabu.casa/
2023-11-27 15:02:07,590 - web_server - INFO - The data path is: /app/data
2023-11-27 15:02:07,595 - web_server - INFO - Using core emhass version: 0.5.1
waitress   INFO  Serving on http://0.0.0.0:5000
2023-11-27 15:02:45,373 - web_server - INFO - Setting up needed data
2023-11-27 15:02:45,419 - web_server - INFO - Retrieving weather forecast data using method = scrapper
2023-11-27 15:02:47,805 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2023-11-27 15:02:47,805 - web_server - INFO - Retrieve hass get data method initiated...
2023-11-27 15:02:48,208 - web_server - ERROR - The retrieved JSON is empty, check that correct day or variable names are passed
2023-11-27 15:02:48,208 - web_server - ERROR - Either the names of the passed variables are not correct or days_to_retrieve is larger than the recorded history of your sensor (check your recorder settings)
2023-11-27 15:02:48,209 - web_server - ERROR - Exception on /action/dayahead-optim [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1455, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 869, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 867, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 852, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "src/emhass/web_server.py", line 179, in action_call
    input_data_dict = set_input_data_dict(config_path, str(data_path), costfun,
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/command_line.py", line 91, in set_input_data_dict
    P_load_forecast = fcst.get_load_forecast(method=optim_conf['load_forecast_method'])
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/forecast.py", line 585, in get_load_forecast
    rh.get_data(days_list, var_list)
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/retrieve_hass.py", line 147, in get_data
    self.df_final = pd.concat([self.df_final, df_day], axis=0)
UnboundLocalError: local variable 'df_day' referenced before assignment
2023-11-27 15:04:18,441 - web_server - INFO - Launching the emhass webserver at: http://0.0.0.0:5000
2023-11-27 15:04:18,441 - web_server - INFO - Home Assistant data fetch will be performed using url: https://0qoww9tsaz9i5shxuci013tyqlk1qdsq.ui.nabu.casa/
2023-11-27 15:04:18,441 - web_server - INFO - The data path is: /app/data
2023-11-27 15:04:18,445 - web_server - INFO - Using core emhass version: 0.5.1
waitress   INFO  Serving on http://0.0.0.0:5000

Logs when i run Nordpool shell command:

2023-11-27 15:05:00,093 - web_server - INFO - Setting up needed data
2023-11-27 15:05:00,138 - web_server - INFO -  >> Publishing data...
2023-11-27 15:05:00,138 - web_server - INFO - Publishing data to HASS instance
2023-11-27 15:05:00,138 - web_server - ERROR - File not found error, run an optimization task first.
2023-11-27 15:05:00,220 - web_server - INFO - Setting up needed data
2023-11-27 15:05:00,222 - web_server - INFO -  >> Publishing data...
2023-11-27 15:05:00,223 - web_server - INFO - Publishing data to HASS instance
2023-11-27 15:05:00,223 - web_server - ERROR - File not found error, run an optimization task first.
2023-11-27 15:05:27,187 - web_server - INFO - Setting up needed data
2023-11-27 15:05:27,192 - web_server - INFO - Retrieving weather forecast data using method = scrapper
2023-11-27 15:05:28,678 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2023-11-27 15:05:28,679 - web_server - INFO - Retrieve hass get data method initiated...
2023-11-27 15:05:29,069 - web_server - ERROR - The retrieved JSON is empty, check that correct day or variable names are passed
2023-11-27 15:05:29,069 - web_server - ERROR - Either the names of the passed variables are not correct or days_to_retrieve is larger than the recorded history of your sensor (check your recorder settings)
2023-11-27 15:05:29,070 - web_server - ERROR - Exception on /action/dayahead-optim [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1455, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 869, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 867, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 852, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "src/emhass/web_server.py", line 179, in action_call
    input_data_dict = set_input_data_dict(config_path, str(data_path), costfun,
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/command_line.py", line 91, in set_input_data_dict
    P_load_forecast = fcst.get_load_forecast(method=optim_conf['load_forecast_method'])
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/forecast.py", line 585, in get_load_forecast
    rh.get_data(days_list, var_list)
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/retrieve_hass.py", line 147, in get_data
    self.df_final = pd.concat([self.df_final, df_day], axis=0)
UnboundLocalError: local variable 'df_day' referenced before assignment

Logs when i push “day ahead optimization” in EMHASS UI

2023-11-27 15:06:35,515 - web_server - INFO - Setting up needed data
2023-11-27 15:06:35,529 - web_server - INFO - Retrieving weather forecast data using method = scrapper
2023-11-27 15:06:37,036 - web_server - INFO - Retrieving data from hass for load forecast using method = naive
2023-11-27 15:06:37,036 - web_server - INFO - Retrieve hass get data method initiated...
2023-11-27 15:06:37,420 - web_server - ERROR - The retrieved JSON is empty, check that correct day or variable names are passed
2023-11-27 15:06:37,421 - web_server - ERROR - Either the names of the passed variables are not correct or days_to_retrieve is larger than the recorded history of your sensor (check your recorder settings)
2023-11-27 15:06:37,421 - web_server - ERROR - Exception on /action/dayahead-optim [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 1455, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 869, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 867, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.8/site-packages/flask/app.py", line 852, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "src/emhass/web_server.py", line 179, in action_call
    input_data_dict = set_input_data_dict(config_path, str(data_path), costfun,
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/command_line.py", line 91, in set_input_data_dict
    P_load_forecast = fcst.get_load_forecast(method=optim_conf['load_forecast_method'])
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/forecast.py", line 585, in get_load_forecast
    rh.get_data(days_list, var_list)
  File "/usr/local/lib/python3.8/site-packages/emhass-0.5.1-py3.8.egg/emhass/retrieve_hass.py", line 147, in get_data
    self.df_final = pd.concat([self.df_final, df_day], axis=0)
UnboundLocalError: local variable 'df_day' referenced before assignment

Your error has been discussed before, perhaps this will help:

On the prices excluding taxes, you can setup 2 nordpool instances, one with and one without tax. These can each go into a different variable, one for load_cost_forecast (including tax), the other for prod_price_forecast (excluding tax)

Good luck!