EMHASS: An Energy Management for Home Assistant

Do you mean this one?

Correct.
This dump shows you what you can do with the Sonnen API
https://jlunz.github.io/homeassistant/

1 Like

The 0w laadpaal where you drew the red rectangles is the current situation (aka now). If you look at the purple laadpaal graph there are moments planned where the value is’t zero. First graph: short 7000w peak around 13u and then 5000w constant during the night.

Searching for an answer some time, but cannot find it in this topic, though it is addressed several times. The problem is the path to the COIN_CMD solver. Getting the message that I have to add the path to that solver. Default: /usr/bin/cbc. Opening the container, there is no directory cbc. If I print the solverlist (>> plp.pulpTestAll()), all solvers are found: Available solvers: [β€˜GLPK_CMD’, β€˜PULP_CBC_CMD’, β€˜COIN_CMD’].
So the question is: where are those solvers stored?

It is mostly the inverter you are integrating with, not the battery. Huawei hybrid inverters over modbus tcp/rtu work quite well trough the GitHub - wlcrs/huawei_solar: Home Assistant integration for Huawei Solar inverters via Modbus integration
Also heard good things about the sonnen batteries.

Basicly a Google search for β€œinvertorbrand you want to buy home assistant integration” is a good plan before making a purchace (same for any device you want to buy actually :wink: )

If you already have solar, a hybrid inverter is the better choice for the battery to avoid the losses converting from DC (solar) to AC and back to DC (battery)

Well that’s a newer version than the 5 year old battery I have but yes.

You can find documentation for the Sonnen eco battery REST API in the following resources:

  1. GitHub - Udhold/SonnenBatteryAPI: This repository provides a Node.js wrapper for interacting with Sonnen’s API. It details functions to fetch data like battery status, operating modes, and configurations, along with examples of API usage. You can explore it here【9】.

  2. Logic Machine Forum: A forum post includes examples and a link to a PDF containing the Sonnen API v2 documentation, with instructions for integrating it into automation systems. The API allows fetching data such as energy consumption, production, and battery status. You can access the details here【10】.
    .

I already have solar panels.
1 Huawei inverter with 5 kW peak (on the house) and a Solaredge with 3 kW peak (on a separate garage).
There is no place at all to put batteries where the Huawei inverter is located.
There is room in my garage.
If I only have 1 set of solar panels, I would like to change the inverter, but I would still like to opt for an AC coupled inverter.
To get back to the Sonnen, we think it is too expensive, I think you pay a little extra because it is beautifully finished. And I would like to hide all of this in a corner in the garage.

The 0W indeed shows the current expected power of the laadpaal/chargepoint.
My point is that this value always remains zero, because in every optimization run, the power curve gets pushed forward.

Looking at the screenshots, in the first run you’ll see that the car should start charging at 13h. By the time we reach 13h, a new optimization run changes the power curve, pushing it forward, so no charging starts at 13h.

And this goes on forever, the planned power curve of my deferrable load gets pushed forward into the future.
I haven’t found the cause or solution yet.

Do you have a fixed prediction horizon?

I see, I thought those where day ahead graphs.

