Add static color support, HAOS light entity, and real-time profile updates
- Add static_color capability to WLED and serial providers with native set_color() dispatch (WLED uses JSON API, serial uses idle client) - Encapsulate device-specific logic in providers instead of device_type checks in ProcessorManager and API routes - Add HAOS light entity for devices with brightness_control + static_color (Adalight/AmbiLED get light entity, WLED keeps number entity) - Fix serial device brightness and turn-off: pass software_brightness through provider chain, clear device on color=null, re-send static color after brightness change - Add global events WebSocket (events-ws.js) replacing per-tab WS, enabling real-time profile state updates on both dashboard and profiles tabs - Fix profile activation: mark active when all targets already running, add asyncio.Lock to prevent concurrent evaluation races, skip process enumeration when no profile has conditions, trigger immediate evaluation on enable/create/update for instant target startup - Add reliable server restart script (restart.ps1) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -119,18 +119,28 @@ class SerialDeviceProvider(LEDDeviceProvider):
|
||||
async def set_color(self, url: str, color: Tuple[int, int, int], **kwargs) -> None:
|
||||
"""Send a solid color frame to the device.
|
||||
|
||||
Requires kwargs: led_count (int), baud_rate (int | None).
|
||||
Accepts optional kwargs:
|
||||
client: An already-connected LEDClient (e.g. cached idle client).
|
||||
brightness (int): Software brightness 0-255 (default 255).
|
||||
led_count (int), baud_rate (int | None).
|
||||
"""
|
||||
led_count = kwargs.get("led_count", 0)
|
||||
baud_rate = kwargs.get("baud_rate")
|
||||
if led_count <= 0:
|
||||
raise ValueError(f"led_count is required to send color frame to {self.device_type} device")
|
||||
|
||||
client = self.create_client(url, led_count=led_count, baud_rate=baud_rate)
|
||||
try:
|
||||
await client.connect()
|
||||
frame = np.full((led_count, 3), color, dtype=np.uint8)
|
||||
await client.send_pixels(frame, brightness=255)
|
||||
logger.info(f"{self.device_type} set_color: sent solid {color} to {url}")
|
||||
finally:
|
||||
await client.close()
|
||||
brightness = kwargs.get("brightness", 255)
|
||||
frame = np.full((led_count, 3), color, dtype=np.uint8)
|
||||
|
||||
existing_client = kwargs.get("client")
|
||||
if existing_client:
|
||||
await existing_client.send_pixels(frame, brightness=brightness)
|
||||
else:
|
||||
baud_rate = kwargs.get("baud_rate")
|
||||
client = self.create_client(url, led_count=led_count, baud_rate=baud_rate)
|
||||
try:
|
||||
await client.connect()
|
||||
await client.send_pixels(frame, brightness=brightness)
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
logger.info(f"{self.device_type} set_color: sent solid {color} to {url}")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""WLED device provider — consolidates all WLED-specific dispatch logic."""
|
||||
|
||||
import asyncio
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import httpx
|
||||
from zeroconf import ServiceStateChange
|
||||
@@ -30,7 +30,7 @@ class WLEDDeviceProvider(LEDDeviceProvider):
|
||||
|
||||
@property
|
||||
def capabilities(self) -> set:
|
||||
return {"brightness_control", "power_control", "standby_required"}
|
||||
return {"brightness_control", "power_control", "standby_required", "static_color"}
|
||||
|
||||
def create_client(self, url: str, **kwargs) -> LEDClient:
|
||||
from wled_controller.core.devices.wled_client import WLEDClient
|
||||
@@ -186,3 +186,16 @@ class WLEDDeviceProvider(LEDDeviceProvider):
|
||||
json={"on": on},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
async def set_color(self, url: str, color: Tuple[int, int, int], **kwargs) -> None:
|
||||
"""Set WLED to a solid color using the native segment color API."""
|
||||
url = url.rstrip("/")
|
||||
async with httpx.AsyncClient(timeout=5.0) as http_client:
|
||||
resp = await http_client.post(
|
||||
f"{url}/json/state",
|
||||
json={
|
||||
"on": True,
|
||||
"seg": [{"col": [[color[0], color[1], color[2]]], "fx": 0}],
|
||||
},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
Reference in New Issue
Block a user