Xiaomi Cloud Vacuum Map Extractor

Hm I’m not sure why it would have stopped working after working for so long?

Edit: IP changed on vacuum… after adjusting the host IP everything works great.
Thanks for your assistance and for all your hard work on this project.

-Russ

1 Like

@Ildar_Gabdullin
I think that you should wait for your vacuum to learn your map (do some full cleanups after enabling map storing), it shouldn’t change afterwards.

@homebot
Well, that explains a lot :smiley:

Hey @3_14 , thanks for your greate support here.
I have since a few days the following “hint”: two factor auth required (see logs)

2021-11-08 22_27_56-Window

I understand what Vacuum Map Extractor like to tell me, but logs don’t show me anything. (settings → logs). If I remeber corret, there is no option to activte the two factor auth. in Vacuum Map Extractor. A few days ago, there was everthing fine and no message or problems.

Could you assist me?
Thanks

Often a simple HA restart helps and the map shows up thereafter. See post #494.

You can also check out these issues: #157, #180

I think you are right!
After several cleanups rooms appeared:

Earlier there were no rooms:

And the most important thing - I do not observe misalignment so far!

Here are some rules to provide same mutual alignment between a floorplan & a vacuum’s map:

  1. Do not shift a charging dock.
  2. Enable “Save map” feature (post).
  3. Do not start cleaning by a vacuum’s “hardware” button.
  4. Do not start cleaning by using a vacuum.start service.
  5. Always start cleaning by using a “zone cleaning” (use this card) or a “segment cleaning” feature by calling this service:
service: vacuum.send_command
service_data:
  entity_id: '[[SENSOR_VACUUM]]'
  command: app_segment_clean
  params: [2, 3, 16, 5, 17]

Room numbers may be retrieved from a vacuum’s map attributes.

Piotr, thank you again for the job!

@ 3_14
Piotr, may be you know:
is it possible to use a template for the params option?

service: vacuum.send_command
service_data:
  entity_id: '[[SENSOR_VACUUM]]'
  command: app_segment_clean
  params: [2, 3, 16, 5, 17]

I would like to generate the params option dynamically dependingly on which rooms are selected for cleaning & how many passes are selected for each room.
I know that there is fantastic Vacuum Interactive Map Card - but so far I cannot integrate it to my floorplan and wanted to select rooms from UI.

Where do you want to achieve it? It should be possible to do in a script

To achieve it:

  1. Create 3 set of entities:
  • input_boolean for each room - whether to add the room to the “cleaning list” or not;
  • input_number for each room - for keeping / changing room numbers (taken from camera);
  • input_number for each room - for keeping counts of passes (1…3, for example).
  1. Create a group containing these input_boolean entities.

  2. Create template sensor:

sensor:
  - platform: template
    sensors:
      vacuum_clean_command:
        value_template: >-
          {% set ns = namespace(COMMAND = "") -%}
          {%- set ROOM_FLAGS = expand('group.vacuum_clean_rooms') -%}

          {%- for flag in ROOM_FLAGS -%}
            {%- if flag.entity_id | regex_match("input_boolean.vacuum_clean_room_", ignorecase=False) and
                   is_state(flag.entity_id,'on') -%}
              {%- set ROOM = flag.entity_id.split("vacuum_clean_room_")[1] -%}
              {%- set ROOM_NUMBER = states('input_number.vacuum_clean_room_number_' + ROOM)|int -%}
              {%- set CLEAN_COUNT = states('input_number.vacuum_clean_room_count_' + ROOM)|int -%}
              {%- set ROOM_NUMBER_STRING = (ROOM_NUMBER|string + ",") * CLEAN_COUNT -%}
              {%- set ns.COMMAND = ns.COMMAND + ROOM_NUMBER_STRING -%}
            {%- endif -%}
          {%- endfor -%}

          {%- set ns.COMMAND = (ns.COMMAND)[:-1] -%}
          [{{ ns.COMMAND }}]

where:

  • input_boolean.vacuum_clean_room_* - names for input_boolean entities;
  • input_number.vacuum_clean_room_number_* - names for room numbers;
  • input_number.vacuum_clean_room_count_* - names for counts;
  • group.vacuum_clean_rooms - name of the group.
  1. Create smth like this:
    image
    You make hide this card inside fold-entity-row and expand it when needed.

So, the question is - is it possible to use smth like this:

service: vacuum.send_command
service_data:
  entity_id: '[[SENSOR_VACUUM]]'
  command: app_segment_clean
  params: "{{ states('sensor.vacuum_clean_command') }}"

Yeah, it should be possible to do in a script:

vacuum_rooms:
  sequence:
    - service: vacuum.send_command
      data:
        entity_id: vacuum.xiaomi
        command: app_segment_clean
        params:  "{{ states('sensor.vacuum_clean_command') }}"

or without template sensor:

