mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 16:17:20 +00:00
Don't allow adding entities which don't belong to an entity platform
This commit is contained in:
parent
c92873bbff
commit
df446f5240
@ -463,9 +463,6 @@ class Entity(
|
||||
# it should be using async_write_ha_state.
|
||||
_async_update_ha_state_reported = False
|
||||
|
||||
# If we reported this entity was added without its platform set
|
||||
_no_platform_reported = False
|
||||
|
||||
# If we reported the name translation placeholders do not match the name
|
||||
_name_translation_placeholders_reported = False
|
||||
|
||||
@ -721,9 +718,6 @@ class Entity(
|
||||
# value.
|
||||
type.__getattribute__(self.__class__, "name")
|
||||
is type.__getattribute__(Entity, "name")
|
||||
# The check for self.platform guards against integrations not using an
|
||||
# EntityComponent and can be removed in HA Core 2024.1
|
||||
and self.platform
|
||||
):
|
||||
name = self._name_internal(
|
||||
self._object_id_device_class_name,
|
||||
@ -736,10 +730,6 @@ class Entity(
|
||||
@cached_property
|
||||
def name(self) -> str | UndefinedType | None:
|
||||
"""Return the name of the entity."""
|
||||
# The check for self.platform guards against integrations not using an
|
||||
# EntityComponent and can be removed in HA Core 2024.1
|
||||
if not self.platform:
|
||||
return self._name_internal(None, {})
|
||||
return self._name_internal(
|
||||
self._device_class_name,
|
||||
self.platform.platform_translations,
|
||||
@ -984,7 +974,7 @@ class Entity(
|
||||
|
||||
# The check for self.platform guards against integrations not using an
|
||||
# EntityComponent and can be removed in HA Core 2024.1
|
||||
if self.platform is None and not self._no_platform_reported: # type: ignore[unreachable]
|
||||
if self.platform is None:
|
||||
report_issue = self._suggest_report_issue() # type: ignore[unreachable]
|
||||
_LOGGER.warning(
|
||||
(
|
||||
@ -996,7 +986,7 @@ class Entity(
|
||||
type(self),
|
||||
report_issue,
|
||||
)
|
||||
self._no_platform_reported = True
|
||||
raise HomeAssistantError("Entity does not have a platform")
|
||||
|
||||
if self.entity_id is None:
|
||||
raise NoEntitySpecifiedError(
|
||||
@ -1015,8 +1005,6 @@ class Entity(
|
||||
@callback
|
||||
def async_write_ha_state(self) -> None:
|
||||
"""Write the state to the state machine."""
|
||||
if not self.hass or not self._verified_state_writable:
|
||||
self._async_verify_state_writable()
|
||||
if self.hass.loop_thread_id != threading.get_ident():
|
||||
report_non_thread_safe_operation("async_write_ha_state")
|
||||
self._async_write_ha_state()
|
||||
@ -1126,6 +1114,9 @@ class Entity(
|
||||
# Polling returned after the entity has already been removed
|
||||
return
|
||||
|
||||
if not self.hass or not self._verified_state_writable:
|
||||
self._async_verify_state_writable()
|
||||
|
||||
if (entry := self.registry_entry) and entry.disabled_by:
|
||||
if not self._disabled_reported:
|
||||
self._disabled_reported = True
|
||||
@ -1486,10 +1477,7 @@ class Entity(
|
||||
|
||||
Not to be extended by integrations.
|
||||
"""
|
||||
# The check for self.platform guards against integrations not using an
|
||||
# EntityComponent and can be removed in HA Core 2024.1
|
||||
if self.platform:
|
||||
del entity_sources(self.hass)[self.entity_id]
|
||||
del entity_sources(self.hass)[self.entity_id]
|
||||
|
||||
@callback
|
||||
def _async_registry_updated(
|
||||
@ -1620,7 +1608,7 @@ class Entity(
|
||||
def _suggest_report_issue(self) -> str:
|
||||
"""Suggest to report an issue."""
|
||||
# The check for self.platform guards against integrations not using an
|
||||
# EntityComponent and can be removed in HA Core 2024.1
|
||||
# EntityComponent which has not been allowed since HA Core 2024.1
|
||||
platform_name = self.platform.platform_name if self.platform else None
|
||||
return async_suggest_report_issue(
|
||||
self.hass, integration_domain=platform_name, module=type(self).__module__
|
||||
|
@ -101,6 +101,7 @@ async def test_async_update_support(hass: HomeAssistant) -> None:
|
||||
|
||||
ent = AsyncEntity()
|
||||
ent.hass = hass
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
|
||||
await ent.async_update_ha_state(True)
|
||||
|
||||
@ -125,6 +126,7 @@ async def test_device_class(hass: HomeAssistant) -> None:
|
||||
ent = entity.Entity()
|
||||
ent.entity_id = "test.overwrite_hidden_true"
|
||||
ent.hass = hass
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent.async_write_ha_state()
|
||||
state = hass.states.get(ent.entity_id)
|
||||
assert state.attributes.get(ATTR_DEVICE_CLASS) is None
|
||||
@ -149,6 +151,7 @@ async def test_warn_slow_update(
|
||||
|
||||
mock_entity = entity.Entity()
|
||||
mock_entity.hass = hass
|
||||
mock_entity.platform = MockEntityPlatform(hass)
|
||||
mock_entity.entity_id = "comp_test.test_entity"
|
||||
mock_entity.async_update = async_update
|
||||
|
||||
@ -340,7 +343,9 @@ async def test_async_parallel_updates_with_zero(hass: HomeAssistant) -> None:
|
||||
await test_lock.wait()
|
||||
|
||||
ent_1 = AsyncEntity("sensor.test_1", 1)
|
||||
ent_1.platform = MockEntityPlatform(hass)
|
||||
ent_2 = AsyncEntity("sensor.test_2", 2)
|
||||
ent_2.platform = MockEntityPlatform(hass)
|
||||
|
||||
try:
|
||||
ent_1.async_schedule_update_ha_state(True)
|
||||
@ -385,7 +390,9 @@ async def test_async_parallel_updates_with_zero_on_sync_update(
|
||||
|
||||
try:
|
||||
ent_1.async_schedule_update_ha_state(True)
|
||||
ent_1.platform = MockEntityPlatform(hass)
|
||||
ent_2.async_schedule_update_ha_state(True)
|
||||
ent_2.platform = MockEntityPlatform(hass)
|
||||
|
||||
while True:
|
||||
if len(updates) >= 2:
|
||||
@ -421,8 +428,11 @@ async def test_async_parallel_updates_with_one(hass: HomeAssistant) -> None:
|
||||
await test_lock.acquire()
|
||||
|
||||
ent_1 = AsyncEntity("sensor.test_1", 1)
|
||||
ent_1.platform = MockEntityPlatform(hass)
|
||||
ent_2 = AsyncEntity("sensor.test_2", 2)
|
||||
ent_2.platform = MockEntityPlatform(hass)
|
||||
ent_3 = AsyncEntity("sensor.test_3", 3)
|
||||
ent_3.platform = MockEntityPlatform(hass)
|
||||
|
||||
await test_lock.acquire()
|
||||
|
||||
@ -497,9 +507,13 @@ async def test_async_parallel_updates_with_two(hass: HomeAssistant) -> None:
|
||||
await test_lock.acquire()
|
||||
|
||||
ent_1 = AsyncEntity("sensor.test_1", 1)
|
||||
ent_1.platform = MockEntityPlatform(hass)
|
||||
ent_2 = AsyncEntity("sensor.test_2", 2)
|
||||
ent_2.platform = MockEntityPlatform(hass)
|
||||
ent_3 = AsyncEntity("sensor.test_3", 3)
|
||||
ent_3.platform = MockEntityPlatform(hass)
|
||||
ent_4 = AsyncEntity("sensor.test_4", 4)
|
||||
ent_4.platform = MockEntityPlatform(hass)
|
||||
|
||||
await test_lock.acquire()
|
||||
|
||||
@ -565,6 +579,8 @@ async def test_async_parallel_updates_with_one_using_executor(
|
||||
locked.append(self.parallel_updates.locked())
|
||||
|
||||
entities = [SyncEntity(f"sensor.test_{i}") for i in range(3)]
|
||||
for ent in entities:
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
|
||||
await asyncio.gather(
|
||||
*[
|
||||
@ -579,17 +595,6 @@ async def test_async_parallel_updates_with_one_using_executor(
|
||||
assert locked == [True, True, True]
|
||||
|
||||
|
||||
async def test_async_remove_no_platform(hass: HomeAssistant) -> None:
|
||||
"""Test async_remove method when no platform set."""
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.entity_id = "test.test"
|
||||
ent.async_write_ha_state()
|
||||
assert len(hass.states.async_entity_ids()) == 1
|
||||
await ent.async_remove()
|
||||
assert len(hass.states.async_entity_ids()) == 0
|
||||
|
||||
|
||||
async def test_async_remove_runs_callbacks(hass: HomeAssistant) -> None:
|
||||
"""Test async_remove runs on_remove callback."""
|
||||
result = []
|
||||
@ -659,6 +664,7 @@ async def test_set_context(hass: HomeAssistant) -> None:
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.entity_id = "hello.world"
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent.async_set_context(context)
|
||||
ent.async_write_ha_state()
|
||||
assert hass.states.get("hello.world").context == context
|
||||
@ -672,6 +678,7 @@ async def test_set_context_expired(hass: HomeAssistant) -> None:
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.entity_id = "hello.world"
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent.async_set_context(context)
|
||||
ent.async_write_ha_state()
|
||||
|
||||
@ -758,6 +765,7 @@ async def test_capability_attrs(hass: HomeAssistant) -> None:
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.entity_id = "hello.world"
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent.async_write_ha_state()
|
||||
|
||||
state = hass.states.get("hello.world")
|
||||
@ -895,6 +903,7 @@ async def test_float_conversion(hass: HomeAssistant) -> None:
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.entity_id = "hello.world"
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent.async_write_ha_state()
|
||||
|
||||
state = hass.states.get("hello.world")
|
||||
@ -907,6 +916,7 @@ async def test_attribution_attribute(hass: HomeAssistant) -> None:
|
||||
mock_entity = entity.Entity()
|
||||
mock_entity.hass = hass
|
||||
mock_entity.entity_id = "hello.world"
|
||||
mock_entity.platform = MockEntityPlatform(hass)
|
||||
mock_entity._attr_attribution = "Home Assistant"
|
||||
|
||||
mock_entity.async_schedule_update_ha_state(True)
|
||||
@ -962,10 +972,12 @@ def test_entity_category_schema_error(value) -> None:
|
||||
schema(value)
|
||||
|
||||
|
||||
async def test_entity_description_fallback() -> None:
|
||||
async def test_entity_description_fallback(hass: HomeAssistant) -> None:
|
||||
"""Test entity description has same defaults as entity."""
|
||||
ent = entity.Entity()
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent_with_description = entity.Entity()
|
||||
ent_with_description.platform = MockEntityPlatform(hass)
|
||||
ent_with_description.entity_description = entity.EntityDescription(key="test")
|
||||
|
||||
for field in dataclasses.fields(entity.EntityDescription._dataclass):
|
||||
@ -1664,28 +1676,27 @@ async def test_warn_using_async_update_ha_state(
|
||||
assert error_message not in caplog.text
|
||||
|
||||
|
||||
async def test_warn_no_platform(
|
||||
async def test_raise_no_platform(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Test we warn am entity does not have a platform."""
|
||||
"""Test we raise if an entity does not have a platform."""
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.platform = None
|
||||
ent.entity_id = "hello.world"
|
||||
error_message = "does not have a platform"
|
||||
|
||||
# Without a platform, it should log a warning and raise an error
|
||||
caplog.clear()
|
||||
with pytest.raises(HomeAssistantError, match="Entity does not have a platform"):
|
||||
ent.async_write_ha_state()
|
||||
assert error_message in caplog.text
|
||||
|
||||
# No warning if the entity has a platform
|
||||
ent = entity.Entity()
|
||||
ent.hass = hass
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent.entity_id = "hello.world"
|
||||
error_message = "does not have a platform"
|
||||
|
||||
# Without a platform, it should trigger the warning
|
||||
ent.platform = None
|
||||
caplog.clear()
|
||||
ent.async_write_ha_state()
|
||||
assert error_message in caplog.text
|
||||
|
||||
# Without a platform, it should not trigger the warning again
|
||||
caplog.clear()
|
||||
ent.async_write_ha_state()
|
||||
assert error_message not in caplog.text
|
||||
|
||||
# No warning if the entity has a platform
|
||||
caplog.clear()
|
||||
ent.async_write_ha_state()
|
||||
assert error_message not in caplog.text
|
||||
@ -1698,6 +1709,7 @@ async def test_invalid_state(
|
||||
ent = entity.Entity()
|
||||
ent.entity_id = "test.test"
|
||||
ent.hass = hass
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
|
||||
ent._attr_state = "x" * 255
|
||||
ent.async_write_ha_state()
|
||||
@ -2605,6 +2617,7 @@ async def test_async_write_ha_state_thread_safety(hass: HomeAssistant) -> None:
|
||||
ent = entity.Entity()
|
||||
ent.entity_id = "test.any"
|
||||
ent.hass = hass
|
||||
ent.platform = MockEntityPlatform(hass)
|
||||
ent.async_write_ha_state()
|
||||
assert hass.states.get(ent.entity_id)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user