ble2wled.colors - Color Calculations

Color conversion and distance estimation from RSSI.

This module provides functions for converting BLE beacon signal strength (RSSI) to distance estimates, mapping distance to colors on a gradient, and generating beacon-specific colors with life-based brightness modulation.

The distance estimation uses the Friis path loss model:

distance = 10^((TX_POWER - RSSI) / (10 * N))
Color Mapping:
  • Beacons near (< 0.5m): Yellow (255, 255, 0)

  • Beacons far (> 10m): Red (255, 0, 0)

  • Linear gradient between near and far distances

Example

Convert beacon data to RGB color:

rssi = -50
distance = estimate_distance_from_rssi(rssi)
color = ble_beacon_to_rgb('beacon_1', rssi, life=0.8)
print(f"Beacon at {distance:.2f}m: RGB{color}")
ble2wled.colors.estimate_distance_from_rssi(rssi: int, tx_power: int = -59, n: float = 2.0) float[source]

Estimate distance from signal strength using path loss model.

Uses the Friis free space path loss model to estimate distance from RSSI. The accuracy depends on the environment and the TX power of the beacon.

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

  • tx_power (int) – Transmit power in dBm. Typical beacon values are -59 dBm at 1 meter. Default: -59.

  • n (float) – Path loss exponent. 2.0 for free space, up to 4.0 in indoor environments. Default: 2.0.

Returns:

Estimated distance in meters.

Return type:

float

Example

Estimate distance from RSSI values:

distance = estimate_distance_from_rssi(-50)  # ~1 meter
distance = estimate_distance_from_rssi(-70)  # ~10 meters
ble2wled.colors.lerp(a: float, b: float, t: float) float[source]

Linear interpolation between two values.

Parameters:
  • a (float) – Start value.

  • b (float) – End value.

  • t (float) – Interpolation factor (0.0 to 1.0). 0.0 returns a, 1.0 returns b.

Returns:

Interpolated value between a and b.

Return type:

float

Example

Interpolate between colors:

mid_value = lerp(0, 255, 0.5)  # Returns 127.5
ble2wled.colors.gradient_color(distance: float, near: float = 0.5, far: float = 10.0) Tuple[float, float, float][source]

Map distance to RGB color gradient (yellow to red).

Maps distance to a color gradient from yellow (near) to red (far). Uses linear interpolation in the middle range. Returns values in 0-1 range.

Color Mapping:
  • distance < near: Yellow (1.0, 1.0, 0.0)

  • distance between near and far: Gradient yellow → red

  • distance > far: Red (1.0, 0.0, 0.0)

Parameters:
  • distance (float) – Distance in meters.

  • near (float) – Distance threshold for “near” color in meters. Default: 0.5 meters (yellow).

  • far (float) – Distance threshold for “far” color in meters. Default: 10.0 meters (red).

Returns:

RGB tuple with values in range 0.0-1.0.

Return type:

tuple

Example

Get color for different distances:

close_color = gradient_color(0.3)   # Yellow-ish
mid_color = gradient_color(5.0)    # Yellow-Red transition
far_color = gradient_color(15.0)   # Red
ble2wled.colors.ble_beacon_to_rgb(beacon_id: str, rssi: int, life: float) Tuple[int, int, int][source]

Convert beacon data to RGB color.

Applies distance-based gradient coloring with unique hue offset per beacon, and modulates brightness based on life value. Each beacon gets a consistent hue offset based on its ID hash, ensuring the same beacon is always the same base color.

Algorithm:
  1. Estimate distance from RSSI

  2. Map distance to base color (yellow to red gradient)

  3. Apply unique hue offset per beacon (0-8% range)

  4. Scale brightness by life value (0-1)

Parameters:
  • beacon_id (str) – Unique beacon identifier.

  • rssi (int) – Signal strength in dBm (typically -30 to -100).

  • life (float) – Beacon life value (0.0 to 1.0), modulates brightness. 1.0 = full brightness, 0.0 = invisible.

Returns:

RGB tuple with values 0-255.

Return type:

tuple

Example

Convert beacon to RGB color:

color = ble_beacon_to_rgb('beacon_1', rssi=-50, life=0.8)
r, g, b = color
print(f"Color: RGB({r}, {g}, {b})")

Overview

The colors module provides color calculations for LED visualization, including:

  • Distance-to-color mapping (position on LED strip → hue)

  • Signal strength mapping (RSSI → brightness)

  • HSV to RGB conversion

  • Color utilities for beacon visualization

Color Strategy

BLE2WLED uses a sophisticated color mapping strategy:

  1. Hue Assignment - Each beacon gets a unique hue based on its ID (hash-based)

  2. Distance Mapping - Distance from transmitter maps to saturation/value

  3. Signal Strength - RSSI maps to brightness

  4. Motion Trails - Recent positions brightest, fade out over time

Quick Example

from ble2wled.colors import (
    position_to_color_hue,
    rssi_to_brightness,
    beacon_id_to_hue,
    hsv_to_rgb
)

# Get hue for a position on the LED strip
position = 30  # Middle of 60-LED strip
hue = position_to_color_hue(position, led_count=60)
print(f"Position {position} → Hue {hue}")

# Get brightness from signal strength
rssi = -50
brightness = rssi_to_brightness(rssi)
print(f"RSSI {rssi} → Brightness {brightness}")

# Get unique color for each beacon
beacon_id = "device_123"
beacon_hue = beacon_id_to_hue(beacon_id)
print(f"Beacon {beacon_id} → Hue {beacon_hue}")

