Automate Fronius Soft Limit

[Blog created showing step-by-step instructions here]

Hi,
I am trying to dynamically change the output of my Fronius inverter. I want do this since I get wholesale power prices, which on occasion charge me to export power when the grid is saturated with solar energy. I can manually change this setting within the inverter, however I would love to automate this through HA.

Any thoughts about how I can go about this?

I think you could do it via Modbus TCP.

Iā€™m trying to do the same as you but still investigating how to. My initial idea is by writing some registers about grid feed

Yes, I got it working exactly as you said with modbus.

Within configuration.yaml Iā€™ve defined my Fronius Primo inveter with the modbus Sunspec model type as ā€œint + SFā€

modbus:
  - type: tcp
    name: mb_fronius
    host: 192.168.1.165
    port: 502
    retry_on_empty: true
    retries: 3

Iā€™ve then have two automations, one which increases the inverter output and the other which decreases to keep it so it is only feeding in when price is not negative.

Here is the action for one which I am sure will help.

I got the modbus details from here

action:
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40236
      value: "0"
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40232
      value: "{{ states.sensor.solar_trim_percent_when_greater_load.state }}"
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40236
      value: "1"
1 Like

Hi there!

Thanks for this. This is actually what Iā€™m looking for.

One question: actually Iā€™m manually configuring the GEN24 inverter to limit a max 150W grid feed to have some threshold in case of a sudden demand

I see you are using a sensor (sensor.solar_trim_percent_when_greater_load) to configure a percentage. How do you have this configuration? is this sensor something fixed that you have?

Could you please elaborate a little bit more?

Appreciated

I have two sensors defined. The first one calculates what the trim percentage should be when the house is consuming more than the current trimmed output. The other when it is less than the trimmed output. I then have two automations that automatically increase or decrease the inverter % to keep feed in as close to 0. It works well.

NB: My inverter is 6000W hence the devision in the below.

     solar_trim_percent_when_greater_load:
       friendly_name: 'Solar Trim Percent When Greater Load'
       value_template: '{{ ((states("sensor.feed_into_power_grid")|float(0)|multiply(-1)+states("sensor.power_load_fronius_power_flow_0_http_fronius")|float(0)|multiply(-1))/6000)|multiply(10000)|int}}'

     solar_trim_percent:
       friendly_name: 'Solar Trim Percent'
       value_template: '{{ ((states("sensor.power_load_fronius_power_flow_0_http_fronius")|float(0))/6000)|multiply(-1)|multiply(10000)|int}}'

Hi Brett.

Thanks for this

To summarize:

I would like to have dynamic_power_reduction activated with max grid feed set to 150W from the inverter

On certain occasions, I would like Home Assistant to:

  1. Set power output at 100%, this is, the Inverter at maximum production
  2. Once finished something i.e. Warm water through electric boiler, return to dynamic_power_reduction mode with max grid feed set to 150W

My question is: can this be done with HA?

I guess the way of doing this is by having manually configured the dynamic power reduction through the web page (at the inverter), then through modbus enable immediate control mode and setting WMaxLimPct = 100%, and once finished something at home, disable the immediate control through modbus so the inverter returns again to dynamic power reduction with max 150w feed.

Am I right?

Thanks for your help

Yes, Iā€™m doing something similar.

No (of course Iā€™ve only tested on one inverter), however, on mine which is a Fronius Primo you do not need the web browser. You enable WMaxLim_Ena through modbus and this switches on WMaxLimPct. So, if you donā€™t want limiting just turn off WMaxLim_Ena and your inverter will run flat out.

Then you need to do the calculations as per those sensors and have an automation that triggers when it wants to INCREASE or DECREASE the output.

Hope these automations help.

I was actually thinking of writing a blog on this since it works really well.

alias: >-
  ON: Throttle (INCREASE) solar output if house load is GREATER than solar
  output 
description: >-
  Providing that the load on the house is greater than solar output other maths
  is wrong
trigger:
  - platform: state
    entity_id:
      - sensor.solar_trim_percent
