Outdoor illuminance estimated from weather conditions

I’m not sure if you have tried this yet, but I was able to comment out all of the lines that had to deal with yr from the sensor.py file, and everything is working for me again. I already have Dark Sky integration working, so that’s why it is working for me.

Here’s the paste of the sensor.py if you need it: https://pastebin.com/Gk39fQ0b

I tried adding the met integration and then changing the sensors in the script from yr to met, but that didn’t work. So just commenting out the yr lines worked.

2 Likes

You can get the yr sensors back https://github.com/Danielhiversen/home_assistant_weather_data

1 Like

Thanks for this. Very helpful. I will definitely use this option if my current plan fails.

I’m in the process of modifying @pnbruckner’s python script to use open weather map. I was going to just include a open weather map (OWM) sensor in my config but I believe that sensor calls the four hour summary. I’m not sure that will produce fine enough results.

Instead I’ve created a rest sensor that calls the OWM API for current cloud percentage every 15 minutes, the free api account allows 60 calls per minute. I’m thinking cloud percentage is the best attribute to monitor for estimated ambient luminance. All weather conditions that affect light levels involve clouds right? I’m assuming fog counts towards cloudiness data??

An API call for current conditions at my longitude and latitude returns this:

{"coord":{"lon":MY_LONG,"lat":MY_LAT},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"base":"stations","main":{"temp":291.92,"feels_like":291.75,"temp_min":290.93,"temp_max":293.15,"pressure":1020,"humidity":80},"visibility":10000,"wind":{"speed":2.68,"deg":13,"gust":7.15},"clouds":{"all":2},"dt":1600599431,"sys":{"type":3,"id":2021002,"country":"GB","sunrise":1600580794,"sunset":1600625214},"timezone":3600,"id":2641429,"name":"MY_COUNTY","cod":200}

I used this in a rest sensor and extracted the cloud percentage

- platform: rest
  # open weather map api call to my longitude and latitude for current weather conditions
  resource: http://api.openweathermap.org/data/2.5/weather?lat=MY_LATITUDE&lon=MY_LONGITUDE&appid=MY_SUPER_SECRET_API_KEY
  name: OWM Clouds
  # template to extract the cloud percentage 
  value_template: '{{ value_json["clouds"]["all"] }}'
  unit_of_measurement: '%'
  # check every 15mins (open weather map updates every ten minutes)
  # free api account allows 60 calls per minute
  scan_interval: 900

Does anyone know if adding this line in @pnbruckner’s python script will return the OWM rest sensor’s state correctly?

from homeassistant.components.darksky.sensor import (
    ATTRIBUTION as DSS_ATTRIBUTION)
    
# I added this line to import cloud percentage from a rest sensor polling OWM API
from homeassistant.components.rest.sensor.owm_cloudiness import ATTRIBUTION as OWM_ATTRIBUTION

from homeassistant.components.yr.sensor import ATTRIBUTION as YRS_ATTRIBUTION

I will then add an extra mapping section for OWM in the script.

If I wanted to migrate to weather_data how would I import that into @pnbruckner’s script, would this work?

from homeassistant.components.weather_data.sensor import ATTRIBUTION as YRS_ATTRIBUTION

I guess this would work without any other modification of the script as weather_data sensor returns the yrs symbol just like the yrs sensor did.

Thought whilst we’re all here you might want to see a template sensor I made to convert the lux value returned by this script into a brightness value to be used by light.turn_on automations. The template also has a sunrise and sunset start time and duration options which fade up/down respectively the brightness value. The template will ramp down to a minimum user defined night time level.

The idea is that after morning has started the lights will fade up over the sunrise period until maximum brightness reached. As sunlight increases brightness drops, more sunlight, less electric light needed. In the evening sun fades, electric lighting increases. There comes a point in the evening though where you want the lights to drop down so you’re not in a fully lit room just before bedtime. The user can specify a night time and also a sunset duration. This starts to fade down the brightness to the user defined night time dimmed value.

I have room occupancy detection so if you’re in a room and the weather becomes overcast or the sun begins to set my lights automatically maintain a comfortable lux level. Never touch a light switch again!

I’ve made a little control panel :

Here’s the template sensor code, any tips and comments very much apprerciated:

