Avoid race in entity_platform.async_add_entities() (#18445)
This avoids a race between multiple concurrent calls to entity_platform.async_add_entities() which may cause entities to be created with non-unique entity_id
This commit is contained in:
parent
7e702d3caa
commit
f241becf7f
2 changed files with 11 additions and 13 deletions
|
@ -206,7 +206,6 @@ class EntityPlatform:
|
|||
return
|
||||
|
||||
hass = self.hass
|
||||
component_entities = set(hass.states.async_entity_ids(self.domain))
|
||||
|
||||
device_registry = await \
|
||||
hass.helpers.device_registry.async_get_registry()
|
||||
|
@ -214,8 +213,7 @@ class EntityPlatform:
|
|||
hass.helpers.entity_registry.async_get_registry()
|
||||
tasks = [
|
||||
self._async_add_entity(entity, update_before_add,
|
||||
component_entities, entity_registry,
|
||||
device_registry)
|
||||
entity_registry, device_registry)
|
||||
for entity in new_entities]
|
||||
|
||||
# No entities for processing
|
||||
|
@ -235,8 +233,7 @@ class EntityPlatform:
|
|||
)
|
||||
|
||||
async def _async_add_entity(self, entity, update_before_add,
|
||||
component_entities, entity_registry,
|
||||
device_registry):
|
||||
entity_registry, device_registry):
|
||||
"""Add an entity to the platform."""
|
||||
if entity is None:
|
||||
raise ValueError('Entity cannot be None')
|
||||
|
@ -329,25 +326,24 @@ class EntityPlatform:
|
|||
if self.entity_namespace is not None:
|
||||
suggested_object_id = '{} {}'.format(self.entity_namespace,
|
||||
suggested_object_id)
|
||||
|
||||
entity.entity_id = entity_registry.async_generate_entity_id(
|
||||
self.domain, suggested_object_id)
|
||||
self.domain, suggested_object_id, self.entities.keys())
|
||||
|
||||
# Make sure it is valid in case an entity set the value themselves
|
||||
if not valid_entity_id(entity.entity_id):
|
||||
raise HomeAssistantError(
|
||||
'Invalid entity id: {}'.format(entity.entity_id))
|
||||
elif entity.entity_id in component_entities:
|
||||
elif (entity.entity_id in self.entities or
|
||||
entity.entity_id in self.hass.states.async_entity_ids(
|
||||
self.domain)):
|
||||
msg = 'Entity id already exists: {}'.format(entity.entity_id)
|
||||
if entity.unique_id is not None:
|
||||
msg += '. Platform {} does not generate unique IDs'.format(
|
||||
self.platform_name)
|
||||
raise HomeAssistantError(
|
||||
msg)
|
||||
raise HomeAssistantError(msg)
|
||||
|
||||
entity_id = entity.entity_id
|
||||
self.entities[entity_id] = entity
|
||||
component_entities.add(entity_id)
|
||||
entity.async_on_remove(lambda: self.entities.pop(entity_id))
|
||||
|
||||
if hasattr(entity, 'async_added_to_hass'):
|
||||
|
|
|
@ -95,7 +95,8 @@ class EntityRegistry:
|
|||
return None
|
||||
|
||||
@callback
|
||||
def async_generate_entity_id(self, domain, suggested_object_id):
|
||||
def async_generate_entity_id(self, domain, suggested_object_id,
|
||||
known_object_ids=None):
|
||||
"""Generate an entity ID that does not conflict.
|
||||
|
||||
Conflicts checked against registered and currently existing entities.
|
||||
|
@ -103,7 +104,8 @@ class EntityRegistry:
|
|||
return ensure_unique_string(
|
||||
'{}.{}'.format(domain, slugify(suggested_object_id)),
|
||||
chain(self.entities.keys(),
|
||||
self.hass.states.async_entity_ids(domain))
|
||||
self.hass.states.async_entity_ids(domain),
|
||||
known_object_ids if known_object_ids else [])
|
||||
)
|
||||
|
||||
@callback
|
||||
|
|
Loading…
Add table
Reference in a new issue