condition:
  - condition: template
    value_template: "{{ states('sensor.solar_trim_percent')|int < 10000 }}"
    enabled: true
  - condition: or
    conditions:
      - condition: template
        value_template: |
          {{ trigger.to_state.state|float > trigger.from_state.state|float }}
      - condition: template
        value_template: |
          {{ trigger.to_state.state|float < trigger.from_state.state|float }}
  - condition: template
    value_template: >
      {{ as_timestamp(now()) -
      as_timestamp(state_attr('automation.on_throttle_solar_if_feed_in_is_negative','last_triggered'))
      > 30 }}
    enabled: true
  - condition: state
    entity_id: sun.sun
    state: above_horizon
  - condition: state
    entity_id: sensor.amber_feedin_price_negative
    state: "True"
  - condition: state
    entity_id: sensor.amber_price_negative
    state: "False"
  - condition: template
    value_template: >-
      {{
      states('sensor.power_load_fronius_power_flow_0_http_fronius')|multiply(-1)|int
      >= states('sensor.current_solar_power_output')|multiply(1)|int }} 
    alias: Test if load in house is less than solar output
action:
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40236
      value: "0"
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40232
      value: "{{ states.sensor.solar_trim_percent_when_greater_load.state }}"
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40236
      value: "1"
mode: single

And

alias: "ON: Throttle (DECREASE) solar output if house load is LESS than solar output"
description: >-
  Providing that the load on the house is less than solar output other maths is
  wrong
trigger:
  - platform: state
    entity_id:
      - sensor.solar_trim_percent
condition:
  - condition: template
    value_template: "{{ states('sensor.solar_trim_percent')|int < 10000 }}"
    enabled: true
  - condition: or
    conditions:
      - condition: template
        value_template: |
          {{ trigger.to_state.state|float > trigger.from_state.state|float }}
      - condition: template
        value_template: |
          {{ trigger.to_state.state|float < trigger.from_state.state|float }}
  - condition: template
    value_template: >
      {{ as_timestamp(now()) -
      as_timestamp(state_attr('automation.on_throttle_solar_output_if_house_load_is_greater_than_solar_output',
      'last_triggered')) > 30 }}
    enabled: true
  - condition: state
    entity_id: sun.sun
    state: above_horizon
  - condition: state
    entity_id: sensor.amber_feedin_price_negative
    state: "True"
  - condition: state
    entity_id: sensor.amber_price_negative
    state: "False"
  - condition: template
    value_template: >-
      {{
      states('sensor.power_load_fronius_power_flow_0_http_fronius')|multiply(-1)|int
      < states('sensor.current_solar_power_output')|multiply(1)|int }} 
    alias: Test if load in house is less than solar output
action:
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40236
      value: "0"
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40232
      value: "{{ states.sensor.solar_trim_percent.state }}"
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40236
      value: "1"
mode: single
1 Like

This would be great!
Thank you

Hi Brett

I have finally configured successfully my setup. Thanks for your help

My setup is slightly different than yours as my company is not charging me for any excess fees, but I donā€™t want to give for free also.

In case anyone wants las is interested, this is my configuration for my Fronius Gen 24 5KW

  1. I normally have the inverter configured in power limitation to have what we call zero injection (no feed to grid). In fact I have exactly Max feed to grid configured to 150w Max.
    This is to absorb minimal peaks.

This way the inverter is managing automatically any increase of power generation due to increased home loads without Home Assistant (also avoids HA point of failure)

  1. In certain occasions during the day, I want the inverter at Maximum throttle to cover high consumptions that happen once a day: water heater, climates, swimming pool filter, etc. after this events have happened, then return to scenario 1 of automatic dynamic power limitation by the inverter.

So to achieve this, HA sends a modbus write message to activate maximum throttle as per Brett indications

Then HA manages based on events and automations the excess of power by activating loads accordingly so there is minimum power unused

  1. Once step 2 is completed then HA send a new Modbus write message to disable max power generation and return to previous active mode, this is managed by the inverter

To accomplish this, I have needed to modify priority of modes as follows:

1.Modbus control
2. Dynamic power reduction
3. Generic I/O

This way is very efficient and works perfectly, with the benefit that the inverter manages increased loads with zero injection in case HA fails

Thank you @tux43 for all your support

Hi Juande, Your setup is more similar to my requirements but the offer of a blog from Brett is a good idea too. As Iā€™m wanting to setup the same sort of thing with limiting export (due to unpredictable negative FIT pricing on the wholesale market). Can you post your additions to the .yaml file & the automations you have. Not sure if you have setup modbus switches or just the raw actions in the automations. I have 3 Fronius inverters & am confident I can adapt your code ideas to my setup.

