"""Support for a ScreenLogic Binary Sensor.""" from dataclasses import dataclass import logging from screenlogicpy.const.common import DEVICE_TYPE, ON_OFF from screenlogicpy.const.data import ATTR, DEVICE, GROUP, VALUE from homeassistant.components.binary_sensor import ( DOMAIN, BinarySensorDeviceClass, BinarySensorEntity, BinarySensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN as SL_DOMAIN, ScreenLogicDataPath from .coordinator import ScreenlogicDataUpdateCoordinator from .data import ( DEVICE_INCLUSION_RULES, DEVICE_SUBSCRIPTION, SupportedValueParameters, build_base_entity_description, iterate_expand_group_wildcard, preprocess_supported_values, ) from .entity import ( ScreenlogicEntity, ScreenLogicEntityDescription, ScreenLogicPushEntity, ScreenLogicPushEntityDescription, ) from .util import cleanup_excluded_entity, generate_unique_id _LOGGER = logging.getLogger(__name__) @dataclass class SupportedBinarySensorValueParameters(SupportedValueParameters): """Supported predefined data for a ScreenLogic binary sensor entity.""" device_class: BinarySensorDeviceClass | None = None SUPPORTED_DATA: list[ tuple[ScreenLogicDataPath, SupportedValueParameters] ] = preprocess_supported_values( { DEVICE.CONTROLLER: { GROUP.SENSOR: { VALUE.ACTIVE_ALERT: SupportedBinarySensorValueParameters(), VALUE.CLEANER_DELAY: SupportedBinarySensorValueParameters(), VALUE.FREEZE_MODE: SupportedBinarySensorValueParameters(), VALUE.POOL_DELAY: SupportedBinarySensorValueParameters(), VALUE.SPA_DELAY: SupportedBinarySensorValueParameters(), }, }, DEVICE.PUMP: { "*": { VALUE.STATE: SupportedBinarySensorValueParameters(), }, }, DEVICE.INTELLICHEM: { GROUP.ALARM: { VALUE.FLOW_ALARM: SupportedBinarySensorValueParameters(), VALUE.ORP_HIGH_ALARM: SupportedBinarySensorValueParameters(), VALUE.ORP_LOW_ALARM: SupportedBinarySensorValueParameters(), VALUE.ORP_SUPPLY_ALARM: SupportedBinarySensorValueParameters(), VALUE.PH_HIGH_ALARM: SupportedBinarySensorValueParameters(), VALUE.PH_LOW_ALARM: SupportedBinarySensorValueParameters(), VALUE.PH_SUPPLY_ALARM: SupportedBinarySensorValueParameters(), VALUE.PROBE_FAULT_ALARM: SupportedBinarySensorValueParameters(), }, GROUP.ALERT: { VALUE.ORP_LIMIT: SupportedBinarySensorValueParameters(), VALUE.PH_LIMIT: SupportedBinarySensorValueParameters(), VALUE.PH_LOCKOUT: SupportedBinarySensorValueParameters(), }, GROUP.WATER_BALANCE: { VALUE.CORROSIVE: SupportedBinarySensorValueParameters(), VALUE.SCALING: SupportedBinarySensorValueParameters(), }, }, DEVICE.SCG: { GROUP.SENSOR: { VALUE.STATE: SupportedBinarySensorValueParameters(), }, }, } ) SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS = {DEVICE_TYPE.ALARM: BinarySensorDeviceClass.PROBLEM} async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up entry.""" entities: list[ScreenLogicBinarySensor] = [] coordinator: ScreenlogicDataUpdateCoordinator = hass.data[SL_DOMAIN][ config_entry.entry_id ] gateway = coordinator.gateway data_path: ScreenLogicDataPath value_params: SupportedBinarySensorValueParameters for data_path, value_params in iterate_expand_group_wildcard( gateway, SUPPORTED_DATA ): entity_key = generate_unique_id(*data_path) device = data_path[0] if not (DEVICE_INCLUSION_RULES.get(device) or value_params.included).test( gateway, data_path ): cleanup_excluded_entity(coordinator, DOMAIN, entity_key) continue try: value_data = gateway.get_data(*data_path, strict=True) except KeyError: _LOGGER.debug("Failed to find %s", data_path) continue entity_description_kwargs = { **build_base_entity_description( gateway, entity_key, data_path, value_data, value_params ), "device_class": SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS.get( value_data.get(ATTR.DEVICE_TYPE) ), } if ( sub_code := ( value_params.subscription_code or DEVICE_SUBSCRIPTION.get(device) ) ) is not None: entities.append( ScreenLogicPushBinarySensor( coordinator, ScreenLogicPushBinarySensorDescription( subscription_code=sub_code, **entity_description_kwargs ), ) ) else: entities.append( ScreenLogicBinarySensor( coordinator, ScreenLogicBinarySensorDescription(**entity_description_kwargs), ) ) async_add_entities(entities) @dataclass class ScreenLogicBinarySensorDescription( BinarySensorEntityDescription, ScreenLogicEntityDescription ): """A class that describes ScreenLogic binary sensor eneites.""" class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity): """Base class for all ScreenLogic binary sensor entities.""" entity_description: ScreenLogicBinarySensorDescription _attr_has_entity_name = True @property def is_on(self) -> bool: """Determine if the sensor is on.""" return self.entity_data[ATTR.VALUE] == ON_OFF.ON @dataclass class ScreenLogicPushBinarySensorDescription( ScreenLogicBinarySensorDescription, ScreenLogicPushEntityDescription ): """Describes a ScreenLogicPushBinarySensor.""" class ScreenLogicPushBinarySensor(ScreenLogicPushEntity, ScreenLogicBinarySensor): """Representation of a basic ScreenLogic sensor entity.""" entity_description: ScreenLogicPushBinarySensorDescription