3 minute read

Introduction

The two most popular companion applications for the Mi Smart Band 5 are Mi Fit and Zepp. Both applications support the tracking of different kinds of workouts, however they do not allow the user to export the collected data for further analysis. Due to the General Data Protection Regulation (GDPR) users may download their personal data from both applications, but the resulting archive does not contain workout specific data. The Zepp application also allows the user to export workouts one-by-one in .gpx format, but it does not contain all the available data and it is infeasible to export thousands of workouts manually.

Screenshot of the Mi Fit application (from Google Play) Screenshot of the Zepp application (from Google Play)

API

Both applications synchronize the workouts to the cloud, which should make it easy to investigate the HTTP requests and responses. My favorite web debugging proxy tool is Telerik’s Fiddler which is able to decrypt HTTPS traffic originating from mobile devices. There is also a good tutorial available which explains how to set up your devices.

Application token

The endpoints of the API require a user context for which an authorization token is needed. There are at least two ways to extract the token, for which you will need to log in to the application first.

If you have root access on your Android device, you can find the token at /data/data/com.xiaomi.hm.health/shared_prefs/hm_id_sdk_android.xml (Mi Fit) or /data/data/com.huami.watch.hmwatchmanager/shared_prefs/hm_id_sdk_android.xml (Zepp). These files can be accessed via a file manager, or via ADB shell (Android 11+).

If you do not have root access, you can use Fiddler or HTTP Toolkit to analyze the requests sent by the application, which contain the exact same apptoken header, which is required by the endpoints discussed below.

Screenshot of the apptoken header in Fiddler

Workout history

By analyzing the traffic of the application, it turned out the following endpoint returns the metadata of all workouts:

https://api-mifit-de2.huami.com/v1/sport/run/history.json

It requires the following header:

Key Example value
apptoken DQVBQE…WHtrY

And the following GET parameter:

Key Example value
source “run.mifit.huami.com”

In Python:

def get_history():
    r = requests.get('https://api-mifit-de2.huami.com/v1/sport/run/history.json', headers={
        'apptoken': token
    }, params={
        'source': 'run.mifit.huami.com',
    })
    r.raise_for_status()

    return r.json()

The response is a list of metadata corresponding to the workouts that were recorded in the requested interval. The format of a workout metadata is as follows (the values were redacted):

{
  "code": 1,
  "message": "success",
  "data": {
    "summary": [
      {
        "trackid": "1234567890",
        "source": "run.mifit.huami.com",
        "dis": "0.0",
        "calorie": "0.0",
        "end_time": "0",
        "run_time": "0",
        "avg_pace": "0.0",
        "avg_frequency": "0.0",
        "avg_heart_rate": "0.0",
        "type": 0,
        "location": "",
        "city": "",
        "forefoot_ratio": "",
        "bind_device": "",
        "max_pace": 0,
        "min_pace": 0,
        "version": 0,
        "altitude_ascend": 0,
        "altitude_descend": 0,
        "total_step": 0,
        "avg_stride_length": 0,
        "max_frequency": 0,
        "max_altitude": 0,
        "min_altitude": 0,
        "lap_distance": 0,
        "sync_to": "",
        "distance_ascend": 0,
        "max_cadence": 0,
        "avg_cadence": 0,
        "landing_time": 0,
        "flight_ratio": 0,
        "climb_dis_descend": 0,
        "climb_dis_ascend_time": 0,
        "climb_dis_descend_time": 0,
        "child_list": "",
        "parent_trackid": 0,
        "max_heart_rate": 0,
        "min_heart_rate": 0,
        "swolf": 0,
        "total_strokes": 0,
        "total_trips": 0,
        "avg_stroke_speed": 0,
        "max_stroke_speed": 0,
        "avg_distance_per_stroke": 0,
        "swim_pool_length": 0,
        "te": 0,
        "swim_style": 0,
        "unit": 0,
        "add_info": "",
        "sport_mode": 0,
        "downhill_num": 0,
        "downhill_max_altitude_desend": 0
      }
    ]
  }
}

Workout detail

For each workout metadata the corresponding detail can be queried by using the following endpoint:

https://api-mifit-de2.huami.com/v1/sport/run/detail.json

It also requires the apptoken header and the above-mentioned GET parameter with an additional identifier:

Key Example value
trackid 123456789

In Python:

def get_detail(track_id, source):
    r = requests.get('https://api-mifit-de2.huami.com/v1/sport/run/detail.json', headers={
        'apptoken': token
    }, params={
        'trackid': track_id,
        'source': source,
    })
    r.raise_for_status()

    return r.json()

The response contains the detail of the requested workout, including the location information. The format of a detailed workout is as follows (the values were redacted):

{
  "code": 1,
  "message": "success",
  "data": {
    "trackid": 1234567890,
    "source": "run.mifit.huami.com",
    "longitude_latitude": "0,0;...",
    "altitude": "0;...",
    "accuracy": "0;...",
    "time": "0;...",
    "gait": "0,0,0,0;...",
    "pace": "0.0;...",
    "pause": "",
    "spo2": "",
    "flag": "0;...",
    "kilo_pace": "",
    "mile_pace": "",
    "heart_rate": "0,0;...",
    "version": 0,
    "provider": "",
    "speed": "0,0.0;...",
    "bearing": "",
    "distance": "0,0;...",
    "lap": "",
    "air_pressure_altitude": "",
    "course": "",
    "correct_altitude": "",
    "stroke_speed": "",
    "cadence": "",
    "daily_performance_info": "",
    "rope_skipping_frequency": "",
    "weather_info": "",
    "coaching_segment": ""
  }
}

Summary

An example Python implementation can be found on GitHub.