Automatic blinds / sunscreen control based on sun platform

Do you perhaps have the distance variable set to 0? We just found that in someone else’s config where it also didn’t move. A distance of 0 effectively means you always want 100% shadow, so the cover will always remain fully closed. In the next version we will set the lower limit of this value to something like 0.01 meter to prevent this from happening.

2 Likes

THAT was the problem, did a duplication and missed the value! Thanks!

1 Like

First of all: thanks! I was searching for a solution like that. But my blinds work vice versa; the open state is 100% and closed state 0%. How can I configure this? Of there an option planned to switch the behavior to the opposite?

Hi, currently this is not possible. But the behaviour can be changed with a simple fix:

In the blueprint code, change:

{%- macro h2perc(x) %}
  {{ 100 * float(norm(x, h_min, h_max)) }}
{%- endmacro %}

To:

{%- macro h2perc(x) %}
  {{ 100 * (1 - float(norm(x, h_min, h_max))) }}
{%- endmacro %}

I think we can add this option to the blueprint as well.

ps. the blueprint file can be found under:
/config/blueprints/automation/langestefan/auto_sun_blind.yaml

That’s normal behavior for covers in HA. The percentage is the x amount the cover is open.
So this blueprint will just work without any alterations

1 Like

Yes good point, I did not properly read @tester25 's reaction. In case you do have the percentages swapped and it is switched around for you anyway you can use the change I shared above.

Just installed the blueprint. My blind is opening completely, when the sun comes around the corner instead of closing.
What am I doing wrong?

Wrong azimuth perhaps?

It is the Northwest window. Azimuth 313 should be ok?

If it’s for the house that’s not visible because it’s under the compass then it’s okay.

Can you share the automation trace?

Restarted HA. Blind was closed. Now it opened up to 60%

Triggered by the state of sun.sun at 6. Juli 2023 um 17:06:05

Choose: Option 1 executed

(cover.wohnzimmer_rolladen_fenster) turned opening

