diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 92c961eb1482f9c5e9e3775f7b9c920274f02116..28991483cdacdf6a23802b5e9ea03ca4ffa809de 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 8fbd85ae288ac940630489b168f1e1fe05222c55..fe2b4aa4369746082509300d33aef491d096afc0 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 1fde6c80cd6252b203078c73793cfb9c424b520f..2f7aeda5c39b239b98a5bca08800c620a0b3fca9 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 1386182372215393fd45514a57549a628292cd97..5174be19760f22deea53042c834e2536287b507f 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 5cdb34883676d3021de95f4eb46ae23c0e5eaee3..c6694fce1096c7d32343b49297f11981b29b5d98 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 95527126a3049e659acec60cde5c6dc49f6e17f8..2320a4af8b753cc023e2049a9d13b2bb31e5b084 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"},