Xiaomi Vacuum Control

I really struggled to get it to work, but now I’ve reached a point where I would love to have your input on further development.

My Xiaomi Roborock S6 was one thing not controlable via OH, with that widget, I want to have room cleaning simplified.

You can find the code for it here: https://github.com/tetsuo-repo/oh3-widgets
Don’t forget that it’s still work in progress.

I can’t really help with the roomIds, I just tried ids between 16 and 24 and moved my vacuum to them one after another and mapped it finally.

What was really hard to figure out was all the javascript stuff, cause loads of functions seem not to be supported by OH - that’s why I did some horrible string concatination for command execution.
If anyone has a neat solution for that, I’d be keen to know.

Gonna focus on functions first, afterwards the look and feel.


The configuration requires a JSON-object to set the room names and icons in this format:

  {"icon":"bed_double", "roomId": 16, "label": "Schlafzimmer" },
  {"icon":"chat_bubble_2", "roomId": 17, "label": "Wohnzimmer" },
  {"icon":"dial", "roomId":18, "label": "Küche" },
  {"icon":"moon_zzz", "roomId":19, "label": "Gästezimmer" },
  {"icon":"drop", "roomId":20, "label": "Badezimmer" },
  {"icon":"person", "roomId": 22, "label": "Gäste-WC" },
  {"icon":"table", "roomId": 23, "label": "Abstellkammer" },
  {"icon":"device_laptop", "roomId": 24, "label": "Arbeitszimmer" }

Version 1.1.0



  • It’s possible to toggle buttons for multiple rooms and start cleaning afterwards.

Update to v1.2.0



  • Added status info for battery and cleaning duration
  • Added control button for vacuum, dock, pause, spot cleaning
  • Added fan control
  • Small UI improvements
  • Changed configration


Latest code:


Very nice! I just started a much simpler widget for the moment :slight_smile:

I implemented the commands as command options, that quite easy and space saving in the widget.

However I need to check my vacuum again, because the one thing that bothers me in OH is that I don’t have the rooms there. Might be a model issue though, as my rooms don’t have names in the official app either. Or how do you get the room ids?

When I understand that right, with the command options there is only one room at a time - or is it possible to have multiselect with it?

I just spent a while sending commands via rule editor and watched the official Xiaomi app which room would be highlighted on the map. So it was just try and error for me.

As I don’t have room options at all, I suppose the command options are always for all rooms.

I didn’t even know that you can send it to individual rooms through the binding. So the command is app_segment_cleanXX ?

If I understand that right, it depends on the version of your Roborock. With the S6 you can send it to one room with app_segment_clean[16] as you already mentioned, or to multiple with app_segment_clean[16,19,20,24].

You can try if get_room_mapping returns something valueable for you. I received a response but was unable to understand how it works together with the map / other features - thus I just tried every id like mentioned before.

Not sure if it is the perfect reference for it, but I found this repo really helpful: GitHub - marcelrv/XiaomiRobotVacuumProtocol at b532096c761ace86e7e3fd89fcd3d710cd1f4690

1 Like

Thank you. Segments don’t seem to be supported by s5. However due to my layout, I might be able to figure something out with zones/areas.

Hi Daniel,
i also use Rooms for my S5 and started with a selection Widget as shown here:

S5 Room select

I think they are not supported with the stock firmware but you can use