Finished at 6. Juli 2023 um 17:06:05 (runtime: 0.18 seconds)
variables:
  cover_entity:
    entity_id: cover.wohnzimmer_rolladen_fenster
  azimuth: 313
  distance: 0.5
  max_height: 1.2
  min_height: 0
  default_height: 100
  min_position: 0
  degrees: 90
  default_template: ''
  azimuth_left: 90
  azimuth_right: 90
  max_elevation: 90
  min_elevation: 0
  cover_height: >
    {%- set deg2rad = pi/180 -%} {# normalize in range [0,1] #} {%- macro
    norm(x, min, max) %}
      {{ (x - min) / (max - min) }}
    {%- endmacro %} {# convert blind height h to percentage [0,100] #} {%- macro
    h2perc(x) %}
      {{ 100 * float(norm(x, h_min, h_max)) }}
    {%- endmacro %} {# clip value between [min, max] #} {%- macro clipv(x,
    x_min, x_max) %}
      {{ max(min(x, x_max), x_min) }}
    {%- endmacro %} {# constants #} {%- set win_azi = azimuth -%} {%- set
    left_azi = azimuth_left | default(90) -%} {%- set right_azi = azimuth_right
    | default(90) -%} {%- set elev_high = deg2rad * (max_elevation |
    default(90)) -%} {# Maximum: 90 #} {%- set elev_low = deg2rad *
    (min_elevation| default(0)) -%} {# Minimum: 0 #} {%- set d = distance |
    default(0.5) -%} {%- set h_max = max_height | default(2.10) -%} {%- set
    h_min = min_height | default(0) -%} {%- set deg = degrees | default(90) -%}
    {%- set def = default_height | default(60) -%} {%- set min_pos =
    min_position | default(0) -%} {%- set def_temp = default_template |
    default('') -%} {% if def_temp | int(-1) >= 0 %}
      {% set def = def_temp %}
    {%endif%}

    {# FOV #} {%- if left_azi != right_azi-%}
      {%- set azi_left = deg2rad * -left_azi -%} {# Minimum: -90 #}
      {%- set azi_right = deg2rad * right_azi -%} {# Maximum: 90 #}
    {%-else-%}
      {%- set azi_left = deg2rad * -deg -%} {# Minimum: -90 #}
      {%- set azi_right = deg2rad * deg -%} {# Maximum: 90 #}
    {%-endif-%} {%- set fov = deg2rad * deg -%} {# get sun elevation / azimuth
    from sun.sun #} {%- set sun_azi = state_attr('sun.sun', 'azimuth') -%} {%-
    set sun_ele = state_attr('sun.sun', 'elevation') -%} {# default height, when
    automatic control is off. #} {%- set def_h = def / 100 * h_max -%} {%- set
    alpha = deg2rad * sun_ele -%} {%- set gamma = deg2rad * (win_azi - sun_azi)
    -%}     {%- set h = (d / cos(gamma)) * tan(alpha) -%} {# gamma is outside of
    FOV #} {%- if gamma < azi_left or gamma > azi_right or alpha < elev_low or
    alpha > elev_high -%}
      {{ clipv(h2perc(def_h) | round(0) | int , 0, 100) }}
    {# gamma is inside of FOV #} {%- else -%}  
      {{ clipv(h2perc(h) | round(0) | int , min_pos, 100) }}
    {%- endif -%}
  change_threshold: 0
  time_out: 1
  condition_mode: and
trigger:
  - platform: state
    entity_id:
      - sun.sun
action:
  - choose:
      - conditions:
          - condition: template
            value_template: '{{condition_mode == ''and''}}'
        sequence:
          - condition: and
            conditions:
              - condition: template
                value_template: >
                  {{ ((state_attr(cover_entity.entity_id,'current_position') |
                  float - cover_height | float) | abs > change_threshold) or
                  (cover_height in [default_height, default_template])}}
              - condition: template
                value_template: >
                  {{now() - timedelta(minutes=time_out) >=
                  states[cover_entity.entity_id].last_updated }}
          - choose: []
            default: []
          - service: cover.set_cover_position
            data:
              position: '{{ cover_height | int(0) }}'
            target:
              entity_id: cover.wohnzimmer_rolladen_fenster
      - conditions:
          - condition: template
            value_template: '{{condition_mode == ''or''}}'
        sequence:
          - condition: or
            conditions:
              - condition: template
                value_template: >
                  {{ ((state_attr(cover_entity.entity_id,'current_position') |
                  float - cover_height | float) | abs > change_threshold) or
                  (cover_height in [default_height, default_template])}}
              - condition: template
                value_template: >
                  {{now() - timedelta(minutes=time_out) >=
                  states[cover_entity.entity_id].last_updated }}
          - choose: []
            default: []
          - service: cover.set_cover_position
            data:
              position: '{{ cover_height | int(0) }}'
            target:
              entity_id: cover.wohnzimmer_rolladen_fenster
mode: single
id: '1688652692458'
alias: Cover Height 313 Grad
description: ''

Everything seems correct. Note that by having a distance of 0.5m direct sunlight is allowed to fall into the room for 0.5 meters. If the sun is just around the corner this could mean that the blind is still fully open, because the 0.5m threshold is not violated.

2 Likes

Just FYI, this is more or less what the blind should do with default 100% open at d=0.5 meter.

1 Like

Now, short before 1200 o’clock, my SW-window blind does nothing:

Trace timeline:

this:
  entity_id: automation.cover_height_222_grad
  state: 'on'
  attributes:
    id: '1688652779293'
    last_triggered: '2023-07-07T09:51:08.003871+00:00'
    mode: single
    current: 0
    friendly_name: Cover Height 223 Grad
  last_changed: '2023-07-07T06:07:10.451689+00:00'
  last_updated: '2023-07-07T09:51:08.013614+00:00'
  context:
    id: 01H4QW6BQ3TJ9N644QJ9R2YFMG
    parent_id: 01H4QW6BQ2BVX2JSEJB4S70B8T
    user_id: null
trigger:
  id: '0'
  idx: '0'
  alias: null
  platform: state
  entity_id: sun.sun
  from_state:
    entity_id: sun.sun
    state: above_horizon
    attributes:
      next_dawn: '2023-07-08T02:44:47.576543+00:00'
      next_dusk: '2023-07-07T20:17:12.182172+00:00'
      next_midnight: '2023-07-07T23:30:58+00:00'
      next_noon: '2023-07-07T11:30:43+00:00'
      next_rising: '2023-07-08T03:27:59.864140+00:00'
      next_setting: '2023-07-07T19:34:00.522694+00:00'
      elevation: 57.04
      azimuth: 134.38
      rising: true
      friendly_name: Sun
    last_changed: '2023-07-07T06:07:07.943914+00:00'
    last_updated: '2023-07-07T09:51:08.002184+00:00'
    context:
      id: 01H4QW6BQ2BVX2JSEJB4S70B8T
      parent_id: null
      user_id: null
  to_state:
    entity_id: sun.sun
    state: above_horizon
    attributes:
      next_dawn: '2023-07-08T02:44:47.576543+00:00'
      next_dusk: '2023-07-07T20:17:12.182172+00:00'
      next_midnight: '2023-07-07T23:30:58+00:00'
      next_noon: '2023-07-07T11:30:43+00:00'
      next_rising: '2023-07-08T03:27:59.864140+00:00'
      next_setting: '2023-07-07T19:34:00.522694+00:00'
      elevation: 57.5
      azimuth: 135.86
      rising: true
      friendly_name: Sun
    last_changed: '2023-07-07T06:07:07.943914+00:00'
    last_updated: '2023-07-07T09:55:08.003121+00:00'
    context:
      id: 01H4QWDP33SDWR1AMFJH2EBVCV
      parent_id: null
      user_id: null
  for: null
  attribute: null
  description: state of sun.sun
cover_entity:
  entity_id:
    - cover.arbeitszimmer_rolladen_rechts
    - cover.kuche_rolladen_rechts
azimuth: 223
distance: 0
max_height: 1.2
min_height: 0
default_height: 100
min_position: 0
degrees: 90
default_template: ''
azimuth_left: 90
azimuth_right: 90
max_elevation: 90
min_elevation: 0
cover_height: 0
change_threshold: 0
time_out: 1
condition_mode: and

Config:

variables:
  cover_entity:
    entity_id:
      - cover.arbeitszimmer_rolladen_rechts
      - cover.kuche_rolladen_rechts
  azimuth: 223
  distance: 0
  max_height: 1.2
  min_height: 0
  default_height: 100
  min_position: 0
  degrees: 90
  default_template: ''
  azimuth_left: 90
  azimuth_right: 90
  max_elevation: 90
  min_elevation: 0
  cover_height: >
    {%- set deg2rad = pi/180 -%} {# normalize in range [0,1] #} {%- macro
    norm(x, min, max) %}
      {{ (x - min) / (max - min) }}
    {%- endmacro %} {# convert blind height h to percentage [0,100] #} {%- macro
    h2perc(x) %}
      {{ 100 * float(norm(x, h_min, h_max)) }}
    {%- endmacro %} {# clip value between [min, max] #} {%- macro clipv(x,
    x_min, x_max) %}
      {{ max(min(x, x_max), x_min) }}
    {%- endmacro %} {# constants #} {%- set win_azi = azimuth -%} {%- set
    left_azi = azimuth_left | default(90) -%} {%- set right_azi = azimuth_right
    | default(90) -%} {%- set elev_high = deg2rad * (max_elevation |
    default(90)) -%} {# Maximum: 90 #} {%- set elev_low = deg2rad *
    (min_elevation| default(0)) -%} {# Minimum: 0 #} {%- set d = distance |
    default(0.5) -%} {%- set h_max = max_height | default(2.10) -%} {%- set
    h_min = min_height | default(0) -%} {%- set deg = degrees | default(90) -%}
    {%- set def = default_height | default(60) -%} {%- set min_pos =
    min_position | default(0) -%} {%- set def_temp = default_template |
    default('') -%} {% if def_temp | int(-1) >= 0 %}
      {% set def = def_temp %}
    {%endif%}

    {# FOV #} {%- if left_azi != right_azi-%}
      {%- set azi_left = deg2rad * -left_azi -%} {# Minimum: -90 #}
      {%- set azi_right = deg2rad * right_azi -%} {# Maximum: 90 #}
    {%-else-%}
      {%- set azi_left = deg2rad * -deg -%} {# Minimum: -90 #}
      {%- set azi_right = deg2rad * deg -%} {# Maximum: 90 #}
    {%-endif-%} {%- set fov = deg2rad * deg -%} {# get sun elevation / azimuth
    from sun.sun #} {%- set sun_azi = state_attr('sun.sun', 'azimuth') -%} {%-
    set sun_ele = state_attr('sun.sun', 'elevation') -%} {# default height, when
    automatic control is off. #} {%- set def_h = def / 100 * h_max -%} {%- set
    alpha = deg2rad * sun_ele -%} {%- set gamma = deg2rad * (win_azi - sun_azi)
    -%}     {%- set h = (d / cos(gamma)) * tan(alpha) -%} {# gamma is outside of
    FOV #} {%- if gamma < azi_left or gamma > azi_right or alpha < elev_low or
    alpha > elev_high -%}
      {{ clipv(h2perc(def_h) | round(0) | int , 0, 100) }}
    {# gamma is inside of FOV #} {%- else -%}  
      {{ clipv(h2perc(h) | round(0) | int , min_pos, 100) }}
    {%- endif -%}
  change_threshold: 0
  time_out: 1
  condition_mode: and
trigger:
  - platform: state
    entity_id:
      - sun.sun
action:
  - choose:
      - conditions:
          - condition: template
            value_template: '{{condition_mode == ''and''}}'
        sequence:
          - condition: and
            conditions:
              - condition: template
                value_template: >
                  {{ ((state_attr(cover_entity.entity_id,'current_position') |
                  float - cover_height | float) | abs > change_threshold) or
                  (cover_height in [default_height, default_template])}}
              - condition: template
                value_template: >
                  {{now() - timedelta(minutes=time_out) >=
                  states[cover_entity.entity_id].last_updated }}
          - choose: []
            default:
              - condition: sun
                before: sunset
                after: sunrise
              - condition: time
                after: '07:30:00'
          - service: cover.set_cover_position
            data:
              position: '{{ cover_height | int(0) }}'
            target:
              entity_id:
                - cover.arbeitszimmer_rolladen_rechts
                - cover.kuche_rolladen_rechts
      - conditions:
          - condition: template
            value_template: '{{condition_mode == ''or''}}'
        sequence:
          - condition: or
            conditions:
              - condition: template
                value_template: >
                  {{ ((state_attr(cover_entity.entity_id,'current_position') |
                  float - cover_height | float) | abs > change_threshold) or
                  (cover_height in [default_height, default_template])}}
              - condition: template
                value_template: >
                  {{now() - timedelta(minutes=time_out) >=
                  states[cover_entity.entity_id].last_updated }}
          - choose: []
            default:
              - condition: sun
                before: sunset
                after: sunrise
              - condition: time
                after: '07:30:00'
          - service: cover.set_cover_position
            data:
              position: '{{ cover_height | int(0) }}'
            target:
              entity_id:
                - cover.arbeitszimmer_rolladen_rechts
                - cover.kuche_rolladen_rechts
mode: single
id: '1688652779293'
alias: Cover Height 223 Grad
description: ''

Blueprint Config:

id: '1688652779293'
alias: Cover Height 223 Grad
description: ''
use_blueprint:
  path: basbruss/cover_height_sun.yaml
  input:
    default_height: 100
    max_height: 1.2
    azimuth: 223
    cover_entity:
      entity_id:
        - cover.arbeitszimmer_rolladen_rechts
        - cover.kuche_rolladen_rechts
    distance: 0
    action_condition:
      - condition: sun
        before: sunset
        after: sunrise
      - condition: time
        after: '07:30:00'

You have the distance set to 0, that’s effectively saying you always want everything shaded. Thus it will never open. If you want a lot of shade, try something like distance = 0.1

Changed it to 0,1m (Blinds are still open)

this:
  entity_id: automation.cover_height_222_grad
  state: 'on'
  attributes:
    id: '1688652779293'
    last_triggered: '2023-07-07T10:09:00.763698+00:00'
    mode: single
    current: 0
    friendly_name: Cover Height 223 Grad
  last_changed: '2023-07-07T10:10:37.537566+00:00'
  last_updated: '2023-07-07T10:10:37.537566+00:00'
  context:
    id: 01H4QXA1V1FCS836DRMGVW5Q7X
    parent_id: null
    user_id: null
trigger:
  id: '0'
  idx: '0'
  alias: null
  platform: state
  entity_id: sun.sun
  from_state:
    entity_id: sun.sun
    state: above_horizon
    attributes:
      next_dawn: '2023-07-08T02:44:47.576543+00:00'
      next_dusk: '2023-07-07T20:17:12.182172+00:00'
      next_midnight: '2023-07-07T23:30:58+00:00'
      next_noon: '2023-07-07T11:30:43+00:00'
      next_rising: '2023-07-08T03:27:59.864140+00:00'
      next_setting: '2023-07-07T19:34:00.522694+00:00'
      elevation: 59.16
      azimuth: 141.86
      rising: true
      friendly_name: Sun
    last_changed: '2023-07-07T10:10:34.156711+00:00'
    last_updated: '2023-07-07T10:10:34.156711+00:00'
    context:
      id: 01H4QX9YHCCEKHS40Y6F32ARNP
      parent_id: null
      user_id: null
  to_state:
    entity_id: sun.sun
    state: above_horizon
    attributes:
      next_dawn: '2023-07-08T02:44:47.576543+00:00'
      next_dusk: '2023-07-07T20:17:12.182172+00:00'
      next_midnight: '2023-07-07T23:30:58+00:00'
      next_noon: '2023-07-07T11:30:43+00:00'
      next_rising: '2023-07-08T03:27:59.864140+00:00'
      next_setting: '2023-07-07T19:34:00.522694+00:00'
      elevation: 59.56
      azimuth: 143.5
      rising: true
      friendly_name: Sun
    last_changed: '2023-07-07T10:10:34.156711+00:00'
    last_updated: '2023-07-07T10:14:34.157950+00:00'
    context:
      id: 01H4QXH8XD6FBRBNPQD2Y9DT8G
      parent_id: null
      user_id: null
  for: null
  attribute: null
  description: state of sun.sun
cover_entity:
  entity_id:
    - cover.arbeitszimmer_rolladen_rechts
    - cover.kuche_rolladen_rechts
azimuth: 223
distance: 0.1
max_height: 1.2
min_height: 0
default_height: 100
min_position: 0
degrees: 90
default_template: ''
azimuth_left: 90
azimuth_right: 90
max_elevation: 90
min_elevation: 0
cover_height: 78
change_threshold: 0
time_out: 1
condition_mode: and

Looks good :+1:

From the programmer’s perspective :wink:
Blinds do nothing. Fully open 100%

It should change to 78% according your logs. Any condition that renders false? Maybe the time-out was not expired between the last change of your blinds?

PS: I also note that your are using an old version of the blueprint. The newest one can be found here: GitHub - langestefan/auto-sun-blind: Automatically control your sun blinds via home assistant based on the position of the sun.

Thx!
Sun is around the corner of my 310 Azimuth window. It was on 60%. Now it is fully open (was done by the script, when sun was on 220)
Nothing changed on the 130 Azimuth window. it is on 20% as set manually today morning. Shouldn’t it go back to 60%?

Update: 310 Azimuth window is back on 60%
Update 2: 310 seems to be working. 130+220 let‘s see tomorrow