Rewrite HAOS integration: target-centric architecture with KC color sensors

- Rewrite integration to target-centric model: each picture target becomes
  a HA device under a server hub with switch, FPS, and status sensors
- Replace KC light entities with color sensors (hex state + RGB attributes)
  for better automation support via WebSocket real-time updates
- Add WebSocket manager for Key Colors color streaming
- Add KC per-stage timing metrics (calc_colors, broadcast) with rolling avg
- Fix KC timing fields missing from API by adding them to Pydantic schema
- Make start/stop processing idempotent to prevent intermittent 404 errors
- Add HAOS localization support (en, ru) using translation_key system
- Rename integration from "WLED Screen Controller" to "LED Screen Controller"
- Remove obsolete select.py (display select) and README.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 13:01:40 +03:00
parent e92fe4eb0a
commit 67da014684
19 changed files with 772 additions and 746 deletions

View File

@@ -1,117 +0,0 @@
"""Select platform for WLED Screen Controller."""
from __future__ import annotations
import logging
from typing import Any
from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import WLEDScreenControllerCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up WLED Screen Controller select entities."""
data = hass.data[DOMAIN][entry.entry_id]
coordinator: WLEDScreenControllerCoordinator = data["coordinator"]
entities = []
if coordinator.data and "devices" in coordinator.data:
for device_id, device_data in coordinator.data["devices"].items():
device_info = device_data["info"]
entities.append(
WLEDScreenControllerDisplaySelect(
coordinator, device_id, device_info, entry.entry_id
)
)
async_add_entities(entities)
class WLEDScreenControllerDisplaySelect(CoordinatorEntity, SelectEntity):
"""Display selection for WLED Screen Controller."""
_attr_has_entity_name = True
_attr_icon = "mdi:monitor-multiple"
def __init__(
self,
coordinator: WLEDScreenControllerCoordinator,
device_id: str,
device_info: dict[str, Any],
entry_id: str,
) -> None:
"""Initialize the select."""
super().__init__(coordinator)
self._device_id = device_id
self._device_info = device_info
self._entry_id = entry_id
self._attr_unique_id = f"{device_id}_display"
self._attr_name = "Display"
@property
def device_info(self) -> dict[str, Any]:
"""Return device information."""
return {
"identifiers": {(DOMAIN, self._device_id)},
}
@property
def options(self) -> list[str]:
"""Return available display options."""
if not self.coordinator.data or "displays" not in self.coordinator.data:
return ["Display 0"]
displays = self.coordinator.data["displays"]
return [f"Display {d['index']}" for d in displays]
@property
def current_option(self) -> str | None:
"""Return current display."""
if not self.coordinator.data:
return None
device_data = self.coordinator.data["devices"].get(self._device_id)
if not device_data or not device_data.get("state"):
return None
display_index = device_data["state"].get("display_index", 0)
return f"Display {display_index}"
async def async_select_option(self, option: str) -> None:
"""Change the selected display."""
try:
# Extract display index from option (e.g., "Display 1" -> 1)
display_index = int(option.split()[-1])
# Get current settings
device_data = self.coordinator.data["devices"].get(self._device_id)
if not device_data:
return
info = device_data["info"]
settings = info.get("settings", {})
# Update settings with new display index
updated_settings = {
"display_index": display_index,
"fps": settings.get("fps", 30),
"border_width": settings.get("border_width", 10),
}
await self.coordinator.update_settings(self._device_id, updated_settings)
except Exception as err:
_LOGGER.error("Failed to update display: %s", err)
raise