alias: Ashley’s Light Fader
description: >
  Fades a lamp over time. If you have any questions or comments about this
  script, feel free to tweet Ashley Bischoff at @FriendlyAshley. Released under
  the Apache 2.0 license. (v2.01)
fields:
  light:
    name: 💡 Light
    description: entity_id of the lamp.
    selector:
      entity:
        domain: light
    example: light.kitchen
    required: true
  lampBrightnessScale:
    name: The lamp’s internal brightness scale
    description: >
      Most lamps seem to internally use a 0 to 255 brightness scale, but some
      lamps internally use a 0% to 100% brightness scale. Either of these
      settings will still fade the lamp, but if you happen to match this setting
      to the lamp’s internal brightness scale, the resulting fade may be
      smoother. (If you’re not sure, you can just leave this as is.)
    advanced: true
    required: true
    selector:
      select:
        options:
          - label: 0% to 100%
            value: zeroToOneHundred
          - label: 0 to 255
            value: zeroToTwoFiftyFive
    default: zeroToTwoFiftyFive
  transitionTime:
    name: ⏱ Fade time
    description: Fade duration.
    selector:
      duration: null
    required: true
  easingTypeInput:
    name: 📉 Easing type
    description: >-
      The easing function that you’d like the fade to use. As a starting
      point—you can’t go wrong with any of the “Ease-In-Out X” easings as those
      will always look pretty good whenever you’re fading between two nonzero
      brightness values. As well, the “Ease-Out X” easings often tend to look
      good if you might be fading up very quickly from zero to another
      brightness, and the “Ease-In X” easings are mostly only included for
      completeness as those tend to only look good if you might be fading down
      to zero very quickly. (See also https://easings.net for visual demos of
      each of these easing types.)
    selector:
      select:
        mode: list
        options:
          - label: Try to automatically select the easing type
            value: auto
          - label: Ease-In-Out Sine [a good all-rounder]
            value: easeInOutSine
          - label: Ease-In-Out Quad
            value: easeInOutQuad
          - label: Ease-In-Out Cubic
            value: easeInOutCubic
          - label: Ease-In-Out Quart
            value: easeInOutQuart
          - label: Ease-Out Sine
            value: easeOutSine
          - label: Ease-Out Quad
            value: easeOutQuad
          - label: Ease-Out Cubic
            value: easeOutCubic
          - label: Ease-Out Quart
            value: easeOutQuart
          - label: Ease-In Sine
            value: easeInSine
          - label: Ease-In Quad
            value: easeInQuad
          - label: Ease-In Cubic
            value: easeInCubic
          - label: Ease-In Quart
            value: easeInQuart
          - label: Linear [somewhat unnatural to the human eye; not recommended]
            value: linear
    default: auto
    required: true
  endBrightnessPercent:
    name: End brightness level
    description: Percentage from 0 to 100 representing the final brightness level.
    selector:
      number:
        min: 0
        max: 100
        step: 1
        mode: slider
        unit_of_measurement: "%"
    default: 50
    example: "50"
    required: true
  endBrightnessEntity:
    name: (optional) Use an entity instead for the end-brightness value?
    description: >-
      You can optionally have the script ignore the end-brightness value above
      and instead use the numeric value of anonther entity that you select here,
      such as an input-number helper, an input-select helper, an input-text
      helper, or a numeric sensor.
    selector:
      entity:
        domain:
          - input_number
          - input_select
          - input_text
          - sensor
    example: input_number.dining_room_entertaining_level
    required: false
    advanced: true
  endBrightnessEntityScale:
    name: The end-brightness entity’s brightness scale (if used)
    description: >-
      If you enable the “use an entity for the end-brightness value instead”
      option, select here whether your chosen entity represents brightness with
      a 0% to 100% scale or a 0 to 255 scale. (Note—in either case, this script
      will expect that entity to solely have an integer value without any
      nonnumeric characters such as “%”.)
    advanced: true
    selector:
      select:
        options:
          - label: 0% to 100%
            value: zeroToOneHundred
          - label: 0 to 255
            value: zeroToTwoFiftyFive
    default: zeroToOneHundred
    required: true
  endColorTemperatureKelvin:
    name: 🎨 (optional) End color temperature (in Kelvin)
    description: >-
      Color temperature from 2000 K to 6500 K representing the final color
      temperature.
    selector:
      number:
        min: 2000
        max: 6500
        step: 50
        mode: slider
        unit_of_measurement: °K
    example: "2700"
    required: false
  endColorTemperatureKelvinEntity:
    name: (optional) Use an entity instead for the ending Kelvin value?
    description: >-
      You can optionally have the script ignore the end-color-temperature value
      above and instead use the numeric value of anonther entity that you select
      here, such as an input-number helper, an input-select helper, an
      input-text helper, or a numeric sensor.
    selector:
      entity:
        domain:
          - input_number
          - input_select
          - input_text
          - sensor
    example: input_number.dining_room_color_temperature
    required: false
    advanced: true
  autoCancelThreshold:
    name: 🚫 (optional) Brightness-change threshold that auto-cancels the fade
    description: >-
      You can optionally have the script automatically cancel its fade if the
      lamp’s brightness were to be manually changed by a certain amount. For
      example, let’s suppose that you were to set this value to 5% and then you
      were to run this script. If there comes a point within the fade where this
      script is expecting the lamp to be at, say, 22% brightness—but you’ve just
      manually set that lamp to 30% brightness—this script will automatically
      cancel its fade since that 8% difference is >= 5%. Note—if you use this,
      Ashley doesn’t recommend setting this to anything less than about 3;
      that’s just because it can be normal for there to be an occasional
      difference of 1 or 2 since not all lamps instantly reflect newly assigned
      brightness values due to processing lag and other factors. In Ashley’s
      home, she personally uses a value of about 5 for this.
    selector:
      number:
        min: 2
        max: 100
        step: 1
        mode: slider
        unit_of_measurement: "%"
    default: 10
    example: "10"
    required: false
  shouldStopIfTheLampIsTurnedOffDuringTheFade:
    name: ⤵️ Cancel the fade if the lamp is turned off during the fade?
    description: >-
      By default, the script will automatically disengage if the lamp is turned
      off during the fade. But if you’d like to, you can also disable that
      behavior with this switch. (And don’t worry—this feature is smart enough
      to not automatically cancel the fade at points when the lamp’s intended
      brightness might be zero, such as if the script were to be fading down to
      zero or fading up from zero.)
    selector:
      boolean: null
    advanced: false
    required: true
    default: true
  stopEntity:
    name: 🛑 (optional) Stop if a certain entity is turned on during the fade?
    description: >-
      You can optionally have the script keep an eye on an input boolean or a
      binary sensor or a light. And if that entity is then turned on during the
      fade, the script will automatically stop. So for example, if you create a
      “Stop Everything” input boolean, and if you set that entity here, you can
      stop your fade at any time by turning on that “Stop Everything” entity.
    selector:
      entity:
        domain:
          - input_boolean
          - binary_sensor
          - light
          - switch
    example: input_boolean.stop_everything
    required: false
    advanced: true
  shouldResetTheStopEntityToOffAtStart:
    name: (optional) Reset that “stop” entity to off just before starting the fade?
    description: >-
      If you make use of the stop entity (above), you can also optionally have
      the script automatically reset that entity to “off” at the start of the
      fade. (By default, the script won’t change the value of the stop entity.)
    selector:
      boolean: null
    advanced: true
    required: true
    default: false
  shouldInvertTheValueOfTheStopEntity:
    name: (optional) Stop only if the “stop” entity is instead turned OFF?
    description: >-
      If you make use of the stop entity (above), you can optionally have the
      script automatically stop only if that entity is turned OFF during the
      fade.
    selector:
      boolean: null
    advanced: true
    required: true
    default: false
  minimumStepDelayInMilliseconds:
    name: Minimum delay per step
    description: >-
      The minimum delay between sending each brightness command. Some lamps only
      accept commands every X milliseconds—so while you can probably leave this
      as is, if by chance your lamp were to behave strangely, you might try
      bumping up this number by another ten or twenty milliseconds.
    advanced: true
    selector:
      number:
        min: 50
        max: 1000
        step: 10
        mode: slider
        unit_of_measurement: ms
    default: 100
    example: "100"
  shouldTryToUseNativeLampTransitionsToo:
    name: 🧪 (experimental) If available, use the lamp's native transitions too?
    description: >-
      If this is enabled and if the lamp natively supports transitions, the
      script will make use the lamp’s native transition effect when moving
      between brightness steps or color steps if those steps are very gradually
      spaced. When enabled, this has the possibility of offering even smoother
      fades. And if the lamp doesn’t happen to support the transition effect in
      the first place, this feature will automatically disengage. (And while
      Ashley feels comfortable using this feature in her own house, this feature
      isn’t as thoroughly tested as the script’s other features, so this feature
      is considered experimental for now.)
    selector:
      boolean: null
    advanced: true
    required: true
    default: false
  isDebugMode:
    name: 🐛 Enable debugging mode?
    description: >-
      If this is enabled, the script will output status messages to your Home
      Assistant log along the way. Unless something were to be acting weirdly,
      you can leave this off.
    selector:
      boolean: null
    advanced: true
    required: true
    default: false
