EMHASS: An Energy Management for Home Assistant

EMHASS only looks forward, it doesn’t remember what had happened in the past. It’s calculations are based on everything forecast in the future what is the best actions to take now.

It makes sense to discharge at 8am for 11¢ because you can charge at 12pm for 5¢.

1 Like

Is it discharging your battery to power your household load or to export to the grid.

I’m my experience it only charges overnight up the minimum sufficient to cover your morning household load and any exports to the grid that it can export for a profit. Often it does not do any overnight charging.

You need to include all the sensors in your screens for to make sense of what is happening.

I had this problem too… it was charging overnight with prices higher compared to the morning when it wants to discharge… I’m just handling it in by taking the battery in offline mode (neither charging or discharging) and let the excess solar export to the grid. this is also how I handle the PBATT=zero

Anyone knows how to handle the status code 500 when running the naive-mpc-optim??? this is making my published data so wrong

Two suggestions.

  • Don’t call the publish end point if the MPC optimisation returns the error code 500, that will keep your published data sensible.

  • Include more robust data checking on the data you inject to the MPC optimisation.

I find with the mix of integral and external sensors in the MPC call there is a high chance of some data being in a funny state.

Do you know which data elements are incorrect?

Lots of debugging code printing out values is really useful.

That is a good workaround but it would be useful to get to the root cause.

After 1pm today (when we have tomorrow’s forecasts) can you share your 24 hour forecasts for tonight/ tomorrow morning if it’s planning on overcharging the battery overnight?

Mine isn’t planning any overcharging.

1 Like

Hi mark… thanks again…

Being new to HA… I’m still have a lot to learn… how to I get the status code when I call the optimization?


alias: EMHASS Post MPC Solcast
description: ""
trigger:
  - platform: time_pattern
    minutes: /1
condition:
action:
  - service: rest_command.naive_mpc_optim
    data: {}
  - service: rest_command.publish_data
    data: {}
mode: single

It looks fine to me…


2023-07-13 03:45:01.645 WARNING (MainThread) [homeassistant.components.rest_command] Error. Url: http://localhost:5000/action/naive-mpc-optim. Status code 500. Payload: b'{\n  "prod_price_forecast": [-0.03, -0.04, -0.01, -0.0, 0.04, 0.06, 0.07, 0.04, 0.12, 0.13, 0.06, 0.02, 0.01, 0.0, -0.01, -0.02, -0.04, -0.05, -0.05, -0.05, -0.05, -0.05, -0.05, -0.02, -0.0, 0.01, 0.04, 0.05, 0.08, 0.07, 0.06, 0.06, 0.06, 0.07, 0.07, 0.06, 0.05, 0.01, -0.0, -0.0, -0.01, -0.01, -0.02, -0.02, -0.02, -0.04, -0.04, -0.04, -0.05],\n  "load_cost_forecast": [0.07, 0.06, 0.1, 0.1, 0.15, 0.17, 0.18, 0.14, 0.24, 0.25, 0.17, 0.13, 0.12, 0.11, 0.1, 0.08, 0.06, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.23, 0.24, 0.26, 0.29, 0.3, 0.34, 0.32, 0.31, 0.31, 0.31, 0.32, 0.32, 0.17, 0.16, 0.12, 0.1, 0.1, 0.1, 0.1, 0.08, 0.08, 0.08, 0.05, 0.06, 0.05, 0.05],\n  "load_power_forecast": [361, 900, 600, 2400, 3100, 2700, 2000, 2200, 2500, 1900, 1800, 1500, 700, 1300, 2100, 2500, 1700, 500, 900, 0, 800, 2000, 1900, 900, 800, 1100, 1100, 1200, 1000, 1000, 900, 1700, 2400, 800, 700, 700, 900, 900, 600, 500, 700, 400, 400, 600, 700, 600, 500, 600],\n  "pv_power_forecast": [0, 0, 0, 0, 0, 0, 0, 0, 133, 1057, 1677, 2178, 2587, 3035, 3350, 3603, 3747, 3722, 3532, 3220, 2816, 2427, 1986, 1483, 979, 523, 162, 15, 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, 32, 227, 845, 1548, 2036, 2458, 2748, 2958, 3072, 3095, 3006, 2863, 2655, 2353, 1952, 1481, 1010, 578, 176, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n  "prediction_horizon": 48,\n  "alpha": 1,\n  "beta": 0,\n  "num_def_loads": 0,"def_total_hours": [0],"P_deferrable_nom": [0],\n  "treat_def_as_semi_cont": [1, 1],\n  "set_def_constant": [0, 0],\n  "soc_init": 0.75,\n  "soc_final": 0.05\n}'

