ble2wled.animation - Animation Loop
Beacon animation and trail rendering.
This module handles animation of beacons moving across LED strips and rendering motion trails for visual effects.
Example
Animate beacons with motion trails:
from ble2wled.animation import BeaconRunner, add_trail
from ble2wled.colors import ble_beacon_to_rgb
runner = BeaconRunner(led_count=60)
leds = [[0, 0, 0] for _ in range(60)]
# Get next position for beacon and add trail
pos = runner.next_position('beacon_1')
color = ble_beacon_to_rgb('beacon_1', rssi=-50, life=0.8)
add_trail(leds, pos, color, trail_length=10, fade_factor=0.75)
- class ble2wled.animation.BeaconRunner(led_count: int)[source]
Bases:
objectManages animation positions for beacons moving along LED strip.
Each beacon gets its own position on the LED strip. Calling next_position() increments the position, creating a moving effect across the strip.
- __init__(led_count: int)[source]
Initialize beacon runner.
- Parameters:
led_count (int) – Total number of LEDs in the strip.
Example
Create a beacon runner for a 60-LED strip:
runner = BeaconRunner(led_count=60)
- next_position(beacon_id: str) int[source]
Get next position for a beacon.
Increments position and wraps around at LED count. Maintains consistent position increment per beacon across calls.
- Parameters:
beacon_id (str) – Unique beacon identifier.
- Returns:
Next position index (0 to led_count-1).
- Return type:
Example
Get beacon positions for animation:
runner = BeaconRunner(led_count=60) pos1 = runner.next_position('beacon_1') # Returns 0 pos1 = runner.next_position('beacon_1') # Returns 1 pos2 = runner.next_position('beacon_2') # Returns 0
- ble2wled.animation.add_trail(leds: List[List[int]], position: int, color: Tuple[int, int, int], trail_length: int, fade_factor: float) None[source]
Add motion trail to LED array.
Creates a fading trail effect behind the beacon by rendering progressively dimmer segments behind the main position. Uses additive blending to combine trail colors with existing LED values.
- Trail Rendering:
LED at position gets full color
Each previous LED gets color * fade_factor^distance
Colors are additively blended (clamped at 255)
- Parameters:
leds (list) – LED color array (list of [R, G, B] with values 0-255).
position (int) – Current beacon position index.
color (tuple) – RGB color tuple (R, G, B) with values 0-255.
trail_length (int) – Number of LEDs in the trail (including main position).
fade_factor (float) – Brightness decay per segment (0.0 to 1.0). 0.75 means each trailing segment is 75% brightness of previous.
- Raises:
ValueError – If trail_length is negative or fade_factor outside 0-1.
Example
Add a red trail to the LED array:
leds = [[0, 0, 0] for _ in range(60)] add_trail(leds, position=10, color=(255, 0, 0), trail_length=8, fade_factor=0.75) # Now leds[10] has max red, leds[9] has 75% red, etc.
Overview
The animation module provides the main animation loop and LED rendering logic. It:
Processes beacon state into LED colors
Renders motion trails
Handles fade-out animations
Updates WLED device at regular intervals
Key Function
run_wled_beacons() is the main entry point for running beacon visualization.
Quick Example
from ble2wled.config import Config
from ble2wled.states import BeaconState
from ble2wled.mqtt import EspresenseBeaconListener
from ble2wled.wled import WLEDUDPController
from ble2wled.animation import run_wled_beacons
# Load configuration
config = Config('.env')
# Create beacon state and MQTT listener
beacon_state = BeaconState()
mqtt_listener = EspresenseBeaconListener(
beacon_state,
broker=config.mqtt_broker,
location=config.mqtt_location
)
mqtt_listener.start()
# Create WLED controller
controller = WLEDUDPController(config.wled_host, config.led_count)
# Run animation loop (blocking)
run_wled_beacons(
controller,
led_count=config.led_count,
beacon_state=beacon_state
)
Functions
run_wled_beacons()
Main animation loop for beacon visualization.
Parameters:
controller- WLED device controller (WLEDHTTPController or WLEDUDPController)led_count(int) - Total number of LEDsbeacon_state(BeaconState) - Beacon state trackerupdate_interval(float, default 0.05) - Update interval in seconds (50ms = 20fps)trail_length(int, default 8) - Length of motion trail in LEDsfade_factor(float, default 0.75) - Trail fade factor (0.1-1.0)
Behavior:
The function runs a continuous animation loop:
Read beacon state - Get active and fading beacons
Initialize LED array - Create array of [R, G, B] for each LED
Render trails - For each beacon, render motion trail on LEDs
Apply fade-out - Reduce brightness for beacons in timeout
Update device - Send LED array to WLED controller
Cleanup - Remove expired beacons
Sleep - Wait update_interval before next iteration
Raises:
KeyboardInterrupt- When user presses Ctrl+C
Example:
from ble2wled.animation import run_wled_beacons
from ble2wled.wled import WLEDUDPController
from ble2wled.states import BeaconState
state = BeaconState()
controller = WLEDUDPController('192.168.1.100', 60)
try:
run_wled_beacons(
controller,
led_count=60,
beacon_state=state,
update_interval=0.05, # 20fps
trail_length=10, # 10-LED trail
fade_factor=0.75 # Dim trail
)
except KeyboardInterrupt:
print("Animation stopped")
Animation Parameters
Update Interval
Controls animation smoothness and CPU usage.
Values:
0.02- 50fps (very smooth, high CPU)0.05- 20fps (good balance, recommended)0.1- 10fps (lower CPU, less smooth)0.2- 5fps (low CPU, clearly animated)
Example:
# Fast animation
run_wled_beacons(controller, ..., update_interval=0.02)
# Smooth default
run_wled_beacons(controller, ..., update_interval=0.05)
# CPU efficient
run_wled_beacons(controller, ..., update_interval=0.1)
Trail Length
Number of LEDs the motion trail extends backward from beacon position.
Values:
4- Short trails (just position)8- Medium trails (default)15- Long trails (motion effect)30- Very long trails (significant motion history)
Example:
# Short trail
run_wled_beacons(controller, ..., trail_length=4)
# Medium trail (default)
run_wled_beacons(controller, ..., trail_length=8)
# Long trail (motion effect)
run_wled_beacons(controller, ..., trail_length=15)
Fade Factor
How much each trail segment fades compared to the previous.
Values:
0.1- Fast fade (dim trails)0.5- Medium fade0.75- Default fade0.9- Slow fade (bright trails)
Behavior:
For a trail with trail_length=8 and fade_factor=0.75:
Position 0 (current): brightness 1.0
Position 1: brightness 0.75
Position 2: brightness 0.56 (0.75²)
Position 3: brightness 0.42 (0.75³)
… and so on
Example:
# Dim trails
run_wled_beacons(controller, ..., fade_factor=0.5)
# Medium trails (default)
run_wled_beacons(controller, ..., fade_factor=0.75)
# Bright trails
run_wled_beacons(controller, ..., fade_factor=0.9)
Examples
Basic Animation Loop
from ble2wled.animation import run_wled_beacons
from ble2wled.states import BeaconState
from ble2wled.wled import WLEDUDPController
from ble2wled.simulator import MockBeaconGenerator
# Setup
state = BeaconState()
controller = WLEDUDPController('192.168.1.100', 60)
# Generate mock beacons for testing
generator = MockBeaconGenerator(state, 3, 60)
generator.start()
# Run animation
try:
run_wled_beacons(controller, 60, state)
finally:
generator.stop()
With MQTT
from ble2wled.animation import run_wled_beacons
from ble2wled.states import BeaconState
from ble2wled.mqtt import EspresenseBeaconListener
from ble2wled.wled import WLEDUDPController
# Setup
state = BeaconState()
listener = EspresenseBeaconListener(state, 'localhost', 'living_room')
controller = WLEDUDPController('192.168.1.100', 60)
# Start listener
listener.start()
# Run animation
try:
run_wled_beacons(
controller,
led_count=60,
beacon_state=state,
update_interval=0.05,
trail_length=8,
fade_factor=0.75
)
finally:
listener.stop()
With Custom Parameters
from ble2wled.animation import run_wled_beacons
from ble2wled.config import Config
from ble2wled.states import BeaconState
from ble2wled.wled import WLEDUDPController
# Load configuration
config = Config('.env')
# Create components
state = BeaconState(
timeout_seconds=config.beacon_timeout_seconds,
fade_out_seconds=config.beacon_fade_out_seconds
)
controller = WLEDUDPController(config.wled_host, config.led_count)
# Run with configuration values
run_wled_beacons(
controller,
led_count=config.led_count,
beacon_state=state,
update_interval=config.animation_update_interval,
trail_length=config.animation_trail_length,
fade_factor=config.animation_fade_factor
)
Performance Considerations
For smooth 20fps animation on 60 LEDs:
run_wled_beacons(
controller,
led_count=60,
beacon_state=state,
update_interval=0.05 # 20fps
)
For CPU-constrained systems:
run_wled_beacons(
controller,
led_count=60,
beacon_state=state,
update_interval=0.1, # 10fps
trail_length=4, # Shorter trail
fade_factor=0.5 # Faster fade
)
For high-end systems with more LEDs:
run_wled_beacons(
controller,
led_count=300, # More LEDs
beacon_state=state,
update_interval=0.02, # 50fps
trail_length=15, # Longer trail
fade_factor=0.9 # Slower fade
)
Debugging Animation
To debug animation issues, capture LED data before sending:
from ble2wled.animation import run_wled_beacons
from ble2wled.wled import WLEDUDPController
class DebugController(WLEDUDPController):
def update(self, leds):
# Print first 10 LEDs
print(f"LEDs: {leds[:10]}")
super().update(leds)
controller = DebugController('192.168.1.100', 60)
run_wled_beacons(controller, 60, state)
See Also
ble2wled.states - Beacon State Management - Beacon state tracking
ble2wled.colors - Color Calculations - Color calculations
ble2wled.wled - WLED Device Communication - WLED device communication
Configuration - Configure animation parameters