Virtual light sensor

I found one at https://github.com/etsinko/ha-config/blob/master/binary_sensors.yaml 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 https://www.wunderground.com/personal-weather-station/dashboard?ID=ILAZIORO38&cm_ven=localwx_pwsdash ) 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
or
the visibility is Very Poor, Poor or Moderate.
you could probably extend it to use the seasons sensor

sensor:
# True if sun is below horizon and time is greater than 16:00 or visibility is Very Poor, Poor or Moderate
  needlights:
    entity_id:
    - 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.

sensor:
  - platform: template
    sensors:
      turn_on_indoor_lights:
        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 %}
6 Likes

image

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

Next, I created a template sensor:

image

3 Likes

Thank you everyone for sharing your solutions.

I am trying the modification from @Kobold currently.

For weather states I just went with

  • platform: yr
    monitored_conditions:
    • 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?

2 Likes

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.

Thanks

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
  sensors:
    sunlight_pct:
      entity_id:
        - 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'
18 Likes

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?

thx,

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:

For me, personally, I like to leave it at -6 and 90 (min, max) despite the fact that the sun never gets to 90degrees where I live either. The reason is, at 90 degrees (i.e. directly overhead) the sun will indeed produce the most solar radiation/light/heat than it will in any other location in the sky. So, it is a true maximum.

This way I can look at sunlight percentage as the percentage of sunlight getting through compared to the maximum anywhere on earth. What’s nice about that is then 20% is roughly when you’d want outside lights to come on/off no matter where in the world you live. If you change the maximum, you’re still going to want the lights on at roughly the same brightness/sunlight level as me, but your number will be quite a bit higher than mine.

It also affects how the cloud cover factors in. With the formula above I did extensive testing using a wunderground weather station near me that happened to provide Solar Radiation in W/m^2 and, using a known maximum W/m^2 for 100%, I was able to make a second sensor that fairly accurately tracked the readings from that weather station.

It’s useful either way, of course. And if it makes more sense for you to set the max at 66 degrees, then, by all means you should. I just wanted to make the argument for using a more “universal” number in case you weren’t aware of the potential positive aspects of doing so.

3 Likes

Thanks for the details @swiftlyfalling, this makes sense.

I’ll move this back to 90, and see how I go hooking this into my existing lighting.

I’ve found that turning outside lights off at > 23 and on at < 20 works great to get the lights on during any time there are thick, dark, clouds, or when the sun is too low to make enough light.

For interior lights, I’ve found every room needs it’s own setting, since the lighting is different in each room depending on how many windows there are and which direction they face. Ultimately, I found that having a lux sensor in each room was an easier way to go for interior lighting, instead of trying to find the best cutoff point for each room. So, I really just use it for exterior lighting these days.

I imagine I could just choose an indoor sensor for a room that gets good outside light and use it to determine outside light, but this formula works so well I haven’t felt the need to change it.

1 Like

very nice indeed, testing as we speak. with the Open weather map cloud percentage. Will see tonight when the lights go off (hardware light sensor) what setting that is. Used to check against sun elevation, but this might be more adequate.

btw you can leave out the entity_id’s, since they are in the template itself. No complaints are thrown.

3 Likes

What is your experience, is it more adequate as sun elevation? Which wheater platform are you using?