diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index 0ed293ea777..d4ca8aa58e8 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -58,6 +58,7 @@ ENTITY_ID_FORMAT = DOMAIN + ".{}" CONF_ALL = "all" ATTR_ADD_ENTITIES = "add_entities" +ATTR_REMOVE_ENTITIES = "remove_entities" ATTR_AUTO = "auto" ATTR_ENTITIES = "entities" ATTR_OBJECT_ID = "object_id" @@ -367,6 +368,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: entity_ids = set(group.tracking) | set(delta) await group.async_update_tracked_entity_ids(entity_ids) + if ATTR_REMOVE_ENTITIES in service.data: + delta = service.data[ATTR_REMOVE_ENTITIES] + entity_ids = set(group.tracking) - set(delta) + await group.async_update_tracked_entity_ids(entity_ids) + if ATTR_ENTITIES in service.data: entity_ids = service.data[ATTR_ENTITIES] await group.async_update_tracked_entity_ids(entity_ids) @@ -405,6 +411,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: vol.Optional(ATTR_ALL): cv.boolean, vol.Exclusive(ATTR_ENTITIES, "entities"): cv.entity_ids, vol.Exclusive(ATTR_ADD_ENTITIES, "entities"): cv.entity_ids, + vol.Exclusive(ATTR_REMOVE_ENTITIES, "entities"): cv.entity_ids, } ) ), diff --git a/homeassistant/components/group/services.yaml b/homeassistant/components/group/services.yaml index cba11b1723d..fdb1a1af014 100644 --- a/homeassistant/components/group/services.yaml +++ b/homeassistant/components/group/services.yaml @@ -38,6 +38,12 @@ set: example: domain.entity_id1, domain.entity_id2 selector: object: + remove_entities: + name: Remove Entities + description: List of members that will be removed from group listening. + example: domain.entity_id1, domain.entity_id2 + selector: + object: all: name: All description: Enable this option if the group should only turn on when all entities are on. diff --git a/tests/components/group/test_init.py b/tests/components/group/test_init.py index 6d3be9db4eb..57c2e9d3352 100644 --- a/tests/components/group/test_init.py +++ b/tests/components/group/test_init.py @@ -549,6 +549,62 @@ async def test_service_group_services(hass): assert hass.services.has_service("group", group.SERVICE_REMOVE) +async def test_service_group_services_add_remove_entities(hass: HomeAssistant) -> None: + """Check if we can add and remove entities from group.""" + + hass.states.async_set("person.one", "Work") + hass.states.async_set("person.two", "Work") + hass.states.async_set("person.three", "home") + + assert await async_setup_component(hass, "person", {}) + with assert_setup_component(0, "group"): + await async_setup_component(hass, "group", {"group": {}}) + + assert hass.services.has_service("group", group.SERVICE_SET) + + await hass.services.async_call( + group.DOMAIN, + group.SERVICE_SET, + { + "object_id": "new_group", + "name": "New Group", + "entities": ["person.one", "person.two"], + }, + ) + await hass.async_block_till_done() + + group_state = hass.states.get("group.new_group") + assert group_state.state == "not_home" + assert group_state.attributes["friendly_name"] == "New Group" + assert list(group_state.attributes["entity_id"]) == ["person.one", "person.two"] + + await hass.services.async_call( + group.DOMAIN, + group.SERVICE_SET, + { + "object_id": "new_group", + "add_entities": "person.three", + }, + ) + await hass.async_block_till_done() + group_state = hass.states.get("group.new_group") + assert group_state.state == "home" + assert "person.three" in list(group_state.attributes["entity_id"]) + + await hass.services.async_call( + group.DOMAIN, + group.SERVICE_SET, + { + "object_id": "new_group", + "remove_entities": "person.one", + }, + ) + await hass.async_block_till_done() + group_state = hass.states.get("group.new_group") + assert group_state.state == "home" + assert "person.one" not in list(group_state.attributes["entity_id"]) + + # pylint: disable=invalid-name async def test_service_group_set_group_remove_group(hass): """Check if service are available."""