Outdoor illuminance template sensor

It’s odd because the original code did use (now) but for some unexplained reason he switched it.

That was a mistake which I made early in development. I didn’t fully understand the math then, but I have been updating the code in the first post with any changes as I go. For example I just switched to now() | as_timestamp() per your suggestion.

1 Like

At the bottom of this post is a link to a snapshot of my personal variation on this sensor. To make it easier for me to troubleshoot the code I have broke the 3 pieces of data out into attributes of the sensor. I am including this for the benefit of advanced users.

You can see the 3 attributes in the below screenshot.

My preferred weather source becomes unavailable after I hit their limit for the day. So the current_condition attribute automatically fails over the the next weather provider in the list of weather_sensors. This is more complexity than most people will need, so use this as a reference and replace that code to your current weather condition from a single sensor.

1 Like

This is great work guys. I am going to use it to turn on some lights gradually from a darker area of the house first then the other areas slightly later. I was using Dawn and Dusk plus offsets, then a sunlight percentage calculation which didn’t quite hit the mark. Going to monitor the values to see what triggers to use.
Thanks again a keep up the good work.
P.S. I used the example from BrianHanifin’s post #43 and it seems to be working.


I have just started to try and get useful information out of this script (which works great BTW)

Here is my automation:

  - id: '1505977024304'
    alias: Turn Lounge Light On Elevation < 25° & after 4PM
    - platform: state
      from: 'on'
      entity_id: 'binary_sensor.sun_above_25'
      to: 'off'
    - platform: time
      at: '16:00:00'
    - condition: and
      - condition: time
        after: '16:00:00'
      - condition: and
        - condition: or
          - "{{ (states('sensor.illuminance') | float < 7500 ) and (states('sensor.estimated_illuminance') | float < 10000) }}"
          - condition: state
            entity_id: 'binary_sensor.sun_above_25'
            state: 'off'
    - condition: state
      entity_id: 'input_boolean.homeandawayauto'
      state: 'on'
    - service: script.turn_on
        entity_id: script.loungenormal

So I want the lights in the lounge room to go on if the sun is below 25° and after 4PM. So I am using Phil Bruckners sun2 binary sensor that switches off at 25°

In Summer though, I don’t want it to switch on unless it’s low light outside. So I use this script to provide sensor.illuminance with a local weather provider in Australia (BOM) and I also use Phil Bruckners illuminance with met.no but I set a different illuminance for both . With the local one < 7500 and the met.no one <10000
(I also have a global overide input_boolean for when we are not home)
Initial testing seems to give the desired result…

1 Like

Hello Guys !

Any of you in Europe ? Which weather data provider do you use ? Did you find any accurate ?
I’ve tested all the ones I could find (DarkSky, OpenWeatherMap,… and the national MeteoFrance) but I’ve never been convinced by the data accuracy.

I’ve ended using a Philips Hue outdoor sensor looking at the sky to get an illuminance sensor, and this is perfect.

1 Like

I’m in Europe (Scotland) David. I use OpenWeather, AccuWeather and Met Office (The latter being UK). Sounds like you have the best solution and no guessing required!


This is nice - Thanks @BrianHanifin.

For others coming to this thread, and uses OpenWeatherMap, I just realized that OWM have got a thing called “Weather Codes”, which somehow put different descriptive weather conditions into categories. :+1:

So instead of using an exhaust dictionary, chances are you could use those weather codes to simplify (or enhance, depends on how you look at this thing) your template sensor!

Other weather services might (or should) have something similar. So… Happy coding!

1 Like

2021.05.x Users: Update Sun2 Component

If you see the following error, you need to update to the latest version of the Sun2 component.

Platform error sensor.sun2 - cannot import name ‘AstralError’ from ‘astral’ (/usr/local/lib/python3.8/site-packages/astral/init.py)

@pnbruckner updated his sun2 component to work with the new version of the library that Home Assistant began using recently.


Potentially dumb question, but I can’t quite pick apart the maths:

Is there a way for me to bring forward the time that the sunset starts to lower the lux level of the sensor? If so, as an example, which part of the sensor would I need to amend to start the lux level dropping 2 hours before sunset?

You will want to take a look at the sun_factor attribute template. The code uses the sun2 custom component to create sensors with the times for for the sunrise, sunset, dawn, dusk, (example: sensor.sunset).

