Some help needed in combining multiple window cover automations based on azimuth into one single automation

That looks correct for one direction (from 80% to 20% as the sun goes from 220 to 270)

but for the other direction (from 20% to 80% as the sun goes from 270 to 320) I think it will be:

y = 1.2x - 304

thanks @finity ! that makes sense for the other way. I used this website to calculate the equation of the line here:

By the way, when try to put this code below in my yaml template, its complaining about:

“Message malformed: template value should be a string for dictionary value @ data[‘action’][0][‘data’]”

but my online yaml editor and checker is not complaining. Where did I miss a space or something?

    data:
      position: >
        {% if state_attr('sun.sun', 'azimuth') | float = 220 %}
          80  
        {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and
        state_attr('sun.sun', 'azimuth') | float < 270 %}
          {{ (((-1.2 * (state_attr('sun.sun', 'azimuth') | float )  + 344) / 10) | int) * 10 }}
        {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and
        state_attr('sun.sun', 'azimuth') | float = 270 %}
          20
        {% elif state_attr('sun.sun', 'azimuth') | float > 270 and
        state_attr('sun.sun', 'azimuth') | float <= 320 %}
          {{ ((( 1.2 * (state_attr('sun.sun', 'azimuth') | float )  - 304) / 10) | int) * 10 }}
        {% if state_attr('sun.sun', 'azimuth') | float = 320 %}
          80  
        {% endif %}

you aren’t using the correct comparator. Should be “==” instead of “=”

        {% if state_attr('sun.sun', 'azimuth') | float == 220 %}
          80  
        {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and
        state_attr('sun.sun', 'azimuth') | float < 270 %}
          {{ (((-1.2 * (state_attr('sun.sun', 'azimuth') | float )  + 344) / 10) | int) * 10 }}
        {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and
        state_attr('sun.sun', 'azimuth') | float == 270 %}
          20
        {% elif state_attr('sun.sun', 'azimuth') | float > 270 and
        state_attr('sun.sun', 'azimuth') | float <= 320 %}
          {{ ((( 1.2 * (state_attr('sun.sun', 'azimuth') | float )  - 304) / 10) | int) * 10 }}
        {% if state_attr('sun.sun', 'azimuth') | float == 320 %}
          80  
        {% endif %}

But I’m still not sure the logic is correct even after those changes.

try this instead:

        {% if state_attr('sun.sun', 'azimuth') | float < 220 %}
          80  
        {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and
        state_attr('sun.sun', 'azimuth') | float < 270 %}
          {{ (((-1.2 * (state_attr('sun.sun', 'azimuth') | float )  + 344) / 10) | int) * 10 }}
        {% elif state_attr('sun.sun', 'azimuth') | float >= 270 and
        state_attr('sun.sun', 'azimuth') | float <= 320 %}
          {{ ((( 1.2 * (state_attr('sun.sun', 'azimuth') | float )  - 304) / 10) | int) * 10 }}
        {% elif state_attr('sun.sun', 'azimuth') | float > 320 %}
          80  
        {% endif %}

ahh ok, that works.

with regards to

…float > 320 %}
vs
…float == 320 %}

…float < 220 %}
vs
…float == 220 %}

does the < mean that an azimuth under 220 and correspondingly, anything more than > 320?

I only want this automation to control my blinds when the azimuth is between 220 and 320 inclusive. Anything else, it would be manual control.

So, should I keep it as == instead of > and <?

If

Ok, @finity this codes works pretty well! Here’s my final code, now

alias: Family Room Shade Control with Sun
description: ''
trigger:
  - platform: state
    entity_id: sun.sun
condition:
  - condition: numeric_state
    entity_id: sensor.solar_radiation
    above: '100'
  - condition: state
    entity_id: group.somebody_home
    state: home
  - condition: numeric_state
    entity_id: sun.sun
    attribute: elevation
    above: '10'
  - condition: template
    value_template: '{{ 3 <= now().month <= 9 }}'
action:
  - service: cover.set_cover_position
    target:
      entity_id:
        - cover.family_room_family_room_shade
    data:
      position: >
        {% if state_attr('sun.sun', 'azimuth') | float < 220 %}
          80  
        {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and
        state_attr('sun.sun', 'azimuth') | float < 270 %}
          {{ (((-1.2 * (state_attr('sun.sun', 'azimuth') | float )  + 344) / 10) | int) * 10 }}
        {% elif state_attr('sun.sun', 'azimuth') | float >= 270 and
        state_attr('sun.sun', 'azimuth') | float <= 320 %}
          {{ ((( 1.2 * (state_attr('sun.sun', 'azimuth') | float )  - 304) / 10) | int) * 10 }}
        {% elif state_attr('sun.sun', 'azimuth') | float > 320 %}
          80  
        {% endif %}
