Break repeat-loop and send actionable notification every time

I have a script that gradually changes the light over a period of time.
I have it set up that when I manually change the lights, it sends an actionable notification after 10 minutes to ask if I want to continue the light change from the current settings.
If I select “Yes”, it will continue immediately, if not, it should keep the lights at the current settings and then ask again in 10 minutes.
Also, if I select “Yes” then change the lights again after a while, it should ask again after 10 minutes.
Now I need help configuring this in my script. Is there an easier way where I don’t have to repeat the same actions (send notification → if “Yes” → continue, etc.)?

This is my script:

mode: single
variables:
  start_time: "08:00:00"
  end_time: "8:20:00"
  steps_per_minute: 5
  start_kelvin: 2200
  end_kelvin: 4500
  start_bright: 20
  end_bright: 85
  manual_override: input_boolean.manual_override
  duration: >
    {% set start = strptime(start_time, '%H:%M:%S') %} {% set end =
    strptime(end_time, '%H:%M:%S') %} {% if start > end %}
      {% set end = end + timedelta(days=1) %}
    {% endif %} {{ (end - start).total_seconds() / 60 }}
  intervals: "{{ duration * steps_per_minute | int }}"
  step_kelvin: "{{ ((end_kelvin - start_kelvin) / intervals) }}"
  step_bright: "{{ ((end_bright - start_bright) / intervals) }}"
sequence:
  - condition: zone
    entity_id: person.elise
    zone: zone.home
  - variables:
      step_kelvin: |
        {% if start_kelvin < end_kelvin %}
          {{ (end_kelvin - start_kelvin) / intervals }}
        {% else %}
          {{ (start_kelvin - end_kelvin) / intervals * -1 }}
        {% endif %}
      step_bright: |
        {% if start_bright < end_bright %}
          {{ (end_bright - start_bright) / intervals }}
        {% else %}
          {{ (start_bright - end_bright) / intervals * -1 }}
        {% endif %}
  - target:
      entity_id:
        - light.keuken
        - light.woonkamer
    data:
      brightness_pct: "{{ start_bright }}"
      color_temp_kelvin: "{{ start_kelvin }}"
    action: light.turn_on
  - delay:
      seconds: 2
  - repeat:
      until:
        - condition: or
          conditions:
            - condition: state
              entity_id: input_boolean.manual_override
              state: "on"
            - condition: template
              value_template: "{{ repeat.index == intervals | int }}"
      sequence:
        - target:
            entity_id:
              - light.keuken
              - light.woonkamer
          data:
            brightness_pct: "{{ (start_bright + (step_bright * repeat.index)) | int }}"
            color_temp_kelvin: "{{ (start_kelvin + (step_kelvin * repeat.index)) | int }}"
          action: light.turn_on
        - delay:
            seconds: 2
        - choose:
            - conditions:
                - condition: state
                  entity_id: input_boolean.manual_override
                  state: "on"
              sequence:
                - delay:
                    hours: 0
                    minutes: 0
                    seconds: 10
                - action: notify.mobile_app_pixel_8
                  metadata: {}
                  data:
                    title: Continue lights?
                    data:
                      actions:
                        - action: CONTINUE_LIGHTS
                          title: "Yes"
                        - action: NO_ACTION
                          title: "No"
                    message: Continue?
                - wait_for_trigger:
                    - trigger: event
                      event_type: mobile_app_notification_action
                      event_data:
                        action: CONTINUE_LIGHTS
                - if:
                    - condition: template
                      value_template: >-
                        {{ wait.trigger and wait.trigger.event.data.action ==
                        'CONTINUE_LIGHTS' }}
                  then:
                    - variables:
                        current_kelvin: "{{ state_attr('light.keuken', 'color_temp_kelvin') }}"
                        current_bright: >-
                          {{ state_attr('light.keuken', 'brightness') * 100 /
                          255 }}
                        duration_until: >
                          {% set now_time = now().replace(tzinfo=None) %} {% set
                          end = strptime(end_time,
                          '%H:%M:%S').replace(year=now_time.year,
                          month=now_time.month, day=now_time.day) %} {% if
                          now_time > end %} {% set end = end + timedelta(days=1)
                          %} {% endif %} {{ ((end - now_time).total_seconds() /
                          60) | int }}
                        new_intervals: "{{ duration_until * steps_per_minute }}"
                        new_step_kelvin: |
                          {% if current_kelvin < end_kelvin %}
                            {{ (end_kelvin - current_kelvin) / new_intervals }}
                          {% else %}
                            {{ (current_kelvin - end_kelvin) / new_intervals * -1 }}
                          {% endif %}
                        new_step_bright: |
                          {% if current_bright < end_bright %}
                            {{ (end_bright - current_bright) / new_intervals }}
                          {% else %}
                            {{ (current_bright - end_bright) / new_intervals * -1 }}
                          {% endif %}
                    - repeat:
                        sequence:
                          - action: light.turn_on
                            metadata: {}
                            data:
                              brightness_pct: >-
                                {{ (current_bright + (new_step_bright *
                                repeat.index)) }}
                              color_temp_kelvin: >-
                                {{ (current_kelvin + (new_step_kelvin *
                                repeat.index)) }}
                            target:
                              entity_id: light.keuken
                        until:
                          - condition: or
                            conditions:
                              - condition: state
                                entity_id: input_boolean.manual_override
                                state: "on"
                              - condition: template
                                value_template: "\"{{ repeat.index == intervals }}\""
                  else:
                    - delay:
                        hours: 0
                        minutes: 0
                        seconds: 10
                    - action: notify.mobile_app_pixel_8
                      metadata: {}
                      data:
                        title: Continue lights?
                        data:
                          actions:
                            - action: CONTINUE_LIGHTS
                              title: "Yes"
                            - action: NO_ACTION
                              title: "No"
                        message: Continue?
