diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 92c961eb148..28991483cda 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -16,7 +16,9 @@ from homeassistant.loader import bind_hass from .config_entry import ( # noqa: F401 ScannerEntity, + ScannerEntityDescription, TrackerEntity, + TrackerEntityDescription, async_setup_entry, async_unload_entry, ) diff --git a/homeassistant/components/device_tracker/config_entry.py b/homeassistant/components/device_tracker/config_entry.py index 8fbd85ae288..fe2b4aa4369 100644 --- a/homeassistant/components/device_tracker/config_entry.py +++ b/homeassistant/components/device_tracker/config_entry.py @@ -24,7 +24,7 @@ from homeassistant.helpers.device_registry import ( EventDeviceRegistryUpdatedData, ) from homeassistant.helpers.dispatcher import async_dispatcher_send -from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_platform import EntityPlatform from homeassistant.helpers.typing import StateType @@ -198,6 +198,10 @@ class BaseTrackerEntity(Entity): return attr +class TrackerEntityDescription(EntityDescription, frozen_or_thawed=True): + """A class that describes tracker entities.""" + + CACHED_TRACKER_PROPERTIES_WITH_ATTR_ = { "latitude", "location_accuracy", @@ -211,6 +215,7 @@ class TrackerEntity( ): """Base class for a tracked device.""" + entity_description: TrackerEntityDescription _attr_latitude: float | None = None _attr_location_accuracy: int = 0 _attr_location_name: str | None = None @@ -285,6 +290,10 @@ class TrackerEntity( return attr +class ScannerEntityDescription(EntityDescription, frozen_or_thawed=True): + """A class that describes tracker entities.""" + + CACHED_SCANNER_PROPERTIES_WITH_ATTR_ = { "ip_address", "mac_address", @@ -297,6 +306,7 @@ class ScannerEntity( ): """Base class for a tracked device that is on a scanned network.""" + entity_description: ScannerEntityDescription _attr_hostname: str | None = None _attr_ip_address: str | None = None _attr_mac_address: str | None = None diff --git a/homeassistant/components/renault/device_tracker.py b/homeassistant/components/renault/device_tracker.py index 1fde6c80cd6..2f7aeda5c39 100644 --- a/homeassistant/components/renault/device_tracker.py +++ b/homeassistant/components/renault/device_tracker.py @@ -2,9 +2,14 @@ from __future__ import annotations +from dataclasses import dataclass + from renault_api.kamereon.models import KamereonVehicleLocationData -from homeassistant.components.device_tracker import TrackerEntity +from homeassistant.components.device_tracker import ( + TrackerEntity, + TrackerEntityDescription, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -12,6 +17,13 @@ from . import RenaultConfigEntry from .entity import RenaultDataEntity, RenaultDataEntityDescription +@dataclass(frozen=True, kw_only=True) +class RenaultTrackerEntityDescription( + TrackerEntityDescription, RenaultDataEntityDescription +): + """Class describing Renault tracker entities.""" + + async def async_setup_entry( hass: HomeAssistant, config_entry: RenaultConfigEntry, @@ -32,6 +44,8 @@ class RenaultDeviceTracker( ): """Mixin for device tracker specific attributes.""" + entity_description: RenaultTrackerEntityDescription + @property def latitude(self) -> float | None: """Return latitude value of the device.""" @@ -43,8 +57,8 @@ class RenaultDeviceTracker( return self.coordinator.data.gpsLongitude if self.coordinator.data else None -DEVICE_TRACKER_TYPES: tuple[RenaultDataEntityDescription, ...] = ( - RenaultDataEntityDescription( +DEVICE_TRACKER_TYPES: tuple[RenaultTrackerEntityDescription, ...] = ( + RenaultTrackerEntityDescription( key="location", coordinator="location", translation_key="location", diff --git a/homeassistant/components/starlink/device_tracker.py b/homeassistant/components/starlink/device_tracker.py index 13861823722..5174be19760 100644 --- a/homeassistant/components/starlink/device_tracker.py +++ b/homeassistant/components/starlink/device_tracker.py @@ -4,10 +4,12 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Any -from homeassistant.components.device_tracker import TrackerEntity +from homeassistant.components.device_tracker import ( + TrackerEntity, + TrackerEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ATTR_ALTITUDE, DOMAIN @@ -28,9 +30,7 @@ async def async_setup_entry( @dataclass(frozen=True, kw_only=True) -class StarlinkDeviceTrackerEntityDescription( # pylint: disable=hass-enforce-class-module - EntityDescription -): +class StarlinkDeviceTrackerEntityDescription(TrackerEntityDescription): """Describes a Starlink button entity.""" latitude_fn: Callable[[StarlinkData], float] diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 5cdb3488367..c6694fce109 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -21,6 +21,7 @@ from aiounifi.models.event import Event, EventKey from homeassistant.components.device_tracker import ( DOMAIN as DEVICE_TRACKER_DOMAIN, ScannerEntity, + ScannerEntityDescription, ) from homeassistant.core import Event as core_Event, HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -141,7 +142,9 @@ def async_device_heartbeat_timedelta_fn(hub: UnifiHub, obj_id: str) -> timedelta @dataclass(frozen=True, kw_only=True) -class UnifiTrackerEntityDescription(UnifiEntityDescription[HandlerT, ApiItemT]): +class UnifiTrackerEntityDescription( + UnifiEntityDescription[HandlerT, ApiItemT], ScannerEntityDescription +): """Class describing UniFi device tracker entity.""" heartbeat_timedelta_fn: Callable[[UnifiHub, str], timedelta] diff --git a/pylint/plugins/hass_enforce_class_module.py b/pylint/plugins/hass_enforce_class_module.py index 95527126a30..2320a4af8b7 100644 --- a/pylint/plugins/hass_enforce_class_module.py +++ b/pylint/plugins/hass_enforce_class_module.py @@ -36,7 +36,13 @@ _MODULES: dict[str, set[str]] = { "cover": {"CoverEntity", "CoverEntityDescription"}, "date": {"DateEntity", "DateEntityDescription"}, "datetime": {"DateTimeEntity", "DateTimeEntityDescription"}, - "device_tracker": {"DeviceTrackerEntity", "ScannerEntity", "TrackerEntity"}, + "device_tracker": { + "DeviceTrackerEntity", + "ScannerEntity", + "ScannerEntityDescription", + "TrackerEntity", + "TrackerEntityDescription", + }, "event": {"EventEntity", "EventEntityDescription"}, "fan": {"FanEntity", "FanEntityDescription"}, "geo_location": {"GeolocationEvent"},