sequence:
  - variables:
      lightFriendlyName: "{{ state_attr(light, 'friendly_name') }}"
      startTimestamp: "{{ as_timestamp(now()) }}"
      fadeTimeInSeconds: >
        {{ ((transitionTime.hours | int(0)) * 60 * 60) + ((transitionTime.minutes | int(0)) * 60) + (transitionTime.seconds | int(0)) }}
      endTimestamp: "{{ startTimestamp + fadeTimeInSeconds }}"
      fadeTimeInMilliseconds: "{{ fadeTimeInSeconds * 1000 }}"
      shouldIgnoreTheEndBrightnessValueAndUseAnEntityForTheEndBrightnessInstead: >
        {% if (endBrightnessEntity is defined) and
        (has_value(endBrightnessEntity)) %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      shouldIgnoreTheEndColorTemperatureValueAndUseAnEntityForTheEndColorTemperatureInstead: >
        {% if (endColorTemperatureKelvinEntity is defined) and
        (has_value(endColorTemperatureKelvinEntity)) %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      isStopEntityEnabled: |
        {% if (stopEntity is defined) and (has_value(stopEntity)) %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      isStopEntityToggleable: |
        {% if isStopEntityEnabled %}
          {% set stopEntityDomain = stopEntity.split('.')[0] %}
          {% if stopEntityDomain == "binary_sensor" %}
            {{ false }}
          {% else %}
            {## branch for stopEntityDomain being an input_boolean or a light or a switch ##} 
            {{ true }}
          {% endif %}
        {% else %}
          {{ false }}
        {% endif %}
      stopEntityFriendlyName: |
        {% if isStopEntityEnabled %}
          {{ state_attr(stopEntity, 'friendly_name') }}
        {% else %}
          {{ "" }}
        {% endif %}
      minColorTemperatureKelvin: >
        {## init ##} {% set minColorTemperature = -1 %}

        {% set minColorTemperatureValue = state_attr(light,
        'min_color_temp_kelvin') %} {% if minColorTemperatureValue %}
          {% if is_number(minColorTemperatureValue) %}
            {% set minColorTemperature = minColorTemperatureValue | int(0) %}
          {% endif %}
        {% endif %} {{ minColorTemperature }}
      maxColorTemperatureKelvin: >
        {## init ##} {% set maxColorTemperature = -1 %}

        {% set maxColorTemperatureValue = state_attr(light,
        'max_color_temp_kelvin') %} {% if maxColorTemperatureValue %}
          {% if is_number(maxColorTemperatureValue) %}
            {% set maxColorTemperature = maxColorTemperatureValue | int(0) %}
          {% endif %}
        {% endif %} {{ maxColorTemperature }}
      doesLampSupportColorTemperatureKelvin: |
        {% if ((minColorTemperatureKelvin | int(0)) > 0) and
        ((maxColorTemperatureKelvin | int(0))) > 0 %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      doesLampSupportTransitions: >+
        {% set supportedFeatures = state_attr(light, 'supported_features') | int(0)
        %}

        {## Bitfield of features supported by the light entity ##} {##
        SUPPORT_BRIGHTNESS = 1 ##} {## SUPPORT_COLOR_TEMP = 2 ##} {##
        SUPPORT_EFFECT = 4 ##} {## SUPPORT_FLASH = 8 ##} {## SUPPORT_COLOR = 16
        ##} {## SUPPORT_TRANSITION = 32 ##} {## SUPPORT_WHITE_VALUE = 128 ##}

        {## via:
        https://github.com/home-assistant/core/blob/787faaa508cf83ca4f850ee7000ad17eade915aa/homeassistant/components/light/__init__.py#L39
        ##}

        {% if (supportedFeatures // 32) % 2 == 1 %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}

      endColorTemperature: >
        {% if

        shouldIgnoreTheEndColorTemperatureValueAndUseAnEntityForTheEndColorTemperatureInstead

        %}
          {% set entityColorTemperatureValue = states(endColorTemperatureKelvinEntity) | float(0) | round(0) %}

          {% set endColorTemperatureKelvinValue = entityColorTemperatureValue | int(0) %}

          {% if endColorTemperatureKelvinValue > (maxColorTemperatureKelvin | int(0)
          ) %}
            {% set endColorTemperatureKelvinValue = maxColorTemperatureKelvin | int(0) %}
          {% endif %}

          {% if endColorTemperatureKelvinValue < (minColorTemperatureKelvin | int(0)
          )%}
            {% set endColorTemperatureKelvinValue = minColorTemperatureKelvin | int(0) %}
          {% endif %}

        {% else %}
          {## branch for shouldIgnoreTheEndColorTemperatureValueAndUseAnEntityForTheEndColorTemperatureInstead being off ##}

          {% if (endColorTemperatureKelvin is defined) and endColorTemperatureKelvin %}
            {% set endColorTemperatureKelvinValue = endColorTemperatureKelvin | round(0) %}
          {% else %}
            {% set endColorTemperatureKelvinValue = 0 %}
          {% endif %}

        {% endif %}


        {{ endColorTemperatureKelvinValue }}
      isColorFadeRequested: |
        {% if (endColorTemperature | int(0)) > 0 %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      endBrightness: |
        {% if shouldIgnoreTheEndBrightnessValueAndUseAnEntityForTheEndBrightnessInstead %}
          {% set entityBrightnessValue = states(endBrightnessEntity) | float(0) | round(0) %}
          {% if (endBrightnessEntityScale == lampBrightnessScale) %}
            {% set endBrightness = entityBrightnessValue %}
          {% elif (lampBrightnessScale == "zeroToTwoFiftyFive") and (endBrightnessEntityScale == "zeroToOneHundred") %}
            {% set endBrightness = (entityBrightnessValue * 2.55) | round(0) %}
          {% elif (lampBrightnessScale == "zeroToOneHundred") and (endBrightnessEntityScale == "zeroToTwoFiftyFive") %}
            {% set endBrightness = (entityBrightnessValue / 2.55) | round(0) %}
          {% endif %}
        {% else %}
          {## branch for using the brightness selector's value rather than an entity's brightness value ##}
          {% if lampBrightnessScale == "zeroToOneHundred" %}
          	{% set endBrightness = endBrightnessPercent %}
          {% else %}
        
            {## lampBrightnessScale == "zeroToTwoFiftyFive" ##}
            {% if endBrightnessPercent == 1 %}
              {## special case to force the endBrightness to 1/255 if the user enters "1%" by inferring that the user is looking to set the lamp to its lowest possible brightness ##}
            	{% set endBrightness = 1 %}
            {% else %}
              {% set endBrightness = (endBrightnessPercent * 2.55) | round(0) %}
            {% endif %}
        
          {% endif %}
        {% endif %}
        
        {{ endBrightness }}
      startBrightness: |
        {% set rawLightLevel = state_attr(light, 'brightness') %}
        
        {## If a light is off, its brightness will return "None"? So first check if there's a valid brightness before setting the value ##}
        {% if rawLightLevel %}
        	{% set currentLightLevel = rawLightLevel | int(0) %}
        {% else %}
        	{% set currentLightLevel = 0 %}
        {% endif %}
        
        {% if lampBrightnessScale == "zeroToOneHundred" %}
          {% set currentLightLevel = (currentLightLevel / 2.55) | round(0) %}
        {% endif %}
        
        {## This next part, which nudges the starting level from 0 to 1, is necessary in cases 
          where there’s a color fade requested but the starting brightness level is 0. 
          And that’s because when a light is at 0, Home Assistant doesn’t know that light's Kelvin color.            So the light first needs to be nudged toward 1 just so that this script can see what it’s
          starting Kelvin color is. ##}
        
        {% if (currentLightLevel == 0) and (endBrightness > currentLightLevel) and isColorFadeRequested %}
          {% set currentLightLevel = 1 %}
        {% endif %}
        
        {{ currentLightLevel }}
      brightnessSpan: "{{ endBrightness - startBrightness }}"
      absoluteBrightnessSpan: "{{ brightnessSpan | abs }}"
      shouldAutoCancelTheFadeIfTheLampBrightnessIsManuallyChanged: >
        {% if (autoCancelThreshold is defined) and
        (is_number(autoCancelThreshold)) %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      normalizedAutoCancelThreshold: |
        {% if not shouldAutoCancelTheFadeIfTheLampBrightnessIsManuallyChanged %}
          {## exit early if shouldAutoCancelTheFadeIfTheLampBrightnessIsManuallyChanged isn’t even enabled ##}
          {{ 255 }}
        {% elif (lampBrightnessScale == "zeroToTwoFiftyFive") %}
          {{ (autoCancelThreshold * 2.55) | round (0) }}
        {% else %}
          {{ autoCancelThreshold }}
        {% endif %}
      expectedBrightness: "{{ startBrightness }}"
  - if:
      - condition: template
        value_template: >
          {{ isStopEntityEnabled and isStopEntityToggleable and
          shouldResetTheStopEntityToOffAtStart }}
    then:
      - data: { }
        target:
          entity_id: "{{ stopEntity }}"
        action: homeassistant.turn_off
  - if:
      - condition: template
        value_template: |
          {## init ##} {% set shouldStop = false %} {% if isStopEntityEnabled %}
            {% if (shouldInvertTheValueOfTheStopEntity is defined) and shouldInvertTheValueOfTheStopEntity %}
              {% if not bool(states(stopEntity), false) %}
                {% set shouldStop = true %}
              {% endif %}
            {% else %}
              {% if bool(states(stopEntity), false) %}
                {% set shouldStop = true %}
              {% endif %}
            {% endif %}
          {% endif %} {{ shouldStop }}
    then:
      - if:
          - condition: template
            value_template: >
              {{ (shouldInvertTheValueOfTheStopEntity is defined) and
              shouldInvertTheValueOfTheStopEntity }}
        then:
          - variables:
              stopMessage: >
                {% set stopMessage = "Stopped Ashley’s Light Fader because " +
                (stopEntityFriendlyName | string) + " is off." %} {{ stopMessage
                }}
          - if:
              - condition: template
                value_template: |
                  {{ isDebugMode }}
            then:
              - data:
                  message: |
                    {{ stopMessage }}
                  level: warning
                action: system_log.write
          - stop: |
              {{ stopMessage }}
        else:
          - variables:
              stopMessage: >
                {% set stopMessage = "Stopped Ashley’s Light Fader because " +
                (stopEntityFriendlyName | string) + " is on." %} {{ stopMessage
                }}
          - if:
              - condition: template
                value_template: |
                  {{ isDebugMode }}
            then:
              - data:
                  message: |
                    {{ stopMessage }}
                  level: warning
                action: system_log.write
          - stop: |
              {{ stopMessage }}
  - variables:
      timestampBeforeServiceCall: "{{ as_timestamp(now()) }}"
  - if:
      - condition: template
        value_template: |
          {{ (lampBrightnessScale == "zeroToTwoFiftyFive") }}
    then:
      - target:
          entity_id: "{{ light }}"
        data:
          brightness: "{{ expectedBrightness }}"
        action: light.turn_on
        continue_on_error: true
    else:
      - target:
          entity_id: "{{ light }}"
        data:
          brightness_pct: "{{ expectedBrightness }}"
        action: light.turn_on
        continue_on_error: true
  - variables:
      processingDelayInMilliseconds: "{{ (as_timestamp(now()) -  timestampBeforeServiceCall) * 1000 }}"
      easingType: |
        {% if "auto" in easingTypeInput %}
          {% if (startBrightness == 0) and (fadeTimeInSeconds <= 20) %}
            {{ "easeOutCubic" }}
          {% elif (startBrightness == 0) and (fadeTimeInSeconds <= 55) %}
            {{ "easeOutQuad" }}
          {% elif (endBrightness == 0) and (fadeTimeInSeconds <= 20) %}
            {{ "easeInCubic" }}
          {% elif (endBrightness == 0) and (fadeTimeInSeconds <= 55) %}
            {{ "easeInQuad" }}
          {% else %}
            {{ "easeInOutSine" }}
          {% endif %}
        {% else %}
          {{ easingTypeInput }}
        {% endif %}
      isLampCurrentColorDefinedUsingKelvin: |+
        {% set rawColorTemperature = state_attr(light, 'color_temp_kelvin') %}

        {## If a light is off or if its color temperature is defined using a non-Kelvin color system, 
          its color temperature will return "None"? So first check if there's a valid color temperature ##}

        {% if rawColorTemperature %}
        	{{ true }}
        {% else %}
        	{{ false }}
        {% endif %}
      startColorTemperature: >+
        {## init ##}
        {% set currentColorTemperature = -1 %}

        {% set rawColorTemperature = state_attr(light, 'color_temp_kelvin') %}

        {## If a light is off or if its color temperature is defined using a
        non-Kelvin color system, 
          its color temperature will return "None"? So first check if there's a valid color temperature ##}

        {% if rawColorTemperature %}
          {% if is_number(rawColorTemperature) %}
            {% set currentColorTemperature = rawColorTemperature | int(0) %}
          {% endif %}
        {% endif %}

        {{ currentColorTemperature }}

      colorTemperatureSpan: |
        {% if doesLampSupportColorTemperatureKelvin %}
          {{ (endColorTemperature | int(0)) - (startColorTemperature | int(0)) }}
        {% else %}
          {{ 0 }}
        {% endif %}
      absoluteColorTemperatureSpan: "{{ colorTemperatureSpan | int(0) | abs }}"
      isBrightnessFadeEnabled: |
        {% if (absoluteBrightnessSpan | int(0)) > 0 %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      isColorFadeEnabled: >
        {% if isColorFadeRequested and doesLampSupportColorTemperatureKelvin and
        ((absoluteColorTemperatureSpan | int(0)) > 0) %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
      remainingTimeInMilliseconds: |
        {% set nowTimestamp = as_timestamp(now()) %}
        {{ (endTimestamp - nowTimestamp) * 1000 }}
      delayInMilliseconds: >
        {## init ##}

        {% set frameMultiplicationFactor = 1 %}

        {% if "Circ" in easingType %}
          {## Four as many frames for Circ-type easings ##}
          {## set idealDelay = (idealDelay / 4) | round(0) ##}
          {% set frameMultiplicationFactor = 3 %}
        {% elif ("Cubic" in easingType) or ("Quart" in easingType) %}
          {## Three as many frames for Cubic-type or Quart-type easings ##}
          {## set idealDelay = (idealDelay / 3) | round(0) ##}
          {% set frameMultiplicationFactor = 2 %}
        {% elif "ease" in easingType %}
          {## Twice as many frames for other easing types ##}
          {## set idealDelay = (idealDelay / 1.5) | round(0) ##}
          {% set frameMultiplicationFactor = 1.2 %}
        {% endif %}

        {% if isBrightnessFadeEnabled %}
          {% set brightnessFramesCount = (frameMultiplicationFactor * absoluteBrightnessSpan) | round(0) %}
          {% set totalExpectedRemainingBrightnessFadeProcessingDelay = processingDelayInMilliseconds * brightnessFramesCount %}
          {% set idealBrightnessFadeDelay = ((remainingTimeInMilliseconds - totalExpectedRemainingBrightnessFadeProcessingDelay) / brightnessFramesCount) | round(0) %}
        {% else %}
          {% set idealBrightnessFadeDelay = 0 %}
        {% endif %}

        {% if isColorFadeEnabled %}
          {## The division by 25 below is working from the idea that you’d want the color fade 
            to progress at least every 25°K, such as 2,000 K, 2,025 K, 2,050 K, and so on ##}

          {% set colorFramesCount = (frameMultiplicationFactor * (absoluteColorTemperatureSpan / 25)) | round(0) %}
          {% set totalExpectedRemainingColorFadeProcessingDelay = (processingDelayInMilliseconds * colorFramesCount) | round(0) %}
          {% set idealColorFadeDelay = ((remainingTimeInMilliseconds - totalExpectedRemainingColorFadeProcessingDelay) / colorFramesCount ) | round(0) %}
        {% else %}
          {% set idealColorFadeDelay = 0 %}
        {% endif %}

        {## init ##} {% set idealDelay = 0 %}

        {## if both brightness-fade is enabled and color-fade is enabled, pick
        the one with the lowest ideal delay ##}

        {% if isBrightnessFadeEnabled and isColorFadeEnabled %}
          {% if idealBrightnessFadeDelay < idealColorFadeDelay %}
            {% set idealDelay = idealBrightnessFadeDelay %} 
          {% else %}
            {% set idealDelay = idealColorFadeDelay %}
          {% endif %}
        {% elif isBrightnessFadeEnabled %}
          {% set idealDelay = idealBrightnessFadeDelay %}
        {% elif isColorFadeEnabled %}
          {% set idealDelay = idealColorFadeDelay %}
        {% endif %}

        {## Make sure that the delay isn’t below the
        minimumStepDelayInMilliseconds ##} {% if idealDelay <
        minimumStepDelayInMilliseconds %}
          {{ minimumStepDelayInMilliseconds }}
        {% else %}
          {{ idealDelay }}
        {% endif %}
      delayInSeconds: |
        {{ (delayInMilliseconds | int(0)) / 1000 }}
      transitionTimeParameterInSeconds: >
        {% set transitionTimeParameterInSeconds = ((delayInSeconds | float(0)) *
        0.9) | round(3) %}

        {## set a ceiling on the transition time ##}

        {% if transitionTimeParameterInSeconds > 10 %}
          {% set transitionTimeParameterInSeconds = 10 %}
        {% endif %}

        {{ transitionTimeParameterInSeconds }}
      minimumDelayInSecondsBeforeUsingNativeLampNativeTransitionsToo: |
        {{ 1 }}
      shouldUseNativeLampTransitionsToo: |
        {% if 
          (
            (shouldTryToUseNativeLampTransitionsToo is defined) 
            and shouldTryToUseNativeLampTransitionsToo 
            and doesLampSupportTransitions
            and ((transitionTimeParameterInSeconds | float(0) ) >=    
            minimumDelayInSecondsBeforeUsingNativeLampNativeTransitionsToo | float(0) )
          )
        %}
          {{ true }}
        {% else %}
          {{ false }}
        {% endif %}
  - if:
      - condition: template
        value_template: >
          {{ ((absoluteBrightnessSpan | int(0)) == 0) and not isColorFadeEnabled
          }}
    then:
      - variables:
          stopMessage: >
            {% set stopMessage = "Stopped Ashley’s Light Fader because " +
            (lightFriendlyName | string) + "’s starting brightness is the same
            as its ending brightness." %} {{ stopMessage }}
      - if:
          - condition: template
            value_template: |
              {{ isDebugMode }}
        then:
          - data:
              message: |
                {{ stopMessage }}
              level: warning
            action: system_log.write
      - stop: |
          {{ stopMessage }}
  - if:
      - condition: template
        value_template: >
          {{ ((absoluteBrightnessSpan | int(0)) == 0) and
          ((absoluteColorTemperatureSpan | int(0)) == 0) and isColorFadeEnabled
          }}
    then:
      - variables:
          stopMessage: >
            {% set stopMessage = "Stopped Ashley’s Light Fader because " +
            (lightFriendlyName | string) + "’s starting brightness is the same
            as its ending brightness and its starting color temperature is the
            same as its ending color temperature." %} {{ stopMessage }}
      - if:
          - condition: template
            value_template: |
              {{ isDebugMode }}
        then:
          - data:
              message: |
                {{ stopMessage }}
              level: warning
            action: system_log.write
      - stop: |
          {{ stopMessage }}
  - if:
      - condition: template
        value_template: >
          {{ isColorFadeRequested and not doesLampSupportColorTemperatureKelvin
          }}
    then:
      - variables:
          stopMessage: >
            {% set stopMessage = "Stopped Ashley’s Light Fader because a color
            fade was requested for " + (lightFriendlyName | string) + ", but it
            doesn’t seem to support Kelvin color temperatures. (You can look
            into this by going into Developer Tools and checking that " +
            (lightFriendlyName | string) + " has the defined attributes for
            “min_color_temp_kelvin” and “max_color_temp_kelvin”.)" %} {{
            stopMessage }}
      - if:
          - condition: template
            value_template: |
              {{ isDebugMode }}
        then:
          - data:
              message: |
                {{ stopMessage }}
              level: warning
            action: system_log.write
      - stop: |
          {{ stopMessage }}
  - if:
      - condition: template
        value_template: >
          {{ isColorFadeRequested and not isLampCurrentColorDefinedUsingKelvin
          }}
    then:
      - variables:
          stopMessage: >
            {% set stopMessage = "Stopped Ashley’s Light Fader because a color
            fade was requested for " + (lightFriendlyName | string) + ", but its
            current color temperature doesn’t seem to be defined using Kelvin
            units. (You can look into this by going into Developer Tools and
            checking that " + (lightFriendlyName | string) + " has a defined
            “color_temp_kelvin” attribute.)" %} {{ stopMessage }}
      - if:
          - condition: template
            value_template: |
              {{ isDebugMode }}
        then:
          - data:
              message: |
                {{ stopMessage }}
              level: warning
            action: system_log.write
      - stop: |
          {{ stopMessage }}
  - if:
      - condition: template
        value_template: |
          {{ isDebugMode }}
    then:
      - if:
          - condition: template
            value_template: |
              {{ isColorFadeEnabled }}
        then:
          - data:
              message: >
                {{ easingType }} easing type with {{ delayInMilliseconds | int(0)
                }} ms delay. remainingTimeInMilliseconds = {{
                remainingTimeInMilliseconds | round(0) }},
                absoluteColorTemperatureSpan = {{ absoluteColorTemperatureSpan
                }}, and absoluteBrightnessSpan = {{ absoluteBrightnessSpan }}
              level: warning
            action: system_log.write
          - data:
              message: >
                startBrightness = {{ startBrightness }}, endBrightness = {{
                endBrightness }}, startColorTemperature = {{
                startColorTemperature }}, endColorTemperature = {{
                endColorTemperature }}, and  processingDelayInMilliseconds = {{
                processingDelayInMilliseconds | int(0) }}
              level: warning
            action: system_log.write
        else:
          - data:
              message: >
                {{ easingType }} easing type with {{ delayInMilliseconds | int(0)
                }} ms delay. remainingTimeInMilliseconds = {{
                remainingTimeInMilliseconds | round(0) }}, and
                absoluteBrightnessSpan = {{ absoluteBrightnessSpan }}
              level: warning
            action: system_log.write
          - data:
              message: >
                startBrightness = {{ startBrightness }}, endBrightness = {{
                endBrightness }}, and processingDelayInMilliseconds = {{
                processingDelayInMilliseconds | int(0) }}
              level: warning
            action: system_log.write
  - repeat:
      sequence:
        - variables:
            percentageOfTimeCompleted: >
              {% set nowTimestamp = as_timestamp(now()) %}

              {% set percentageOfTimeCompleted = ((nowTimestamp -
              startTimestamp) / fadeTimeInSeconds) %}

              {% if (percentageOfTimeCompleted < 0) %}
                {% set percentageOfTimeCompleted = 0 %}
              {% elif (percentageOfTimeCompleted > 1) %}
                {% set percentageOfTimeCompleted = 1 %}
              {% endif %}

              {{ percentageOfTimeCompleted }}
            easingAdjustedPercentageCompleted: >
              {% set percentageOfTimeCompleted = percentageOfTimeCompleted |
              float(0) %} {% if easingType == "easeInSine" %}
                {## https://easings.net/#easeInSine ##}
                {{  1 - cos((percentageOfTimeCompleted * pi) / 2) }}

              {% elif easingType == "easeInQuad" %}
                {## https://easings.net/#easeInQuad ##}
                {{ percentageOfTimeCompleted * percentageOfTimeCompleted }} 

              {% elif easingType == "easeInCubic" %}
                {## https://easings.net/#easeInCubic ##}
                {{ percentageOfTimeCompleted * percentageOfTimeCompleted * percentageOfTimeCompleted }}

              {% elif easingType == "easeInQuart" %}
                {## https://easings.net/#easeInQuart ##}
                {{ percentageOfTimeCompleted * percentageOfTimeCompleted * percentageOfTimeCompleted * percentageOfTimeCompleted }}

              {% elif easingType == "easeOutSine" %}
                {## https://easings.net/#easeOutSine ##}
                {{ sin((percentageOfTimeCompleted * pi) / 2) }}

              {% elif easingType == "easeOutQuad" %}
                {## https://easings.net/#easeOutQuad ##}
                {{ 1 - (1 - percentageOfTimeCompleted)* (1 - percentageOfTimeCompleted) }} 

              {% elif easingType == "easeOutCubic" %}
                {## https://easings.net/#easeOutCubic ##}
                {{ 1 - ((1 - percentageOfTimeCompleted) ** 3) }}

              {% elif easingType == "easeOutQuart" %}
                {## https://easings.net/#easeOutQuart ##}
                {{ 1 - ((1 - percentageOfTimeCompleted) ** 4) }}

              {% elif easingType == "easeInOutSine" %}
                {## https://easings.net/#easeInOutSine ##}
                {{ -1 * ((cos(pi * percentageOfTimeCompleted) - 1) / 2) }}

              {% elif easingType == "easeInOutQuad" %}
                {## https://easings.net/#easeInOutQuad ##}
                {% if (percentageOfTimeCompleted < 0.5) %}
                  {{ 2 * percentageOfTimeCompleted * percentageOfTimeCompleted }} 
                {% else %}
                  {{ 1- (((-2 * percentageOfTimeCompleted + 2) ** 2) / 2) }} 
                {% endif %}

              {% elif easingType == "easeInOutCubic" %}
                {## https://easings.net/#easeInOutCubic ##}
                {% if (percentageOfTimeCompleted < 0.5) %}
                  {{ 4 * percentageOfTimeCompleted * percentageOfTimeCompleted * percentageOfTimeCompleted }} 
                {% else %}
                  {{ 1- (((-2 * percentageOfTimeCompleted + 2) ** 3) / 2) }} 
                {% endif %}

              {% elif easingType == "easeInOutQuart" %}
                {## https://easings.net/#easeInOutQuart ##}
                {% if (percentageOfTimeCompleted < 0.5) %}
                  {{ 8 * percentageOfTimeCompleted * percentageOfTimeCompleted * percentageOfTimeCompleted * percentageOfTimeCompleted }} 
                {% else %}
                  {{ 1 - (((-2 * percentageOfTimeCompleted + 2) ** 4) / 2) }} 
                {% endif %}
              {% else %}
                {## linear ##}
                {{ percentageOfTimeCompleted }}
              {% endif %}
            expectedBrightness: |
              {% if isBrightnessFadeEnabled %}
                {% set startBrightness = startBrightness | int(0) %}
                {% set brightnessSpan = brightnessSpan | int(0) %}
                {% set easingAdjustedPercentageCompleted = easingAdjustedPercentageCompleted | float(0) %}

                {% set calculatedBrightness = (startBrightness + (brightnessSpan * easingAdjustedPercentageCompleted)) | round(0) %}
                {% if (calculatedBrightness < 0) %}
                  {% set calculatedBrightness = 0 %}
                {% elif (calculatedBrightness > 255) %}
                  {% set calculatedBrightness = 255 %}
                {% endif %}

                {% if (endBrightness >= startBrightness) and (calculatedBrightness > endBrightness) %}
                  {% set calculatedBrightness = endBrightness %}
                {% elif (endBrightness < startBrightness) and (calculatedBrightness < endBrightness) %}
                  {% set calculatedBrightness = endBrightness %}
                {% endif %}
              {% else %}
                {## if isBrightnessFadeEnabled is false ##}
                {% set calculatedBrightness = startBrightness %}
              {% endif %}

              {{ calculatedBrightness }}
            linearExpectedBrightness: |
              {% if isBrightnessFadeEnabled %}              
                {% set startBrightness = startBrightness | int(0) %}
                {% set brightnessSpan = brightnessSpan | int(0) %}
                {% set percentageOfTimeCompleted = percentageOfTimeCompleted | float(0) %}

                {% set calculatedBrightness = (startBrightness + (brightnessSpan * percentageOfTimeCompleted)) | round(0) %}
                {% if (calculatedBrightness < 0) %}
                  {% set calculatedBrightness = 0 %}
                {% elif (calculatedBrightness > 255) %}
                  {% set calculatedBrightness = 255 %}
                {% endif %}

                {% if (endBrightness >= startBrightness) and (calculatedBrightness > endBrightness) %}
                  {% set calculatedBrightness = endBrightness %}
                {% elif (endBrightness < startBrightness) and (calculatedBrightness < endBrightness) %}
                  {% set calculatedBrightness = endBrightness %}
                {% endif %}
              {% else %}
                {## if isBrightnessFadeEnabled is false ##}
                {% set calculatedBrightness = startBrightness %}
              {% endif %}

              {{ calculatedBrightness }}
            currentBrightness: |+
              {% if isBrightnessFadeEnabled %}
                {% set rawLightLevel = state_attr(light, 'brightness') %}
              
                {## If a light is off, its brightness will return "None"? So first check if there's a valid brightness before getting the value ##}
                {% if rawLightLevel %}
                	{% set currentLightLevel = rawLightLevel | int(0) %}
                {% else %}
                	{% set currentLightLevel = 0 %}
                {% endif %}
                {% if lampBrightnessScale == "zeroToOneHundred" %}
                  {% set currentLightLevel = (currentLightLevel / 2.55) | round(0) %}
                {% endif %}
              {% else %}
                {% set currentLightLevel = startBrightness %}
              {% endif %}
              
              {{ currentLightLevel }}
            expectedColorTemperature: |
              {% if isColorFadeEnabled %}
                {% set startColorTemperature = startColorTemperature | int(0) %}
                {% set colorTemperatureSpan = colorTemperatureSpan | int(0) %}
                {% set easingAdjustedPercentageCompleted = easingAdjustedPercentageCompleted | float(0) %}

                {% set calculatedColorTemperature = (startColorTemperature + (colorTemperatureSpan * easingAdjustedPercentageCompleted)) | round(0) %}

                {% set minColorTemperatureKelvin = minColorTemperatureKelvin | int(0) %}
                {% set maxColorTemperatureKelvin = maxColorTemperatureKelvin | int(0) %}

                {% if (calculatedColorTemperature < minColorTemperatureKelvin) %}
                  {% set calculatedColorTemperature = minColorTemperatureKelvin %}

                {% elif (calculatedColorTemperature > maxColorTemperatureKelvin) %}
                  {% set calculatedColorTemperature = maxColorTemperatureKelvin %}
                {% endif %}

                {% if (endColorTemperature >= startColorTemperature) and (calculatedColorTemperature > endColorTemperature) %}
                  {% set calculatedColorTemperature = endColorTemperature %}
                {% elif (endColorTemperature < startColorTemperature) and (calculatedColorTemperature < endColorTemperature) %}
                  {% set calculatedColorTemperature = endColorTemperature %}
                {% endif %}
              {% else %}
                {% set calculatedColorTemperature = 0 %}
              {% endif %}

              {{ calculatedColorTemperature }}
            currentColorTemperature: |
              {## init ##}
              {% set currentColorTemperature = -1 %}
              {% if isColorFadeEnabled %}
                {% set rawColorTemperature = state_attr(light, 'color_temp_kelvin') %}

                {## If a light is off or if its color temperature is defined using a non-Kelvin color system, its color temperature will return "None"? So first check if there's a valid color temperature ##}
                {% if rawColorTemperature %}

                  {% if is_number(rawColorTemperature) %}
                    {% set currentColorTemperature = rawColorTemperature | int(0) %}
                  {% endif %}
                {% endif %}
              {% endif %}

              {{ currentColorTemperature }}
        - if:
            - condition: template
              value_template: |
                {{ isBrightnessFadeEnabled and isColorFadeEnabled }}
          then:
            - if:
                - condition: template
                  value_template: |
                    {{ 
                      (
                        ( (currentBrightness | int(0)) != (endBrightness | int(0)) )
                        and
                        ( (currentBrightness | int(0)) != (expectedBrightness | int(0)) )
                      )
                      or
                      (
                        ( (currentColorTemperature | int(0)) != (endColorTemperature | int(0)) )
                        and
                        ( (currentColorTemperature | int(0)) != (expectedColorTemperature | int(0)) )
                      )
                    }}
              then:
                - if:
                    - condition: template
                      value_template: |
                        {{ shouldUseNativeLampTransitionsToo }}
                  then:
                    - if:
                        - condition: template
                          value_template: |
                            {{ (lampBrightnessScale == "zeroToTwoFiftyFive") }}
                      then:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness: "{{ expectedBrightness }}"
                            color_temp_kelvin: "{{ expectedColorTemperature }}"
                            transition: "{{ transitionTimeParameterInSeconds | float(0)  }}"
                          action: light.turn_on
                          continue_on_error: true
                      else:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness_pct: "{{ expectedBrightness }}"
                            color_temp_kelvin: "{{ expectedColorTemperature }}"
                            transition: "{{ transitionTimeParameterInSeconds | float(0) }}"
                          action: light.turn_on
                          continue_on_error: true
                  else:
                    - if:
                        - condition: template
                          value_template: |
                            {{ (lampBrightnessScale == "zeroToTwoFiftyFive") }}
                      then:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness: "{{ expectedBrightness }}"
                            color_temp_kelvin: "{{ expectedColorTemperature }}"
                          action: light.turn_on
                          continue_on_error: true
                      else:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness_pct: "{{ expectedBrightness }}"
                            color_temp_kelvin: "{{ expectedColorTemperature }}"
                          action: light.turn_on
                          continue_on_error: true
                - if:
                    - condition: template
                      value_template: |
                        {{ isDebugMode }}
                  then:
                    - if:
                        - condition: template
                          value_template: |
                            {{ isColorFadeEnabled and isBrightnessFadeEnabled }}
                      then:
                        - data:
                            message: >
                              Set {{ lightFriendlyName }} to {{
                              expectedBrightness | int(0) }} brightness and {{
                              expectedColorTemperature | int(0) }}°K. (Linear
                              brightness would have been {{
                              linearExpectedBrightness | int(0) }}.) Delay is {{
                              delayInMilliseconds | int(0) }} ms. Elapsed time is
                              {{ (as_timestamp(now()) - startTimestamp) |
                              round(2) }} seconds. (endBrightness is {{
                              endBrightness}} and endColorTemperature is {{
                              endColorTemperature}}.)
                            level: warning
                          action: system_log.write
          else:
            - if:
                - condition: template
                  value_template: |
                    {{ isBrightnessFadeEnabled and 
                      (
                        ( (currentBrightness | int(0)) != (endBrightness | int(0)) ) 
                        and
                        ( (currentBrightness | int(0)) != (expectedBrightness | int(0)) ) 
                      )
                      }}
              then:
                - if:
                    - condition: template
                      value_template: |
                        {{ shouldUseNativeLampTransitionsToo }}
                  then:
                    - if:
                        - condition: template
                          value_template: |
                            {{ (lampBrightnessScale == "zeroToTwoFiftyFive") }}
                      then:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness: "{{ expectedBrightness }}"
                            transition: "{{ transitionTimeParameterInSeconds }}"
                          action: light.turn_on
                          continue_on_error: true
                      else:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness_pct: "{{ expectedBrightness }}"
                            transition: "{{ transitionTimeParameterInSeconds }}"
                          action: light.turn_on
                          continue_on_error: true
                  else:
                    - if:
                        - condition: template
                          value_template: |
                            {{ (lampBrightnessScale == "zeroToTwoFiftyFive") }}
                      then:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness: "{{ expectedBrightness }}"
                          action: light.turn_on
                          continue_on_error: true
                      else:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            brightness_pct: "{{ expectedBrightness }}"
                          action: light.turn_on
                          continue_on_error: true
                - if:
                    - condition: template
                      value_template: |
                        {{ isDebugMode }}
                  then:
                    - data:
                        message: >
                          Set {{ lightFriendlyName }} to {{ expectedBrightness | int(0) }} brightness. (Linear brightness would have been
                          {{ linearExpectedBrightness | int(0) }}.) Delay is {{
                          delayInMilliseconds | int(0) }} ms. Elapsed time is {{
                          (as_timestamp(now()) - startTimestamp) | round(2) }}
                          seconds. (endBrightness is {{ endBrightness}}.)
                        level: warning
                      action: system_log.write
              else:
                - if:
                    - condition: template
                      value_template: |
                        {{ isColorFadeEnabled and 
                          (
                            ( (currentColorTemperature | int(0)) != (endColorTemperature | int(0)) ) 
                            and
                            ( (currentColorTemperature | int(0)) != (expectedColorTemperature | int(0)) ) 
                          )
                        }}
                  then:
                    - if:
                        - condition: template
                          value_template: |
                            {{ shouldUseNativeLampTransitionsToo }}
                      then:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            color_temp_kelvin: "{{ expectedColorTemperature }}"
                            transition: "{{ transitionTimeParameterInSeconds | round(3) }}"
                          action: light.turn_on
                          continue_on_error: true
                      else:
                        - target:
                            entity_id: "{{ light }}"
                          data:
                            color_temp_kelvin: "{{ expectedColorTemperature }}"
                          action: light.turn_on
                          continue_on_error: true
                    - if:
                        - condition: template
                          value_template: |
                            {{ isDebugMode }}
                      then:
                        - data:
                            message: >
                              Set {{ lightFriendlyName }} to {{
                              expectedColorTemperature | int(0) }}°K. Delay is {{
                              delayInMilliseconds | int(0) }} ms. Elapsed time is
                              {{ (as_timestamp(now()) - startTimestamp) |
                              round(2) }} seconds. (endColorTemperature is {{
                              endColorTemperature}}.)
                            level: warning
                          action: system_log.write
        - delay:
            milliseconds: "{{ delayInMilliseconds | float(0) }}"
        - if:
            - condition: template
              value_template: >
                {## init ##} {% set shouldStop = false %} {% if
                isStopEntityEnabled %}
                  {% if (shouldInvertTheValueOfTheStopEntity is defined) and shouldInvertTheValueOfTheStopEntity %}
                    {% if not bool(states(stopEntity), false) %}
                      {% set shouldStop = true %}
                    {% endif %}
                  {% else %}
                    {% if bool(states(stopEntity), false) %}
                      {% set shouldStop = true %}
                    {% endif %}
                  {% endif %}
                {% endif %} {{ shouldStop }}
          then:
            - if:
                - condition: template
                  value_template: >
                    {{ (shouldInvertTheValueOfTheStopEntity is defined) and
                    shouldInvertTheValueOfTheStopEntity }}
              then:
                - variables:
                    stopMessage: >
                      {% set stopMessage = "Stopped Ashley’s Light Fader because
                      " + (stopEntityFriendlyName | string) + " turned off." %}
                      {{ stopMessage }}
                - if:
                    - condition: template
                      value_template: |
                        {{ isDebugMode }}
                  then:
                    - data:
                        message: |
                          {{ stopMessage }}
                        level: warning
                      action: system_log.write
                - stop: |
                    {{ stopMessage }}
              else:
                - variables:
                    stopMessage: >
                      {% set stopMessage = "Stopped Ashley’s Light Fader because
                      " + (stopEntityFriendlyName | string) + " turned on." %}
                      {{ stopMessage }}
                - if:
                    - condition: template
                      value_template: |
                        {{ isDebugMode }}
                  then:
                    - data:
                        message: |
                          {{ stopMessage }}
                        level: warning
                      action: system_log.write
                - stop: |
                    {{ stopMessage }}
        - variables:
            currentBrightness: |+
              {% if is_state(light, 'off') %}
                {% set currentLightLevel = 0 %}
                {{ currentLightLevel }}
              {% else %}
                {% set rawLightLevel = state_attr(light, 'brightness') %}
              
                {## If a light is off, its brightness will return 'None'. So first check if there's a valid brightness before getting the value ##}
              
                {% if rawLightLevel %}
                	{% set currentLightLevel = rawLightLevel | int(0) %}
                {% else %}
                	{% set currentLightLevel = 0 %}
                {% endif %}
                {% if lampBrightnessScale == "zeroToOneHundred" %}
                  {% set currentLightLevel = (currentLightLevel / 2.55) | round(0) %}
                {% endif %}
              
                {{ currentLightLevel }}
              {% endif %}
            brightnessDifferenceFromExpected: >
              {{ ( (currentBrightness | int(0)) - (expectedBrightness | int(0)) ) |
              abs }}
            isBrightnessDifferenceAboveAutoCancelThreshold: >
              {% if shouldAutoCancelTheFadeIfTheLampBrightnessIsManuallyChanged
              %}
                {{ (brightnessDifferenceFromExpected | int(0)) >= (normalizedAutoCancelThreshold | int(0)) }}
              {% else %}
                {{ false }}
              {% endif %}
        - if:
            - condition: template
              value_template: |
                {{ (
                    (
                    (not (shouldStopIfTheLampIsTurnedOffDuringTheFade is defined)) or
                       (
                         (shouldStopIfTheLampIsTurnedOffDuringTheFade is defined) and 
                         shouldStopIfTheLampIsTurnedOffDuringTheFade
                        )
                     )
                     and ( ((expectedBrightness | int(0)) > 0) and ((currentBrightness | int(0)) == 0) )
                   ) 
                  }}
          then:
            - variables:
                stopMessage: >
                  {% set stopMessage = "Stopped Ashley’s Light Fader because " +
                  (lightFriendlyName | string) + " was turned off during the
                  fade." %}

                  {{ stopMessage }}
            - if:
                - condition: template
                  value_template: |
                    {{ isDebugMode }}
              then:
                - data:
                    message: |
                      {{ stopMessage }}
                    level: warning
                  action: system_log.write
            - stop: |
                {{ stopMessage }}
        - if:
            - condition: template
              value_template: >
                {{ shouldAutoCancelTheFadeIfTheLampBrightnessIsManuallyChanged
                and isBrightnessDifferenceAboveAutoCancelThreshold }}
          then:
            - variables:
                stopMessage: >
                  {% if lampBrightnessScale == "zeroToTwoFiftyFive" %}
                    {% set currentBrightnessAsPercentage = (currentBrightness / 2.55) | round(0) %}
                    {% set expectedBrightnessAsPercentage = (expectedBrightness / 2.55) | round(0) %}
                    {% set differenceFromExpectedAsPercentage = (brightnessDifferenceFromExpected / 2.55) | round(0) %}
                  {% else %}
                    {% set currentBrightnessAsPercentage = currentBrightness %}
                    {% set expectedBrightnessAsPercentage = expectedBrightness %}
                    {% set differenceFromExpectedAsPercentage = brightnessDifferenceFromExpected %}
                  {% endif %}


                  {% set stopMessage = "Stopped Ashley’s Light Fader because " +
                  (lightFriendlyName | string) + " was found to be at " +
                  (currentBrightnessAsPercentage | string) + "%, a difference of
                  " + (differenceFromExpectedAsPercentage | string) + "
                  percentage points from the expected brightness of " +
                  (expectedBrightnessAsPercentage | string) + "%, which is
                  higher than the auto-cancel threshold of " +
                  (autoCancelThreshold | string) + " percentage points." %}

                  {{ stopMessage }}
            - if:
                - condition: template
                  value_template: |
                    {{ isDebugMode }}
              then:
                - data:
                    message: |
                      {{ stopMessage }}
                    level: warning
                  action: system_log.write
            - stop: |
                {{ stopMessage }}
      until:
        - condition: template
          value_template: |+
            {% if (as_timestamp(now()) >= endTimestamp) %}
              {{ true }}
            {% elif isBrightnessFadeEnabled or isColorFadeEnabled %}
              {## init ##}
              {% set okayToEndTheLoop = true %}
            
              {% if isBrightnessFadeEnabled %}
                {% set rawLightLevel = state_attr(light, 'brightness') %}
            
                {## If a light is off, its brightness will return "None"? So first check if there's a valid brightness before getting the value ##}
                {% if rawLightLevel %}
                	{% set currentLightLevel = rawLightLevel | int(0) %}
                {% else %}
                	{% set currentLightLevel = 0 %}
                {% endif %}
                {% if lampBrightnessScale == "zeroToOneHundred" %}
                  {% set currentLightLevel = (currentLightLevel / 2.55) | round(0) %}
                {% endif %}
            
                {% if (endBrightness >= startBrightness) and (currentLightLevel < endBrightness) %}
                  {% set okayToEndTheLoop = false %}
                {% elif (endBrightness < startBrightness) and (currentLightLevel > endBrightness) %}
                  {% set okayToEndTheLoop = false %}
                {% endif %}
              {% endif %}
            
              {## The "and okayToEndTheLoop" part is to avoid needlessly checking whether the color temp fails if the brightness check already fails ##}
              {% if isColorFadeEnabled and okayToEndTheLoop %}

                {## init ##}
                {% set currentColorTemperature = -1 %}
            
                {% set rawColorTemperature = state_attr(light, 'color_temp_kelvin') %}
            
                {## If a light is off or if its color temperature is defined using a non-Kelvin color system, its color temperature will return "None"? So first check if there's a valid color temperature ##}
                {% if rawColorTemperature %}
            
                  {% if is_number(rawColorTemperature) %}
                    {% set currentColorTemperature = rawColorTemperature | int(0) %}
                  {% endif %}
                {% endif %}
            
                {% if (currentColorTemperature != -1) and (endColorTemperature >= startColorTemperature) and (currentColorTemperature < endColorTemperature) %}
                  {% set okayToEndTheLoop = false %}
                {% elif (currentColorTemperature != -1) and (endColorTemperature < startColorTemperature) and (currentColorTemperature > endColorTemperature) %}
                  {% set okayToEndTheLoop = false %}
                {% endif %}

              {% endif %}
            
              {{ okayToEndTheLoop }}
            
            {% else %}
              {## neither isBrightnessFadeEnabled nor isColorFadeEnabled ##} 
              {{ true }}
            {% endif %}
  - variables:
      currentBrightness: |+
        {% set rawLightLevel = state_attr(light, 'brightness') %}
        
        {## If a light is off, its brightness will return 'None'. So first check if there's a valid brightness before getting the value ##}
        {% if rawLightLevel %}
        	{% set currentLightLevel = rawLightLevel | int(0) %}
        {% else %}
        	{% set currentLightLevel = 0 %}
        {% endif %}
        {% if lampBrightnessScale == "zeroToOneHundred" %}
          {% set currentLightLevel = (currentLightLevel / 2.55) | round(0) %}
        {% endif %}
        
        {{ currentLightLevel }}
      brightnessDifferenceFromExpected: |
        {{ ( (currentBrightness | int(0)) - (endBrightness | int(0)) ) | abs }}
      isBrightnessDifferenceAboveAutoCancelThreshold: |
        {% if shouldAutoCancelTheFadeIfTheLampBrightnessIsManuallyChanged %}
          {{ (brightnessDifferenceFromExpected | int(0)) > (normalizedAutoCancelThreshold | int(0)) }}
        {% else %}
          {{ false }}
        {% endif %}
      currentColorTemperature: >
        {## init ##} {% set currentColorTemperature = -1 %} {% if
        isColorFadeEnabled %}
          {% set rawColorTemperature = state_attr(light, 'color_temp_kelvin') %}

          {## If a light is off or if its color temperature is defined using a non-Kelvin color system, its color temperature will return "None"? So first check if there's a valid color temperature ##}
          {% if rawColorTemperature %}

            {% if is_number(rawColorTemperature) %}
              {% set currentColorTemperature = rawColorTemperature | int(0) %}
            {% endif %}
          {% endif %}
        {% endif %}

        {{ currentColorTemperature }}
  - if:
      - condition: template
        value_template: |
          {{ (
                (not shouldAutoCancelTheFadeIfTheLampBrightnessIsManuallyChanged) or 
                (not isBrightnessDifferenceAboveAutoCancelThreshold)
              ) 
              and
              (
                (not (shouldStopIfTheLampIsTurnedOffDuringTheFade is defined)) or
                (not shouldStopIfTheLampIsTurnedOffDuringTheFade) or
                ((currentBrightness | int(0)) != 0)
              )
              }}
    then:
      - if:
          - condition: template
            value_template: |
              {{ isColorFadeEnabled and isBrightnessFadeEnabled 
              and
              (
                (isBrightnessFadeEnabled and ((currentBrightness | int(0)) != (endBrightness | int(0))) )
                or (isColorFadeEnabled and ((currentColorTemperature | int(0)) != (endColorTemperature | int(0))) )
              )
              }}
        then:
          - if:
              - condition: template
                value_template: |
                  {{ (lampBrightnessScale == "zeroToTwoFiftyFive") }}
            then:
              - target:
                  entity_id: "{{ light }}"
                data:
                  brightness: "{{ endBrightness }}"
                  color_temp_kelvin: "{{ endColorTemperature }}"
                action: light.turn_on
                continue_on_error: true
            else:
              - target:
                  entity_id: "{{ light }}"
                data:
                  brightness_pct: "{{ endBrightness }}"
                  color_temp_kelvin: "{{ endColorTemperature }}"
                action: light.turn_on
                continue_on_error: true
        else:
          - if:
              - condition: template
                value_template: >
                  {{ isBrightnessFadeEnabled and ((currentBrightness | int(0)) !=
                  (endBrightness | int(0))) }}
            then:
              - if:
                  - condition: template
                    value_template: |
                      {{ (lampBrightnessScale == "zeroToTwoFiftyFive") }}
                then:
                  - target:
                      entity_id: "{{ light }}"
                    data:
                      brightness: "{{ endBrightness }}"
                    action: light.turn_on
                    continue_on_error: true
                else:
                  - target:
                      entity_id: "{{ light }}"
                    data:
                      brightness_pct: "{{ endBrightness }}"
                    action: light.turn_on
                    continue_on_error: true
            else:
              - if:
                  - condition: template
                    value_template: >
                      {{ isColorFadeEnabled and ((currentColorTemperature | int(0))
                      != (endColorTemperature | int(0))) }}
                then:
                  - target:
                      entity_id: "{{ light }}"
                    data:
                      color_temp_kelvin: "{{ endColorTemperature }}"
                    action: light.turn_on
                    continue_on_error: true
      - if:
          - condition: template
            value_template: |
              {{ isDebugMode }}
        then:
          - if:
              - condition: template
                value_template: |
                  {{ isColorFadeEnabled and isBrightnessFadeEnabled }}
            then:
              - data:
                  message: >
                    Set {{ lightFriendlyName }} to {{ endBrightness | int(0) }}
                    brightness and {{ endColorTemperature | int(0) }}°K. Elapsed
                    time is {{ (as_timestamp(now()) - startTimestamp) | round(2)
                    }} seconds.
                  level: warning
                action: system_log.write
            else:
              - if:
                  - condition: template
                    value_template: |
                      {{ isBrightnessFadeEnabled }}
                then:
                  - data:
                      message: >
                        Set {{ lightFriendlyName }} to {{ endBrightness | int(0) }}
                        brightness. Elapsed time is {{ (as_timestamp(now()) -
                        startTimestamp) | round(2) }} seconds.
                      level: warning
                    action: system_log.write
                else:
                  - if:
                      - condition: template
                        value_template: |
                          {{ isColorFadeEnabled }}
                    then:
                      - data:
                          message: >
                            Set {{ lightFriendlyName }} to {{
                            endColorTemperature | int(0) }}°K. Elapsed time is {{
                            (as_timestamp(now()) - startTimestamp) | round(2) }}
                            seconds.
                          level: warning
                        action: system_log.write
mode: parallel
max: 15
icon: mdi:lightbulb-on-50