Yes. Mostly trying to match house load I think.
I’ll continue to monitor.

@Mheloy @rcruikshank You should have your plan for overnight available now. I’m not showing any overnight charging. What does you plan look like?

1 Like

I’m charging early morning… maybe to support the load in the morning…but no force discharge with negative profit this time… I only have 9.6KWH battery… I guess that’s a big factor…

1 Like

I’m charging at 4:30 to 5:00 at 1680W and 16c/kWh price
Then a discharge at 5:30 to 6:00 at 319W and 9c FiT and 20c price I think to balance load forecast
Then a discharge at 6:30 to 7:00 at 780W and 12c FiT and 22c price again to offset predicted load
Looking good.

2 Likes

Thank you again!!! I see you are in SEQ. I owe you quite a few beers at the very least. Do you have any where to point me for setup of those apex cards. I have managed to get a few sensors but not sure how to get the headers such as Daily forecast plan value ($) and solar production forecast? Is your code handy-ish for me to adapt (like I have the rest of my EMHASS code)? Cheers, Tom

Here are my current charts:

type: custom:apexcharts-card
span:
  start: minute
header:
  show: true
  title: EMHASS Daily Forecast
  show_states: true
  colorize_states: true
now:
  show: false
  label: now
yaxis:
  - min: -7000
    max: 13000
    decimals: 2
    apex_config:
      forceNiceScale: true
      tick_amount: 4
series:
  - entity: sensor.total_cost_fun_value
    unit: $
    name: Plan Value (+ve credit)
    show:
      legend_value: false
      in_chart: false
  - entity: sensor.pv_forecast_energy
    unit: kWh
    name: Solar Production Forecast
    show:
      legend_value: false
      in_chart: false
  - entity: sensor.p_pv_forecast
    type: line
    stroke_width: 2
    color: orange
    show:
      in_header: false
      legend_value: false
    data_generator: |
      return entity.attributes.forecasts.map((entry) => {
        return [new Date(entry.date), entry.p_pv_forecast];
      });
  - entity: sensor.p_load_forecast
    type: line
    color: purple
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.forecasts.map((entry) => {
        return [new Date(entry.date), entry.p_load_forecast];
      });
  - entity: sensor.p_batt_forecast
    curve: stepline
    color: lightblue
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    type: area
    data_generator: |
      return entity.attributes.battery_scheduled_power.map((entry) => {
        return [new Date(entry.date), entry.p_batt_forecast];
      });
  - entity: sensor.p_grid_forecast
    curve: stepline
    color: cyan
    type: area
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.forecasts.map((entry) => {
        return [new Date(entry.date), entry.p_grid_forecast];
      });
  - entity: sensor.p_deferrable0
    curve: stepline
    color: blue
    name: Pool Pump
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.deferrables_schedule.map((entry) => {
        return [new Date(entry.date), entry.p_deferrable0];
      });
  - entity: sensor.p_deferrable1
    curve: stepline
    color: darkblue
    name: Pool Heater
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.deferrables_schedule.map((entry) => {
        return [new Date(entry.date), entry.p_deferrable1];
      });
  - entity: sensor.p_deferrable3
    curve: stepline
    name: Heating/ Cooling
    color: red
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.deferrables_schedule.map((entry) => {
        return [new Date(entry.date), entry.p_deferrable3];
      });
  - entity: sensor.p_deferrable4
    curve: stepline
    color: red
    name: Hot Water
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.deferrables_schedule.map((entry) => {
        return [new Date(entry.date), entry.p_deferrable4];
      });
  - entity: sensor.p_deferrable2
    curve: stepline
    color: green
    name: Car
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.deferrables_schedule.map((entry) => {
        return [new Date(entry.date), entry.p_deferrable2];
      });
  - entity: sensor.p_deferrable5
    curve: stepline
    name: Car2
    color: darkgreen
    show:
      in_header: false
      legend_value: false
    stroke_width: 1
    data_generator: |
      return entity.attributes.deferrables_schedule.map((entry) => {
        return [new Date(entry.date), entry.p_deferrable5];
      });
