Automatic blinds / sunscreen control based on sun platform

I Like this Project, more flexible and looks very good.
After testing something goes wrong on my part und i do not understand the reason. My Sensor looks like:

- sensor:
    name: Solar blinds height percentage Osten
    unique_id: blinds_height_perc_east
    unit_of_measurement: '%'
    state: >
       {% set deg2rad = pi/180 %}

        {%- 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 %}

        {%- macro clipv(x, x_min, x_max) %}
        	{{ max(min(x, x_max), x_min) }}
        {%- endmacro %}

        {% set win_azi = 108.25 %}
        {% set d = 0.5 %}
        {% set h_max = 2.15 %}
        {% set h_min = 0 %}
        
        {# field of view you want to enable control #}
        {# !!! MUST be equal or lower than 90 degrees !!! #}
        {# > 90 degrees is behind the window and then calculation is invalid! #}
        {% set fov = deg2rad * 60 %}
        
        {# get sun elevation / azimuth from sun.sun #}
        {% set sun_azi = state_attr('sun.sun', 'azimuth') %}
        {% set sun_ele = state_attr('sun.sun', 'elevation') %}
        
        {% set def_h = 0.6 * h_max %}
        
        {% set alpha = deg2rad * sun_ele %}
        {% set gamma = deg2rad * (win_azi - sun_azi) %}    

        {% set h = (d / cos(gamma)) * tan(alpha) %}

        {% if (alpha > 0) and (gamma | abs < fov) %}
            {{ clipv(h2perc(h) | round(0) | int , 50, 100) }}
        {% else %}  
            {{ clipv(h2perc(def_h) | round(0) | int , 50, 100) }}
        {% endif %}

The calculation itself works but in time where the blinds have to be set to 100% it jumps to 60%
image

Hopefully someone have an idear whats going wrong

Thanks in Advanced

Did you set the azimuth angle correctly? It jumps to 60% (the standard value def_h = 0.6 * h_max) when either the sun moves behind the window or the sun moves below the horizon. Since it happened


I am Living in the building 55 next to the border of the compass

So far as i know its right. What do i have to do to set the default to 100% (full open) after the sun moves around the corner

Yes if your window faces south east then that’s fine. To change the default value you can change this line:

{% set def_h = 0.6 * h_max %}

To:

{% set def_h = h_max %}

This will set the default height to 100% so the window will be fully open.

PS. I think it will be easier for you if you import the blueprint. The import button is at the top of this post

1 Like

i am still using your blueprint. it is perfect. Many thanks for your work. But i need this sensor for working or i am wrong?

edit: I have put a new blueprint at the top of the page which does not need a template sensor anymore!

You can also find it here: :sun_with_face: Automatic blinds / sunscreen control based on sun platform

1 Like

i tested it, but i always get a error

and my blueprint code

alias: Sonnenschutz - Wohnzimmer Automatisch
description: ""
use_blueprint:
  path: basbruss/cover_height_sun.yaml
  input:
    cover_entity:
      device_id: 55d960f40a449d4e7586a344e92fe3cd
    azimuth: 314
    max_height: 1.3
    default_height: 100
    change_threshold: 3

can you perhaps help @langestefan

You will need to use the entity_id instead of the device_id. Entity_id also adds the attributes to the dataset :wink:

1 Like

you are right, i think now it works, gives a way to disable the automation ?

for example when i want to control the cover manuall ?

You can add a manual override input_boolean as the blueprint supports both extra actions and conditions in the configurable action part

2 Likes

You can also just use the automation.turn_off service: Automation Services - Home Assistant

2 Likes

okay, then i missunderstand the Conditions in the blueprint, when i add a Condition , for example

condition: state entity_id: binary_sensor.someone_home state: "off"

the blueprint will not execute when the sensor state is “off” correct?

Hello there!

First, I want to say that this is really amazing work. Well done. Good job!

With the blueprint everything was working out of the box. Almost flawless and well, here I need a bit of asssistance.

On the east side of my house I have a big hill and for today every window on that side could have gone into the “normal state” (= cover 100% open) 2 hours earlier because there was no direct sunlight of this direction.
I read about the FOV angle, but it didn’t help (reduced it from 90° to 45°). So I’d need to finish the day at azimuth 284° instead of 305°.

Any chance to solve that problem?

Thanks,
naturnorbert

Hi.

Do you mean you have hill on the west side of your house? Otherwise I don’t understand the azimuth angle of 284°.

image

I think the FOV angle could be a bit more flexible. I think it would be nice to have the ability to set a tracking ‘window’ by defining right/ left azimuth and top/bottom elevation. Kind of like this:

Would have solve your problem?

Hi!

Yes.

I guess how I draw it it’s not like the FOV works. Currently I subtract on both sides of the window (like a cone), right?
So maybe it works somehow to just decide left/right/both from the view point inside house → outside window?

Does that make sense?

Greetings

Yes it is indeed a cone. The normal FOV would be 180°. You can also see it as half a circle from the point of view of the window.

I have simulated your drawing, now with independent upper and lower limits (azi_min and azi_max for the azimuth range). You can see nicely in the plot that it starts tracking when the sun moves from behind the window to in front of it at 13:00, and stops tracking at 20:00 when the azimuth exceeds 284°.

Would this work for you?

(do3esn’t exactly match your situation, but I hope you get the point? ps. I chose a random location in EU west)

1 Like

Yes, I guess that would be OK.

Wouldn’t it make sense to just give the Blueprint a condition where the user can set “between x and y do cover hight z%” + the option to recheck it every x minutes?
x, y should then maybe be the results out of your calculation to make it fit for summer and winter?

But just to be clear about the FOV:
image

So in my case if I rotate it just clockwise around the corner, I’d need to fill in “alpha” = 0° and “beta” = 90°. Maybe to script it that way is easier?

Edit:
If you want to I could prepare more data around my house. I control almost all of my windows (except childs sleeping room) with your blueprint.
Or can you maybe share the way you simulate it and I can recheck with my house if it fits?
Would that help?

Wouldn’t it make sense to just give the Blueprint a condition where the user can set “between x and y do cover hight z%” + the option to recheck it every x minutes?
x, y should then maybe be the results out of your calculation to make it fit for summer and winter?

That could work. However my experience with such rule based systems is that they solve the problem for a specific location and time of the year. What may work well in the summer may not work in the winter because the sun will be at a different elevation for the same azimuth.

So in my case if I rotate it just clockwise around the corner, I’d need to fill in “alpha” = 0° and “beta” = 90°. Maybe to script it that way is easier?

Your drawing is correct. There are basically two ways to define the tracking FOV. Reference North (0°) or the window (win_azi). I actually already do the second one to come up with valid angles in the first place:

# all angles above the horizon and within the half circle of the window facade
valid = np.logical_and((np.abs(gamma) < np.pi / 2), (alpha > 0))

# filter out sun azimuth outside azi_min and azi_max    
valid = np.logical_and(valid, ((sol_azi > azi_min) & (sol_azi < azi_max)))

So the final tracking range is thus the intersection of the half circle (as in your drawing) with the upper and lower limits defined by the user.

Here is the python code if you want to try the simulation yourself:

from pvlib import solarposition
import pandas as pd
import numpy as np
np.set_printoptions(suppress=True)
import matplotlib.pyplot as plt

tz = 'CET'
lat, lon = 52.35843822282988, 4.881058028007153

# summer
start_date = '2022-06-05'
end_date = '2022-06-06'

# winter
# start_date = '2022-12-05'
# end_date = '2022-12-06'

times = pd.date_range(start=start_date, end=end_date, freq='5min', tz=tz)
solpos = solarposition.get_solarposition(times, lat, lon)

# plot of elevation and azimuth
plt.figure(figsize=(15, 5))
solpos['elevation'].plot(label='elevation')
solpos['azimuth'].plot(label='azimuth')
plt.ylabel('Angle [°]')
plt.xlabel('Time [HH:MM]')
plt.legend()
plt.show()

from numpy import cos, tan, radians

def calculate_blind_height(sol_elev: float, sol_azi: float, win_azi, azi_min, azi_max, d: float) -> np.ndarray:
    """
    Calculate the height of the blind based on the sun position and the panel tilt and azimuth.

    :param sol_elev: elevation of the sun in degrees
    :param sol_azi: azimuth of the sun in degrees
    :param win_azi: azimuth of the panel in degrees from north
    :param win_tilt: tilt of the panel in degrees
    :param d: distance between the working area and window facade in meters
    """

    # surface solar azimuth
    gamma = radians(sol_azi - win_azi)

    # solar elevation
    alpha = radians(sol_elev)

    # all angles above the horizon and within the half circle of the window facade
    valid = np.logical_and((np.abs(gamma) < np.pi / 2), (alpha > 0))

    # filter out sun azimuth outside azi_min and azi_max    
    valid = np.logical_and(valid, ((sol_azi > azi_min) & (sol_azi < azi_max)))

    # calculate blind height
    return np.where(valid, (d / cos(gamma)) * tan(alpha), 0)

# variables
win_azi = 310
win_tilt = 0
h_max = 1.96
dis_workplane = 1
time = solpos.index

# azi min and max
azi_min = 90
azi_max = 284

# plot of blind height
ax, fig = plt.subplots(figsize=(12, 5))
plt.title(f"Window azimuth: {win_azi}°, azi_min: {azi_min}°, azi_max: {azi_max}°")
plt.plot(solpos['elevation'].values, label='elevation')
plt.plot(solpos['azimuth'].values, label='azimuth')

# two vlines, one at azi_min and one at azi_max
pos = np.where(np.logical_and(solpos['azimuth'].values > azi_min, solpos['azimuth'].values < azi_max))[0]
plt.vlines(pos[0], -10, 360, color='black', linestyle='--', linewidth=2, label='azi_min/azi_max')
plt.vlines(pos[-1], -10, 360, color='black', linestyle='--', linewidth=2 )
plt.xticks(np.arange(0, len(time), 12), time[::12].strftime('%H:%M'), rotation=90)
plt.ylabel('Angle [°]')
plt.xlabel('Time [HH:MM]')
plt.legend()

# plot of blind height
ax2 = fig.twinx()
blind_height = np.clip(calculate_blind_height(solpos['elevation'], solpos['azimuth'], win_azi, azi_min=azi_min, azi_max=azi_max, d=dis_workplane), 0, h_max)
ax2.plot(blind_height, label='blind height', color='red')

plt.ylabel('Blind height [m]')
plt.legend()
plt.show()
1 Like

Where do I find this line? My code is way shorter ? I dont want my covers to close to 0% fully to let some light in, a minimum of 15% woild be great.

My Automation looks like this:

alias: Auto Cover Height BĂźro
description: ""
use_blueprint:
  path: basbruss/cover_height_sun.yaml
  input:
    azimuth: 284
    distance: 0.1
    default_height: 100
    condition_on: true
    action_condition:
      - condition: state
        entity_id: input_boolean.automatikmodus_verschattung_buro
        state: "on"
    cover_entity:
      entity_id: cover.hmip_broll_2_00369f29974cf9_buero

Oh sorry that was before we had this nice blueprint. I need to add an option to set min percentage to the blueprint. Ps, the blueprint got updated yesterday so you don’t need an external sensor anymore to calculate the blind height.

1 Like