-
summary:Automated zero feed-in control for 1-2 Deye solar inverters using real-time grid power monitoring from Shelly meter. Prevents reverse power flow to the grid while maximizing self-consumption.
-
Remark:Testing has revealed that when the Solarman integration sets the Deye inverter’s power output percentage to 0%, the value is automatically reverted to rated power output. To resolve this behavior, the automation constrains the inverter’s minimum output power to 1% of its rated power.
blueprint:
name: Deye Zero Feed-in Control
description: Automated zero-feed-in control for one or two Deye inverters using Shelly power meter
domain: automation
input:
shelly_power_sensor:
name: Shelly total active power sensor (Required)
selector:
entity:
domain: sensor
device_class: power
inverter1_control:
name: Inverter 1 active power regulation entity (Required)
selector:
entity:
domain: number
inverter1_rated:
name: Inverter 1 rated power (W) (Required)
selector:
number:
min: 300
max: 2500
step: 100
unit_of_measurement: W
inverter2_control:
name: Inverter 2 active power regulation entity (Optional)
default: none
selector:
entity:
domain: number
inverter2_rated:
name: Inverter 2 rated power (W) (Optional)
default: 0
selector:
number:
min: 300
max: 2500
step: 100
unit_of_measurement: W
last_shelly_active_power_entity:
name: Helper entity to store last grid power (Required)
selector:
entity:
domain: input_number
variables:
shelly_entity: !input shelly_power_sensor
inv1_control_entity: !input inverter1_control
inv1_rated: !input inverter1_rated
inv2_control_entity: !input inverter2_control
inv2_rated: !input inverter2_rated
last_power_entity_id: !input last_shelly_active_power_entity
inv1_min_power: >
{{ ((inv1_rated | int) * 0.01) | round(1) }}
inv2_exists: >
{{
inv2_control_entity != 'none' and
inv2_rated | int > 0
}}
inv2_min_power: >
{% if inv2_exists %}
{{ ((inv2_rated | int) * 0.01) | round(1) }}
{% else %}
0
{% endif %}
total_min_power: >
{{ inv1_min_power + inv2_min_power }}
# Inverter 1 current percentage setting
inv1_percent: >
{{ states(inv1_control_entity) | float(0) | round(1) }}
# Inverter 2 current percentage setting
inv2_percent: >
{{ (states(inv2_control_entity) | float(0) | round(1)) if inv2_exists else 0 }}
trigger:
- platform: time_pattern
# Timer triggered every 9 seconds
seconds: /9
condition:
- condition: template
# Automation is triggered when the current purchased power is greater than 1% of the sum of the rated power of all the inverters
# or when the current is feeding and the micro-reverse is not the minimum (1%) output.
value_template: >
{% set value = states(shelly_entity) | float(0) %}
{{ (value < 0 and (inv1_percent > 1 or inv2_percent > 1)) or value > total_min_power }}
actions:
- variables:
last_power: >
{{ states(last_power_entity_id) | float(0) | round(1) }}
# Current grid power (positive = importing, negative = exporting)
grid_power: >
{{ states(shelly_entity) | float(0) | round(1) }}
# Check whether the grid power changes,
# no change means that the power data of Shelly has not been updated, 、
# and the command of the inverter output power has not been issued successfully, try to retry.
power_unchanged: >
{{ (grid_power - last_power) | abs < 1 }}
# Inverter 1 current output power = (percentage * rated power) / 100
inv1_current_power: >
{{ (inv1_percent * (inv1_rated | int)) / 100 | round(1) }}
# Inverter 2 current output power = (percentage * rated power) / 100
inv2_current_power: >
{{ (inv2_percent * (inv2_rated | int)) / 100 | round(1) if inv2_exists else 0 }}
# Current total output power
total_current: >
{{ inv1_current_power + inv2_current_power }}
# Total rated power
total_rated: >
{{ (inv1_rated | int) + (inv2_rated | int if inv2_exists else 0) }}
total_target: >
{{ (total_current | round(1)) if power_unchanged else ((total_current + grid_power) | round(1))}}
# Allocation percentage
allocation_percent: >
{{
(total_target * 100) / total_rated | round(1) if total_rated > 0 else 0
}}
# Inverter 1 target power with 1% minimum
inv1_target_power: >
{% set unclamped = (inv1_rated | int * allocation_percent) / 100 %}
{% set min_power = (inv1_rated | int) * 0.01 %}
{% if unclamped < min_power%}
{{ min_power | round(1) }}
{% elif unclamped > inv1_rated | int %}
{{ inv1_rated | int }}
{% else %}
{{ unclamped | round(1) }}
{% endif %}
inv1_target_percent: >
{% if (inv1_rated | int) > 0 %}
{{ ((inv1_target_power * 100) / (inv1_rated | int)) | round(1) }}
{% else %}
0
{% endif %}
# Inverter 2 target power with 1% minimum
inv2_target_power: >
{% if inv2_exists %}
{% set unclamped = (inv2_rated | int * allocation_percent) / 100 %}
{% set min_power = (inv2_rated | int) * 0.01 %}
{% if unclamped < min_power %}
{{ min_power | round(1) }}
{% elif unclamped > inv2_rated | int %}
{{ inv2_rated | int }}
{% else %}
{{ unclamped | round(1) }}
{% endif %}
{% else %}
0
{% endif %}
inv2_target_percent: >
{% if inv2_exists and (inv2_rated | int) > 0 %}
{{ ((inv2_target_power * 100) / (inv2_rated | int)) | round(1) }}
{% else %}
0
{% endif %}
log_message: >
{{
"Grid:" ~ grid_power ~ "W | " ~
"Current:" ~ total_current ~ "W (Inv1:" ~ inv1_current_power ~ "W@" ~ inv1_percent ~ "%)" ~ " | " ~
"power_unchanged:" ~ power_unchanged ~
(", Inv2:" ~ inv2_current_power ~ "W@" ~ inv2_percent ~ "%" if inv2_exists else "") ~ " | " ~
"inv1_rated:" ~ inv1_rated ~ "W | " ~
("inv2_rated:" ~ inv2_rated ~ "W | " if inv2_exists else "") ~ " | " ~
"Target:" ~ total_target ~ "W | " ~
"inv1_target_power:" ~ inv1_target_power ~ "W" ~
(" | inv2_target_power:" ~ inv2_target_power ~ "W" if inv2_exists else "") ~
" | Set: Inv1=" ~ inv1_target_percent ~ "%" ~
(", Inv2=" ~ inv2_target_percent ~ "%" if inv2_exists else "")
}}
# Log record
- service: system_log.write
data:
level: info
message: "ZeroFid: {{ now().strftime('%H:%M:%S') }} | {{ log_message }}"
# Control inverter 1
- service: number.set_value
target:
entity_id: "{{ inv1_control_entity }}"
data:
value: "{{ inv1_target_percent | float }}"
# Control inverter 2 only if exists
- if:
- condition: template
value_template: "{{ inv2_exists }}"
then:
- service: number.set_value
target:
entity_id: "{{ inv2_control_entity }}"
data:
value: "{{ inv2_target_percent | float }}"
- service: input_number.set_value
target:
entity_id: "{{ last_power_entity_id }}"
data:
value: "{{ grid_power | float }}"