1 Like

Hi Ccspack

My Setup is quite simple. Here is how it works

As mentioned before, I have some appliances that must run their function every day:

  1. Heat water for domestic consumption
  2. Run the swimming pool filter for a number of hours (7h in summer, 3h rest of year)
  3. Climate the house (based on some room priorities)

Those actions normally run every day, and they normally run if there is solar power enough, otherwise HA stops those appliances.

To achieve this, there is an event that at sunrise shuts down zero injection, allowing maximum power generation.

As the sun rises up, power will be increasing more and more, and those 3 appliances will be activated ensuring there is no extra grid demand

For any sudden power demand coming from the rest of the house, HA will be monitoring and deactivating/activating those 3 appliances that normally run on power surplus

Once those 3 appliances have completed their daily function, then there is an event that activates again zero power injection, so the inverter generates just what is only needed by the house.

This way, we ensure that there is no ā€œfree electricityā€ for the grid company (Iā€™m not doing power compensation)

As mentioned before I have a minimum of 150W sent to the grid (so in reality is not true zero injection). This is to absorb sudden minimal peaks.

So here it is my configuration:

  1. Inverter Configuration

First establish the control priorities as modbus control first, then power limitation as shown in the image:

Then, configure power limitation (zero injection), in my case min 150w, but you could put just 0

  1. Home Assistant events:

Then define 2 events to activate and deactivate export limitation (as you can see in my case those events respond to an input_boolean helper that also allows to manually activate/disable mode)

This event deactivates via Modbus full throttle (MAX power generation) in the inverter, so the inverter then stays in the second mode configured in controlling priorities:

alias: GestiĆ³n de excedentes - Activa InyecciĆ³n CERO
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.zero_injection
    from: "off"
    to: "on"
condition: []
action:
  - service: modbus.write_register
    data:
      address: 40236
      unit: 1
      value: 0
      hub: mb_fronius
mode: single

This event activates via Modbus full throttle (MAX power generation) in the inverter, so the inverter then stays in the first mode configured in controlling priorities:

alias: GestiĆ³n de excedentes - Desactiva InyecciĆ³n ZERO
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.zero_injection
    from: "on"
    to: "off"
condition: []
action:
  - service: modbus.write_register
    data:
      address: 40236
      unit: 1
      value: 0
      hub: mb_fronius
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40232
      value: 10000
  - service: modbus.write_register
    data:
      unit: 1
      hub: mb_fronius
      address: 40236
      value: 1
mode: single

This way Iā€™m able to optimize my power generation based on my houseā€™s needs

I hope it helps

2 Likes

Hey @tux43 this is perfect.

Iā€™m an Amber customer with a Fronius Symo and yes I just started to think about rate limiting, so started my investigation some 15mins back and found your post :slight_smile:

Did you write a blog on this, or is this the only record of what you did?

I ask as I would love to validate the procedure end to end as I implement it .

Cheers Simon

No, not yet. I haventā€™ had a quiet moment!

Did you get it working?

Hey @tux43, yes I think it is working OK. I went a different track similar to the way @juande implemented it.

I will have 100% certainty tomorrow if exports run negative (again). The Fronius Modbus setup tripped me up for a bit - but I think all is working nowā€¦

Thanks heaps

Iā€™ve just re-read @juande implementation and it seems better than mine. Mine was way too complicated getting the calulations right. I am going to give it a go, with what I understand how it works and report back (no sun at the moment)

I have @juande solution working. It is great and better than mine since the inverter does the ā€˜heavy liftingā€™. I donā€™t need to continually send ModBus commands to the inverter since the settings below limit exporting when my feed-in tarrif is negative.

1 Like

Glad it worked for you Brett!

I found this was the best solution as the inverter makes everything automatic and HA only selects the desired mode: Full throttle or zero injection.

1 Like

OK, itā€™s taken me probably far too long, but here is my setup. Ive go two 8.2kW Symos linked to together & a 5kW Sumo Hybrid inverter at our place, as we are with Amber & paying wholesale prices, we wanted our inverter to all ramp down to just match household consumption when ever the FIT price goes negative.
I have setup all inverters to be controlled by ModBus as a priority, on the Sunspec Float type. when the inverters arenā€™t receiving any commands they revert to the Dynamic Power Reduction settings.

Inside the configuration.yml file I have added:

