"""Support for number entities.""" from __future__ import annotations from dataclasses import dataclass, field from gardena_bluetooth.const import DeviceConfiguration, Sensor, Valve from gardena_bluetooth.parse import ( Characteristic, CharacteristicInt, CharacteristicLong, CharacteristicUInt16, ) from homeassistant.components.number import ( NumberEntity, NumberEntityDescription, NumberMode, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTime from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN from .coordinator import ( Coordinator, GardenaBluetoothDescriptorEntity, GardenaBluetoothEntity, ) @dataclass(frozen=True) class GardenaBluetoothNumberEntityDescription(NumberEntityDescription): """Description of entity.""" char: CharacteristicInt | CharacteristicUInt16 | CharacteristicLong = field( default_factory=lambda: CharacteristicInt("") ) connected_state: Characteristic | None = None @property def context(self) -> set[str]: """Context needed for update coordinator.""" data = {self.char.uuid} if self.connected_state: data.add(self.connected_state.uuid) return data DESCRIPTIONS = ( GardenaBluetoothNumberEntityDescription( key=Valve.manual_watering_time.uuid, translation_key="manual_watering_time", native_unit_of_measurement=UnitOfTime.SECONDS, mode=NumberMode.BOX, native_min_value=0.0, native_max_value=24 * 60 * 60, native_step=60, entity_category=EntityCategory.CONFIG, char=Valve.manual_watering_time, ), GardenaBluetoothNumberEntityDescription( key=Valve.remaining_open_time.uuid, translation_key="remaining_open_time", native_unit_of_measurement=UnitOfTime.SECONDS, native_min_value=0.0, native_max_value=24 * 60 * 60, native_step=60.0, entity_category=EntityCategory.DIAGNOSTIC, char=Valve.remaining_open_time, ), GardenaBluetoothNumberEntityDescription( key=DeviceConfiguration.rain_pause.uuid, translation_key="rain_pause", native_unit_of_measurement=UnitOfTime.MINUTES, mode=NumberMode.BOX, native_min_value=0.0, native_max_value=7 * 24 * 60, native_step=6 * 60.0, entity_category=EntityCategory.CONFIG, char=DeviceConfiguration.rain_pause, ), GardenaBluetoothNumberEntityDescription( key=DeviceConfiguration.seasonal_adjust.uuid, translation_key="seasonal_adjust", native_unit_of_measurement=UnitOfTime.DAYS, mode=NumberMode.BOX, native_min_value=-128.0, native_max_value=127.0, native_step=1.0, entity_category=EntityCategory.CONFIG, char=DeviceConfiguration.seasonal_adjust, ), GardenaBluetoothNumberEntityDescription( key=Sensor.threshold.uuid, translation_key="sensor_threshold", native_unit_of_measurement=PERCENTAGE, mode=NumberMode.BOX, native_min_value=0.0, native_max_value=100.0, native_step=1.0, entity_category=EntityCategory.CONFIG, char=Sensor.threshold, connected_state=Sensor.connected_state, ), ) async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up entity based on a config entry.""" coordinator: Coordinator = hass.data[DOMAIN][entry.entry_id] entities: list[NumberEntity] = [ GardenaBluetoothNumber(coordinator, description, description.context) for description in DESCRIPTIONS if description.key in coordinator.characteristics ] if Valve.remaining_open_time.uuid in coordinator.characteristics: entities.append(GardenaBluetoothRemainingOpenSetNumber(coordinator)) async_add_entities(entities) class GardenaBluetoothNumber(GardenaBluetoothDescriptorEntity, NumberEntity): """Representation of a number.""" entity_description: GardenaBluetoothNumberEntityDescription def _handle_coordinator_update(self) -> None: data = self.coordinator.get_cached(self.entity_description.char) if data is None: self._attr_native_value = None else: self._attr_native_value = float(data) if char := self.entity_description.connected_state: self._attr_available = bool(self.coordinator.get_cached(char)) else: self._attr_available = True super()._handle_coordinator_update() async def async_set_native_value(self, value: float) -> None: """Set new value.""" await self.coordinator.write(self.entity_description.char, int(value)) self.async_write_ha_state() class GardenaBluetoothRemainingOpenSetNumber(GardenaBluetoothEntity, NumberEntity): """Representation of a entity with remaining time.""" _attr_translation_key = "remaining_open_set" _attr_native_unit_of_measurement = "min" _attr_mode = NumberMode.BOX _attr_native_min_value = 0.0 _attr_native_max_value = 24 * 60 _attr_native_step = 1.0 def __init__( self, coordinator: Coordinator, ) -> None: """Initialize the remaining time entity.""" super().__init__(coordinator, {Valve.remaining_open_time.uuid}) self._attr_unique_id = f"{coordinator.address}-remaining_open_set" async def async_set_native_value(self, value: float) -> None: """Set new value.""" await self.coordinator.write(Valve.remaining_open_time, int(value * 60)) self.async_write_ha_state()