"""Support for a ScreenLogic number entity.""" from dataclasses import dataclass import logging from typing import override from screenlogicpy.const.common import ScreenLogicCommunicationError, ScreenLogicError from screenlogicpy.const.data import ATTR, DEVICE, GROUP, VALUE from screenlogicpy.const.msg import CODE from screenlogicpy.device_const.system import EQUIPMENT_FLAG from homeassistant.components.number import ( DOMAIN as NUMBER_DOMAIN, NumberEntity, NumberEntityDescription, NumberMode, ) from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import ScreenlogicDataUpdateCoordinator from .entity import ( ScreenLogicEntity, ScreenLogicEntityDescription, ScreenLogicPushEntity, ScreenLogicPushEntityDescription, ) from .types import ScreenLogicConfigEntry from .util import cleanup_excluded_entity, get_ha_unit _LOGGER = logging.getLogger(__name__) PARALLEL_UPDATES = 1 @dataclass(frozen=True, kw_only=True) class ScreenLogicNumberDescription( NumberEntityDescription, ScreenLogicEntityDescription, ): """Describes a ScreenLogic number entity.""" @dataclass(frozen=True, kw_only=True) class ScreenLogicPushNumberDescription( ScreenLogicNumberDescription, ScreenLogicPushEntityDescription, ): """Describes a ScreenLogic push number entity.""" SUPPORTED_INTELLICHEM_NUMBERS = [ ScreenLogicPushNumberDescription( subscription_code=CODE.CHEMISTRY_CHANGED, data_root=(DEVICE.INTELLICHEM, GROUP.CONFIGURATION), key=VALUE.CALCIUM_HARDNESS, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, translation_key="calcium_hardness", ), ScreenLogicPushNumberDescription( subscription_code=CODE.CHEMISTRY_CHANGED, data_root=(DEVICE.INTELLICHEM, GROUP.CONFIGURATION), key=VALUE.CYA, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, translation_key="cya", ), ScreenLogicPushNumberDescription( subscription_code=CODE.CHEMISTRY_CHANGED, data_root=(DEVICE.INTELLICHEM, GROUP.CONFIGURATION), key=VALUE.TOTAL_ALKALINITY, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, translation_key="total_alkalinity", ), ScreenLogicPushNumberDescription( subscription_code=CODE.CHEMISTRY_CHANGED, data_root=(DEVICE.INTELLICHEM, GROUP.CONFIGURATION), key=VALUE.SALT_TDS_PPM, entity_category=EntityCategory.CONFIG, mode=NumberMode.BOX, translation_key="salt_tds_ppm", ), ] SUPPORTED_SCG_NUMBERS = [ ScreenLogicNumberDescription( data_root=(DEVICE.SCG, GROUP.CONFIGURATION), key=VALUE.POOL_SETPOINT, entity_category=EntityCategory.CONFIG, translation_key="pool_setpoint", ), ScreenLogicNumberDescription( data_root=(DEVICE.SCG, GROUP.CONFIGURATION), key=VALUE.SPA_SETPOINT, entity_category=EntityCategory.CONFIG, translation_key="spa_setpoint", ), ] async def async_setup_entry( hass: HomeAssistant, config_entry: ScreenLogicConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up entry.""" entities: list[ScreenLogicNumber] = [] coordinator = config_entry.runtime_data gateway = coordinator.gateway for chem_number_description in SUPPORTED_INTELLICHEM_NUMBERS: chem_number_data_path = ( *chem_number_description.data_root, chem_number_description.key, ) if EQUIPMENT_FLAG.INTELLICHEM not in gateway.equipment_flags: cleanup_excluded_entity(coordinator, NUMBER_DOMAIN, chem_number_data_path) continue if gateway.get_data(*chem_number_data_path): entities.append( ScreenLogicChemistryNumber(coordinator, chem_number_description) ) for scg_number_description in SUPPORTED_SCG_NUMBERS: scg_number_data_path = ( *scg_number_description.data_root, scg_number_description.key, ) if EQUIPMENT_FLAG.CHLORINATOR not in gateway.equipment_flags: cleanup_excluded_entity(coordinator, NUMBER_DOMAIN, scg_number_data_path) continue if gateway.get_data(*scg_number_data_path): entities.append(ScreenLogicSCGNumber(coordinator, scg_number_description)) async_add_entities(entities) class ScreenLogicNumber(ScreenLogicEntity, NumberEntity): """Base class to represent a ScreenLogic Number entity.""" entity_description: ScreenLogicNumberDescription def __init__( self, coordinator: ScreenlogicDataUpdateCoordinator, entity_description: ScreenLogicNumberDescription, ) -> None: """Initialize a ScreenLogic number entity.""" super().__init__(coordinator, entity_description) self._attr_native_unit_of_measurement = get_ha_unit( self.entity_data.get(ATTR.UNIT) ) if entity_description.native_max_value is None and isinstance( max_val := self.entity_data.get(ATTR.MAX_SETPOINT), int | float ): self._attr_native_max_value = max_val if entity_description.native_min_value is None and isinstance( min_val := self.entity_data.get(ATTR.MIN_SETPOINT), int | float ): self._attr_native_min_value = min_val if entity_description.native_step is None and isinstance( step := self.entity_data.get(ATTR.STEP), int | float ): self._attr_native_step = step @property @override def native_value(self) -> float: """Return the current value.""" return self.entity_data[ATTR.VALUE] @override async def async_set_native_value(self, value: float) -> None: """Update the current value.""" raise NotImplementedError class ScreenLogicPushNumber(ScreenLogicPushEntity, ScreenLogicNumber): """Base class to preresent a ScreenLogic Push Number entity.""" entity_description: ScreenLogicPushNumberDescription class ScreenLogicChemistryNumber(ScreenLogicPushNumber): """Class to represent a ScreenLogic Chemistry Number entity.""" @override async def async_set_native_value(self, value: float) -> None: """Update the current value.""" # Current API requires int values for the currently supported numbers. value = int(value) try: await self.gateway.async_set_chem_data(**{self._data_key: value}) except (ScreenLogicCommunicationError, ScreenLogicError) as sle: raise HomeAssistantError( f"Failed to set '{self._data_key}' to {value}: {sle.msg}" ) from sle _LOGGER.debug("Set '%s' to %s", self._data_key, value) await self._async_refresh() class ScreenLogicSCGNumber(ScreenLogicNumber): """Class to represent a ScreenLoigic SCG Number entity.""" @override async def async_set_native_value(self, value: float) -> None: """Update the current value.""" # Current API requires int values for the currently supported numbers. value = int(value) try: await self.gateway.async_set_scg_config(**{self._data_key: value}) except (ScreenLogicCommunicationError, ScreenLogicError) as sle: raise HomeAssistantError( f"Failed to set '{self._data_key}' to {value}: {sle.msg}" ) from sle _LOGGER.debug("Set '%s' to %s", self._data_key, value) await self._async_refresh()