ble2wled.simulator - LED Simulator and Mock Beacons

LED strip simulator with visual CLI output.

This module provides a simulator for testing beacon visualization without requiring a real WLED device. The simulator displays the LED strip state as colored dots in the terminal using ANSI escape codes.

The simulator implements the LEDController interface and can be used as a drop-in replacement for WLEDHTTPController or WLEDUDPController.

Example

Simulate beacon visualization:

from ble2wled.simulator import LEDSimulator
from ble2wled import BeaconState, run_wled_beacons

# Create simulator
simulator = LEDSimulator(led_count=60, rows=10, cols=6)

# Create beacon state
beacon_state = BeaconState()

# Run animation with simulator (no real WLED needed)
run_wled_beacons(
    simulator,
    led_count=60,
    beacon_state=beacon_state,
    update_interval=0.05
)
class ble2wled.simulator.LEDSimulator(led_count: int = 60, rows: int = 10, cols: int = 6)[source]

Bases: LEDController

LED strip simulator with visual terminal output.

Displays the LED strip as a grid of colored dots in the terminal using ANSI 24-bit color codes. Useful for testing beacon visualization without a real WLED device.

The grid is arranged in rows x cols format. For example, with 60 LEDs in a 10x6 grid, LED 0 is top-left, LED 5 is top-right, LED 6 is second-row-left, etc.

led_count

Total number of LEDs.

Type:

int

rows

Number of rows in display grid.

Type:

int

cols

Number of columns in display grid.

Type:

int

current_leds

Current LED RGB values.

Type:

List[List[int]]

lock

Thread-safe access to LED data.

Type:

threading.Lock

Example

Create a 10x6 grid simulator:

simulator = LEDSimulator(led_count=60, rows=10, cols=6)
leds = [[255, 0, 0] for _ in range(60)]  # All red
simulator.update(leds)
__init__(led_count: int = 60, rows: int = 10, cols: int = 6)[source]

Initialize the LED simulator.

Parameters:
  • led_count (int) – Total number of LEDs. Default: 60. Must equal rows x cols.

  • rows (int) – Number of rows in display grid. Default: 10.

  • cols (int) – Number of columns in display grid. Default: 6.

Raises:

ValueError – If rows x cols does not equal led_count.

Example

Create 120-LED simulator (12 rows x 10 cols):

simulator = LEDSimulator(led_count=120, rows=12, cols=10)
update(leds: List[List[int]]) None[source]

Update and display the LED strip.

Updates the current LED state and renders the grid to terminal. Uses ANSI 24-bit true color escape codes for each LED.

Parameters:

leds (List[List[int]]) – LED data as list of [R, G, B] triplets. Expected length: led_count.

Example

Display a gradient:

import time
from ble2wled.simulator import LEDSimulator

simulator = LEDSimulator(led_count=60)
for i in range(60):
    leds = [[0, 0, 0] for _ in range(60)]
    leds[i] = [255, 100, 0]  # Orange
    simulator.update(leds)
    time.sleep(0.05)
get_snapshot() List[List[int]][source]

Get current LED state.

Returns:

Copy of current LED state as list of [R, G, B].

Return type:

List[List[int]]

Example

Check current state:

leds = simulator.get_snapshot()
print(f"LED 0 color: RGB{tuple(leds[0])}")
class ble2wled.simulator.MockBeaconGenerator(num_beacons: int = 3, rssi_range: tuple = (-90, -30))[source]

Bases: object

Generate mock beacon data for simulator testing.

Creates synthetic BLE beacon data that changes over time to simulate real beacon movement and signal strength variation.

beacon_ids

List of simulated beacon IDs.

Type:

List[str]

positions

Current position (0.0-1.0) for each beacon.

Type:

dict

rssi_base

Base RSSI value for each beacon.

Type:

dict

Example

Generate beacon updates:

from ble2wled.simulator import MockBeaconGenerator
from ble2wled import BeaconState

generator = MockBeaconGenerator(num_beacons=3)
beacon_state = BeaconState()

for _ in range(100):
    beacons = generator.update()
    for beacon_id, rssi in beacons.items():
        beacon_state.update(beacon_id, rssi)
__init__(num_beacons: int = 3, rssi_range: tuple = (-90, -30))[source]

Initialize mock beacon generator.

Parameters:
  • num_beacons (int) – Number of simulated beacons. Default: 3.

  • rssi_range (tuple) – (min_rssi, max_rssi) signal range. Default: (-90, -30). Typical RSSI range for BLE: -90 (far/weak) to -30 (close/strong).

