EMHASS: An Energy Management for Home Assistant

Trying to catch up with all the info in this thread. Lots of good stuff to boil my head with. I’ve tried to implement everything up until actually executing automations to defer loads or charge/discharge etc. Want to make sure it’s working correctly before I go that far.

I’ve a 5 kWp system, half facing NNW and the other half facing ENE and all on one Fronius Primo 6.0-1 inverter.

So, I’ve set this up in solcast taking their advice and configuring an azimuth of 10° (halfway between the two directions my panels face) and tilt of 21°.

I’m also with Amber Electric and on the NSW AusGrid bonus trial.

I’ve installed the EMHASS add-on as a Home Assistant OS user. HA is running on a VM under Proxmox on an Intel NUC along with other things like Tuya convert and a docker instance running TeslaMate.

Configuration for EMHASS:

hass_url: empty
long_lived_token: empty
costfun: profit
logging_level: INFO
optimization_time_step: 30
historic_days_to_retrieve: 2
method_ts_round: first
set_total_pv_sell: false
lp_solver: COIN_CMD
lp_solver_path: /usr/bin/cbc
set_nocharge_from_grid: false
set_nodischarge_to_grid: false
set_battery_dynamic: false
battery_dynamic_max: 0.9
battery_dynamic_min: -0.9
load_forecast_method: naive
sensor_power_photovoltaics: sensor.sonnenbatterie_84324_production_w
sensor_power_load_no_var_loads: sensor.house_power_consumption_less_deferrables
number_of_deferrable_loads: 2
list_nominal_power_of_deferrable_loads:
  - nominal_power_of_deferrable_loads: 1500
  - nominal_power_of_deferrable_loads: 750
list_operating_hours_of_each_deferrable_load:
  - operating_hours_of_each_deferrable_load: 2
  - operating_hours_of_each_deferrable_load: 2
list_peak_hours_periods_start_hours:
  - peak_hours_periods_start_hours: "02:54"
  - peak_hours_periods_start_hours: "17:24"
list_peak_hours_periods_end_hours:
  - peak_hours_periods_end_hours: "15:24"
  - peak_hours_periods_end_hours: "20:24"
list_treat_deferrable_load_as_semi_cont:
  - treat_deferrable_load_as_semi_cont: true
  - treat_deferrable_load_as_semi_cont: true
load_peak_hours_cost: 0.1907
load_offpeak_hours_cost: 0.1419
photovoltaic_production_sell_price: 0.065
maximum_power_from_grid: 14490
list_pv_module_model:
  - pv_module_model: CSUN_Eurasia_Energy_Systems_Industry_and_Trade_CSUN295_60M
list_pv_inverter_model:
  - pv_inverter_model: Fronius_International_GmbH__Fronius_Primo_5_0_1_208_240__240V_
list_surface_tilt:
  - surface_tilt: 21
list_surface_azimuth:
  - surface_azimuth: 350
list_modules_per_string:
  - modules_per_string: 17
list_strings_per_inverter:
  - strings_per_inverter: 1
set_use_battery: true
battery_discharge_power_max: 3300
battery_charge_power_max: 3300
battery_discharge_efficiency: 0.95
battery_charge_efficiency: 0.95
battery_nominal_energy_capacity: 10000
battery_minimum_state_of_charge: 0.1
battery_maximum_state_of_charge: 0.9
battery_target_state_of_charge: 0.1

Deferrable loads:

  1. I did have an Arlec GridConnect power point for the pool pump with Tasmota firmware but as that doesn’t monitor power usage, I’ve replaced it with a Zigbee power point that does monitor power. The power measurement is being subtracted from the total consumption see below.

  2. I’ve installed a Tuya power monitoring plug behind the dish washer and added the local tuya HACS to integrate it (I hate Tuya but bit my tongue). This is also being subtracted from total consumption, see below.

  3. I’ll also installed a zigbee power point for the dryer and washing machine (when I get around to it).

  4. I’ve also install the Tesla HACS for my Model Y Performance. I only have a dumb Tesla charger that came with my old Model 3 so have to depend on load data and control via the car itself. Not using it yet.

Only the first two deferrable loads are being subtracted from total home consumption at this point:

  - platform: template
    sensors:
      dw_power:
        unique_id: dw_power
        friendly_name: "Dish Washer Power"
        value_template: "{{ states.switch.dw_switch.attributes.current_consumption | float(0) }}"
        unit_of_measurement: W

  - platform: template
    sensors:
      house_power_consumption_less_deferrables:
        unit_of_measurement: W
        unique_id: house_power_consumption_less_deferrables
        value_template: >-
          {% set consumption = states.sensor.sonnenbatterie_84324_meter_consumption_4_2_w_total.state|float(0) %}
          {% set deferrable1 = states.sensor.garage_power_point_power.state|float(0) %}
          {% set deferrable2 = states.sensor.dw_power.state|float(0) %}
          {{ (consumption - (deferrable1 + deferrable2))|float(0) }}

