Xiaomi Robot Vacuum Binding

The offline map viewer is actually included in openhab.
It reads the rrmap files that are saved when pulling the map.

Hope that someone is clever to find the right command line.
Maybe someone with some more java knowledge can help figuring it out. The class to run is RoboMapViewer . In the eclipse IDE it is as simple as ’ run as java program’

Trial and error :face_with_raised_eyebrow:
try 3, 5, 10, 15, …

Here is an updated version with some fixes:

// ----------------------------------------------------------------------------
rule "zone control - zone selection"
// ----------------------------------------------------------------------------
when 
    Member of gZoneSwitch received command
then
    logDebug("zone.rules", "update received from item: " + triggeringItem.name)

    // if (triggeringItem.name == "gZoneSwitch")
    // {
    //     logInfo("zone.rules", "ignoring gZoneSwitch group item")
    //     return;
    // }

    val zoneSwitch = triggeringItem as SwitchItem
    val lastUpdate = now

    zoneSwitch.getGroupNames().forEach[groupName | 
        
        if (groupName != "gZoneSwitch" && groupName != "gAllZones")
        {
            logDebug("zone.rules", "find related members of " + groupName + " in other group gZoneLastUpdate from triggering item " + triggeringItem.name)

            gZoneLastUpdate.members.filter[updateItem | 
                    updateItem.getGroupNames.contains(groupName)
                &&  updateItem.getGroupNames.contains("gZoneLastUpdate")].forEach[ updateItem | 
                logDebug("zone.rules", "posting update to " + updateItem.name)
                updateItem.postUpdate(lastUpdate.toString())
            ]
        }
        else
        {
            logDebug("zone.rules", "ignoring group " + groupName) 
        }
    ]
end

// ----------------------------------------------------------------------------
rule "zone control - action"
// ----------------------------------------------------------------------------
when 
    Item zoneAction received command