modbus:
  - type: tcp
    name: symo_fronius
    host: 192.168.1.18
    port: 502
    retry_on_empty: true
    retries: 3
  - type: tcp
    name: hybrid_fronius
    host: 192.168.1.14
    port: 502
    retry_on_empty: true
    retries: 3

I am using a input_boolean as the mechanism (switch), so that it can be controlled manually or with automations:

input_boolean:
  solarlimitor:
    initial: 0

Now I have 4 automations, 2 as triggers & 2 as instructions for each mode. The triggers:

alias: Amber SolarLimiter Deactivate when FIT returns Positive
description: ""
trigger:
  - platform: numeric_state
    entity_id: sensor.amber_feed_in_price
    above: -0.01
    for:
      hours: 0
      minutes: 1
      seconds: 0
condition: []
action:
  - wait_for_trigger:
      - platform: numeric_state
        entity_id: sensor.opennem_price
        above: -0.5
    timeout:
      hours: 0
      minutes: 16
      seconds: 0
      milliseconds: 0
    continue_on_timeout: false
  - service: input_boolean.turn_off
    data: {}
    target:
      entity_id: input_boolean.solarlimitor
mode: single
alias: Amber SolarLimitor Activate when FIT is Negative
description: ""
trigger:
  - platform: numeric_state
    entity_id: sensor.amber_feed_in_price
    for:
      hours: 0
      minutes: 1
      seconds: 30
    below: 0
condition: []
action:
  - wait_for_trigger:
      - platform: numeric_state
        entity_id: sensor.opennem_price
        below: 0
    timeout:
      hours: 0
      minutes: 16
      seconds: 0
      milliseconds: 0
    continue_on_timeout: false
  - service: input_boolean.turn_on
    data: {}
    target:
      entity_id: input_boolean.solarlimitor
mode: single

Iā€™m using the data from OpenNEM as second reference to prevent false positive triggering. And the notification for the mobile is probably an overkill, but helpful in the early days. I realise these are different address then everyone else has posted, but they work for my inverters. So these are the 2 actions:

alias: Amber SolarLimitor when Turned On
description: ""
trigger:
  - platform: state
    entity_id: input_boolean.solarlimitor
    from: "off"
    to: "on"
action:
  - service: notify.mobile_app_name_of_phone
    data:
      message: >-
        The FIT price has Dropped to {{
        (states('sensor.amber_feed_in_price')|float * 100) }}c. Solar {{
        (states('sensor.solar_production')|float / 1000) | round(1) }}kW,
        Battery {{ (states('sensor.battery')|float / 1000) | round(1) }}kW,
        Consumption {{ (states('sensor.household_consumption')|float / 1000) |
        round(1) }}kW & exporting {{
        (states('sensor.smappee_grid_realtime')|float / -1000) | round(2) }}kW.
      title: Amber Alert - Stopping Export!
  - repeat:
      until:
        - condition: state
          entity_id: input_boolean.solarlimitor
          state: "off"
      sequence:
        - service: modbus.write_register
          data:
            unit: 1
            hub: hybrid_fronius
            address: 40242
            value: >-
              {{ ((states('sensor.smappee_consumption_realtime') | float * 0.25
              ) / 5000 * 10000 ) | round(0) }}
        - service: modbus.write_register
          data:
            unit: 1
            hub: hybrid_fronius
            address: 40246
            value: 1
        - service: modbus.write_register
          data:
            unit: 1
            hub: symo_fronius
            address: 40242
            value: >-
              {{ ((states('sensor.smappee_consumption_realtime') | float * 0.44)
              / 8000 * 10000 ) | round(0) }}
        - service: modbus.write_register
          data:
            unit: 1
            hub: symo_fronius
            address: 40246
            value: 1
        - service: modbus.write_register
          data:
            slave: 2
            hub: symo_fronius
            address: 40242
            value: >-
              {{ ((states('sensor.smappee_consumption_realtime') | float *
              0.315) / 8000 * 10000 ) | round(0) }}
          enabled: true
        - service: modbus.write_register
          data:
            slave: 2
            hub: symo_fronius
            address: 40246
            value: 1
        - delay:
            hours: 0
            minutes: 0
            seconds: 10
            milliseconds: 0
mode: single

The more often you repeat the loop above, the more accurately is follows the usage. The 2 inverters together, each receive the codes on seperate slave addresses.