view_layout:
  position: main

type: custom:apexcharts-card
experimental:
  color_threshold: true
span:
  start: minute
header:
  show: true
  title: Battery, Price & Cost Forecast
  show_states: true
  colorize_states: true
series:
  - entity: sensor.amber_general_forecast
    float_precision: 2
    yaxis_id: first
    curve: stepline
    show:
      in_header: after_now
      legend_value: false
    stroke_width: 1
    color: red
    color_threshold:
      - value: 0
        color: cyan
      - value: 0.19
        color: green
      - value: 0.3
        color: yellow
      - value: 0.4
        color: red
    name: general cost
    data_generator: |
      return entity.attributes.forecasts.map((entry) => {
        return [new Date(entry.start_time), entry.per_kwh];
      });
  - entity: sensor.amber_feed_in_forecast
    name: feedin min
    float_precision: 2
    yaxis_id: first
    curve: stepline
    show:
      in_header: after_now
      legend_value: false
    color: cyan
    stroke_width: 1
    data_generator: |
      return entity.attributes.forecasts.map((entry) => {
        return [new Date(entry.start_time), entry.range_min];
      });
  - entity: sensor.amber_feed_in_forecast
    float_precision: 2
    yaxis_id: first
    curve: stepline
    show:
      in_header: after_now
      legend_value: false
    color: blue
    stroke_width: 1
    name: feed in price
    data_generator: |
      return entity.attributes.forecasts.map((entry) => {
        return [new Date(entry.start_time), entry.per_kwh];
      });
  - entity: sensor.soc_batt_forecast
    float_precision: 2
    yaxis_id: second
    show:
      in_header: before_now
      legend_value: false
    color: green
    stroke_width: 2
    name: battery SOC
    data_generator: |
      return entity.attributes.battery_scheduled_soc.map((entry) => { 
        return [new Date(entry.date), entry.soc_batt_forecast]; });
yaxis:
  - id: first
    decimals: 1
    max: 1
    min: 0
    apex_config:
      tickAmount: 2
      logarithmic: false
  - id: second
    show: false
    opposite: false
    decimals: 1
    max: 100
    min: 0
    apex_config:
      tickAmount: 2
view_layout:
  position: main
1 Like


1 Like

Thank you so much!! My rest command and charts are now working. Just need to wait for the p_load data to fill in again after our battery was installed. Really appreciate your time!!

1 Like

Hi Mark, impressive what you have set up; I’m inclined to take the plunge. Question, how do you account for the battery round-trip efficiency and potentially the cost of battery degradation? Do you account for this in the discharge automation, did you make a “fake” cost sensor to address this in Emhass or do you disregard it?

My experience (growatt battery) is on average 90% round-trip efficiency and ca. 12c economic cost per kWh assuming 6000 cycles economic life.
Right now I manually set charge and discharge based om a minimum 20% spread between moment of selling and buying; suspect Emhass can do this for me going forward?

EMHASS includes factors for both charging and discharging efficiency in its configuration.

It doesn’t explicitly factor in the cost of battery degradation but my experience has been that it doesn’t export to the grid for less than about 15¢/ kWh. It doesn’t have a memory per say of the prices it purchased energy for, rather it is always looking forward to the best optimisation to take now.

You can spin it up to see what it would do without actually having it take any actions. You can also put your own limits on any actions so for example don’t discharge below a certain price.

3 Likes

There is not a direct penalty to account for battery degradation in the objective function during the optimization.
However I can see two options that can be used directly. The first is to set your own values for SOC min and max. This will limit the depth of discharge (DOD) allowed which is directly related to the total number of cycles that you battery will endure. The second option is the dynamic power parameter. Limiting the dynamic power of the battery will certainly help to some extent to battery degradation.

1 Like

Is this correct? Exporting to the grid with Negative fit?

or is it because the battery is full at this time?

Im having the same issue, negative FIT and emhas wants to discharge the battery, mine is still half empty.