Python
🚀 Getting Started
Hardware Connection Guide
Before starting your development, please check out our FAQ for details on turning the device on/off, understanding the LED indicators, and device pairing procedures across all platforms: Device Setup & FAQ.
System Requirements
The Python SDK is designed with cross-platform compatibility in mind. Ensure your host machine meets the following targets:
- Python Version: Requires Python 3.10 ~ 3.12 (Compatibility with older or newer versions has not been verified).
- Supported OS:
- macOS 10.15 or higher.
- Windows 10 build 10.0.15063 or higher.
Windows Environment Debugging Tips
Due to heavy fragmentation of built-in Bluetooth modules on Windows, your network card's LMP (Link Manager Protocol) must be 11.x (Bluetooth 5.2) or higher. Otherwise, scanning and connections will be highly unstable. If your current PC does not natively support this, we highly recommend buying a dedicated USB Bluetooth 5.0/5.2 adapter.
Installation & Integration
Integrating the Python SDK is straightforward. Simply configure the dependencies and use it directly.
Configure Dependencies
We recommend working inside a virtual environment (conda is preferred):
bash# Conda (Recommended) conda create -n my_env python=3.10 conda activate my_env # Or using standard venv python3 -m venv venv source venv/bin/activate # On Windows, use `venv\Scripts\activate`Install the necessary libraries:
bashpip3 install bleak>=0.20.0 # The core cross-platform BLE client pip3 install numpy>=1.21.0 # For fast array and math computations
(Note: If you plan on building a desktop GUI or data visualization charts, you might also want to install PyQt5 and pyqtgraph. Check out our open-source examples for a complete GUI setup).
Core API Reference
ZenLiteSDK (Class)
Global SDK configurations, logging, and device discovery. (The name ZenLite acts as a historical identifier to maintain backwards compatibility):
class ZenLiteSDK:
@staticmethod
def start_scan(callback):
"""Start scanning for nearby devices"""
pass
@staticmethod
def stop_scan():
"""Stop the scanning process"""
pass
@staticmethod
def set_log_level(level):
"""Set log level"""
passZenLiteDevice (Class)
Device proxy object to manage device status and send commands.
class ZenLiteDevice:
# --- Properties ---
uuid: str # Physical BLE address/UUID (OS-abstracted)
name: str # Bluetooth broadcast name
rssi: float # Signal strength (dBm)
in_pairing_mode: bool # Indicates if device is in pairing mode
connectivity: Connectivity # Connection state enum
battery_level: int # Battery percentage (0-100%)
hardware_revision: str # Hardware version
firmware_revision: str # Firmware version
# --- Connection Triggers ---
def set_listener(self, listener):
"""Set listener for data and state callbacks."""
pass
def connect(self):
"""Connect to the device."""
pass
def disconnect(self):
"""Disconnect the device."""
pass
def zl_pair(self, in_pairing_mode, callback):
"""Pair the device."""
pass
# --- Data Channel Constraints ---
def zl_config_afe(self, sample_rate, callback):
"""Set EEG sample rate."""
pass
def zl_config_imu(self, sample_rate, mode, callback):
"""Set IMU sample rate and mode."""
pass
def zl_config_ppg(self, report_rate, mode, raw_set_reg, raw_set_value, callback):
"""Set PPG report rate and mode."""
pass
# --- Device Settings ---
def zl_set_device_name(self, name, callback):
"""Set Bluetooth broadcast name."""
pass
def zl_set_sleep_idle_time(self, seconds, callback):
"""Set device idle (not worn) auto-shutdown time (seconds)."""
passZenLiteDeviceListener (Class)
Override methods in this class to receive device events and data callbacks:
class ZenLiteDeviceListener:
# --- System and Connection Events ---
def on_connectivity_change(self, connectivity): pass
def on_device_info_ready(self, device_info): pass
def on_contact_state_change(self, contact_state): pass
def on_orientation_change(self, orientation): pass
def on_battery_level_change(self, battery_level): pass
def on_error(self, error): pass
# --- Sensor Data ---
def on_eeg_data(self, eeg_data): pass
def on_raw_eeg_data(self, raw_eeg): pass
def on_imu_data(self, imu_data): pass
def on_ppg_data(self, ppg_data): pass
# --- Algorithm Indicators ---
def on_brain_wave(self, brain_wave): pass
def on_attention(self, attention, weighted_attention): pass
def on_meditation(self, meditation, calmness, awareness): pass
def on_sleep_stage(self, stage, confidence, drowsiness): passData Models
EEGData (Brainwave Data)
class EEGData:
sequence_num: int # Sequence number
sample_rate: float # Sample rate
eeg_data: list[float] # Microvolts arrayBrainWave (Frequency Band Indicators)
class BrainWave:
delta: float # 0.5-4 Hz
theta: float # 4-8 Hz
alpha: float # 8-13 Hz
low_beta: float # 13-17 Hz
high_beta: float # 17-30 Hz
gamma: float # 30-50 HzIMUData (Inertial Measurement Unit Data)
class IMUData:
acc_data: ACCData # Accelerometer data
gyro_data: GyroData # Gyroscope data
euler_angle_data: EulerAngleData # Euler Angle data
sample_rate: float # Sample rate
head: ImuPoseHead # Head posture
body: ImuPoseBody # Body posture
class ACCData:
x, y, z: list[float] # Accelerometer vectors
class GyroData:
x, y, z: list[float] # Gyroscope vectors
class EulerAngleData:
yaw, pitch, roll: list[float] # Yaw, pitch, roll (-180~180)PPGData (Optical Heart Rate Data)
class PPGData:
sequence_num: int # Sequence number
sample_rate: float # Sample rate
raw_data: list[PPGRawData] # Raw optical readings
algo_data: list[PPGAlgoData] # Analysis indicators
respiratory_rate: float # Respiration rate
respiratory_curve: list[float] # Respiration curve
respiratory_state: RespiratoryState
class PPGAlgoData:
hr: float # Heart Rate (bpm)
hr_conf: int # HR Confidence (0-100%)
rr: float # Respiration rate
spo2: float # Blood Oxygen Saturation
spo2_conf: int # SpO2 Confidence (0-100%)
hrv: float # Heart Rate Variability
stress: float # Stress index
class PPGRawData:
green1_count, green2_count: int # Green light counts
ir_count, red_count: int # Infrared/Red countsEnums & Constants
Connectivity and Physical Contact
class Connectivity(IntEnum):
connecting = 0
connected = 1
disconnecting = 2
disconnected = 3
class ContactState(IntEnum):
unknown = 0
off = 1 # Not worn correctly
eeg = 2 # Good EEG contact
all = 3 # Both EEG and PPG have good contact
class Orientation(IntEnum):
unknown = 0
upward = 1 # Worn normally
downward = 2 # Worn upside downStates
class SleepStage(IntEnum):
unknown = -1
awake = 0 # Awake
rem = 1 # REM sleep
light = 2 # Light sleep
deep = 3 # Deep sleep
class RespiratoryState(IntEnum):
rest = 0 # Resting
inward = 1 # Inhaling
outward = 2 # ExhalingStream Configuration
class EEGSampleRate(IntEnum):
# Pass `1` to turn OFF the EEG stream
sr128 = 2 # 128 Hz
sr256 = 3 # 256 Hz
class IMUSampleRate(IntEnum):
# Pass `1` to turn OFF the IMU stream
sr25, sr50, sr100, sr200, sr400, sr800 = 2, 3, 4, 5, 6, 7 # (Hz)
class IMUMode(IntEnum):
# Pass `0` to set NONE
acc = 1 # Accelerometer only
gyro = 2 # Gyroscope only
acc_gyro = 3 # Both
euler = 4 # Computed Euler Angles
class PPGReportRate(IntEnum):
# Usually sr1 (param: 2) is used; HRV analysis may need sr50 (param: 5)
none, off, sr1, sr5, sr25, sr50, sr100 = 0, 1, 2, 3, 4, 5, 6
class PPGMode(IntEnum):
# Usually you'd select `algo = 2` to obtain human-readable outputs.
raw_data = 1 # Only optical arrays
algo = 2 # Algorithm indicators (HR, SpO2, HRV, etc.)
spo2, hr, hrv = 3, 4, 5 # Specific single-target computationsTroubleshooting & Best Practices
Connection Issues
If you're having trouble discovering or connecting to the device, be sure to check the global troubleshooting section: 👉 Device Setup & FAQ
Python Development Gotchas
- Thread Safety and GUI Updates: All data callbacks (e.g.,
on_eeg_data) run on background threads, so you must NEVER update UI components directly from callbacks. Doing so will cause instant crashes or deadlocks.- Solution: Always use thread-safe dispatch mechanisms provided by your framework (such as PyQt's
Signal.emit), or use Python's built-inqueue.Queueproducer-consumer model to pass data back to the main thread securely.
- Solution: Always use thread-safe dispatch mechanisms provided by your framework (such as PyQt's
- Memory Management: The hardware streams data at high frequency (especially 256 Hz EEG arrays). If you append all incoming data to a standard Python
list()without truncation, memory usage will grow unbounded. Use fixed-length Ring Buffers ornumpyrolling arrays instead.
📖 Complete Integration Example
We provide open-source reference implementations, including both a headless CLI data sampler and a PyQt desktop app with real-time visualization.
See the full source code for asynchronous thread safety, data buffering, and lifecycle handling:
