Skip to main content

Unofficial Python client for the Leapmotor vehicle cloud API

Project description

leapmotor-api

PyPI version Python versions License Downloads CI codecov Ruff mypy Ko-fi

Unofficial Python client for the Leapmotor vehicle cloud API.

Extracted from the leapmotor-ha Home Assistant integration to provide a reusable, framework-agnostic library.

Acknowledgments

Special thanks to Jakob Kern for the impressive reverse engineering work on the Leapmotor application and for generously sharing his work.

Installation

pip install leapmotor-api

Certificates

The library requires the Leapmotor app certificate and private key to authenticate API requests. You can download them from the dedicated repository:

wget https://github.com/markoceri/leapmotor-certs/archive/refs/tags/v1.0.0.zip
unzip v1.0.0.zip

The extracted folder contains app_cert.pem and app_key.pem. Pass their paths to the client via the app_cert_path and app_key_path parameters.

Quick Start

from leapmotor_api import LeapmotorApiClient

client = LeapmotorApiClient(
    username="user@example.com",
    password="password",
    app_cert_path="/path/to/app_cert.pem",
    app_key_path="/path/to/app_key.pem",
    language="it-IT",  # default: en-GB
)

client.login()
vehicles = client.get_vehicle_list()

for vehicle in vehicles:
    print(f"{vehicle.vin} ({vehicle.car_type}) — {vehicle.vehicle_nickname}")
    status = client.get_vehicle_status(vehicle)
    print(f"  Battery: {status.battery.soc}%")
    print(f"  Range: {status.battery.expected_mileage} km")
    print(f"  Odometer: {status.driving.total_mileage} km")

client.close()

Typed Models vs Raw Data

The library exposes two ways to access vehicle data:

Typed models (recommended)

get_vehicle_status() returns a VehicleStatus dataclass with typed sub-objects:

status = client.get_vehicle_status(vehicle)

# Battery & charging
status.battery.soc                  # int | None — state of charge %
status.battery.expected_mileage     # int | None — remaining range km
status.battery.charge_state         # ChargeState | None — NOT_CONNECTED, AC_CONNECTED, DC_CONNECTED
status.battery.is_charging          # bool | None
status.battery.is_discharging       # bool | None
status.battery.charging_power_kw    # float | None
status.battery.discharging_power_kw # float | None
status.battery.battery_power        # float | None — power in kW (voltage × current)
status.battery.dump_energy_kwh      # float | None — available energy in kWh
status.battery.battery_voltage      # float | None
status.battery.battery_current      # float | None
status.battery.charge_remain_time   # int | None — minutes remaining
status.battery.charge_soc_setting   # int | None — charge limit %

# Driving
status.driving.total_mileage        # int | None — odometer km
status.driving.speed                # int | None
status.driving.gear_status          # int | None
status.driving.is_parked            # bool | None

# Location
status.location.latitude            # float | None
status.location.longitude           # float | None

# Climate
status.climate.ac_switch            # bool | None
status.climate.ac_setting           # float | None — target temperature
status.climate.ac_air_volume        # int | None
status.climate.outdoor_temp         # int | None
status.climate.ptc_state            # int | None

# Doors & locks
status.doors.is_locked              # bool | None
status.doors.bbcm_back_door_status  # bool | None — trunk

# Windows
status.windows.left_front_window_percent   # int | None
status.windows.right_front_window_percent  # int | None
status.windows.left_rear_window_percent    # int | None
status.windows.right_rear_window_percent   # int | None
status.windows.sun_shade                   # int | None

# Tire pressure
status.tires.front_left_bar         # float | None — pressure in bar
status.tires.front_right_bar        # float | None
status.tires.rear_left_bar          # float | None
status.tires.rear_right_bar         # float | None
status.tires.all_ok                 # bool | None — all pressures normal
status.tires.all_bar                # dict[str, float | None]

# Connectivity
status.connectivity.bluetooth_state # bool | None
status.connectivity.hotspot_state   # bool | None

# Ignition
status.ignition.bcm_key_position_on1  # bool | None
status.ignition.bcm_key_position_on3  # bool | None

# Top-level convenience properties
status.is_locked                    # bool | None
status.is_charging                  # bool | None — plugged in, parked, and charging
status.is_regening                  # bool | None — regenerative braking
status.is_parked                    # bool | None
status.tire_pressure_bar            # dict[str, float | None]

# Timestamps
status.collect_time                 # datetime | None
status.create_time                  # datetime | None

