Skip to content

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

  1. 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`
  2. Install the necessary libraries:

    bash
    pip3 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):

python
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"""
        pass

ZenLiteDevice (Class)

Device proxy object to manage device status and send commands.

python
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)."""
        pass

ZenLiteDeviceListener (Class)

Override methods in this class to receive device events and data callbacks:

python
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): pass

Data Models

EEGData (Brainwave Data)

python
class EEGData:
    sequence_num: int           # Sequence number
    sample_rate: float          # Sample rate
    eeg_data: list[float]       # Microvolts array

BrainWave (Frequency Band Indicators)

python
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 Hz

IMUData (Inertial Measurement Unit Data)

python
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)

python
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 counts

Enums & Constants

Connectivity and Physical Contact

python
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 down

States

python
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         # Exhaling

Stream Configuration

python
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 computations

Troubleshooting & 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-in queue.Queue producer-consumer model to pass data back to the main thread securely.
  • 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 or numpy rolling 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:

👉 OxyZen Python Example Repository