Advanced Scheduled Vacuum Room Script

Hi there brains trust!

I am new to Home Assistant and have been working to setup some functionality to improve some scheduling.

I have done hours worth of work trying to reverse engineer scripts and automations after reading through various threads with similar questions however, I am struggling to get to my goal. My intention is to create a script which iterates through a list of rooms which the Roborock stores as ‘Segments’. I have listed a few examples here. Ideally, I can have this script call on a weekday and within the script, I will specify the segment ID/rooms to be cleaned. It will then repeat the script for each one of the listed “for_each” segments. Ideally, I’d also like for it to wait to complete the first segment before going to the next itteration of the “for_each”. It appears to be just running all of these checks at once however.

I also have a helper setup to store the last time the clean was run for a set area. I am using this to store when it was last run so that if I have manually cleaned that room within the specified period, it will skip this room and go to the next in the list.

I have stripped this back to almost nothing to try and start from the bottom but now I am left wondering how to even achieve that. Hopefully someone who knows more about it can assist. I appreciate any guidance. :slight_smile:

I have the below:

alias: Advanced Vacuum Loop
sequence:
  - repeat:
      for_each:
        - "17" #Entryway
        - "23" #Kitchen
        - "24" #Dining Room
      sequence:
        - choose:
            - conditions:
                - condition: and
                  conditions:
                    - condition: template
                      value_template: >-
                        {{ as_timestamp(now()) - (10) >
                        as_timestamp(states('input_datetime.{{ repeat.item }}'))
                        }}
                    - condition: or
                      conditions:
                        - condition: template
                          value_template: >-
                            {{ is_state('vacuum.roborock_s6', 'returning') or
                            is_state('vacuum.roborock_s6', 'docked') }}
              sequence:
                - action: vacuum.send_command
                  metadata: {}
                  data:
                    command: app_segment_clean
                    params:
                      - {{ repeat.item}
                    entity_id: vacuum.roborock_s6
                - wait_template: >-
                    {{ is_state('vacuum.roborock_s6', 'returning') or
                    is_state('vacuum.roborock_s6', 'docked') }}
                  continue_on_timeout: false
                - action: input_datetime.set_datetime
                  metadata: {}
                  data:
                    timestamp: "{{ now().timestamp() }}"
                  target:
                    entity_id: input_datetime.{{ repeat.item }}

Making some assumptions here but I think this is what you’re looking for

alias: Advanced Vacuum Loop
sequence:
  - repeat:
      for_each:
        - datetime: input_datetime.entryway
          value: 17
        - datetime: input_datetime.kitchen
          value: 23
        - datetime: input_datetime.dining_room
          value: 24
      sequence:
        - choose:
            - conditions:
                - condition: or
                  conditions:
                    - condition: template
                      value_template: >-
                        {{ now() - states(repeat.item.datetime) | as_datetime | as_local > timedelta(seconds=10) }}
                    - condition: template
                      value_template: >-
                        {{ is_state('vacuum.roborock_s6', 'returning') or is_state('vacuum.roborock_s6', 'docked') }}
              sequence:
                - action: vacuum.send_command
                  metadata: {}
                  data:
                    command: app_segment_clean
                    params:
                      - "{{ repeat.item.value }}"
                    entity_id: vacuum.roborock_s6
                - wait_template: >-
                    {{ is_state('vacuum.roborock_s6', 'returning') or is_state('vacuum.roborock_s6', 'docked') }}
                  continue_on_timeout: false
                - action: input_datetime.set_datetime
                  metadata: {}
                  data:
                    datetime: "{{ now() }}"
                  target:
                    entity_id: "{{ repeat.item.datetime }}"

You were doing a number of incorrect things.

  1. Using templates without quotes
  2. Using templates within templates (It’s a template, don’t nest {{ }} inside {{ }}, it’s already executing code)
  3. You had an or condition with only 1 choice inside an and condition. I’m fairly sure you just wanted an or condition and no and condition.

Thanks for the start petro!

I will try and qualify some of what I was thinking, but with plain text as I am unsure of the syntax.

alias: Advanced Vacuum Loop
sequence:
  - repeat:
      for_each:
        #here i need to create a list of variables which I can add or remove from. In this case, the numbers are 'segment IDs' which I can send to my vacuum using the command 'app_segment_clean'.
        #e.g If i can go through the value 17, it will clean my 'Entryway', then value 23 cleans my kitchen etc.
        #These weren't input_datetime values, but just an array of integers I suppose.
      sequence:
        - choose:
            - conditions:
                - condition: or
                  conditions:
                    - condition: template
                      value_template: >-
                      #The first value template here is checking a 'helper' which I have being set once this action completes succesfully further below.
                      #It will be set to the time this function is last trigerred in the script. I then compare the "NOW" time against this stored helper input_datetime so that I can check if the room was recently cleaned and not double up.
                      #My thoughts here with the nested 'repeat.item' inside the template, is that I can get the repeat.item values and insert them into my template to check this value is true for each of the values in the variable array.
                        {{ as_timestamp(now()) - (10) >
                        as_timestamp(states('input_datetime.{{ repeat.item }}'))
                        }}
                    - condition: template
                      value_template: >-
                        {{ is_state('vacuum.roborock_s6', 'returning') or is_state('vacuum.roborock_s6', 'docked') }}
                        #I expect that this first formula will look something like this below
                        #{{ as_timestamp(now()) - (I will put a value in seconds here to represent how long I want to make the time before it re-cleans the same room) >
                        #as_timestamp(states('input_datetime.last_clean_{{ repeat.item }}')) <---This section here will say "_.last_clean_17" and collect the value that has been stored in the helper being a date/time value.
                        #}}
              sequence:
                - action: vacuum.send_command
                  metadata: {}
                  data:
                    command: app_segment_clean
                    params: #This parameter section will need to grab the 'segment IDs' from above and for each time it send the command it will send app_segment_clean params: - 17, then it will send - 23 etc.
                      - "{{ repeat.item.value }}"
                    entity_id: vacuum.roborock_s6
                - wait_template: >-
                    {{ is_state('vacuum.roborock_s6', 'returning') or is_state('vacuum.roborock_s6', 'docked') }}
                  continue_on_timeout: false
                - action: input_datetime.set_datetime
                #This action is now collecting the current datetime that this action is run and will then update the helper value with the current time so I can vallidate against it.
                #The logic Id like here is that if I decide to manually select a room to clean at some time within my window, I will have it update this value. If the last clean now falls within my tolerance window, this room will be skipped in the Advanced Vacuum schedule script as the value will become false.
                  metadata: {}
                  data:
                    datetime: "{{ now() }}"
                  target:
                    entity_id: "{{ repeat.item.datetime }}"

Ok what I wrote does exactly what you want then.

Hi Petro, thanks again for the help.
In this case, why are is it stored like this?

      for_each:
        - datetime: input_datetime.entryway
          value: 17
        - datetime: input_datetime.kitchen
          value: 23
        - datetime: input_datetime.dining_room
          value: 24

Ideally, in the end, I’d like to be able to map each rooms ‘segment’ number to a friendly text that I can see without having to refer back to my notes on which number represents a room.

      for_each:
        - loungeroom: '17'
        - entryway: '16'
        - kitchen: '23'

I have been referring to this example from another user. You will see I left a comment today as I am a little lost with how they are achieving this:

I am learning lots along the way with Syntax, languages and the like… plenty I don’t know, that’s for certain!

Please note, you will need to open this link as the pasted code from the link is not the final result from this user.

If you use that format, you have to duplicate the list as a dictionary… What you have there is a list of dictionaries with a dynamic key. If you use that over what I wrote, the code is significantly more complicated. I can post that code in a bit, on mobile at the moment

1 Like

I don’t see why you think this:

is any different than this

where it would need you to take notes. It’s the same data, just presented slightly differently.

In regards to that guys post, he has a script that executes the run vacuum command, so you can’t see what he’s doing inside it. He’s likely has and maintains a dictionary that maps room names to input datetimes and numerical values for the rooms.

Anyways, I digress.

This will do what you want:

alias: Advanced Vacuum Loop
sequence:
  - variables:
      config:
        entryway: 17
        kitchen: 23
        dining_room: 24
  - repeat:
      for_each: >
        [{% for k, v in config.items() %}{{ dict(datetime='input_datetime.' ~ k, value=v) }},{% endfor %}]
      sequence:
        - choose:
            - conditions:
                - condition: or
                  conditions:
                    - condition: template
                      value_template: >-
                        {{ now() - states(repeat.item.datetime) | as_datetime | as_local > timedelta(seconds=10) }}
                    - condition: template
                      value_template: >-
                        {{ is_state('vacuum.roborock_s6', 'returning') or is_state('vacuum.roborock_s6', 'docked') }}
              sequence:
                - action: vacuum.send_command
                  metadata: {}
                  data:
                    command: app_segment_clean
                    params:
                      - "{{ repeat.item.value }}"
                    entity_id: vacuum.roborock_s6
                - wait_template: >-
                    {{ is_state('vacuum.roborock_s6', 'returning') or is_state('vacuum.roborock_s6', 'docked') }}
                  continue_on_timeout: false
                - action: input_datetime.set_datetime
                  metadata: {}
                  data:
                    datetime: "{{ now() }}"
                  target:
                    entity_id: "{{ repeat.item.datetime }}"

Personally, I wouldn’t even have a loop. I’d just spool the data together and output the entire list at once with one command. Then the vacuum would just do all the rooms without needing to loop.

Thanks once again Petro! I guess I was more interested in learning the functionality of what is possible for future endeavours and not just going the datetime field route.

I will give this a try soon and continue learning I suppose!

I don’t think it will work because the command params requires a segments key according to the documentation.

https://python-roborock.readthedocs.io/en/latest/api_commands.html#app-segment-clean

Yes, the intention with the loop is to hopefully create the ability to pause between loops if something changes. i.e. If i only want the vacuum to run when I am not home, it will check this before it loops back again to continue to the next room.

Anyway, it’s just ongoing project and learning I suppose. :slight_smile:

Yeah, you’re over complicating this for sure. Just make an automation based on anyone arriving or leaving home.

- alias:  Vacuum when not home
  triggers:
    - id: start
      trigger: state
      entity_id: zone.home
      state: 0
      for: 
        minutes: 1
    - id: stop
      trigger: numeric_state
      entity_id: zone.home
      above: 0
  variables:
    config:
      entryway: 17
      kitchen: 23
      dining_room: 24
    segments: "{{ config.values() | list }}"
  conditions:
    - condition: state
      entity_id: vacuum.roborock_s6
      state:
      - returning
      - docked
  actions:
    - choose:
          # START VACUUM
        - conditions:
            - condition: trigger
              id: start
          action:
            - action: vacuum.send_command
              data:
                command: app_segment_clean
                params:
                   segments: "{{ segments }}"
                   repeat: 1

          # STOP VACUUM
        - conditions:
            - condition: trigger
              id: stop
          action:
            - action: vacuum.send_command
              data:
                command: stop_segment_clean

You may want to make it return to the base in the stop vacuum section. I’d probably add a counter helper to ensure you only vacuum once a day. And I’d probably add a condition to only do this on weekdays or something like that.

1 Like

Agree. The loop is an issue. I very much prefer to build something that gathers state and responds in kind at runtime.

I do what you suggest. Twice a day I fire a script that collects information similar to Op… Last cleaned, days allowed between cleaning, is the room ready(some I require a manual check) the scheduler fires. And it goes. None of this loop nonsense. If it needs to charge or clean it’s mop halfway it does. The it goes back to the dock and waits for the next trigger. (usually on less than 4 hours)

I could also easily trigger on vacancy (just fire the scheduler script…)

But the trick is the scheduler is ALWAYS active - as templates - with state of if I fire the schedule now -who goes? (list of room names) The the trigger can just about be anything.

As for resolving the room names. I created a macro that resolves names from room numbers and vice versa. Then I use that macro when I need to refer to a room by name.