💡 Adaptive Lighting Custom Component that automatically adapts the brightness and color of your lights based on the sun's temperature but stops when you manually make a change

Tags: #<Tag:0x00007f3264d35340> #<Tag:0x00007f3264d35278> #<Tag:0x00007f3264d35048>

Adaptive Lighting component for Home Assistant

Try out this code by adding :star: https://github.com/basnijholt/adaptive-lighting :star: to your custom repos in HACS and install it!
This custom_component is also being added to core, see this PR, although it might take a looooooong time before it makes it in.

The adaptive_lighting platform changes the settings of your lights throughout the day.
It uses the position of the sun to calculate the color temperature and brightness that is most fitting for that time of the day.
Scientific research has shown that this helps to maintain your natural circadian rhythm (your biological clock) and might lead to improved sleep, mood, and general well-being.

In practical terms, this means that after the sun sets, the brightness of your lights will decrease to a certain minimum brightness, while the color temperature will be at its coolest color temperature at noon, after which it will decrease and reach its warmest color at sunset.
Around sunrise, the opposite will happen.

Additionally, the integration provides a way to define and set your lights in “sleep mode”.
When “sleep mode” is enabled, the lights will be at a minimal brightness and have a very warm color.

The integration creates 4 switches (in this example the component’s name is "living_room"):

  1. switch.adaptive_lighting_living_room, which turns the Adaptive Lighting integration on or off. It has several attributes that show the current light settings.
  2. switch.adaptive_lighting_sleep_mode_living_room, which when activated, turns on “sleep mode” (you can set a specific sleep_brightness and sleep_color_temp).
  3. switch.adaptive_lighting_adapt_brightness_living_room, which sets whether the integration should adapt the brightness of the lights (if supported by the light).
  4. switch.adaptive_lighting_adapt_color_living_room, which sets whether the integration should adapt the color of the lights (if supported by the light).

This integration was originally based on the great work of @claytonjn https://github.com/claytonjn/hass-circadian_lighting, but has been 100% rewritten and extended with new features.

Taking back control

Although having your lights automatically adapt is great most of the time, there might be times at which you want to set the lights to a different color/brightness and keep it that way.
For this purpose, the integration (when take_over_control is enabled) automatically detects whether someone (e.g., person toggling the light switch) or something (automation) changes the lights.
If this happens and the light is already on, the light that was changed gets marked as “manually controlled” and the Adaptive Lighting component will stop adapting that light until it turns off and on again (or if you use the service call adaptive_lighting.set_manual_control).
This mechanism works by listening to all light.turn_on calls that change the color or brightness and by noting that the component did not make the call.
Additionally, there is an option to detect all state changes (when detect_non_ha_changes is enabled), so also changes to the lights that were not made by a light.turn_on call (e.g., through an app or via something outside of Home Assistant.)
It does this by comparing a light’s state to Adaptive Lighting’s previously used settings.
Whenever a light gets marked as “manually controlled”, an adaptive_lighting.manual_control event is fired, such that one can use this information in automations.

Configuration

This integration is both fully configurable through YAML and the frontend. (Configuration -> Integrations -> Adaptive Lighting, Adaptive Lighting -> Options)
Here, the options in the frontend and in YAML have the same names.

# Example configuration.yaml entry
adaptive_lighting:
  lights:
    - light.living_room_lights

Options

option description required default type
name The name to use when displaying this switch. False default string
lights List of light entities for Adaptive Lighting to control (may be empty). False list []
prefer_rgb_color Whether to use RGB color adjustment instead of native light color temperature. False False boolean
initial_transition How long the first transition is when the lights go from off to on (or when “sleep mode” is toggled). False 1 time
transition How long the transition is when the lights change, in seconds. False 45 integer
interval How often to adapt the lights, in seconds. False 90 integer
min_brightness The minimum percent of brightness to set the lights to. False 1 integer
max_brightness The maximum percent of brightness to set the lights to. False 100 integer
min_color_temp The warmest color temperature to set the lights to, in Kelvin. False 2000 integer
max_color_temp The coldest color temperature to set the lights to, in Kelvin. False 5500 integer
sleep_brightness Brightness of lights while the sleep mode is enabled. False 1 integer
sleep_color_temp Color temperature of lights while the sleep mode is enabled. False 1000 integer
sunrise_time Override the sunrise time with a fixed time. False time
sunrise_offset Change the sunrise time with a positive or negative offset. False 0 time
sunset_time Override the sunset time with a fixed time. False time
sunset_offset Change the sunset time with a positive or negative offset. False 0 time
only_once Whether to keep adapting the lights (false) or to only adapt the lights as soon as they are turned on (true). False False boolean
take_over_control If another source calls light.turn_on while the lights are on and being adapted, disable Adaptive Lighting. False True boolean
detect_non_ha_changes Whether to detect state changes and stop adapting lights, even not from light.turn_on. Needs take_over_control to be enabled. Note that by enabling this option, it calls ‘homeassistant.update_entity’ every ‘interval’! False False boolean
separate_turn_on_commands Whether to use separate light.turn_on calls for color and brightness, needed for some types of lights False False boolean

