Allow inheriting base component entity descriptions in frozen dataclasses (#105512)

Co-authored-by: J. Nick Koston <nick@koston.org>
pull/105841/head^2
Erik Montnemery 2023-12-16 10:33:50 +01:00 committed by GitHub
parent 47f8e08261
commit 104bcc64b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 35 additions and 85 deletions

View File

@ -1,7 +1,6 @@
"""Component to interface with an alarm control panel.""" """Component to interface with an alarm control panel."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import logging import logging
from typing import Any, Final, final from typing import Any, Final, final
@ -121,8 +120,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class AlarmControlPanelEntityDescription(EntityDescription, frozen_or_thawed=True):
class AlarmControlPanelEntityDescription(EntityDescription):
"""A class that describes alarm control panel entities.""" """A class that describes alarm control panel entities."""

View File

@ -1,7 +1,6 @@
"""Component to interface with binary sensors.""" """Component to interface with binary sensors."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import StrEnum from enum import StrEnum
import logging import logging
@ -176,8 +175,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class BinarySensorEntityDescription(EntityDescription, frozen_or_thawed=True):
class BinarySensorEntityDescription(EntityDescription):
"""A class that describes binary sensor entities.""" """A class that describes binary sensor entities."""
device_class: BinarySensorDeviceClass | None = None device_class: BinarySensorDeviceClass | None = None

View File

@ -93,7 +93,10 @@ def deserialize_entity_description(
descriptions_class: type[EntityDescription], data: dict[str, Any] descriptions_class: type[EntityDescription], data: dict[str, Any]
) -> EntityDescription: ) -> EntityDescription:
"""Deserialize an entity description.""" """Deserialize an entity description."""
# pylint: disable=protected-access
result: dict[str, Any] = {} result: dict[str, Any] = {}
if hasattr(descriptions_class, "_dataclass"):
descriptions_class = descriptions_class._dataclass
for field in cached_fields(descriptions_class): for field in cached_fields(descriptions_class):
field_name = field.name field_name = field.name
# It would be nice if field.type returned the actual # It would be nice if field.type returned the actual

View File

@ -1,7 +1,6 @@
"""Component to pressing a button as platforms.""" """Component to pressing a button as platforms."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import StrEnum from enum import StrEnum
import logging import logging
@ -73,8 +72,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class ButtonEntityDescription(EntityDescription, frozen_or_thawed=True):
class ButtonEntityDescription(EntityDescription):
"""A class that describes button entities.""" """A class that describes button entities."""
device_class: ButtonDeviceClass | None = None device_class: ButtonDeviceClass | None = None

View File

@ -5,7 +5,7 @@ import asyncio
import collections import collections
from collections.abc import Awaitable, Callable, Iterable from collections.abc import Awaitable, Callable, Iterable
from contextlib import suppress from contextlib import suppress
from dataclasses import asdict, dataclass from dataclasses import asdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import IntFlag from enum import IntFlag
from functools import partial from functools import partial
@ -132,8 +132,7 @@ CAMERA_SERVICE_RECORD: Final = {
} }
@dataclass class CameraEntityDescription(EntityDescription, frozen_or_thawed=True):
class CameraEntityDescription(EntityDescription):
"""A class that describes camera entities.""" """A class that describes camera entities."""

View File

@ -1,7 +1,6 @@
"""Provides functionality to interact with climate devices.""" """Provides functionality to interact with climate devices."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import functools as ft import functools as ft
import logging import logging
@ -201,8 +200,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class ClimateEntityDescription(EntityDescription, frozen_or_thawed=True):
class ClimateEntityDescription(EntityDescription):
"""A class that describes climate entities.""" """A class that describes climate entities."""

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import IntFlag, StrEnum from enum import IntFlag, StrEnum
import functools as ft import functools as ft
@ -212,8 +211,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class CoverEntityDescription(EntityDescription, frozen_or_thawed=True):
class CoverEntityDescription(EntityDescription):
"""A class that describes cover entities.""" """A class that describes cover entities."""
device_class: CoverDeviceClass | None = None device_class: CoverDeviceClass | None = None

View File

@ -1,7 +1,6 @@
"""Component to allow setting date as platforms.""" """Component to allow setting date as platforms."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import date, timedelta from datetime import date, timedelta
import logging import logging
from typing import final from typing import final
@ -62,8 +61,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class DateEntityDescription(EntityDescription, frozen_or_thawed=True):
class DateEntityDescription(EntityDescription):
"""A class that describes date entities.""" """A class that describes date entities."""

View File

@ -1,7 +1,6 @@
"""Component to allow setting date/time as platforms.""" """Component to allow setting date/time as platforms."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
import logging import logging
from typing import final from typing import final
@ -71,8 +70,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class DateTimeEntityDescription(EntityDescription, frozen_or_thawed=True):
class DateTimeEntityDescription(EntityDescription):
"""A class that describes date/time entities.""" """A class that describes date/time entities."""

View File

