Virtual light sensor

Did anyone ever made a virtual light sensor based on sun, time and weather?
That would be useful to understand when it’s the right time to let motion presence lights to turn on or let them off.

You could use a template binary sensor which uses the values from the other sensors to set itself on or off.

well, yes, I could combine cloud coverage, sun below the horizon and stuff… there are many variables, I was just wondering if anyone already did something like that because it’s not really simple :wink:

Yeah, it’ll be a Jinja template, however the example in the link I added before uses the sun component. So it might not be too hard to do. I’ll have a go later.
What is the true and false criteria which you are looking for? E.g. sun below horizon, time < 16:00 ??

Only the sun couldn’t be enough. I mean, it could be noon and so cloudy that the lights in my house should turn on…and the light of the sun is different on different places on the planet. The light here is Italy it’s probably brighter than, i don’t know, Norway maybe…
Yes, a binary sensor would use this value (probably lumens) calculated using the variables I said before like sun, date, time (or maybe not, because the sun could be enough), weather and my position.

I found one at that might be of interest to you.

1 Like

FHEM has a twilight module which uses sunset + indoor horizon + yahoo weather conditions. Maybe there is some useful code snippet to borrow?

I’ve been using that for my indoor light automation the past 5 years and never had the impression it would toggle too early or too late.

1 Like

For a different take on the topic you can look on Weather Underground for a station near your location with a Solar light sensor (for example see ) and set a minimum light value in a template binary sensor.

1 Like

How about this?
It is true if the sun is below horizon and the hour is greater or equal to 16
the visibility is Very Poor, Poor or Moderate.
you could probably extend it to use the seasons sensor

# True if sun is below horizon and time is greater than 16:00 or visibility is Very Poor, Poor or Moderate
    - sun.sun
    - sensor.met_office_visibility
    friendly_name: Need lights
    value_template: "{{(states.sun.sun.attributes.elevation|float < 2 and now().hour >= 16) or states.sensor.met_office_visibility.state in ['VP','PO','MO']}}"
1 Like

Here’s what I use. It depends on Dark Sky (and the sun platform), but can easily be adapted to alternate weather platforms.

The bit about timestamps helps de-bounce things so the lights aren’t going on and off when it’s right on the edge.

  - platform: template
        friendly_name: 'Turn On Indoor Lights'
        value_template: >
          {% if (states.sun.sun.attributes.elevation | int < 30) %}true
          {% elif ( (states.sun.sun.attributes.elevation | int < 40) and (states.sensor.dark_sky_cloud_coverage.state | int > 50)) %}true
          {% elif (states.sensor.dark_sky_cloud_coverage.state | int > 90) %}true
          {% elif ( states.sensor.turn_on_indoor_lights.state == 'true' and ((as_timestamp(now()) - as_timestamp(states.sensor.turn_on_indoor_lights.last_changed)) | float < 1200)) %}true
          {% else %}false
          {% endif %}


I solved this by creating an additional attribute to the sun.sun sensor. This goes in custom_components/

Next, I created a template sensor:



Thank you everyone for sharing your solutions.

I am trying the modification from @Kobold currently.

For weather states I just went with

  • platform: yr
    • fog
    • cloudiness

and substituted this line accordingly:
value_template: '{{ ((100-states.sensor.yr_cloudiness.state|float)/100 * states.sun.sun.attributes.daylight) | round(0) }}'

(The code assumes that on a cloudy day you get about 1/10th of the brightness which is a rough estimate).
One thing I am wondering about: does darksky never reach 100% cloudiness? If that would happen we will get: 0/100 = 0 * daylight?


I just found out it will hit 100%, and sometimes the cloud cover isn’t very “tall”. I’ll have to use some of the weather conditions to strengthen it. From YR you can get the cloud level, which I suspect would inform a better calculation.

In a perfect world, Hass would create standard containers for the conditions, which we could standardize apps on top of.

1 Like

Any update on this?
I am looking for the same sensor. Right now trying your config.


I’ve used this code forever and it works flawlessly:

It requires you to have the “sun” component enabled as well as the dark_sky_cloud_coverage sensor. You can use a different cloud coverage sensor if you edit the template code accordingly.

It is written, intentionally, verbose, to promote easy understanding and easy changing of variables if you’d like to fiddle with it to your liking.

- platform: template
        - sun.sun
        - sensor.dark_sky_cloud_coverage
      value_template: >-
        {%- set elevation = state_attr('sun.sun','elevation') | float %}
        {%- set cloud_coverage = states('sensor.dark_sky_cloud_coverage') | float %}
        {%- set cloud_factor = (1 - (0.75 * ( cloud_coverage / 100) ** 3 )) %}
        {%- set min_elevation = -6 %}
        {%- set max_elevation = 90 %}
        {%- set adjusted_elevation = elevation - min_elevation %}
        {%- set adjusted_elevation = [adjusted_elevation,0] | max %}
        {%- set adjusted_elevation = [adjusted_elevation,max_elevation - min_elevation] | min %}
        {%- set adjusted_elevation = adjusted_elevation / (max_elevation - min_elevation) %}
        {%- set adjusted_elevation = adjusted_elevation %}
        {%- set adjusted_elevation = adjusted_elevation * 100 %}
        {%- set brightness = adjusted_elevation * cloud_factor %}
        {{ brightness | round }}
      unit_of_measurement: '%'
      device_class: 'illuminance'

interesting this is indeed thanks.
2 questions please:
what unit it the value in percentage? of what? If i fill it in locally it says 9, but that’s hardly near anything else I can read of my sensors…

also, if you take out the correct sensor in cloud_coverage, or make a typo for that matter, it still calculates to a value, 21 in my settings, which is bit odd, since it is a multiplier in the end result, and should return an error?


The percentage is basically what percentage of the maximum possible amount of light (based on the max elevation of 90 and the minimum of -6). For instance, if you want maximum brightness to be when the sun is 25 degrees off the horizon, then setting max_elevation to 25 will give you 100% when it’s above 25 degrees (assuming a cloudless day).

Basically, if you take cloud coverage out of the equation, 0% == min_elevation and 100% == max_elevation.

The reason, I think, that the formula still works even if you typo or incorrectly indicate the cloud coverage sensor is because the code converts it to a “float” and uses state_attr(). Those two things together, in most cases, means a type will result in a cloud_coverage of 0, which would mean a cloud_factor of 1 which, basically, mean there are no clouds, so count all of the light. The cloud factor formula you see ensure that clouds can only block 75% of all light and that the cloud cover does not affect the light in a linear scale… 50% of cloud cover does not mean 50% of light getting through and also, 100% cloud cover is not half as much light as 50%.

This sensor could be made better by taking cloud HEIGHT into account as well, but, for my needs, this works perfectly.

I am playing with this and I have set it the maximum possible elevation for where I live (i.e. 60). I am not sure if this is the best way to do it but I will see over the next few days / weeks.

I wonder if it would be better set to the lowest possible maximum elevation though?

This is awesome!

Adjusted my max elevation to 66% as that’s the best we get here in Seattle :frowning: