Unofficial Python library for controlling ClicBot modular robots over TCP/UDP
Project description
ClicBot for Python (Unofficial)
Unofficial library — not affiliated with, endorsed by, or associated with KEYi Technology or ClicBot. Protocol inferred from network traffic generated by my own device and the official app, solely for interoperability purposes.
Python library for controlling ClicBot modular robots — motor control, structure discovery, and event-driven programming over TCP/UDP.
Install
pip install clicbot-unofficial
from clicbot_unofficial import ClicBot, discover_first
Connecting to the robot
The robot communicates over TCP. Your computer and the robot must be on the same IP network.
There are two network setups:
WiFi (recommended)
The robot stores WiFi credentials and reconnects automatically on every boot. The QR code step is only needed once when joining a new network for the first time.
First time — provision the robot onto your WiFi:
from clicbot_unofficial import build_qr_content, show_qr_code, wait_for_robot
content = build_qr_content("MyWifi", "secret")
show_qr_code(content) # terminal (default), or mode="file"/"text"
device = wait_for_robot()
Hold the QR code in front of the robot's camera. It joins your network, announces its address, and remembers the credentials from then on.
Already provisioned — just discover it:
from clicbot_unofficial import discover_first
device = discover_first(timeout=5.0)
Robot hotspot
The robot can also expose its own WiFi access point. Connect your computer to it, then discover the robot via UDP or connect directly to its known IP.
Connecting
Once you have the device from either method above:
from clicbot_unofficial import ClicBot, BrainState
bot = ClicBot()
bot.connect(device.ip, device.port)
bot.send_client_info()
bot.set_brain_state(BrainState.CUSTOM)
modules = bot.get_structure() # blocks until the robot replies
for joint in bot.servo_joints:
joint.rotate_to(90, speed=60)
Authentication
This library connects in guest mode only, using the built-in tourist username and userId: 0. No account or login is required. This is sufficient for all supported robot control operations.
Discovery
| Function | Description |
|---|---|
discover_all(timeout=3.0) |
Return all robots found on the local network within the timeout |
discover_first(timeout=5.0) |
Return the first robot found, or raise TimeoutError |
discover_via_qrcode(ssid, password, qr_output=...) |
Show a QR code; wait for the robot to join WiFi and announce its address |
ClicBot API
Connection
bot.connect(host, port) # open TCP connection
bot.send_client_info() # handshake (call once after connect)
bot.set_brain_state(BrainState.CUSTOM) # enable motion commands
bot.disconnect()
Structure
modules = bot.get_structure() # request tree and block until received
bot.set_structure_watchdog(True) # continuous change notifications
bot.request_angles() # triggers on_angles callback
Motor control
# Servo joints — absolute angle (degrees)
bot.rotate_to(module_id, angle, speed=50)
bot.rotate_to_many([{"module_id": 1, "angle": 90, "speed": 50}])
# Continuous rotation (wheels or joints)
bot.rotate_start(module_id, forward=True, speed=60)
bot.rotate_start_many([{"module_id": 2, "forward": True, "speed": 60}])
bot.rotate_stop() # stop all rotating modules
# Push-rotate mode
bot.set_push_rotate(module_id, enabled=True)
# Lock / unlock
bot.lock(module_id)
bot.unlock(module_id)
bot.lock_all()
bot.lock_all(locked=False) # unlock all
# Emergency stop
bot.full_stop(lock=False)
Module accessors
After get_structure() the robot tree is available:
bot.root # BrainModule (id 0)
bot.servo_joints # List[ServoJointModule]
bot.servo_wheels # List[ServoWheelModule]
bot.get_module(module_id)
Each module exposes the same controls directly:
joint.rotate_to(90, speed=50)
joint.rotate_start(forward=True, speed=60)
joint.rotate_stop()
joint.set_push_rotate(enabled=True)
joint.lock()
joint.unlock()
wheel.rotate_start(forward=False, speed=80)
wheel.rotate_stop()
Callbacks
Assign a function to any of these attributes to receive events:
bot.on_battery = lambda level: print(f"Battery: {level:.0%}")
bot.on_structure_changed = lambda modules: print(f"{len(modules)} modules")
bot.on_angles = lambda angles: print(angles)
bot.on_close = lambda: print("disconnected")
bot.on_error = lambda exc: print("error:", exc)
| Attribute | Arguments | Description |
|---|---|---|
on_close |
— | Connection closed |
on_error |
exc |
Socket error |
on_battery |
level (0.0–1.0) |
Battery level update |
on_structure_changed |
modules (dict[id, Module]) |
Module tree updated |
on_angles |
angles (dict[id, float]) |
Joint angle update |
Examples
| File | Description |
|---|---|
examples/01_discovery.py |
Scan the network and list all robots |
examples/02_tree.py |
Print the module tree |
examples/03_servo.py |
Move servo joints to 0° then 90° |
examples/04_rotate.py |
Spin wheel modules for 2 seconds |
examples/05_qrcode.py |
Connect a new robot via QR code |
examples/06_mermaid.py |
Save module tree as a Mermaid diagram |
Structure visualization
to_mermaid(structure) converts the raw module tree into a Mermaid graph. Each module becomes a labelled subgraph with one node per port; edges show parent–child connections. See examples/06_mermaid.py — it saves structure.md and reminds you to paste the content into mermaid.live.
Example output for a simple arm assembly:
graph TD
subgraph BRAIN 0
direction LR
0_conn0(Port 0)
end
subgraph DISTANCE_BAR 1
direction LR
1_conn0(Port 0)
1_conn1(Port 1)
end
0_conn0 --> 1_conn0
subgraph SERVO_JOINT 2
direction LR
2_conn0(Port 0)
2_conn1(Port 1)
2_conn2(Port 2)
2_conn3(Port 3)
end
1_conn1 --> 2_conn2
subgraph SERVO_JOINT 3
direction LR
3_conn0(Port 0)
3_conn1(Port 1)
3_conn2(Port 2)
3_conn3(Port 3)
end
2_conn0 --> 3_conn1
subgraph SERVO_JOINT 4
direction LR
4_conn0(Port 0)
4_conn1(Port 1)
4_conn2(Port 2)
4_conn3(Port 3)
end
3_conn0 --> 4_conn0
subgraph SERVO_JOINT 5
direction LR
5_conn0(Port 0)
5_conn1(Port 1)
5_conn2(Port 2)
5_conn3(Port 3)
end
3_conn2 --> 5_conn1
subgraph SERVO_WHEEL 6
direction LR
6_conn0(Port 0)
end
4_conn2 --> 6_conn0
subgraph SERVO_WHEEL 7
direction LR
7_conn0(Port 0)
end
5_conn3 --> 7_conn0
Not supported
The following features are absent from this library and will not be added without significant further reverse-engineering:
- Official programs — the built-in robot configurations shipped by KEYi (commands 300, 400, 401, 1034–1037). These require an authenticated account and upload opaque binary assets.
- Pro actions / face animations — the "Pro" timeline system sends a ZIP to the brain (cmd 13) that can include servo splines, video, audio, and
SCREENkeyframe curves that animate the brain's face display. The wire format of the SCREEN block is not fully understood. - Brain face display — the brain's built-in LCD shows animated eye/face expressions. Expressions are controlled via Pro-action SCREEN data or pre-loaded assets uploaded by the official app. There is no standalone "show expression X" command.
- Steering / locomotion — the drive mode for wheeled robots (cmds 1012/1013). The wire format is not known.
- Guide / activate flow — the factory calibration guide channels (cmd 300). Account-gated and device-specific.
- Veriface / face asset sync — the brain sends a ZIP of face-expression images and audio to the app (cmd 14) for preview purposes. Not handled.
Building and publishing
Build a wheel and source distribution:
pip install build twine
python -m build
# → dist/clicbot_unofficial-0.1.0-py3-none-any.whl
# → dist/clicbot_unofficial-0.1.0.tar.gz
Upload to PyPI:
twine upload dist/*
Install the wheel directly without publishing:
pip install dist/clicbot_unofficial-0.1.0-py3-none-any.whl
Or install in editable mode from the project root for development:
pip install -e .
Requirements
- Python ≥ 3.10
qrcode— included automatically; needed forshow_qr_codeterminal modePillow— optional, only needed for PNG export (mode="file"); install viapip install "clicbot-unofficial[png]"
License
MIT © 2026 Hannes Rüger
Legal / Interoperability Notice
This is an independent, unofficial Python library for ClicBot-compatible devices. It is not affiliated with, endorsed by, sponsored by, or associated with KEYi Technology or the ClicBot brand.
The protocol implementation was independently developed by observing network traffic generated by my own device and the official app, solely for the purpose of interoperability. No source code, firmware, binaries, assets, icons, trademarks, private keys, certificates, or other proprietary materials from KEYi Technology are included in this project.
This project is intended to enable lawful control of devices owned by the user. It is not intended to bypass authentication, digital rights management, copy protection, access controls, or any other technical protection measures.
"ClicBot" and related names may be trademarks of their respective owners and are used here only to identify compatibility.
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 clicbot_unofficial-1.0.3.tar.gz.
File metadata
- Download URL: clicbot_unofficial-1.0.3.tar.gz
- Upload date:
- Size: 15.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27ff5495b4adac79eb607a1ae021c840ecf8747872c15c466ea4cb758e558367
|
|
| MD5 |
2206297711cfba5f8434ed968fecb950
|
|
| BLAKE2b-256 |
e6be0442b6d9081fc581d4cb31a279c208267991f7e5f84cb0b6f9cefa1294f7
|
File details
Details for the file clicbot_unofficial-1.0.3-py3-none-any.whl.
File metadata
- Download URL: clicbot_unofficial-1.0.3-py3-none-any.whl
- Upload date:
- Size: 17.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dffe561d8ebb66fb150c4b5a725551fba189821b9c3871f4bb485c96e2caf439
|
|
| MD5 |
be6c0107bd4e2bcb91a38c52c8a0a9dd
|
|
| BLAKE2b-256 |
38344169b15bf2b1fc2f67eaa504fd5fe39e50a2f0276572d121ccd0e554b545
|