Connect to the Unitree Go2 and G1 with WebRTC
Project description
Unitree WebRTC Connect
Python WebRTC driver for Unitree Go2 and G1 robots. Provides high-level control through the same WebRTC protocol used by the Unitree Go/Unitree Explore mobile apps — no jailbreak or firmware modification required.
Supported Models
| Model | Variants |
|---|---|
| Go2 | AIR, PRO, EDU |
| G1 | AIR, EDU |
Supported Firmware
| Robot | Firmware Versions | Auth |
|---|---|---|
| Go2 | 1.0.19 – 1.0.25 1.1.1 – 1.1.14 1.1.15+ (latest) |
static GCM key (data2=2)static GCM key ( data2=2)per-device AES-128 key ( data2=3) — see AES-128 Key |
| G1 | 1.2.0 – 1.4.5 1.5.1+ (latest) |
static GCM key (data2=2)per-device AES-128 key ( data2=3) — see AES-128 Key |
Features
| Feature | Go2 | G1 |
|---|---|---|
| Data channel (pub/sub, RPC) | yes | yes |
| Sport / arm-action control | yes | yes |
| Video stream (receive) | yes | yes |
| Audio stream (send/receive) | yes | — |
| LiDAR point cloud decoding | yes | — |
| VUI (LED, brightness, volume) | yes | — |
| AudioHub (audio file management) | yes | — |
| Obstacle avoidance API | yes | — |
| Multicast device discovery | yes | — |
data2=3 per-device key auth |
yes (Go2 ≥ 1.1.15) | yes (G1 ≥ 1.5.1) |
Installation
PyPI (recommended)
sudo apt update
sudo apt install -y python3-pip portaudio19-dev
pip install unitree_webrtc_connect
From source
sudo apt update
sudo apt install -y python3-pip portaudio19-dev
git clone https://github.com/legion1581/unitree_webrtc_connect.git
cd unitree_webrtc_connect
pip install -e .
Quick Start
from unitree_webrtc_connect import UnitreeWebRTCConnection, WebRTCConnectionMethod
# Legacy firmware (Go2 < 1.1.15, G1 < 1.5.1) — no extra auth needed
conn = UnitreeWebRTCConnection(WebRTCConnectionMethod.LocalSTA, ip="192.168.123.18")
await conn.connect()
# V3-capable firmware (G1 ≥ 1.5.1, Go2 ≥ 1.1.15) — per-device AES-128 key
# required for the LAN handshake (see "AES-128 Key" below for how to fetch it)
conn = UnitreeWebRTCConnection(
WebRTCConnectionMethod.LocalSTA,
ip="192.168.10.225",
aes_128_key="<32-hex-chars>",
)
await conn.connect()
Connection Methods
AP Mode
Robot is in Access Point mode, client connects directly to the robot's WiFi.
UnitreeWebRTCConnection(WebRTCConnectionMethod.LocalAP)
# V3-capable firmware (G1 ≥ 1.5.1, Go2 ≥ 1.1.15) in AP mode
UnitreeWebRTCConnection(
WebRTCConnectionMethod.LocalAP,
aes_128_key="<32-hex-chars>",
)
STA-L Mode (Local Network)
Robot and client are on the same local network. Requires IP or serial number.
# By IP
UnitreeWebRTCConnection(WebRTCConnectionMethod.LocalSTA, ip="192.168.8.181")
# By serial number (uses multicast discovery, Go2 only)
UnitreeWebRTCConnection(WebRTCConnectionMethod.LocalSTA, serialNumber="B42D2000XXXXXXXX")
# V3-capable firmware (G1 ≥ 1.5.1, Go2 ≥ 1.1.15) by IP, with per-device key
UnitreeWebRTCConnection(
WebRTCConnectionMethod.LocalSTA,
ip="192.168.10.225",
aes_128_key="<32-hex-chars>",
)
STA-T Mode (Remote)
Remote connection through Unitree's TURN server. Control your robot from a different network. Requires Unitree account credentials.
region and device_type pick the cloud endpoint and the AppName header — Go2 hits global-robot-api.unitree.com with AppName: Go2, G1 does the same host with AppName: G1. Use region="cn" for accounts registered in China.
# Go2 (default)
UnitreeWebRTCConnection(
WebRTCConnectionMethod.Remote,
serialNumber="B42D2000XXXXXXXX",
username="email@gmail.com",
password="pass",
)
# G1 in China region
UnitreeWebRTCConnection(
WebRTCConnectionMethod.Remote,
serialNumber="E21D6000XXXXXXXX",
username="email@gmail.com",
password="pass",
region="cn",
device_type="G1",
)
AES-128 Key (data2=3, G1 ≥ 1.5.1 / Go2 ≥ 1.1.15)
Starting with G1 firmware 1.5.1 (back-ported to Go2 firmware 1.1.15), the LAN signaling handshake (con_notify) returns data2=3, which means the embedded RSA public key is wrapped under a per-device AES-128-GCM key. Without that key the WebRTC handshake can't decrypt the public key and the connection never starts. (Older firmware — G1 < 1.5.1 / Go2 < 1.1.15 — uses a static AES-GCM key, handled automatically.)
The key is per device, stable across re-pairings, stored on the robot at /unitree/etc/key/aes_key.bin (RSA-wrapped), and surfaced to the cloud as dev.key in device/bind/list.
Fetch via the bundled CLI
After pip install unitree_webrtc_connect you get a console script:
# By default: region=global, device family=G1.
# Use --device-type Go2 for Unitree Go2 (≥ 1.1.15).
unitree-fetch-aes-key --email you@example.com --password '...'
# Go2 in global region:
unitree-fetch-aes-key --email you@example.com --password '...' --device-type Go2
# China region:
unitree-fetch-aes-key --email you@example.com --password '...' --region cn
# Single SN, scriptable (bare key on stdout):
unitree-fetch-aes-key --email you@example.com --sn E21D6000XXXXXXXX --quiet
# Pre-existing access token (skip login):
unitree-fetch-aes-key --token <accessToken> --sn E21D6000XXXXXXXX
Equivalent if you don't have it on $PATH:
python -m unitree_webrtc_connect._cli --help
The interactive output is a labelled panel (SN / alias / online / region / key); --quiet swaps that for a bare key on stdout so you can pipe it:
KEY=$(unitree-fetch-aes-key --email ... --sn E21D... --quiet)
Fetch programmatically
from unitree_webrtc_connect import UnitreeCloud, fetch_aes_key
# One-shot lookup
key = fetch_aes_key("you@example.com", "...", sn="E21D6000XXXXXXXX",
region="global", device_type="G1")
# Or keep the cloud client around for other calls
cloud = UnitreeCloud(region="global", device_type="G1")
cloud.login_email("you@example.com", "...")
for d in cloud.list_devices():
print(d.sn, d.alias, d.key)
Typed errors
When you supply the wrong / missing key, the SDK raises typed exceptions you can catch instead of relying on stack traces:
from unitree_webrtc_connect import (
AesKeyRequiredError, # data2=3 robot reached without a key
AesKeyRejectedError, # GCM tag check failed (wrong key)
LocalSignalingPortError, # neither :9991 nor :8081 reachable on the IP
RobotBusyError, # robot rejected — another WebRTC client is connected
NoSdpAnswerError, # signaling round-trip returned no SDP
DataChannelTimeoutError, # data channel didn't validate in time
)
try:
await conn.connect()
except AesKeyRejectedError as e:
...
Examples
Examples are organized by robot model under the /examples directory.
All examples default to ip="192.168.8.181" but read the UNITREE_ROBOT_IP
environment variable if set, so you can point them at your robot without
editing the source:
export UNITREE_ROBOT_IP=192.168.8.181
python examples/go2/data_channel/sportmode/sportmode.py
On V3-capable firmware (G1 ≥ 1.5.1, Go2 ≥ 1.1.15), also set
UNITREE_AES_128_KEY (fetch via unitree-fetch-aes-key).
Go2
| Category | Example | Description |
|---|---|---|
| Data Channel | data_channel/sportmode/ |
Sport mode movement commands |
data_channel/sportmode_mcf/ |
Interactive MCF sport menu (firmware ≥ 1.1.7) | |
data_channel/sportmodestate/ |
Subscribe to sport mode state | |
data_channel/lowstate/ |
Subscribe to low-level state (IMU, motors) | |
data_channel/multiplestate/ |
Subscribe to multiple state topics | |
data_channel/vui/ |
VUI control (LED, volume, brightness) | |
data_channel/obstacles_avoid/ |
Toggle obstacle avoidance + WASD drive | |
data_channel/lidar/lidar_stream.py |
LiDAR point cloud subscription | |
data_channel/lidar/plot_lidar_stream.py |
LiDAR 3D visualization (Three.js) | |
| Audio | audio/live_audio/ |
Live audio receive |
audio/save_audio/ |
Save audio to file | |
audio/mp3_player/ |
Play MP3 through robot speaker | |
audio/internet_radio/ |
Stream internet radio | |
| Video | video/camera_stream/ |
Display video stream |
G1
| Category | Example | Description |
|---|---|---|
| Data Channel | data_channel/sport_mode/ |
Sport mode movement commands |
| Video | video/camera_stream/ |
Display video stream |
Imports
All public classes, helpers and exception types are exported from the package root:
from unitree_webrtc_connect import (
# Core driver
UnitreeWebRTCConnection,
WebRTCConnectionMethod,
WebRTCDataChannel,
WebRTCDataChannelPubSub,
DATA_CHANNEL_TYPE,
RTC_TOPIC,
SPORT_CMD,
# Cloud helpers (data2=3, account / TURN flow, bind list)
UnitreeCloud,
UnitreeCloudError,
RobotDevice,
fetch_aes_key,
# Typed errors
AesKeyRequiredError,
AesKeyRejectedError,
DataChannelTimeoutError,
LocalSignalingPortError,
NoSdpAnswerError,
RobotBusyError,
)
Acknowledgements
A big thank you to TheRoboVerse community! Visit us at TheRoboVerse for more information and support.
Special thanks to the tfoldi WebRTC project and abizovnuralem for adding LiDAR support, MrRobotoW for the LiDAR visualization example, and Nico for the aiortc monkey patch.
Support
If you like this project, please consider buying me a coffee:
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unitree_webrtc_connect-2.1.2.tar.gz.
File metadata
- Download URL: unitree_webrtc_connect-2.1.2.tar.gz
- Upload date:
- Size: 47.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a85cfa97b0e039065e149a013b2459719c7189a5482fae837ca64b00a700c4ba
|
|
| MD5 |
a091e91d60786fce74fd69bda9e4c16d
|
|
| BLAKE2b-256 |
ecfd27837faefd2e7bf6dbcf2e563863aef62dfa889b012b46a88fc6b1a29972
|
File details
Details for the file unitree_webrtc_connect-2.1.2-py3-none-any.whl.
File metadata
- Download URL: unitree_webrtc_connect-2.1.2-py3-none-any.whl
- Upload date:
- Size: 52.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
230bcbc5cc39f0dd621cdebf0568e96274c3531e232611293f8182af9e827d80
|
|
| MD5 |
a2b872acac07c9d5217bb4700a656022
|
|
| BLAKE2b-256 |
09ef697ccbf91833155fa36ee0919918eb02df49a47005d1ff8a78ca13e09893
|