From ee38dbd6986590c3ea36cd5d7c4e6dcdbb18ed31 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 10 Mar 2022 16:18:26 +0100 Subject: [PATCH] Add helper to set name of helper config entries (#67950) --- .../components/switch_as_x/config_flow.py | 19 ++--- .../helpers/helper_config_entry_flow.py | 26 ++++++- .../switch_as_x/test_config_flow.py | 71 +++---------------- tests/components/switch_as_x/test_light.py | 28 -------- .../helpers/test_helper_config_entry_flow.py | 38 ++++++++++ 5 files changed, 75 insertions(+), 107 deletions(-) create mode 100644 tests/helpers/test_helper_config_entry_flow.py diff --git a/homeassistant/components/switch_as_x/config_flow.py b/homeassistant/components/switch_as_x/config_flow.py index 90fb2e4bc0c..d59aecbc060 100644 --- a/homeassistant/components/switch_as_x/config_flow.py +++ b/homeassistant/components/switch_as_x/config_flow.py @@ -6,12 +6,7 @@ from typing import Any import voluptuous as vol -from homeassistant.core import split_entity_id -from homeassistant.helpers import ( - entity_registry as er, - helper_config_entry_flow, - selector, -) +from homeassistant.helpers import helper_config_entry_flow, selector from . import DOMAIN @@ -40,12 +35,6 @@ class SwitchAsXConfigFlowHandler( def async_config_entry_title(self, options: Mapping[str, Any]) -> str: """Return config entry title.""" - registry = er.async_get(self.hass) - object_id = split_entity_id(options["entity_id"])[1] - entry = registry.async_get(options["entity_id"]) - if entry: - return entry.name or entry.original_name or object_id - state = self.hass.states.get(options["entity_id"]) - if state: - return state.name or object_id - return object_id + return helper_config_entry_flow.wrapped_entity_config_entry_title( + self.hass, options["entity_id"] + ) diff --git a/homeassistant/helpers/helper_config_entry_flow.py b/homeassistant/helpers/helper_config_entry_flow.py index 7ef69b7f360..2e9c55baedd 100644 --- a/homeassistant/helpers/helper_config_entry_flow.py +++ b/homeassistant/helpers/helper_config_entry_flow.py @@ -10,9 +10,11 @@ from typing import Any import voluptuous as vol from homeassistant import config_entries -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback, split_entity_id from homeassistant.data_entry_flow import FlowResult, UnknownHandler +from . import entity_registry as er + @dataclass class HelperFlowStep: @@ -181,3 +183,25 @@ class HelperOptionsFlowHandler(config_entries.OptionsFlow): ) -> FlowResult: """Finish config flow and create a config entry.""" return super().async_create_entry(title="", **kwargs) + + +@callback +def wrapped_entity_config_entry_title( + hass: HomeAssistant, entity_id_or_uuid: str +) -> str: + """Generate title for a config entry wrapping a single entity. + + If the entity is registered, use the registry entry's name. + If the entity is in the state machine, use the name from the state. + Otherwise, fall back to the object ID. + """ + registry = er.async_get(hass) + entity_id = er.async_validate_entity_id(registry, entity_id_or_uuid) + object_id = split_entity_id(entity_id)[1] + entry = registry.async_get(entity_id) + if entry: + return entry.name or entry.original_name or object_id + state = hass.states.get(entity_id) + if state: + return state.name or object_id + return object_id diff --git a/tests/components/switch_as_x/test_config_flow.py b/tests/components/switch_as_x/test_config_flow.py index 223547dfb75..98770610f48 100644 --- a/tests/components/switch_as_x/test_config_flow.py +++ b/tests/components/switch_as_x/test_config_flow.py @@ -48,72 +48,17 @@ async def test_config_flow(hass: HomeAssistant, target_domain) -> None: "target_domain": target_domain, } - assert hass.states.get(f"{target_domain}.ceiling") + # Check the wrapped switch has a state and is added to the registry + state = hass.states.get(f"{target_domain}.ceiling") + assert state.state == "unavailable" + # Name copied from config entry title + assert state.name == "ceiling" -@pytest.mark.parametrize("target_domain", ("light",)) -async def test_name(hass: HomeAssistant, target_domain) -> None: - """Test the config flow name is copied from registry entry, with fallback to state.""" + # Check the light is added to the entity registry registry = er.async_get(hass) - - # No entry or state, use Object ID - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"entity_id": "switch.ceiling", "target_domain": target_domain}, - ) - assert result["title"] == "ceiling" - - # State set, use name from state - hass.states.async_set("switch.ceiling", "on", {"friendly_name": "State Name"}) - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"entity_id": "switch.ceiling", "target_domain": target_domain}, - ) - assert result["title"] == "State Name" - - # Entity registered, use original name from registry entry - hass.states.async_remove("switch.ceiling") - entry = registry.async_get_or_create( - "switch", - "test", - "unique", - suggested_object_id="ceiling", - original_name="Original Name", - ) - assert entry.entity_id == "switch.ceiling" - hass.states.async_set("switch.ceiling", "on", {"friendly_name": "State Name"}) - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"entity_id": "switch.ceiling", "target_domain": target_domain}, - ) - assert result["title"] == "Original Name" - - # Entity has customized name - registry.async_update_entity("switch.ceiling", name="Custom Name") - - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"entity_id": "switch.ceiling", "target_domain": target_domain}, - ) - assert result["title"] == "Custom Name" + entity_entry = registry.async_get(f"{target_domain}.ceiling") + assert entity_entry.unique_id == config_entry.entry_id @pytest.mark.parametrize("target_domain", ("light",)) diff --git a/tests/components/switch_as_x/test_light.py b/tests/components/switch_as_x/test_light.py index 302311b3845..26b6199cbc6 100644 --- a/tests/components/switch_as_x/test_light.py +++ b/tests/components/switch_as_x/test_light.py @@ -106,34 +106,6 @@ async def test_switch_service_calls(hass): assert hass.states.get("light.decorative_lights").state == "on" -@pytest.mark.parametrize("target_domain", ("light",)) -async def test_config_entry(hass: HomeAssistant, target_domain): - """Test light switch setup from config entry.""" - config_entry = MockConfigEntry( - data={}, - domain=DOMAIN, - options={"entity_id": "switch.abc", "target_domain": target_domain}, - title="ABC", - ) - - config_entry.add_to_hass(hass) - - assert await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - - assert DOMAIN in hass.config.components - - state = hass.states.get(f"{target_domain}.abc") - assert state.state == "unavailable" - # Name copied from config entry title - assert state.name == "ABC" - - # Check the light is added to the entity registry - registry = er.async_get(hass) - entity_entry = registry.async_get(f"{target_domain}.abc") - assert entity_entry.unique_id == config_entry.entry_id - - @pytest.mark.parametrize("target_domain", ("light",)) async def test_config_entry_uuid(hass: HomeAssistant, target_domain): """Test light switch setup from config entry with entity registry id.""" diff --git a/tests/helpers/test_helper_config_entry_flow.py b/tests/helpers/test_helper_config_entry_flow.py new file mode 100644 index 00000000000..a92a0cd3b36 --- /dev/null +++ b/tests/helpers/test_helper_config_entry_flow.py @@ -0,0 +1,38 @@ +"""Test helper_config_entry_flow.""" +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.helper_config_entry_flow import ( + wrapped_entity_config_entry_title, +) + + +async def test_name(hass: HomeAssistant) -> None: + """Test the config flow name is copied from registry entry, with fallback to state.""" + registry = er.async_get(hass) + entity_id = "switch.ceiling" + + # No entry or state, use Object ID + assert wrapped_entity_config_entry_title(hass, entity_id) == "ceiling" + + # State set, use name from state + hass.states.async_set(entity_id, "on", {"friendly_name": "State Name"}) + assert wrapped_entity_config_entry_title(hass, entity_id) == "State Name" + + # Entity registered, use original name from registry entry + hass.states.async_remove(entity_id) + entry = registry.async_get_or_create( + "switch", + "test", + "unique", + suggested_object_id="ceiling", + original_name="Original Name", + ) + hass.states.async_set(entity_id, "on", {"friendly_name": "State Name"}) + assert entry.entity_id == entity_id + assert wrapped_entity_config_entry_title(hass, entity_id) == "Original Name" + assert wrapped_entity_config_entry_title(hass, entry.id) == "Original Name" + + # Entity has customized name + registry.async_update_entity("switch.ceiling", name="Custom Name") + assert wrapped_entity_config_entry_title(hass, entity_id) == "Custom Name" + assert wrapped_entity_config_entry_title(hass, entry.id) == "Custom Name"