Light turn on brightness based on sun's horizon position?

Hey Everyone,

I want to adjust the “turn on” brightness of my laundry room light based on the position of the sun above/below the horizon within a 30-minute window.

I’ve seen automation examples that use the sun above/below horizon state to set the brightness of a light whenever it is turned on, but those automations are rather binary, meaning the change happens exactly when the sun’s horizon position changes.

           Bright (100%)
       ____________________
       |                  |
       |                  |
       * sunrise          * sunset
       |                  | 
_______|                  |__________  Dim (50%)

I am looking for a way to calculate the brightness value over a small timeframe of +/- 30 minutes of the sun’s horizon change.

                Bright (100%)
+30 mins -> _____________________ <- -30 mins
           /                     \
          /                       \
         * sunrise                 * sunset (75%)
        /                           \     
_______/ <- -30 mins     +30 mins -> \__________  Dim (50%)

I know there are plugins and templates that will constantly adjust the brightness and color temperature of lights over time while the lights are on. For my situation, I don’t want the brightness to change while the light is on, just adjust the “turn on” value based on the position of the sun.

For my example above, I would like the “turn on” brightness to calculate to 87% at 15 minutes after sunrise or 15 minutes before sunset.

I’m sure there is straightforward math around these calculations, but I’m not quite getting there myself.

What is a good single formula to determine the “turn on” brightness value based on the sun’s horizon position?

Thank you!!

Your graph shows a linear relationship between time and brightness, after sunrise and before sunset. It indicates 75% at the 15-minute mark, not 87%. 15 minutes is halfway between 0 and 30 minutes so halfway between 50% and 100% is 75%.

If you want 87% at 15 minutes, it implies there’s a non-linear relationship between time and brightness during the 30-minute time period. If so then what’s the formula you have in mind for this non-linear curve?

@123 The full time width of the transition is 60 minutes between 100% and 50%. The +/- 30 minutes is relative to the sunrise/sunset moment. The sunrise/sunset position is halfway between the transition, so the sunrise/sunset position will be 30 minutes through the full 60-minute transition, therefore 75% brightness. The 15-minute mark between full day (100%) and the sunrise/sunset point (75%) will put the brightness at 87.5%.

What would be a linear yaml formula for this transition between -30 minutes before sunrise/sunset and +30 minutes after sunrise/sunset?

Thanks again!

1 Like

Thanks for the clarification; my initial interpretation of the graph was incorrect.

Deleted original post because I noticed a error…

Your post doesn’t supply us with any position-based desired threshold values, only time values

The following isn’t pretty, but it should work for the time-based thresholds you have supplied.

{% set time = now() %}
{%- set sunrise = today_at((state_attr('sun.sun', 'next_rising')
|as_datetime|as_local).strftime('%H:%M')) %}
{%- set sunset = today_at((state_attr('sun.sun', 'next_setting')
|as_datetime|as_local).strftime('%H:%M')) %}
{%- set br_factor = 0.0138 %}
{%- set time_list = [time, sunrise - timedelta(minutes=30), 
sunrise + timedelta(minutes=30), sunset - timedelta(minutes=30), sunset + timedelta(minutes=30)] %}
{%- set z = (time_list | sort()).index(time) %}
{%- if z == 2 %} 100
{%- elif z in [ 0, 4 ]%} 50
{%- elif z in [ 1, 3] %}
  {%- set (ref, neg) = (sunrise, 1) if z == 1 else (sunset, -1) %}
  {{- (((time - (ref - (timedelta(minutes=30)* neg ))
  ).total_seconds() * br_factor) + (50 * neg )) | int * neg }}
{%- endif %}

Because sun.sun only gives the next sunrise and sunset there’s going to be a slight inaccuracy in the 30 min after both sunrise and sunset.

Aside the support of guru’s above, for other/future things … also have a look at the sun2 integration (hacs)

Copy-paste the following into the Template Editor and confirm it reports the desired result.

{% set sr = today_at((state_attr('sun.sun', 'next_rising') | as_datetime | as_local).strftime('%H:%M')) %}
{% set pre_sunrise =  sr - timedelta(minutes=30) %}
{% set post_sunrise = sr + timedelta(minutes=30) %}

{% set ss = today_at((state_attr('sun.sun', 'next_setting') | as_datetime | as_local).strftime('%H:%M')) %}
{% set pre_sunset = ss - timedelta(minutes=30) %}
{% set post_sunset = ss + timedelta(minutes=30) %}

