"""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)