@ -71,8 +71,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class EventEntityDescription(EntityDescription, frozen_or_thawed=True):
class EventEntityDescription(EntityDescription):
"""A class that describes event entities.""" """A class that describes event entities."""
device_class: EventDeviceClass | None = None device_class: EventDeviceClass | None = None

View File

@ -1,7 +1,6 @@
"""Provides functionality to interact with fans.""" """Provides functionality to interact with fans."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import IntFlag from enum import IntFlag
import functools as ft import functools as ft
@ -187,8 +186,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class FanEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
class FanEntityDescription(ToggleEntityDescription):
"""A class that describes fan entities.""" """A class that describes fan entities."""

View File

@ -1,7 +1,6 @@
"""Provides functionality to interact with humidifier devices.""" """Provides functionality to interact with humidifier devices."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import StrEnum from enum import StrEnum
import logging import logging
@ -124,8 +123,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class HumidifierEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
class HumidifierEntityDescription(ToggleEntityDescription):
"""A class that describes humidifier entities.""" """A class that describes humidifier entities."""
device_class: HumidifierDeviceClass | None = None device_class: HumidifierDeviceClass | None = None

View File

@ -44,8 +44,7 @@ _RND: Final = SystemRandom()
GET_IMAGE_TIMEOUT: Final = 10 GET_IMAGE_TIMEOUT: Final = 10
@dataclass class ImageEntityDescription(EntityDescription, frozen_or_thawed=True):
class ImageEntityDescription(EntityDescription):
"""A class that describes image entities.""" """A class that describes image entities."""

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import StrEnum from enum import StrEnum
import logging import logging
@ -120,8 +119,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True return True
@dataclass class ImageProcessingEntityDescription(EntityDescription, frozen_or_thawed=True):
class ImageProcessingEntityDescription(EntityDescription):
"""A class that describes sensor entities.""" """A class that describes sensor entities."""
device_class: ImageProcessingDeviceClass | None = None device_class: ImageProcessingDeviceClass | None = None

View File

@ -1,7 +1,6 @@
"""The lawn mower integration.""" """The lawn mower integration."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import logging import logging
from typing import final from typing import final
@ -65,8 +64,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class LawnMowerEntityEntityDescription(EntityDescription, frozen_or_thawed=True):
class LawnMowerEntityEntityDescription(EntityDescription):
"""A class that describes lawn mower entities.""" """A class that describes lawn mower entities."""

View File

@ -816,8 +816,7 @@ class Profiles:
params.setdefault(ATTR_TRANSITION, profile.transition) params.setdefault(ATTR_TRANSITION, profile.transition)
@dataclasses.dataclass class LightEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
class LightEntityDescription(ToggleEntityDescription):
"""A class that describes binary sensor entities.""" """A class that describes binary sensor entities."""

View File

@ -1,7 +1,6 @@
"""Component to interface with locks that can be controlled remotely.""" """Component to interface with locks that can be controlled remotely."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import IntFlag from enum import IntFlag
import functools as ft import functools as ft
@ -101,8 +100,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class LockEntityDescription(EntityDescription, frozen_or_thawed=True):
class LockEntityDescription(EntityDescription):
"""A class that describes lock entities.""" """A class that describes lock entities."""

View File

@ -5,7 +5,6 @@ import asyncio
import collections import collections
from collections.abc import Callable from collections.abc import Callable
from contextlib import suppress from contextlib import suppress
from dataclasses import dataclass
import datetime as dt import datetime as dt
from enum import StrEnum from enum import StrEnum
import functools as ft import functools as ft
@ -449,8 +448,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class MediaPlayerEntityDescription(EntityDescription, frozen_or_thawed=True):
class MediaPlayerEntityDescription(EntityDescription):
"""A class that describes media player entities.""" """A class that describes media player entities."""
device_class: MediaPlayerDeviceClass | None = None device_class: MediaPlayerDeviceClass | None = None

View File

@ -120,8 +120,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclasses.dataclass class NumberEntityDescription(EntityDescription, frozen_or_thawed=True):
class NumberEntityDescription(EntityDescription):
"""A class that describes number entities.""" """A class that describes number entities."""
device_class: NumberDeviceClass | None = None device_class: NumberDeviceClass | None = None

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Iterable from collections.abc import Iterable
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import IntFlag from enum import IntFlag
import functools as ft import functools as ft
@ -155,8 +154,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class RemoteEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
class RemoteEntityDescription(ToggleEntityDescription):
"""A class that describes remote entities.""" """A class that describes remote entities."""

View File