{% set t = now() %}
{# {% set t = sr + timedelta(minutes=15) %} #}
{# {% set t = ss + timedelta(minutes=-15) %} #}

{{ { pre_sunrise <= t < post_sunrise: 50 + ((t - pre_sunrise).seconds * 50/3600)|int(0),
     post_sunrise <= t < pre_sunset: 100,
     pre_sunset <= t < post_sunset: 100 - ((t - pre_sunset).seconds * 50/3600)|int(0)
   }.get(true, 50) }}

If you uncomment either one of these lines:

{# {% set t = sr + timedelta(minutes=15) %} #}
{# {% set t = ss + timedelta(minutes=-15) %} #}

You can conveniently set the current time to whatever you want in order to properly exercise the template.

After you are satisfied it works, you can eliminate the commented lines because they’re not needed. If it fails to work, let me know and I can help you correct it

1 Like

Hey @123 ,

That template seems to work!

However, I’m used to just creating simple automations. How do I put that value into the “Turn on the laundry room light” automation?

The current automation action looks like this:

action:
  - service: light.turn_on
    data:
      transition: 1
      brightness: 255

Obviously, I need to swap the current value of “255” for the value generated by the script. Do I use a data_template? Do I create a separate script to store the value into an input number helper and then read that value into the brightness? Something else entirely?

Thanks again for being awesome!!

Add the template to the brightness_pct option like this:

action:
  - service: light.turn_on
    data:
      transition: 1
      brightness_pct: >
        {% set sr = today_at((state_attr('sun.sun', 'next_rising') | as_datetime | as_local).strftime('%H:%M')) %}
        {% set pre_sunrise =  sr - timedelta(minutes=30) %}
        {% set post_sunrise = sr + timedelta(minutes=30) %}
        {% set ss = today_at((state_attr('sun.sun', 'next_setting') | as_datetime | as_local).strftime('%H:%M')) %}
        {% set pre_sunset = ss - timedelta(minutes=30) %}
        {% set post_sunset = ss + timedelta(minutes=30) %}
        {% set t = now() %}
        {{ { pre_sunrise <= t < post_sunrise: 50 + ((t - pre_sunrise).seconds * 50/3600)|int(0),
             post_sunrise <= t < pre_sunset: 100,
             pre_sunset <= t < post_sunset: 100 - ((t - pre_sunset).seconds * 50/3600)|int(0)
           }.get(true, 50) }}

EDIT

Correction. The template produces a value from 50 to 100 so it should be used with the brightness_pct option.

1 Like

It works! Frickin’ awesome!! Thank you so much!!!

1 Like

Here is the same solution but with variables at the top for easier future tweaking:

action:
  - service: light.turn_on
    data:
      transition: 1
      brightness_pct: >
        {% set min_bright_percent = 50 %}
        {% set max_bright_percent = 100 %}
        {% set mins_span = 30 %}
        {% set sr = today_at((state_attr('sun.sun', 'next_rising') | as_datetime | as_local).strftime('%H:%M')) %}
        {% set pre_sunrise =  sr - timedelta(minutes=mins_span ) %}
        {% set post_sunrise = sr + timedelta(minutes=mins_span ) %}
        {% set ss = today_at((state_attr('sun.sun', 'next_setting') | as_datetime | as_local).strftime('%H:%M')) %}
        {% set pre_sunset = ss - timedelta(minutes=mins_span ) %}
        {% set post_sunset = ss + timedelta(minutes=mins_span ) %}
        {% set t = now() %}
        {{ { pre_sunrise <= t < post_sunrise: max_bright_percent - min_bright_percent + ((t - pre_sunrise).seconds * (max_bright_percent - min_bright_percent )/3600)|int(0),
             post_sunrise <= t < pre_sunset: max_bright_percent,
             pre_sunset <= t < post_sunset: max_bright_percent - ((t - pre_sunset).seconds * (max_bright_percent - min_bright_percent )/3600)|int(0)
           }.get(true, min_bright_percent) }}

Hi,

Thanks for this. I’ve managed to get it working great.

However, there is one issue, I guess it’s location dependent but at some point when increasing mins_span it will spaz out. For example I tried setting mins_span to 120 (two hours).

It works fine here 98 minutes, returning 100%

But breaks at 99 minutes returning 214%

The breakpoint changes depending on what time of day it is (for example 99, 100, 101 minutes now returns 100% but 102 minutes returns 219%) so I guess it’s something to do with the calculation. During nighttime it will spaz out and give negative digits in a similar manner, for example last night it returned -174 when I checked why my lights weren’t turning on any more.

@123 @Phade Any ideas, seeing anything similar?