Add DDP protocol support, fix event loop blocking, and add LED offset calibration
Some checks failed
Validate / validate (push) Failing after 8s

- Add DDP client for LED strips >500 LEDs (UDP port 4048), with automatic
  fallback from HTTP JSON API when LED count exceeds limit
- Wrap blocking operations (screen capture, image processing) in
  asyncio.to_thread() to prevent event loop starvation
- Turn on WLED device and enable live mode when starting DDP streaming
- Add LED strip offset field to calibration (rotates color array to match
  physical LED position vs start corner)
- Add server management scripts (start, stop, restart, background start)
- Fix WebUI auth error handling and auto-refresh loop
- Add development API key to default config
- Add i18n translations for offset field (en/ru)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 12:44:06 +03:00
parent ec3c40d59c
commit 579821a69b
15 changed files with 504 additions and 48 deletions

View File

@@ -32,6 +32,7 @@ class CalibrationConfig:
layout: Literal["clockwise", "counterclockwise"]
start_position: Literal["top_left", "top_right", "bottom_left", "bottom_right"]
segments: List[CalibrationSegment]
offset: int = 0 # Physical LED offset from start corner (number of LEDs from LED 0 to start corner)
def validate(self) -> bool:
"""Validate calibration configuration.
@@ -181,7 +182,14 @@ class PixelMapper:
color = self._calc_color(pixel_segment)
led_colors[led_idx] = color
logger.debug(f"Mapped border pixels to {total_leds} LED colors")
# Apply physical LED offset by rotating the array
# Offset = number of LEDs from LED 0 to the start corner
# Physical LED[i] should get calibration color[(i - offset) % total]
offset = self.calibration.offset % total_leds if total_leds > 0 else 0
if offset > 0:
led_colors = led_colors[total_leds - offset:] + led_colors[:total_leds - offset]
logger.debug(f"Mapped border pixels to {total_leds} LED colors (offset={offset})")
return led_colors
def test_calibration(self, edge: str, color: Tuple[int, int, int]) -> List[Tuple[int, int, int]]:
@@ -309,6 +317,7 @@ def calibration_from_dict(data: dict) -> CalibrationConfig:
layout=data["layout"],
start_position=data["start_position"],
segments=segments,
offset=data.get("offset", 0),
)
config.validate()
@@ -332,6 +341,7 @@ def calibration_to_dict(config: CalibrationConfig) -> dict:
return {
"layout": config.layout,
"start_position": config.start_position,
"offset": config.offset,
"segments": [
{
"edge": seg.edge,