So I’m getting this consumable sensor from the sonnen battery (sensor.sonnenbatterie_84324_meter_consumption_4_2_w_total).
Am I right in assuming that it should go negative if I’m selling energy into the grid?

The battery I have is a sonnen eco 9.43 10 kWh (9 kWh usable, although I think it’s getting closer to 8 now that it’s nearly 5 years old, you can upgrade them to 15 I think but not sure it’s worth it).

I have control of battery mode (Auto Self Consumptionm, manual and ToU) and charging and discharging via API thanks to @julianlu.

So to integrate the solcast forecast data:

sensor:
  # Solar forecast for EMHASS
  - platform: rest
    name: "Solcast Forecast Data"
    json_attributes:
      - forecasts
    resource: https://api.solcast.com.au/rooftop_sites/SOLCAST_RESOURCE_ID/forecasts?format=json&api_key=SOLCAST_API_KEY&hours=24
    method: GET
    value_template: "{{ (value_json.forecasts[0].pv_estimate)|round(2) }}"
    unit_of_measurement: "kW"
    device_class: power
    scan_interval: 00:30
    force_update: true

  - platform: template
    sensors:
      solcast_24hrs_forecast:
        value_template: >-
          {%- set power = state_attr('sensor.solcast_forecast_data', '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] }}

I see some discussion about suitability of ‘Solcast Forecast’ Data being kW not W but it appears we only use solcast_24hrs_forecast which corrects this?

Next are the shell commands:

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_amber_forecast:
    'curl -i -H ''Content-Type: application/json'' -X POST -d ''{"prod_price_forecast":{{(
    state_attr(''sensor.amber_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)
    }},"load_cost_forecast":{{(
    state_attr(''sensor.amber_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)
    }},"prediction_horizon":33}'' http://localhost:5000/action/dayahead-optim'
  post_emhass_forecast:
    'curl -i -H ''Content-Type: application/json'' -X POST -d ''{"prod_price_forecast":{{(
    state_attr(''sensor.amber_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)
    }},{{states(''sensor.solcast_24hrs_forecast'')}},"load_cost_forecast":{{(
    state_attr(''sensor.amber_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)
    }}}'' http://localhost:5000/action/dayahead-optim'
  post_mpc_optim_solcast:
    'curl -i -H "Content-Type: application/json" -X POST -d ''{"load_cost_forecast":{{(
    ([states(''sensor.amber_general_price'')|float(0)] +
    state_attr(''sensor.amber_general_forecast'', ''forecasts'') |map(attribute=''per_kwh'')|list)[:48])
    }}, "prod_price_forecast":{{(
    ([states(''sensor.amber_feed_in_price'')|float(0)] +
    state_attr(''sensor.amber_feed_in_forecast'', ''forecasts'')|map(attribute=''per_kwh'')|list)[:48])
    }}, "pv_power_forecast":{{states(''sensor.solcast_24hrs_forecast'')
    }}, "prediction_horizon":48,"soc_init":{{(states(''sensor.sonnenbatterie_84324_state_charge_user'')|float(0))/100
    }},"soc_final":0.05,"def_total_hours":[2,0,0,0]}'' http://localhost:5000/action/naive-mpc-optim'

This is where I get a bit confused with all the different methods.
I understand the basic ‘dayahead_optim’ is a good place to start and that’s what I’m running now. But can I run the other post commands since I have the forecast data to use?

Do I run them as well as the dayahead_optim or instead of?
Do I run all three post commands?

I have the following node-red flows to execute these various curl commands (although I’ve left them as shell commands for the time being and called them from node-red).


So at the moment I’m only running

  1. dayahead_optim at 05:30 and
  2. publish_data every 5 mins

The battery controls are simply a set of posts linked back to 4 buttons

I don’t actually use the 5th TOU mode, but I’ve put it in anyway.

These lead to the flows below:


Apart from the buttons that I can control manually, there are three flows that also control the battery state.

  1. The first is an afternoon charge that activates if the Amber price is “very low” and the battery is less than 80% charges. Its a rainy day charge to get me over the evening hump at a better price.
  2. The second is an early morning charge starting at 03:00 early morning flow (at the top) which charges the battery if it’s less that 20% charged and then lets it sit idle for 2 hours until people start to wake up and consume energy (first coffee and heaters on in winter).
  3. The last one down the bottom is to catch any spikes in the FiT what seem to happen from time to time where the tariff can hit $12 per kWh. Only problem is I’m often burning everything out of the 3.3kW output of the battery in winter time so can’t take advantage sometimes unless I run around turning aircons and heaters off. 5 adults in the house at the moment so can be difficult.

So, these are my rules based configurations that I will replace with flows that use the output of EMHASS when its working correctly.
What I’m getting so far is:


Seems to have died in the last day. Probably me mucking around too much.

Any advice greatly appreciated.