This is the automation that I use to control a Hoymiles HM-600 Solar Microinverter to achieve a zero export on a battery with some other features. I use an ESP32 with OpenDTU for the communication and a shelly 3em to monitor my usage. I found the core template section somewhere else on the internet and changed it a bit, but its definetly not perfect.
alias: zero_energy_export
trigger:
- platform: time_pattern
seconds: /30
enabled: false
- platform: mqtt
topic: solar/114190918869/0/power
enabled: true
condition:
- condition: state
entity_id: binary_sensor.hm_600_producing
state: "on"
- condition: state
entity_id: binary_sensor.hm_600_reachable
state: "on"
action:
- service: number.set_value
data_template:
value: |
{ set newSolarLimit = 250 }
{{ newSolarLimit }}
target:
entity_id: number.hm_600_limit_nonpersistent_absolute
enabled: false
- if:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.pack_state_of_charge
above: 95
- condition: numeric_state
entity_id: sensor.battery_voltage_1
above: 54.5
enabled: false
- condition: not
conditions:
- condition: state
entity_id: input_boolean.batterievoll
for:
hours: 0
minutes: 5
seconds: 0
state: "off"
then:
- service: number.set_value
data:
value: "600"
target:
entity_id:
- number.hm_600_limit_persistent_absolute
else:
- if:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.gesamtstromverbrauch
below: 300
- condition: or
conditions:
- condition: state
entity_id: input_boolean.lowpower
state: "off"
for:
hours: 0
minutes: 5
seconds: 0
- condition: state
entity_id: input_boolean.lowpower
state: "on"
enabled: true
then:
- service: number.set_value
data_template:
value: >
{% set maxSolarPower = 600 | float %}
{% set newSolarLimit = 0 | float %}
{% set negThreshold = 15 | float %}
{% set minSolarLimit = 195 | float %}
{% set curGridUsage = states('sensor.gesamtverbrauch_power') |
float %}
{% set curSolarLimit =
states('number.hm_600_limit_nonpersistent_relative') | float %}
{% if is_number(curGridUsage) and curGridUsage + ( curSolarLimit
* maxSolarPower / 100 ) - negThreshold <= minSolarLimit %}
{% set newSolarLimit = ( minSolarLimit / maxSolarPower * 100 ) %}
{{ newSolarLimit }}
{% elif is_number(curGridUsage) and (curGridUsage +
(curSolarLimit * maxSolarPower / 100)) < maxSolarPower and
(curGridUsage + (curSolarLimit * maxSolarPower / 100) -
negThreshold) > minSolarLimit %}
{% set newSolarLimit = ((curGridUsage + (curSolarLimit *
maxSolarPower / 100) - negThreshold) / maxSolarPower * 100 )|
round(2, 'fold') %}
{{ newSolarLimit }}
{% elif is_number(curGridUsage) and curGridUsage +
(curSolarLimit * maxSolarPower / 100) - negThreshold >=
maxSolarPower %}
{% set newSolarLimit = (maxSolarPower / maxSolarPower * 100) %}
{{ newSolarLimit }}
{% else %}
{% set newSolarLimit = curSolarLimit %}
{{ newSolarLimit }}
{% endif %}
target:
entity_id: number.hm_600_limit_persistent_relative
- service: input_boolean.turn_on
data: {}
target:
entity_id: input_boolean.lowpower
else:
- service: number.set_value
data_template:
value: >
{% set maxSolarPower = 600 | float %}
{% set newSolarLimit = 0 | float %}
{% set negThreshold = 30 | float %}
{% set minSolarLimit = 195 | float %}
{% set curGridUsage = states('sensor.gesamtverbrauch_power') |
float %}
{% set curSolarLimit =
states('number.hm_600_limit_nonpersistent_relative') | float %}
{% if is_number(curGridUsage) and curGridUsage + ( curSolarLimit
* maxSolarPower / 100 ) - negThreshold <= minSolarLimit %}
{% set newSolarLimit = ( minSolarLimit / maxSolarPower * 100 ) %}
{{ newSolarLimit }}
{% elif is_number(curGridUsage) and (curGridUsage +
(curSolarLimit * maxSolarPower / 100)) < maxSolarPower and
(curGridUsage + (curSolarLimit * maxSolarPower / 100) -
negThreshold) > minSolarLimit %}
{% set newSolarLimit = ((curGridUsage + (curSolarLimit *
maxSolarPower / 100) - negThreshold) / maxSolarPower * 100 )|
round(2, 'fold') %}
{{ newSolarLimit }}
{% elif is_number(curGridUsage) and curGridUsage +
(curSolarLimit * maxSolarPower / 100) - negThreshold >=
maxSolarPower %}
{% set newSolarLimit = (maxSolarPower / maxSolarPower * 100) %}
{{ newSolarLimit }}
{% else %}
{% set newSolarLimit = curSolarLimit %}
{{ newSolarLimit }}
{% endif %}
target:
entity_id: number.hm_600_limit_persistent_relative
- service: input_boolean.turn_off
data: {}
target:
entity_id: input_boolean.lowpower
enabled: true
- if:
- condition: and
conditions:
- condition: numeric_state
entity_id: sensor.pack_state_of_charge
above: 95
- condition: numeric_state
entity_id: sensor.battery_voltage_1
above: 54.5
then:
- service: input_boolean.turn_on
data: {}
target:
entity_id: input_boolean.batterievoll
else:
- service: input_boolean.turn_off
data: {}
target:
entity_id: input_boolean.batterievoll
mode: single
I also have an automation that pushes the virtual buttons shown by OpenDTU that turns the inverter off when the battery is too low and back on again. Only do this if you have a BMS that can reliably disconnect the battery if the automation doesnt work.
The advantage is that I dont have to build a switch with an inrush current reduction. If you connect a solar microinverter to a battery I would also recommend fusing the cables from the battery to the inverter, because a battery can provide a much higher peak current than a solar panel.
It would be a kinda fun league to watch, but I dont want to hear about athletes dying because they took obscene amounts of steroids to be the best.