renemarc/home-assistant-config

View on GitHub
automations/cct_limitlessled.yaml

Summary

Maintainability
Test Coverage
#
# Adjust LimitlessLED/MiLight colour temperature (CCT) based on cyrcadian rythm
#
# White temperature is based on a custom curve, not f.lux nor using the sun,
# otherwise it would be kind of gloomy to see warm yellows a lot in winter time.
#
# Todo: User-configurable targets and hours?
#
# Milight FUT035 controllers colour range:
#   Mireds: 154 - 500
#   Kelvins: 6500K - 2000K
#
# @subscribe input_boolean.kitchen_lights_override
# @subscribe input_boolean.night_mode
# @subscribe input_boolean.scene_mode
#
# @link https://community.home-assistant.io/t/lifx-circadian-rhythm-time-of-day-automation/23091
# @link https://sigmaluminous.com/the-circadian-rhythm-and-color-temperature/
#
- id: cct_limitlessled
  alias: "CCT LimitlessLED"

  trigger:
    # Every five minutes.
    - platform: time_pattern
      minutes: '/5'
      seconds: 00

    # When an item which was turned off or disconnected comes back online.
    - platform: state
      entity_id:
        - light.countertop
        - light.island
        - light.microwave
        - light.sink
      to: 'on'

    # When night mode is disabled.
    - platform: state
      entity_id: input_boolean.night_mode
      to: 'off'

    # When scenes are disabled.
    - platform: state
      entity_id: input_boolean.scene_mode
      to: 'off'

    # When kitchen lights override has been set a while ago already.
    - platform: state
      entity_id: input_boolean.kitchen_lights_override
      to: 'on'
      for:
        minutes: 20

  condition:
    # If at least one light is on.
    - condition: state
      entity_id: group.kitchen_lights
      state: 'on'

    # If no scenes are enabled.
    - condition: state
      entity_id: input_boolean.scene_mode
      state: 'off'

    # It's not during the night mode.
    - condition: state
      entity_id: input_boolean.night_mode
      state: 'off'

  action:
    # Set light colour and brightness.
    - service: light.turn_on
      data_template:
        entity_id: >-
          {% set entities = '' %}
          {% if is_state('light.countertop', 'on') %}
            {% set entities = entities + 'light.countertop ' %}
          {% endif %}
          {% if is_state('light.island', 'on') %}
            {% set entities = entities + 'light.island ' %}
          {% endif %}
          {% if is_state('light.microwave', 'on') %}
            {% set entities = entities + 'light.microwave ' %}
          {% endif %}
          {% if is_state('light.sink', 'on') %}
            {% set entities = entities + 'light.sink ' %}
          {% endif %}

          {{ entities | trim | replace(' ',',') }}
        color_temp: >-
          {% set schedule = {
            '00': 3000,
            '01': 2750,
            '02': 2500,
            '03': 2000,
            '04': 2500,
            '05': 2750,

            '06': 3000,
            '07': 3500,
            '08': 4000,
            '09': 4500,
            '10': 5000,
            '11': 5500,

            '12': 6000,
            '13': 6000,
            '14': 6000,
            '15': 6000,
            '16': 6000,
            '17': 5500,

            '18': 5000,
            '19': 4500,
            '20': 4000,
            '21': 3500,
            '22': 3500,
            '23': 3500,
          } %}
          {% set hour = now().hour %}
          {% set next = hour + 1%}
          {% if next > 23 %}
            {% set next = 0 %}
          {% endif %}
          {% set hour = '%02d' | format(hour) %}
          {% set next = '%02d' | format(next) %}

          {% set current = schedule[hour] %}
          {% set target = schedule[next] %}
          {% set color_temp = current %}

          {% if current != target %}
            {% set delta = target - current %}
            {% set seconds = now().second + now().minute * 60 %}
            {% set diff = (seconds / 3600 * delta) | round(0) %}
            {% set color_temp = current + diff %}
          {% endif %}

          {% set color = (1000000 / color_temp) | round(0) %}

          {{ color }}
        brightness: >-
          {% set schedule = {
            '00': 50,
            '01': 50,
            '02': 45,
            '03': 40,
            '04': 35,
            '05': 40,

            '06': 45,
            '07': 50,
            '08': 60,
            '09': 70,
            '10': 80,
            '11': 90,

            '12': 100,
            '13': 100,
            '14': 100,
            '15': 100,
            '16': 95,
            '17': 85,

            '18': 80,
            '19': 75,
            '20': 70,
            '21': 65,
            '22': 60,
            '23': 55,
          } %}
          {% set hour = now().hour %}
          {% set next = hour + 1%}
          {% if next > 23 %}
            {% set next = 0 %}
          {% endif %}
          {% set hour = '%02d' | format(hour) %}
          {% set next = '%02d' | format(next) %}

          {% set current = schedule[hour] %}
          {% set target = schedule[next] %}
          {% set brightness_pct = current %}

          {% if current != target %}
            {% set delta = target - current %}
            {% set seconds = now().second + now().minute * 60 %}
            {% set diff = (seconds / 3600 * delta) | round(0) %}
            {% set brightness_pct = current + diff %}
          {% endif %}

          {% set brightness = (brightness_pct / 100 * 255) | round(0) %}

          {{ brightness }}