diff --git a/.coveragerc b/.coveragerc index 703523ed364..6b2870e8488 100644 --- a/.coveragerc +++ b/.coveragerc @@ -408,6 +408,7 @@ omit = homeassistant/components/gardena_bluetooth/__init__.py homeassistant/components/gardena_bluetooth/const.py homeassistant/components/gardena_bluetooth/coordinator.py + homeassistant/components/gardena_bluetooth/number.py homeassistant/components/gardena_bluetooth/switch.py homeassistant/components/gc100/* homeassistant/components/geniushub/* diff --git a/homeassistant/components/gardena_bluetooth/__init__.py b/homeassistant/components/gardena_bluetooth/__init__.py index 05ac16381d1..2954a5fe377 100644 --- a/homeassistant/components/gardena_bluetooth/__init__.py +++ b/homeassistant/components/gardena_bluetooth/__init__.py @@ -20,7 +20,7 @@ import homeassistant.util.dt as dt_util from .const import DOMAIN from .coordinator import Coordinator, DeviceUnavailable -PLATFORMS: list[Platform] = [Platform.SWITCH] +PLATFORMS: list[Platform] = [Platform.NUMBER, Platform.SWITCH] LOGGER = logging.getLogger(__name__) TIMEOUT = 20.0 DISCONNECT_DELAY = 5 diff --git a/homeassistant/components/gardena_bluetooth/number.py b/homeassistant/components/gardena_bluetooth/number.py new file mode 100644 index 00000000000..367e2f727bc --- /dev/null +++ b/homeassistant/components/gardena_bluetooth/number.py @@ -0,0 +1,146 @@ +"""Support for number entities.""" +from __future__ import annotations + +from dataclasses import dataclass, field + +from gardena_bluetooth.const import DeviceConfiguration, Valve +from gardena_bluetooth.parse import ( + CharacteristicInt, + CharacteristicLong, + CharacteristicUInt16, +) + +from homeassistant.components.number import ( + NumberEntity, + NumberEntityDescription, + NumberMode, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import Coordinator, GardenaBluetoothEntity + + +@dataclass +class GardenaBluetoothNumberEntityDescription(NumberEntityDescription): + """Description of entity.""" + + char: CharacteristicInt | CharacteristicUInt16 | CharacteristicLong = field( + default_factory=lambda: CharacteristicInt("") + ) + + +DESCRIPTIONS = ( + GardenaBluetoothNumberEntityDescription( + key=Valve.manual_watering_time.uuid, + translation_key="manual_watering_time", + native_unit_of_measurement="s", + 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="s", + 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="d", + mode=NumberMode.BOX, + native_min_value=0.0, + native_max_value=127.0, + native_step=1.0, + entity_category=EntityCategory.CONFIG, + char=DeviceConfiguration.rain_pause, + ), + GardenaBluetoothNumberEntityDescription( + key=DeviceConfiguration.season_pause.uuid, + translation_key="season_pause", + native_unit_of_measurement="d", + mode=NumberMode.BOX, + native_min_value=0.0, + native_max_value=365.0, + native_step=1.0, + entity_category=EntityCategory.CONFIG, + char=DeviceConfiguration.season_pause, + ), +) + + +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) + for description in DESCRIPTIONS + if description.key in coordinator.characteristics + ] + entities.append(GardenaBluetoothRemainingOpenSetNumber(coordinator)) + async_add_entities(entities) + + +class GardenaBluetoothNumber(GardenaBluetoothEntity, NumberEntity): + """Representation of a number.""" + + entity_description: GardenaBluetoothNumberEntityDescription + + def __init__( + self, + coordinator: Coordinator, + description: GardenaBluetoothNumberEntityDescription, + ) -> None: + """Initialize the number entity.""" + super().__init__(coordinator, {description.key}) + self._attr_unique_id = f"{coordinator.address}-{description.key}" + self.entity_description = description + + def _handle_coordinator_update(self) -> None: + if data := self.coordinator.data.get(self.entity_description.char.uuid): + self._attr_native_value = float(self.entity_description.char.decode(data)) + else: + self._attr_native_value = None + 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() diff --git a/homeassistant/components/gardena_bluetooth/strings.json b/homeassistant/components/gardena_bluetooth/strings.json index 165e336bbec..c7a6e9637df 100644 --- a/homeassistant/components/gardena_bluetooth/strings.json +++ b/homeassistant/components/gardena_bluetooth/strings.json @@ -19,6 +19,23 @@ } }, "entity": { + "number": { + "remaining_open_time": { + "name": "Remaining open time" + }, + "remaining_open_set": { + "name": "Open for" + }, + "manual_watering_time": { + "name": "Manual watering time" + }, + "rain_pause": { + "name": "Rain pause" + }, + "season_pause": { + "name": "Season pause" + } + }, "switch": { "state": { "name": "Open"