Valetudo from Hypfer
valetudo from rand256 Fork of Hypfer’s

            val mqttActions = getActions("mqtt","mqtt:broker:name")
            var json = "{\"command\": \"segmented_cleanup\",\"segment_ids\": ["
            g_vac_eG.members.forEach [ room |
                    if (room.state.toString == "ON")
                            val tags = room.getTags
                            val tag1 = tags.get(1)
                            json += "\"" + tag1 + "\","
                            logInfo("valetudo.rules", "..." + tag1)

            json = json.substring(0, json.length -1)
            json += "],\"repeats\": 1}"

With Valetudo you are also able to save different Maps in S5 and via MQTT/Web UI you can select which map should be loaded.


1 Like

Thank you! Guess it’s time to hack the S5!

@Tetsuo: here is my rather minimalistic widget:

uid: widget_robovac
tags: []
    - description: Group name (for details popup) and common item prefix (e.g. robovac when item names are like robovac_State)
      label: prefix
      name: prefix
      required: true
      type: TEXT
timestamp: Apr 13, 2021, 4:36:57 PM
component: f7-card
  outline: false
  noBorder: false
  padding: true
  noShadow: false
    --f7-card-margin-horizontal: 5px
    --f7-card-content-padding-vertical: 0px
    --f7-card-header-font-size: 14px
    --f7-card-header-font-weight: 600
    - component: oh-label-card
        action: options
        icon: f7:play
        actionItem: =props.prefix+'_ControlVacuum'
        item: =props.prefix+'_State'
          color: white
    - component: f7-chip
        bgColor: "=((items[props.prefix+'_BatteryLevel'].state > 60) ? 'green': ((items[props.prefix+'_BatteryLevel'].state > 30) ? 'yellow': 'orange'))"
        visible: =(items[props.prefix+'_BatteryLevel'].state < 100)
        text: =items[props.prefix+'_BatteryLevel'].state + '%'
          color: white
          size: 20
          z-index: 2
          bottom: 75px
          border-radius: 5px 5px 5px 5px
          margin-top: 2px
          right: -85%
    - component: f7-row
          - padding-bottom
          - padding-right
          - component: f7-col
                - component: oh-button
                    title: "=((items[props.prefix+'_WaterBoxState'].state == 'ON') ? 'Wassertank angesteckt': 'Kein Wassertank angesteckt')"
                    iconF7: drop
                    iconColor: "=((items[props.prefix+'_WaterBoxState'].state == 'ON') ? 'blue': 'gray')"
                    textColor: blue
          - component: f7-col
                - component: oh-button
                    title: "=((items[props.prefix+'_DoNotDisturb'].state == 'ON') ? 'DND aktiv': 'DND inaktiv')"
                    iconF7: moon_zzz
                    iconColor: "=((items[props.prefix+'_DoNotDisturb'].state == 'ON') ? 'yellow': 'gray')"
                    textColor: green
                    action: toggle
                    actionItem: =props.prefix+'_DoNotDisturb'
          - component: f7-col
                - component: oh-button
                    title: "=('Saugleistung:  '+items[props.prefix+'_ControlFanLevel'].displayState)"
                    text: =items[props.prefix+'_ControlFanLevel'].displayState
                    action: options
                    actionItem: =props.prefix+'_ControlFanLevel'
                    actionOptions: 101=Quiet,102=Balanced,103=Turbo,104=Max,105=Mob
                    iconF7: "=((items[props.prefix+'_ControlFanLevel'].state <= 101) ? 'ear': ((items[props.prefix+'_ControlFanLevel'].state <= 102) ? 'wind': (items[props.prefix+'_ControlFanLevel'].state <= 103) ? 'tornado': (items[props.prefix+'_ControlFanLevel'].state <= 104) ? 'hurricane': 'circle_bottomthird_split'))"
                    iconColor: "=((items[props.prefix+'_ControlFanLevel'].state <= 101) ? 'green': ((items[props.prefix+'_ControlFanLevel'].state <= 102) ? 'lightblue': (items[props.prefix+'_ControlFanLevel'].state <= 103) ? 'blue': (items[props.prefix+'_ControlFanLevel'].state <= 104) ? 'purple': 'white'))"
                    textColor: "=((items[props.prefix+'_ControlFanLevel'].state <= 101) ? 'green': ((items[props.prefix+'_ControlFanLevel'].state <= 102) ? 'lightblue': (items[props.prefix+'_ControlFanLevel'].state <= 103) ? 'blue': (items[props.prefix+'_ControlFanLevel'].state <= 104) ? 'purple': 'white'))"
          - component: f7-col
                - component: oh-button
                    title: Reinigungskarte ansehen
                    action: photos
                    actionPhotos: "='[{\"item\": \"' + props.prefix + '_CleaningMap\"}]'"
                    iconF7: rectangle_3_offgrid_fill
                    iconColor: purple
                    textColor: red
          - component: f7-col
                - component: oh-button
                    title: Alle Parameter anzeigen
                    action: group
                    actionGroupPopupItem: =props.prefix
                    iconF7: square_list
                    iconColor: white
                    textColor: gray

Just some status icons and the current status

When the battery is less than a 100% you also see the charge

Fan level and cleaning more are controlled through command options. Depending on your model you might need to change them (current ones are for a s5)

Right the other icons are to open the cleaning map as a pop up or all the vacuum items from the group (e.g. To check an error code or consumables)


Update v1.3.0

Found some time to work on the widget. New version 1.3.0 is on github available.

So far I managed to implement everything without using proxy items, rules or anything external. But depending on the activity item to change the UI while running is really slow so I might need to do that in the upcoming version.



  • Using displayState instead of state for actions and power
  • When in dock, it indicates that it’s charging
  • Hiding room selection on activity (takes a while, depends on the activity item)
  • Trend Item for charging level
  • Minor style improvements
  • “Return to dock” button when running

This is pretty cool! I could use this for my s6 maxv… Hey, Do you want me to translate this?
Just need you to make it clear what needs translating on the front end for the users and I’ll happily do it.

Glad you like it! :slight_smile:

Have you done translations of widgets before? I’m currently thinking about a proper way to do it.
Could implement all labels in a map, but inside the widget is quite difficult to achieve that.

Otherwise I could just add all labels as properties and allow each user a translation.

What would you suggest?

1 Like

Definitely go with labels as properties and just allow users to map what they want with that. This will allow everyone to map any value or translation.
More effective that way :slight_smile:

1 Like

Hello, sorry for my english… ich schreib es doch lieber auf deutsch :slight_smile: kann mir jemand bitte behilflich sein mit dem Widget. Hab einen S5 Max. Widget ist auf der Page aber bekomme keine Daten übermittelt an das Widget. Thing Items vom Sauger wurden alle angelegt und funktionieren bzw. geben Daten aus. Ich habe als ItemPrefix den Namen von Einstellungen\Items RoborockS5Max_ genommen

danke für eure Hilfe

Sorry, but English only please. This is an international community.

I prefer to write it in German: slight_smile: can someone please help me with the widget. Have an S5 Max. Widget is on the page but I do not get any data transmitted to the widget. Thing items from the vacuum cleaner were all created and work or output data. I took the name of Settings \ Items RoborockS5Max_ as the ItemPrefix

thank you for your help

It does not matter what you prefer, we have community rules regarding use of language.

Problem solved. just enter the itemname in the prefix without _

How can you change the FrameWork7 icons to Material icons?
I tried changing: iconF7: =loop.roomChunk.icon, but that didn’t work. Can you give an example?

Didn’t try it myself, but from the documentation, you should be able to use material icons by changing

iconF7: =loop.roomChunk.icon


iconMaterial: =loop.roomChunk.icon

and - for sure - using a material icon definition in the room-json.

Hope that helps.

Yep, thanks. I think I had a typo when I first tried.