Change Entity.name default to UNDEFINED (#94574)
* Change Entity.name default to UNDEFINED * Update typing * Update Pylint plugin * Update TTS test
This commit is contained in:
parent
d369d679c7
commit
334dacc322
17 changed files with 277 additions and 67 deletions
|
@ -1,7 +1,7 @@
|
|||
"""Support for Adax wifi-enabled home heaters."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from adax import Adax
|
||||
from adax_local import Adax as AdaxLocal
|
||||
|
@ -79,7 +79,10 @@ class AdaxDevice(ClimateEntity):
|
|||
self._attr_unique_id = f"{heater_data['homeId']}_{heater_data['id']}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, heater_data["id"])},
|
||||
name=self.name,
|
||||
# Instead of setting the device name to the entity name, adax
|
||||
# should be updated to set has_entity_name = True, and set the entity
|
||||
# name to None
|
||||
name=cast(str | None, self.name),
|
||||
manufacturer="Adax",
|
||||
)
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.typing import UndefinedType
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
|
@ -14,7 +16,7 @@ def service_signal(service: str, *args: str) -> str:
|
|||
def log_update_error(
|
||||
logger: logging.Logger,
|
||||
action: str,
|
||||
name: str | None,
|
||||
name: str | UndefinedType | None,
|
||||
entity_type: str,
|
||||
error: Exception,
|
||||
level: int = logging.ERROR,
|
||||
|
|
|
@ -330,7 +330,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||
trace_config: ConfigType,
|
||||
) -> None:
|
||||
"""Initialize an automation entity."""
|
||||
self._attr_name = name
|
||||
self._name = name
|
||||
self._trigger_config = trigger_config
|
||||
self._async_detach_triggers: CALLBACK_TYPE | None = None
|
||||
self._cond_func = cond_func
|
||||
|
@ -348,6 +348,11 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||
self._trace_config = trace_config
|
||||
self._attr_unique_id = automation_id
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the entity."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the entity state attributes."""
|
||||
|
|
|
@ -115,10 +115,15 @@ class CupsSensor(SensorEntity):
|
|||
def __init__(self, data: CupsData, printer_name: str) -> None:
|
||||
"""Initialize the CUPS sensor."""
|
||||
self.data = data
|
||||
self._attr_name = printer_name
|
||||
self._name = printer_name
|
||||
self._printer: dict[str, Any] | None = None
|
||||
self._attr_available = False
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the entity."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state of the sensor."""
|
||||
|
@ -149,7 +154,6 @@ class CupsSensor(SensorEntity):
|
|||
def update(self) -> None:
|
||||
"""Get the latest data and updates the states."""
|
||||
self.data.update()
|
||||
assert self.name is not None
|
||||
assert self.data.printers is not None
|
||||
self._printer = self.data.printers.get(self.name)
|
||||
self._attr_available = self.data.available
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""Base DirecTV Entity."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
|
||||
from directv import DIRECTV
|
||||
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
@ -24,7 +26,10 @@ class DIRECTVEntity(Entity):
|
|||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device_id)},
|
||||
manufacturer=self.dtv.device.info.brand,
|
||||
name=self.name,
|
||||
# Instead of setting the device name to the entity name, directv
|
||||
# should be updated to set has_entity_name = True, and set the entity
|
||||
# name to None
|
||||
name=cast(str | None, self.name),
|
||||
sw_version=self.dtv.device.info.version,
|
||||
via_device=(DOMAIN, self.dtv.device.info.receiver_id),
|
||||
)
|
||||
|
|
|
@ -6,7 +6,7 @@ from collections.abc import Awaitable, Callable, Coroutine
|
|||
from datetime import datetime
|
||||
from functools import wraps
|
||||
import logging
|
||||
from typing import Any, Concatenate, ParamSpec, TypeVar
|
||||
from typing import Any, Concatenate, ParamSpec, TypeVar, cast
|
||||
|
||||
import httpx
|
||||
from iaqualink.client import AqualinkClient
|
||||
|
@ -243,6 +243,8 @@ class AqualinkEntity(Entity):
|
|||
identifiers={(DOMAIN, self.unique_id)},
|
||||
manufacturer=self.dev.manufacturer,
|
||||
model=self.dev.model,
|
||||
name=self.name,
|
||||
# Instead of setting the device name to the entity name, iaqualink
|
||||
# should be updated to set has_entity_name = True
|
||||
name=cast(str | None, self.name),
|
||||
via_device=(DOMAIN, self.dev.system.serial),
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
@ -28,7 +28,9 @@ class KaleidescapeEntity(Entity):
|
|||
self._attr_name = f"{KALEIDESCAPE_NAME} {device.system.friendly_name}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(KALEIDESCAPE_DOMAIN, self._device.serial_number)},
|
||||
name=self.name,
|
||||
# Instead of setting the device name to the entity name, kaleidescape
|
||||
# should be updated to set has_entity_name = True
|
||||
name=cast(str | None, self.name),
|
||||
model=self._device.system.type,
|
||||
manufacturer=KALEIDESCAPE_NAME,
|
||||
sw_version=f"{self._device.system.kos_version}",
|
||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||
from collections.abc import Callable
|
||||
from functools import wraps
|
||||
import logging
|
||||
from typing import Any, Concatenate, ParamSpec, TypeVar
|
||||
from typing import Any, Concatenate, ParamSpec, TypeVar, cast
|
||||
|
||||
import plexapi.exceptions
|
||||
import requests.exceptions
|
||||
|
@ -535,7 +535,10 @@ class PlexMediaPlayer(MediaPlayerEntity):
|
|||
identifiers={(DOMAIN, self.machine_identifier)},
|
||||
manufacturer=self.device_platform or "Plex",
|
||||
model=self.device_product or self.device_make,
|
||||
name=self.name,
|
||||
# Instead of setting the device name to the entity name, plex
|
||||
# should be updated to set has_entity_name = True, and set the entity
|
||||
# name to None
|
||||
name=cast(str | None, self.name),
|
||||
sw_version=self.device_version,
|
||||
via_device=(DOMAIN, self.plex_server.machine_identifier),
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from roonapi import split_media_path
|
||||
import voluptuous as vol
|
||||
|
@ -159,7 +159,10 @@ class RoonDevice(MediaPlayerEntity):
|
|||
dev_model = self.player_data["source_controls"][0].get("display_name")
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self.unique_id)},
|
||||
name=self.name,
|
||||
# Instead of setting the device name to the entity name, roon
|
||||
# should be updated to set has_entity_name = True, and set the entity
|
||||
# name to None
|
||||
name=cast(str | None, self.name),
|
||||
manufacturer="RoonLabs",
|
||||
model=dev_model,
|
||||
via_device=(DOMAIN, self._server.roon_id),
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""Base SamsungTV Entity."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_MAC, CONF_MODEL, CONF_NAME
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
@ -20,7 +22,9 @@ class SamsungTVEntity(Entity):
|
|||
self._attr_name = config_entry.data.get(CONF_NAME)
|
||||
self._attr_unique_id = config_entry.unique_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
name=self.name,
|
||||
# Instead of setting the device name to the entity name, samsungtv
|
||||
# should be updated to set has_entity_name = True
|
||||
name=cast(str | None, self.name),
|
||||
manufacturer=config_entry.data.get(CONF_MANUFACTURER),
|
||||
model=config_entry.data.get(CONF_MODEL),
|
||||
)
|
||||
|
|
|
@ -44,7 +44,7 @@ from homeassistant.helpers.entity_component import EntityComponent
|
|||
from homeassistant.helpers.event import async_call_later
|
||||
from homeassistant.helpers.network import get_url
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.helpers.typing import UNDEFINED, ConfigType
|
||||
from homeassistant.util import dt as dt_util, language as language_util
|
||||
|
||||
from .const import (
|
||||
|
@ -610,7 +610,7 @@ class SpeechManager:
|
|||
|
||||
async def get_tts_data() -> str:
|
||||
"""Handle data available."""
|
||||
if engine_instance.name is None:
|
||||
if engine_instance.name is None or engine_instance.name is UNDEFINED:
|
||||
raise HomeAssistantError("TTS engine name is not set.")
|
||||
|
||||
if isinstance(engine_instance, Provider):
|
||||
|
|
|
@ -15,6 +15,7 @@ from homeassistant.const import EntityCategory, Platform, UnitOfMass, UnitOfTemp
|
|||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import UndefinedType
|
||||
|
||||
from .core import discovery
|
||||
from .core.const import (
|
||||
|
@ -334,7 +335,7 @@ class ZhaNumber(ZhaEntity, NumberEntity):
|
|||
return super().native_step
|
||||
|
||||
@property
|
||||
def name(self) -> str | None:
|
||||
def name(self) -> str | UndefinedType | None:
|
||||
"""Return the name of the number entity."""
|
||||
description = self._analog_output_cluster_handler.description
|
||||
if description is not None and len(description) > 0:
|
||||
|
|
|
@ -258,6 +258,9 @@ class Entity(ABC):
|
|||
# it should be using async_write_ha_state.
|
||||
_async_update_ha_state_reported = False
|
||||
|
||||
# If we reported this entity is implicitly using device name
|
||||
_implicit_device_name_reported = False
|
||||
|
||||
# If we reported this entity was added without its platform set
|
||||
_no_platform_reported = False
|
||||
|
||||
|
@ -319,6 +322,53 @@ class Entity(ABC):
|
|||
"""Return a unique ID."""
|
||||
return self._attr_unique_id
|
||||
|
||||
@property
|
||||
def use_device_name(self) -> bool:
|
||||
"""Return if this entity does not have its own name.
|
||||
|
||||
Should be True if the entity represents the single main feature of a device.
|
||||
"""
|
||||
|
||||
def report_implicit_device_name() -> None:
|
||||
"""Report entities which use implicit device name."""
|
||||
if self._implicit_device_name_reported:
|
||||
return
|
||||
report_issue = self._suggest_report_issue()
|
||||
_LOGGER.warning(
|
||||
(
|
||||
"Entity %s (%s) is implicitly using device name by not setting its "
|
||||
"name. Instead, the name should be set to None, please %s"
|
||||
),
|
||||
self.entity_id,
|
||||
type(self),
|
||||
report_issue,
|
||||
)
|
||||
self._implicit_device_name_reported = True
|
||||
|
||||
if hasattr(self, "_attr_name"):
|
||||
return not self._attr_name
|
||||
|
||||
if name_translation_key := self._name_translation_key():
|
||||
if name_translation_key in self.platform.platform_translations:
|
||||
return False
|
||||
|
||||
if hasattr(self, "entity_description"):
|
||||
if not (name := self.entity_description.name):
|
||||
return True
|
||||
if name is UNDEFINED:
|
||||
# Backwards compatibility with leaving EntityDescription.name unassigned
|
||||
# for device name.
|
||||
# Deprecated in HA Core 2023.6, remove in HA Core 2023.9
|
||||
report_implicit_device_name()
|
||||
return True
|
||||
return False
|
||||
if self.name is UNDEFINED:
|
||||
# Backwards compatibility with not overriding name property for device name.
|
||||
# Deprecated in HA Core 2023.6, remove in HA Core 2023.9
|
||||
report_implicit_device_name()
|
||||
return True
|
||||
return not self.name
|
||||
|
||||
@property
|
||||
def has_entity_name(self) -> bool:
|
||||
"""Return if the name of the entity is describing only the entity itself."""
|
||||
|
@ -344,16 +394,23 @@ class Entity(ABC):
|
|||
"""Return True if an unnamed entity should be named by its device class."""
|
||||
return False
|
||||
|
||||
def _name_translation_key(self) -> str | None:
|
||||
"""Return translation key for entity name."""
|
||||
if self.translation_key is None:
|
||||
return None
|
||||
return (
|
||||
f"component.{self.platform.platform_name}.entity.{self.platform.domain}"
|
||||
f".{self.translation_key}.name"
|
||||
)
|
||||
|
||||
@property
|
||||
def name(self) -> str | None:
|
||||
def name(self) -> str | UndefinedType | None:
|
||||
"""Return the name of the entity."""
|
||||
if hasattr(self, "_attr_name"):
|
||||
return self._attr_name
|
||||
if self.translation_key is not None and self.has_entity_name:
|
||||
name_translation_key = (
|
||||
f"component.{self.platform.platform_name}.entity.{self.platform.domain}"
|
||||
f".{self.translation_key}.name"
|
||||
)
|
||||
if self.has_entity_name and (
|
||||
name_translation_key := self._name_translation_key()
|
||||
):
|
||||
if name_translation_key in self.platform.platform_translations:
|
||||
name: str = self.platform.platform_translations[name_translation_key]
|
||||
return name
|
||||
|
@ -361,15 +418,13 @@ class Entity(ABC):
|
|||
description_name = self.entity_description.name
|
||||
if description_name is UNDEFINED and self._default_to_device_class_name():
|
||||
return self._device_class_name()
|
||||
if description_name is not UNDEFINED:
|
||||
return description_name
|
||||
return None
|
||||
return description_name
|
||||
|
||||
# The entity has no name set by _attr_name, translation_key or entity_description
|
||||
# Check if the entity should be named by its device class
|
||||
if self._default_to_device_class_name():
|
||||
return self._device_class_name()
|
||||
return None
|
||||
return UNDEFINED
|
||||
|
||||
@property
|
||||
def state(self) -> StateType:
|
||||
|
@ -653,16 +708,20 @@ class Entity(ABC):
|
|||
If has_entity_name is False, this returns self.name
|
||||
If has_entity_name is True, this returns device.name + self.name
|
||||
"""
|
||||
name = self.name
|
||||
if name is UNDEFINED:
|
||||
name = None
|
||||
|
||||
if not self.has_entity_name or not self.registry_entry:
|
||||
return self.name
|
||||
return name
|
||||
|
||||
device_registry = dr.async_get(self.hass)
|
||||
if not (device_id := self.registry_entry.device_id) or not (
|
||||
device_entry := device_registry.async_get(device_id)
|
||||
):
|
||||
return self.name
|
||||
return name
|
||||
|
||||
if not (name := self.name):
|
||||
if self.use_device_name:
|
||||
return device_entry.name_by_user or device_entry.name
|
||||
return f"{device_entry.name_by_user or device_entry.name} {name}"
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ from .device_registry import DeviceRegistry
|
|||
from .entity_registry import EntityRegistry, RegistryEntryDisabler, RegistryEntryHider
|
||||
from .event import async_call_later, async_track_time_interval
|
||||
from .issue_registry import IssueSeverity, async_create_issue
|
||||
from .typing import ConfigType, DiscoveryInfoType
|
||||
from .typing import UNDEFINED, ConfigType, DiscoveryInfoType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .entity import Entity
|
||||
|
@ -552,6 +552,10 @@ class EntityPlatform:
|
|||
suggested_object_id: str | None = None
|
||||
generate_new_entity_id = False
|
||||
|
||||
entity_name = entity.name
|
||||
if entity_name is UNDEFINED:
|
||||
entity_name = None
|
||||
|
||||
# Get entity_id from unique ID registration
|
||||
if entity.unique_id is not None:
|
||||
registered_entity_id = entity_registry.async_get_entity_id(
|
||||
|
@ -645,12 +649,12 @@ class EntityPlatform:
|
|||
else:
|
||||
if device and entity.has_entity_name:
|
||||
device_name = device.name_by_user or device.name
|
||||
if not entity.name:
|
||||
if entity.use_device_name:
|
||||
suggested_object_id = device_name
|
||||
else:
|
||||
suggested_object_id = f"{device_name} {entity.name}"
|
||||
suggested_object_id = f"{device_name} {entity_name}"
|
||||
if not suggested_object_id:
|
||||
suggested_object_id = entity.name
|
||||
suggested_object_id = entity_name
|
||||
|
||||
if self.entity_namespace is not None:
|
||||
suggested_object_id = f"{self.entity_namespace} {suggested_object_id}"
|
||||
|
@ -678,7 +682,7 @@ class EntityPlatform:
|
|||
known_object_ids=self.entities.keys(),
|
||||
original_device_class=entity.device_class,
|
||||
original_icon=entity.icon,
|
||||
original_name=entity.name,
|
||||
original_name=entity_name,
|
||||
suggested_object_id=suggested_object_id,
|
||||
supported_features=entity.supported_features,
|
||||
translation_key=entity.translation_key,
|
||||
|
@ -705,7 +709,7 @@ class EntityPlatform:
|
|||
# Generate entity ID
|
||||
if entity.entity_id is None or generate_new_entity_id:
|
||||
suggested_object_id = (
|
||||
suggested_object_id or entity.name or DEVICE_DEFAULT_NAME
|
||||
suggested_object_id or entity_name or DEVICE_DEFAULT_NAME
|
||||
)
|
||||
|
||||
if self.entity_namespace is not None:
|
||||
|
@ -732,7 +736,7 @@ class EntityPlatform:
|
|||
self.logger.debug(
|
||||
"Not adding entity %s because it's disabled",
|
||||
entry.name
|
||||
or entity.name
|
||||
or entity_name
|
||||
or f'"{self.platform_name} {entity.unique_id}"',
|
||||
)
|
||||
entity.add_to_platform_abort()
|
||||
|
|
|
@ -573,7 +573,7 @@ _ENTITY_MATCH: list[TypeHintMatch] = [
|
|||
),
|
||||
TypeHintMatch(
|
||||
function_name="name",
|
||||
return_type=["str", None],
|
||||
return_type=["str", "UndefinedType", None],
|
||||
),
|
||||
TypeHintMatch(
|
||||
function_name="state",
|
||||
|
|
|
@ -22,6 +22,7 @@ from homeassistant.config_entries import ConfigEntryState
|
|||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.typing import UNDEFINED
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.util.network import normalize_url
|
||||
|
@ -68,7 +69,7 @@ async def test_default_entity_attributes() -> None:
|
|||
entity = DefaultEntity()
|
||||
|
||||
assert entity.hass is None
|
||||
assert entity.name is None
|
||||
assert entity.name is UNDEFINED
|
||||
assert entity.default_language == DEFAULT_LANG
|
||||
assert entity.supported_languages == SUPPORT_LANGUAGES
|
||||
assert entity.supported_options is None
|
||||
|
|
|
@ -17,6 +17,7 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import Context, HomeAssistant, HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr, entity, entity_registry as er
|
||||
from homeassistant.helpers.typing import UNDEFINED
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
|
@ -948,39 +949,24 @@ async def test_entity_description_fallback() -> None:
|
|||
assert getattr(ent, field.name) == getattr(ent_with_description, field.name)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("has_entity_name", "entity_name", "expected_friendly_name"),
|
||||
(
|
||||
(False, "Entity Blu", "Entity Blu"),
|
||||
(False, None, None),
|
||||
(True, "Entity Blu", "Device Bla Entity Blu"),
|
||||
(True, None, "Device Bla"),
|
||||
),
|
||||
)
|
||||
async def test_friendly_name(
|
||||
async def _test_friendly_name(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
ent: entity.Entity,
|
||||
has_entity_name: bool,
|
||||
entity_name: str | None,
|
||||
expected_friendly_name: str | None,
|
||||
warn_implicit_name: bool,
|
||||
) -> None:
|
||||
"""Test entity_id is influenced by entity name."""
|
||||
"""Test friendly name."""
|
||||
|
||||
expected_warning = (
|
||||
f"Entity {ent.entity_id} ({type(ent)}) is implicitly using device name"
|
||||
)
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Mock setup entry method."""
|
||||
async_add_entities(
|
||||
[
|
||||
MockEntity(
|
||||
unique_id="qwer",
|
||||
device_info={
|
||||
"identifiers": {("hue", "1234")},
|
||||
"connections": {(dr.CONNECTION_NETWORK_MAC, "abcd")},
|
||||
"name": "Device Bla",
|
||||
},
|
||||
has_entity_name=has_entity_name,
|
||||
name=entity_name,
|
||||
),
|
||||
]
|
||||
)
|
||||
async_add_entities([ent])
|
||||
return True
|
||||
|
||||
platform = MockPlatform(async_setup_entry=async_setup_entry)
|
||||
|
@ -995,6 +981,132 @@ async def test_friendly_name(
|
|||
assert len(hass.states.async_entity_ids()) == 1
|
||||
state = hass.states.async_all()[0]
|
||||
assert state.attributes.get(ATTR_FRIENDLY_NAME) == expected_friendly_name
|
||||
assert (expected_warning in caplog.text) is warn_implicit_name
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("has_entity_name", "entity_name", "expected_friendly_name", "warn_implicit_name"),
|
||||
(
|
||||
(False, "Entity Blu", "Entity Blu", False),
|
||||
(False, None, None, False),
|
||||
(True, "Entity Blu", "Device Bla Entity Blu", False),
|
||||
(True, None, "Device Bla", False),
|
||||
),
|
||||
)
|
||||
async def test_friendly_name_attr(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
has_entity_name: bool,
|
||||
entity_name: str | None,
|
||||
expected_friendly_name: str | None,
|
||||
warn_implicit_name: bool,
|
||||
) -> None:
|
||||
"""Test friendly name when the entity uses _attr_*."""
|
||||
|
||||
ent = MockEntity(
|
||||
unique_id="qwer",
|
||||
device_info={
|
||||
"identifiers": {("hue", "1234")},
|
||||
"connections": {(dr.CONNECTION_NETWORK_MAC, "abcd")},
|
||||
"name": "Device Bla",
|
||||
},
|
||||
)
|
||||
ent._attr_has_entity_name = has_entity_name
|
||||
ent._attr_name = entity_name
|
||||
await _test_friendly_name(
|
||||
hass,
|
||||
caplog,
|
||||
ent,
|
||||
has_entity_name,
|
||||
entity_name,
|
||||
expected_friendly_name,
|
||||
warn_implicit_name,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("has_entity_name", "entity_name", "expected_friendly_name", "warn_implicit_name"),
|
||||
(
|
||||
(False, "Entity Blu", "Entity Blu", False),
|
||||
(False, None, None, False),
|
||||
(False, UNDEFINED, None, False),
|
||||
(True, "Entity Blu", "Device Bla Entity Blu", False),
|
||||
(True, None, "Device Bla", False),
|
||||
(True, UNDEFINED, "Device Bla", True),
|
||||
),
|
||||
)
|
||||
async def test_friendly_name_description(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
has_entity_name: bool,
|
||||
entity_name: str | None,
|
||||
expected_friendly_name: str | None,
|
||||
warn_implicit_name: bool,
|
||||
) -> None:
|
||||
"""Test friendly name when the entity has an entity description."""
|
||||
|
||||
ent = MockEntity(
|
||||
unique_id="qwer",
|
||||
device_info={
|
||||
"identifiers": {("hue", "1234")},
|
||||
"connections": {(dr.CONNECTION_NETWORK_MAC, "abcd")},
|
||||
"name": "Device Bla",
|
||||
},
|
||||
)
|
||||
ent.entity_description = entity.EntityDescription(
|
||||
"test", has_entity_name=has_entity_name, name=entity_name
|
||||
)
|
||||
await _test_friendly_name(
|
||||
hass,
|
||||
caplog,
|
||||
ent,
|
||||
has_entity_name,
|
||||
entity_name,
|
||||
expected_friendly_name,
|
||||
warn_implicit_name,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("has_entity_name", "entity_name", "expected_friendly_name", "warn_implicit_name"),
|
||||
(
|
||||
(False, "Entity Blu", "Entity Blu", False),
|
||||
(False, None, None, False),
|
||||
(False, UNDEFINED, None, False),
|
||||
(True, "Entity Blu", "Device Bla Entity Blu", False),
|
||||
(True, None, "Device Bla", False),
|
||||
(True, UNDEFINED, "Device Bla", True),
|
||||
),
|
||||
)
|
||||
async def test_friendly_name_property(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
has_entity_name: bool,
|
||||
entity_name: str | None,
|
||||
expected_friendly_name: str | None,
|
||||
warn_implicit_name: bool,
|
||||
) -> None:
|
||||
"""Test friendly name when the entity has overridden the name property."""
|
||||
|
||||
ent = MockEntity(
|
||||
unique_id="qwer",
|
||||
device_info={
|
||||
"identifiers": {("hue", "1234")},
|
||||
"connections": {(dr.CONNECTION_NETWORK_MAC, "abcd")},
|
||||
"name": "Device Bla",
|
||||
},
|
||||
has_entity_name=has_entity_name,
|
||||
name=entity_name,
|
||||
)
|
||||
await _test_friendly_name(
|
||||
hass,
|
||||
caplog,
|
||||
ent,
|
||||
has_entity_name,
|
||||
entity_name,
|
||||
expected_friendly_name,
|
||||
warn_implicit_name,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -1028,7 +1140,7 @@ async def test_friendly_name_updated(
|
|||
expected_friendly_name2: str,
|
||||
expected_friendly_name3: str,
|
||||
) -> None:
|
||||
"""Test entity_id is influenced by entity name."""
|
||||
"""Test friendly name is updated when device or entity registry updates."""
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Mock setup entry method."""
|
||||
|
|
Loading…
Add table
Reference in a new issue