Full example:

# Example configuration.yaml entry
adaptive_lighting:
- name: "default"
  lights: []
  prefer_rgb_color: false
  transition: 45
  initial_transition: 1
  interval: 90
  min_brightness: 1
  max_brightness: 100
  min_color_temp: 2000
  max_color_temp: 5500
  sleep_brightness: 1
  sleep_color_temp: 1000
  sunrise_time: "08:00:00"  # override the sunrise time
  sunrise_offset:
  sunset_time:
  sunset_offset: 1800  # in seconds or '00:15:00'
  take_over_control: true
  detect_non_ha_changes: false
  separate_turn_on_commands: false
  only_once: false

Services

adaptive_lighting.apply applies Adaptive Lighting settings to lights on demand.

Service data attribute Optional Description
entity_id no The entity_id of the switch with the settings to apply.
lights no A light (or list of lights) to apply the settings to.
transition yes The number of seconds for the transition.
adapt_brightness yes Whether to change the brightness of the light or not.
adapt_color yes Whether to adapt the color on supporting lights.
prefer_rgb_color yes Whether to prefer RGB color adjustment over of native light color temperature when possible.
turn_on_lights yes Whether to turn on lights that are currently off.

adaptive_lighting.set_manual_control can mark (or unmark) whether a light is “manually controlled”, meaning that when a light has manual_control, the light is not adapted.

Service data attribute Optional Description
entity_id no The entity_id of the switch in which to (un)mark the light as being “manually controlled”.
lights no A light (or list of lights) to apply the settings to.
manual_control no Whether to mark (true) or unmark (false) the light as “manually controlled”, when not specified it selects all lights in the switch.

Automation examples

Reset the manual_control status of a light after an hour.

- alias: "Adaptive lighting: reset manual_control after 1 hour"
  mode: parallel
  trigger:
    platform: event
    event_type: adaptive_lighting.manual_control
  variables:
    light: "{{ trigger.event.data.entity_id }}"
    switch: "{{ trigger.event.data.switch }}"
  action:
    - delay: "01:00:00"
    - condition: template
      value_template: "{{ light in state_attr(switch, 'manual_control') }}"
    - service: adaptive_lighting.set_manual_control
      data:
        entity_id: "{{ switch }}"
        lights: "{{ light }}"
        manual_control: false

Toggle multiple Adaptive Lighting switches to “sleep mode” using an input_boolean.sleep_mode.

- alias: "Adaptive lighting: toggle 'sleep mode'"
  trigger:
    - platform: state
      entity_id: input_boolean.sleep_mode
    - platform: homeassistant
      event: start  # in case the states aren't properly restored
  variables:
    sleep_mode: "{{ states('input_boolean.sleep_mode') }}"
  action:
    service: "switch.turn_{{ sleep_mode }}"
    entity_id:
      - switch.adaptive_lighting_sleep_mode_living_room
      - switch.adaptive_lighting_sleep_mode_bedroom

Other

See the documentation of the PR at https://deploy-preview-14877--home-assistant-docs.netlify.app/integrations/adaptive_lighting/ and this video on Reddit to see how to add the integration and set the options.
The was already another topic on the forums (New Adaptive Lighting Integration) by @dbrunt. I opened a new one to be able to control the first post with docs.

Having problems?

Please enable debug logging by putting this in configuration.yaml:

logger:
  default: warning
  logs:
    custom_components.adaptive_lighting: debug

and after the problem occurs please create an issue with the log (/config/home-assistant.log).

Graphs!

These graphs were generated using the values calculated by the Adaptive Lighting sensor/switch(es).

Sun Position:

Color Temperature:

Brightness:

17 Likes

Already using this for quite some time now, it’s awesome. I can highly recommend it.

Thanks a lot for your work on this! Highly appreciated!

2 Likes

What about this option?

separate_turn_on_commands
For each attribute (color, brightness) in light_turn_on or light.toggle. Required for some lights.
required - False
default - False
type - boolean