alias: Amber SolarLimitor when Turned Off
description: ""
trigger:
  - platform: state
    entity_id: input_boolean.solarlimitor
    from: "on"
    to: "off"
action:
  - delay:
      hours: 0
      minutes: 0
      seconds: 15
      milliseconds: 0
  - service: notify.mobile_app_name_of_phone
    data:
      message: >-
        The FIT price has returned to Positive {{
        (states('sensor.amber_feed_in_price')|float * 100) }}c. Solar {{
        (states('sensor.solar_production')|float / 1000) | round(1) }}kW,
        Battery {{ (states('sensor.battery')|float / 1000) | round(1) }}kW,
        Consumption {{ (states('sensor.household_consumption')|float / 1000) |
        round(1) }}kW & exporting {{
        (states('sensor.smappee_grid_realtime')|float / -1000) | round(2) }}kW.
      title: Amber Alert - Restarting Export!
  - service: modbus.write_register
    data:
      address: 40246
      unit: 1
      value: 0
      hub: hybrid_fronius
    enabled: true
  - service: modbus.write_register
    data:
      address: 40246
      unit: 1
      value: 0
      hub: symo_fronius
    enabled: true
  - service: modbus.write_register
    data:
      address: 40246
      unit: 2
      value: 0
      hub: symo_fronius
mode: single

Hopefully that all makes sense & will be useful to some. It has been a lot of work to figure out, but happy to help if anyone has any questions about it all. I am currently working on the same sort of thing the automate the charging & discharging of my battery when it is the most profitable

5 Likes

Thatā€™s looking great!

What settings did you use on the fronius inverter when enabling modbus? Iā€™m having trouble working out what Iā€™m missing. The logs do show a ā€œGatewayNoResponseā€ message, but apart from that I really donā€™t get anything at allā€¦

[EDIT: I had modbus on the inverter set to float instead of int+SF. Now Iā€™m not getting any messages in the logs, which leads me to assume that the message is actually getting through to the inverter now]

Is there any feedback on the inverter side I can check to ensure the modbus is actually working?

Hi @tux43

Wondering if you have any other tips/setup knowledge.

I am running a Fronius Primo 5, and for the life of me cannot write to the Fronius Modbus. Other devices on LAN can (Victron Cerbo), but I canā€™t get HA to do it.

Config.Yaml


modbus:
  - type: tcp
    name: primo1_fronius
    host: 192.168.10.122
    port: 502
    retry_on_empty: true
    retries: 30
    sensors:
    - name: MB_FRONIUS_1_40072_AC_Current
      slave: 1
      count: 1
      address: 40071
      data_type: uint16
      unit_of_measurement: "A"
      scale: 0.01
      precision: 1
      scan_interval: 5
      device_class: current
      state_class: measurement
..........

Automation

alias: Fronius Negative Power - Limit Output
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.negative_fit_state
    from: "off"
    to: "on"
condition: []
action:
  - service: modbus.write_register
    data:
      unit: 1
      hub: primo1_fronius
      address: 40236
      value: "0"
  - service: modbus.write_register
    data:
      unit: 1
      hub: primo1_fronius
      address: 40232
      value: "9000"
  - service: modbus.write_register
    data:
      unit: 1
      hub: primo1_fronius
      address: 40236
      value: "1"
mode: single

Fronius:

HA Dashboard showing data on Modbus

HA Logs:

2023-02-05 13:29:27.606 ERROR (SyncWorker_0) [homeassistant.components.modbus.modbus] Pymodbus: primo1_fronius: Modbus Error: [Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 8 bytes (0 received)
2023-02-05 13:44:47.522 ERROR (SyncWorker_11) [homeassistant.components.modbus.modbus] Pymodbus: primo1_fronius: Modbus Error: [Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 8 bytes (0 received)
2023-02-05 13:53:04.623 ERROR (SyncWorker_0) [homeassistant.components.modbus.modbus] Pymodbus: primo1_fronius: Modbus Error: [Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 8 bytes (0 received)
2023-02-05 13:59:46.316 ERROR (SyncWorker_0) [homeassistant.components.modbus.modbus] Pymodbus: primo1_fronius: Modbus Error: [Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 8 bytes (0 received)

Wondering if there is a setting/adding/code Iā€™ve missed that seems obvious to others?

1 Like