mode: single

I was wondering, as you can see in this part of the code for the conditions:

  - condition: numeric_state
    entity_id: sensor.solar_radiation
    above: '100'

This is a lux (radiation sensor) from my weather station.

How simple would it be to add an action that states if its cloudy (lux value less than 100 w/m2) for more than 5 minutes during the sun azimuth range of the automation, to open the shade to 80%

Wondering if I can place this in the current automation, somehow, rather than make a new automation for this?

you can use the “choose:” option.

I think this should work:

alias: Family Room Shade Control with Sun
description: ''
trigger:
  - platform: state
    entity_id: sun.sun
condition:
 - condition: state
    entity_id: group.somebody_home
    state: home
  - condition: numeric_state
    entity_id: sun.sun
    attribute: elevation
    above: '10'
  - condition: template
    value_template: '{{ 3 <= now().month <= 9 }}'
action:
  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: sensor.solar_radiation
            below: '100'
        sequence:
          - service: cover.set_cover_position
            target:
              entity_id:
                - cover.family_room_family_room_shade
            data:
              position: 80
    default:
      - service: cover.set_cover_position
        target:
          entity_id:
            - cover.family_room_family_room_shade
        data:
          position: >
            {% if state_attr('sun.sun', 'azimuth') | float < 220 %}
              80  
            {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and state_attr('sun.sun', 'azimuth') | float < 270 %}
              {{ (((-1.2 * (state_attr('sun.sun', 'azimuth') | float )  + 344) / 10) | int) * 10 }}
            {% elif state_attr('sun.sun', 'azimuth') | float >= 270 and state_attr('sun.sun', 'azimuth') | float <= 320 %}
              {{ ((( 1.2 * (state_attr('sun.sun', 'azimuth') | float )  - 304) / 10) | int) * 10 }}
            {% elif state_attr('sun.sun', 'azimuth') | float > 320 %}
              80  
            {% endif %}
mode: single

thanks very much for your help!

I tried to add the condition that it has to be cloudy for more than 5 minutes (otherwise my shade would go up and down with a passing cloud. or every minute when the solar sensors updates):

          - condition: numeric_state
            entity_id: sensor.solar_radiation
            below: '100'
            for:
              hours: 0
              minutes: 5
              seconds: 0

it doesn’t seem to take the above, unless the “numeric_state” is changed to “state”

What does that mean?

are you seeing errors somewhere?

where are you putting it in the code?

yes, an error appears:

Message malformed: extra keys not allowed @ data[‘action’][0][‘choose’][0][‘conditions’][0][‘for’]

when I change this"

  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: sensor.solar_radiation
            below: '100'
        sequence:
          - service: cover.set_cover_position
            target:
              entity_id:
                - cover.family_room_family_room_shade
            data:
              position: 80

to this

  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: sensor.solar_radiation
            below: '100'
            for:
              hours: 0
              minutes: 5
              seconds: 0
        sequence:
          - service: cover.set_cover_position
            target:
              entity_id:
                - cover.family_room_family_room_shade
            data:
              position: 80

Unlike the Numeric State Trigger, the Numeric State Condition doesn’t support the for option.

thanks, @123 @finity

You’re right, I can get the condition in the Trigger conditions.

