|
## |
|
# Automatically limit inverters based on grid excess with Home Assistant and OpenDTU |
|
# by Mathieu Carbou |
|
# Ref: https://gist.github.com/mathieucarbou/382556f1279d612962e03232544692d1 |
|
## |
|
|
|
# https://www.home-assistant.io/integrations/input_number |
|
input_number: |
|
# Inverters nominal power, usually 400W, 500W, 1000W, etc |
|
inverters_nominal_power: |
|
name: Inverters Nominal Power |
|
unit_of_measurement: W |
|
min: 0 |
|
max: 2000 |
|
step: 1 |
|
mode: box |
|
# Manual control of the production limit of inverters |
|
inverters_power_limit: |
|
name: Inverters Power Limit |
|
unit_of_measurement: W |
|
min: 0 |
|
max: 1000 |
|
step: 1 |
|
mode: slider |
|
# Target excess or import to stay close to |
|
inverters_excess_setpoint: |
|
name: Inverters Excess Setpoint |
|
unit_of_measurement: W |
|
min: -10000 |
|
max: 10000 |
|
step: 1 |
|
mode: box |
|
# Total number of inverters. Inverters should be named Inverter 1, Inverter 2, etc |
|
inverters_count: |
|
name: Inverters Count |
|
min: 0 |
|
max: 30 |
|
step: 1 |
|
mode: box |
|
|
|
# https://www.home-assistant.io/integrations/rest |
|
rest: |
|
# Get the 3ERL trend (https://3erl.fr) for 3ERL subscribers |
|
- resource: https://3erl.fr/api.json |
|
scan_interval: 60 |
|
binary_sensor: |
|
- name: "3ERL: Bridage Demandé" |
|
icon: "mdi:car-speed-limiter" |
|
unique_id: 347A6F4D-6B09-4A20-9C2C-D611ED23EF4B |
|
value_template: "{{ value_json['Bridage'] }}" |
|
sensor: |
|
- name: "3ERL: Tendance du jour" |
|
icon: "mdi:trending-up" |
|
unique_id: 31F1BED4-8100-44A4-88BA-DB45450611CF |
|
# multiply by 10 because the value is in c€/kWh |
|
value_template: | |
|
{% set data = value_json['PREP_Profile'] %} |
|
{% if data == '3+' %} 🟢🟢🟢 |
|
{% elif data == '2+' %} 🟢🟢 |
|
{% elif data == '1+' %} 🟢 |
|
{% elif data == '0' %} ⚠️ |
|
{% elif data == '1-' %} ⛔️ |
|
{% elif data == '2-' %} ⛔️⛔️ |
|
{% elif data == '3-' %} ⛔️⛔️⛔️ |
|
{% else %} ⁉️ |
|
{% endif %} |
|
|
|
# Get the electricity market price trend from RTE |
|
- resource_template: https://www.services-rte.com/cms/open_data/v1/price/table?startDate={{ now().strftime('%d%%2F%m%%2F%Y') }} |
|
scan_interval: 60 |
|
sensor: |
|
- name: "Électricité: Prix de règlement des écarts positifs" |
|
icon: "mdi:currency-eur" |
|
unique_id: EA2D2A8C-5327-47C8-92EF-C16E9BE7A4C5 |
|
state_class: measurement |
|
unit_of_measurement: "€/MWh" |
|
value_template: "{{ value_json['values'][0]['pre']['positive'] | float }}" |
|
- name: "Électricité: Prix de règlement des écarts négatifs" |
|
icon: "mdi:currency-eur" |
|
unique_id: 95AE2165-7E9A-40BA-A555-1C7B7B93B26D |
|
value_template: "{{ value_json['values'][0]['pre']['negative'] | float }}" |
|
state_class: measurement |
|
unit_of_measurement: "€/MWh" |
|
|
|
mqtt: |
|
# https://www.home-assistant.io/integrations/sensor.mqtt |
|
sensor: |
|
- name: Inverters YieldTotal |
|
unique_id: D6A6659B-EE6A-4C3F-91F4-C0FBD46E8CF5 |
|
state_class: total_increasing |
|
device_class: energy |
|
unit_of_measurement: "kWh" |
|
state_topic: "opendtu/ac/yieldtotal" |
|
value_template: "{{ value|float }}" |
|
# availability_topic: "opendtu/ac/is_valid" |
|
# payload_available: "1" |
|
# payload_not_available: "0" |
|
|
|
- name: Inverters Produced PowerDC |
|
unique_id: "01e37eed-3045-4eee-a786-1249567fe316" |
|
state_class: measurement |
|
device_class: power |
|
unit_of_measurement: W |
|
state_topic: "opendtu/dc/power" |
|
value_template: "{{ value|int }}" |
|
availability_topic: "opendtu/dc/is_valid" |
|
payload_available: "1" |
|
payload_not_available: "0" |
|
|
|
- name: Inverters Produced Power |
|
unique_id: "01e37eed-3045-4eee-a786-1249567fe315" |
|
state_class: measurement |
|
device_class: power |
|
unit_of_measurement: W |
|
state_topic: "opendtu/ac/power" |
|
value_template: "{{ value|int }}" |
|
availability_topic: "opendtu/ac/is_valid" |
|
payload_available: "1" |
|
payload_not_available: "0" |
|
|
|
template: |
|
# https://www.home-assistant.io/integrations/sensor/ |
|
- sensor: |
|
# Compute the number of inverters currently producing power |
|
- name: Inverters Producing Count |
|
unique_id: 7FAAECB4-2105-4F86-B63E-A1962F7FC81E |
|
state: >- |
|
{{ ['1410A01EC916', '1410A01ED6CA', '1410A01ED5A9', '1410A01ED5F0', '1410A01F092D', '1410A01ED604', '1410A01F010C']|map('regex_replace', '(.+)', "sensor.inverter_\\1_power")|map('states')|map('float', 0)|select('gt', 0)|list|count }} |
|
|
|
# Fix OpenDTU YieldTotal |
|
- name: Inverters Energy |
|
unique_id: 6E14EC86-0965-463E-BFBF-6008B61B56AF |
|
state_class: total_increasing |
|
device_class: energy |
|
unit_of_measurement: "kWh" |
|
state: "{{ [states('sensor.inverters_yieldtotal')|float(0), states('sensor.inverters_energy')|float(0) ] | max }}" |
|
|
|
# Shelly EM clamp based |
|
- name: Solar Production Power |
|
unique_id: "01e37eed-3045-4eee-a786-1249567fe300" |
|
state_class: measurement |
|
device_class: power |
|
unit_of_measurement: W |
|
state: "{{ states.sensor.solar_plant_group_a_power.state|float + states.sensor.solar_plant_group_b_power.state|float }}" |
|
|
|
# Shelly EM clamp based |
|
- name: Solar Consumed Power |
|
unique_id: "01e37eed-3045-4eee-a786-1249567fe303" |
|
state_class: measurement |
|
device_class: power |
|
unit_of_measurement: W |
|
availability: "{{ ['sensor.solar_production_power', 'sensor.grid_returned_power']|map('states')|map('is_number')|min }}" |
|
state: "{{ [states.sensor.solar_production_power.state|float - states.sensor.grid_returned_power.state|float, 0] | max }}" |
|
|
|
# Shelly EM clamp based |
|
- name: Home Consumed Power |
|
unique_id: BE34D1AD-AB8E-4909-ACE0-BBA7D3877105 |
|
state_class: measurement |
|
device_class: power |
|
unit_of_measurement: W |
|
availability: "{{ ['sensor.solar_production_power', 'sensor.grid_power']|map('states')|map('is_number')|min }}" |
|
state: "{{ states.sensor.solar_production_power.state|float + states.sensor.grid_power.state|float }}" |
|
|
|
# c == this sensor last value (consumed solar production) |
|
# p == daily solar production |
|
# e == daily return to grid (loss) |
|
# we should always have: p >= c and p >= e |
|
# At midnight, yield day resets but not at the same time so we can have p < c or p < e |
|
# When not at midnight, we compare last value of c with newly computed value (c == p - e) |
|
- name: Solar Consumed Energy Daily |
|
unique_id: "01e37eed-3045-4eee-a786-1249567fe305" |
|
state_class: total_increasing |
|
device_class: energy |
|
unit_of_measurement: kWh |
|
state: >- |
|
{% set p = states.sensor.inverters_energy_meter_daily.state|float(0) %} |
|
{% set c = states.sensor.solar_consumed_energy_daily.state|float(0) %} |
|
{% set e = states.sensor.grid_energy_returned_meter_daily.state|float(0) %} |
|
{{ [0 if p == 0 or p < c or p < e else c, [0, p - e] | max] | max }} |
|
|
|
- name: Solar Autoconsumption Level Daily |
|
unique_id: "01e37eed-3045-4eee-a786-1249567fe304" |
|
state_class: measurement |
|
unit_of_measurement: "%" |
|
availability: "{{ states('sensor.inverters_energy_meter_daily')|float(0) > 0 }}" |
|
state: "{{ [100, (100 - 100 * states('sensor.grid_energy_returned_meter_daily')|float(0) / states('sensor.inverters_energy_meter_daily')|float )] | min }}" |
|
|
|
sensor: |
|
# https://www.home-assistant.io/integrations/integration/ |
|
# Shelly EM clamp based |
|
- platform: integration |
|
name: Solar Production Energy |
|
unique_id: E85A7E482-7937-4ABC-A0C6-1B9790DF417F |
|
source: sensor.solar_production_power |
|
unit_prefix: k |
|
# Shelly EM clamp based |
|
- platform: integration |
|
name: Home Consumed Energy |
|
unique_id: E2BA4424-8CA8-4D74-9A2F-F0E5EFF84622 |
|
source: sensor.home_consumed_power |
|
unit_prefix: k |
|
|
|
# https://www.home-assistant.io/integrations/utility_meter/ |
|
utility_meter: |
|
# Compute the daily yield of all inverters |
|
inverters_energy_meter_daily: |
|
name: Inverters Energy Meter Daily |
|
unique_id: 4C4D8D06-C9D2-4408-B21A-1274A6E0F041 |
|
source: sensor.inverters_energy |
|
always_available: true |
|
cycle: daily |
|
|
|
# https://www.home-assistant.io/docs/automation/ |
|
automation: |
|
# Detect changes to "Inverters Power Limit" input and propagate it to imverters |
|
- id: "0000000000038" |
|
alias: "Solar: Propagate power limit change to inverters" |
|
trigger: |
|
- trigger: state |
|
entity_id: |
|
- input_number.inverters_power_limit |
|
condition: [] |
|
action: |
|
- action: number.set_value |
|
data: |
|
value: "{{states('input_number.inverters_power_limit')|int(0)}}" |
|
target: |
|
entity_id: |
|
- number.inverter_1410A01EC916_limit_nonpersistent_absolute |
|
- number.inverter_1410A01ED6CA_limit_nonpersistent_absolute |
|
- number.inverter_1410A01ED5A9_limit_nonpersistent_absolute |
|
- number.inverter_1410A01ED5F0_limit_nonpersistent_absolute |
|
- number.inverter_1410A01F092D_limit_nonpersistent_absolute |
|
- number.inverter_1410A01ED604_limit_nonpersistent_absolute |
|
- number.inverter_1410A01F010C_limit_nonpersistent_absolute |
|
|
|
# Each 2 minutes, compute the new limit to set to inverters |
|
- id: "0000000000039" |
|
alias: "Solar: Auto adjust power limits" |
|
trigger: |
|
- trigger: time_pattern |
|
minutes: /1 |
|
# seconds: /30 |
|
condition: |
|
- condition: state |
|
entity_id: binary_sensor.opendtu_ff4930_status |
|
state: "on" |
|
action: |
|
- if: |
|
- condition: numeric_state |
|
entity_id: sensor.inverters_producing_count |
|
above: 0 |
|
then: |
|
- variables: |
|
new_limit: >- |
|
{% set producing = states('sensor.inverters_producing_count')|int(0) %} |
|
{% set nominalPower = states('input_number.inverters_nominal_power')|int(0) %} |
|
{% if producing <= 0 %} |
|
{{nominalPower}} |
|
{% else %} |
|
{% set grid = states('sensor.grid_power')|float(0) %} |
|
{% set setpoint = states('input_number.inverters_excess_setpoint')|int(0) %} |
|
{% set limit = states('input_number.inverters_power_limit')|int(nominalPower) %} |
|
{% set miss = grid - setpoint %} |
|
{% set error = miss / producing %} |
|
{{ [nominalPower, [10, limit + error]|max]|min|round }} |
|
{% endif %} |
|
- action: input_number.set_value |
|
data: |
|
value: "{{ new_limit|int(0) }}" |
|
target: |
|
entity_id: |
|
- input_number.inverters_power_limit |
|
else: |
|
- action: input_number.set_value |
|
data: |
|
value: "{{ states('input_number.inverters_nominal_power')|int(0) }}" |
|
target: |
|
entity_id: |
|
- input_number.inverters_power_limit |
|
|
|
# For 3ERL subscribers, activate or deactivate the automation to automatically limit the inverters when 3ERL asks for it |
|
- id: "0000000000040" |
|
alias: "Solar: Auto activate 3ERL Zero-Inject" |
|
triggers: |
|
- trigger: state |
|
entity_id: |
|
- binary_sensor.3erl_bridage_demande |
|
to: "on" |
|
- trigger: homeassistant |
|
event: start |
|
conditions: |
|
- condition: state |
|
entity_id: binary_sensor.3erl_bridage_demande |
|
state: "on" |
|
actions: |
|
- action: notify.whatsapp_mathieu |
|
data: |
|
message: "[SOLAR] 3RL Bridage: ON" |
|
- action: automation.turn_on |
|
target: |
|
entity_id: automation.solar_auto_adjust_power_limits |
|
|
|
- id: "0000000000052" |
|
alias: "Solar: Auto deactivate 3ERL Zero-Inject" |
|
triggers: |
|
- trigger: state |
|
entity_id: |
|
- binary_sensor.3erl_bridage_demande |
|
from: "on" |
|
to: "off" |
|
conditions: [] |
|
actions: |
|
- action: notify.whatsapp_mathieu |
|
data: |
|
message: "[SOLAR] 3RL Bridage: OFF" |
|
- action: automation.turn_off |
|
target: |
|
entity_id: automation.solar_auto_adjust_power_limits |
|
- action: input_number.set_value |
|
data: |
|
value: "{{ states('input_number.inverters_nominal_power')|int(0) }}" |
|
target: |
|
entity_id: |
|
- input_number.inverters_power_limit |
|
# - action: input_number.set_value |
|
# metadata: {} |
|
# data: |
|
# value: -3000 |
|
# target: |
|
# entity_id: input_number.inverters_excess_setpoint |
|
|
|
- id: "0000000000041" |
|
alias: "Solar: Auto update Setpoint" |
|
triggers: |
|
- trigger: state |
|
entity_id: |
|
- binary_sensor.3erl_bridage_demande |
|
- binary_sensor.openevse_vehicle_connected |
|
- sensor.openevse_vehicle_battery_level |
|
- trigger: homeassistant |
|
event: start |
|
conditions: |
|
- condition: state |
|
entity_id: binary_sensor.3erl_bridage_demande |
|
state: "on" |
|
actions: |
|
- if: |
|
- condition: state |
|
entity_id: binary_sensor.openevse_vehicle_connected |
|
state: "on" |
|
- condition: numeric_state |
|
entity_id: sensor.openevse_vehicle_battery_level |
|
below: 100 |
|
then: |
|
- action: input_number.set_value |
|
metadata: {} |
|
data: |
|
value: -600 |
|
target: |
|
entity_id: input_number.inverters_excess_setpoint |
|
else: |
|
- action: input_number.set_value |
|
metadata: {} |
|
data: |
|
value: -100 |
|
target: |
|
entity_id: input_number.inverters_excess_setpoint |
|
|
|
- id: "0000000000042" |
|
alias: "Solar: Notify of high grid voltage" |
|
trigger: |
|
- trigger: numeric_state |
|
entity_id: |
|
- sensor.grid_voltage |
|
above: 250 |
|
action: |
|
- action: notify.whatsapp_mathieu |
|
data: |
|
message: "[GRID] High Voltage (> 250V)" |
|
- delay: |
|
hours: 0 |
|
minutes: 1 |
|
seconds: 0 |
|
milliseconds: 0 |
|
mode: single |
|
max_exceeded: silent |
|
|
|
- id: "0000000000043" |
|
alias: "Solar: Notify of inverter producing count" |
|
trigger: |
|
- trigger: state |
|
entity_id: |
|
- sensor.inverters_producing_count |
|
to: null |
|
action: |
|
- action: notify.whatsapp_mathieu |
|
data: |
|
message: "[SOLAR] Inverters producing: {{ states('sensor.inverters_producing_count')|int(0) }}" |
|
mode: single |
|
max_exceeded: silent |
Meme un peu d'aide ne serait pas de refus j'ai tous mis dans config package comme explique plus bas dans le gith mais ca fonctionne pas