The value of sensor.sunset is converted into a timestamp (example: 1630462507.71866). My code subtracts 40 minutes (40 * 60 = 2400 seconds) from sunset to get the sunset_begin value. So change the 40 to 120 if you want to subtract 2 hours instead.

{%- set sunset = states("sensor.sunset") | as_timestamp %}
{%- set sunset_begin = sunset - (120 * 60) %}{# (Number of minutes * 60 seconds) #}

If you prefer to use the newer timedelta() function, you could change the sunrise filter to as_datetime. Afterwards, you’d have to convert the sunset_begin value to a timestamp for comparison further down in the code (example below).

{%- set sunset = states("sensor.sunset") | as_datetime %}
{%- set sunset_begin = as_timestamp( sunset - timedelta(hours=2) ) %}

Likely a very silly question, but is it possible to run this with without Sun2? Or put differently, to run this while only leveraging the native sun?



It’s not a silly question, but the answer is no.

Sun2 calculates and provides the sunrise, sunset etc for TODAY (and yesterday/tomorrow) where the native sun component gives you the NEXT sunrise, sunset etc.

I suppose it would be technically possible if you want to do all those calculations yourself in a template, but why - that is exactly what the sun2 integration does for you!

EDIT - So as I thought, I was doing something stupid. Initially it was not realizing that the some of the names for the configuration variables had changed, and then it was not noticing that the difference between “sensors” and “sensor”. Once I made that change, it seems to be working as expected (still to be further tested). Anyways, I posted the code below in the hopes that maybe it will help someone else. To note, this is to go into a separate templates.yaml file.

# pnbruckner's sensor component as a template.
# https://github.com/pnbruckner/ha-illuminance/blob/master/custom_components/illuminance/sensor.py
- sensor:
    - name: "Virtual Outdoor Illuminance"
      icon: "mdi:brightness-auto"
      unit_of_measurement: "lx"
      state: >
        {%- set factors = namespace(condition='',sun='') %}

        {#- Retrieve the current condition and normalize the value #}
        {%- set current_condition = states("weather.YOUR_PROVIDER") %}
        {%- set current_condition = current_condition|lower|replace("partly cloudy w/ ","")|replace("mostly cloudy w/ ","")|replace("freezing","")|replace("and","")|replace(" ", "")|replace("-", " ")|replace("_", " ")|replace("(","")|replace(")","") %}
        {#- Assign a seemingly arbitrary number to the condition factor #}
        {%- set condition_factors = {
          "10000": ("clear", "clearnight", "sunny", "windy", "exceptional"),
          "7500": ("partlycloudy", "partlysunny", "mostlysunny", "mostlyclear", "hazy", "hazysunshine", "intermittentclouds"),
          "2500": ("cloudy", "mostlycloudy"),
          "1000": ("fog", "rainy", "showers", "snowy", "snowyheavy", "snowyrainy", "flurries", "chanceflurries", "chancerain", "chancesleet", "drearyovercast", "sleet"),
          "200": ("hail", "lightning", "tstorms")
        } %}
        {%- for factor in condition_factors if current_condition in condition_factors[factor] %}
          {%- set factors.condition = factor %}
        {%- endfor %}
        {#- Compute Sun Factor #}
        {%- set right_now = states.sensor.time.last_updated.timestamp() %}
        {%- set sunrise = states("sensor.sunrise") | as_timestamp %}
        {%- set sunrise_begin = states("sensor.dawn") | as_timestamp %}
        {%- set sunrise_end = sunrise + (40 * 60) %}
        {%- set sunset = states("sensor.sunset") | as_timestamp %}
        {%- set sunset_begin = sunset - (40 * 60) %}
        {%- set sunset_end = states("sensor.dusk") | as_timestamp %}
        {%- if sunrise_end < right_now and right_now < sunset_begin %}
          {%- set factors.sun = 1 %}
        {%- elif sunset_end < right_now or right_now < sunrise_begin %}
          {%- set factors.sun = 0 %}
        {%- elif right_now <= sunrise_end %}
          {%- set factors.sun = (right_now - sunrise_begin) / (60*60) %}
        {%- else %}
          {%- set factors.sun = (sunset_end - right_now) / (60*60) %}
        {%- endif %}
        {%- set factors.sun = 1 if factors.sun > 1 else factors.sun %}
        {# Take an educated guess #}
        {%- set illuminance = (factors.sun|float * factors.condition|float) | round %}
        {%- set illuminance = 10 if illuminance < 10 else illuminance %}
        {{ illuminance }}

ORIGINAL Post- "Thank you for the reply, I appreciate it. Apologies, but I have another question I’d like to pose.

I’ve installed Sun2 without issue and am trying to get this sensor to function. I’ve previously broken out config.yaml into a sensors.yaml and a template.yaml. I was under the impression that the “new” way of doing things with home assistant was to not use “platform:template”, but perhaps I misunderstood.

In any case, when I try to copy the code into either the templates or sensors.yaml file, it fails when I check my config. I know I must be doing something stupid, but I can’t figure out what… any help or guidance would be appreciated."


There is a newer method for instantiating template sensors, but the “old” method still works so that is not your issue. Can you post the relevant code? It’s hard to guess where you went wrong without seeing your code.

You can also have a look at my config to see how I have split my configuration up if you want to try and work your way through it yourself. For example, in my configuration.yaml file I have this line which points to a folder that contains all the files that define all my template sensors (these are “new” style templates).

template: !include_dir_merge_list templates

Example: /config/templates/template_spa.yaml
- binary_sensor:
    - name: "Balboa Connected"
      unique_id: balboa_connected
      icon: "{{ 'mdi:spa' if is_state('binary_sensor.balboa_connected','on') else 'mdi:spa-outline' }}"
      device_class: connectivity
        minutes: 30
      state: "{{ not states('climate.spa')|lower in ['unknown','unavailable','none'] }}"

    - name: "Balboa Connected Alert"
      unique_id: balboa_connected_alert
      icon: mdi:spa
      state: >
        {{ is_state('binary_sensor.balboa_connected','off')
            and is_state('input_boolean.spa_alerts','on')
            and is_state('input_boolean.startup_pending','off') }}

    - unique_id: spa_low_temperature_alert
        minutes: 15 # let temp stabalize, avoid false alerts
      state: >
        {{ states('sensor.current_temperature')|lower not in ['unknown','unavailable','none']
            and states('sensor.spa_temperature')|int(0) < states('input_number.spa_low_temperature_threshold')|int(0)
            and is_state('input_boolean.spa_alerts','on')
            and is_state('input_boolean.startup_pending','off') }}

- sensor:
    - name: "Spa Temperature"
      unique_id: spa_temperature
      icon: mdi:thermometer
      device_class: temperature
      unit_of_measurement: '°F'
      state: "{{ state_attr('climate.spa','current_temperature') }}"
      availability: "{{ state_attr('climate.spa','current_temperature')|lower not in ['unknown','unavailable','none'] }}"

    - name: "Spa Target Temperature"
      unique_id: spa_target_temperature
      icon: mdi:thermometer
      device_class: temperature
      unit_of_measurement: '°F'
      state: "{{ state_attr('climate.spa','temperature') }}"
      availability: "{{ state_attr('climate.spa','temperature')|lower not in ['unknown','unavailable','none'] }}"

Pay attention to the difference between !include_dir_named and !include_dir_merge_list if you choose to use a similar file structure as I have used in my config. It is easy to get tripped up on that detail.

BTW: You probably should have posted your new question in a new thread so we don’t pollute this thread for the illuminance sensor with unrelated posts.

Thank you for the guidance, I really appreciate it. I had thought of posting a new thread, but figured that it may help some other newb who is trying to figure out the same thing. If the mod team wants to move it, that’s more than ok otherwise I’ll edit my post if/when I find the problem :slight_smile:


This template sensor seems great! However, I can’t get it to work. I just get the sensor as “Unavailable” in HA.

Sun2 is working as it should. I have added the template sensor code to my configuration.yaml, but I’m not sure about the “weather.santee”. Shall that be replaced by the current weather provider, like weather.roskildegatan, as in my case. “weather.roskildegatan” is working fine for another template sensor I use.

Thanks in advance!
Screenshot of configuration.yaml

Absolutely correct!

Hmm. Strange. Then my code should be correct, but the sensor is unavailable. Any ideas based on my screenshot?

You have too many spaces in your code. YAML is very particular about spacing.