173 lines
5.7 KiB
Python
173 lines
5.7 KiB
Python
"""Switch platform for the Riedel RRCS integration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from homeassistant.components.switch import SwitchEntity
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
|
|
from .coordinator import RRCSCoordinator
|
|
from .entity import RRCSEntity
|
|
from .models import RRCSConfigEntry
|
|
from .rrcs import GpioAddress, RRCSError
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: RRCSConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the switch entities."""
|
|
runtime = entry.runtime_data
|
|
coordinator = runtime.coordinator
|
|
|
|
entities: list[SwitchEntity] = [
|
|
RRCSLogicSourceSwitch(coordinator, entry.entry_id, object_id)
|
|
for object_id in (coordinator.data.logic_sources if coordinator.data else {})
|
|
]
|
|
entities.extend(
|
|
RRCSGpOutputSwitch(
|
|
coordinator,
|
|
entry.entry_id,
|
|
address,
|
|
runtime.gpio_names.get(address.key),
|
|
)
|
|
for address in runtime.gpio_outputs
|
|
)
|
|
|
|
async_add_entities(entities)
|
|
|
|
known = {
|
|
object_id for object_id in (coordinator.data.logic_sources if coordinator.data else {})
|
|
}
|
|
|
|
@callback
|
|
def _add_new_logic_sources() -> None:
|
|
"""Pick up logic sources added to the Artist configuration later."""
|
|
if coordinator.data is None:
|
|
return
|
|
new = set(coordinator.data.logic_sources) - known
|
|
if not new:
|
|
return
|
|
known.update(new)
|
|
async_add_entities(
|
|
RRCSLogicSourceSwitch(coordinator, entry.entry_id, object_id)
|
|
for object_id in new
|
|
)
|
|
|
|
entry.async_on_unload(coordinator.async_add_listener(_add_new_logic_sources))
|
|
|
|
|
|
class RRCSLogicSourceSwitch(RRCSEntity, SwitchEntity):
|
|
"""A logic source, driven through SetLogicSourceState."""
|
|
|
|
_attr_icon = "mdi:toggle-switch-outline"
|
|
|
|
def __init__(
|
|
self, coordinator: RRCSCoordinator, entry_id: str, object_id: int
|
|
) -> None:
|
|
"""Initialise the switch."""
|
|
super().__init__(coordinator, entry_id)
|
|
self._object_id = object_id
|
|
self._attr_unique_id = f"{entry_id}_logic_source_{object_id}"
|
|
source = coordinator.data.logic_sources.get(object_id) if coordinator.data else None
|
|
self._attr_name = (source.long_name or source.label) if source else f"Logic source {object_id}"
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""Return whether the logic source is still in the configuration."""
|
|
return (
|
|
super().available
|
|
and self.coordinator.data is not None
|
|
and self._object_id in self.coordinator.data.logic_sources
|
|
)
|
|
|
|
@property
|
|
def is_on(self) -> bool | None:
|
|
"""Return the logic source state."""
|
|
if self.coordinator.data is None:
|
|
return None
|
|
source = self.coordinator.data.logic_sources.get(self._object_id)
|
|
return source.state if source else None
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> dict[str, Any]:
|
|
"""Expose the Artist identifiers for use in automations."""
|
|
source = (
|
|
self.coordinator.data.logic_sources.get(self._object_id)
|
|
if self.coordinator.data
|
|
else None
|
|
)
|
|
return {
|
|
"object_id": self._object_id,
|
|
"label": source.label if source else None,
|
|
}
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Activate the logic source."""
|
|
await self._async_set(True)
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Deactivate the logic source.
|
|
|
|
RRCS only clears logic sources it set itself; one driven from a panel
|
|
key will not respond here.
|
|
"""
|
|
await self._async_set(False)
|
|
|
|
async def _async_set(self, state: bool) -> None:
|
|
try:
|
|
await self.coordinator.client.set_logic_source(self._object_id, state)
|
|
except RRCSError as err:
|
|
raise HomeAssistantError(f"Could not set logic source: {err}") from err
|
|
self.coordinator.apply_logic_source_change(self._object_id, state)
|
|
|
|
|
|
class RRCSGpOutputSwitch(RRCSEntity, SwitchEntity):
|
|
"""A general purpose output."""
|
|
|
|
_attr_icon = "mdi:electric-switch"
|
|
|
|
def __init__(
|
|
self,
|
|
coordinator: RRCSCoordinator,
|
|
entry_id: str,
|
|
address: GpioAddress,
|
|
name: str | None,
|
|
) -> None:
|
|
"""Initialise the switch."""
|
|
super().__init__(coordinator, entry_id)
|
|
self._address = address
|
|
self._attr_unique_id = f"{entry_id}_gpo_{address.key}"
|
|
self._attr_name = name or f"GP out {address.node}.{address.slot}.{address.index}"
|
|
|
|
@property
|
|
def is_on(self) -> bool | None:
|
|
"""Return the output state."""
|
|
if self.coordinator.data is None:
|
|
return None
|
|
return self.coordinator.data.gpio_outputs.get(self._address.key)
|
|
|
|
@property
|
|
def extra_state_attributes(self) -> dict[str, Any]:
|
|
"""Expose the GPIO address."""
|
|
return {"address": self._address.pretty}
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""Close the output."""
|
|
await self._async_set(True)
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Open the output."""
|
|
await self._async_set(False)
|
|
|
|
async def _async_set(self, state: bool) -> None:
|
|
try:
|
|
await self.coordinator.client.set_gp_output(self._address, state)
|
|
except RRCSError as err:
|
|
raise HomeAssistantError(f"Could not switch GP output: {err}") from err
|
|
self.coordinator.apply_gpio_change(self._address, state)
|