Skip to content

Instantly share code, notes, and snippets.

@lukasvice
Last active June 27, 2024 11:54
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
@FlyingDodo86
Copy link

FlyingDodo86 commented Oct 12, 2023

I freshly started with HA and Shellys but after hours of figuring out issues I made it work. Here a summary of my conclusions:

In order to get the position of the covers I had to remove the shellys from HA and Shelly App. Then re-integrate them via the Shelly app and the run the Calibration on the Shelly app (Settings->Calibration). Once completed, re-integrated the Shellys in HA. Based on above replies maybe one additional remark:
The cover_position_tilt.yaml does not need to be modified simply e.g. create a new folder "Packages" via Studio Code Server. Then create a new file in the packages folder with name cover_position_tilt.yaml and copy paste the script content. Next is to add the following in the configuration.yaml

homeassistant:
  packages: !include_dir_named packages/

Once done go in Developer Tools and click on Check Configuration. If everything is good restart HA. Testing of the script: In Developer Tools click on Services and paste the below and click on Call Service

service: script.cover_position_tilt
data:
  entity_id: cover.replace_with_your_shelly_entity_ID
  position: 10
  tilt_position: 40

@lukasvice
Copy link
Author

lukasvice commented Oct 12, 2023

@FlyingDodo86 Thanks for sharing.

Note that in the meantime, I have updated the script to exclude the "package" line to make it easier for new users to use it. I still use it as a package though.

@xbmcgotham
Copy link

Hi @lukasvice hope your doing great!, I see you have made great progress. I am just wondering, as I am not a developer, can you confirm to me if this script will do what I am expecting for my setup? :-)

I have the blind boxes installed and all wired up, but have not yet the blinds, so I can still adapt to the best situation.

Like shown on the drawing attached, this is the setup I like and have wired up for. A Shelly (Pro series I have now) unit that is controlled by a manual switch on the wall and the HA. I know that in the past the Shelly HA script needs tweaking to make the tilt work, and I am not sure how this currently works with the wall switches in parallel. I am happy to buy additional WAREMA control system if that would allow for easy integration. I do try to stay away from Wifi or RF and like all wired as some blinds are to far away to reach that way.

Hope you can get back to me.

Appreciated, thanks!

Screenshot 2023-10-24 at 10 12 20

@Kepro
Copy link

Kepro commented Dec 22, 2023

@xbmcgotham yes it will work, checking wiring diagram for shelly 2PM

@xbmcgotham
Copy link

xbmcgotham commented Dec 22, 2023 via email

@MatejMonika
Copy link

Does work with Shelly 2PM Plus

@u20p17
Copy link

u20p17 commented Apr 9, 2024

@FlyingDodo86 Thanks for sharing.

Note that in the meantime, I have updated the script to exclude the "package" line to make it easier for new users to use it. I still use it as a package though.

Hello @lukasvice, I created a package folder and inside this folder I copied the latest cover_position_tilt.yaml. When I add

homeassistant:
  packages: !include_dir_named packages/

to the configuration.yaml and then check the configuration I get the following warning:


 Konfigurationswarnungen
 Setup of package 'script' failed: Integration 'cover_position_tilt' not found.
 Setup of package 'automation' failed: Invalid package definition 'automation': expected a dictionary. Package will not be initialized

What's my issue?

@lukasvice
Copy link
Author

Hi @u20p17, your approach seems fine to me. It looks like script and automation are somehow being interpreted as package names. Are you sure you're not using !include_dir_merge_named (That would work a bit differently)?

You can also look at some examples here and try to compare your configurations: https://www.home-assistant.io/examples/#example-configurationyaml

@u20p17
Copy link

u20p17 commented Apr 9, 2024

@lukasvice, i indeed did use the !include_dir_merge_named… if i delete this line in the configuration.yaml and restart HA i do not see any error/warning, but i can also not see any new script/automation…
something i am doing wrong 🤗

@lukasvice
Copy link
Author

lukasvice commented Apr 9, 2024

@u20p17, there's a difference between !include_dir_named and !include_dir_merge_named. The merge one requires the package name at the beginning of the file. Try using the one without merge, as you wrote in your original comment. See also the documentation on this: https://www.home-assistant.io/docs/configuration/packages/#create-a-packages-folder.

@u20p17
Copy link

u20p17 commented Apr 10, 2024

danke, hatte es tatsächlich falsch^^ jetzt funktionierts (Y)

@u20p17
Copy link

u20p17 commented Apr 17, 2024

today i had some time to play with this script, but in my case it is not working as expected. the problem I think is that my venetian blinds do need different times for a full tilt upwards (1400ms) and downwards (1000ms). Do you have the same issue and just took the middle value? if you send tilt position 50 should the blinds stop at around 45deg?

@lukasvice
Copy link
Author

@u20p17 Hmm, my blinds always take the same amount of time regardless of the direction. Maybe you could modify the script so that you have two variables, tilt_time_opening_ms and tilt_time_closing_ms. You can then use these variables in the "opening" or "closing" condition of the script. This might work.

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