alias: 08:00 (Duplicate)
description: ""

I suggest breaking this up into multiple pieces.

For example:

  1. An input_boolean helper, to be used as described in the points below.

  2. An automation that runs as often as you want to change the light temperature in ordinary circumstances. (Every two seconds?) Use a time_pattern trigger. A condition for running the automation is that the input boolean is TRUE.

  3. An automation that runs when you want to prompt the user, for example, triggered by however you detect whether there was a manual change to the light state. Depending on the user response, set the input_boolean to TRUE or FALSE.

  4. An automation that runs when the input_boolean has been FALSE for some period of time, like 10 minutes, which prompts the user whether to resume. A state trigger with “for:” could work here, as one option. Set the input_boolean to TRUE if the user wants to resume.

You can probably factor the user prompt part out to a script to reduce code duplication between 3 and 4.

Does that seem like a good starting point?

Hey, thanks for your reply!
I’m not sure how this would work, how would the automation know where to restart the script from and how long to pause the script?

I am planning to use the script in an automation so I can determine the start time (not necessarily time based, could be based on template) and also so my input_boolean.manual_override is not triggered.

Your solution does seem what I’m looking for, since I also want to use this script in an automation when I get home, where the lights will turn on and after a couple minutes, will send me the notification asking if I want to start the script.
But I don’t see how it works. I’ve also never used time_pattern

The nice thing is that you wouldn’t really need to worry about restarting the script, since each of the automations would run pretty much instantaneously and quit. Instead of using delay statements in the YAML, you would “farm out” the delays to the time_pattern triggers.

I think the challenge you might be getting at in your response is your brightness_pct and color_temp_kelvin variables, which you’re computing based on repeat.index. One way to handle that is by creating an input_number helper that does the equivalent of what the loop’s repeat.index is doing in your script. Every time automation #1 runs, it reads the input_number to compute those values, then increments it by one and stores it.

For example, in your code that’s #2 in my list, you could do:

- variables:
    i: "{{ states('input_number.YOUR_INPUT_NUMBER')|int }}"
    brightness_pct: "{{ (start_bright + (step_bright * i)) | int }}"
    color_temp_kelvin: "{{ (start_kelvin + (step_kelvin * i)) | int }}" 
- action: input_number.set_value
  data:
    entity_id: input_number.YOUR_INPUT_NUMBER
    value: "{{ i + 1}}"

Your script already uses input_boolean.manual_override, so you took care of #1 in your original approach.

Since this would be an automation at the top level, you can trigger it every two seconds like this:

automation:
  - alias: update_light_temps
    trigger:
      - platform: time_pattern
        seconds: "/2"
    conditions:
      - condition: state
        entity_id: input_boolean.manual_override
        state: "off"  # Assuming that a value of FALSE means no override, keep updating
    action:
      # This action block will run every two seconds, so long as the input_boolean is FALSE.
      # So here you put your code that updates the temperature of your lights.

Is this enough to get started?

You’d probably want a separate automation to prompt the user. That automation should not use a wait_for_trigger; rather, you should have still a third automation, triggered by the mobile_app_notification_action event, that will update the input_boolean.manual_override.