then
    logInfo("zone.rules", "zoneAction command received: " + receivedCommand)

    var StringBuilder zoneCmd = new StringBuilder
    var StringBuilder roomCmd = new StringBuilder

    // use this to get the room numbers
    // actionCommand.sendCommand("get_room_mapping[]")

    if (gAllZones.state == ON)
    {
        logInfo("zone.rules", "sending cmd to device: app_start")
        actionCommand.sendCommand("app_start")
        return;
     }

    // sort by timestamp and loop throug items
    gZoneLastUpdate.members.sortBy[(state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli].forEach[zoneLastUpdateItem | 
        logDebug("zone.rules", "--> "+ zoneLastUpdateItem)

        // loop until we find the right group...
        zoneLastUpdateItem.getGroupNames().forEach[groupName | 
            logDebug("zone.rules", "----> "+ groupName)
            if (groupName != "gZoneLastUpdate")
            {
                logDebug("zone.rules", "find related members of " + groupName + " in other group gZoneSwitch")

                // find the switch item from the same room group
                gZoneSwitch.members.filter[zoneSwitchItem | 
                            zoneSwitchItem.getGroupNames.contains(groupName)
                        &&  zoneSwitchItem.getGroupNames.contains("gZoneSwitch")].forEach[ zoneSwitchItem | 
                    
                    logDebug("zone.rules", "found related switch item " + zoneSwitchItem.name + " with state " + zoneSwitchItem.state)
                    
                    if (zoneSwitchItem.state == ON)
                    {
                        var zoneType = transform("MAP", "robo_room_coordinates.map", zoneSwitchItem.name + "#type")
                        var zoneCoordinates = transform("MAP", "robo_room_coordinates.map", zoneSwitchItem.name + "#coordinates")

                        logInfo("zone.rules", zoneCoordinates + "-" + zoneSwitchItem.name + " is ON. {zoneType:" + zoneType + "; zoneCoordinates: " + zoneCoordinates + "}")

                        // not sure if we can assign a new var by reference, so to be safe, just use if..else and duplicate code
                        if (zoneType == "zone")
                        {
                            if (zoneCmd.length == 0)
                            {
                                zoneCmd.append("app_zoned_clean[")
                            }

                            zoneCmd.append(zoneCoordinates)
                        }
                        else if (zoneType == "room")
                        {
                            if (roomCmd.length == 0)
                            {
                                roomCmd.append("app_segment_clean[" + zoneCoordinates)
                            }
                            else
                            {
                                roomCmd.append(", " + zoneCoordinates)
                            }
                        }

                        logDebug("zone.rules", roomCmd.toString)
                    }
                ]
            }
        ]
    ]

    // if we have rooms and zones, zone wins - duplicate code here as well
    if (roomCmd.length != 0)
    {
        val cmd = roomCmd.toString + "]"
        logInfo("zone.rules", "sending cmd to device: " + cmd )
        actionCommand.sendCommand(cmd)
    }
    else if (zoneCmd.length != 0)
    {
        val cmd = zoneCmd.toString + "]"
        logInfo("zone.rules", "sending cmd to device: " + cmd )
        actionCommand.sendCommand(cmd)
    }
    else
    {
        logInfo("zone.rules", "no zone or room selected. nothing to do.")
    }

    zoneAction.postUpdate("start")

end

// ----------------------------------------------------------------------------
rule "zone control - init"
// ----------------------------------------------------------------------------
when
    System started
then
    val lastUpdate = now

    gZoneLastUpdate.members.forEach(item | 
        item.postUpdate(lastUpdate.toString())
    )
end

This one is also nice if you have a KNX environment. You can start cleaning by double clicking a wall switch

import org.joda.time.*

var DateTime lastClick = new DateTime().minusMinutes(1)

// ----------------------------------------------------------------------------
rule "KNX - Roborock"
// ----------------------------------------------------------------------------
when 
    Item KnxLichtGarderobe changed from OFF to ON
then
    if (lastClick.plusMillis(2000).afterNow)
    {
        var zoneCoordinates = transform("MAP", "robo_room_coordinates.map", "zEsstisch" + "#coordinates")
        var cmd = "app_zoned_clean[" + zoneCoordinates + "]"
        actionCommand.sendCommand(cmd)
    }

    lastClick = now
end

Is it possible to start cleaning a room, like in the app, without getting the coordinates?

It is a regular java program, expect it to run in all environments (win, linux, Max)
I made it now so it can be easily run. Find it in this new topic:

Hi
Thanks for the java program.
But I have problem that the miio folder is empty except the token.json file.
The basicUI/APP show the map just fine and it get updated when the roborock is moving.
So I’m unsure where the maps is stored.
I have also tried to search for *.rrmap files but nothing is found.
I’m running openHab 2.5.6 on Rasbian.

sorry, checking the code I see the map file is only saved when you have debugging switched on.
Hence I think you need to run in the terminal
log:set debug org.openhab.binding.miio and then have the robot send another map

2 Likes

Great! I will try that :slight_smile:

Hi again.
I managed to get the rrmap files.
But when I open the file (OSX) it doesn’t show the map but I can click on the java program and get coordinates.

When I try to open the java program on Windows 10 I get this error:

for this offline viewer, suggest to move the discussion to the dedicated viewer topic.
I responded to the similar question there.

Hi Marcel,
Any chance you can add support for the s6 pure?
roborock.vacuum.a08

Please give a try by changing the modelId in your thingConfig to the rockrobo.vacuum.v1 and see if you can control it that way. Based on the similarities, I believe it has high chance for success.
Pls feedback your results/findings

Changed the device model string as stated but no results. Just the basic generic channels are avaiable. Power on/off and execute test commands.

Log:
Mi Device model roborock.vacuum.a08 has model config: rockrobo.vacuum.v1. Unexpected unless manual override

pls add it manually then as miio:vacuum thing. the override message is expected and okay.

That seems to work. Thanks for the quick response.

Hello,

I have this Robot Vaccum: https://www.amazon.de/dp/B085BPCV7W/.
It shows as unsupported, what is needed to have it added and working in the binding ?

Since the binding does not support my viomi, i am using the exec script and miiocli:

I can also get state info, like dock State and so on :slight_smile:

State: ViomiVacuumState.Docked
Mode: ViomiMode.Vacuum
Error: Unknown error 2105
Battery: 100
Fan speed: ViomiVacuumSpeed.Turbo
Box type: ViomiBinType.Vacuum
Mop type: 0
Clean time: 0:01:11
Clean area: 51.6
Water grade: ViomiWaterGrade.High
Remember map: True
Has map: True
Has new map: True
Mop mode: ViomiMode.Vacuum

Can you please indicate the modelId you see in Openhab. Cannot judge from the cover…

Please give a try by manually add it as miio:vacuum device and changing the modelId in your thing config to the rockrobo.vacuum.v1 and see if you can control it that way.
Pls feedback your results/findings.

I think with the proper json db file this vacuum can largely be controlled by openhab if you configure it as basic device. If you want to try, let me know and we can try to develop a db file for it,

1 Like