diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index 1dc9a187030..cda49591d5c 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -334,18 +334,39 @@ async def async_setup(hass, config): async def _async_process_config(hass, config, component): """Process group configuration.""" + hass.data.setdefault(GROUP_ORDER, 0) + + tasks = [] + for object_id, conf in config.get(DOMAIN, {}).items(): name = conf.get(CONF_NAME, object_id) entity_ids = conf.get(CONF_ENTITIES) or [] icon = conf.get(CONF_ICON) mode = conf.get(CONF_ALL) - # Don't create tasks and await them all. The order is important as - # groups get a number based on creation order. - await Group.async_create_group( - hass, name, entity_ids, icon=icon, object_id=object_id, mode=mode + # We keep track of the order when we are creating the tasks + # in the same way that async_create_group does to make + # sure we use the same ordering system. This overcomes + # the problem with concurrently creating the groups + tasks.append( + Group.async_create_group( + hass, + name, + entity_ids, + icon=icon, + object_id=object_id, + mode=mode, + order=hass.data[GROUP_ORDER], + ) ) + # Keep track of the group order without iterating + # every state in the state machine every time + # we setup a new group + hass.data[GROUP_ORDER] += 1 + + await asyncio.gather(*tasks) + class GroupEntity(Entity): """Representation of a Group of entities.""" @@ -418,11 +439,12 @@ class Group(Entity): icon=None, object_id=None, mode=None, + order=None, ): """Initialize a group.""" return asyncio.run_coroutine_threadsafe( Group.async_create_group( - hass, name, entity_ids, user_defined, icon, object_id, mode + hass, name, entity_ids, user_defined, icon, object_id, mode, order ), hass.loop, ).result() @@ -436,28 +458,30 @@ class Group(Entity): icon=None, object_id=None, mode=None, + order=None, ): """Initialize a group. This method must be run in the event loop. """ - hass.data.setdefault(GROUP_ORDER, 0) + if order is None: + hass.data.setdefault(GROUP_ORDER, 0) + order = hass.data[GROUP_ORDER] + # Keep track of the group order without iterating + # every state in the state machine every time + # we setup a new group + hass.data[GROUP_ORDER] += 1 group = Group( hass, name, - order=hass.data[GROUP_ORDER], + order=order, icon=icon, user_defined=user_defined, entity_ids=entity_ids, mode=mode, ) - # Keep track of the group order without iterating - # every state in the state machine every time - # we setup a new group - hass.data[GROUP_ORDER] += 1 - group.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, object_id or name, hass=hass ) diff --git a/tests/components/group/test_init.py b/tests/components/group/test_init.py index 3709c4856a2..9684c107bb7 100644 --- a/tests/components/group/test_init.py +++ b/tests/components/group/test_init.py @@ -512,3 +512,54 @@ async def test_group_order(hass): assert hass.states.get("group.group_zero").attributes["order"] == 0 assert hass.states.get("group.group_one").attributes["order"] == 1 assert hass.states.get("group.group_two").attributes["order"] == 2 + + +async def test_group_order_with_dynamic_creation(hass): + """Test that order gets incremented when creating a new group.""" + hass.states.async_set("light.bowl", STATE_ON) + + assert await async_setup_component( + hass, + "group", + { + "group": { + "group_zero": {"entities": "light.Bowl", "icon": "mdi:work"}, + "group_one": {"entities": "light.Bowl", "icon": "mdi:work"}, + "group_two": {"entities": "light.Bowl", "icon": "mdi:work"}, + } + }, + ) + await hass.async_block_till_done() + + assert hass.states.get("group.group_zero").attributes["order"] == 0 + assert hass.states.get("group.group_one").attributes["order"] == 1 + assert hass.states.get("group.group_two").attributes["order"] == 2 + + await hass.services.async_call( + group.DOMAIN, + group.SERVICE_SET, + {"object_id": "new_group", "name": "New Group", "entities": "light.bowl"}, + ) + await hass.async_block_till_done() + + assert hass.states.get("group.new_group").attributes["order"] == 3 + + await hass.services.async_call( + group.DOMAIN, + group.SERVICE_REMOVE, + { + "object_id": "new_group", + }, + ) + await hass.async_block_till_done() + + assert not hass.states.get("group.new_group") + + await hass.services.async_call( + group.DOMAIN, + group.SERVICE_SET, + {"object_id": "new_group2", "name": "New Group 2", "entities": "light.bowl"}, + ) + await hass.async_block_till_done() + + assert hass.states.get("group.new_group2").attributes["order"] == 4