- platform: template
  sensors:
    dynamic_brightness:
      friendly_name: dynamic brightness
      value_template: >-
        {# start of brightness calculations due to weather sensor data #}

        {# if sunlight higher than user defined maximum then dynamic brightness must be zero #}
        {% if states('input_number.high_ambient_lux') | float < states('sensor.yrs_illuminance') | float %}
        
          {% set dynamic_brightness = 0 | float %}     
         
        {# if sunlight lower than user defined minimum then dynamic brightness must be user defined maximum brightness #}
        {% elif states('sensor.yrs_illuminance') | float < states('input_number.low_ambient_lux') | float %}

          {% set dynamic_brightness = states('input_number.max_brightness') | float %}   
          
        {# sunlight levels somewhere in between high and low thresholds so calculate brightness needed to maintain light levels #} 
        {% else %}
        
          {% set illuminance = states('sensor.yrs_illuminance') | float %}
          {% set low_lux = states('input_number.low_ambient_lux') | float %}
          {% set high_lux = states('input_number.high_ambient_lux') | float %}
          {% set max_bright = states('input_number.max_brightness') | float %}
          {% set min_bright = states('input_number.min_brightness') | float %}
          
          {# map illuminance sensor lux value to brightness value between minimum and maximum user defined limits #}
          {% set dynamic_brightness = (((illuminance - high_lux) * (max_bright - min_bright )) / (low_lux - high_lux )) + min_bright %}
              
        {% endif %}
        
        {# end of brightness calculations #}
        

        {# list of variables required by sunrise, sunset and night time routines below #}
        
        {# convert time sensor and input_datetime values into minutes to simplify comparisons #}
        {% set time_now_to_minutes = states('sensor.time')[:2] | int * 60 + states('sensor.time')[3:] | float  %}
        {% set night_start_time_to_minutes = states('input_datetime.nighttime')[:2] | float * 60 + states('input_datetime.nighttime')[3:] | float %}
        {% set morning_start_time_to_minutes = states('input_datetime.daytime')[:2] | float * 60 + states('input_datetime.daytime')[3:] | float %}
        {% set sunrise_duration_to_minutes = states('input_datetime.sunrise_duration')[:2] | float * 60 + states('input_datetime.sunrise_duration')[3:5] | float %}
        {% set sunset_duration_to_minutes = states('input_datetime.sunset_duration')[:2] | float * 60 + states('input_datetime.sunset_duration')[3:5] | float %}

        {# shorten descriptive variable names so I don't have to type long names #} 
        {% set time = time_now_to_minutes %}
        {% set night = night_start_time_to_minutes %}
        {% set morning = morning_start_time_to_minutes %}
        {% set sunrise = sunrise_duration_to_minutes %}
        {% set sunset = sunset_duration_to_minutes %}
        
        {# convert user defined dimming strength into percentile and apply to dynamic_brightness #}
        {% set dim_percent = states('input_number.night_dim_strength') | float / 100.0 %}
        {% set night_brightness = dynamic_brightness - (dynamic_brightness * dim_percent) %}
        
        {# end of variables list #}
        
      
      
        {# this part applies night time dimming to reduce dynamic brightness down to a comfortable value at user defined night time #}

        {# if current time is past 'night' time plus 'sunset' duration or current time less than 'morning' time then set full night dimming strength #}
        {% if (time >= (night + sunset)) or (time <= morning) %}
        
          {# apply full night dimming strength to brightness #}
          {% set dynamic_brightness = night_brightness %}
        
        {# sunset routine #}  
        {# if current time is within 'sunset' period apply a gradually increasing level of dimming #}
        {% elif (night < time) and (time < (night + sunset)) %}
        
          {# go from no dimming to full night dimming strength as 'sunset' progresses #}
          {% set dynamic_brightness = ((time - night) * (night_brightness - dynamic_brightness)) / ((night + sunset) - night) + dynamic_brightness %}
          
        {# sunrise routine #}
        {# if current time is within 'sunrise' period apply a gradually decreasing level of dimming #}
        {% elif (morning < time) and (time < (morning + sunrise)) %}
        
          {% set dynamic_brightness = ((time - morning) * (dynamic_brightness - night_brightness)) / ((morning + sunrise) - morning) + night_brightness %}
          
        {% endif %}
        

        {{ dynamic_brightness | round(0) | int }}
1 Like

If you’re wondering what the dynamic color temp setting is, thats just a time based sensor that cools my lights at midday and warms them in the evenings. fades down in the morning, up in the afternoon.

- platform: template
  sensors:
    # this sensor returns 154-370 mireds (cool to warm) dependent on time of day
    # if time is after night and before morning return warm (370)
    # if time after morning and before midday ramp down from warm to cool (370-154)
    # if time after midday and before night ramp up from cool to warm (154-370)
    dynamic_color_temp:
      friendly_name: dynamic color temp 154-370
      value_template: >-
        {# convert time sensor and input_datetime values into minutes to simplify comparisons #}
        {% set time_now_to_minutes = states('sensor.time')[:2] | int * 60 + states('sensor.time')[3:] | float  %}
        {% set night_start_time_to_minutes = states('input_datetime.nighttime')[:2] | float * 60 + states('input_datetime.nighttime')[3:] | float %}
        {% set morning_start_time_to_minutes = states('input_datetime.daytime')[:2] | float * 60 + states('input_datetime.daytime')[3:] | float %}
        {% set midday = 12 * 60 %}
        {% set midnight = 0 %}
        {% set cool = 154 %}
        {% set warm = 370 %}
        
        {# shorten descriptive variable names so I don't have to type long names #} 
        {% set time = time_now_to_minutes %}
        {% set night = night_start_time_to_minutes %}
        {% set morning = morning_start_time_to_minutes %}
        
        {# set maximum warm during night time #}
        {% if (time <= morning) or (time >= night) %}
          {% set dynamic_color_temp = 370 %}
        
        {# ramp from warm to cool from morning to midday #}
        {% elif time <= midday %}
          {% set dynamic_color_temp = (time - morning) * (cool - warm) / (midday - morning) + warm %}

        {# ramp from cool to warm from midday to night #}    
        {% elif time < night %}
          {% set dynamic_color_temp = (time - midday) * (warm - cool) / (night - midday) + cool %}
        
        {% endif %}
        
        {{ dynamic_color_temp | int }}

sorry for going way off topic but I’m super chuffed with myself! Estimated sunlight has helped me go light switch free and It’s awesome sitting in a room and not noticing the sun has gone down because the lights have taken over pretty seamlessly!

1 Like

Hi, the link gives a 404 error, but for those looking for it, the change are pretty basic, search the file sensor.py for lines with ‘yr’ in them and comment them out (’#’ in front of the line). Restart HA and you’re good to go.

That doesn’t help people using the yr platform. Darsky and Weather Underground aren’t an option for new users. @Danielhiversen’s solution above works for me as a drop in replacement for YR.

@Danielhiversen I’ve manually installed you custom component but get this error

Platform error sensor.weather_data - cannot import name 'PERCENTAGE' from 'homeassistant.const' (/usr/src/homeassistant/homeassistant/const.py)

any idea what I’ve got wrong?

I can’t see PERCENTAGE in https://github.com/NAStools/homeassistant/blob/7ca1180bd42713f2d77bbc3f0b27b231ba8784aa/homeassistant/const.py
so I’ve removed it from the import and then just decalared
PERCENTAGE = ‘%’

the read me gives an example sensor config of

# Example configuration.yaml entry
sensor:
  - platform: weather

mine is


sensor:
  - platform: weather_data

which gives me a sensor named yr_symbol

Thanks. I had I marked as private, it’s fixed now.

OK, got the script illuminance.py working and have successfully upgraded HA to 0.115.2
I’ve used the weather_data custom component as a replacement for the yr component.

I got an error when installing weather_data custom component. I removed PERCENTAGE from the imports in weather_data’s sensor.py file and just added the line

PERCENTAGE = '%'

on its own line.

in my sensor.yaml file I added

- platform: weather_data

- platform: illuminance
  name: YRS Illuminance
  entity_id: sensor.yr_symbol

and finally in the custom component illuminance sensor.py file I had to remove the yr import line and add an import from custom_component weather_data

# line below added to use custom component weather_data for yr sensor data
from custom_components.weather_data.sensor import ATTRIBUTION as YRS_ATTRIBUTION
# i removed the line below because yr platform has been removed
# from homeassistant.components.yr.sensor import ATTRIBUTION as YRS_ATTRIBUTION

I think it’s working, I have no errors in the log. It’s dark now so I’ll find out in the morning. One thing I’ve noticed is the sensor.yrs_illuminance is reading 10lx even though it’s pitch black outside. Not sure but I thought it used to read zero at night.

nice coding :wink:

how do you automate this? what the automation triggering both sensors into a service? If you’d share that would be appreciated :wink:
thanks!

Some of you might be interested in a template sensor version of this code I worked on today. I don’t know that it works 100% yet, but I think it is working pretty well so far! :slight_smile:

1 Like

I could talk about my light switch free smart home all day, happy to share.

I have installed AM312 mini pirs throughout my apartment. I have loft access which has been a God send. The AM312s are connected to my HA Pi via GPIO. Each door in my home has a pair of PIRs, one either side of the door. I have removed the lens from the PIRs and they peer through a 4mm hole in the ceiling, creating a 99 pence ‘laser’ tripwire. As you walk through a door you trip one sensor then the other. Direction of travel can be determined by the order that the PIR pair trigger. An input number is incremented for the room you entered, an input_number is decremented for the room you left. Everyone is well aware how the system works, so no messing about in doorways. If you change your mind about walking into a room you must continue through the door and wait one second before turning around! A small price to pay.

This system means you can be motionless in a room reading a book and the weather suddenly gets gloomy or the sun begins to set and the system will know to light the room just enough for you. You can set a night time dimming level so after a user defined time an artificial sunset will gradually dim down the now full power lights (because the real sun set hours ago) to a comfortable value before bedtime.

The automations which turn on the lights are fairly straightforward. Three state triggers, room occupancy above zero, a change of dynamic brightness sensor, a change of dynamic color temp sensor. Conditions are applied to check the room is occupied and that it is dark enough outside to need lighting.

Every room’s automations are the same, count people in and out, turn on/adjust the lights, turn lights off. Here’s the autoamtions for my study which has MiLight RGB+CCT down lighters:

### STUDY ###
- id: turn on study lights
  alias: turn on study lights
  trigger:
  - platform: numeric_state
    entity_id: input_number.study_occupancy
    above: 0
  - platform: state
    entity_id: sensor.dynamic_brightness
  - platform: state
    entity_id: sensor.dynamic_color_temp
  condition:
  - condition: numeric_state
    entity_id: input_number.study_occupancy
    above: 0
  - condition: numeric_state
    entity_id: sensor.dynamic_brightness
    above: 0
  action:
  - service: light.turn_on
    data_template:
    entity_id: light.study_lights
      brightness: "{{ states('sensor.dynamic_brightness') | int }}"
      color_temp: "{{ states('senosr.dynamic_color_temp') | int }}"
      transition: 1
      
- id: turn off study lights
  alias: turn off study lights
  trigger:
  - entity_id: input_number.study_occupancy
    platform: numeric_state
    below: 1
  action:
  - service: light.turn_off
    entity_id: light.study_lights
    transition: 1

- id: someone entered the study
  alias: someone entered the study
  trigger:
  - platform: state
    entity_id: binary_sensor.pir_study
    from: 'off'
    to: 'on'
  condition:
  - condition: state
    entity_id: binary_sensor.pir_hall_study
    state: 'on'
  action:
  - service: input_number.increment
    entity_id: input_number.study_occupancy
  - service: input_number.decrement
    entity_id: input_number.hall_occupancy
    
- id: someone left the study
  alias: someone left the study
  trigger:
  - platform: state
    entity_id: binary_sensor.pir_hall_study
    from: 'off'
    to: 'on'
  condition:
  - condition: state
    entity_id: binary_sensor.pir_study
    state: 'on'
  action:
  - service: input_number.increment
    entity_id: input_number.hall_occupancy
  - service: input_number.decrement
    entity_id: input_number.study_occupancy

I’m so glad I’m not the only one with crazy template sensors! I wasn’t sure if it was a good thing to do but I haven’t noticed any issues so far. If It’s ok with you I might use your template and see if adding my new open weather map cloudiness percentage sensor as the base for illuminance estimation returns finer results. I’m no Python programmer.

2 Likes

@telecomguy

Hi all. Sorry I haven’t been able to work on this for a while. I made a couple releases today that should hopefully resolve the problems:

2.1.1 – Properly handles the case when the darksky integration isn’t loaded (e.g., you’re not using it in your configuration.)
2.2.0 – Properly handles the YR integration being removed starting with HA release 0.115, and adds support for the met integration.

1 Like

Which attributes of weather.home does this require? I have a different weather platform and maybe it works as well?

Unfortunately it’s not that generic. It looks at the entity’s attribution attribute to decide which integration it is and hence how to interpret its state and how to translate that to a number. It’s not all that hard to modify the code to add support for new integrations, but it’s not generic enough to do it simply via configuration parameters.

1 Like

That’s a shame.

What weather platform would you like to use with this? FYI, based on some other input I’ve received I’m currently working on adding support for AccuWeather & ecobee (which is pretty much done.)

Released 2.3.0 which adds support for AccuWeather & ecobee.