diff --git a/homeassistant/components/group/__init__.py b/homeassistant/components/group/__init__.py index 82c2651e764..1092bc5834b 100644 --- a/homeassistant/components/group/__init__.py +++ b/homeassistant/components/group/__init__.py @@ -294,7 +294,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def reload_service_handler(service: ServiceCall) -> None: """Remove all user-defined groups and load new ones from config.""" - auto = [e for e in component.entities if not e.user_defined] + auto = [e for e in component.entities if e.created_by_service] if (conf := await component.async_prepare_reload()) is None: return @@ -329,20 +329,15 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: or None ) - extra_arg = { - attr: service.data[attr] - for attr in (ATTR_ICON,) - if service.data.get(attr) is not None - } - await Group.async_create_group( hass, service.data.get(ATTR_NAME, object_id), - object_id=object_id, + created_by_service=True, entity_ids=entity_ids, - user_defined=False, + icon=service.data.get(ATTR_ICON), mode=service.data.get(ATTR_ALL), - **extra_arg, + object_id=object_id, + order=None, ) return @@ -449,7 +444,8 @@ async def _async_process_config(hass: HomeAssistant, config: ConfigType) -> None Group.async_create_group_entity( hass, name, - entity_ids, + created_by_service=False, + entity_ids=entity_ids, icon=icon, object_id=object_id, mode=mode, @@ -570,11 +566,12 @@ class Group(Entity): self, hass: HomeAssistant, name: str, - order: int | None = None, - icon: str | None = None, - user_defined: bool = True, - entity_ids: Collection[str] | None = None, - mode: bool | None = None, + *, + created_by_service: bool, + entity_ids: Collection[str] | None, + icon: str | None, + mode: bool | None, + order: int | None, ) -> None: """Initialize a group. @@ -588,7 +585,7 @@ class Group(Entity): self._on_off: dict[str, bool] = {} self._assumed: dict[str, bool] = {} self._on_states: set[str] = set() - self.user_defined = user_defined + self.created_by_service = created_by_service self.mode = any if mode: self.mode = all @@ -596,36 +593,18 @@ class Group(Entity): self._assumed_state = False self._async_unsub_state_changed: CALLBACK_TYPE | None = None - @staticmethod - def create_group( - hass: HomeAssistant, - name: str, - entity_ids: Collection[str] | None = None, - user_defined: bool = True, - icon: str | None = None, - object_id: str | None = None, - mode: bool | None = None, - order: int | None = None, - ) -> Group: - """Initialize a group.""" - return asyncio.run_coroutine_threadsafe( - Group.async_create_group( - hass, name, entity_ids, user_defined, icon, object_id, mode, order - ), - hass.loop, - ).result() - @staticmethod @callback def async_create_group_entity( hass: HomeAssistant, name: str, - entity_ids: Collection[str] | None = None, - user_defined: bool = True, - icon: str | None = None, - object_id: str | None = None, - mode: bool | None = None, - order: int | None = None, + *, + created_by_service: bool, + entity_ids: Collection[str] | None, + icon: str | None, + mode: bool | None, + object_id: str | None, + order: int | None, ) -> Group: """Create a group entity.""" if order is None: @@ -639,11 +618,11 @@ class Group(Entity): group = Group( hass, name, - order=order, - icon=icon, - user_defined=user_defined, + created_by_service=created_by_service, entity_ids=entity_ids, + icon=icon, mode=mode, + order=order, ) group.entity_id = async_generate_entity_id( @@ -656,19 +635,27 @@ class Group(Entity): async def async_create_group( hass: HomeAssistant, name: str, - entity_ids: Collection[str] | None = None, - user_defined: bool = True, - icon: str | None = None, - object_id: str | None = None, - mode: bool | None = None, - order: int | None = None, + *, + created_by_service: bool, + entity_ids: Collection[str] | None, + icon: str | None, + mode: bool | None, + object_id: str | None, + order: int | None, ) -> Group: """Initialize a group. This method must be run in the event loop. """ group = Group.async_create_group_entity( - hass, name, entity_ids, user_defined, icon, object_id, mode, order + hass, + name, + created_by_service=created_by_service, + entity_ids=entity_ids, + icon=icon, + mode=mode, + object_id=object_id, + order=order, ) # If called before the platform async_setup is called (test cases) @@ -704,7 +691,7 @@ class Group(Entity): def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes for the group.""" data = {ATTR_ENTITY_ID: self.tracking, ATTR_ORDER: self._order} - if not self.user_defined: + if self.created_by_service: data[ATTR_AUTO] = True return data diff --git a/tests/components/device_sun_light_trigger/test_init.py b/tests/components/device_sun_light_trigger/test_init.py index 6b563f1cb5f..724ae612f0d 100644 --- a/tests/components/device_sun_light_trigger/test_init.py +++ b/tests/components/device_sun_light_trigger/test_init.py @@ -182,7 +182,16 @@ async def test_lights_turn_on_when_coming_home_after_sun_set_person( assert await async_setup_component(hass, "group", {}) await hass.async_block_till_done() - await group.Group.async_create_group(hass, "person_me", ["person.me"]) + await group.Group.async_create_group( + hass, + "person_me", + created_by_service=False, + entity_ids=["person.me"], + icon=None, + mode=None, + object_id=None, + order=None, + ) assert await async_setup_component( hass, diff --git a/tests/components/group/test_init.py b/tests/components/group/test_init.py index 3ea75fbce06..c439506b52a 100644 --- a/tests/components/group/test_init.py +++ b/tests/components/group/test_init.py @@ -39,7 +39,14 @@ async def test_setup_group_with_mixed_groupable_states(hass: HomeAssistant) -> N assert await async_setup_component(hass, "group", {}) await group.Group.async_create_group( - hass, "person_and_light", ["light.Bowl", "device_tracker.Paulus"] + hass, + "person_and_light", + created_by_service=False, + entity_ids=["light.Bowl", "device_tracker.Paulus"], + icon=None, + mode=None, + object_id=None, + order=None, ) await hass.async_block_till_done() @@ -54,7 +61,14 @@ async def test_setup_group_with_a_non_existing_state(hass: HomeAssistant) -> Non assert await async_setup_component(hass, "group", {}) grp = await group.Group.async_create_group( - hass, "light_and_nothing", ["light.Bowl", "non.existing"] + hass, + "light_and_nothing", + created_by_service=False, + entity_ids=["light.Bowl", "non.existing"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert grp.state == STATE_ON @@ -68,7 +82,14 @@ async def test_setup_group_with_non_groupable_states(hass: HomeAssistant) -> Non assert await async_setup_component(hass, "group", {}) grp = await group.Group.async_create_group( - hass, "chromecasts", ["cast.living_room", "cast.bedroom"] + hass, + "chromecasts", + created_by_service=False, + entity_ids=["cast.living_room", "cast.bedroom"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert grp.state is None @@ -76,7 +97,16 @@ async def test_setup_group_with_non_groupable_states(hass: HomeAssistant) -> Non async def test_setup_empty_group(hass: HomeAssistant) -> None: """Try to set up an empty group.""" - grp = await group.Group.async_create_group(hass, "nothing", []) + grp = await group.Group.async_create_group( + hass, + "nothing", + created_by_service=False, + entity_ids=[], + icon=None, + mode=None, + object_id=None, + order=None, + ) assert grp.state is None @@ -89,7 +119,14 @@ async def test_monitor_group(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) # Test if group setup in our init mode is ok @@ -108,7 +145,14 @@ async def test_group_turns_off_if_all_off(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) await hass.async_block_till_done() @@ -127,7 +171,14 @@ async def test_group_turns_on_if_all_are_off_and_one_turns_on( assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) # Turn one on @@ -148,7 +199,14 @@ async def test_allgroup_stays_off_if_all_are_off_and_one_turns_on( assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False, mode=True + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=True, + object_id=None, + order=None, ) # Turn one on @@ -167,7 +225,14 @@ async def test_allgroup_turn_on_if_last_turns_on(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False, mode=True + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=True, + object_id=None, + order=None, ) # Turn one on @@ -186,7 +251,14 @@ async def test_expand_entity_ids(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert sorted(["light.ceiling", "light.bowl"]) == sorted( @@ -204,7 +276,14 @@ async def test_expand_entity_ids_does_not_return_duplicates( assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert ["light.bowl", "light.ceiling"] == sorted( @@ -226,8 +305,12 @@ async def test_expand_entity_ids_recursive(hass: HomeAssistant) -> None: test_group = await group.Group.async_create_group( hass, "init_group", - ["light.Bowl", "light.Ceiling", "group.init_group"], - False, + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling", "group.init_group"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert sorted(["light.ceiling", "light.bowl"]) == sorted( @@ -248,7 +331,14 @@ async def test_get_entity_ids(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert ["light.bowl", "light.ceiling"] == sorted( @@ -263,7 +353,14 @@ async def test_get_entity_ids_with_domain_filter(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) mixed_group = await group.Group.async_create_group( - hass, "mixed_group", ["light.Bowl", "switch.AC"], False + hass, + "mixed_group", + created_by_service=True, + entity_ids=["light.Bowl", "switch.AC"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert ["switch.ac"] == group.get_entity_ids( @@ -293,7 +390,14 @@ async def test_group_being_init_before_first_tracked_state_is_set_to_on( assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "test group", ["light.not_there_1"] + hass, + "test group", + created_by_service=False, + entity_ids=["light.not_there_1"], + icon=None, + mode=None, + object_id=None, + order=None, ) hass.states.async_set("light.not_there_1", STATE_ON) @@ -314,7 +418,14 @@ async def test_group_being_init_before_first_tracked_state_is_set_to_off( """ assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "test group", ["light.not_there_1"] + hass, + "test group", + created_by_service=False, + entity_ids=["light.not_there_1"], + icon=None, + mode=None, + object_id=None, + order=None, ) hass.states.async_set("light.not_there_1", STATE_OFF) @@ -330,8 +441,26 @@ async def test_groups_get_unique_names(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) - grp1 = await group.Group.async_create_group(hass, "Je suis Charlie") - grp2 = await group.Group.async_create_group(hass, "Je suis Charlie") + grp1 = await group.Group.async_create_group( + hass, + "Je suis Charlie", + created_by_service=False, + entity_ids=None, + icon=None, + mode=None, + object_id=None, + order=None, + ) + grp2 = await group.Group.async_create_group( + hass, + "Je suis Charlie", + created_by_service=False, + entity_ids=None, + icon=None, + mode=None, + object_id=None, + order=None, + ) assert grp1.entity_id != grp2.entity_id @@ -342,13 +471,34 @@ async def test_expand_entity_ids_expands_nested_groups(hass: HomeAssistant) -> N assert await async_setup_component(hass, "group", {}) await group.Group.async_create_group( - hass, "light", ["light.test_1", "light.test_2"] + hass, + "light", + created_by_service=False, + entity_ids=["light.test_1", "light.test_2"], + icon=None, + mode=None, + object_id=None, + order=None, ) await group.Group.async_create_group( - hass, "switch", ["switch.test_1", "switch.test_2"] + hass, + "switch", + created_by_service=False, + entity_ids=["switch.test_1", "switch.test_2"], + icon=None, + mode=None, + object_id=None, + order=None, ) await group.Group.async_create_group( - hass, "group_of_groups", ["group.light", "group.switch"] + hass, + "group_of_groups", + created_by_service=False, + entity_ids=["group.light", "group.switch"], + icon=None, + mode=None, + object_id=None, + order=None, ) assert [ @@ -367,7 +517,14 @@ async def test_set_assumed_state_based_on_tracked(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling", "sensor.no_exist"] + hass, + "init_group", + created_by_service=False, + entity_ids=["light.Bowl", "light.Ceiling", "sensor.no_exist"], + icon=None, + mode=None, + object_id=None, + order=None, ) state = hass.states.get(test_group.entity_id) @@ -398,7 +555,14 @@ async def test_group_updated_after_device_tracker_zone_change( assert await async_setup_component(hass, "device_tracker", {}) await group.Group.async_create_group( - hass, "peeps", ["device_tracker.Adam", "device_tracker.Eve"] + hass, + "peeps", + created_by_service=False, + entity_ids=["device_tracker.Adam", "device_tracker.Eve"], + icon=None, + mode=None, + object_id=None, + order=None, ) hass.states.async_set("device_tracker.Adam", "cool_state_not_home") @@ -417,7 +581,14 @@ async def test_is_on(hass: HomeAssistant) -> None: await hass.async_block_till_done() test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) await hass.async_block_till_done() @@ -446,7 +617,14 @@ async def test_reloading_groups(hass: HomeAssistant) -> None: await hass.async_block_till_done() await group.Group.async_create_group( - hass, "all tests", ["test.one", "test.two"], user_defined=False + hass, + "all tests", + created_by_service=True, + entity_ids=["test.one", "test.two"], + icon=None, + mode=None, + object_id=None, + order=None, ) await hass.async_block_till_done() @@ -523,14 +701,24 @@ async def test_setup(hass: HomeAssistant) -> None: await hass.async_block_till_done() test_group = await group.Group.async_create_group( - hass, "init_group", ["light.Bowl", "light.Ceiling"], False + hass, + "init_group", + created_by_service=True, + entity_ids=["light.Bowl", "light.Ceiling"], + icon=None, + mode=None, + object_id=None, + order=None, ) await group.Group.async_create_group( hass, "created_group", - ["light.Bowl", f"{test_group.entity_id}"], - True, - "mdi:work", + created_by_service=False, + entity_ids=["light.Bowl", f"{test_group.entity_id}"], + icon="mdi:work", + mode=None, + object_id=None, + order=None, ) await hass.async_block_till_done() diff --git a/tests/components/zwave_js/test_services.py b/tests/components/zwave_js/test_services.py index ccbe956fbe5..84d9b457d18 100644 --- a/tests/components/zwave_js/test_services.py +++ b/tests/components/zwave_js/test_services.py @@ -224,7 +224,16 @@ async def test_set_config_parameter( # Test groups get expanded assert await async_setup_component(hass, "group", {}) - await Group.async_create_group(hass, "test", [AIR_TEMPERATURE_SENSOR]) + await Group.async_create_group( + hass, + "test", + created_by_service=False, + entity_ids=[AIR_TEMPERATURE_SENSOR], + icon=None, + mode=None, + object_id=None, + order=None, + ) await hass.services.async_call( DOMAIN, SERVICE_SET_CONFIG_PARAMETER, @@ -594,7 +603,16 @@ async def test_bulk_set_config_parameters( # Test groups get expanded assert await async_setup_component(hass, "group", {}) - await Group.async_create_group(hass, "test", [AIR_TEMPERATURE_SENSOR]) + await Group.async_create_group( + hass, + "test", + created_by_service=False, + entity_ids=[AIR_TEMPERATURE_SENSOR], + icon=None, + mode=None, + object_id=None, + order=None, + ) await hass.services.async_call( DOMAIN, SERVICE_BULK_SET_PARTIAL_CONFIG_PARAMETERS, @@ -728,7 +746,16 @@ async def test_refresh_value( # Test groups get expanded assert await async_setup_component(hass, "group", {}) - await Group.async_create_group(hass, "test", [CLIMATE_RADIO_THERMOSTAT_ENTITY]) + await Group.async_create_group( + hass, + "test", + created_by_service=False, + entity_ids=[CLIMATE_RADIO_THERMOSTAT_ENTITY], + icon=None, + mode=None, + object_id=None, + order=None, + ) client.async_send_command.return_value = {"result": 2} await hass.services.async_call( DOMAIN, @@ -848,7 +875,16 @@ async def test_set_value( # Test groups get expanded assert await async_setup_component(hass, "group", {}) - await Group.async_create_group(hass, "test", [CLIMATE_DANFOSS_LC13_ENTITY]) + await Group.async_create_group( + hass, + "test", + created_by_service=False, + entity_ids=[CLIMATE_DANFOSS_LC13_ENTITY], + icon=None, + mode=None, + object_id=None, + order=None, + ) await hass.services.async_call( DOMAIN, SERVICE_SET_VALUE, @@ -1150,7 +1186,14 @@ async def test_multicast_set_value( # Test groups get expanded for multicast call assert await async_setup_component(hass, "group", {}) await Group.async_create_group( - hass, "test", [CLIMATE_DANFOSS_LC13_ENTITY, CLIMATE_EUROTRONICS_SPIRIT_Z_ENTITY] + hass, + "test", + created_by_service=False, + entity_ids=[CLIMATE_DANFOSS_LC13_ENTITY, CLIMATE_EUROTRONICS_SPIRIT_Z_ENTITY], + icon=None, + mode=None, + object_id=None, + order=None, ) await hass.services.async_call( DOMAIN, @@ -1516,7 +1559,14 @@ async def test_ping( # Test groups get expanded for multicast call assert await async_setup_component(hass, "group", {}) await Group.async_create_group( - hass, "test", [CLIMATE_DANFOSS_LC13_ENTITY, CLIMATE_RADIO_THERMOSTAT_ENTITY] + hass, + "test", + created_by_service=False, + entity_ids=[CLIMATE_DANFOSS_LC13_ENTITY, CLIMATE_RADIO_THERMOSTAT_ENTITY], + icon=None, + mode=None, + object_id=None, + order=None, ) await hass.services.async_call( DOMAIN, diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 03a8b5e11b2..04324cdbfa3 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -465,7 +465,14 @@ async def test_extract_entity_ids(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) await hass.async_block_till_done() await hass.components.group.Group.async_create_group( - hass, "test", ["light.Ceiling", "light.Kitchen"] + hass, + "test", + created_by_service=False, + entity_ids=["light.Ceiling", "light.Kitchen"], + icon=None, + mode=None, + object_id=None, + order=None, ) call = ServiceCall("light", "turn_on", {ATTR_ENTITY_ID: "light.Bowl"}) diff --git a/tests/helpers/test_template.py b/tests/helpers/test_template.py index c466bfed213..5f7ef594909 100644 --- a/tests/helpers/test_template.py +++ b/tests/helpers/test_template.py @@ -2649,7 +2649,16 @@ async def test_closest_function_home_vs_group_entity_id(hass: HomeAssistant) -> assert await async_setup_component(hass, "group", {}) await hass.async_block_till_done() - await group.Group.async_create_group(hass, "location group", ["test_domain.object"]) + await group.Group.async_create_group( + hass, + "location group", + created_by_service=False, + entity_ids=["test_domain.object"], + icon=None, + mode=None, + object_id=None, + order=None, + ) info = render_to_info(hass, '{{ closest("group.location_group").entity_id }}') assert_result_info( @@ -2677,7 +2686,16 @@ async def test_closest_function_home_vs_group_state(hass: HomeAssistant) -> None assert await async_setup_component(hass, "group", {}) await hass.async_block_till_done() - await group.Group.async_create_group(hass, "location group", ["test_domain.object"]) + await group.Group.async_create_group( + hass, + "location group", + created_by_service=False, + entity_ids=["test_domain.object"], + icon=None, + mode=None, + object_id=None, + order=None, + ) info = render_to_info(hass, '{{ closest("group.location_group").entity_id }}') assert_result_info( @@ -2727,7 +2745,16 @@ async def test_expand(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) await hass.async_block_till_done() - await group.Group.async_create_group(hass, "new group", ["test.object"]) + await group.Group.async_create_group( + hass, + "new group", + created_by_service=False, + entity_ids=["test.object"], + icon=None, + mode=None, + object_id=None, + order=None, + ) info = render_to_info( hass, @@ -2769,7 +2796,14 @@ async def test_expand(hass: HomeAssistant) -> None: assert await async_setup_component(hass, "group", {}) await hass.async_block_till_done() await group.Group.async_create_group( - hass, "power sensors", ["sensor.power_1", "sensor.power_2", "sensor.power_3"] + hass, + "power sensors", + created_by_service=False, + entity_ids=["sensor.power_1", "sensor.power_2", "sensor.power_3"], + icon=None, + mode=None, + object_id=None, + order=None, ) info = render_to_info(