Skip to content

Instantly share code, notes, and snippets.

@daniel-sc
Last active May 27, 2026 06:11
Show Gist options
  • Select an option

  • Save daniel-sc/074465be04dd9a793cba5b841ab63f83 to your computer and use it in GitHub Desktop.

Select an option

Save daniel-sc/074465be04dd9a793cba5b841ab63f83 to your computer and use it in GitHub Desktop.
Homeassistant blueprint to close/open covers depending on sun position and weather
# see https://community.home-assistant.io/t/close-open-curtain-cover-blinds-based-on-sun-and-weather/584240
blueprint:
name: cover sun
description: Close cover when sun is shining and open when stops. This considers weather (sunny, partly cloudy), sun position (elevation, azimuth) and temperature.
domain: automation
source_url: https://gist.github.com/daniel-sc/074465be04dd9a793cba5b841ab63f83
input:
cover_entity:
name: cover
selector:
entity:
domain: cover
sun_position_start:
name: Sun azimuth position start
description: Sun azimuth. When sun is over this value, shutter could closed.
default: 89
selector:
number:
min: 0
max: 360
unit_of_measurement: °
sun_position_end:
name: Sun azimuth position end
description: Sun azimuth. when sun is over this value, shutter should open.
default: 200
selector:
number:
min: 0
max: 360
unit_of_measurement: °
sun_elevation_start:
name: Sun elevation to start
description: Sun elevation. When sun is over this value, shutter could be closed.
default: 32
selector:
number:
min: -100
max: 360
unit_of_measurement: °
outdoor_temp:
name: Temperature threshold
description: Temperature above which the shutters will close. Based on Temperature source (see below).
default: 16
selector:
number:
min: 0
max: 30
unit_of_measurement: °C
close_position:
name: Closing position of curtain
description: "Target position for closed curtain. Note: 100 is open!"
default: 30
selector:
number:
min: 1
max: 99
no_open_after:
name: Night time begin
description: >
Any open/close action that would happen during the configured night time is ignored.
The automation does not re-evaluate automatically at night time end.
By default, night time handling is disabled because night time begin and end are the same value.
default: "00:00:00"
selector:
time:
no_open_before:
name: Night time end
description: >
Any open/close action that would happen during the configured night time is ignored.
The automation does not re-evaluate automatically at night time end.
By default, night time handling is disabled because night time begin and end are the same value.
default: "00:00:00"
selector:
time:
weather:
name: Weather service
default: "weather.home"
selector:
entity:
domain: weather
temperature_entity:
name: Temperature source
default: "weather.home"
selector:
entity:
filter:
- domain:
- weather
- device_class:
- temperature
close_weather_conditions:
name: Weather conditions
description: Close curtain on all of the selected weather conditions (if all other conditions are met)
default:
- sunny
- partlycloudy
selector:
select:
multiple: true
options:
- "clear-night"
- "cloudy"
- "fog"
- "hail"
- "lightning"
- "lightning-rainy"
- "partlycloudy"
- "pouring"
- "rainy"
- "snowy"
- "snowy-rainy"
- "sunny"
- "windy"
- "windy-variant"
- "exceptional"
variables:
sun_elevation_close: !input sun_elevation_start
sun_azimuth_close: !input sun_position_start
sun_azimuth_open: !input sun_position_end
temp_close: !input outdoor_temp
curtain_entity_id: !input cover_entity
curtain_target_position: !input close_position
no_open_after_time: !input no_open_after
no_open_before_time: !input no_open_before
is_night: "{{ no_open_after_time is not none and no_open_before_time is not none and (( today_at(no_open_after_time) < now() < today_at(no_open_before_time)) or (today_at(no_open_after_time) > today_at(no_open_before_time) and (now() > today_at(no_open_after_time) or now() < today_at(no_open_before_time)))) }}"
weather_entity_id: !input weather
temperature_entity_id: !input temperature_entity
weather_close: !input close_weather_conditions
curtain_current_position: "{{ state_attr(curtain_entity_id, 'current_position') if state_attr(curtain_entity_id, 'current_position') is not none else state_attr(curtain_entity_id, 'position') }}"
current_temp: >
{% set t = state_attr(temperature_entity_id, 'temperature') %}
{{ (t if t is not none else states(temperature_entity_id)) | float(none) }}
mode: queued # We rely on trigger changes, so 'single' and 'restart' are not possible. 'parallel' might work.
trigger:
- platform: numeric_state
entity_id: sun.sun
attribute: elevation
above: !input sun_elevation_start
id: t_sun_elevation_above
- platform: numeric_state
entity_id: sun.sun
attribute: elevation
below: !input sun_elevation_start
id: t_sun_elevation_below
- platform: numeric_state
entity_id: sun.sun
attribute: azimuth
above: !input sun_position_start
id: t_sun_azimuth_start_above
- platform: numeric_state
entity_id: sun.sun
attribute: azimuth
above: !input sun_position_end
id: t_sun_azimuth_end_above
- platform: state
entity_id: !input weather
to: !input close_weather_conditions
not_from: !input close_weather_conditions
id: t_weather_sunny
- platform: state
entity_id: !input weather
not_to: !input close_weather_conditions
from: !input close_weather_conditions
id: t_weather_not_sunny
# temperature_entity can either be:
# - a weather entity, where temperature is an attribute
# - a sensor entity, where temperature is the state
- platform: numeric_state
entity_id: !input temperature_entity
value_template: >
{{ state.attributes.get('temperature', state.state) | float(none) }}
above: !input outdoor_temp
id: t_temp_above
- platform: numeric_state
entity_id: !input temperature_entity
value_template: >
{{ state.attributes.get('temperature', state.state) | float(none) }}
below: !input outdoor_temp
id: t_temp_below
condition:
# During night time, triggers are ignored
- condition: template
value_template: "{{ not is_night }}"
- condition: or
conditions:
# do close when current cover position is above curtain_target_position
- condition: and
conditions:
- condition: template
value_template: "{{ curtain_current_position > curtain_target_position }}"
- condition: template
value_template: "{{ states(weather_entity_id) in weather_close }}"
- condition: template
value_template: "{{ state_attr('sun.sun', 'elevation') >= sun_elevation_close }}"
- condition: template
value_template: "{{ (sun_azimuth_close <= sun_azimuth_open and sun_azimuth_close <= state_attr('sun.sun', 'azimuth') <= sun_azimuth_open) or (sun_azimuth_close > sun_azimuth_open and (state_attr('sun.sun', 'azimuth') >= sun_azimuth_close or state_attr('sun.sun', 'azimuth') <= sun_azimuth_open)) }}"
- condition: template
value_template: "{{ current_temp is number and current_temp >= temp_close }}"
# do open (when it (first) leaves the closed conditions (prevent e.g. opening at night when any trigger fires)):
- condition: and
conditions:
- condition: template
value_template: "{{ curtain_current_position < curtain_target_position }}"
- condition: or
conditions:
- condition: and
conditions:
- condition: trigger
id: t_weather_not_sunny
- condition: template
value_template: "{{ state_attr('sun.sun', 'elevation') >= sun_elevation_close }}"
- condition: template
value_template: "{{ (sun_azimuth_close <= sun_azimuth_open and sun_azimuth_close <= state_attr('sun.sun', 'azimuth') <= sun_azimuth_open) or (sun_azimuth_close > sun_azimuth_open and (state_attr('sun.sun', 'azimuth') >= sun_azimuth_close or state_attr('sun.sun', 'azimuth') <= sun_azimuth_open)) }}"
- condition: template
value_template: "{{ current_temp is number and current_temp >= temp_close }}"
- condition: and
conditions:
- condition: template
value_template: "{{ states(weather_entity_id) in weather_close }}"
- condition: trigger
id: t_sun_elevation_below
- condition: template
value_template: "{{ (sun_azimuth_close <= sun_azimuth_open and sun_azimuth_close <= state_attr('sun.sun', 'azimuth') <= sun_azimuth_open) or (sun_azimuth_close > sun_azimuth_open and (state_attr('sun.sun', 'azimuth') >= sun_azimuth_close or state_attr('sun.sun', 'azimuth') <= sun_azimuth_open)) }}"
- condition: template
value_template: "{{ current_temp is number and current_temp >= temp_close }}"
- condition: and
conditions:
- condition: template
value_template: "{{ states(weather_entity_id) in weather_close }}"
- condition: template
value_template: "{{ state_attr('sun.sun', 'elevation') >= sun_elevation_close }}"
- condition: trigger
id: t_sun_azimuth_end_above
- condition: template
value_template: "{{ current_temp is number and current_temp >= temp_close }}"
- condition: and
conditions:
- condition: template
value_template: "{{ states(weather_entity_id) in weather_close }}"
- condition: template
value_template: "{{ state_attr('sun.sun', 'elevation') >= sun_elevation_close }}"
- condition: template
value_template: "{{ (sun_azimuth_close <= sun_azimuth_open and sun_azimuth_close <= state_attr('sun.sun', 'azimuth') <= sun_azimuth_open) or (sun_azimuth_close > sun_azimuth_open and (state_attr('sun.sun', 'azimuth') >= sun_azimuth_close or state_attr('sun.sun', 'azimuth') <= sun_azimuth_open)) }}"
- condition: trigger
id: t_temp_below
action:
- choose:
# close cover
- conditions:
- condition: template
value_template: "{{ curtain_current_position > curtain_target_position }}"
- condition: template
value_template: "{{ states(weather_entity_id) in weather_close }}"
- condition: template
value_template: "{{ state_attr('sun.sun', 'elevation') >= sun_elevation_close }}"
- condition: template
value_template: "{{ (sun_azimuth_close <= sun_azimuth_open and sun_azimuth_close <= state_attr('sun.sun', 'azimuth') <= sun_azimuth_open) or (sun_azimuth_close > sun_azimuth_open and (state_attr('sun.sun', 'azimuth') >= sun_azimuth_close or state_attr('sun.sun', 'azimuth') <= sun_azimuth_open)) }}"
- condition: template
value_template: "{{ current_temp is number and current_temp >= temp_close }}"
sequence:
- service: cover.set_cover_position
data:
position: "{{ curtain_target_position }}"
target:
entity_id: "{{ curtain_entity_id }}"
# open cover (conditions are somewhat redundant here, as 'condition' is more specific..)
- conditions:
- condition: template
value_template: "{{ curtain_current_position <= curtain_target_position }}"
- condition: or
conditions:
- condition: template
value_template: "{{ states(weather_entity_id) not in weather_close }}"
- condition: template
value_template: "{{ state_attr('sun.sun', 'elevation') < sun_elevation_close }}"
- condition: template
value_template: "{{ (sun_azimuth_close <= sun_azimuth_open and (sun_azimuth_close > state_attr('sun.sun', 'azimuth') or state_attr('sun.sun', 'azimuth') > sun_azimuth_open)) or (sun_azimuth_close > sun_azimuth_open and (state_attr('sun.sun', 'azimuth') < sun_azimuth_close and state_attr('sun.sun', 'azimuth') > sun_azimuth_open)) }}"
- condition: template
value_template: "{{ current_temp is number and current_temp < temp_close }}"
sequence:
- service: cover.set_cover_position
data:
position: 100
target:
entity_id: "{{ curtain_entity_id }}"
@riddik14
Copy link
Copy Markdown

Logger: homeassistant.components.homeassistant.triggers.numeric_state
Source: components/homeassistant/triggers/numeric_state.py:200
Integration: Home Assistant Core Integration (documentation, issues)
First occurred: 9 febbraio 2024 alle ore 23:03:01 (20 occurrences)
Last logged: 08:23:01

Error in 'cover sun auto' trigger: In 'numeric_state' condition: entity weather.forecast_ home state 'rainy' cannot be processed as a number
Error in 'cover sun auto' trigger: In 'numeric_state' condition: entity weather.forecast_home state 'cloudy' cannot be processed as a number

@daniel-sc
Copy link
Copy Markdown
Author

@daniel-sc
Copy link
Copy Markdown
Author

@riddik14 this was a warning because the blueprint did not cleanly handle variance between weather/temperature entity - this should be fixed with the latest version.

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