Automatic blinds / sunscreen control based on sun platform

Here’s the trace of the shade that never closes. What do I have set incorrectly?

{
  "trace": {
    "last_step": "action/0/choose/0/sequence/0/conditions/0",
    "run_id": "4a6f9843e85507935f31da1e4c3ad8a8",
    "state": "stopped",
    "script_execution": "finished",
    "timestamp": {
      "start": "2023-10-20T14:26:43.175734+00:00",
      "finish": "2023-10-20T14:26:43.201279+00:00"
    },
    "domain": "automation",
    "item_id": "1695845347770",
    "trigger": "state of sun.sun",
    "trace": {
      "trigger/0": [
        {
          "path": "trigger/0",
          "timestamp": "2023-10-20T14:26:43.176651+00:00",
          "changed_variables": {
            "this": {
              "entity_id": "automation.office_shade_front_open_with_blueprint",
              "state": "on",
              "attributes": {
                "id": "1695845347770",
                "last_triggered": "2023-10-20T14:22:43.175463+00:00",
                "mode": "single",
                "current": 0,
                "friendly_name": "Office Front Shade Close with Blueprint"
              },
              "last_changed": "2023-10-14T18:53:05.499259+00:00",
              "last_updated": "2023-10-20T14:22:43.187127+00:00",
              "context": {
                "id": "01HD6QF3Z6TE3ZV9SCSD84BHH4",
                "parent_id": "01HD6QF3Z2PJPGAG35Z4VNC9CJ",
                "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-10-21T11:55:10.694198+00:00",
                  "next_dusk": "2023-10-20T23:55:32.168170+00:00",
                  "next_midnight": "2023-10-21T05:55:01+00:00",
                  "next_noon": "2023-10-20T17:55:19+00:00",
                  "next_rising": "2023-10-21T12:21:17.461132+00:00",
                  "next_setting": "2023-10-20T23:29:29.144072+00:00",
                  "elevation": 22.51,
                  "azimuth": 121.63,
                  "rising": true,
                  "friendly_name": "Sun"
                },
                "last_changed": "2023-10-20T12:20:26.007880+00:00",
                "last_updated": "2023-10-20T14:22:43.170773+00:00",
                "context": {
                  "id": "01HD6QF3Z2PJPGAG35Z4VNC9CJ",
                  "parent_id": null,
                  "user_id": null
                }
              },
              "to_state": {
                "entity_id": "sun.sun",
                "state": "above_horizon",
                "attributes": {
                  "next_dawn": "2023-10-21T11:55:10.694198+00:00",
                  "next_dusk": "2023-10-20T23:55:32.168170+00:00",
                  "next_midnight": "2023-10-21T05:55:01+00:00",
                  "next_noon": "2023-10-20T17:55:19+00:00",
                  "next_rising": "2023-10-21T12:21:17.461132+00:00",
                  "next_setting": "2023-10-20T23:29:29.144072+00:00",
                  "elevation": 23.2,
                  "azimuth": 122.38,
                  "rising": true,
                  "friendly_name": "Sun"
                },
                "last_changed": "2023-10-20T12:20:26.007880+00:00",
                "last_updated": "2023-10-20T14:26:43.171620+00:00",
                "context": {
                  "id": "01HD6QPEB3RX70A982263YFW2D",
                  "parent_id": null,
                  "user_id": null
                }
              },
              "for": null,
              "attribute": null,
              "description": "state of sun.sun"
            },
            "cover_entity": {
              "device_id": "27fabd93ed4193cb1387377fbccd1714"
            },
            "azimuth": 163,
            "distance": 4,
            "max_height": 2.1,
            "min_height": 0,
            "default_height": 60,
            "min_position": 0,
            "degrees": 90,
            "default_template": "",
            "azimuth_left": 90,
            "azimuth_right": 90,
            "max_elevation": 90,
            "min_elevation": 0,
            "cover_height": 100,
            "change_threshold": 1,
            "time_out": 1,
            "condition_mode": "and",
            "dict_var": {
              "condition_1": [],
              "condition_2": [],
              "names_1": [],
              "names_2": []
            },
            "condition_1": false,
            "condition_2": false,
            "entities_1": [],
            "entities_2": []
          }
        }
      ],
      "action/0": [
        {
          "path": "action/0",
          "timestamp": "2023-10-20T14:26:43.192693+00:00",
          "changed_variables": {
            "context": {
              "id": "01HD6QPEB7CEMK7RP1Y8X1MT7M",
              "parent_id": "01HD6QPEB3RX70A982263YFW2D",
              "user_id": null
            }
          },
          "result": {
            "choice": 0
          }
        }
      ],
      "action/0/choose/0": [
        {
          "path": "action/0/choose/0",
          "timestamp": "2023-10-20T14:26:43.192764+00:00",
          "result": {
            "result": true
          }
        }
      ],
      "action/0/choose/0/conditions/0": [
        {
          "path": "action/0/choose/0/conditions/0",
          "timestamp": "2023-10-20T14:26:43.192786+00:00",
          "result": {
            "result": true,
            "entities": []
          }
        }
      ],
      "action/0/choose/0/sequence/0": [
        {
          "path": "action/0/choose/0/sequence/0",
          "timestamp": "2023-10-20T14:26:43.195805+00:00",
          "result": {
            "result": false
          }
        }
      ],
      "action/0/choose/0/sequence/0/conditions/0": [
        {
          "path": "action/0/choose/0/sequence/0/conditions/0",
          "timestamp": "2023-10-20T14:26:43.195903+00:00",
          "result": {
            "result": false,
            "entities": []
          }
        }
      ]
    },
    "config": {
      "variables": {
        "cover_entity": {
          "device_id": "27fabd93ed4193cb1387377fbccd1714"
        },
        "azimuth": 163,
        "distance": 4,
        "max_height": 2.1,
        "min_height": 0,
        "default_height": 60,
        "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 -%} {

I do notice you do have a quiet large (work-area) distance set. The larger the distance, the closer the sun’s elevation must be to the horizon to close the cover.
Did you try to lower the distance?

EDIT:
I think to see you’re using the device_id to setup the entity. But you should rather select the entity_id for the entity selection.

Both of your suggestions were spot on for this Noob (I should have caught those) but changing from Device to Entity causes an error. The messages above mentioned this type of error but the new version of the BP should have fixed it. I’m on version 1.1.2.

{
  "trace": {
    "last_step": null,
    "run_id": "38a6e57b0b6f03298cc2734507cf308f",
    "state": "stopped",
    "script_execution": null,
    "timestamp": {
      "start": "2023-10-23T19:04:57.712273+00:00",
      "finish": "2023-10-23T19:04:57.713287+00:00"
    },
    "domain": "automation",
    "item_id": "1697055708549",
    "error": "ValueError: Template error: int got invalid input 'None' when rendering template '{%- set ns = namespace(list_1=[],name_1=[],list_2=[],name_2=[]) %} {%- set  entity = cover_entity['entity_id'] -%} {%- if entity is iterable and (entity is not string and entity is not mapping) -%}\n  {%- set cover = entity -%}\n{%- else -%}\n  {%- set cover = [entity] -%}\n{%- endif -%} {%- for c in cover %}\n  {%- set position = state_attr(c,'current_position') | int -%}\n  {%- set con_1 = ((position - cover_height | float) | abs >= change_threshold)\n  or (cover_height in [default_height, default_template] and position not in [default_height, default_template])%}\n  {%- set ns.list_1 = ns.list_1 + [con_1]  -%}\n  {%- set con_2 = now() - timedelta(minutes=time_out) >= states[c].last_updated %}\n  {%- set ns.list_2 = ns.list_2 + [con_2] %}\n  {%- if con_1 == true -%}\n    {%- set ns.name_1 = ns.name_1 + [c] -%}\n  {% endif %}\n  {%if con_2 == true%}\n    {%- set ns.name_2 = ns.name_2 + [c] -%}\n  {% endif %}\n{%endfor%} {%- set dict = {\n  'condition_1':ns.list_1,\n  'condition_2':ns.list_2,\n  'names_1':ns.name_1,\n  'names_2':ns.name_2\n  } %}\n{{dict}}' but no default was specified",
    "trigger": null,
    "trace": {},
    "config": {
      "variables": {
        "cover_entity": {
          "entity_id": "cover.officeshade1"
        },
        "azimuth": 180,
        "distance": 0.1,
        "max_height": 0.4,
        "min_height": 0.2,
        "default_height": 60,
        "min_position": 8,
        "degrees": 90,
        "default_template": "",
        "azimuth_left": 90,
        "azimuth_right": 90,
        "max_elevation": 90,
        "min_elevation": 0,
        "cover_height": "{%- set deg2rad = pi/180 -%} {

@basbrus - any idea on the error when I switch from Device_ID (no error) to Entity_ID (error) ?

Thanks for this great Automation and Blueprint, first!

Is it possible to edit the Blueprint .yaml to open the blinds whenever there is direct sunlight onto the window?
I made a copy of the blueprint already which I can edit accordingly, but I am not shure where and how.

Background:
I live in a poorly insulated building and I use this blueprint during summer to avoid my living room getting too hot whenever there is direct sunlight. During workdays, I am not home anyway at these times of day.
In Winter, I lower the heating to a certain degree whenever I am away from home, and the blinds close automatically to give some additional insulation.
However, on sunny winter days, I would want to let the sun in instead of keeping it out, thus heating the living room a bit.
Conditions on what is a “summer” or “winter” day are set, but how to change the blueprint to work in the reverse way?

Thanks

I don’t think the blueprint can be used in that way. But I shared a template in this thread which may help you: https://community.home-assistant.io/t/automatic-blinds-sunscreen-control-based-on-sun-platform/573818/175?u=langestefan

This will return true whenever there is direct sunlight hitting your window. You can use that in an automation to control the blind

Oh, thanks for pointing me towards your old post, easy to overread for me…
I think I can go from there.

I use the UV index to decide whether to lower the blinds or not. It works pretty well in any season and also based on weather conditions. This could be an idea to combine with the angular similarity.

2 Likes

That could work well as a conditional boolean indeed. It’s not a direct replacement for brightness (what you would really like to know) but it does correlate well with it I believe.

How do I set the maximum height the cover will be automatically adjusted too?

Did I miss the obvious post on how to run the simulation?

Simulation is just the python notebook. Not much to explain

Just to share where this has gone since summer when I shared this: I now have a couple input booleans that I added to this. I use those to allow “nap time” which keeps the shades closed in my bedroom even when all other inputs say they should remain open. I also have a “work mode” which keeps my office shade closed about 75% when it is bright (because my office has 8 foot high south facing windows and I cannot see my monitor!).

1 Like

EDIT: in the meantime I’ve designed my own automation in Node Red (as it’s homemade it matches better my specific needs, looking at weather forecasts, cloud cover etc), but I leave the comment below in case it helps improving this custom integration.

Thanks a lot for this cool custom integration @basbrus
I’ve been playing around with it for my tilted blinds.
I think I’ve been able to set it up correctly:

However the resulting proposed angle is a bit confusing:
image

It’s winter and it’s cold, the used temperature sensor has a value of 20°C so the proposed angle should aim at getting maximum sunrays inside.

It stays at the default position of 60% until 11:27 which makes sense as that is the moment the sunlight can start hitting the window.
Then it shifts to an almost closed position of 97%, as if the sun is at a very high elevation. while in reality the elevation of the sun at that moment is only 15.43° (winter), so I had expected a value of 58.57% (=50% for horizontal + an additional 8.57% to match the elevation of the sun, since a 90° change in tilt angle of the blinds corresponds to 50% so for each degree of elevation the blinds need to increase by 0.56%)

Then it decreases quite quickly (while in reality the elevation of the sun is still increasing until 12:50

Here’s the elevation for the sun today (it’s winter so it doesn’t go higher than 17.78°
image

Around 13:00 the proposed tilt even goes below 50%, which I don’t understand the purpose of with the sun above the horizon.

The second element I don’t understand, is the blueprint. I guess do to my incompetence with blueprints and Home Assistant automations as I normally use Node Red.
The blueprint doesn’t change the tilt angle of my blinds. I see in the ‘traces’ tab that there is an entry for each time a new timt angle is proposed, but all I have is “Stopped because of unknown reason “null” at 13 January 2024 at 13:18:56 (runtime: 0.01 seconds)”
Not a major issue as I can simply use Node Red to pass on the proposed value to the blinds.

Any help on getting this to work as expected would be very much appreciated!

Suggestion for special windows.
In Europe and especially in Germany, so-called “tilt-turn” windows are used. They have a “tilt” ventilation position and an open “turn” position like a door.
The difference would then have to be determined with 2 sensors.
Furthermore, a multi function would certainly be useful for more than one roller shutter.

1 Like

This is not entirely correct. This only holds when there is no presence detected. If there is presence the blinds should first of all block sunrays to prevent visual discomfort within the work area.

The presence of direct radiation could cause visual discomfort on the work surface to people. In this case, the optimal configuration consists in shielding sun’s rays by keeping the lamellas as open as possible.

quote by scientific research used for the calculations

In general this means that the slat angle is determined in the normal way, which seems to correspond with the graph.

I think the blueprint misses the service to adjust tilted blinds, this could be solved in a future update, although already stated by yourself all the needed data is already available by the sensor and can be used in an user build automation (with more variables).

1 Like

Makes sense of course. However in the place where I have the blinds, I always want them to let in as much sunlight as possible, even when I’m home.
My understanding so far is that using only the sun elevation to calculate the blinds angle is not sufficient. The azimuth is also important. The more the sun is at an angle to the blinds, the steeper the angle of the blinds should be to maximise sunrays. But I’m not sure what the best formula is. Any hints would be greatly appreciate.
At the moment I’m considering going the experimental way and noting the azimuth and elevation at various moments and checking what angle minimises shadows at that moment. I can then do a multiple lineair regression to find the best formula (Multiple Linear Regression Calculator). But that will take a loong time to find a good formule (hopefully) as there are of course strong difference between summer and winter.

Any tips for calculating the optimal angle of venetian blinds to maximise sun radiation would be most welcome :slight_smile:

But that’s exactly what the blueprint does. It uses both azimuth and elevation, and then calculates the position of the blinds. You can’t ‘maximize the sunligh’, because then the blinds would always be open. So what the blueprint does is calculate the position of the blinds in order to guarantee shade in a given area with a given position of the sun.

I don’t think you’re going to find a good formula using that method, there are too many variables to consider. Using the blueprint + some heuristics on when to open/close it based on lighting conditions and weather should cover all usecases.

1 Like

The one thing you can do is to increase the distance parameter. This increases the area where direct sunrays are allowed.

I also added an update 2 days ago after your suggestions to the integration. That update allows to set a weather entity in climate mode. The state of that entity will be used to evaluate if there are direct sun rays possible (e.q. the state is "sunny","partlycloudy","windy"). If not and the climate operating mode is winter, the blinds will return to its default value to let in as much light by the user’s preference.

Another thing you can do is to create a mock input_boolean that always reports ‘False’ and use that as presence detection entity or even automate its’ state based on other preference than purely the presence.

@langestefan any idea what would cause this error? I’m running the latest version of the BP.

{
  "trace": {
    "last_step": null,
    "run_id": "38a6e57b0b6f03298cc2734507cf308f",
    "state": "stopped",
    "script_execution": null,
    "timestamp": {
      "start": "2023-10-23T19:04:57.712273+00:00",
      "finish": "2023-10-23T19:04:57.713287+00:00"
    },
    "domain": "automation",
    "item_id": "1697055708549",
    "error": "ValueError: Template error: int got invalid input 'None' when rendering template '{%- set ns = namespace(list_1=[],name_1=[],list_2=[],name_2=[]) %} {%- set  entity = cover_entity['entity_id'] -%} {%- if entity is iterable and (entity is not string and entity is not mapping) -%}\n  {%- set cover = entity -%}\n{%- else -%}\n  {%- set cover = [entity] -%}\n{%- endif -%} {%- for c in cover %}\n  {%- set position = state_attr(c,'current_position') | int -%}\n  {%- set con_1 = ((position - cover_height | float) | abs >= change_threshold)\n  or (cover_height in [default_height, default_template] and position not in [default_height, default_template])%}\n  {%- set ns.list_1 = ns.list_1 + [con_1]  -%}\n  {%- set con_2 = now() - timedelta(minutes=time_out) >= states[c].last_updated %}\n  {%- set ns.list_2 = ns.list_2 + [con_2] %}\n  {%- if con_1 == true -%}\n    {%- set ns.name_1 = ns.name_1 + [c] -%}\n  {% endif %}\n  {%if con_2 == true%}\n    {%- set ns.name_2 = ns.name_2 + [c] -%}\n  {% endif %}\n{%endfor%} {%- set dict = {\n  'condition_1':ns.list_1,\n  'condition_2':ns.list_2,\n  'names_1':ns.name_1,\n  'names_2':ns.name_2\n  } %}\n{{dict}}' but no default was specified",
    "trigger": null,
    "trace": {},
    "config": {
      "variables": {
        "cover_entity": {
          "entity_id": "cover.officeshade1"
        },
        "azimuth": 180,
        "distance": 0.1,
        "max_height": 0.4,
        "min_height": 0.2,
        "default_height": 60,
        "min_position": 8,
        "degrees": 90,
        "default_template": "",
        "azimuth_left": 90,
        "azimuth_right": 90,
        "max_elevation": 90,
        "min_elevation": 0,
        "cover_height": "{%- set deg2rad = pi/180 -%}
}}