ble2wled.wled - WLED Device Communication

WLED LED controller implementations.

This module provides abstract and concrete LED controller classes for communicating with WLED devices via HTTP and UDP protocols.

Protocols:
  • HTTP: JSON-based API, slower but more reliable

  • UDP: DRGB protocol, real-time updates with low latency

Example

Create and use an LED controller:

from ble2wled.wled import WLEDUDPController

controller = WLEDUDPController('wled.local', led_count=60, port=21324)
leds = [[255, 0, 0] for _ in range(60)]  # All red
controller.update(leds)
class ble2wled.wled.LEDController(host: str, led_count: int)[source]

Bases: ABC

Abstract base class for LED controllers.

Defines the interface that all LED controller implementations must follow.

host

WLED device hostname or IP address.

Type:

str

led_count

Total number of LEDs in the strip.

Type:

int

__init__(host: str, led_count: int)[source]

Initialize LED controller.

Parameters:
  • host (str) – WLED device hostname or IP address.

  • led_count (int) – Total number of LEDs in the strip.

Example

Subclass initialization:

class MyController(LEDController):
    def __init__(self, host, led_count):
        super().__init__(host, led_count)
abstractmethod update(leds: List[List[int]]) None[source]

Send LED color update to device.

Parameters:

leds (list) – List of RGB color values [R, G, B] 0-255.

class ble2wled.wled.WLEDHTTPController(host: str, led_count: int, max_retries: int = 3)[source]

Bases: LEDController

WLED controller using HTTP API.

Sends LED updates via WLED’s JSON HTTP API. Slower than UDP but more reliable and easier to debug.

Implements automatic retry logic for handling temporary connection failures and timeouts.

Example

Use HTTP controller:

controller = WLEDHTTPController('192.168.1.100', led_count=60)
leds = [[255, 100, 50] for _ in range(60)]
controller.update(leds)
__init__(host: str, led_count: int, max_retries: int = 3)[source]

Initialize HTTP controller.

Parameters:
  • host (str) – WLED device hostname or IP address.

  • led_count (int) – Total number of LEDs in the strip.

  • max_retries (int) – Maximum number of retry attempts on timeout. Default: 3.

update(leds: List[List[int]]) None[source]

Send LED update via HTTP POST with retry logic.

Sends LED data to WLED device using the /json/state endpoint. Automatically retries on timeout errors to handle temporary connection issues.

Parameters:

leds (list) – List of RGB color values [R, G, B] 0-255.

Example

Update LEDs via HTTP:

leds = [[255, 0, 0] for _ in range(60)]  # All red
controller.update(leds)
class ble2wled.wled.WLEDUDPController(host: str, led_count: int, port: int = 21324)[source]

Bases: LEDController

WLED controller using UDP DRGB protocol.

Sends LED updates via UDP using the DRGB protocol for real-time updates with minimal latency. Requires WLED to have UDP realtime protocol enabled.

The DRGB protocol is simple:
  • Header: ‘DRGB’ (4 bytes)

  • Data: RGB triplets (3 bytes per LED)

Example

Use UDP controller for real-time updates:

controller = WLEDUDPController('wled.local', led_count=60, port=21324)
leds = [[0, 255, 0] for _ in range(60)]  # All green
controller.update(leds)
__init__(host: str, led_count: int, port: int = 21324)[source]

Initialize UDP controller.

Parameters:
  • host (str) – WLED device hostname or IP address.

  • led_count (int) – Total number of LEDs in the strip.

  • port (int) – DRGB protocol port. Default: 21324.

Example

Create UDP controller with custom port:

controller = WLEDUDPController('wled.local', 60, port=21325)
update(leds: List[List[int]]) None[source]

Send LED update via UDP DRGB protocol.

Constructs DRGB packet with header and RGB data, sends via UDP. No response is expected or required.

Parameters:

leds (list) – List of RGB color values [R, G, B] 0-255.

Example

Update LEDs via UDP:

leds = [[255, 0, 255] for _ in range(60)]  # All magenta
controller.update(leds)

Overview

The wled module provides controllers for communicating with WLED devices via HTTP and UDP protocols. It handles:

  • HTTP requests with automatic retry logic

  • UDP DRGB protocol for fast updates

  • Graceful error handling and recovery

Quick Example

from ble2wled.wled import WLEDUDPController, WLEDHTTPController

# UDP is faster and doesn't require connection setup
controller = WLEDUDPController(
    host='192.168.1.100',
    led_count=60
)

# HTTP is slower but more reliable for some networks
controller = WLEDHTTPController(
    host='192.168.1.100',
    led_count=60,
    timeout=1.0,
    max_retries=3
)

# Send LED data
leds = [[255, 0, 0]] * 60  # All red
controller.update(leds)

Classes

WLEDUDPController

Fast UDP-based communication using DRGB protocol.

Attributes:

  • host (str) - WLED device hostname or IP

  • led_count (int) - Number of LEDs in strip

  • port (int, default 21324) - UDP port

Methods:

  • update(leds: List[List[int]]) - Send LED data to device

Advantages:

  • Faster (no connection overhead)

  • Fire-and-forget (no waiting for response)

  • Works with most WLED devices

Disadvantages:

  • No error handling (packets can be lost)

  • No acknowledgment

Example:

from ble2wled.wled import WLEDUDPController

controller = WLEDUDPController('192.168.1.100', 60)

# Send all red
leds = [[255, 0, 0] for _ in range(60)]
controller.update(leds)

