Skip to content

Zero Export and Export Limiting

hilman2 edited this page Sep 2, 2026 · 2 revisions

Zero export and export limiting

Automations that cap what the inverter produces, from a fixed percentage to a control loop that holds the grid connection at zero. All of them use the export controls, which are a beta and off by default. Read Battery and export control first; it lists the entities, explains the revert timer, and says what can go wrong. This page assumes you have.

One thing to get straight first

The export limit caps the inverter's output, not the flow at the grid connection. A limit of 0 % means the inverter produces nothing at all, and your house draws everything from the grid. Zero feed-in while still using your own production needs a control loop that reads a grid meter and moves the limit up and down. That loop is the third recipe below.

Feed-in limits may be regulated where you live. A permanent cap that your grid operator requires is usually configured by the installer in the inverter itself, and what you set from Home Assistant comes on top.

Entities used on this page

Placeholders. Entity names and IDs explains how to find yours. Whether you get the block 123 or the block 704 entities depends on your inverter; the names are the same.

Placeholder What it is
number.inverter_export_limit Export limit in percent of the inverter's WMax
switch.inverter_export_limit_enabled The limit only applies while this is on
number.inverter_export_limit_revert_time Seconds until the inverter drops the limit on its own
number.inverter_active_power_setpoint Block 704 only: the limit in watts
switch.inverter_active_power_setpoint_enabled Block 704 only
select.inverter_active_power_setpoint_mode Block 704 only, watts or w_max_pct
sensor.grid_meter_ac_power Power at the grid connection, positive = import

And the sunspec2.set_export_limit action, which writes the percentage and the enable flag together. It needs the config entry id of the inverter. Build the action once in the visual automation editor, pick the inverter from the dropdown, switch to Edit in YAML, and copy the id from there.

Recipe 1: a fixed cap

Suppose you want the inverter never to produce more than 70 % of its rating. Two ways, and the choice is about what happens when Home Assistant is down.

With the inverter's timeout switched off. Set Export limit revert time to 0, set Export limit to 70, switch Export limit enabled on. Done, and it survives a Home Assistant outage, because nothing has to keep it alive. Not every inverter accepts 0 as "no timeout"; if yours reverts anyway, use the second way.

With a keepalive. The revert timer is a dead-man switch: the inverter honours the limit for that many seconds and then lets go, so a controller that died does not leave it throttled forever. Keep the timer, and re-write the limit more often than it expires:

input_number:
  export_limit_pct:
    name: Export limit
    min: 0
    max: 100
    step: 1
    unit_of_measurement: "%"
    initial: 70

input_boolean:
  export_limit_active:
    name: Export limit active

Set Export limit revert time to 120 once. Then:

alias: Export limit keepalive
description: >
  Re-writes the limit every 15 s. The inverter drops it by itself
  120 s after the last write, so a stopped Home Assistant means an
  unlimited inverter, not a stuck one.
mode: single
triggers:
  - trigger: time_pattern
    seconds: "/15"
conditions:
  - condition: state
    entity_id: input_boolean.export_limit_active
    state: "on"
actions:
  - action: sunspec2.set_export_limit
    data:
      config_entry_id: 0123456789abcdef0123456789abcdef
      percent: "{{ states('input_number.export_limit_pct') | float(100) }}"
      enable: true

And one automation to switch it off cleanly rather than waiting for the lapse:

alias: Export limit off
triggers:
  - trigger: state
    entity_id: input_boolean.export_limit_active
    to: "off"
actions:
  - action: switch.turn_off
    target:
      entity_id: switch.inverter_export_limit_enabled

Why the action rather than the Number entity: the action writes the percentage and the enable flag in one operation. On a Fronius the new value only takes effect once the enable flag goes from off to on again, and the Number entity alone never touches it. The integration option Re-apply the export limit and power factor by switching them off and on when their value changes does that cycle for every write, including through the Number entity; it defaults on for the Symo/Symo Hybrid generation and off for GEN24, see Vendor notes.

Recipe 2: curtail at negative prices

With a dynamic tariff there are hours in which feeding in costs money. If in those hours you would rather produce nothing at all, drive the helpers from recipe 1:

alias: Curtail at negative prices
triggers:
  - trigger: numeric_state
    entity_id: sensor.electricity_price
    below: 0
    id: negative
  - trigger: numeric_state
    entity_id: sensor.electricity_price
    above: 0
    id: positive
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: negative
        sequence:
          - action: input_number.set_value
            target:
              entity_id: input_number.export_limit_pct
            data:
              value: 0
          - action: input_boolean.turn_on
            target:
              entity_id: input_boolean.export_limit_active
      - conditions:
          - condition: trigger
            id: positive
        sequence:
          - action: input_boolean.turn_off
            target:
              entity_id: input_boolean.export_limit_active

