1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-21 16:00:12 +01:00
Files
Paulus Schoutsen d7568a8f16 Move valve entity classes to entity module (#170524)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 23:39:05 +02:00

178 lines
5.9 KiB
Python

"""Base entity for the Valve platform."""
from dataclasses import dataclass
from typing import Any, final
from homeassistant.helpers.entity import Entity, EntityDescription
from .const import ValveDeviceClass, ValveEntityFeature, ValveState
ATTR_CURRENT_POSITION = "current_position"
ATTR_IS_CLOSED = "is_closed"
@dataclass(frozen=True, kw_only=True)
class ValveEntityDescription(EntityDescription):
"""A class that describes valve entities."""
device_class: ValveDeviceClass | None = None
reports_position: bool = False
class ValveEntity(Entity):
"""Base class for valve entities."""
entity_description: ValveEntityDescription
_attr_current_valve_position: int | None = None
_attr_device_class: ValveDeviceClass | None
_attr_is_closed: bool | None = None
_attr_is_closing: bool | None = None
_attr_is_opening: bool | None = None
_attr_reports_position: bool
_attr_supported_features: ValveEntityFeature = ValveEntityFeature(0)
__is_last_toggle_direction_open = True
@property
def reports_position(self) -> bool:
"""Return True if entity reports position, False otherwise."""
if hasattr(self, "_attr_reports_position"):
return self._attr_reports_position
if hasattr(self, "entity_description"):
return self.entity_description.reports_position
raise ValueError(f"'reports_position' not set for {self.entity_id}.")
@property
def current_valve_position(self) -> int | None:
"""Return current position of valve.
None is unknown, 0 is closed, 100 is fully open.
"""
return self._attr_current_valve_position
@property
def device_class(self) -> ValveDeviceClass | None:
"""Return the class of this entity."""
if hasattr(self, "_attr_device_class"):
return self._attr_device_class
if hasattr(self, "entity_description"):
return self.entity_description.device_class
return None
@property
@final
def state(self) -> str | None:
"""Return the state of the valve."""
reports_position = self.reports_position
if self.is_opening:
self.__is_last_toggle_direction_open = True
return ValveState.OPENING
if self.is_closing:
self.__is_last_toggle_direction_open = False
return ValveState.CLOSING
if reports_position is True:
if (current_valve_position := self.current_valve_position) is None:
return None
position_zero = current_valve_position == 0
return ValveState.CLOSED if position_zero else ValveState.OPEN
if (closed := self.is_closed) is None:
return None
return ValveState.CLOSED if closed else ValveState.OPEN
@final
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
data: dict[str, Any] = {}
if self.reports_position:
if (current_valve_position := self.current_valve_position) is None:
data[ATTR_IS_CLOSED] = None
else:
data[ATTR_IS_CLOSED] = current_valve_position == 0
data[ATTR_CURRENT_POSITION] = current_valve_position
else:
data[ATTR_IS_CLOSED] = self.is_closed
return data
@property
def supported_features(self) -> ValveEntityFeature:
"""Flag supported features."""
return self._attr_supported_features
@property
def is_opening(self) -> bool | None:
"""Return if the valve is opening or not."""
return self._attr_is_opening
@property
def is_closing(self) -> bool | None:
"""Return if the valve is closing or not."""
return self._attr_is_closing
@property
def is_closed(self) -> bool | None:
"""Return if the valve is closed or not."""
return self._attr_is_closed
def open_valve(self) -> None:
"""Open the valve."""
raise NotImplementedError
async def async_open_valve(self) -> None:
"""Open the valve."""
await self.hass.async_add_executor_job(self.open_valve)
@final
async def async_handle_open_valve(self) -> None:
"""Open the valve."""
if self.supported_features & ValveEntityFeature.SET_POSITION:
await self.async_set_valve_position(100)
return
await self.async_open_valve()
def close_valve(self) -> None:
"""Close valve."""
raise NotImplementedError
async def async_close_valve(self) -> None:
"""Close valve."""
await self.hass.async_add_executor_job(self.close_valve)
@final
async def async_handle_close_valve(self) -> None:
"""Close the valve."""
if self.supported_features & ValveEntityFeature.SET_POSITION:
await self.async_set_valve_position(0)
return
await self.async_close_valve()
async def async_toggle(self) -> None:
"""Toggle the entity."""
if self.supported_features & ValveEntityFeature.STOP and (
self.is_closing or self.is_opening
):
return await self.async_stop_valve()
if self.is_closed:
return await self.async_handle_open_valve()
if self.__is_last_toggle_direction_open:
return await self.async_handle_close_valve()
return await self.async_handle_open_valve()
def set_valve_position(self, position: int) -> None:
"""Move the valve to a specific position."""
raise NotImplementedError
async def async_set_valve_position(self, position: int) -> None:
"""Move the valve to a specific position."""
await self.hass.async_add_executor_job(self.set_valve_position, position)
def stop_valve(self) -> None:
"""Stop the valve."""
raise NotImplementedError
async def async_stop_valve(self) -> None:
"""Stop the valve."""
await self.hass.async_add_executor_job(self.stop_valve)