|
|
|
|
@@ -191,7 +191,7 @@ class ProcessorManager:
|
|
|
|
|
|
|
|
|
|
# Check if any processor is using this device
|
|
|
|
|
for proc in self._processors.values():
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.device_id == device_id:
|
|
|
|
|
if proc.device_id == device_id:
|
|
|
|
|
raise RuntimeError(
|
|
|
|
|
f"Cannot remove device {device_id}: target {proc.target_id} is using it"
|
|
|
|
|
)
|
|
|
|
|
@@ -217,7 +217,7 @@ class ProcessorManager:
|
|
|
|
|
def update_calibration(self, device_id: str, calibration: CalibrationConfig):
|
|
|
|
|
"""Update calibration for a device.
|
|
|
|
|
|
|
|
|
|
Also propagates to any WledTargetProcessor using this device.
|
|
|
|
|
Also propagates to any target processor using this device.
|
|
|
|
|
"""
|
|
|
|
|
if device_id not in self._devices:
|
|
|
|
|
raise ValueError(f"Device {device_id} not found")
|
|
|
|
|
@@ -233,9 +233,9 @@ class ProcessorManager:
|
|
|
|
|
|
|
|
|
|
ds.calibration = calibration
|
|
|
|
|
|
|
|
|
|
# Propagate to active WLED processors
|
|
|
|
|
# Propagate to active processors using this device
|
|
|
|
|
for proc in self._processors.values():
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.device_id == device_id:
|
|
|
|
|
if proc.device_id == device_id:
|
|
|
|
|
proc.update_calibration(calibration)
|
|
|
|
|
|
|
|
|
|
logger.info(f"Updated calibration for device {device_id}")
|
|
|
|
|
@@ -358,10 +358,8 @@ class ProcessorManager:
|
|
|
|
|
proc.update_source(picture_source_id)
|
|
|
|
|
|
|
|
|
|
def update_target_device(self, target_id: str, device_id: str):
|
|
|
|
|
"""Update the device for a WLED target."""
|
|
|
|
|
"""Update the device for a target."""
|
|
|
|
|
proc = self._get_processor(target_id)
|
|
|
|
|
if not isinstance(proc, WledTargetProcessor):
|
|
|
|
|
raise ValueError(f"Target {target_id} is not a WLED target")
|
|
|
|
|
if device_id not in self._devices:
|
|
|
|
|
raise ValueError(f"Device {device_id} not registered")
|
|
|
|
|
proc.update_device(device_id)
|
|
|
|
|
@@ -370,12 +368,11 @@ class ProcessorManager:
|
|
|
|
|
"""Start processing for a target (any type)."""
|
|
|
|
|
proc = self._get_processor(target_id)
|
|
|
|
|
|
|
|
|
|
# Enforce one-target-per-device for WLED targets
|
|
|
|
|
if isinstance(proc, WledTargetProcessor):
|
|
|
|
|
# Enforce one-target-per-device for device-aware targets
|
|
|
|
|
if proc.device_id is not None:
|
|
|
|
|
for other_id, other in self._processors.items():
|
|
|
|
|
if (
|
|
|
|
|
other_id != target_id
|
|
|
|
|
and isinstance(other, WledTargetProcessor)
|
|
|
|
|
and other.device_id == proc.device_id
|
|
|
|
|
and other.is_running
|
|
|
|
|
):
|
|
|
|
|
@@ -383,8 +380,7 @@ class ProcessorManager:
|
|
|
|
|
f"Device {proc.device_id} is already being processed by target {other_id}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Close cached idle client — processor creates its own connection
|
|
|
|
|
if isinstance(proc, WledTargetProcessor):
|
|
|
|
|
# Close cached idle client — processor creates its own connection
|
|
|
|
|
await self._close_idle_client(proc.device_id)
|
|
|
|
|
|
|
|
|
|
await proc.start()
|
|
|
|
|
@@ -400,7 +396,7 @@ class ProcessorManager:
|
|
|
|
|
await proc.stop()
|
|
|
|
|
|
|
|
|
|
# Auto-shutdown device if applicable
|
|
|
|
|
if isinstance(proc, WledTargetProcessor):
|
|
|
|
|
if proc.device_id is not None:
|
|
|
|
|
await self._restore_device_idle_state(proc.device_id)
|
|
|
|
|
|
|
|
|
|
def get_target_state(self, target_id: str) -> dict:
|
|
|
|
|
@@ -411,8 +407,8 @@ class ProcessorManager:
|
|
|
|
|
proc = self._get_processor(target_id)
|
|
|
|
|
state = proc.get_state()
|
|
|
|
|
|
|
|
|
|
# Merge device health for WLED targets
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.device_id in self._devices:
|
|
|
|
|
# Merge device health for device-aware targets
|
|
|
|
|
if proc.device_id is not None and proc.device_id in self._devices:
|
|
|
|
|
h = self._devices[proc.device_id].health
|
|
|
|
|
state.update({
|
|
|
|
|
"device_online": h.online,
|
|
|
|
|
@@ -440,14 +436,14 @@ class ProcessorManager:
|
|
|
|
|
def is_device_processing(self, device_id: str) -> bool:
|
|
|
|
|
"""Check if any target is processing for a device."""
|
|
|
|
|
for proc in self._processors.values():
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.device_id == device_id and proc.is_running:
|
|
|
|
|
if proc.device_id == device_id and proc.is_running:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def get_processing_target_for_device(self, device_id: str) -> Optional[str]:
|
|
|
|
|
"""Get the target_id that is currently processing for a device."""
|
|
|
|
|
for proc in self._processors.values():
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.device_id == device_id and proc.is_running:
|
|
|
|
|
if proc.device_id == device_id and proc.is_running:
|
|
|
|
|
return proc.target_id
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
@@ -468,7 +464,7 @@ class ProcessorManager:
|
|
|
|
|
await proc.start_overlay(target_name)
|
|
|
|
|
|
|
|
|
|
# Light up device LEDs with edge test colors while overlay is visible
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and not proc.is_running:
|
|
|
|
|
if proc.device_id is not None and not proc.is_running:
|
|
|
|
|
try:
|
|
|
|
|
await self.set_test_mode(proc.device_id, self._OVERLAY_EDGE_COLORS)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
@@ -479,7 +475,7 @@ class ProcessorManager:
|
|
|
|
|
await proc.stop_overlay()
|
|
|
|
|
|
|
|
|
|
# Clear device LEDs when overlay is dismissed
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and not proc.is_running:
|
|
|
|
|
if proc.device_id is not None and not proc.is_running:
|
|
|
|
|
try:
|
|
|
|
|
await self.set_test_mode(proc.device_id, {})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
@@ -596,9 +592,9 @@ class ProcessorManager:
|
|
|
|
|
logger.error(f"Failed to clear pixels for {device_id}: {e}")
|
|
|
|
|
|
|
|
|
|
def _find_active_led_client(self, device_id: str):
|
|
|
|
|
"""Find an active LED client for a device (from a running WLED processor)."""
|
|
|
|
|
"""Find an active LED client for a device (from a running processor)."""
|
|
|
|
|
for proc in self._processors.values():
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.device_id == device_id and proc.is_running and proc.led_client:
|
|
|
|
|
if proc.device_id == device_id and proc.is_running and proc.led_client:
|
|
|
|
|
return proc.led_client
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
@@ -607,14 +603,14 @@ class ProcessorManager:
|
|
|
|
|
def is_display_locked(self, display_index: int) -> bool:
|
|
|
|
|
"""Check if a display is currently being captured by any target."""
|
|
|
|
|
for proc in self._processors.values():
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.is_running and proc.settings.display_index == display_index:
|
|
|
|
|
if proc.is_running and proc.get_display_index() == display_index:
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def get_display_lock_info(self, display_index: int) -> Optional[str]:
|
|
|
|
|
"""Get the device ID that is currently capturing from a display."""
|
|
|
|
|
for proc in self._processors.values():
|
|
|
|
|
if isinstance(proc, WledTargetProcessor) and proc.is_running and proc.settings.display_index == display_index:
|
|
|
|
|
if proc.is_running and proc.get_display_index() == display_index:
|
|
|
|
|
return proc.device_id
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
@@ -749,9 +745,8 @@ class ProcessorManager:
|
|
|
|
|
def _device_is_processing(self, device_id: str) -> bool:
|
|
|
|
|
"""Check if any target is actively streaming to this device."""
|
|
|
|
|
return any(
|
|
|
|
|
isinstance(p, WledTargetProcessor) and p.is_running
|
|
|
|
|
p.device_id == device_id and p.is_running
|
|
|
|
|
for p in self._processors.values()
|
|
|
|
|
if isinstance(p, WledTargetProcessor) and p.device_id == device_id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async def _health_check_loop(self, device_id: str):
|
|
|
|
|
|