EMHASS: An Energy Management for Home Assistant

You use kW it seems

{
  "pv_power_forecast": [
    7.4181, 9.0889, 10.5273, 11.7604, 12.7256, 13.5107, 14.0035, 14.2905, 14.3557, 14.1663, 13.756, 13.1713, 12.2564, 11.1387, 9.7234, 7.7384, 5.5771, 3.6349, 1.7682, 0.3907,
    0.0287, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2146, 1.0774, 2.6719, 4.2955
  ]

I use W instead of kW, also for the battery settings. Don’t know if that’s the issue what you see.

Thank you so much! That was definitely an error!

It’s still not reporting any battery optimisations. I have battery configuration in Watts, and soc_init and soc_final as a value between 0-1.

  "battery_charge_efficiency": 0.95,
  "battery_charge_power_max": 4997,
  "battery_discharge_efficiency": 0.95,
  "battery_discharge_power_max": 5000,
  "battery_dynamic_max": 0.9,
  "battery_dynamic_min": -0.9,
  "battery_maximum_state_of_charge": 0.99,
  "battery_minimum_state_of_charge": 0.05,
  "battery_nominal_energy_capacity": 10000,
  "battery_target_state_of_charge": 0.1,

Any clue?

It almost feels like the battery is not configured / enabled in the settings. Or the Forbid charging battery from or from the grid is turned on. What settings under Battery have you configured.

Yes it seems that way, very odd. Here’s the config:

  "set_nocharge_from_grid": false,
  "set_nodischarge_to_grid": false,
  "set_total_pv_sell": false,
  "set_use_battery": true,
  "set_use_pv": true,

(full config here)

I have some questions about the configuration of my inverters and my pv strings.
I have 1 inverter (Huawei SUN2000-4KTL-M1) which has 2 strings in different orientations.
One string with 14 panels azimut 227 degrees, declination of 44 degrees.
And another with 10 panels azimut 138 degrees, declination of 45 degrees.
Those are SANYO_ELECTRIC_CO_LTD_OF_PANASONIC_GROUP_VBHN235SA04 panels (Why can’t cant you copy this name in the streamlit app??)

And I have another inverter, an Solaredge SE3000 with one string with 12 panels,
azimut of 160 degrees and a declination of 30 degrees.
Those panels are Talesun_Solar_TP660M_290

How do I configure this in the emhass.json file.

Another question about the inverters, There are almost no european inverters.
Can I add my inverters to emhass/src/emhass/data/emhass_inverters.csv at cb2a93adf28d41387881d4024eb718fb1d0d26e6 · davidusb-geek/emhass · GitHub ? I think I can find all values online except the C values, are ALL those values needed?

I think this is not good:

“weight_battery_charge”: 1,
“weight_battery_discharge”: 1

I have:

“weight_battery_charge”: 0.05,
“weight_battery_discharge”: 0.05

from recommendation in this thread.

Sounds promising, mine config is:
“weight_battery_charge”: 0
“weight_battery_discharge”: 0.04

I use this to say “only discharge from the battery if I will get a profit of at least 4 cents” otherwise the wear-and-tear on the battery is not worth discharging for 1 - 3 cents profit over the life of the battery as it’ll be discharging slightly less frequently and “do not jump on any minor pricing change” (i.e. previously my battery was charging overnight from the grid when it thought it would make a profit in the morning peak but due to predictions changing overnight by the time morning came the price would only reflect a 1 or 2 c profit, or worse still actually be cheaper so it wasn’t profitable at all)

With your current setting of 1 for each it’ll be like saying I’ve got to make $1 (1 Euro, or whatever your currency is) per kwh on the charge rate, and then also $1 pre kwh on the discharge for it to be worth using the battery.

3 Likes

That’s exactly it, thank you so much @RudolfRendier and @altonius for your help!

1 Like

Is it possible to insert a sensor that can dynamically adjust the attenuation or threshold on PV output, such as 100% for full production, 80% for partial attenuation, or lower values depending on factors like snow coverage, shading, temperature, or grid constraints? This would enable real-time optimization of solar energy production by scaling output based on environmental and operational conditions.

Hi @mohssinDR, can you please share how you configured HASS to predict the hot water temperature with KNN (I expect with EMHASS)? I want to do exactly the same but this is new for me. Your graph looks so promising!

1 Like

Hey @bkleef, Sure here is the code that you should put at your configuration.yaml file. Also, since my last post I have experienced with the days_to_retrieve argument in order to get better predictions. In my case 15 days works great as you can see in this screenshot. Also, you should create automations for each command (fit, predict and tune) calling the curl command using an adequate time frequency for the predict endpoint at least. Finally, In my case the tune endpoint is triggered by a weather API (rather than time) upon detecting bad weather conditions or a significant outdoor temperature drop.

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"
  
  
  emhass_ml_temperature_fit: 'curl -i -H ''Content-Type:application/json'' -X POST -d ''{"model_type": "room_temperature_forecast", "var_model": "sensor.your_sensor", "days_to_retrieve": 15}'' http://localhost:5000/action/forecast-model-fit'
  emhass_ml_temperature_predict: 'curl -i -H ''Content-Type:application/json'' -X POST -d ''{"model_type": "room_temperature_forecast", "var_model": "sensor.sensor", "days_to_retrieve": 15, "model_predict_publish": true, "model_predict_entity_id": "sensor.p_HotWater_forecast_model", "model_predict_unit_of_measurement": "°C", "model_predict_friendly_name": "Hot Water Temperature Forecast custom ML model"}'' http://localhost:5000/action/forecast-model-predict'
  emhass_ml_temperature_tune: 'curl -i -H ''Content-Type:application/json'' -X POST -d ''{"model_type": "room_temperature_forecast", "var_model": "sensor.sensor", "days_to_retrieve": 15}'' http://localhost:5000/action/forecast-model-tune'

3 Likes

Dear all, I just realize that the new open-meteo implementation does not survive DST change coming up this week-end.
We will fix this later.
But for now change the PV forecast method to something else. In my case changing to solar.forecast allowed me today to run my optimization without issue

I’m now seeing the same behaviour using open meteo:

File “tzconversion.pyx”, line 431, in pandas._libs.tslibs.tzconversion.tz_localize_to_utc
pytz.exceptions.NonExistentTimeError: 2025-03-30 02:00:00

I’m facing this, maybe the same, problem.


[2025-03-28 20:00:04 +0100] [23] [INFO] Retrieving weather forecast data using method = open-meteo
[2025-03-28 20:00:04 +0100] [23] [ERROR] Exception on /action/naive-mpc-optim [POST]
Traceback (most recent call last):
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 1511, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 919, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 917, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 902, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/src/emhass/web_server.py", line 414, in action_call
    input_data_dict = set_input_data_dict(
                      ^^^^^^^^^^^^^^^^^^^^
  File "/app/src/emhass/command_line.py", line 299, in set_input_data_dict
    df_weather = fcst.get_weather_forecast(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/src/emhass/forecast.py", line 272, in get_weather_forecast
    data_15min.index = data_15min.index.tz_localize(self.time_zone)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/pandas/core/indexes/datetimes.py", line 293, in tz_localize
    arr = self._data.tz_localize(tz, ambiguous, nonexistent)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/pandas/core/arrays/_mixins.py", line 81, in method
    return meth(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/pandas/core/arrays/datetimes.py", line 1088, in tz_localize
    new_dates = tzconversion.tz_localize_to_utc(
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "tzconversion.pyx", line 431, in pandas._libs.tslibs.tzconversion.tz_localize_to_utc
pytz.exceptions.NonExistentTimeError: 2025-03-30 02:00:00

Yes the same problem. Change you PV forecast method like I said before. On Sunday this problem would have dissappeared

Anyone has this one?:

[2025-03-29 15:50:02 +0100] [21] [INFO] Publishing data to HASS instance

/app/src/emhass/command_line.py:1131: FutureWarning:

In a future version of pandas, parsing datetimes with mixed time zones will raise an error unless `utc=True`. Please specify `utc=True` to opt in to the new behaviour and silence this warning. To create a `Series` with mixed offsets and `object` dtype, please use `apply` and `datetime.datetime.strptime`

Yesterday yes, I believe that was related to DST because my MPC’s horizon was 3.30 AM. Today I haven’t seen it. (but I also upgraded to 0.12.8, maybe that solved it)

who can help me with the following:

  1. What does your automation look like for controlling your home battery? How do you determine if it should charge from the grid or via the solar panels?
  2. Can someone give me an example of how you create the graphs in your dashboard. I am unable to visualize future data now.
  3. How can you run an emhass version for the temperature in addition to the emhass for the power?
  1. I don’t think you can, there’s are request to publish the power source:
    Feature request: show where battery is charging from · Issue #422 · davidusb-geek/emhass · GitHub

  2. Look for apex in this thread, MarkPurcell has posted some nice charts with that

  3. You don’t need to run two versions. The thermal model can be used in the regular version, you need to supply a specific payload

I am having problem to uppdate from emhass 10.6 to the last version. In version 10.6 evrything is working fine. I don’t know what I am missing. can some one help me out.

Here are some loggs

[2025-04-01 18:19:48 +0300] [21] [INFO] Passed runtime parameters: {'prediction_horizon': 24, 'soc_init': 1.0, 'soc_final': 1, 'num_def_loads': 6, 'def_total_hours': [3, 1, 1, 1, 1, 1], 'P_deferrable_nom': [2000, 1000, 1100, 1500, 500, 500], 'treat_def_as_semi_cont': [True, True, True, True, False, False], 'def_start_timestep': [0, 0, 0, 0, 0, 0], 'def_end_timestep': [12, 12, 12, 12, 6, 6], 'set_def_constant': [False, False, False, False, True, True], 'load_cost_forecast': [0.1023, 0.09354, 0.08046, 0.07961, 0.0773, 0.07717, 0.07675, 0.07478, 0.07429, 0.07415, 0.07461, 0.07474, 0.07616, 0.07717, 0.0774, 0.07917, 0.07982, 0.08083, 0.07867, 0.07715, 0.07675, 0.07682, 0.07715, 0.08004], 'prod_price_forecast': [0.0154, 0.00842, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'pv_power_forecast': [1007, 405, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 405, 842, 1500, 3364, 5342, 6187, 6737, 6857, 6256, 5390, 4207], 'alpha': 0.2, 'beta': 0.8}
[2025-04-01 18:19:48 +0300] [21] [INFO]  >> Setting input data dict
[2025-04-01 18:19:48 +0300] [21] [INFO] Setting up needed data
[2025-04-01 18:19:48 +0300] [21] [INFO] Retrieve hass get data method initiated...
[2025-04-01 18:19:49 +0300] [21] [ERROR] Exception on /action/naive-mpc-optim [POST]
Traceback (most recent call last):
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 1511, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 919, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 917, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/flask/app.py", line 902, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/src/emhass/web_server.py", line 414, in action_call
    input_data_dict = set_input_data_dict(
                      ^^^^^^^^^^^^^^^^^^^^
  File "/app/src/emhass/command_line.py", line 284, in set_input_data_dict
    if not rh.prepare_data(
           ^^^^^^^^^^^^^^^^
  File "/app/src/emhass/retrieve_hass.py", line 401, in prepare_data
    self.df_final[new_var_interp] = self.df_final[new_var_interp].interpolate(
                                    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/pandas/core/frame.py", line 4108, in __getitem__
    indexer = self.columns._get_indexer_strict(key, "columns")[1]
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py", line 6200, in _get_indexer_strict
    self._raise_if_missing(keyarr, indexer, axis_name)
  File "/app/.venv/lib/python3.12/site-packages/pandas/core/indexes/base.py", line 6252, in _raise_if_missing
    raise KeyError(f"{not_found} not in index")
KeyError: "['sensor.total_dc_power'] not in index"
[2025-04-01 18:20:04 +0300] [21] [INFO]  >> Obtaining params: 
[2025-04-01 18:20:04 +0300] [21] [INFO] Passed runtime parameters: {}
[2025-04-01 18:20:04 +0300] [21] [INFO]  >> Setting input data dict
[2025-04-01 18:20:04 +0300] [21] [INFO] Setting up needed data
[2025-04-01 18:20:04 +0300] [21] [INFO] Continual publish thread service started
[2025-04-01 18:20:04 +0300] [21] [INFO]  >> Publishing data...
[2025-04-01 18:20:04 +0300] [21] [INFO] Publishing data to HASS instance
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_pv_forecast = 3033.43
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_load_forecast = 891.16
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_hybrid_inverter = 3891.16
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_deferrable0 = 2000.0
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_deferrable1 = 0.0
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_deferrable2 = 0.0
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_deferrable3 = 0.0
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_deferrable4 = 500.0
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_batt_forecast = 857.73
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.soc_batt_forecast = 90.6
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.p_grid_forecast = 0.0
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.total_cost_fun_value = -0.7
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.optim_status = Optimal
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.unit_load_cost = 0.1023
[2025-04-01 18:20:04 +0300] [21] [INFO] Successfully posted to sensor.unit_prod_price = 0.0154

my config:

{
  "battery_charge_efficiency": 0.95,
  "battery_charge_power_max": 5000,
  "battery_discharge_efficiency": 0.95,
  "battery_discharge_power_max": 5000,
  "battery_dynamic_max": 0.9,
  "battery_dynamic_min": -0.9,
  "battery_maximum_state_of_charge": 1,
  "battery_minimum_state_of_charge": 0,
  "battery_nominal_energy_capacity": 9500,
  "battery_target_state_of_charge": 1,
  "compute_curtailment": false,
  "continual_publish": true,
  "costfun": "profit",
  "delta_forecast_daily": 1,
  "end_timesteps_of_each_deferrable_load": [
    0,
    0,
    0,
    0,
    0
  ],
  "historic_days_to_retrieve": 2,
  "inverter_is_hybrid": true,
  "load_cost_forecast_method": "csv",
  "load_forecast_method": "naive",
  "load_negative": false,
  "load_offpeak_hours_cost": 0.1419,
  "load_peak_hour_periods": {
    "period_hp_1": [
      {
        "start": "02:54"
      },
      {
        "end": "15:24"
      }
    ],
    "period_hp_2": [
      {
        "start": "17:24"
      },
      {
        "end": "20:24"
      }
    ]
  },
  "load_peak_hours_cost": 0.1907,
  "logging_level": "INFO",
  "lp_solver": "default",
  "lp_solver_path": "empty",
  "maximum_power_from_grid": 9000,
  "maximum_power_to_grid": 9000,
  "method_ts_round": "nearest",
  "modules_per_string": [
    16
  ],
  "nominal_power_of_deferrable_loads": [
    2000,
    1000,
    1000,
    1000,
    1000
  ],
  "number_of_deferrable_loads": 5,
  "operating_hours_of_each_deferrable_load": [
    2,
    1,
    1,
    1,
    2
  ],
  "optimization_time_step": 60,
  "photovoltaic_production_sell_price": 0.1419,
  "production_price_forecast_method": "csv",
  "pv_inverter_model": [
    "Fronius_International_GmbH__Fronius_Primo_5_0_1_208_240__240V_"
  ],
  "pv_module_model": [
    "CSUN_Eurasia_Energy_Systems_Industry_and_Trade_CSUN295_60M"
  ],
  "sensor_linear_interp": [
    "sensor.total_dc_power",
    "sensor.power_load_no_var_loads"
  ],
  "sensor_power_load_no_var_loads": "sensor.power_load_no_var_loads",
  "sensor_power_photovoltaics": "sensor.total_dc_power",
  "sensor_replace_zero": [
    "sensor.total_dc_power"
  ],
  "set_battery_dynamic": true,
  "set_deferrable_load_single_constant": [
    false,
    false,
    false,
    false,
    false
  ],
  "set_deferrable_startup_penalty": [
    0,
    0,
    0,
    0,
    0
  ],
  "set_nocharge_from_grid": false,
  "set_nodischarge_to_grid": true,
  "set_total_pv_sell": false,
  "set_use_battery": true,
  "set_use_pv": false,
  "set_zero_min": true,
  "start_timesteps_of_each_deferrable_load": [
    0,
    0,
    0,
    0,
    0
  ],
  "strings_per_inverter": [
    1
  ],
  "surface_azimuth": [
    205
  ],
  "surface_tilt": [
    30
  ],
  "treat_deferrable_load_as_semi_cont": [
    true,
    true,
    true,
    true,
    false
  ],
  "weather_forecast_method": "csv",
  "weight_battery_charge": 1,
  "weight_battery_discharge": 1
}

In config I am using the these rest commands

# run by script
emhass_naive_mpc_optim_script:
  url: http://localhost:5000/action/naive-mpc-optim
  method: POST
  content_type: "application/json"
  payload: >
    { {{json}} }

# publish data via Rest command
publish_data2:
  url: http://localhost:5000/action/publish-data
  method: POST
  content_type: "application/json"
  payload: "{}"

The script that I am using:

alias: EMHASS Naive MPC optim call
mode: single
variables:
  json_string: >
    {%- set nordpool=((state_attr('sensor.nordpool_kwh_fi_eur_6_10_0',
    'raw_today') | map(attribute='value') | list+
    state_attr('sensor.nordpool_kwh_fi_eur_6_10_0', 'raw_tomorrow') |
    map(attribute='value') | list))[now().hour:][:24] %} {%- set nordpool_sell =
    namespace(values=[]) %} {%- set nordpool_buy = namespace(values=[]) %} {%-
    for i in nordpool %}   {%- set
    nordpool_sell.values=nordpool_sell.values+[(((i)-(states('input_number.export_margnial')|float*0.01))
    |round(5),0)|max]%} {%- set
    nordpool_buy.values=nordpool_buy.values+[(i*((states('input_number.moms_skatt')|float/100)+1)+(states('input_number.import_marginal')|float*0.01)+(states('input_number.overforings_avgift_import')|float*0.01)+(states('input_number.elskatt')|float)*0.01)|round(5)]%}
    {%- endfor %} {%- set
    solcast=((state_attr('sensor.solcast_pv_forecast_forecast_today','detailedHourly')
    | map(attribute='pv_estimate') | map('multiply',1000) | map('int') | list +
    state_attr('sensor.solcast_pv_forecast_forecast_tomorrow', 'detailedHourly')
    | map(attribute='pv_estimate') | map('multiply',1000) | map('int') |
    list))[now().hour:][:24] %} {%- set soc_init =
    min(max((states('sensor.battery_level')|float(0)/100)|round(2),0.1),1)%} 
    {%-set horizon = [nordpool_sell.values|length,
    nordpool_buy.values|length,solcast|length]|min %} "prediction_horizon": {{
    horizon }}, "soc_init":{{soc_init }} , "soc_final": 1,  "num_def_loads":
    6,   "def_total_hours":[3,1,1,1,1,1],   "P_deferrable_nom":
    [2000,1000,1100,1500,500,500],
    "treat_def_as_semi_cont":[true,true,true,true,false,false],
    "def_start_timestep":[0,0,0,0,0,0], "def_end_timestep":[12,12,12,12,6,6],
    "set_def_constant": [false,false,false,false,true,true],
    "load_cost_forecast":{{nordpool_buy.values}},  "prod_price_forecast":
    {{nordpool_sell.values}}, "pv_power_forecast":{{solcast}}, "alpha": 0.2, 
    "beta": 0.8   
sequence:
  - data:
      json: "{{ json_string }}"
    action: rest_command.emhass_naive_mpc_optim_script

And some automation to run everything:

alias: EMHASS mpc optim script
description: ""
triggers:
  - minutes: /5
    trigger: time_pattern
  - event: start
    trigger: homeassistant
conditions: []
actions:
  - delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
  - action: script.1694970038100
    data: {}
  - delay:
      hours: 0
      minutes: 0
      seconds: 15
      milliseconds: 0
  - data: {}
    action: rest_command.publish_data2
mode: single

And as I said before. In 10.6 everything is working fine… But not in 12.8