579553a850
HACS-compatible custom component split out from the main LedGrab repo. Creates light, switch, sensor, number, and select entities for each configured LedGrab device.
226 lines
7.2 KiB
Python
226 lines
7.2 KiB
Python
"""Sensor platform for LED Screen Controller."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from homeassistant.components.sensor import (
|
|
SensorDeviceClass,
|
|
SensorEntity,
|
|
SensorStateClass,
|
|
)
|
|
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,
|
|
TARGET_TYPE_HA_LIGHT,
|
|
DATA_COORDINATOR,
|
|
)
|
|
from .coordinator import LedGrabCoordinator
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up LED Screen Controller sensors."""
|
|
data = hass.data[DOMAIN][entry.entry_id]
|
|
coordinator: LedGrabCoordinator = data[DATA_COORDINATOR]
|
|
|
|
entities: list[SensorEntity] = []
|
|
if coordinator.data and "targets" in coordinator.data:
|
|
for target_id, target_data in coordinator.data["targets"].items():
|
|
entities.append(LedGrabFPSSensor(coordinator, target_id, entry.entry_id))
|
|
entities.append(
|
|
LedGrabStatusSensor(coordinator, target_id, entry.entry_id)
|
|
)
|
|
|
|
# Add mapped lights sensor for HA Light targets
|
|
info = target_data["info"]
|
|
if info.get("target_type") == TARGET_TYPE_HA_LIGHT:
|
|
entities.append(HALightMappedLightsSensor(coordinator, target_id, entry.entry_id))
|
|
|
|
async_add_entities(entities)
|
|
|
|
|
|
class LedGrabFPSSensor(CoordinatorEntity, SensorEntity):
|
|
"""FPS sensor for a LED Screen Controller target."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_state_class = SensorStateClass.MEASUREMENT
|
|
_attr_native_unit_of_measurement = "FPS"
|
|
_attr_icon = "mdi:speedometer"
|
|
_attr_suggested_display_precision = 1
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
target_id: str,
|
|
entry_id: str,
|
|
) -> None:
|
|
"""Initialize the sensor."""
|
|
super().__init__(coordinator)
|
|
self._target_id = target_id
|
|
self._entry_id = entry_id
|
|
self._attr_unique_id = f"{target_id}_fps"
|
|
self._attr_translation_key = "fps"
|
|
|
|
@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 FPS value."""
|
|
target_data = self._get_target_data()
|
|
if not target_data or not target_data.get("state"):
|
|
return None
|
|
state = target_data["state"]
|
|
if not state.get("processing"):
|
|
return None
|
|
return state.get("fps_actual")
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> dict[str, Any]:
|
|
"""Return additional attributes."""
|
|
target_data = self._get_target_data()
|
|
if not target_data or not target_data.get("state"):
|
|
return {}
|
|
return {"fps_target": target_data["state"].get("fps_target")}
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return if entity is available."""
|
|
return self._get_target_data() is not None
|
|
|
|
def _get_target_data(self) -> dict[str, Any] | None:
|
|
if not self.coordinator.data:
|
|
return None
|
|
return self.coordinator.data.get("targets", {}).get(self._target_id)
|
|
|
|
|
|
class LedGrabStatusSensor(CoordinatorEntity, SensorEntity):
|
|
"""Status sensor for a LED Screen Controller target."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_icon = "mdi:information-outline"
|
|
_attr_device_class = SensorDeviceClass.ENUM
|
|
_attr_options = ["processing", "idle", "error", "unavailable"]
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
target_id: str,
|
|
entry_id: str,
|
|
) -> None:
|
|
"""Initialize the sensor."""
|
|
super().__init__(coordinator)
|
|
self._target_id = target_id
|
|
self._entry_id = entry_id
|
|
self._attr_unique_id = f"{target_id}_status"
|
|
self._attr_translation_key = "status"
|
|
|
|
@property
|
|
def device_info(self) -> dict[str, Any]:
|
|
"""Return device information."""
|
|
return {"identifiers": {(DOMAIN, self._target_id)}}
|
|
|
|
@property
|
|
def native_value(self) -> str:
|
|
"""Return the status."""
|
|
target_data = self._get_target_data()
|
|
if not target_data:
|
|
return "unavailable"
|
|
state = target_data.get("state")
|
|
if not state:
|
|
return "unavailable"
|
|
if state.get("processing"):
|
|
errors = state.get("errors", [])
|
|
if errors:
|
|
return "error"
|
|
return "processing"
|
|
return "idle"
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return if entity is available."""
|
|
return self._get_target_data() is not None
|
|
|
|
def _get_target_data(self) -> dict[str, Any] | None:
|
|
if not self.coordinator.data:
|
|
return None
|
|
return self.coordinator.data.get("targets", {}).get(self._target_id)
|
|
|
|
|
|
class HALightMappedLightsSensor(CoordinatorEntity, SensorEntity):
|
|
"""Sensor showing the number of mapped HA lights for an HA Light target."""
|
|
|
|
_attr_has_entity_name = True
|
|
_attr_icon = "mdi:lightbulb-group"
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: LedGrabCoordinator,
|
|
target_id: str,
|
|
entry_id: str,
|
|
) -> None:
|
|
"""Initialize the sensor."""
|
|
super().__init__(coordinator)
|
|
self._target_id = target_id
|
|
self._entry_id = entry_id
|
|
self._attr_unique_id = f"{target_id}_mapped_lights"
|
|
self._attr_translation_key = "mapped_lights"
|
|
|
|
@property
|
|
def device_info(self) -> dict[str, Any]:
|
|
"""Return device information."""
|
|
return {"identifiers": {(DOMAIN, self._target_id)}}
|
|
|
|
@property
|
|
def native_value(self) -> int | None:
|
|
"""Return the number of mapped lights."""
|
|
target_data = self._get_target_data()
|
|
if not target_data:
|
|
return None
|
|
mappings = target_data.get("info", {}).get("light_mappings", [])
|
|
return len(mappings)
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> dict[str, Any]:
|
|
"""Return light mapping details as attributes."""
|
|
target_data = self._get_target_data()
|
|
if not target_data:
|
|
return {}
|
|
mappings = target_data.get("info", {}).get("light_mappings", [])
|
|
entity_ids = [m.get("entity_id", "") for m in mappings]
|
|
return {
|
|
"entity_ids": entity_ids,
|
|
"mappings": [
|
|
{
|
|
"entity_id": m.get("entity_id", ""),
|
|
"led_start": m.get("led_start", 0),
|
|
"led_end": m.get("led_end", -1),
|
|
"brightness_scale": m.get("brightness_scale", 1.0),
|
|
}
|
|
for m in mappings
|
|
],
|
|
}
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return if entity is available."""
|
|
return self._get_target_data() is not None
|
|
|
|
def _get_target_data(self) -> dict[str, Any] | None:
|
|
if not self.coordinator.data:
|
|
return None
|
|
return self.coordinator.data.get("targets", {}).get(self._target_id)
|