# Send gradient
leds = [[i * 4, 0, 255 - i * 4] for i in range(60)]
controller.update(leds)

WLEDHTTPController

Reliable HTTP-based communication with automatic retry logic.

Attributes:

  • host (str) - WLED device hostname or IP

  • led_count (int) - Number of LEDs in strip

  • timeout (float, default 1.0) - HTTP request timeout in seconds

  • max_retries (int, default 3) - Number of retry attempts on timeout

  • port (int, default 80) - HTTP port

Methods:

  • update(leds: List[List[int]]) - Send LED data to device

Advantages:

  • HTTP retries on timeout (automatic recovery)

  • Error handling and logging

  • Works with unreliable networks

Disadvantages:

  • Slower (HTTP overhead)

  • Connection setup time

Example:

from ble2wled.wled import WLEDHTTPController

# Standard configuration
controller = WLEDHTTPController(
    host='192.168.1.100',
    led_count=60,
    timeout=1.0,
    max_retries=3
)

# For unreliable networks
controller = WLEDHTTPController(
    host='192.168.1.100',
    led_count=60,
    timeout=2.0,        # Longer timeout
    max_retries=5       # More retries
)

# For fast networks
controller = WLEDHTTPController(
    host='192.168.1.100',
    led_count=60,
    timeout=0.5,        # Shorter timeout
    max_retries=1       # Fewer retries
)

# Send update
leds = [[255, 0, 0]] * 60
controller.update(leds)

LED Data Format

Both controllers accept LED data as a list of RGB tuples:

# List of [R, G, B] values for each LED
# Each value is 0-255

leds = [
    [255, 0, 0],      # LED 0: Red
    [0, 255, 0],      # LED 1: Green
    [0, 0, 255],      # LED 2: Blue
    [255, 255, 255],  # LED 3: White
    # ... 56 more LEDs for 60-LED strip
]

controller.update(leds)

Requirements:

  • List length must equal led_count

  • Each element must be a list/tuple of [R, G, B]

  • Each value must be 0-255

Examples

Basic LED Updates

from ble2wled.wled import WLEDUDPController

controller = WLEDUDPController('192.168.1.100', 60)

# All red
leds = [[255, 0, 0]] * 60
controller.update(leds)

# Rainbow gradient
leds = [
    [int(255 * (i / 60)), int(255 * (1 - i / 60)), 0]
    for i in range(60)
]
controller.update(leds)

# Off
leds = [[0, 0, 0]] * 60
controller.update(leds)

With Animation Loop

from ble2wled.wled import WLEDUDPController
import time

controller = WLEDUDPController('192.168.1.100', 60)

# Simple animation: moving red dot
for iteration in range(100):
    position = iteration % 60
    leds = [[0, 0, 0]] * 60
    leds[position] = [255, 0, 0]
    controller.update(leds)
    time.sleep(0.05)  # 20fps

HTTP with Retry Handling

from ble2wled.wled import WLEDHTTPController
import logging

# Enable logging to see retries
logging.basicConfig(level=logging.INFO)

controller = WLEDHTTPController(
    host='192.168.1.100',
    led_count=60,
    max_retries=3
)

# Try to update
leds = [[255, 0, 0]] * 60
controller.update(leds)

# If device times out, will automatically retry
# Logs will show retry attempts

Choosing Between UDP and HTTP

Use UDP when:

  • WLED device is on stable local network

  • Speed is critical

  • You want fire-and-forget behavior

from ble2wled.wled import WLEDUDPController
controller = WLEDUDPController('wled.local', 60)

Use HTTP when:

  • Network is unreliable or WiFi

  • You need automatic recovery from timeouts

  • Device is temporarily unreachable

from ble2wled.wled import WLEDHTTPController
controller = WLEDHTTPController('wled.local', 60)

Use HTTP with more retries on bad networks:

from ble2wled.wled import WLEDHTTPController
controller = WLEDHTTPController(
    'wled.local',
    60,
    timeout=2.0,
    max_retries=5
)

Error Handling

HTTP Errors

Handled automatically with retry logic:

from ble2wled.wled import WLEDHTTPController

controller = WLEDHTTPController('192.168.1.100', 60)

# These errors are retried automatically:
# - ReadTimeout
# - Timeout
# - ConnectionError

# Logs will show retry attempts:
# "HTTP timeout on attempt 1/3 for 192.168.1.100, retrying..."
# If all retries fail: "HTTP request failed after 3 attempts for 192.168.1.100..."

leds = [[255, 0, 0]] * 60
controller.update(leds)  # May log timeout messages but continues

UDP Errors

UDP doesn’t wait for responses, so no timeouts. Packets may be lost:

from ble2wled.wled import WLEDUDPController

controller = WLEDUDPController('192.168.1.100', 60)

# UDP doesn't throw exceptions for lost packets
# If device unreachable, packets are silently dropped
leds = [[255, 0, 0]] * 60
controller.update(leds)

Debugging

Check Device Connectivity

# Test HTTP connection
curl http://192.168.1.100/json/state

# Test UDP with netcat (Linux/Mac)
echo "test" | nc -u 192.168.1.100 21324

See WLED Logs

Access WLED web interface at http://192.168.1.100 and check logs.

Performance Tips

  • UDP is ~10x faster than HTTP

  • HTTP adds network overhead but handles errors

  • Update interval of 0.05s (20fps) is good balance

  • Longer trails require more color calculations

See Also