Last active
January 16, 2024 10:51
-
-
Save PimDoos/71017197efc2b2892fc0b831efb53f0f to your computer and use it in GitHub Desktop.
Sessy X on the Meter blueprint
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mode: single | |
max_exceeded: silent | |
blueprint: | |
name: Sessy X on the Meter | |
description: Control a Sessy Battery according to a grid sensor | |
author: PimDoos | |
domain: automation | |
input: | |
grid_entity: | |
name: Grid | |
description: Entity measuring grid net consumption | |
selector: | |
entity: | |
domain: sensor | |
device_class: power | |
battery_device: | |
name: Battery | |
description: Sessy Battery devices to control | |
selector: | |
device: | |
integration: sessy | |
multiple: true | |
setpoint_smoothing_value: | |
name: Smoothing value | |
description: Maximum delta between two setpoints. This should not be lower than the Minimum Power of the battery. | |
default: 100 | |
selector: | |
number: | |
min: 50 | |
max: 2000 | |
setpoint_optimal_value: | |
name: Max optimal power | |
description: Amount of power required before loadbalancing to more batteries | |
default: 1200 | |
selector: | |
number: | |
min: 50 | |
max: 2200 | |
setpoint_target_value: | |
name: Target value | |
description: Target value (X) for the Grid entity | |
default: 0 | |
selector: | |
number: | |
min: -3600 | |
max: 3600 | |
mode: box | |
offset_entity: | |
name: Offset entities | |
description: Entities whose value (in Watts) should be subtracted from the grid sensor. E.g. Car charger power sensor | |
default: [] | |
selector: | |
entity: | |
multiple: true | |
domain: | |
- sensor | |
- number | |
- input_number | |
update_interval: | |
name: Update interval | |
description: Time to wait before accepting new data fron the grid sensor | |
selector: | |
duration: | |
update_timeout: | |
name: Update timeout | |
description: Maximum time to wait for new data from the grid sensor | |
selector: | |
duration: | |
battery_soc_min: | |
name: Minimum State of Charge | |
description: Batteries below this level will not participate in the load balancing pool | |
default: 0 | |
selector: | |
number: | |
min: 0 | |
max: 99 | |
battery_soc_max: | |
name: Maximum State of Charge | |
description: Batteries above this level will not participate in the load balancing pool | |
default: 100 | |
selector: | |
number: | |
min: 1 | |
max: 100 | |
priority_offset_template: | |
name: Priority offset template | |
description: Template defining a value to rotate battery priority by. Values higher than the number of batteries will loop back to 0. | |
default: "{{ now().day }}" | |
selector: | |
template: | |
variables: | |
device_ids: !input battery_device | |
optimal_max_power: !input setpoint_optimal_value | |
grid_entity: !input grid_entity | |
smoothing: !input setpoint_smoothing_value | |
offset: !input setpoint_target_value | |
offset_entity: !input offset_entity | |
min_battery_level: !input battery_soc_min | |
max_battery_level: !input battery_soc_max | |
trigger: | |
- platform: time_pattern | |
minutes: "/1" | |
- platform: homeassistant | |
event: start | |
action: | |
- variables: | |
device_ids: | | |
{% if device_ids is string %}{% set device_ids = [ device_ids ] %}{% endif %} | |
{{ device_ids }} | |
battery_count: | | |
{{ device_ids | count }} | |
# Get Entity IDs from device IDs | |
battery_strategy_entity: | | |
{% set entities = namespace(strategy=[]) %} | |
{% for device_id in device_ids %} | |
{% set entities.strategy = entities.strategy + [device_entities(device_id) | list | select("search","_power_strategy$") | list | join("")] %} | |
{% endfor %} | |
{{ entities.strategy }} | |
battery_setpoint_entity: | | |
{% set entities = namespace(setpoint=[]) %} | |
{% for device_id in device_ids %} | |
{% set entities.setpoint = entities.setpoint + [device_entities(device_id) | list | select("search","_power_setpoint$") | list | join("")] %} | |
{% endfor %} | |
{{ entities.setpoint }} | |
battery_soc_entity: | | |
{% set entities = namespace(soc=[]) %} | |
{% for device_id in device_ids %} | |
{% set entities.soc = entities.soc + [device_entities(device_id) | list | select("search","_state_of_charge$") | list | join("")] %} | |
{% endfor %} | |
{{ entities.soc }} | |
battery_state_entity: | | |
{% set entities = namespace(state=[]) %} | |
{% for device_id in device_ids %} | |
{% set entities.state = entities.state + [device_entities(device_id) | list | select("search","_system_state$") | list | join("")] %} | |
{% endfor %} | |
{{ entities.state }} | |
- repeat: | |
while: [] | |
sequence: | |
- variables: | |
# Get grid power and convert to Watts | |
grid: > | |
{% set multiplier = 0 %} | |
{% set grid_unit = state_attr(grid_entity, "unit_of_measurement") %} | |
{% if grid_unit == "W" %} | |
{% set multiplier = 1 %} | |
{% elif grid_unit == "kW" %} | |
{% set multiplier = 1000 %} | |
{% endif %} | |
{% set grid_power = states(grid_entity) | float %} | |
{{ grid_power * multiplier }} | |
# Calculate initial setpoint | |
battery: > | |
{{ battery_setpoint_entity | map("states") | list | map('int') | sum }} | |
# Calculate offset | |
offset: > | |
{% if offset_entity is string %}{% set offset_entity = [offset_entity] %}{% endif %} | |
{% set offset = offset + offset_entity | map("states") | map("int") | sum %} | |
{{ offset }} | |
# Calculate new setpoint | |
setpoint: > | |
{% set load = battery + grid %} | |
{% set setpoint_target = load - offset %} | |
{% set setpoint_smooth = [[setpoint_target, battery - smoothing] | max, battery + smoothing] | min %} | |
{{ setpoint_smooth }} | |
priority_offset_template: !input priority_offset_template | |
priority_offset: | | |
{{ priority_offset_template % battery_count }} | |
battery_setpoint_entity: | | |
{{ battery_setpoint_entity[priority_offset:] + battery_setpoint_entity[:priority_offset] }} | |
battery_strategy_entity: | | |
{{ battery_strategy_entity[priority_offset:] + battery_strategy_entity[:priority_offset] }} | |
battery_soc_entity: | | |
{{ battery_soc_entity[priority_offset:] + battery_soc_entity[:priority_offset] }} | |
battery_state_entity: | | |
{{ battery_state_entity[priority_offset:] + battery_state_entity[:priority_offset] }} | |
# Distribute setpoint across batteries | |
battery_setpoints: | | |
{% set num_optimal = ((setpoint | abs) / optimal_max_power) | round(0,'ceil') %} | |
{% set batteries = namespace(entities=[], available=[], standby=[]) %} | |
{% for i in range(0,battery_count) %} | |
{% if batteries.available | count < num_optimal | |
and states(battery_strategy_entity[i]) == "api" | |
and (states(battery_soc_entity[i]) | int >= min_battery_level or setpoint < 0) | |
and (states(battery_state_entity[i]) != "battery_empty" or setpoint < 0) | |
and (states(battery_soc_entity[i]) | int <= max_battery_level or setpoint > 0) | |
and (states(battery_state_entity[i]) != "battery_full" or setpoint > 0) %} | |
{% set batteries.available = batteries.available + [i] %} | |
{% else %} | |
{% set batteries.standby = batteries.standby + [i] %} | |
{% endif %} | |
{% endfor %} | |
{% for i in batteries.available %} | |
{% set value = (setpoint / (batteries.available | count)) | int %} | |
{% set value = [[value,-2200] | max, 2200] | min %} | |
{% set batteries.entities = batteries.entities + [{"setpoint":battery_setpoint_entity[i], "strategy": battery_strategy_entity[i], "value":value}] %} | |
{% endfor %} | |
{% for i in batteries.standby %} | |
{% set batteries.entities = batteries.entities + [{"setpoint":battery_setpoint_entity[i], "strategy": battery_strategy_entity[i], "value":0}] %} | |
{% endfor %} | |
{{ batteries.entities }} | |
- repeat: | |
for_each: | | |
{{ battery_setpoints }} | |
sequence: | |
- if: | |
- condition: template | |
value_template: | | |
{{ states(repeat.item.strategy) == "api" }} | |
then: | |
- service: number.set_value | |
continue_on_error: true | |
target: | |
entity_id: | | |
{{ repeat.item.setpoint }} | |
data: | |
value: | | |
{{ repeat.item.value | int }} | |
- delay: !input update_interval | |
- wait_for_trigger: | |
- platform: state | |
entity_id: !input grid_entity | |
timeout: !input update_timeout |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment