ble2wled.states - Beacon State Management

Beacon state management with timeout and fade-out.

This module provides the BeaconState class for tracking BLE beacons with automatic timeout and fade-out effects. Beacons update their “life” value from 1.0 (fully visible) to 0.0 (faded out) as they age.

Example

Track beacons and get their current state:

state = BeaconState(timeout_seconds=6.0, fade_out_seconds=4.0)
state.update('beacon_1', -50)  # Update with RSSI
beacons = state.snapshot()     # Get current beacons
for beacon_id, (rssi, life) in beacons.items():
    print(f"{beacon_id}: RSSI={rssi}, life={life}")
class ble2wled.states.BeaconState(timeout_seconds: float = 5.0, fade_out_seconds: float = 3.0)[source]

Bases: object

Manages the state of detected beacons with automatic timeout and fade-out.

This class maintains a thread-safe collection of beacons with their RSSI values and life metrics. Beacons automatically timeout when not updated and fade out gracefully over a configurable period.

timeout

Seconds before a beacon is considered timed out.

Type:

float

fade_out

Seconds to fade out after timeout.

Type:

float

__init__(timeout_seconds: float = 5.0, fade_out_seconds: float = 3.0)[source]

Initialize beacon state tracker.

Parameters:
  • timeout_seconds (float) – Duration before a beacon is considered timed out (no updates received). Default is 5.0 seconds.

  • fade_out_seconds (float) – Duration to fade out the beacon after timeout. Default is 3.0 seconds.

Example

Create a beacon state tracker with 6-second timeout and 4-second fade:

state = BeaconState(timeout_seconds=6.0, fade_out_seconds=4.0)
update(beacon_id: str, rssi: int) None[source]

Update beacon with signal strength.

Updates or creates a beacon entry with the current timestamp and RSSI. Thread-safe operation.

Parameters:
  • beacon_id (str) – Unique beacon identifier (e.g., ‘iBeacon:2686f39c-bada-4658-854a-a62e7e5e8b8d-1-0’).

  • rssi (int) – Received Signal Strength Indicator in dBm (typically -30 to -100).

Example

Update beacon with RSSI value:

state.update('beacon_1', -50)  # Update beacon_1 with -50 dBm
snapshot() Dict[str, Tuple[int, float]][source]

Get current active beacons with RSSI and life values.

Returns a thread-safe snapshot of all active beacons. Automatically removes beacons that have completely faded out. Each beacon entry includes its RSSI and life value (0.0 = invisible, 1.0 = fully visible).

Returns:

Dictionary mapping beacon_id (str) to (rssi, life) tuples.

rssi (int): Signal strength in dBm. life (float): Beacon visibility (0.0 to 1.0).

Return type:

dict

Example

Get current beacon snapshot:

beacons = state.snapshot()
if 'beacon_1' in beacons:
    rssi, life = beacons['beacon_1']
    print(f"Beacon 1: {rssi} dBm, {life*100:.0f}% visible")

Overview

The states module manages beacon state and lifecycle. It tracks:

  • Active beacons and their positions

  • Signal strength (RSSI) over time

  • Beacon timeouts (when beacons disappear)

  • Fade-out animations for departing beacons

Key Classes

Beacon

Represents a single beacon with its current state.

BeaconState

Tracks all active and fading beacons.

Quick Example

from ble2wled.states import BeaconState, Beacon

# Create beacon state tracker
state = BeaconState(
    timeout_seconds=6.0,      # Beacon times out after 6 seconds
    fade_out_seconds=4.0      # Fade out over 4 seconds after timeout
)

# Add a beacon (usually from MQTT)
state.update_or_create(
    beacon_id='device_123',
    position=30,              # Position on LED strip (0-60)
    rssi=-45                  # Signal strength in dBm
)

# Get active beacon
beacon = state.beacons['device_123']
print(f"Position: {beacon.position}, RSSI: {beacon.rssi}")

# Check if beacon is in timeout
if beacon.is_in_timeout:
    print("Beacon has timed out!")

# Update beacon positions/signals
state.update_or_create('device_123', position=32, rssi=-48)

# Cleanup expired beacons
state.cleanup()

Classes

Beacon

A single beacon with position and signal information.

Attributes:

  • beacon_id (str) - Unique beacon identifier

  • position (float) - Position on LED strip (0 to led_count)

  • rssi (int) - Signal strength in dBm (typically -20 to -100)

  • last_update_time (float) - Timestamp of last update

  • timeout_at (float) - Timestamp when beacon times out

  • is_in_timeout (bool) - Whether beacon has timed out

Methods:

  • should_be_removed(current_time: float) -> bool - Check if beacon should be removed

  • get_fade_factor(current_time: float) -> float - Get fade factor (0.0-1.0) during fade-out

BeaconState

Tracks all active and fading beacons.

Attributes:

  • beacons (Dict[str, Beacon]) - Active and fading beacons

  • timeout_seconds (float) - Beacon timeout duration

  • fade_out_seconds (float) - Fade-out animation duration

Methods:

  • update_or_create(beacon_id: str, position: float, rssi: int) - Update or create beacon

  • cleanup() - Remove expired beacons

  • clear() - Remove all beacons

Examples

Basic Beacon Tracking

from ble2wled.states import BeaconState

state = BeaconState()

# Update beacon position and signal
state.update_or_create('beacon_1', position=15, rssi=-50)
state.update_or_create('beacon_2', position=45, rssi=-60)

# Check active beacons
print(f"Active beacons: {len(state.beacons)}")
for beacon_id, beacon in state.beacons.items():
    print(f"  {beacon_id}: pos={beacon.position}, rssi={beacon.rssi}")

Beacon Lifecycle

from ble2wled.states import BeaconState
import time

state = BeaconState(timeout_seconds=2.0, fade_out_seconds=1.0)

# Beacon appears
state.update_or_create('device_1', position=30, rssi=-45)
print(f"Beacon active: {state.beacons['device_1'].is_in_timeout}")  # False

# After 2 seconds without update: beacon times out
time.sleep(2.1)
print(f"Beacon timed out: {state.beacons['device_1'].is_in_timeout}")  # True

# Get fade factor for animation
fade = state.beacons['device_1'].get_fade_factor(time.time())
print(f"Fade factor: {fade}")  # 0.5 (halfway through fade-out)

# After 3 seconds: beacon is removed
time.sleep(1.0)
state.cleanup()
print(f"Beacon count: {len(state.beacons)}")  # 0

MQTT Integration

from ble2wled.states import BeaconState
from ble2wled.mqtt import EspresenseBeaconListener

state = BeaconState()

# MQTT listener updates beacon state automatically
listener = EspresenseBeaconListener(
    state,
    broker='localhost',
    location='living_room'
)
listener.start()

# Beacon state is automatically updated from MQTT
time.sleep(0.1)
for beacon_id, beacon in state.beacons.items():
    print(f"{beacon_id}: {beacon.position} @ {beacon.rssi} dBm")

Animation Integration

from ble2wled.states import BeaconState
from ble2wled.colors import position_to_color_hue

state = BeaconState()
state.update_or_create('beacon_1', position=30, rssi=-50)

# Get beacon for animation
beacon = state.beacons['beacon_1']

# Calculate color based on distance/signal
hue = position_to_color_hue(beacon.position, led_count=60)
print(f"Color hue: {hue}")

# Use fade factor if beacon is timing out
brightness = 1.0 * beacon.get_fade_factor(time.time())
print(f"Brightness: {brightness}")

Custom Timeout Configuration

from ble2wled.states import BeaconState

# Fast timeout: 2 seconds active, 1 second fade-out
state_fast = BeaconState(timeout_seconds=2.0, fade_out_seconds=1.0)

# Slow timeout: 10 seconds active, 5 seconds fade-out
state_slow = BeaconState(timeout_seconds=10.0, fade_out_seconds=5.0)

# Very persistent: 30 seconds before timeout
state_persistent = BeaconState(timeout_seconds=30.0, fade_out_seconds=2.0)

See Also