Raw API data

For forward-compatibility or debugging, use get_vehicle_raw_status():

raw = client.get_vehicle_raw_status(vehicle)
# Returns the full API JSON dict with signal codes, config, etc.
# raw["data"]["signal"]["1204"]  → battery SOC
# raw["data"]["config"]["3"]     → charging plan

The VehicleStatus object also retains the raw dict in status.raw for convenience.

Async Usage

from leapmotor_api import LeapmotorApiClient
from leapmotor_api.async_client import AsyncLeapmotorApiClient

sync_client = LeapmotorApiClient(
    username="user@example.com",
    password="password",
    app_cert_path="/path/to/app_cert.pem",
    app_key_path="/path/to/app_key.pem",
    language="it-IT",
)
client = AsyncLeapmotorApiClient(sync_client)

await client.login()
vehicles = await client.get_vehicle_list()
status = await client.get_vehicle_status(vehicles[0])
await client.close()

Remote Control

Remote actions require the vehicle PIN:

client = LeapmotorApiClient(
    username="user@example.com",
    password="password",
    app_cert_path="/path/to/app_cert.pem",
    app_key_path="/path/to/app_key.pem",
    operation_password="1234",
    language="de-DE",
)

client.login()
client.lock_vehicle("WLM...")
client.unlock_vehicle("WLM...")
client.open_trunk("WLM...")
client.close_trunk("WLM...")
client.find_vehicle("WLM...")
client.open_windows("WLM...")
client.close_windows("WLM...")
client.ac_switch("WLM...")
client.quick_cool("WLM...")
client.quick_heat("WLM...")
client.windshield_defrost("WLM...")
client.open_sunshade("WLM...")
client.close_sunshade("WLM...")
client.battery_preheat("WLM...")
client.set_charge_limit("WLM...", charge_limit_percent=80)
client.close()

Token Management

The Leapmotor API tokens expire after ~20 minutes. The client handles this automatically: all public methods detect expired-token errors and transparently refresh the token (or fall back to a full re-login if the refresh token has also expired).

You can also manage token refresh manually:

# Explicit refresh (rotates token + refresh token, reuses sign material)
client.token_refresh()

# Async
await client.token_refresh()

No configuration is needed — the refreshToken is obtained during login() and rotated on each refresh call.

Configuration

Parameter Type Default Description
username str Leapmotor account email
password str Leapmotor account password
app_cert_path str Path to app certificate PEM
app_key_path str Path to app private key PEM
operation_password str | None None Vehicle PIN (required for remote control)
language str "en-GB" API language (en-GB, it-IT, de-DE, fr-FR, …)
verify_ssl bool False Verify server TLS certificate
base_url str DEFAULT_BASE_URL API base URL
timeout int 30 HTTP timeout in seconds
device_id str | None None Custom device ID (auto-generated if omitted)

License

This project is licensed under the GNU Affero General Public License v3.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

leapmotor_api-0.1.5.tar.gz (53.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

leapmotor_api-0.1.5-py3-none-any.whl (42.1 kB view details)

Uploaded Python 3

File details

Details for the file leapmotor_api-0.1.5.tar.gz.

File metadata

  • Download URL: leapmotor_api-0.1.5.tar.gz
  • Upload date:
  • Size: 53.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for leapmotor_api-0.1.5.tar.gz
Algorithm Hash digest
SHA256 49650e6da93db58cf3f13a6257ffa42add5875f85264c7f05a94e52fa8ea6e47
MD5 0f8d497bf247820c4cf98e6d5626abfa
BLAKE2b-256 864ef0638e6ed3bc517d74d85374ea763ef244e13adf685b2520bdb3df42bfaf

See more details on using hashes here.

Provenance

The following attestation bundles were made for leapmotor_api-0.1.5.tar.gz:

Publisher: publish.yml on markoceri/leapmotor-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file leapmotor_api-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: leapmotor_api-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 42.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for leapmotor_api-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 c7fb2aa8cccfb248d84f994b749e3a3d2c995304bf3c8d0d7584aa17c28ec5c8
MD5 27f1648abb2469a22646f1527f544c53
BLAKE2b-256 e02fa44f9e7f2540a62638ed612a69b4207a0198673db9f02e1dbd70030f965a

See more details on using hashes here.

Provenance

The following attestation bundles were made for leapmotor_api-0.1.5-py3-none-any.whl:

Publisher: publish.yml on markoceri/leapmotor-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page