Extract shared base entity for Subaru (#176246)

This commit is contained in:
John Pettitt
2026-07-15 14:02:16 +02:00
committed by GitHub
parent 77ee9e2ca5
commit 730735a81b
5 changed files with 63 additions and 50 deletions
+3 -8
View File
@@ -10,15 +10,14 @@ from homeassistant.components.button import ButtonEntity, ButtonEntityDescriptio
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import get_device_info
from .const import (
SERVICE_REMOTE_START,
SERVICE_REMOTE_STOP,
VEHICLE_HAS_EV,
VEHICLE_HAS_REMOTE_START,
VEHICLE_VIN,
)
from .coordinator import SubaruConfigEntry, SubaruDataUpdateCoordinator
from .entity import SubaruEntity
from .remote_service import async_call_remote_service
@@ -59,10 +58,9 @@ async def async_setup_entry(
)
class SubaruButton(ButtonEntity):
class SubaruButton(SubaruEntity, ButtonEntity):
"""Class for a Subaru button."""
_attr_has_entity_name = True
entity_description: SubaruButtonEntityDescription
def __init__(
@@ -73,13 +71,10 @@ class SubaruButton(ButtonEntity):
description: SubaruButtonEntityDescription,
) -> None:
"""Initialize the button for the vehicle."""
super().__init__(vehicle_info, description.key)
self.controller = controller
self.coordinator = coordinator
self.vehicle_info = vehicle_info
self.entity_description = description
vin = vehicle_info[VEHICLE_VIN]
self._attr_unique_id = f"{vin}_{description.key}"
self._attr_device_info = get_device_info(vehicle_info)
@override
async def async_press(self) -> None:
@@ -7,11 +7,10 @@ from subarulink.const import LATITUDE, LONGITUDE, TIMESTAMP
from homeassistant.components.device_tracker import TrackerEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import get_device_info
from .const import VEHICLE_HAS_REMOTE_SERVICE, VEHICLE_STATUS, VEHICLE_VIN
from .const import VEHICLE_HAS_REMOTE_SERVICE, VEHICLE_STATUS
from .coordinator import SubaruConfigEntry, SubaruDataUpdateCoordinator
from .entity import SubaruCoordinatorEntity
async def async_setup_entry(
@@ -29,23 +28,17 @@ async def async_setup_entry(
)
class SubaruDeviceTracker(
CoordinatorEntity[SubaruDataUpdateCoordinator], TrackerEntity
):
class SubaruDeviceTracker(SubaruCoordinatorEntity, TrackerEntity):
"""Class for Subaru device tracker."""
_attr_translation_key = "location"
_attr_has_entity_name = True
_attr_name = None
def __init__(
self, vehicle_info: dict, coordinator: SubaruDataUpdateCoordinator
) -> None:
"""Initialize the device tracker."""
super().__init__(coordinator)
self.vin = vehicle_info[VEHICLE_VIN]
self._attr_device_info = get_device_info(vehicle_info)
self._attr_unique_id = f"{self.vin}_location"
super().__init__(vehicle_info, coordinator, "location")
@property
@override
@@ -72,8 +65,8 @@ class SubaruDeviceTracker(
@property
@override
def available(self) -> bool:
"""Return if entity is available."""
if vehicle_data := self.coordinator.data.get(self.vin):
if status := vehicle_data.get(VEHICLE_STATUS):
return status.keys() & {LATITUDE, LONGITUDE, TIMESTAMP}
return False
"""Return if available; not gated on last_update_success, only on the relevant status keys being present."""
if not (vehicle_data := (self.coordinator.data or {}).get(self.vin)):
return False
status = vehicle_data.get(VEHICLE_STATUS) or {}
return bool(status.keys() & {LATITUDE, LONGITUDE, TIMESTAMP})
+45
View File
@@ -0,0 +1,45 @@
"""Base entities for the Subaru integration."""
from typing import Any, override
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import get_device_info
from .const import VEHICLE_VIN
from .coordinator import SubaruDataUpdateCoordinator
class SubaruEntity(Entity):
"""Base class for Subaru entities: device_info, unique_id, has_entity_name."""
_attr_has_entity_name = True
def __init__(self, vehicle_info: dict[str, Any], unique_id_suffix: str) -> None:
"""Initialize the entity from the vehicle_info dict."""
self.vehicle_info = vehicle_info
self.vin: str = vehicle_info[VEHICLE_VIN]
self._attr_device_info = get_device_info(vehicle_info)
self._attr_unique_id = f"{self.vin}_{unique_id_suffix}"
class SubaruCoordinatorEntity(
CoordinatorEntity[SubaruDataUpdateCoordinator], SubaruEntity
):
"""Base class for coordinator-backed Subaru entities."""
def __init__(
self,
vehicle_info: dict[str, Any],
coordinator: SubaruDataUpdateCoordinator,
unique_id_suffix: str,
) -> None:
"""Initialize the coordinator-backed entity."""
super().__init__(coordinator)
SubaruEntity.__init__(self, vehicle_info, unique_id_suffix)
@property
@override
def available(self) -> bool:
"""Return if available; also gates on data for this vehicle being present."""
return super().available and self.vin in self.coordinator.data
+3 -8
View File
@@ -11,7 +11,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import get_device_info
from .const import (
ATTR_DOOR,
SERVICE_UNLOCK_SPECIFIC_DOOR,
@@ -19,9 +18,9 @@ from .const import (
UNLOCK_VALID_DOORS,
VEHICLE_HAS_REMOTE_SERVICE,
VEHICLE_NAME,
VEHICLE_VIN,
)
from .coordinator import SubaruConfigEntry
from .entity import SubaruEntity
from .remote_service import async_call_remote_service
_LOGGER = logging.getLogger(__name__)
@@ -50,7 +49,7 @@ async def async_setup_entry(
)
class SubaruLock(LockEntity):
class SubaruLock(SubaruEntity, LockEntity):
"""Representation of a Subaru door lock.
Note that the Subaru API currently does not support
@@ -58,17 +57,13 @@ class SubaruLock(LockEntity):
always unknown.
"""
_attr_has_entity_name = True
_attr_translation_key = "door_locks"
def __init__(self, vehicle_info, controller):
"""Initialize the locks for the vehicle."""
super().__init__(vehicle_info, "door_locks")
self.controller = controller
self.vehicle_info = vehicle_info
vin = vehicle_info[VEHICLE_VIN]
self.car_name = vehicle_info[VEHICLE_NAME]
self._attr_unique_id = f"{vin}_door_locks"
self._attr_device_info = get_device_info(vehicle_info)
@override
async def async_lock(self, **kwargs: Any) -> None:
+3 -18
View File
@@ -27,11 +27,9 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.unit_conversion import DistanceConverter, VolumeConverter
from homeassistant.util.unit_system import METRIC_SYSTEM
from . import get_device_info
from .const import (
API_GEN_2,
API_GEN_3,
@@ -42,9 +40,9 @@ from .const import (
VEHICLE_HAS_EV,
VEHICLE_HEALTH,
VEHICLE_STATUS,
VEHICLE_VIN,
)
from .coordinator import SubaruConfigEntry, SubaruDataUpdateCoordinator
from .entity import SubaruCoordinatorEntity
_LOGGER = logging.getLogger(__name__)
@@ -260,10 +258,9 @@ def create_vehicle_sensors(
]
class SubaruSensor(CoordinatorEntity[SubaruDataUpdateCoordinator], SensorEntity):
class SubaruSensor(SubaruCoordinatorEntity, SensorEntity):
"""Class for Subaru sensors."""
_attr_has_entity_name = True
entity_description: SubaruSensorEntityDescription
def __init__(
@@ -273,11 +270,8 @@ class SubaruSensor(CoordinatorEntity[SubaruDataUpdateCoordinator], SensorEntity)
description: SubaruSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.vin = vehicle_info[VEHICLE_VIN]
super().__init__(vehicle_info, coordinator, description.key)
self.entity_description = description
self._attr_device_info = get_device_info(vehicle_info)
self._attr_unique_id = f"{self.vin}_{description.key}"
@property
@override
@@ -312,15 +306,6 @@ class SubaruSensor(CoordinatorEntity[SubaruDataUpdateCoordinator], SensorEntity)
return FUEL_CONSUMPTION_LITERS_PER_HUNDRED_KILOMETERS
return self.entity_description.native_unit_of_measurement
@property
@override
def available(self) -> bool:
"""Return if entity is available."""
last_update_success = super().available
if last_update_success and self.vin not in self.coordinator.data:
return False
return last_update_success
async def _async_migrate_entries(
hass: HomeAssistant, config_entry: ConfigEntry