# Convert HSV to RGB
h, s, v = beacon_hue, 0.8, 1.0
r, g, b = hsv_to_rgb(h, s, v)
print(f"HSV({h}, {s}, {v}) → RGB({r}, {g}, {b})")

Functions

position_to_color_hue()

Map a position on the LED strip to a color hue.

Parameters:

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

  • led_count (int) - Total number of LEDs

Returns:

  • float - Hue value (0-360 degrees)

Example:

from ble2wled.colors import position_to_color_hue

# 60-LED strip
hue_start = position_to_color_hue(0, 60)      # Red (0°)
hue_middle = position_to_color_hue(30, 60)    # Cyan (180°)
hue_end = position_to_color_hue(59, 60)       # Purple (359°)

rssi_to_brightness()

Map signal strength (RSSI) to brightness.

Parameters:

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

Returns:

  • float - Brightness (0.0-1.0)

Behavior:

  • Very strong signal (-20 to -30): brightness 1.0 (full)

  • Strong signal (-30 to -50): brightness 0.8-1.0

  • Medium signal (-50 to -70): brightness 0.3-0.8

  • Weak signal (-70 to -100): brightness 0.0-0.3

Example:

from ble2wled.colors import rssi_to_brightness

rssi_strong = -40
rssi_weak = -80

brightness_strong = rssi_to_brightness(rssi_strong)  # ~0.9
brightness_weak = rssi_to_brightness(rssi_weak)      # ~0.2

beacon_id_to_hue()

Get a unique, consistent hue for each beacon based on its ID.

Parameters:

  • beacon_id (str) - Unique beacon identifier

Returns:

  • float - Hue value (0-360 degrees)

Behavior:

  • Same beacon ID always gets same hue

  • Different beacon IDs get different hues

  • Uses hash function for distribution

Example:

from ble2wled.colors import beacon_id_to_hue

beacon1_hue = beacon_id_to_hue("device_123")
beacon2_hue = beacon_id_to_hue("device_456")

# Same beacon, same hue (consistent)
assert beacon_id_to_hue("device_123") == beacon1_hue

# Different beacon, different hue
assert beacon1_hue != beacon2_hue

hsv_to_rgb()

Convert HSV color to RGB.

Parameters:

  • h (float) - Hue (0-360 degrees)

  • s (float) - Saturation (0.0-1.0)

  • v (float) - Value/Brightness (0.0-1.0)

Returns:

  • tuple - RGB values (r, g, b) each 0-255

Example:

from ble2wled.colors import hsv_to_rgb

# Red
r, g, b = hsv_to_rgb(0, 1.0, 1.0)
assert (r, g, b) == (255, 0, 0)

# Green
r, g, b = hsv_to_rgb(120, 1.0, 1.0)
assert (r, g, b) == (0, 255, 0)

# Blue
r, g, b = hsv_to_rgb(240, 1.0, 1.0)
assert (r, g, b) == (0, 0, 255)

# Gray (low saturation)
r, g, b = hsv_to_rgb(0, 0.0, 0.5)
assert (r, g, b) == (128, 128, 128)

Examples

Complete Color Calculation

from ble2wled.colors import (
    position_to_color_hue,
    rssi_to_brightness,
    beacon_id_to_hue,
    hsv_to_rgb
)

# For a beacon at position 30 with RSSI -50
beacon_id = "device_123"
position = 30
rssi = -50
led_count = 60

# Get base hue from beacon ID (unique per beacon)
hue = beacon_id_to_hue(beacon_id)

# Get saturation from position (distance-based)
# Closer beacons are more saturated
position_hue = position_to_color_hue(position, led_count)
saturation = 1.0 - abs(hue - position_hue) / 180.0

# Get brightness from signal strength
brightness = rssi_to_brightness(rssi)

# Convert HSV to RGB for LED
r, g, b = hsv_to_rgb(hue, saturation, brightness)

print(f"Beacon {beacon_id} at LED {position}")
print(f"  HSV: ({hue:.1f}°, {saturation:.2f}, {brightness:.2f})")
print(f"  RGB: ({r}, {g}, {b})")

Multiple Beacons

from ble2wled.colors import beacon_id_to_hue, hsv_to_rgb

beacons = {
    "device_1": {"position": 10, "rssi": -45},
    "device_2": {"position": 30, "rssi": -60},
    "device_3": {"position": 50, "rssi": -75},
}

for beacon_id, data in beacons.items():
    hue = beacon_id_to_hue(beacon_id)
    # ... calculate saturation, brightness ...
    r, g, b = hsv_to_rgb(hue, 0.8, brightness)
    print(f"{beacon_id}: RGB({r}, {g}, {b})")

Gradient Visualization

from ble2wled.colors import position_to_color_hue, hsv_to_rgb

# Show color gradient across LED strip
led_count = 60
for position in range(0, led_count, 10):
    hue = position_to_color_hue(position, led_count)
    r, g, b = hsv_to_rgb(hue, 1.0, 1.0)
    print(f"LED {position:2d}: RGB({r:3d}, {g:3d}, {b:3d})")

Signal Strength Visualization

from ble2wled.colors import rssi_to_brightness, hsv_to_rgb

# Show brightness at different signal strengths
rssi_values = [-30, -50, -70, -90]
hue = 120  # Green

for rssi in rssi_values:
    brightness = rssi_to_brightness(rssi)
    r, g, b = hsv_to_rgb(hue, 1.0, brightness)
    print(f"RSSI {rssi:3d} dBm: Brightness {brightness:.2f} → RGB({r}, {g}, {b})")

See Also