Example

Generate 5 beacons with custom RSSI range:

generator = MockBeaconGenerator(
    num_beacons=5,
    rssi_range=(-100, -20)
)
update(time_delta: float = 0.1) dict[source]

Update beacon positions and signal strengths.

Beacons move in a circular pattern and signal strength oscillates around their base RSSI value, simulating realistic beacon movement.

Parameters:

time_delta (float) – Time step for simulation. Default: 0.1 seconds.

Returns:

Beacon data as {beacon_id: rssi_value}.

Return type:

dict

Example

Step through beacon updates:

import time
generator = MockBeaconGenerator(num_beacons=3)
for _ in range(100):
    beacons = generator.update(time_delta=0.05)
    # Update beacon_state with beacons data
    time.sleep(0.05)

Overview

The simulator module provides tools for testing beacon visualization without requiring a real WLED device or MQTT broker. It includes:

  • LEDSimulator - Visual LED display in terminal using ANSI colors

  • MockBeaconGenerator - Generates realistic simulated beacon data

  • CLI interface - Command-line tool for testing

Quick Example

from ble2wled.simulator import LEDSimulator, MockBeaconGenerator
from ble2wled.states import BeaconState
from ble2wled.animation import run_wled_beacons

# Create simulator
simulator = LEDSimulator(led_count=60, rows=10, cols=6)

# Create beacon state
state = BeaconState()

# Generate mock beacons
generator = MockBeaconGenerator(state, num_beacons=3, led_count=60)
generator.start()

# Run animation
try:
    run_wled_beacons(simulator, 60, state, update_interval=0.1)
finally:
    generator.stop()

Classes

LEDSimulator

Displays LEDs as colored blocks in terminal using ANSI 24-bit colors.

Attributes:

  • led_count (int) - Total number of LEDs

  • rows (int, default 10) - Grid rows

  • cols (int, default 6) - Grid columns

  • width (int) - Terminal width needed (cols * 4)

  • height (int) - Terminal height needed (rows + 5)

Methods:

  • update(leds: List[List[int]]) - Display LED data

  • clear() - Clear display

Requirements:

  • Terminal must support ANSI 24-bit colors

  • Terminal width must be at least cols * 4

  • Terminal height must be at least rows + 5

Example:

from ble2wled.simulator import LEDSimulator

# Create 60-LED display in 10×6 grid
simulator = LEDSimulator(led_count=60, rows=10, cols=6)

# Display colors
leds = [[255, 0, 0]] * 30 + [[0, 255, 0]] * 30
simulator.update(leds)

# Clear display
simulator.clear()

LED Count and Grid Layout

The LED count must equal rows × cols:

# 60 LEDs (10 rows × 6 columns)
sim = LEDSimulator(led_count=60, rows=10, cols=6)

# 100 LEDs (20 rows × 5 columns)
sim = LEDSimulator(led_count=100, rows=20, cols=5)

# 120 LEDs (12 rows × 10 columns)
sim = LEDSimulator(led_count=120, rows=12, cols=10)

MockBeaconGenerator

Generates simulated beacon data for testing.

Attributes:

  • beacon_state (BeaconState) - State to update with beacons

  • num_beacons (int) - Number of beacons to simulate

  • led_count (int) - Total LEDs (for position bounds)

  • update_interval (float, default 0.1) - Update interval in seconds

Methods:

  • start() - Start generating beacons (non-blocking, background thread)

  • stop() - Stop generator

  • is_running (property) - Whether generator is running

Behavior:

Generates random beacon movements:

  • Beacons move randomly along LED strip

  • RSSI values change realistically (-20 to -90 dBm)

  • Updates at specified interval

  • Beacons persist until stopped

Example:

from ble2wled.simulator import MockBeaconGenerator
from ble2wled.states import BeaconState

state = BeaconState()

# Generate 5 mock beacons
gen = MockBeaconGenerator(state, num_beacons=5, led_count=60)
gen.start()

# Beacons are now updating in background
import time
time.sleep(5)

# Stop generator
gen.stop()

Examples

Visual LED Display

from ble2wled.simulator import LEDSimulator
from ble2wled.states import BeaconState
from ble2wled.colors import beacon_id_to_hue, hsv_to_rgb
import time

# Create display
simulator = LEDSimulator(led_count=60, rows=10, cols=6)
state = BeaconState()