2024-11-24 14:40:02,372 - web_server - INFO - Successfully posted to sensor.mpc_p_deferrable1 = 0.0
2024-11-24 14:40:02,374 - web_server - DEBUG - Saved sensor.mpc_p_deferrable1 to json file
2024-11-24 14:40:02,377 - web_server - INFO - Successfully posted to sensor.mpc_p_batt_forecast = -47.27
2024-11-24 14:40:02,380 - web_server - DEBUG - Saved sensor.mpc_p_batt_forecast to json file
2024-11-24 14:40:02,383 - web_server - INFO - Successfully posted to sensor.mpc_soc_batt_forecast = 54.83
2024-11-24 14:40:02,386 - web_server - DEBUG - Saved sensor.mpc_soc_batt_forecast to json file
2024-11-24 14:40:02,389 - web_server - INFO - Successfully posted to sensor.mpc_p_grid_forecast = 0.0
2024-11-24 14:40:02,391 - web_server - DEBUG - Saved sensor.mpc_p_grid_forecast to json file
2024-11-24 14:40:02,394 - web_server - INFO - Successfully posted to sensor.mpc_total_cost_fun_value = -1.59
2024-11-24 14:40:02,396 - web_server - DEBUG - Saved sensor.mpc_total_cost_fun_value to json file
2024-11-24 14:40:02,397 - web_server - INFO - Successfully posted to sensor.mpc_optim_status = Optimal
2024-11-24 14:40:02,399 - web_server - DEBUG - Saved sensor.mpc_optim_status to json file
2024-11-24 14:40:02,402 - web_server - INFO - Successfully posted to sensor.mpc_unit_load_cost = 0.264
2024-11-24 14:40:02,404 - web_server - DEBUG - Saved sensor.mpc_unit_load_cost to json file
2024-11-24 14:40:02,407 - web_server - INFO - Successfully posted to sensor.mpc_unit_prod_price = 0.055
2024-11-24 14:40:02,409 - web_server - DEBUG - Saved sensor.mpc_unit_prod_price to json file
2024-11-24 14:40:12,899 - web_server - INFO - Passed runtime parameters: {'publish_prefix': 'mpc_'}
2024-11-24 14:40:12,904 - web_server - INFO -  >> Setting input data dict
2024-11-24 14:40:12,904 - web_server - INFO - Setting up needed data
2024-11-24 14:40:12,931 - web_server - WARNING - warning `days_to_retrieve` is set to a value less than 9, this could cause an error with the fit
2024-11-24 14:40:12,932 - web_server - WARNING - setting`passed_data:days_to_retrieve` to 9 for fit/predict/tune
2024-11-24 14:40:12,936 - web_server - INFO -  >> Publishing data...
2024-11-24 14:40:12,936 - web_server - INFO - Publishing data to HASS instance
2024-11-24 14:40:12,990 - web_server - INFO - Successfully posted to sensor.mpc_soc_batt_forecast = 54.83
2024-11-24 14:40:13,008 - web_server - INFO - Successfully posted to sensor.mpc_p_pv_forecast = 283.97
2024-11-24 14:40:13,024 - web_server - INFO - Successfully posted to sensor.mpc_p_load_forecast = 236.7
2024-11-24 14:40:13,038 - web_server - INFO - Successfully posted to sensor.mpc_total_cost_fun_value = -1.59
2024-11-24 14:40:13,055 - web_server - INFO - Successfully posted to sensor.mpc_p_batt_forecast = -47.27
2024-11-24 14:40:13,070 - web_server - INFO - Successfully posted to sensor.mpc_unit_prod_price = 0.055
2024-11-24 14:40:13,085 - web_server - INFO - Successfully posted to sensor.mpc_p_deferrable0 = 0
2024-11-24 14:40:13,101 - web_server - INFO - Successfully posted to sensor.mpc_unit_load_cost = 0.264
2024-11-24 14:40:13,121 - web_server - INFO - Successfully posted to sensor.mpc_p_deferrable1 = 0
2024-11-24 14:40:13,138 - web_server - INFO - Successfully posted to sensor.mpc_optim_status = Optimal
2024-11-24 14:40:13,158 - web_server - INFO - Successfully posted to sensor.mpc_p_grid_forecast = 0.0
2024-11-24 14:40:22,924 - web_server - INFO - Passed runtime parameters: {'publish_prefix': '', 'prediction_horizon': 19, 'alpha': 0.25, 'beta': 0.75, 'battery_maximum_state_of_charge': 1, 'battery_minimum_state_of_charge': 0.1, 'battery_target_state_of_charge': 0.26, 'battery_discharge_power_max': 400, 'battery_charge_power_max': 340, 'prod_price_forecast': [0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055, 0.055], 'soc_final': 0.26, 'soc_init': 0.54, 'number_of_deferrable_loads': 2, 'operating_hours_of_each_deferrable_load': [0, 0], 'pv_power_forecast': [264, 171, 81, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'load_power_forecast': [237, 653, 762, 639, 681, 606, 703, 683, 703, 1911, 855, 762, 806, 747, 1023, 526, 492, 566, 578], 'load_cost_forecast': [0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402, 0.26402], 'weather_forecast_cache_only': True}
2024-11-24 14:40:22,928 - web_server - INFO -  >> Setting input data dict
2024-11-24 14:40:22,928 - web_server - INFO - Setting up needed data
2024-11-24 14:40:22,963 - web_server - WARNING - warning `days_to_retrieve` is set to a value less than 9, this could cause an error with the fit
2024-11-24 14:40:22,964 - web_server - WARNING - setting`passed_data:days_to_retrieve` to 9 for fit/predict/tune
2024-11-24 14:40:22,969 - web_server - INFO - Retrieve hass get data method initiated...
2024-11-24 14:40:24,410 - web_server - INFO - Retrieving weather forecast data using method = list
2024-11-24 14:40:24,414 - web_server - INFO -  >> Performing naive MPC optimization...
2024-11-24 14:40:24,415 - web_server - INFO - Performing naive MPC optimization
2024-11-24 14:40:24,425 - web_server - INFO - Perform an iteration of a naive MPC controller
2024-11-24 14:40:24,471 - web_server - DEBUG - Deferrable load 0: Proposed optimization window: 0 --> 0
2024-11-24 14:40:24,471 - web_server - DEBUG - Deferrable load 0: Validated optimization window: 0 --> 0
2024-11-24 14:40:24,474 - web_server - DEBUG - Deferrable load 1: Proposed optimization window: 0 --> 0
2024-11-24 14:40:24,474 - web_server - DEBUG - Deferrable load 1: Validated optimization window: 0 --> 0
2024-11-24 14:40:24,484 - 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/81707bd2abd9405a814a90f4607cfd9d-pulp.mps -max -timeMode elapsed -branch -printingOptions all -solution /tmp/81707bd2abd9405a814a90f4607cfd9d-pulp.sol (default strategy 1)
At line 2 NAME          MODEL
At line 3 ROWS
At line 386 COLUMNS
At line 2112 RHS
At line 2494 BOUNDS
At line 2761 ENDATA
Problem MODEL has 381 rows, 228 columns and 1421 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Continuous objective value is -1.62358 - 0.00 seconds
Cgl0003I 0 fixed, 0 tightened bounds, 6 strengthened rows, 0 substitutions
Cgl0003I 0 fixed, 0 tightened bounds, 1 strengthened rows, 0 substitutions
Cgl0004I processed model has 66 rows, 60 columns (20 integer (20 of which binary)) and 725 elements
Cbc0038I Initial state - 1 integers unsatisfied sum - 0.396
Cbc0038I Solution found of 1.62358
Cbc0038I Relaxing continuous gives 1.62358
Cbc0038I Before mini branch and bound, 19 integers at bound fixed and 35 continuous
Cbc0038I Mini branch and bound did not improve solution (0.01 seconds)
Cbc0038I Freeing continuous variables gives a solution of 1.62358
Cbc0038I After 0.01 seconds - Feasibility pump exiting with objective of 1.62358 - took 0.00 seconds
Cbc0012I Integer solution of 1.6235761 found by feasibility pump after 0 iterations and 0 nodes (0.01 seconds)
Cbc0001I Search completed - best objective 1.623576052631849, took 0 iterations and 0 nodes (0.01 seconds)
Cbc0035I Maximum depth 0, 0 variables fixed on reduced cost
Cuts at root node changed objective from 1.62358 to 1.62358
Probing was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Gomory was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Knapsack was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Clique was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
MixedIntegerRounding2 was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
FlowCover was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
TwoMirCuts was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
ZeroHalf was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Result - Optimal solution found
Objective value:                -1.62357605
Enumerated nodes:               0
Total iterations:               0
Time (CPU seconds):             0.02
Time (Wallclock seconds):       0.02
Option for printingOptions changed from normal to all
Total time (CPU seconds):       0.02   (Wallclock seconds):       0.03
2024-11-24 14:40:24,573 - web_server - INFO - Status: Optimal
2024-11-24 14:40:24,573 - web_server - INFO - Total value of the Cost function = -1.62

This is the last logging of the add-on before it died on me. There are no new updates for the published data after this visible in HA.

I run the MPC with a fixed horizon, until around next midnight. (19 x 30 minutes remaining = 9h 30)
Publish the results with prefix mpc_; if that is feasible. I make HA run the same optimization again, without prefix and publish that as well.
What is it that makes EMHASS stop?

I provide the prediction horizon at runtime, based on a template calculation:

This is in my Node-Red node that prepares the runtime parameters to be sent with my REST API call to EMHASS:

  "prediction_horizon": {{
    min(48, remaining_halfhours)
  }}, 

where β€œremaining_halfhours” is calculated as follows:

{% set da_time = '14:05' %}
{% set da_datetime_str = now().strftime('%Y-%m-%d') + ' ' + da_time %}
{% set da_datetime = strptime(da_datetime_str, '%Y-%m-%d %H:%M') %}
{% set prediction_end_time = '00:00' %}
{% set prediction_end_datetime_str = (now() + timedelta(days=1)).strftime('%Y-%m-%d') + ' ' + prediction_end_time %}
{% set prediction_end_datetime = strptime(prediction_end_datetime_str, '%Y-%m-%d %H:%M') %}
{% if now().tzinfo and now().tzinfo.utcoffset(now()) %}
  {% set da_datetime = da_datetime.replace(tzinfo=now().tzinfo) %}
  {% set prediction_end_datetime = prediction_end_datetime.replace(tzinfo=now().tzinfo) %}
{% endif %}
{% if now() > da_datetime %}
  {% set prediction_end_datetime = (now() + timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0) %}
{% endif %}
{% set delta = as_timestamp(prediction_end_datetime) - as_timestamp(now()) %}
{% set remaining_halfhours = (delta/1800) | round(0, 'ceil') %}

What this code does:
It calculates the number of half hours remaining between now and midnight of current day (if now < 14h05). If now > 14h05, then calculated the remaining number of half hours between now and midnight of next day.
The time 14h05 is the moment where I expect the day-ahead electricity prices for the next day to be known. As from that point in time, I extend my prediction horizon to end of tomorrow.

What happens if you set the def_end_timestep for that load to the end of the day?

I previously had the the feeling the latest results were retained in memory following a reboot, that’s the reason why I implemented an automation that on boot published them, so my automations had something to work on, waiting for the next scheduled EMHASS run.
Now I have the feeling it’s not the case anymore; if I open the EMHASS front-end it’s empty.
Is it just me?

This is the log:

2024-11-24 23:09:42,188 - web_server - INFO - EMHASS server online, serving index.html...
2024-11-24 23:09:42,193 - web_server - INFO - The data container dictionary is empty... Please launch an optimization task
2024-11-24 23:10:14,646 - web_server - INFO - Passed runtime parameters: {'publish_prefix': ''}
2024-11-24 23:10:14,646 - web_server - INFO -  >> Setting input data dict
2024-11-24 23:10:14,646 - web_server - INFO - Setting up needed data
2024-11-24 23:10:14,651 - web_server - INFO -  >> Publishing data...
2024-11-24 23:10:14,651 - web_server - INFO - Publishing data to HASS instance
2024-11-24 23:10:14,664 - web_server - INFO - Successfully posted to sensor.p_pv_forecast = 0.0
2024-11-24 23:10:14,670 - web_server - INFO - Successfully posted to sensor.p_load_forecast = 2254.23
2024-11-24 23:10:14,675 - web_server - INFO - Successfully posted to sensor.p_deferrable0 = 0.0
2024-11-24 23:10:14,675 - web_server - ERROR - P_batt was not found in results DataFrame. Optimization task may need to be relaunched or it did not converge to a solution.
2024-11-24 23:10:14,680 - web_server - INFO - Successfully posted to sensor.p_grid_forecast = 2254.23
2024-11-24 23:10:14,685 - web_server - INFO - Successfully posted to sensor.total_cost_fun_value = -5.06
2024-11-24 23:10:14,690 - web_server - INFO - Successfully posted to sensor.optim_status = Optimal
2024-11-24 23:10:14,695 - web_server - INFO - Successfully posted to sensor.unit_load_cost = 0.1419
2024-11-24 23:10:14,700 - web_server - INFO - Successfully posted to sensor.unit_prod_price = 0.065

G’day @GeoDerp,
Do you know if β€œset_nocharge_from_grid” can be set as a runtime parameter (in a RESTful command on ver v0.11.2)?
I’ve tried all of the following but it doesn’t appear to be working:
With the config set to "set_nocharge_from_grid": false,
I’ve tried including the following in the payload with no luck, i.e. the model still has the system charging from the grid during some blocks

-  "set_nocharge_from_grid": true,
-  "set_nocharge_from_grid": "true",
-  "set_nocharge_from_grid": "True",
-  "set_nocharge_from_grid": ["true"],
-  "set_nocharge_from_grid": ["True"],

I can see it included in the payload data in the logs.

I’m still struggling with the ML Forecaster Model Tune

This is my Model Fit rest command, which runs great:

  ml_forecast_model_fit:
    url: http://192.168.1.35:5001/action/forecast-model-fit
    method: POST
    content_type: 'application/json'
    timeout: 60
    payload: >-
      {
        "days_to_retrieve": 19,
        "model_type": "load_forecast",
        "var_model": "sensor.ss_load_power_without_deferrable",
        "sklearn_model": "KNeighborsRegressor",
        "num_lags": 192,
        "split_date_delta": "48h",
        "perform_backtest": "True"
      }

This is my Model Tune command:

  ml_forecast_model_tune:
    url: http://192.168.1.35:5001/action/forecast-model-tune
    method: POST
    content_type: 'application/json'
    payload: >-
      {
        "days_to_retrieve": 19,
        "model_type": "load_forecast",
        "var_model": "sensor.ss_load_power_without_deferrable",
        "sklearn_model": "KNeighborsRegressor",
        "num_lags": 192,
        "split_date_delta": "48h",
        "perform_backtest": "True"
      }

Here are the logs:

2024-11-25 09:19:41,303 - web_server - INFO - Passed runtime parameters: {'days_to_retrieve': 19, 'model_type': 'load_forecast', 'var_model': 'sensor.ss_load_power_without_deferrable', 'sklearn_model': 'KNeighborsRegressor', 'num_lags': 192, 'split_date_delta': '48h', 'perform_backtest': 'True'}
2024-11-25 09:19:41,303 - web_server - INFO -  >> Setting input data dict
2024-11-25 09:19:41,303 - web_server - INFO - Setting up needed data
2024-11-25 09:19:41,305 - web_server - INFO - Retrieve hass get data method initiated...
2024-11-25 09:19:53,692 - web_server - INFO -  >> Performing a machine learning forecast model tune...
2024-11-25 09:19:53,693 - web_server - INFO - Bayesian hyperparameter optimization with backtesting

  0%|          | 0/10 [00:00<?, ?it/s]
Best trial: 0. Best value: 0.0841749:   0%|          | 0/10 [00:00<?, ?it/s]
Best trial: 0. Best value: 0.0841749:  10%|β–ˆ         | 1/10 [00:00<00:01,  7.32it/s]
Best trial: 0. Best value: 0.0841749:  20%|β–ˆβ–ˆ        | 2/10 [00:00<00:00, 14.64it/s]
Best trial: 0. Best value: 0.0841749:  20%|β–ˆβ–ˆ        | 2/10 [00:00<00:00, 14.64it/s]
Best trial: 0. Best value: 0.0841749:  30%|β–ˆβ–ˆβ–ˆ       | 3/10 [00:00<00:00, 14.64it/s]
Best trial: 0. Best value: 0.0841749:  40%|β–ˆβ–ˆβ–ˆβ–ˆ      | 4/10 [00:00<00:00, 16.05it/s]
Best trial: 0. Best value: 0.0841749:  40%|β–ˆβ–ˆβ–ˆβ–ˆ      | 4/10 [00:00<00:00, 16.05it/s]
Best trial: 0. Best value: 0.0841749:  50%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ     | 5/10 [00:00<00:00, 16.05it/s]
Best trial: 0. Best value: 0.0841749:  60%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ    | 6/10 [00:00<00:00, 16.41it/s]
Best trial: 6. Best value: -0.00633527:  60%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ    | 6/10 [00:00<00:00, 16.41it/s]
Best trial: 6. Best value: -0.00633527:  70%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   | 7/10 [00:00<00:00, 16.41it/s]
Best trial: 6. Best value: -0.00633527:  80%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  | 8/10 [00:00<00:00, 15.96it/s]
Best trial: 6. Best value: -0.00633527:  80%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  | 8/10 [00:00<00:00, 15.96it/s]
Best trial: 6. Best value: -0.00633527:  90%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ | 9/10 [00:00<00:00, 15.96it/s]
Best trial: 6. Best value: -0.00633527: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 10/10 [00:00<00:00, 15.63it/s]
Best trial: 6. Best value: -0.00633527: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 10/10 [00:00<00:00, 15.75it/s]
`Forecaster` refitted using the best-found lags and parameters, and the whole data set: 
  Lags: [ 1  2  3  4  5  6  7  8  9 10 11 12] 
  Parameters: {'n_neighbors': 16, 'leaf_size': 25, 'weights': 'distance'}
  Backtesting metric: -0.006335272525369806
2024-11-25 09:19:54,348 - web_server - INFO - Elapsed time: 0.6548476219177246
2024-11-25 09:19:54,544 - web_server - INFO - R2 score for optimized prediction in train period: 0.006335272525369806
2024-11-25 09:19:54,545 - web_server - INFO - R2 score for optimized prediction in test period: -0.16820666070518286
2024-11-25 09:19:54,545 - web_server - INFO - Number of optimal lags obtained: 12

The result is only 12 optimal lags optained, which results the MPC run error, as i needs 59 lags:

2024-11-25 09:21:13,151 - web_server - ERROR - Unable to obtain: 59 lags_opt values from sensor: power load no var loads, check optimization_time_step/freq and historic_days_to_retrieve/days_to_retrieve parameters

I have tried to delete the database and start from scratch, nut no luck.

I have 15 minutes timestep and detal forecast of 2 days.