I don’t see it in the post above…

1 Like

Thanks for noting its absence, I’ve added this option in the first post.

@basnijholt How are you generating those graphs? I only see the 4 switches after setting it up. Would love to see the ‘logic’ etc so I can get a feel for how it’s working.

I have a question, which I recently posted to circadian lighting component thread.

What is the motivation to make brightness dimming out up to midnight and then immediatelly dim in again, making light bright hours before sunrise?

By definition found on Internet, light should stay at lowest intensity until sunrise

You could add template sensors like I did here.

Then either plot it using Grafana or using this card (download the custom cards first):

cards:
  - color_thresholds_transition: smooth
    entities:
      - entity: sensor.adaptive_lighting_brightness
        name: Brightness
        show_state: true
      - entity: sensor.adaptive_lighting_color_temp_kelvin
        name: Color temp (Kelvin)
        show_state: true
        y_axis: secondary
    font_size: 80
    group: false
    name: Brightness vs color temp
    hours_to_show: 72
    hour24: true
    line_width: 2
    points_per_hour: 2
    lower_bound: 60
    upper_bound: 105
    lower_bound_secondary: 0
    upper_bound_secondary: ~5500
    show:
      extrema: false
      fill: fade
      labels: false
      name: true
      state: true
    type: 'custom:mini-graph-card'
  - color_thresholds_transition: smooth
    entities:
      - entity: sensor.adaptive_lighting_brightness
        name: Brightness
        show_state: true
      - entity: sensor.adaptive_lighting_sun_position
        name: Sun position
        show_state: true
        y_axis: secondary
    font_size: 80
    group: false
    name: Brightness vs sun position
    hours_to_show: 72
    hour24: true
    line_width: 2
    points_per_hour: 2
    lower_bound: 40
    upper_bound: 100
    lower_bound_secondary: -100
    upper_bound_secondary: 100
    show:
      extrema: false
      fill: fade
      labels: false
      name: true
      state: true
    type: 'custom:mini-graph-card'
  - color_thresholds_transition: smooth
    entities:
      - entity: sensor.adaptive_lighting_color_temp_kelvin
        name: Color temp
        show_state: true
      - entity: sensor.adaptive_lighting_sun_position
        name: Sun position
        show_state: true
        y_axis: secondary
    font_size: 80
    group: false
    hours_to_show: 72
    hour24: true
    name: Color temp vs sun position
    line_width: 2
    points_per_hour: 2
    lower_bound: 0
    upper_bound: ~5500
    lower_bound_secondary: -100
    upper_bound_secondary: 100
    show:
      extrema: false
      fill: fade
      labels: false
      name: true
      state: true
    type: 'custom:mini-graph-card'
type: 'custom:swipe-card'

Result:
card

1 Like

Is it possible to directly deactivate adaptive lighting if the lights were turned on by the hue_activate_scene service?

first of all thank you for the amazing work. started to use the addon, couldn’t wait till it is added officially.
just a little feedback; can you make the reported brightness and RGB numbers integers and maybe fewer decimal points for the sun position? it will make reading this info much easier and easier for the eye

Currently not. This is an open feature request: https://github.com/basnijholt/adaptive-lighting/issues/12

1 Like

your component works fine on my Shelly Duo bulbs via Shelly binding, but not on my Tasmota flashed ones. The difference seems to be that the Tasmota firmware wants CT, not kelvin. Can you make forcing CT an option? Circadian lighting component can do this.

thanks

1 Like

Hi,
it would be cool if we could use something like variables in the config, so we do not have to change the min_brightness or sunset_offset for example. In case we have a couple of switches that share the same values. i mean we could use an input_text or an input number (is this even allowed? edit: templates are not allowed i guess…), but it seems a bit overkill.

solution: using secrets

This looks really cool, I’m anxious to try it out, but when I add the repository link to Supervisor -> Add On Store - Repositories I always get “Invalid Add-on respository”.

Being new to this, just wondering what type of lights this works best with? Is this intended for tunable bulbs such as IKEA and Hue or can it also be used for RGB bulbs and light strips?

This Integration is not an Add-on, it’s a custom_component. Better use HACS.

Would it be possible to add a time when the light level control would start? I’d like the color to change after sunset but I don’t want it to already be so dark at 6pm.

+1 an offset parameter would be great, it does get a little dark a little quickly.

also would love to set up groups or multiple instances, as I have some indirect lights which get too dim compared to the direct overhead lights.

otherwise it’s a FANTASTIC project!

That’s already possible you can have as many instances as you like.