# Add some beacons
state.update_or_create('beacon_1', position=15, rssi=-50)
state.update_or_create('beacon_2', position=30, rssi=-60)
state.update_or_create('beacon_3', position=45, rssi=-70)

# Render beacons
leds = [[0, 0, 0]] * 60
for beacon_id, beacon in state.beacons.items():
    pos = int(beacon.position)
    hue = beacon_id_to_hue(beacon_id)
    r, g, b = hsv_to_rgb(hue, 1.0, 1.0)
    leds[pos] = [r, g, b]

# Display
simulator.update(leds)

Mock Beacon Animation

from ble2wled.simulator import LEDSimulator, MockBeaconGenerator
from ble2wled.states import BeaconState
from ble2wled.animation import run_wled_beacons

# Setup
simulator = LEDSimulator(led_count=60, rows=10, cols=6)
state = BeaconState()

# Generate mock beacons
generator = MockBeaconGenerator(state, num_beacons=3, led_count=60)
generator.start()

# Run animation
try:
    run_wled_beacons(
        simulator,
        led_count=60,
        beacon_state=state,
        update_interval=0.1,
        trail_length=8,
        fade_factor=0.75
    )
finally:
    generator.stop()

Custom Grid Layouts

Adjust grid for different terminal sizes:

from ble2wled.simulator import LEDSimulator

# Narrow terminal (12 columns wide)
sim_narrow = LEDSimulator(led_count=60, rows=5, cols=12)

# Wide terminal (4 columns wide)
sim_wide = LEDSimulator(led_count=60, rows=15, cols=4)

# Very wide display
sim_very_wide = LEDSimulator(led_count=120, rows=6, cols=20)

Programmatic LED Display

from ble2wled.simulator import LEDSimulator

simulator = LEDSimulator(led_count=60, rows=10, cols=6)

# Rainbow gradient
leds = []
for i in range(60):
    hue = (i / 60) * 360
    from ble2wled.colors import hsv_to_rgb
    r, g, b = hsv_to_rgb(hue, 1.0, 1.0)
    leds.append([r, g, b])

simulator.update(leds)

Display Output Format

The simulator displays LEDs as colored blocks:

██████████████████████████████
██████████████████████████████
██████████████████████████████
███░░░░███░░░░██████████░░████
██░░░░░░░░░░░░██████████████░░
██░░░░░░░░░░░░██████████░░░░░░
██████████░░░░██░░░░██░░░░░░░░
██████████░░░░██░░░░██░░░░░░░░
██████████░░░░██░░░░██░░░░░░░░
██████████████████████████░░░░
  • Bright colors = bright LEDs

  • Dim colors = dim LEDs

  • Each block is one LED

CLI Usage

Run simulator from command line:

python -m ble2wled.cli_simulator

With options:

python -m ble2wled.cli_simulator \
  --led-count 120 \
  --rows 12 \
  --cols 10 \
  --beacons 5 \
  --update-interval 0.05 \
  --trail-length 15 \
  --fade-factor 0.75 \
  --duration 60

With MQTT:

python -m ble2wled.cli_simulator --mqtt \
  --mqtt-broker 192.168.1.100 \
  --mqtt-location living_room \
  --mqtt-username user \
  --mqtt-password pass

Performance

Terminal Requirements

For smooth animation:

  • Terminal must support ANSI 24-bit colors

  • Terminal width >= cols * 4

  • Terminal height >= rows + 5

  • Adequate CPU for rendering

Typical Performance

  • 60 LEDs (10×6): ~50fps on modern CPU

  • 120 LEDs (12×10): ~25fps

  • 300 LEDs (20×15): ~5-10fps

Optimization Tips

For CPU-constrained systems:

# Use fewer LEDs
simulator = LEDSimulator(led_count=30, rows=5, cols=6)

# Use slower update interval
run_wled_beacons(controller, ..., update_interval=0.2)

# Use shorter trails
run_wled_beacons(controller, ..., trail_length=4)

For high-speed animation:

# Use moderate LED count
simulator = LEDSimulator(led_count=100, rows=10, cols=10)

# Use fast update interval
run_wled_beacons(controller, ..., update_interval=0.02)

Troubleshooting

Nothing displays or error about terminal:

Try smaller grid:

simulator = LEDSimulator(led_count=30, rows=5, cols=6)

Colors look wrong:

Ensure terminal supports 24-bit colors. Most modern terminals do.

Animation stutters:

Reduce LED count or increase update_interval.

See Also