Skip to content

Instantly share code, notes, and snippets.

@lukasvice
Last active January 19, 2025 15:53
Show Gist options
  • Save lukasvice/b364724d84c3ac4e160f7a7d8fa37066 to your computer and use it in GitHub Desktop.
Save lukasvice/b364724d84c3ac4e160f7a7d8fa37066 to your computer and use it in GitHub Desktop.
Home Assistant script to control venetian blinds with Shelly
# Have a look at the blog post about this script:
# https://medium.com/@lukasvice/a-utility-script-for-controlling-venetian-blinds-with-shelly-in-home-assistant-2e5cbf2d8d5f
script:
cover_position_tilt:
mode: parallel
fields:
entity_id:
description: "The cover entity"
example: "cover.X"
position:
description: "Position of the cover"
example: 100
tilt_position:
description: "Tilt position (optional)"
example: 100
sequence:
- alias: "Set variables"
variables:
# Time in ms for a full tilt
tilt_time_ms: 1800
# Time between blinds move commands
cmd_wait_time_ms: 500
_original_position: "{{ state_attr(entity_id, 'current_position') }}"
- alias: "Open/Close tilt depending on current position"
choose:
# When closing
- conditions: "{{ state_attr(entity_id, 'current_position') > position|int }}"
sequence:
# Move to the desired position
- service: cover.set_cover_position
data_template:
entity_id: "{{ entity_id }}"
position: "{{ position|int }}"
# Blinds have to be tilted, if tilt_position is set and tilt_position is not fully closed
- alias: "Check if blinds should be tilted"
condition: template
value_template: "{{ tilt_position is defined and tilt_position != none and tilt_position|int > 0 }}"
# Wait for the blinds to stop (Shelly updates current_position on start/stop)
# Cancel the script if the position was not received after 100 seconds
- wait_for_trigger:
- platform: template
value_template: "{% if state_attr(entity_id, 'current_position') != _original_position %}true{% endif %}"
timeout: 100
continue_on_timeout: false
# If it's not the desired position, the blinds were stopped manually (in this case cancel the script)
- alias: "Check if blinds have reached desired position"
condition: template
value_template: "{% if is_state_attr(entity_id, 'current_position', position|int) %}true{% endif %}"
- delay:
milliseconds: "{{ cmd_wait_time_ms }}"
# Move the blinds the required time for tilting in the original direction
- service: cover.close_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ tilt_time_ms / 100 * tilt_position|int }}"
- service: cover.stop_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ cmd_wait_time_ms }}"
# Move the blinds the required time for tilting in the opposite direction
- service: cover.open_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ tilt_time_ms / 100 * tilt_position|int }}"
- service: cover.stop_cover
data_template:
entity_id: "{{ entity_id }}"
# When opening
- conditions: "{{ state_attr(entity_id, 'current_position') < position|int }}"
sequence:
# Move to the desired position
- service: cover.set_cover_position
data_template:
entity_id: "{{ entity_id }}"
position: "{{ position|int }}"
# Blinds have to be tilted, if tilt_position is set and tilt_position is not fully open
- alias: "Check if blinds should be tilted"
condition: template
value_template: "{{ tilt_position is defined and tilt_position != none and tilt_position|int < 100 }}"
# Wait for the blinds to stop (Shelly updates current_position on start/stop)
# Cancel the script if the position was not received after 100 seconds
- wait_for_trigger:
- platform: template
value_template: "{% if state_attr(entity_id, 'current_position') != _original_position %}true{% endif %}"
timeout: 100
continue_on_timeout: false
# If it's not the desired position, the blinds were stopped manually (in this case cancel the script)
- alias: "Check if blinds have reached desired position"
condition: template
value_template: "{% if is_state_attr(entity_id, 'current_position', position|int) %}true{% endif %}"
- delay:
milliseconds: "{{ cmd_wait_time_ms }}"
# Move the blinds the required time for tilting in the original direction
- service: cover.open_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ tilt_time_ms / 100 * (100 - tilt_position|int) }}"
- service: cover.stop_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ cmd_wait_time_ms }}"
# Move the blinds the required time for tilting in the opposite direction
- service: cover.close_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ tilt_time_ms / 100 * (100 - tilt_position|int) }}"
- service: cover.stop_cover
data_template:
entity_id: "{{ entity_id }}"
# Special case: the blinds are already in the desired position
default:
- alias: "Continue only if blinds are not fully opened or closed"
condition: template
value_template: "{{ state_attr(entity_id, 'current_position') > 0 and state_attr(entity_id, 'current_position') < 100 }}"
- choose:
# When the blinds are almost closed, move up for the tilt time
- conditions: "{{ state_attr(entity_id, 'current_position') < 10 }}"
sequence:
- service: cover.open_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ tilt_time_ms }}"
- service: cover.stop_cover
data_template:
entity_id: "{{ entity_id }}"
# When the blinds are open, move down for the tilt time
default:
- service: cover.close_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ tilt_time_ms }}"
- service: cover.stop_cover
data_template:
entity_id: "{{ entity_id }}"
- delay:
milliseconds: "{{ cmd_wait_time_ms }}"
# Trigger event to restart the script with the original parameters
- event: start_cover_position_tilt
event_data:
entity_id: "{{ entity_id }}"
position: "{{ position }}"
tilt_position: "{{ tilt_position }}"
automation:
# Automation triggered by a custom event to restart the script
- id: start_cover_position_tilt
alias: "Start Cover Position Tilt"
mode: parallel
trigger:
- platform: event
event_type: "start_cover_position_tilt"
action:
- service: script.cover_position_tilt
data_template:
entity_id: "{{ trigger.event.data.entity_id }}"
position: "{{ trigger.event.data.position }}"
tilt_position: "{{ trigger.event.data.tilt_position }}"
service: script.cover_position_tilt
data:
entity_id: cover.shelly_XXX
position: 70
tilt_position: 50
@okos111
Copy link

okos111 commented Nov 24, 2024

Hi. Nice script. I'm trying to make an action where at a specified time 4 blinds (4 x Shelly 2PM) are set to a certain position and no way I can get it to work. Only maybe 2 of the 4 always respond. Or some don't set the tilt position. It doesn't even work when I enter actions in succession or in parallel. If I make an action on just one blinds, it works.
Any idea?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment