Overlay: fix 404, crash on repeat, missing edge test colors, device reset on stop
- Target overlay works without active processing: route pre-loads calibration and display info from the CSS store, passes to processor as fallback - Fix server crash on repeated overlay: replace per-window tk.Tk() with single persistent hidden root; each overlay is a Toplevel child dispatched via root.after() — eliminates Tcl interpreter crashes on Windows - Fix edge test colors not lighting up: always call set_test_mode regardless of processing state (was guarded by 'not proc.is_running'); pass calibration so _send_test_pixels knows which LEDs map to which edges - Fix device reset on overlay stop: keep idle serial client cached after clearing test mode; start_processing() already closes it before connecting Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -435,16 +435,18 @@ class ProcessorManager:
|
||||
"left": [255, 255, 0],
|
||||
}
|
||||
|
||||
async def start_overlay(self, target_id: str, target_name: str = None) -> None:
|
||||
async def start_overlay(self, target_id: str, target_name: str = None, calibration=None, display_info=None) -> None:
|
||||
proc = self._get_processor(target_id)
|
||||
if not proc.supports_overlay():
|
||||
raise ValueError(f"Target {target_id} does not support overlays")
|
||||
await proc.start_overlay(target_name)
|
||||
await proc.start_overlay(target_name, calibration=calibration, display_info=display_info)
|
||||
|
||||
# Light up device LEDs with edge test colors while overlay is visible
|
||||
if proc.device_id is not None and not proc.is_running:
|
||||
# Light up device LEDs with edge test colors while overlay is visible.
|
||||
# Always do this regardless of whether processing is running — the processing
|
||||
# loop pauses itself when test_mode_active is set.
|
||||
if proc.device_id is not None:
|
||||
try:
|
||||
await self.set_test_mode(proc.device_id, self._OVERLAY_EDGE_COLORS)
|
||||
await self.set_test_mode(proc.device_id, self._OVERLAY_EDGE_COLORS, calibration)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to set edge test for overlay on {proc.device_id}: {e}")
|
||||
|
||||
@@ -452,8 +454,8 @@ class ProcessorManager:
|
||||
proc = self._get_processor(target_id)
|
||||
await proc.stop_overlay()
|
||||
|
||||
# Clear device LEDs when overlay is dismissed
|
||||
if proc.device_id is not None and not proc.is_running:
|
||||
# Clear device LEDs when overlay is dismissed.
|
||||
if proc.device_id is not None:
|
||||
try:
|
||||
await self.set_test_mode(proc.device_id, {})
|
||||
except Exception as e:
|
||||
@@ -462,6 +464,23 @@ class ProcessorManager:
|
||||
def is_overlay_active(self, target_id: str) -> bool:
|
||||
return self._get_processor(target_id).is_overlay_active()
|
||||
|
||||
# ===== CSS OVERLAY (direct, no target processor required) =====
|
||||
|
||||
async def start_css_overlay(self, css_id: str, display_info, calibration, css_name: str = None) -> None:
|
||||
await asyncio.to_thread(
|
||||
self._overlay_manager.start_overlay,
|
||||
css_id, display_info, calibration, css_name,
|
||||
)
|
||||
|
||||
async def stop_css_overlay(self, css_id: str) -> None:
|
||||
await asyncio.to_thread(
|
||||
self._overlay_manager.stop_overlay,
|
||||
css_id,
|
||||
)
|
||||
|
||||
def is_css_overlay_active(self, css_id: str) -> bool:
|
||||
return self._overlay_manager.is_running(css_id)
|
||||
|
||||
# ===== WEBSOCKET (delegates to processor) =====
|
||||
|
||||
def add_kc_ws_client(self, target_id: str, ws) -> None:
|
||||
@@ -508,7 +527,8 @@ class ProcessorManager:
|
||||
ds.test_mode_edges = {}
|
||||
ds.test_calibration = None
|
||||
await self._send_clear_pixels(device_id)
|
||||
await self._close_idle_client(device_id)
|
||||
# Keep idle client open — serial reconnect causes device reset.
|
||||
# start_processing() closes it before connecting its own client.
|
||||
|
||||
async def _get_idle_client(self, device_id: str):
|
||||
"""Get or create a cached idle LED client for a device.
|
||||
|
||||
@@ -268,30 +268,34 @@ class WledTargetProcessor(TargetProcessor):
|
||||
def supports_overlay(self) -> bool:
|
||||
return True
|
||||
|
||||
async def start_overlay(self, target_name: Optional[str] = None) -> None:
|
||||
async def start_overlay(self, target_name: Optional[str] = None, calibration=None, display_info=None) -> None:
|
||||
if self._overlay_active:
|
||||
raise RuntimeError(f"Overlay already active for {self._target_id}")
|
||||
|
||||
# Calibration comes from the active color strip stream
|
||||
if self._color_strip_stream is None:
|
||||
raise ValueError(
|
||||
f"Cannot start overlay for {self._target_id}: no color strip stream active. "
|
||||
f"Start processing first."
|
||||
)
|
||||
if calibration is None or display_info is None:
|
||||
# Calibration comes from the active color strip stream
|
||||
if self._color_strip_stream is None:
|
||||
raise ValueError(
|
||||
f"Cannot start overlay for {self._target_id}: no color strip stream active "
|
||||
f"and no calibration provided."
|
||||
)
|
||||
|
||||
calibration = self._color_strip_stream.calibration
|
||||
display_index = self._resolved_display_index
|
||||
if display_index is None:
|
||||
display_index = self._color_strip_stream.display_index
|
||||
if calibration is None:
|
||||
calibration = self._color_strip_stream.calibration
|
||||
|
||||
if display_index is None or display_index < 0:
|
||||
raise ValueError(f"Invalid display index {display_index} for overlay")
|
||||
if display_info is None:
|
||||
display_index = self._resolved_display_index
|
||||
if display_index is None:
|
||||
display_index = self._color_strip_stream.display_index
|
||||
|
||||
displays = get_available_displays()
|
||||
if display_index >= len(displays):
|
||||
raise ValueError(f"Invalid display index {display_index}")
|
||||
if display_index is None or display_index < 0:
|
||||
raise ValueError(f"Invalid display index {display_index} for overlay")
|
||||
|
||||
display_info = displays[display_index]
|
||||
displays = get_available_displays()
|
||||
if display_index >= len(displays):
|
||||
raise ValueError(f"Invalid display index {display_index}")
|
||||
|
||||
display_info = displays[display_index]
|
||||
|
||||
await asyncio.to_thread(
|
||||
self._ctx.overlay_manager.start_overlay,
|
||||
|
||||
Reference in New Issue
Block a user