sensor.electricity_price stands for whatever your tariff integration provides. If you want to keep using your own production during those hours and only stop the feed-in, run recipe 3 instead and switch it on and off from the price.

Recipe 3: zero feed-in against a grid meter

A control loop. Every 10 s it reads the grid power, compares it with a small target import, and nudges the limit. Exporting more than the target lowers the limit, importing raises it, and the limit stays between 0 and 100.

It needs the inverter's rated power in watts to turn a watt error into a percentage step. Block 120 has it, but the block is not polled by default and its sensor starts disabled, so an input helper is the simpler source:

input_number:
  inverter_nameplate_w:
    name: Inverter rated power
    min: 500
    max: 100000
    step: 100
    unit_of_measurement: W
    initial: 8000
  zero_export_target_w:
    name: Zero export target
    min: -500
    max: 500
    step: 10
    unit_of_measurement: W
    initial: 50

input_boolean:
  zero_export_active:
    name: Zero export active

A positive target means "import a little", which keeps the loop off the export side while the meter and the inverter disagree by a few watts. Set Export limit revert time to 60 once, so a stopped loop frees the inverter within a minute.

alias: Zero export control loop
mode: single
triggers:
  - trigger: time_pattern
    seconds: "/10"
conditions:
  - condition: state
    entity_id: input_boolean.zero_export_active
    state: "on"
  - condition: template
    value_template: >
      {{ has_value('sensor.grid_meter_ac_power')
         and has_value('number.inverter_export_limit') }}
variables:
  grid_w: "{{ states('sensor.grid_meter_ac_power') | float(0) }}"
  target_w: "{{ states('input_number.zero_export_target_w') | float(50) }}"
  nameplate_w: "{{ states('input_number.inverter_nameplate_w') | float(8000) }}"
  current_pct: "{{ states('number.inverter_export_limit') | float(100) }}"
  gain: 0.5
  step_pct: "{{ (grid_w - target_w) / nameplate_w * 100 * gain }}"
  new_pct: "{{ [0, [100, current_pct + step_pct] | min] | max | round(1) }}"
actions:
  - action: sunspec2.set_export_limit
    data:
      config_entry_id: 0123456789abcdef0123456789abcdef
      percent: "{{ new_pct }}"
      enable: true

Tuning:

  • Gain 0.5 takes half the error per step. Oscillation means too much, a slow crawl towards zero means too little. Inverters ramp their output over a few seconds, so a gain above 1 fights the ramp.
  • The poll interval of the meter sets the real speed. A meter read by this integration every 30 s makes a 10 s loop pointless; either poll the meter's config entry at 5 s, or read the meter through something faster.
  • Smooth the import side. A dishwasher heater switching on for two seconds should not open the limit fully. Feed the meter through Home Assistant's filter platform with a low-pass and use the filtered value for raising the limit and the raw value for lowering it. That asymmetry is what a KACO owner in #17 has run for two years, before the beta existed, through a Modbus proxy.

While the loop runs, block 123 reads back whatever was last written, so the Export limit entity is not evidence that the limit is in force. The grid meter is. On block 704 the sensor with sunspec_key WMaxLimPctRvrtRem counts down to the lapse and is worth a card.

Recipe 4: watts instead of percent, block 704

If your inverter has block 704 you get an absolute setpoint. The loop becomes simpler because the nameplate drops out:

variables:
  grid_w: "{{ states('sensor.grid_meter_ac_power') | float(0) }}"
  target_w: "{{ states('input_number.zero_export_target_w') | float(50) }}"
  current_w: "{{ states('number.inverter_active_power_setpoint') | float(0) }}"
  nameplate_w: "{{ states('input_number.inverter_nameplate_w') | float(8000) }}"
  gain: 0.5
  new_w: "{{ [0, [nameplate_w, current_w + (grid_w - target_w) * gain] | min] | max | round(0) }}"
actions:
  - action: select.select_option
    target:
      entity_id: select.inverter_active_power_setpoint_mode
    data:
      option: watts
  - action: number.set_value
    target:
      entity_id: number.inverter_active_power_setpoint
    data:
      value: "{{ new_w }}"
  - action: switch.turn_on
    target:
      entity_id: switch.inverter_active_power_setpoint_enabled

The mode select starts disabled; enable it once in the entity settings. The watt setpoint's own revert timer is not exposed as a control, so watch the WSetRvrtTms and WSetRvrtRem sensors to see what your device does with it.

Nobody has confirmed the watt setpoint on real hardware yet. The percent limit on block 704 is confirmed on a KACO. If you run the watt path, a line in #17 with the result is what everyone after you needs.

Making sure it lets go

Before you rely on any of this, stop Home Assistant for two minutes on a sunny day and watch the inverter. It should return to full output within the revert time. If it does not, the timeout is off or your inverter ignores it, and a Home Assistant outage would then leave the inverter throttled until you notice.

Clone this wiki locally