Thank you guys for sharing the above, took some inspiration and made a variant with multiple inverters.
This is like a Plant Controller where multiple inverters (different in size) need to be adjusted in parallel based on information coming from a single Fronius SmartMeter. Actually I think any kind of “smart” meter could be used with this approach, as it doesn’t interact directly with any of the inverters, only through HA automation.
See attached picture. The SmartMeter (in consumption path) is attached to one of the inverters with DataManager card, but there’s two new GEN24 inverters added to the system, which don’t connect to the SmartMeter.
I have no automated limiting options enabled in any of the inverters. I only define the priorty so that “Controlling via Modbus” will be first, and of course enable “Inverter control via Modbus”. SunSpec model tpye is “Int + SF”.
With these settings to limit manually the output power, one has to change the percentage output at holding register 40233, and then enables throttling by writing value 1 to register number 40237 (In Fronius’s docs the addresses are base1, but since HA uses base0, we use address numbers decreased by 1, that’s why also in the examples above we have addresses 40232 and 40236 accordingly).
As a cleaner approach, I decided to read the data from the inverters with the same modbus
integration as is used to write the registers. This is independent from the Fronius or SunSpec integrations.
modbus:
- name: inverter_symo #repeat this section for other two gen24 inverters
type: tcp
host: 192.168.1.80
port: 502
retry_on_empty: true
retries: 3
delay: 3
sensors:
- name: smartmeter_w_raw #adding this only to the inverter where the SmartMeter connects
slave: 240 #(snapinverter symo: 240, gen24 default: 200)
input_type: holding
address: 40087
scan_interval: 5
unique_id: smartmeter_w_raw
count: 5
data_type: custom
structure: ">hhhhh"
- name: inverter_symo_raw_lim
slave: 1
input_type: holding
address: 40232
scan_interval: 5
unique_id: inverter_symo_raw_lim
count: 5
data_type: custom
structure: ">hhhhh"
- name: inverter_symo_raw_w
slave: 1
input_type: holding
address: 40083
scan_interval: 5
unique_id: inverter_symo_raw_w
count: 2
data_type: custom
structure: ">hh"
Template sensors to calculate from modbus raw registers and scaling factors:
sensor:
- name: "Smartmeter W"
unique_id: smartmeter_W
state_class: measurement
device_class: power
unit_of_measurement: W
icon: mdi:home-lightning-bolt
availability: "{{ has_value('sensor.smartmeter_w_raw') }}"
state: >
{{
(states('sensor.smartmeter_w_raw').split(',')[0]) | int(default=0)
/10**(states('sensor.smartmeter_w_raw').split(',')[4] | int(default=0)*-1)
}}
- name: "Inverter Symo W"
unique_id: inverter_symo_W
state_class: measurement
device_class: power
unit_of_measurement: W
icon: mdi:solar-power
availability: "{{ has_value('sensor.inverter_symo_raw_w') }}"
state: >
{{
(states('sensor.inverter_symo_raw_w').split(',')[0]) | int(default=0)
/10**(states('sensor.inverter_symo_raw_w').split(',')[1] | int(default=0)*-1)
}}
- name: "Inverter Symo limiter percentage"
unique_id: inverter_symo_WMaxLimPct
state_class: measurement
unit_of_measurement: "%"
icon: mdi:car-cruise-control
availability: "{{ has_value('sensor.inverter_symo_raw_lim') }}"
state: >
{{ (states('sensor.inverter_symo_raw_lim').split(',')[0] | int(default=10000))/100 }}
binary_sensor:
- name: "Inverter Symo limiter enabled"
unique_id: inverter_symo_limiter
icon: mdi:car-speed-limiter
availability: "{{ has_value('sensor.inverter_symo_raw_lim') }}"
state: >
{{ (states('sensor.inverter_symo_raw_lim').split(',')[4] | bool(default=false)) }}
The above is for the Symo, similar config is added for the other two inverters (without the SmartMeter sensor of course).
What I did so far is I decided to also have an input_number
to configure the maximum export value. Note that this can also be negative, for some very strict conditions when you want to make 100% sure that absolutely no power is fed into the grid, but some little is always drawn:
input_number:
- max_export:
name: Maximum export
min: -300
max: 10000
step: 100
icon: mdi:transmission-tower-import
unit_of_measurement: 'W'
The approach here is that you can use a separate automation to simply adjust this number when you don’t want to feed in, or you want to feed just until a maximum value.
Calculation of the target percentage happens similarly as in the examples above. My smartmeter is in the consumption path.
Needed to define two sensors because the Symo is 3700W, the other two GEN24s are 6000W (they can use the same template sensor).
Note that since there are 3 inverters, we divide the sum by 3, so we distribute the limits accordingly to the inverters.
- name: "Inverter Symo limit"
unique_id: inverter_target_limit_sym
state_class: measurement
state: >
{% set proc = ((states("sensor.smartmeter_w")|float(default=0)-states("input_number.max_export")|float(default=0))/3/3700)|multiply(-1)|multiply(10000)|int %}
{% if proc > 10000 %}10000{% elif proc < 0 %}0{% else %}{{ proc }}{% endif -%}
- name: "Inverter Gen24 limit"
unique_id: inverter_target_limit_gen
state_class: measurement
state: >
{% set proc = ((states("sensor.smartmeter_w")|float(default=0)-states("input_number.max_export")|float(default=0))/3/6000)|multiply(-1)|multiply(10000)|int %}
{% if proc > 10000 %}10000{% elif proc < 0 %}0{% else %}{{ proc }}{% endif -%}
And the automations:
- alias: Inverter limiter Symo
mode: single
trigger:
- platform: state
entity_id:
- sensor.inverter_symo_limit
action:
- choose:
- conditions: # this is for the case when export limit is to be disabled
- condition: template
value_template: "{{ states('sensor.inverter_symo_limit')|int == 10000}}"
sequence:
- service: modbus.write_register
data:
unit: 1
hub: inverter_symo
address: 40232
value: "10000"
- service: modbus.write_register
data:
unit: 1
hub: inverter_symo
address: 40236
value: "0"
default:
- service: modbus.write_register
data:
unit: 1
hub: inverter_symo
address: 40232
value: "{{ states.sensor.inverter_symo_limit.state }}"
- service: modbus.write_register
data:
unit: 1
hub: inverter_symo
address: 40236
value: "1"
- alias: Inverter limiter Gen24
mode: single
trigger:
- platform: state
entity_id:
- sensor.inverter_gen24_limit
action:
- choose:
- conditions:
- condition: template
value_template: "{{ states('sensor.inverter_gen24_limit')|int == 10000}}"
sequence:
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_a
address: 40232
value: "10000"
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_b
address: 40232
value: "10000"
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_a
address: 40236
value: "0"
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_b
address: 40236
value: "0"
default:
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_a
address: 40232
value: "{{ states.sensor.inverter_gen24_limit.state }}"
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_b
address: 40232
value: "{{ states.sensor.inverter_gen24_limit.state }}"
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_a
address: 40236
value: "1"
- service: modbus.write_register
data:
unit: 1
hub: inverter_gen24_b
address: 40236
value: "1"
Result (with max_export
set to 1700):
The production of the 3 inverters dynamically follows the house demand, always feeding in not more than 1700W.