-
-
Save lukasvice/b364724d84c3ac4e160f7a7d8fa37066 to your computer and use it in GitHub Desktop.
# 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 |
Can you please help me understand how exactly should I use this? Do you have any tutorial? I am also not familiar much about scripts.
I have several external venetian blinds using Shelly 2PM plus and I need to make tilt control in HA.
If I understand correctly, I have to create "script" in HA for each one, copy in it the content after "-script" and rename entity_id to my id of cover.shelly....
Then create automation with content under "-automation"
What next?
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
@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.
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!
@xbmcgotham yes it will work, checking wiring diagram for shelly 2PM
Does work with Shelly 2PM Plus
@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?
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
@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 🤗
@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.
danke, hatte es tatsächlich falsch^^ jetzt funktionierts (Y)
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?
@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.
Hi Lukas,
thanks for your work. Somehow the line
value_template: "{% if state_attr(entity_id, 'current_position') != _original_position %}true{% endif %}"
didn't work in my setup. The related trigger wasn't activated and run in a timeout. I changed it tovalue_template: {% if is_state(entity_id, 'open') or is_state(entity_id, 'closed') %}true{% endif %}
which also indicates that the blinds are not moving anymore.This approach works for me. Just for information - if you or anyone else has some similiar problems.
I had the exact same issue and your hint solved it. Thanks!
Just for curiosity: Does anyone have an idea, why this is happening? I am running HA in Docker on a Synology NAS.
BR Tim
Hi @lukasvice!
This is a nice script.
But what does the following addition you posted do?
If I understand correctly, you save the last position and the tilt position, but for what? :-)
Why I ask: It would be nice to control the position and tilt position separately.
For example, if I move the blind to position 50 and tilt it to 50 and then move it to position 70 without tilt, it would be nice to remember the last tilt and tilt it back to 50.
Is this possible?
I'm pretty sure you've seen my blog post about this script - if not, it's linked here :)
You can split up your scripts, sounds reasonable. With the new Shelly Plus 2 PM firmware version 1.0.0 you can get the
last_direction
directly from the device. But even with this information, you can't be sure if it's fully tilted or if it's only moved for 0.2 seconds, so maybe it's half tilted. Maybe I'm missing something, but I haven't found a way how to use this information. Anyone with good ideas is welcome! :)To store the last position and tilt position, I use a trigger-based template sensor that stores the data of each cover as JSON. This script can be triggered by two events:
* Custom (manual): This event is fired by the end of the `cover_position_tilt` script and the `entity_id`, `position`, and `tilt_position` are passed as arguments * A cover changes to `opening` or `closing`: This resets the stored position in the data to `unknown`
At the beginning of the
cover_position_tilt
script, I read the stored position from the JSON and compare it to the desired position. If it's the same, the script stops, otherwise it continues as normal.I don't think this is the prettiest approach, but I couldn't think of a better one - and it works :) As I said, I'm open to any ideas and improvements.
Here's the template sensor:
template: - trigger: - id: "cover_position_tilt" platform: event event_type: "cover_position_tilt" - platform: state entity_id: - cover.shelly_2_5_COVER_ID - cover.shelly_2_5_COVER_ID - cover.shelly_2_5_COVER_ID to: - "opening" - "closing" sensor: - name: "cover_states" state: > {% set current = ('{}' if states('sensor.cover_states') == 'unknown' else states('sensor.cover_states')) | from_json %} {% if trigger.id == "cover_position_tilt" -%} {% set new = { trigger.event.data.entity_id: { 'position': trigger.event.data.position, 'tilt_position': trigger.event.data.tilt_position } } %} {%- else -%} {% set new = { trigger.entity_id: 'unknown' } %} {%- endif %} {{ dict(current, **new) | to_json }}This is how to trigger it manually at the end of the
open
andclose
movements in the script:- event: cover_position_tilt event_data: entity_id: "{{ entity_id }}" position: "{{ position }}" tilt_position: "{{ tilt_position }}"And this is the comparison at the beginning of the script:
- alias: "Set variables" variables: _last_position: > {% set cover_states = ('{}' if states('sensor.cover_states') == 'unknown' else states('sensor.cover_states')) | from_json %} {{ cover_states[entity_id].position if entity_id in cover_states and 'position' in cover_states[entity_id] else 'unknown' }} _last_tilt_position: > {% set cover_states = ('{}' if states('sensor.cover_states') == 'unknown' else states('sensor.cover_states')) | from_json %} {{ cover_states[entity_id].tilt_position if entity_id in cover_states and 'tilt_position' in cover_states[entity_id] else 'unknown' }} - condition: template value_template: > {{ _last_position == 'unknown' or _last_tilt_position == 'unknown' or position != _last_position or tilt_position != _last_tilt_position }}
Hi @maxschloegl, I save the last position/tilt so that the blinds do not move when I request the same position/tilt again. This is useful if you run automations that want to move the blinds even if they are already in that position. I see what you're trying to do, and yes, you could use the saved tilt position if no tilt is provided as a parameter, so it wouldn't change. Personally, I have not come across this use case yet.
Hi everyone, Shelly has announced the new 2PM Gen3 with drum roll blind angle control! Let's hope they got it right and this is the feature we've been waiting for!
Hi everyone, Shelly has announced the new 2PM Gen3 with drum roll blind angle control! Let's hope they got it right and this is the feature we've been waiting for!
It actually is. I raised a ticket with them a few weeks ago where they told me that i should stay tuned for this feature to come. The 3rd Gen Pro devices already have that feature. Now HomeAssistant integration needs an update I guess and than things should work.
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?
@lukasvice
Here are currently my try:
Like you, you have to play around with the time a bit.
Maybe I need 2 times.
I would like to do the following if the position moves just 1%.
2a. If the current tilt position is then over 100% I would set it back to 0.
But here my technical knowledge is too weak how and where to find out that the position moved only 1%.
I'm aware that you can't get a 100% accurate solution with this, but I don't have the use case to change the tilt 4 or 5 times either ;).
Most of the time you do it once, maybe a second time to adjust. That works quite well. It would be important to show the manual status.
With this information, I can remove some settings but I didn't find this information into HA