Fix serial send bloat when sharing CSS stream with higher-LED device

When two targets share the same color strip source, configure() resizes
the stream to the last caller's LED count. If a WLED device (934 LEDs)
starts after a serial device (fewer LEDs), the serial target sends the
full 934-LED frame over serial, massively inflating send time.

Add _fit_to_device() to truncate/tile colors to the target's actual
device LED count before sending, so each consumer only transmits what
its device needs regardless of the shared stream's current size.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 03:12:45 +03:00
parent 6d33686b79
commit 1204676c30

View File

@@ -362,6 +362,22 @@ class WledTargetProcessor(TargetProcessor):
return (colors.astype(np.uint16) * device_info.software_brightness >> 8).astype(np.uint8) return (colors.astype(np.uint16) * device_info.software_brightness >> 8).astype(np.uint8)
return colors return colors
@staticmethod
def _fit_to_device(colors: np.ndarray, device_led_count: int) -> np.ndarray:
"""Truncate or repeat colors to match the target device LED count.
Shared streams may be sized to a different consumer's LED count.
This ensures each target only sends what its device actually needs.
"""
n = len(colors)
if n == device_led_count or device_led_count <= 0:
return colors
if n > device_led_count:
return colors[:device_led_count]
# Tile to fill (rare — usually streams are >= device count)
reps = (device_led_count + n - 1) // n
return np.tile(colors, (reps, 1))[:device_led_count]
async def _processing_loop(self) -> None: async def _processing_loop(self) -> None:
"""Main processing loop — poll ColorStripStream → apply brightness → send.""" """Main processing loop — poll ColorStripStream → apply brightness → send."""
stream = self._color_strip_stream stream = self._color_strip_stream
@@ -430,7 +446,10 @@ class WledTargetProcessor(TargetProcessor):
if prev_colors is not None and (loop_start - last_send_time) >= standby_interval: if prev_colors is not None and (loop_start - last_send_time) >= standby_interval:
if not self._is_running or self._led_client is None: if not self._is_running or self._led_client is None:
break break
send_colors = self._apply_brightness(prev_colors, device_info) kc = prev_colors
if device_info and device_info.led_count > 0:
kc = self._fit_to_device(kc, device_info.led_count)
send_colors = self._apply_brightness(kc, device_info)
if self._led_client.supports_fast_send: if self._led_client.supports_fast_send:
self._led_client.send_pixels_fast(send_colors) self._led_client.send_pixels_fast(send_colors)
else: else:
@@ -448,6 +467,10 @@ class WledTargetProcessor(TargetProcessor):
prev_colors = colors prev_colors = colors
# Fit to this device's LED count (stream may be shared)
if device_info and device_info.led_count > 0:
colors = self._fit_to_device(colors, device_info.led_count)
# Apply device software brightness # Apply device software brightness
send_colors = self._apply_brightness(colors, device_info) send_colors = self._apply_brightness(colors, device_info)