Perhaps I have to make to make trigger conditions using a trigger ID where if the value is above 100 proceed with the actions to adjust shade by by azimuth (“sun shine” trigger ID name), but if its below 100 for 5 minutes, open shade to 80% (using another trigger ID, “no sun”, for example"

Here’s my attempt this this, I just made a trigger ID for when the there is no sun for more than 5 minutes here. There might be a better way to do this, so im open for suggestions.

alias: 'Family Room Shade Control with Sun (Duplicate) '
description: ''
trigger:
  - platform: state
    entity_id: sun.sun
  - platform: numeric_state
    entity_id: sensor.solar_radiation
    for:
      hours: 0
      minutes: 5
      seconds: 0
    below: '100'
    id: no sun
condition:
  - condition: state
    entity_id: group.somebody_home
    state: home
  - condition: numeric_state
    entity_id: sun.sun
    attribute: elevation
    above: '7'
  - condition: template
    value_template: '{{ 3 <= now().month <= 9 }}'
action:
  - choose:
      - conditions:
          - condition: trigger
            id: no sun
        sequence:
          - service: cover.set_cover_position
            target:
              entity_id:
                - cover.family_room_family_room_shade
            data:
              position: 80
    default:
      - service: cover.set_cover_position
        target:
          entity_id:
            - cover.family_room_family_room_shade
        data:
          position: >
            {% if state_attr('sun.sun', 'azimuth') | float < 220 %}
              80  
            {% elif state_attr('sun.sun', 'azimuth') | float >= 220 and
            state_attr('sun.sun', 'azimuth') | float < 270 %}
              {{ (((-1.2 * (state_attr('sun.sun', 'azimuth') | float )  + 344) / 10) | int) * 10 }}
            {% elif state_attr('sun.sun', 'azimuth') | float >= 270 and
            state_attr('sun.sun', 'azimuth') | float <= 320 %}
              {{ ((( 1.2 * (state_attr('sun.sun', 'azimuth') | float )  - 304) / 10) | int) * 10 }}
            {% elif state_attr('sun.sun', 'azimuth') | float > 320 %}
              80  
            {% endif %}
mode: single

1 Like

Looks OK to me.

But the most important question is does it do what you want? If so then I don’t see a need to change it.

@finity you’re right. Time will tell! I will keep posted.

Thanks all for the help and for sharing all your steps for this, couldn’t have done it without the community effort here.

1 Like

@finity adding this to the code, didn’t seem to work, in fact it ignored the condition for stop controlling the cover when the lux went below 100, i.e. the shade continued to move with the azimuth even though clouds rolled in and the lux was below 100.

however, the original code works perfectly fine:

  - conditions:
          - condition: numeric_state
            entity_id: sensor.solar_radiation
            below: '100'
        sequence:
          - service: cover.set_cover_position
            target:
              entity_id:
                - cover.family_room_family_room_shade
            data:
              position: 80

it just seems that adding a delay causes the issues. I might just leave it as is (the original code of just open the shade to 80% when the lux goes under 100), because my weather station reports every 60 seconds. I can imagine a scenario, with clouds rolling in and broken, worst case scenario is the shade will bouce up and down every minute between current azimuth position to 80% and back. Not ideal, but with having a delay of 3 to 5 minutes, it would do it every 3 to 5 minute (worse case), instead.

you might be confusing triggers and conditions.

triggers have zero impact on conditions. they only dtermine when the automation is told to run.

then the conditions are evaluated at that point.

In the first choose function you tell the actions to run only if the trigger was from the numeric state trigger. So the cover should go to 80%.

but if in the next second the sun.sun state changes then the trigger id condition is not satisfied since it was only true a second ago and the trigger is no longer the numeric state trigger but instead it’s now the sun.sun state trigger. So the first choose condition is no longer satisfied and then the default action is taken to run the template actions.

1 Like

Greetings, all…
Although this topic is closed, I have a shade control solution that might be helpful to some. I’ve got several motorized shades in windows that face multiple directions, and I figured out how to provide precise control for each of them based entirely on the position of the sun.

Here’s the approach I took:

  1. Create two sensors that provide the sun’s position relative to the window.
    Using azimuth, elevation, and trigonometry, create sensors that provide the angle of the sun relative to each window in the vertical direction (perpendicular to the window) and in the horizontal direction (also perpendicular to the window). Each window will have two sensors, but windows that face the same direction (either side-by-side or one above the other) can use the same pair of sensors.

  2. Use the two sun position sensors to create a binary sensor for the window that indicates whether the sun is shining directly in.
    As an example, when the sun is above the treetops and below the roofline in the vertical direction, and it’s between where it would shine directly in from the left and from the right in the horizontal direction, the sensor state would be on. You’ll probably need to fine-tune the limits of each angle used in your binary sensor over several months as the sun’s path changes. In my case, windows facing the same direction cannot use the same binary sensor (but they can and should use the same vertical and horizontal sensors) because the direct sunlight is blocked (or not) for each window differently.

  3. Use the binary sensor in an automation that opens or closes the shade depending on the state of the sensor.
    This requires only one automation per window. When I started trying to solve this puzzle, I had multiple automations per window. At first, they were based only on azimuth and elevation. Then, I came up with the trigonometry idea (but not yet the binary sensor idea). In both cases, I tried using one angle as a trigger and the other as a condition, then would sometimes reverse which angle was the trigger and which was the condition in a different automation that would take effect at a different time of year. That was sooo hard to understand and to fine tune. I eventually came up with the binary sensor idea and it has worked perfectly by comparison.

I might not have been clear enough in explaining this, so please ask if I you have questions or if you would like to see any specific code I’ve used. If you think the trigonometry might be a challenge for you (it sure was for me), you might be able to use some of what I’ve already done. My house is located at about 28° N latitude (Florida, USA), and I’ve implemented this on windows that face east, southeast, south, and southwest.

Hope this helps. I’ve gotten so much help from the HA community over the years and, and this is the first time I thought I might have something helpful to contribute myself.

Happy coding!

1 Like

@sysCrusher that sounds interesting. Could you share your code?

Also interested here.