@ -1,7 +1,6 @@
"""Component to allow selecting an option from a list as platforms.""" """Component to allow selecting an option from a list as platforms."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import logging import logging
from typing import Any, final from typing import Any, final
@ -118,8 +117,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class SelectEntityDescription(EntityDescription, frozen_or_thawed=True):
class SelectEntityDescription(EntityDescription):
"""A class that describes select entities.""" """A class that describes select entities."""
options: list[str] | None = None options: list[str] | None = None

View File

@ -136,8 +136,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class SensorEntityDescription(EntityDescription, frozen_or_thawed=True):
class SensorEntityDescription(EntityDescription):
"""A class that describes sensor entities.""" """A class that describes sensor entities."""
device_class: SensorDeviceClass | None = None device_class: SensorDeviceClass | None = None

View File

@ -1,7 +1,6 @@
"""Component to interface with various sirens/chimes.""" """Component to interface with various sirens/chimes."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import logging import logging
from typing import Any, TypedDict, cast, final from typing import Any, TypedDict, cast, final
@ -149,8 +148,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class SirenEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
class SirenEntityDescription(ToggleEntityDescription):
"""A class that describes siren entities.""" """A class that describes siren entities."""
available_tones: list[int | str] | dict[int, str] | None = None available_tones: list[int | str] | dict[int, str] | None = None

View File

@ -1,7 +1,6 @@
"""Component to interface with switches that can be controlled remotely.""" """Component to interface with switches that can be controlled remotely."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import StrEnum from enum import StrEnum
import logging import logging
@ -89,8 +88,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class SwitchEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
class SwitchEntityDescription(ToggleEntityDescription):
"""A class that describes switch entities.""" """A class that describes switch entities."""
device_class: SwitchDeviceClass | None = None device_class: SwitchDeviceClass | None = None

View File

@ -98,8 +98,7 @@ class TextMode(StrEnum):
TEXT = "text" TEXT = "text"
@dataclass class TextEntityDescription(EntityDescription, frozen_or_thawed=True):
class TextEntityDescription(EntityDescription):
"""A class that describes text entities.""" """A class that describes text entities."""
native_min: int = 0 native_min: int = 0

View File

@ -1,7 +1,6 @@
"""Component to allow setting time as platforms.""" """Component to allow setting time as platforms."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import time, timedelta from datetime import time, timedelta
import logging import logging
from typing import final from typing import final
@ -62,8 +61,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class TimeEntityDescription(EntityDescription, frozen_or_thawed=True):
class TimeEntityDescription(EntityDescription):
"""A class that describes time entities.""" """A class that describes time entities."""

View File

@ -1,7 +1,6 @@
"""Component to allow for providing device or service updates.""" """Component to allow for providing device or service updates."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import StrEnum from enum import StrEnum
from functools import lru_cache from functools import lru_cache
@ -175,8 +174,7 @@ async def async_clear_skipped(entity: UpdateEntity, service_call: ServiceCall) -
await entity.async_clear_skipped() await entity.async_clear_skipped()
@dataclass class UpdateEntityDescription(EntityDescription, frozen_or_thawed=True):
class UpdateEntityDescription(EntityDescription):
"""A class that describes update entities.""" """A class that describes update entities."""
device_class: UpdateDeviceClass | None = None device_class: UpdateDeviceClass | None = None

View File

@ -3,7 +3,6 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Mapping from collections.abc import Mapping
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import IntFlag from enum import IntFlag
from functools import partial from functools import partial
@ -367,8 +366,7 @@ class _BaseVacuum(Entity):
) )
@dataclass class VacuumEntityDescription(ToggleEntityDescription, frozen_or_thawed=True):
class VacuumEntityDescription(ToggleEntityDescription):
"""A class that describes vacuum entities.""" """A class that describes vacuum entities."""
@ -490,8 +488,7 @@ class VacuumEntity(_BaseVacuum, ToggleEntity):
await self.hass.async_add_executor_job(partial(self.start_pause, **kwargs)) await self.hass.async_add_executor_job(partial(self.start_pause, **kwargs))
@dataclass class StateVacuumEntityDescription(EntityDescription, frozen_or_thawed=True):
class StateVacuumEntityDescription(EntityDescription):
"""A class that describes vacuum entities.""" """A class that describes vacuum entities."""

View File

@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping from collections.abc import Mapping
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from enum import IntFlag from enum import IntFlag
import functools as ft import functools as ft
@ -156,8 +155,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class WaterHeaterEntityEntityDescription(EntityDescription, frozen_or_thawed=True):
class WaterHeaterEntityEntityDescription(EntityDescription):
"""A class that describes water heater entities.""" """A class that describes water heater entities."""

View File

@ -5,7 +5,6 @@ import abc
import asyncio import asyncio
from collections.abc import Callable, Iterable from collections.abc import Callable, Iterable
from contextlib import suppress from contextlib import suppress
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from functools import partial from functools import partial
import logging import logging
@ -251,8 +250,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await component.async_unload_entry(entry) return await component.async_unload_entry(entry)
@dataclass class WeatherEntityDescription(EntityDescription, frozen_or_thawed=True):
class WeatherEntityDescription(EntityDescription):
"""A class that describes weather entities.""" """A class that describes weather entities."""

View File

@ -1313,8 +1313,7 @@ class Entity(ABC):
) )
@dataclasses.dataclass(slots=True) class ToggleEntityDescription(EntityDescription, frozen_or_thawed=True):
class ToggleEntityDescription(EntityDescription):
"""A class that describes toggle entities.""" """A class that describes toggle entities."""