Fix device provider kwargs, camera crash guard, target API, and graph color picker

- Refactor all device providers to use explicit kwargs.get() instead of
  fragile pop-then-passthrough (fixes WLED target start failing with
  unexpected dmx_protocol kwarg)
- Add process-wide camera index registry to prevent concurrent opens of
  the same physical camera which crashes the DSHOW backend on Windows
- Fix OutputTargetResponse validation error when brightness_value_source_id
  is None (coerce to empty string in response and from_dict)
- Replace native <input type="color"> in graph editor with the custom
  color picker popover used throughout the app, positioned via
  getScreenCTM() inside an absolute overlay on .graph-container

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 21:36:26 +03:00
parent 8960e7dca3
commit 153972fcd5
11 changed files with 135 additions and 70 deletions

View File

@@ -13,10 +13,8 @@ class AdalightDeviceProvider(SerialDeviceProvider):
def create_client(self, url: str, **kwargs) -> LEDClient:
from wled_controller.core.devices.adalight_client import AdalightClient
led_count = kwargs.pop("led_count", 0)
baud_rate = kwargs.pop("baud_rate", None)
kwargs.pop("use_ddp", None) # Not applicable for serial
kwargs.pop("send_latency_ms", None)
kwargs.pop("rgbw", None)
return AdalightClient(url, led_count=led_count, baud_rate=baud_rate)
return AdalightClient(
url,
led_count=kwargs.get("led_count", 0),
baud_rate=kwargs.get("baud_rate"),
)

View File

@@ -13,10 +13,8 @@ class AmbiLEDDeviceProvider(SerialDeviceProvider):
def create_client(self, url: str, **kwargs) -> LEDClient:
from wled_controller.core.devices.ambiled_client import AmbiLEDClient
led_count = kwargs.pop("led_count", 0)
baud_rate = kwargs.pop("baud_rate", None)
kwargs.pop("use_ddp", None)
kwargs.pop("send_latency_ms", None)
kwargs.pop("rgbw", None)
return AmbiLEDClient(url, led_count=led_count, baud_rate=baud_rate)
return AmbiLEDClient(
url,
led_count=kwargs.get("led_count", 0),
baud_rate=kwargs.get("baud_rate"),
)

View File

@@ -24,8 +24,11 @@ class MockDeviceProvider(LEDDeviceProvider):
return {"manual_led_count", "power_control", "brightness_control"}
def create_client(self, url: str, **kwargs) -> LEDClient:
kwargs.pop("use_ddp", None)
return MockClient(url, **kwargs)
return MockClient(
url,
led_count=kwargs.get("led_count", 0),
send_latency_ms=kwargs.get("send_latency_ms", 0),
)
async def check_health(self, url: str, http_client, prev_health=None) -> DeviceHealth:
return DeviceHealth(online=True, latency_ms=0.0, last_checked=datetime.now(timezone.utc))

View File

@@ -31,7 +31,10 @@ class MQTTDeviceProvider(LEDDeviceProvider):
return {"manual_led_count"}
def create_client(self, url: str, **kwargs) -> LEDClient:
return MQTTLEDClient(url, **kwargs)
return MQTTLEDClient(
url,
led_count=kwargs.get("led_count", 0),
)
async def check_health(
self, url: str, http_client, prev_health=None,

View File

@@ -30,12 +30,10 @@ class OpenRGBDeviceProvider(LEDDeviceProvider):
return {"health_check", "auto_restore", "static_color"}
def create_client(self, url: str, **kwargs) -> LEDClient:
zone_mode = kwargs.pop("zone_mode", "combined")
kwargs.pop("led_count", None)
kwargs.pop("baud_rate", None)
kwargs.pop("send_latency_ms", None)
kwargs.pop("rgbw", None)
return OpenRGBLEDClient(url, zone_mode=zone_mode, **kwargs)
return OpenRGBLEDClient(
url,
zone_mode=kwargs.get("zone_mode", "combined"),
)
async def check_health(self, url: str, http_client, prev_health=None) -> DeviceHealth:
return await OpenRGBLEDClient.check_health(url, http_client, prev_health)

View File

@@ -53,12 +53,10 @@ class WLEDDeviceProvider(LEDDeviceProvider):
def create_client(self, url: str, **kwargs) -> LEDClient:
from wled_controller.core.devices.wled_client import WLEDClient
kwargs.pop("led_count", None)
kwargs.pop("baud_rate", None)
kwargs.pop("send_latency_ms", None)
kwargs.pop("rgbw", None)
kwargs.pop("zone_mode", None)
return WLEDClient(url, **kwargs)
return WLEDClient(
url,
use_ddp=kwargs.get("use_ddp", False),
)
async def check_health(self, url: str, http_client, prev_health=None) -> DeviceHealth:
from wled_controller.core.devices.wled_client import WLEDClient

View File

@@ -27,7 +27,10 @@ class WSDeviceProvider(LEDDeviceProvider):
return {"manual_led_count"}
def create_client(self, url: str, **kwargs) -> LEDClient:
return WSLEDClient(url, **kwargs)
return WSLEDClient(
url,
led_count=kwargs.get("led_count", 0),
)
async def check_health(
self, url: str, http_client, prev_health=None,