vacuum_rooms:
  sequence:
    - service: vacuum.send_command
      data:
        entity_id: vacuum.xiaomi
        command: app_segment_clean
        params:  >-
          {% set ns = namespace(COMMAND = "") -%}
          {%- set ROOM_FLAGS = expand('group.vacuum_clean_rooms') -%}

          {%- for flag in ROOM_FLAGS -%}
            {%- if flag.entity_id | regex_match("input_boolean.vacuum_clean_room_", ignorecase=False) and
                   is_state(flag.entity_id,'on') -%}
              {%- set ROOM = flag.entity_id.split("vacuum_clean_room_")[1] -%}
              {%- set ROOM_NUMBER = states('input_number.vacuum_clean_room_number_' + ROOM)|int -%}
              {%- set CLEAN_COUNT = states('input_number.vacuum_clean_room_count_' + ROOM)|int -%}
              {%- set ROOM_NUMBER_STRING = (ROOM_NUMBER|string + ",") * CLEAN_COUNT -%}
              {%- set ns.COMMAND = ns.COMMAND + ROOM_NUMBER_STRING -%}
            {%- endif -%}
          {%- endfor -%}

          {%- set ns.COMMAND = (ns.COMMAND)[:-1] -%}
          [{{ ns.COMMAND }}]
1 Like

Great, will try it next day! Thank you!

@3_14
Piotr, is it possible to support templates for yaml config file?
I think that it would be great to switch on/off displaying rooms etc…

maybe someday :wink:

1 Like

Piotr, I got some questions about your script, could you clarify?
I made a small chart of the script:

1) python_script.vacuum_send_command
   send first 5 segments

if command == 'app_zoned_clean':
     if `there more than 5 segments to clean`
          2) Wait until `vacuum = CLEANING`
          3) Wait until `vacuum != CLEANING`  (waiting for completion of cleaning the first 5 segments)
          4) Pause the vacuum
          5) script.vacuum_send_command_multiple_zones (recursive call of the same script)
             send rest of the segments
     endif
endif

Questions:

  1. What is a format of the params variable? Should it be like
[1, 2, 3, 45, 67, 8]

I see that you provided some checks & parsing & cleanup to ensure that the input data format is valid.

  1. Regarding the python_script.vacuum_send_command:
    According to the script, you are passing first 5 segments to the vacuum - does it mean that the vacuum can process max 5 segments per service call?

  2. According to the py script definition, that params value (i.e. first 5 segments) will be converted to

['1', '2', '3', '45', '67']

before sending to the vacuum. Does it mean that a vacuum accepts this format only?

  1. I think this line is not required for the first call:
    {%- set rest = cleaned | replace(firstBatch, "") | replace("[,[", "[[") -%}

  2. I think that the firstBatch and rest may be defined as variables. Just a speculation, I am still learning and never used variables in scripts so far…

Well, the script has been created over a year ago and some stuff has changed in HA . Right now it should be possible to make this script simpler - I think usage of python script can be avoided.

Answers:

  1. Yes
  2. Yes, as far as I know. You can try to check it in Xiaomi Home (my vacuum doesn’t have rooms)
  3. Actually it should be [1, 2, 3, 45, 67]
  4. Yes, it isn’t. I have left it there for consistency reasons
  5. Yes, the script has been created before variables in script appeared in HA :wink:

Thank you very much for your answers!
Now - about my problem.

I described my approach to generate a command for cleaning segments.
The template sensor with this code

          {% set ns = namespace(COMMAND = "") -%}
          {%- set ROOM_FLAGS = expand('group.vacuum_clean_rooms') -%}

          {%- for flag in ROOM_FLAGS -%}
            {%- if flag.entity_id | regex_match("input_boolean.vacuum_clean_room_", ignorecase=False) and
                   is_state(flag.entity_id,'on') -%}
              {%- set ROOM = flag.entity_id.split("vacuum_clean_room_")[1] -%}
              {%- set ROOM_NUMBER = states('input_number.vacuum_clean_room_number_' + ROOM)|int -%}
              {%- set CLEAN_COUNT = states('input_number.vacuum_clean_room_count_' + ROOM)|int -%}
              {%- set ROOM_NUMBER_STRING = (ROOM_NUMBER|string + ",") * CLEAN_COUNT -%}
              {%- set ns.COMMAND = ns.COMMAND + ROOM_NUMBER_STRING -%}
            {%- endif -%}
          {%- endfor -%}

          {%- set ns.COMMAND = (ns.COMMAND)[:-1] -%}
          {{ ns.COMMAND }}

generates this output:
image
Note the “Result type: list” text.
But using this script

script:
  vacuum_clean_rooms:
    alias: 'vacuum: Clean rooms'
    sequence:
      - service: vacuum.send_command
        data:
          entity_id: vacuum.xiaomi_roborock_s50
          command: app_segment_clean
          params: "{{states('sensor.vacuum_clean_command')}}"

gives an error:
Unable to send command to the vacuum: {'code': -10000, 'message': 'data for segment is not a number'}
Tried to discuss this issue, no solution so far.
Using the code directly in script (w/o that template sensor like you proposed before) does not help…

I started learning your script to find answers, not succeeded so far.

Can you try using service xiaomi_miio.vacuum_clean_segment?

service: xiaomi_miio.vacuum_clean_segment
data:
  entity_id: vacuum.xiaomi_roborock_s50
  segments: "{{states('sensor.vacuum_clean_command')}}"

Error message:
Failed to call service xiaomi_miio.vacuum_clean_segment. expected int for dictionary value @ data['segments']. Got None
image

:confused: it seems like a bug in HA template engine… So probably the only way is to launch it via python script

I haven’t played with that for a long time, but last time I checked, the segments were not “templatable”.