Skip to content

Instantly share code, notes, and snippets.

@gjuhasz86
Last active May 28, 2025 19:05
Show Gist options
  • Save gjuhasz86/b05333830cc616b70869a324350691ad to your computer and use it in GitHub Desktop.
Save gjuhasz86/b05333830cc616b70869a324350691ad to your computer and use it in GitHub Desktop.
Download Xiaomi Vacuum Map

How to download the Map generated by the vacuum from the Xiaomi cloud

Dependencies

  • Install python-miio (docs)
  • Get the Xiaomi-Cloud-Map-Extractor add-on for Home Assistant (follow instructions below)

Get the necessary files

$ git clone https://github.com/PiotrMachowski/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor.git map-extractor
$ cd map-extractor/custom_components
$ git init .
$ git remote add origin https://gist.github.com/b05333830cc616b70869a324350691ad.git
$ git fetch
$ git checkout origin/master -b dev

At this point, open map-dl.py and edit the configuration section with the mi home credentials and the vacuum details

Run it

$ python map-dl.py

The map will be saved as map_data.gz.

View it

Use roboMapViewer to open the map.

import io
import time
import miio
from xiaomi_cloud_map_extractor.xiaomi_cloud_connector import XiaomiCloudConnector
# configuration
miHomeEmail = 'my-mihome-email@example.com' # replace with the email you use for the Mi Home app
passwd = 'mysecretpassword' # replace this with the password you use for the Mi Home app
vacuumIp = '10.1.2.3' # replace this with the vacuum's ip
token = '1234567890abcdef' # replace this with the vacuum's token
connector = XiaomiCloudConnector(miHomeEmail, passwd)
print(connector)
logged = connector.login()
print(logged)
vacuum = miio.Vacuum(vacuumIp, token)
map_name = "retry"
while map_name=="retry":
map_name=vacuum.map()[0]
print(map_name)
time.sleep(2)
raw_map = connector.get_raw_map_data('de', map_name)
raw_file = open("map_data.gz", "wb")
raw_file.write(raw_map)
raw_file.close()
@janniks95
Copy link

Hello while trying to implement, I encountered some errors that I was able to work around as follows:

  1. Update the relevant path to "XiaomiCloundConnector". Needed to add the "common" folder between as following:
    from xiaomi_cloud_map_extractor.common.xiaomi_cloud_connector import XiaomiCloudConnector
  2. pip install requests
  3. pip install pycryptodome for Crypto.Cipher reference

Now the name of the map is already pulled from the server. However, when processing or saving it, the error TypeError: XiaomiCloudConnector.get_raw_map_data() takes 2 positional arguments but 3 were given occurs. Do you know what the problem could be?

@bartwell
Copy link

Original script doesn't work for me (or may be for my Roborok S8 Ultra Pro). I wrote another one:

#!/usr/bin/env python3
import os
import sys

# Add current directory to sys.path so that the local xiaomi_cloud_map_extractor package can be found
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if SCRIPT_DIR not in sys.path:
    sys.path.insert(0, SCRIPT_DIR)

import time
import json
from miio import RoborockVacuum
from xiaomi_cloud_map_extractor.common.xiaomi_cloud_connector import XiaomiCloudConnector

# === SETTINGS: replace with your own ===
MI_HOME_EMAIL = 'your_email@example.com'           # your Mi Home email
PASSWORD      = 'your_password'                    # your Mi Home password
VACUUM_IP     = '192.168.1.2'                      # your vacuum's IP address on the local network
TOKEN         = 'your_token_here'                  # your vacuum's token
# ======================================

def main():
    # 1) Log in to Xiaomi Cloud
    connector = XiaomiCloudConnector(MI_HOME_EMAIL, PASSWORD)
    print('Initializing cloud connector…')
    logged = connector.login()
    if logged is None:
        print('Additional authentication required. Please open this URL in your browser:')
        print(connector.two_factor_auth_url)
        sys.exit(1)
    if not logged:
        print('Failed to log in to Xiaomi Cloud')
        sys.exit(1)
    print('Successfully logged in to the cloud.')

    # 2) Get the current map name via the local API
    vac = RoborockVacuum(VACUUM_IP, TOKEN)
    print('Requesting map name from the vacuum…')
    while True:
        try:
            result = vac.map()
            # vac.map() usually returns [<map_name>]
            map_name = result[0] if isinstance(result, (list, tuple)) and result else result
        except Exception as e:
            print('Error while calling map():', e)
            map_name = None

        print('Map name:', map_name)
        if map_name and str(map_name).lower() != 'retry':
            break
        time.sleep(2)

    # 3) Retrieve country, user_id, device_id and model from the cloud
    country, user_id, device_id, model = connector.get_device_details(TOKEN, None)
    if not country:
        print('Failed to retrieve device details from the cloud')
        sys.exit(1)
    print(f'Device details: country={country}, user_id={user_id}, device_id={device_id}, model={model}')

    # 4) Request the actual download URL for the map from the cloud API
    api_url = connector.get_api_url(country) + '/home/getmapfileurl'
    params = {'data': json.dumps({'obj_name': map_name})}
    api_resp = connector.execute_api_call_encrypted(api_url, params)
    map_url = api_resp.get('result', {}).get('url')
    if not map_url:
        print('Failed to get map URL. API response:')
        print(api_resp)
        sys.exit(1)
    print('Download URL:', map_url)

    # 5) Download the raw gz map data
    raw = connector.get_raw_map_data(map_url)
    if raw is None:
        print('Error downloading raw map data')
        sys.exit(1)

    # 6) Save to file
    out_path = os.path.join(SCRIPT_DIR, f'map_{map_name}.gz')
    with open(out_path, 'wb') as f:
        f.write(raw)
    print('Map has been saved to', out_path)


if __name__ == '__main__':
    main()

Save it in map-dl.py and use as described by gjuhasz86.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment