Add Entity.has_entity_name attribute (#73217)
This commit is contained in:
parent
c883aec711
commit
26a85c6644
9 changed files with 186 additions and 18 deletions
|
@ -218,6 +218,7 @@ def _entry_ext_dict(entry):
|
||||||
data = _entry_dict(entry)
|
data = _entry_dict(entry)
|
||||||
data["capabilities"] = entry.capabilities
|
data["capabilities"] = entry.capabilities
|
||||||
data["device_class"] = entry.device_class
|
data["device_class"] = entry.device_class
|
||||||
|
data["has_entity_name"] = entry.has_entity_name
|
||||||
data["options"] = entry.options
|
data["options"] = entry.options
|
||||||
data["original_device_class"] = entry.original_device_class
|
data["original_device_class"] = entry.original_device_class
|
||||||
data["original_icon"] = entry.original_icon
|
data["original_icon"] = entry.original_icon
|
||||||
|
|
|
@ -38,7 +38,7 @@ from homeassistant.exceptions import HomeAssistantError, NoEntitySpecifiedError
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util import dt as dt_util, ensure_unique_string, slugify
|
from homeassistant.util import dt as dt_util, ensure_unique_string, slugify
|
||||||
|
|
||||||
from . import entity_registry as er
|
from . import device_registry as dr, entity_registry as er
|
||||||
from .device_registry import DeviceEntryType
|
from .device_registry import DeviceEntryType
|
||||||
from .entity_platform import EntityPlatform
|
from .entity_platform import EntityPlatform
|
||||||
from .event import async_track_entity_registry_updated_event
|
from .event import async_track_entity_registry_updated_event
|
||||||
|
@ -221,6 +221,7 @@ class EntityDescription:
|
||||||
entity_registry_visible_default: bool = True
|
entity_registry_visible_default: bool = True
|
||||||
force_update: bool = False
|
force_update: bool = False
|
||||||
icon: str | None = None
|
icon: str | None = None
|
||||||
|
has_entity_name: bool = False
|
||||||
name: str | None = None
|
name: str | None = None
|
||||||
unit_of_measurement: str | None = None
|
unit_of_measurement: str | None = None
|
||||||
|
|
||||||
|
@ -277,6 +278,7 @@ class Entity(ABC):
|
||||||
_attr_device_class: str | None
|
_attr_device_class: str | None
|
||||||
_attr_device_info: DeviceInfo | None = None
|
_attr_device_info: DeviceInfo | None = None
|
||||||
_attr_entity_category: EntityCategory | None
|
_attr_entity_category: EntityCategory | None
|
||||||
|
_attr_has_entity_name: bool
|
||||||
_attr_entity_picture: str | None = None
|
_attr_entity_picture: str | None = None
|
||||||
_attr_entity_registry_enabled_default: bool
|
_attr_entity_registry_enabled_default: bool
|
||||||
_attr_entity_registry_visible_default: bool
|
_attr_entity_registry_visible_default: bool
|
||||||
|
@ -303,6 +305,15 @@ class Entity(ABC):
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
return self._attr_unique_id
|
return self._attr_unique_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_entity_name(self) -> bool:
|
||||||
|
"""Return if the name of the entity is describing only the entity itself."""
|
||||||
|
if hasattr(self, "_attr_has_entity_name"):
|
||||||
|
return self._attr_has_entity_name
|
||||||
|
if hasattr(self, "entity_description"):
|
||||||
|
return self.entity_description.has_entity_name
|
||||||
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str | None:
|
def name(self) -> str | None:
|
||||||
"""Return the name of the entity."""
|
"""Return the name of the entity."""
|
||||||
|
@ -583,7 +594,26 @@ class Entity(ABC):
|
||||||
if (icon := (entry and entry.icon) or self.icon) is not None:
|
if (icon := (entry and entry.icon) or self.icon) is not None:
|
||||||
attr[ATTR_ICON] = icon
|
attr[ATTR_ICON] = icon
|
||||||
|
|
||||||
if (name := (entry and entry.name) or self.name) is not None:
|
def friendly_name() -> str | None:
|
||||||
|
"""Return the friendly name.
|
||||||
|
|
||||||
|
If has_entity_name is False, this returns self.name
|
||||||
|
If has_entity_name is True, this returns device.name + self.name
|
||||||
|
"""
|
||||||
|
if not self.has_entity_name or not self.registry_entry:
|
||||||
|
return self.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
|
||||||
|
|
||||||
|
if not self.name:
|
||||||
|
return device_entry.name_by_user or device_entry.name
|
||||||
|
return f"{device_entry.name_by_user or device_entry.name} {self.name}"
|
||||||
|
|
||||||
|
if (name := (entry and entry.name) or friendly_name()) is not None:
|
||||||
attr[ATTR_FRIENDLY_NAME] = name
|
attr[ATTR_FRIENDLY_NAME] = name
|
||||||
|
|
||||||
if (supported_features := self.supported_features) is not None:
|
if (supported_features := self.supported_features) is not None:
|
||||||
|
|
|
@ -440,15 +440,6 @@ class EntityPlatform:
|
||||||
|
|
||||||
# Get entity_id from unique ID registration
|
# Get entity_id from unique ID registration
|
||||||
if entity.unique_id is not None:
|
if entity.unique_id is not None:
|
||||||
if entity.entity_id is not None:
|
|
||||||
requested_entity_id = entity.entity_id
|
|
||||||
suggested_object_id = split_entity_id(entity.entity_id)[1]
|
|
||||||
else:
|
|
||||||
suggested_object_id = entity.name # type: ignore[unreachable]
|
|
||||||
|
|
||||||
if self.entity_namespace is not None:
|
|
||||||
suggested_object_id = f"{self.entity_namespace} {suggested_object_id}"
|
|
||||||
|
|
||||||
if self.config_entry is not None:
|
if self.config_entry is not None:
|
||||||
config_entry_id: str | None = self.config_entry.entry_id
|
config_entry_id: str | None = self.config_entry.entry_id
|
||||||
else:
|
else:
|
||||||
|
@ -503,6 +494,22 @@ class EntityPlatform:
|
||||||
except RequiredParameterMissing:
|
except RequiredParameterMissing:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if entity.entity_id is not None:
|
||||||
|
requested_entity_id = entity.entity_id
|
||||||
|
suggested_object_id = split_entity_id(entity.entity_id)[1]
|
||||||
|
else:
|
||||||
|
if device and entity.has_entity_name: # type: ignore[unreachable]
|
||||||
|
device_name = device.name_by_user or device.name
|
||||||
|
if not entity.name:
|
||||||
|
suggested_object_id = device_name
|
||||||
|
else:
|
||||||
|
suggested_object_id = f"{device_name} {entity.name}"
|
||||||
|
if not suggested_object_id:
|
||||||
|
suggested_object_id = entity.name
|
||||||
|
|
||||||
|
if self.entity_namespace is not None:
|
||||||
|
suggested_object_id = f"{self.entity_namespace} {suggested_object_id}"
|
||||||
|
|
||||||
disabled_by: RegistryEntryDisabler | None = None
|
disabled_by: RegistryEntryDisabler | None = None
|
||||||
if not entity.entity_registry_enabled_default:
|
if not entity.entity_registry_enabled_default:
|
||||||
disabled_by = RegistryEntryDisabler.INTEGRATION
|
disabled_by = RegistryEntryDisabler.INTEGRATION
|
||||||
|
|
|
@ -60,7 +60,7 @@ SAVE_DELAY = 10
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
STORAGE_VERSION_MAJOR = 1
|
STORAGE_VERSION_MAJOR = 1
|
||||||
STORAGE_VERSION_MINOR = 6
|
STORAGE_VERSION_MINOR = 7
|
||||||
STORAGE_KEY = "core.entity_registry"
|
STORAGE_KEY = "core.entity_registry"
|
||||||
|
|
||||||
# Attributes relevant to describing entity
|
# Attributes relevant to describing entity
|
||||||
|
@ -111,6 +111,7 @@ class RegistryEntry:
|
||||||
hidden_by: RegistryEntryHider | None = attr.ib(default=None)
|
hidden_by: RegistryEntryHider | None = attr.ib(default=None)
|
||||||
icon: str | None = attr.ib(default=None)
|
icon: str | None = attr.ib(default=None)
|
||||||
id: str = attr.ib(factory=uuid_util.random_uuid_hex)
|
id: str = attr.ib(factory=uuid_util.random_uuid_hex)
|
||||||
|
has_entity_name: bool = attr.ib(default=False)
|
||||||
name: str | None = attr.ib(default=None)
|
name: str | None = attr.ib(default=None)
|
||||||
options: Mapping[str, Mapping[str, Any]] = attr.ib(
|
options: Mapping[str, Mapping[str, Any]] = attr.ib(
|
||||||
default=None, converter=attr.converters.default_if_none(factory=dict) # type: ignore[misc]
|
default=None, converter=attr.converters.default_if_none(factory=dict) # type: ignore[misc]
|
||||||
|
@ -328,6 +329,7 @@ class EntityRegistry:
|
||||||
config_entry: ConfigEntry | None = None,
|
config_entry: ConfigEntry | None = None,
|
||||||
device_id: str | None = None,
|
device_id: str | None = None,
|
||||||
entity_category: EntityCategory | None = None,
|
entity_category: EntityCategory | None = None,
|
||||||
|
has_entity_name: bool | None = None,
|
||||||
original_device_class: str | None = None,
|
original_device_class: str | None = None,
|
||||||
original_icon: str | None = None,
|
original_icon: str | None = None,
|
||||||
original_name: str | None = None,
|
original_name: str | None = None,
|
||||||
|
@ -349,6 +351,9 @@ class EntityRegistry:
|
||||||
config_entry_id=config_entry_id or UNDEFINED,
|
config_entry_id=config_entry_id or UNDEFINED,
|
||||||
device_id=device_id or UNDEFINED,
|
device_id=device_id or UNDEFINED,
|
||||||
entity_category=entity_category or UNDEFINED,
|
entity_category=entity_category or UNDEFINED,
|
||||||
|
has_entity_name=has_entity_name
|
||||||
|
if has_entity_name is not None
|
||||||
|
else UNDEFINED,
|
||||||
original_device_class=original_device_class or UNDEFINED,
|
original_device_class=original_device_class or UNDEFINED,
|
||||||
original_icon=original_icon or UNDEFINED,
|
original_icon=original_icon or UNDEFINED,
|
||||||
original_name=original_name or UNDEFINED,
|
original_name=original_name or UNDEFINED,
|
||||||
|
@ -393,6 +398,7 @@ class EntityRegistry:
|
||||||
entity_category=entity_category,
|
entity_category=entity_category,
|
||||||
entity_id=entity_id,
|
entity_id=entity_id,
|
||||||
hidden_by=hidden_by,
|
hidden_by=hidden_by,
|
||||||
|
has_entity_name=has_entity_name or False,
|
||||||
original_device_class=original_device_class,
|
original_device_class=original_device_class,
|
||||||
original_icon=original_icon,
|
original_icon=original_icon,
|
||||||
original_name=original_name,
|
original_name=original_name,
|
||||||
|
@ -499,6 +505,7 @@ class EntityRegistry:
|
||||||
entity_category: EntityCategory | None | UndefinedType = UNDEFINED,
|
entity_category: EntityCategory | None | UndefinedType = UNDEFINED,
|
||||||
hidden_by: RegistryEntryHider | None | UndefinedType = UNDEFINED,
|
hidden_by: RegistryEntryHider | None | UndefinedType = UNDEFINED,
|
||||||
icon: str | None | UndefinedType = UNDEFINED,
|
icon: str | None | UndefinedType = UNDEFINED,
|
||||||
|
has_entity_name: bool | UndefinedType = UNDEFINED,
|
||||||
name: str | None | UndefinedType = UNDEFINED,
|
name: str | None | UndefinedType = UNDEFINED,
|
||||||
new_entity_id: str | UndefinedType = UNDEFINED,
|
new_entity_id: str | UndefinedType = UNDEFINED,
|
||||||
new_unique_id: str | UndefinedType = UNDEFINED,
|
new_unique_id: str | UndefinedType = UNDEFINED,
|
||||||
|
@ -548,6 +555,7 @@ class EntityRegistry:
|
||||||
("entity_category", entity_category),
|
("entity_category", entity_category),
|
||||||
("hidden_by", hidden_by),
|
("hidden_by", hidden_by),
|
||||||
("icon", icon),
|
("icon", icon),
|
||||||
|
("has_entity_name", has_entity_name),
|
||||||
("name", name),
|
("name", name),
|
||||||
("original_device_class", original_device_class),
|
("original_device_class", original_device_class),
|
||||||
("original_icon", original_icon),
|
("original_icon", original_icon),
|
||||||
|
@ -621,6 +629,7 @@ class EntityRegistry:
|
||||||
entity_category: EntityCategory | None | UndefinedType = UNDEFINED,
|
entity_category: EntityCategory | None | UndefinedType = UNDEFINED,
|
||||||
hidden_by: RegistryEntryHider | None | UndefinedType = UNDEFINED,
|
hidden_by: RegistryEntryHider | None | UndefinedType = UNDEFINED,
|
||||||
icon: str | None | UndefinedType = UNDEFINED,
|
icon: str | None | UndefinedType = UNDEFINED,
|
||||||
|
has_entity_name: bool | UndefinedType = UNDEFINED,
|
||||||
name: str | None | UndefinedType = UNDEFINED,
|
name: str | None | UndefinedType = UNDEFINED,
|
||||||
new_entity_id: str | UndefinedType = UNDEFINED,
|
new_entity_id: str | UndefinedType = UNDEFINED,
|
||||||
new_unique_id: str | UndefinedType = UNDEFINED,
|
new_unique_id: str | UndefinedType = UNDEFINED,
|
||||||
|
@ -642,6 +651,7 @@ class EntityRegistry:
|
||||||
entity_category=entity_category,
|
entity_category=entity_category,
|
||||||
hidden_by=hidden_by,
|
hidden_by=hidden_by,
|
||||||
icon=icon,
|
icon=icon,
|
||||||
|
has_entity_name=has_entity_name,
|
||||||
name=name,
|
name=name,
|
||||||
new_entity_id=new_entity_id,
|
new_entity_id=new_entity_id,
|
||||||
new_unique_id=new_unique_id,
|
new_unique_id=new_unique_id,
|
||||||
|
@ -742,6 +752,7 @@ class EntityRegistry:
|
||||||
else None,
|
else None,
|
||||||
icon=entity["icon"],
|
icon=entity["icon"],
|
||||||
id=entity["id"],
|
id=entity["id"],
|
||||||
|
has_entity_name=entity["has_entity_name"],
|
||||||
name=entity["name"],
|
name=entity["name"],
|
||||||
options=entity["options"],
|
options=entity["options"],
|
||||||
original_device_class=entity["original_device_class"],
|
original_device_class=entity["original_device_class"],
|
||||||
|
@ -778,6 +789,7 @@ class EntityRegistry:
|
||||||
"hidden_by": entry.hidden_by,
|
"hidden_by": entry.hidden_by,
|
||||||
"icon": entry.icon,
|
"icon": entry.icon,
|
||||||
"id": entry.id,
|
"id": entry.id,
|
||||||
|
"has_entity_name": entry.has_entity_name,
|
||||||
"name": entry.name,
|
"name": entry.name,
|
||||||
"options": entry.options,
|
"options": entry.options,
|
||||||
"original_device_class": entry.original_device_class,
|
"original_device_class": entry.original_device_class,
|
||||||
|
@ -944,6 +956,11 @@ async def _async_migrate(
|
||||||
for entity in data["entities"]:
|
for entity in data["entities"]:
|
||||||
entity["hidden_by"] = None
|
entity["hidden_by"] = None
|
||||||
|
|
||||||
|
if old_major_version == 1 and old_minor_version < 7:
|
||||||
|
# Version 1.6 adds has_entity_name
|
||||||
|
for entity in data["entities"]:
|
||||||
|
entity["has_entity_name"] = False
|
||||||
|
|
||||||
if old_major_version > 1:
|
if old_major_version > 1:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -1007,6 +1007,11 @@ class MockEntity(entity.Entity):
|
||||||
"""Return the entity category."""
|
"""Return the entity category."""
|
||||||
return self._handle("entity_category")
|
return self._handle("entity_category")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_entity_name(self):
|
||||||
|
"""Return the has_entity_name name flag."""
|
||||||
|
return self._handle("has_entity_name")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def entity_registry_enabled_default(self):
|
def entity_registry_enabled_default(self):
|
||||||
"""Return if the entity should be enabled when first added to the entity registry."""
|
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||||
|
|
|
@ -117,6 +117,7 @@ async def test_get_entity(hass, client):
|
||||||
"entity_id": "test_domain.name",
|
"entity_id": "test_domain.name",
|
||||||
"hidden_by": None,
|
"hidden_by": None,
|
||||||
"icon": None,
|
"icon": None,
|
||||||
|
"has_entity_name": False,
|
||||||
"name": "Hello World",
|
"name": "Hello World",
|
||||||
"options": {},
|
"options": {},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
@ -146,6 +147,7 @@ async def test_get_entity(hass, client):
|
||||||
"entity_id": "test_domain.no_name",
|
"entity_id": "test_domain.no_name",
|
||||||
"hidden_by": None,
|
"hidden_by": None,
|
||||||
"icon": None,
|
"icon": None,
|
||||||
|
"has_entity_name": False,
|
||||||
"name": None,
|
"name": None,
|
||||||
"options": {},
|
"options": {},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
@ -208,6 +210,7 @@ async def test_update_entity(hass, client):
|
||||||
"entity_id": "test_domain.world",
|
"entity_id": "test_domain.world",
|
||||||
"hidden_by": "user", # We exchange strings over the WS API, not enums
|
"hidden_by": "user", # We exchange strings over the WS API, not enums
|
||||||
"icon": "icon:after update",
|
"icon": "icon:after update",
|
||||||
|
"has_entity_name": False,
|
||||||
"name": "after update",
|
"name": "after update",
|
||||||
"options": {},
|
"options": {},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
@ -279,6 +282,7 @@ async def test_update_entity(hass, client):
|
||||||
"entity_id": "test_domain.world",
|
"entity_id": "test_domain.world",
|
||||||
"hidden_by": "user", # We exchange strings over the WS API, not enums
|
"hidden_by": "user", # We exchange strings over the WS API, not enums
|
||||||
"icon": "icon:after update",
|
"icon": "icon:after update",
|
||||||
|
"has_entity_name": False,
|
||||||
"name": "after update",
|
"name": "after update",
|
||||||
"options": {},
|
"options": {},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
@ -315,6 +319,7 @@ async def test_update_entity(hass, client):
|
||||||
"entity_id": "test_domain.world",
|
"entity_id": "test_domain.world",
|
||||||
"hidden_by": "user", # We exchange strings over the WS API, not enums
|
"hidden_by": "user", # We exchange strings over the WS API, not enums
|
||||||
"icon": "icon:after update",
|
"icon": "icon:after update",
|
||||||
|
"has_entity_name": False,
|
||||||
"name": "after update",
|
"name": "after update",
|
||||||
"options": {"sensor": {"unit_of_measurement": "beard_second"}},
|
"options": {"sensor": {"unit_of_measurement": "beard_second"}},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
@ -373,6 +378,7 @@ async def test_update_entity_require_restart(hass, client):
|
||||||
"entity_id": "test_domain.world",
|
"entity_id": "test_domain.world",
|
||||||
"icon": None,
|
"icon": None,
|
||||||
"hidden_by": None,
|
"hidden_by": None,
|
||||||
|
"has_entity_name": False,
|
||||||
"name": None,
|
"name": None,
|
||||||
"options": {},
|
"options": {},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
@ -479,6 +485,7 @@ async def test_update_entity_no_changes(hass, client):
|
||||||
"entity_id": "test_domain.world",
|
"entity_id": "test_domain.world",
|
||||||
"hidden_by": None,
|
"hidden_by": None,
|
||||||
"icon": None,
|
"icon": None,
|
||||||
|
"has_entity_name": False,
|
||||||
"name": "name of entity",
|
"name": "name of entity",
|
||||||
"options": {},
|
"options": {},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
@ -564,6 +571,7 @@ async def test_update_entity_id(hass, client):
|
||||||
"entity_id": "test_domain.planet",
|
"entity_id": "test_domain.planet",
|
||||||
"hidden_by": None,
|
"hidden_by": None,
|
||||||
"icon": None,
|
"icon": None,
|
||||||
|
"has_entity_name": False,
|
||||||
"name": None,
|
"name": None,
|
||||||
"options": {},
|
"options": {},
|
||||||
"original_device_class": None,
|
"original_device_class": None,
|
||||||
|
|
|
@ -12,16 +12,18 @@ import voluptuous as vol
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ATTRIBUTION,
|
ATTR_ATTRIBUTION,
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
|
ATTR_FRIENDLY_NAME,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Context, HomeAssistantError
|
from homeassistant.core import Context, HomeAssistantError
|
||||||
from homeassistant.helpers import entity, entity_registry
|
from homeassistant.helpers import device_registry as dr, entity, entity_registry as er
|
||||||
|
|
||||||
from tests.common import (
|
from tests.common import (
|
||||||
MockConfigEntry,
|
MockConfigEntry,
|
||||||
MockEntity,
|
MockEntity,
|
||||||
MockEntityPlatform,
|
MockEntityPlatform,
|
||||||
|
MockPlatform,
|
||||||
get_test_home_assistant,
|
get_test_home_assistant,
|
||||||
mock_registry,
|
mock_registry,
|
||||||
)
|
)
|
||||||
|
@ -594,11 +596,11 @@ async def test_set_context_expired(hass):
|
||||||
|
|
||||||
async def test_warn_disabled(hass, caplog):
|
async def test_warn_disabled(hass, caplog):
|
||||||
"""Test we warn once if we write to a disabled entity."""
|
"""Test we warn once if we write to a disabled entity."""
|
||||||
entry = entity_registry.RegistryEntry(
|
entry = er.RegistryEntry(
|
||||||
entity_id="hello.world",
|
entity_id="hello.world",
|
||||||
unique_id="test-unique-id",
|
unique_id="test-unique-id",
|
||||||
platform="test-platform",
|
platform="test-platform",
|
||||||
disabled_by=entity_registry.RegistryEntryDisabler.USER,
|
disabled_by=er.RegistryEntryDisabler.USER,
|
||||||
)
|
)
|
||||||
mock_registry(hass, {"hello.world": entry})
|
mock_registry(hass, {"hello.world": entry})
|
||||||
|
|
||||||
|
@ -621,7 +623,7 @@ async def test_warn_disabled(hass, caplog):
|
||||||
|
|
||||||
async def test_disabled_in_entity_registry(hass):
|
async def test_disabled_in_entity_registry(hass):
|
||||||
"""Test entity is removed if we disable entity registry entry."""
|
"""Test entity is removed if we disable entity registry entry."""
|
||||||
entry = entity_registry.RegistryEntry(
|
entry = er.RegistryEntry(
|
||||||
entity_id="hello.world",
|
entity_id="hello.world",
|
||||||
unique_id="test-unique-id",
|
unique_id="test-unique-id",
|
||||||
platform="test-platform",
|
platform="test-platform",
|
||||||
|
@ -640,7 +642,7 @@ async def test_disabled_in_entity_registry(hass):
|
||||||
assert hass.states.get("hello.world") is not None
|
assert hass.states.get("hello.world") is not None
|
||||||
|
|
||||||
entry2 = registry.async_update_entity(
|
entry2 = registry.async_update_entity(
|
||||||
"hello.world", disabled_by=entity_registry.RegistryEntryDisabler.USER
|
"hello.world", disabled_by=er.RegistryEntryDisabler.USER
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert entry2 != entry
|
assert entry2 != entry
|
||||||
|
@ -749,7 +751,7 @@ async def test_setup_source(hass):
|
||||||
|
|
||||||
async def test_removing_entity_unavailable(hass):
|
async def test_removing_entity_unavailable(hass):
|
||||||
"""Test removing an entity that is still registered creates an unavailable state."""
|
"""Test removing an entity that is still registered creates an unavailable state."""
|
||||||
entry = entity_registry.RegistryEntry(
|
entry = er.RegistryEntry(
|
||||||
entity_id="hello.world",
|
entity_id="hello.world",
|
||||||
unique_id="test-unique-id",
|
unique_id="test-unique-id",
|
||||||
platform="test-platform",
|
platform="test-platform",
|
||||||
|
@ -886,3 +888,49 @@ async def test_entity_description_fallback():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
assert getattr(ent, field.name) == getattr(ent_with_description, field.name)
|
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(
|
||||||
|
hass, has_entity_name, entity_name, expected_friendly_name
|
||||||
|
):
|
||||||
|
"""Test entity_id is influenced by entity 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,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
platform = MockPlatform(async_setup_entry=async_setup_entry)
|
||||||
|
config_entry = MockConfigEntry(entry_id="super-mock-id")
|
||||||
|
entity_platform = MockEntityPlatform(
|
||||||
|
hass, platform_name=config_entry.domain, platform=platform
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await entity_platform.async_setup_entry(config_entry)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.states.async_entity_ids()) == 1
|
||||||
|
state = hass.states.async_all()[0]
|
||||||
|
assert state.attributes.get(ATTR_FRIENDLY_NAME) == expected_friendly_name
|
||||||
|
|
|
@ -1392,3 +1392,49 @@ class SlowEntity(MockEntity):
|
||||||
"""Make sure control is returned to the event loop on add."""
|
"""Make sure control is returned to the event loop on add."""
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"has_entity_name, entity_name, expected_entity_id",
|
||||||
|
(
|
||||||
|
(False, "Entity Blu", "test_domain.entity_blu"),
|
||||||
|
(False, None, "test_domain.test_qwer"), # Set to <platform>_<unique_id>
|
||||||
|
(True, "Entity Blu", "test_domain.device_bla_entity_blu"),
|
||||||
|
(True, None, "test_domain.device_bla"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def test_entity_name_influences_entity_id(
|
||||||
|
hass, has_entity_name, entity_name, expected_entity_id
|
||||||
|
):
|
||||||
|
"""Test entity_id is influenced by entity name."""
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
platform = MockPlatform(async_setup_entry=async_setup_entry)
|
||||||
|
config_entry = MockConfigEntry(entry_id="super-mock-id")
|
||||||
|
entity_platform = MockEntityPlatform(
|
||||||
|
hass, platform_name=config_entry.domain, platform=platform
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await entity_platform.async_setup_entry(config_entry)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.states.async_entity_ids()) == 1
|
||||||
|
assert registry.async_get(expected_entity_id) is not None
|
||||||
|
|
|
@ -80,6 +80,7 @@ def test_get_or_create_updates_data(registry):
|
||||||
disabled_by=er.RegistryEntryDisabler.HASS,
|
disabled_by=er.RegistryEntryDisabler.HASS,
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
hidden_by=er.RegistryEntryHider.INTEGRATION,
|
hidden_by=er.RegistryEntryHider.INTEGRATION,
|
||||||
|
has_entity_name=True,
|
||||||
original_device_class="mock-device-class",
|
original_device_class="mock-device-class",
|
||||||
original_icon="initial-original_icon",
|
original_icon="initial-original_icon",
|
||||||
original_name="initial-original_name",
|
original_name="initial-original_name",
|
||||||
|
@ -101,6 +102,7 @@ def test_get_or_create_updates_data(registry):
|
||||||
hidden_by=er.RegistryEntryHider.INTEGRATION,
|
hidden_by=er.RegistryEntryHider.INTEGRATION,
|
||||||
icon=None,
|
icon=None,
|
||||||
id=orig_entry.id,
|
id=orig_entry.id,
|
||||||
|
has_entity_name=True,
|
||||||
name=None,
|
name=None,
|
||||||
original_device_class="mock-device-class",
|
original_device_class="mock-device-class",
|
||||||
original_icon="initial-original_icon",
|
original_icon="initial-original_icon",
|
||||||
|
@ -122,6 +124,7 @@ def test_get_or_create_updates_data(registry):
|
||||||
disabled_by=er.RegistryEntryDisabler.USER,
|
disabled_by=er.RegistryEntryDisabler.USER,
|
||||||
entity_category=None,
|
entity_category=None,
|
||||||
hidden_by=er.RegistryEntryHider.USER,
|
hidden_by=er.RegistryEntryHider.USER,
|
||||||
|
has_entity_name=False,
|
||||||
original_device_class="new-mock-device-class",
|
original_device_class="new-mock-device-class",
|
||||||
original_icon="updated-original_icon",
|
original_icon="updated-original_icon",
|
||||||
original_name="updated-original_name",
|
original_name="updated-original_name",
|
||||||
|
@ -143,6 +146,7 @@ def test_get_or_create_updates_data(registry):
|
||||||
hidden_by=er.RegistryEntryHider.INTEGRATION, # Should not be updated
|
hidden_by=er.RegistryEntryHider.INTEGRATION, # Should not be updated
|
||||||
icon=None,
|
icon=None,
|
||||||
id=orig_entry.id,
|
id=orig_entry.id,
|
||||||
|
has_entity_name=False,
|
||||||
name=None,
|
name=None,
|
||||||
original_device_class="new-mock-device-class",
|
original_device_class="new-mock-device-class",
|
||||||
original_icon="updated-original_icon",
|
original_icon="updated-original_icon",
|
||||||
|
@ -196,6 +200,7 @@ async def test_loading_saving_data(hass, registry):
|
||||||
disabled_by=er.RegistryEntryDisabler.HASS,
|
disabled_by=er.RegistryEntryDisabler.HASS,
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
hidden_by=er.RegistryEntryHider.INTEGRATION,
|
hidden_by=er.RegistryEntryHider.INTEGRATION,
|
||||||
|
has_entity_name=True,
|
||||||
original_device_class="mock-device-class",
|
original_device_class="mock-device-class",
|
||||||
original_icon="hass:original-icon",
|
original_icon="hass:original-icon",
|
||||||
original_name="Original Name",
|
original_name="Original Name",
|
||||||
|
@ -237,6 +242,7 @@ async def test_loading_saving_data(hass, registry):
|
||||||
assert new_entry2.entity_category == "config"
|
assert new_entry2.entity_category == "config"
|
||||||
assert new_entry2.icon == "hass:user-icon"
|
assert new_entry2.icon == "hass:user-icon"
|
||||||
assert new_entry2.hidden_by == er.RegistryEntryHider.INTEGRATION
|
assert new_entry2.hidden_by == er.RegistryEntryHider.INTEGRATION
|
||||||
|
assert new_entry2.has_entity_name is True
|
||||||
assert new_entry2.name == "User Name"
|
assert new_entry2.name == "User Name"
|
||||||
assert new_entry2.options == {"light": {"minimum_brightness": 20}}
|
assert new_entry2.options == {"light": {"minimum_brightness": 20}}
|
||||||
assert new_entry2.original_device_class == "mock-device-class"
|
assert new_entry2.original_device_class == "mock-device-class"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue