diff --git a/custom_components/wled_screen_controller/__init__.py b/custom_components/wled_screen_controller/__init__.py index d1f243c..db811d8 100644 --- a/custom_components/wled_screen_controller/__init__.py +++ b/custom_components/wled_screen_controller/__init__.py @@ -29,6 +29,7 @@ _LOGGER = logging.getLogger(__name__) PLATFORMS: list[Platform] = [ Platform.SWITCH, Platform.SENSOR, + Platform.NUMBER, ] diff --git a/custom_components/wled_screen_controller/coordinator.py b/custom_components/wled_screen_controller/coordinator.py index 38c277a..d8ec925 100644 --- a/custom_components/wled_screen_controller/coordinator.py +++ b/custom_components/wled_screen_controller/coordinator.py @@ -107,8 +107,12 @@ class WLEDScreenControllerCoordinator(DataUpdateCoordinator): target_id, data = r targets_data[target_id] = data + # Fetch devices with capabilities and brightness + devices_data = await self._fetch_devices() + return { "targets": targets_data, + "devices": devices_data, "server_version": self.server_version, } @@ -184,6 +188,64 @@ class WLEDScreenControllerCoordinator(DataUpdateCoordinator): ) return [] + async def _fetch_devices(self) -> dict[str, dict[str, Any]]: + """Fetch all devices with capabilities and brightness.""" + try: + async with self.session.get( + f"{self.server_url}/api/v1/devices", + headers=self._auth_headers, + timeout=aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT), + ) as resp: + resp.raise_for_status() + data = await resp.json() + devices = data.get("devices", []) + except Exception as err: + _LOGGER.warning("Failed to fetch devices: %s", err) + return {} + + devices_data: dict[str, dict[str, Any]] = {} + + for device in devices: + device_id = device["id"] + entry: dict[str, Any] = {"info": device, "brightness": None} + + if "brightness_control" in (device.get("capabilities") or []): + try: + async with self.session.get( + f"{self.server_url}/api/v1/devices/{device_id}/brightness", + headers=self._auth_headers, + timeout=aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT), + ) as resp: + if resp.status == 200: + bri_data = await resp.json() + entry["brightness"] = bri_data.get("brightness") + except Exception as err: + _LOGGER.warning( + "Failed to fetch brightness for device %s: %s", + device_id, err, + ) + + devices_data[device_id] = entry + + return devices_data + + async def set_brightness(self, device_id: str, brightness: int) -> None: + """Set brightness for a device.""" + async with self.session.put( + f"{self.server_url}/api/v1/devices/{device_id}/brightness", + headers={**self._auth_headers, "Content-Type": "application/json"}, + json={"brightness": brightness}, + timeout=aiohttp.ClientTimeout(total=DEFAULT_TIMEOUT), + ) as resp: + if resp.status != 200: + body = await resp.text() + _LOGGER.error( + "Failed to set brightness for device %s: %s %s", + device_id, resp.status, body, + ) + resp.raise_for_status() + await self.async_request_refresh() + async def start_processing(self, target_id: str) -> None: """Start processing for a target.""" async with self.session.post( diff --git a/custom_components/wled_screen_controller/number.py b/custom_components/wled_screen_controller/number.py new file mode 100644 index 0000000..30062e9 --- /dev/null +++ b/custom_components/wled_screen_controller/number.py @@ -0,0 +1,112 @@ +"""Number platform for LED Screen Controller (device brightness).""" +from __future__ import annotations + +import logging +from typing import Any + +from homeassistant.components.number import NumberEntity, NumberMode +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, DATA_COORDINATOR, TARGET_TYPE_KEY_COLORS +from .coordinator import WLEDScreenControllerCoordinator + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up LED Screen Controller brightness numbers.""" + data = hass.data[DOMAIN][entry.entry_id] + coordinator: WLEDScreenControllerCoordinator = data[DATA_COORDINATOR] + + entities = [] + if coordinator.data and "targets" in coordinator.data: + devices = coordinator.data.get("devices") or {} + + for target_id, target_data in coordinator.data["targets"].items(): + info = target_data["info"] + + # Only LED targets have a device_id + if info.get("target_type") == TARGET_TYPE_KEY_COLORS: + continue + + device_id = info.get("device_id", "") + if not device_id: + continue + + # Check if the device supports brightness control + device_data = devices.get(device_id) + if not device_data: + continue + + capabilities = device_data.get("info", {}).get("capabilities") or [] + if "brightness_control" not in capabilities: + continue + + entities.append( + WLEDScreenControllerBrightness( + coordinator, target_id, device_id, entry.entry_id, + ) + ) + + async_add_entities(entities) + + +class WLEDScreenControllerBrightness(CoordinatorEntity, NumberEntity): + """Brightness control for an LED device associated with a target.""" + + _attr_has_entity_name = True + _attr_native_min_value = 0 + _attr_native_max_value = 255 + _attr_native_step = 1 + _attr_mode = NumberMode.SLIDER + _attr_icon = "mdi:brightness-6" + + def __init__( + self, + coordinator: WLEDScreenControllerCoordinator, + target_id: str, + device_id: str, + entry_id: str, + ) -> None: + """Initialize the brightness number.""" + super().__init__(coordinator) + self._target_id = target_id + self._device_id = device_id + self._entry_id = entry_id + self._attr_unique_id = f"{target_id}_brightness" + self._attr_translation_key = "brightness" + + @property + def device_info(self) -> dict[str, Any]: + """Return device information.""" + return {"identifiers": {(DOMAIN, self._target_id)}} + + @property + def native_value(self) -> float | None: + """Return the current brightness value.""" + if not self.coordinator.data: + return None + device_data = self.coordinator.data.get("devices", {}).get(self._device_id) + if not device_data: + return None + return device_data.get("brightness") + + @property + def available(self) -> bool: + """Return if entity is available.""" + if not self.coordinator.data: + return False + targets = self.coordinator.data.get("targets", {}) + devices = self.coordinator.data.get("devices", {}) + return self._target_id in targets and self._device_id in devices + + async def async_set_native_value(self, value: float) -> None: + """Set brightness value.""" + await self.coordinator.set_brightness(self._device_id, int(value)) diff --git a/custom_components/wled_screen_controller/strings.json b/custom_components/wled_screen_controller/strings.json index 296bdc6..59d0a22 100644 --- a/custom_components/wled_screen_controller/strings.json +++ b/custom_components/wled_screen_controller/strings.json @@ -45,6 +45,11 @@ "rectangle_color": { "name": "{rectangle_name} Color" } + }, + "number": { + "brightness": { + "name": "Brightness" + } } } } diff --git a/custom_components/wled_screen_controller/translations/en.json b/custom_components/wled_screen_controller/translations/en.json index 296bdc6..59d0a22 100644 --- a/custom_components/wled_screen_controller/translations/en.json +++ b/custom_components/wled_screen_controller/translations/en.json @@ -45,6 +45,11 @@ "rectangle_color": { "name": "{rectangle_name} Color" } + }, + "number": { + "brightness": { + "name": "Brightness" + } } } } diff --git a/custom_components/wled_screen_controller/translations/ru.json b/custom_components/wled_screen_controller/translations/ru.json index 67f4dbf..f0b8497 100644 --- a/custom_components/wled_screen_controller/translations/ru.json +++ b/custom_components/wled_screen_controller/translations/ru.json @@ -45,6 +45,11 @@ "rectangle_color": { "name": "{rectangle_name} Цвет" } + }, + "number": { + "brightness": { + "name": "Яркость" + } } } } diff --git a/server/src/wled_controller/static/js/features/perf-charts.js b/server/src/wled_controller/static/js/features/perf-charts.js index 66e5642..ce69258 100644 --- a/server/src/wled_controller/static/js/features/perf-charts.js +++ b/server/src/wled_controller/static/js/features/perf-charts.js @@ -77,7 +77,7 @@ function _createChart(canvasId, color, fillColor) { options: { responsive: true, maintainAspectRatio: false, - animation: false, + animation: true, plugins: { legend: { display: false }, tooltip: { enabled: false } }, scales: { x: { display: false },