169 lines
7.3 KiB
YAML
169 lines
7.3 KiB
YAML
- alias: PV clipping - Forecast clipped PV for next 24 hours
|
|
description: 'Calculates forecast clipped PV for day = sum of 5 minute predbat.pv_energy forecasts
|
|
> inverter limit.
|
|
|
|
Change the variables to suit your set-up. Adjust the tuning_factor to optimise
|
|
battery availability. The tuning factor is designed to account for spikey solar
|
|
due to intermittent clouds and how aggressive you want to be with making room
|
|
in the battery - the lower the value, the more room it will make.'
|
|
triggers:
|
|
- event: sunrise
|
|
offset: 0
|
|
trigger: sun
|
|
conditions: []
|
|
actions:
|
|
- variables:
|
|
tuning_factor: 0.9
|
|
inverter_limit_kW: 5
|
|
inverter_limit: '{{ inverter_limit_kW / 12 }}'
|
|
- metadata: {}
|
|
data:
|
|
value: "{% set pv_clipped = namespace(value=0.0) %} {% set pv_prev\
|
|
\ = namespace(value=0.0) %} {% set pv_cur = namespace(value=0.0) %}\
|
|
\ {% if state_attr('predbat.pv_energy', 'results') %}\n {%- for five_min_period\
|
|
\ in state_attr('predbat.pv_energy', 'results') -%}\n {% if loop.index0\
|
|
\ == 0 %}\n {%- set pv_prev.value = state_attr('predbat.pv_energy', 'results')[five_min_period]\
|
|
\ | float -%}\n {% else %}\n {%- set pv_cur.value = state_attr('predbat.pv_energy',\
|
|
\ 'results')[five_min_period] | float -%}\n {% set tuned_clipped = (pv_cur.value\
|
|
\ - pv_prev.value) / tuning_factor %}\n {% if tuned_clipped > inverter_limit\
|
|
\ %}\n {%- set pv_clipped.value = pv_clipped.value + tuned_clipped\
|
|
\ - inverter_limit -%}\n {% endif %}\n {%- set pv_prev.value = pv_cur.value\
|
|
\ -%}\n {% endif %}\n {# Only look at the next 24 hours (each loop is\
|
|
\ 5 minutes) #}\n {% if loop.index0 == 288 %}\n {% break %}\n {%\
|
|
\ endif %}\n {%- endfor -%} \n{% endif %} {{ pv_clipped.value }}\n"
|
|
target:
|
|
entity_id: input_number.pv_clipped_in_next_24_hours
|
|
action: input_number.set_value
|
|
mode: single
|
|
- alias: PV clipping - make room in battery at sunrise
|
|
description: "Make room in battery at sunrise if PV clipping is forecast in the\
|
|
\ next 24 hours \nAdjust the variables to suit your set-up. The sunrise_to_clipping_hours\
|
|
\ variable is the number of hours from sunrise to when your set-up is first at\
|
|
\ risk of clipping. Round up to the nearest half-hour eg 3.5\nIf any clipping\
|
|
\ is predicted, the automation sets the first 30-min export for (sunrise_to_clipping_hours\
|
|
\ - 0.5) hours after sunrise. Then backfills towards sunrise with appropriate\
|
|
\ force export and freeze export slots. Assumes a straight-line increase in export\
|
|
\ capacity being taken up by solar export from sunrise to clipping start. And\
|
|
\ a corresponding decrease in availability for battery export. Does not account\
|
|
\ for load."
|
|
triggers:
|
|
- event: sunrise
|
|
offset: '120'
|
|
trigger: sun
|
|
conditions:
|
|
- condition: numeric_state
|
|
entity_id: predbat.rates
|
|
above: 0
|
|
- condition: template
|
|
value_template: '{{ states(''input_number.pv_clipped_in_next_24_hours'') | float
|
|
> 0 }}
|
|
|
|
'
|
|
actions:
|
|
- variables:
|
|
inverter_limit: 5
|
|
bat_capacity_max: 9.5
|
|
bat_discharge_max: 3.6
|
|
sunrise_to_clipping_hours: 4
|
|
slots: '{{ (sunrise_to_clipping_hours * 2) | int }}'
|
|
soc: '{{ states(''predbat.soc_kw_h0'') | float }}'
|
|
- repeat:
|
|
sequence:
|
|
- if:
|
|
- condition: template
|
|
value_template: '{{ states(''input_number.pv_clipped_in_next_24_hours'')
|
|
| float > 0 }}'
|
|
then:
|
|
- metadata: {}
|
|
data:
|
|
option: '{% set t = utcnow().strftime("%s") | int %} {# Round up
|
|
the time to nearest half-hour #} {# Set 30-min export for offset
|
|
seconds after sunrise #} {% set offset = (slots - repeat.index) *
|
|
1800 %} {{ (t - (t % 1800) + offset) | timestamp_custom("%H:%M:%S")
|
|
}}
|
|
|
|
'
|
|
target:
|
|
entity_id: select.predbat_manual_export
|
|
action: select.select_option
|
|
- action: input_number.set_value
|
|
metadata: {}
|
|
data:
|
|
value: "{# Assume straight line climb in solar export from 0 to inverter_limit\
|
|
\ over sunrise_to_clipping_hours. #} {% set solar_export = (slots\
|
|
\ - repeat.index) * (inverter_limit / slots) %} {% set available_export_capacity\
|
|
\ = inverter_limit - solar_export %} {# How much battery can be\
|
|
\ exported in 30-min period? #} {% set battery_export = min(bat_discharge_max,\
|
|
\ available_export_capacity) / 2 %} {# If the battery isn't full,\
|
|
\ we already have some spare capacity for clipping. #} {% set clipping\
|
|
\ = namespace(value=states('input_number.pv_clipped_in_next_24_hours')\
|
|
\ | float) %} {% if repeat.index == 1 %}\n {%- set clipping.value\
|
|
\ = clipping.value - (bat_capacity_max - soc) -%}\n{% endif %} {# We\
|
|
\ don't need to export more than the current battery SOC. #} {% set\
|
|
\ excess = min(soc, clipping.value) %} {{ max(excess - battery_export,\
|
|
\ 0) }}\n"
|
|
target:
|
|
entity_id: input_number.pv_clipped_in_next_24_hours
|
|
else:
|
|
- metadata: {}
|
|
data:
|
|
option: '{% set t = utcnow().strftime("%s") | int %} {# Round up
|
|
the time to nearest half-hour #} {# Set 30-min freeze charge for
|
|
offset seconds after sunrise #} {% set offset = (slots - repeat.index)
|
|
* 1800 %} {{ (t - (t % 1800) + offset) | timestamp_custom("%H:%M:%S")
|
|
}}
|
|
|
|
'
|
|
target:
|
|
entity_id: select.predbat_manual_freeze_export
|
|
action: select.select_option
|
|
- delay:
|
|
hours: 0
|
|
minutes: 0
|
|
seconds: 1
|
|
milliseconds: 0
|
|
count: '{{ slots }}'
|
|
mode: single
|
|
- alias: PV clipping - PV goes above inverter limit
|
|
description: Set predbat force manual export for this 30-min period. (Replace geserial
|
|
with your GE inverter serial number. Replace 5000 with your hybrid inverter limit
|
|
in Watts.)
|
|
triggers:
|
|
- entity_id:
|
|
- sensor.givtcp_geserial_pv_power
|
|
above: 5000
|
|
trigger: numeric_state
|
|
- minutes: '1'
|
|
trigger: time_pattern
|
|
- minutes: '31'
|
|
trigger: time_pattern
|
|
conditions:
|
|
- condition: sun
|
|
before: sunset
|
|
after: sunrise
|
|
- condition: numeric_state
|
|
entity_id: predbat.rates
|
|
above: 0
|
|
- condition: template
|
|
value_template: "{# Check this half-hour is not already set to force export #}\
|
|
\ \n{% set t = utcnow().strftime(\"%s\") | int %} \n{% set half_hour = (t -\
|
|
\ (t % 1800 )) | timestamp_custom(\"%H:%M:%S\") %}\n{{ half_hour not in states('select.predbat_manual_export')\
|
|
\ }}"
|
|
- condition: numeric_state
|
|
entity_id: sensor.givtcp_geserial_pv_power
|
|
above: 5000
|
|
- condition: numeric_state
|
|
entity_id: predbat.soc_kw_h0
|
|
above: 1
|
|
actions:
|
|
- metadata: {}
|
|
data:
|
|
option: '{# Round down the time to nearest half-hour #} {% set t = utcnow().strftime("%s")
|
|
| int %} {{ (t - (t % 1800 )) | timestamp_custom("%H:%M:%S") }}
|
|
|
|
'
|
|
target:
|
|
entity_id: select.predbat_manual_export
|
|
action: select.select_option
|
|
mode: single
|