diff --git a/homeassistant/components/vizio/config_flow.py b/homeassistant/components/vizio/config_flow.py index 97d54f2e874..9cca89f77aa 100644 --- a/homeassistant/components/vizio/config_flow.py +++ b/homeassistant/components/vizio/config_flow.py @@ -11,6 +11,7 @@ from pyvizio.const import APP_HOME import voluptuous as vol from homeassistant import config_entries +from homeassistant.components import zeroconf from homeassistant.components.media_player import DEVICE_CLASS_SPEAKER, DEVICE_CLASS_TV from homeassistant.config_entries import ( SOURCE_IGNORE, @@ -26,14 +27,11 @@ from homeassistant.const import ( CONF_INCLUDE, CONF_NAME, CONF_PIN, - CONF_PORT, - CONF_TYPE, ) from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers import config_validation as cv from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.util.network import is_ip_address from .const import ( @@ -338,28 +336,25 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return await self.async_step_user(user_input=import_config) async def async_step_zeroconf( - self, discovery_info: DiscoveryInfoType | None = None + self, discovery_info: zeroconf.ZeroconfServiceInfo ) -> FlowResult: """Handle zeroconf discovery.""" + host = discovery_info.host # If host already has port, no need to add it again - if ":" not in discovery_info[CONF_HOST]: - discovery_info[ - CONF_HOST - ] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}" + if ":" not in host: + host = f"{host}:{discovery_info.port}" # Set default name to discovered device name by stripping zeroconf service # (`type`) from `name` - num_chars_to_strip = len(discovery_info[CONF_TYPE]) + 1 - discovery_info[CONF_NAME] = discovery_info[CONF_NAME][:-num_chars_to_strip] + num_chars_to_strip = len(discovery_info.type) + 1 + name = discovery_info.name[:-num_chars_to_strip] - discovery_info[CONF_DEVICE_CLASS] = await async_guess_device_type( - discovery_info[CONF_HOST] - ) + device_class = await async_guess_device_type(host) # Set unique ID early for discovery flow so we can abort if needed unique_id = await VizioAsync.get_unique_id( - discovery_info[CONF_HOST], - discovery_info[CONF_DEVICE_CLASS], + host, + device_class, session=async_get_clientsession(self.hass, False), ) @@ -372,7 +367,13 @@ class VizioConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # Form must be shown after discovery so user can confirm/update configuration # before ConfigEntry creation. self._must_show_form = True - return await self.async_step_user(user_input=discovery_info) + return await self.async_step_user( + user_input={ + CONF_HOST: host, + CONF_NAME: name, + CONF_DEVICE_CLASS: device_class, + } + ) async def async_step_pair_tv(self, user_input: dict[str, Any] = None) -> FlowResult: """ diff --git a/tests/components/vizio/const.py b/tests/components/vizio/const.py index 963947f1bd3..b4a3fc04766 100644 --- a/tests/components/vizio/const.py +++ b/tests/components/vizio/const.py @@ -1,4 +1,5 @@ """Constants for the Vizio integration tests.""" +from homeassistant.components import zeroconf from homeassistant.components.media_player import ( DEVICE_CLASS_SPEAKER, DEVICE_CLASS_TV, @@ -23,8 +24,6 @@ from homeassistant.const import ( CONF_INCLUDE, CONF_NAME, CONF_PIN, - CONF_PORT, - CONF_TYPE, ) from homeassistant.util import slugify @@ -198,10 +197,11 @@ ZEROCONF_NAME = f"{NAME}.{VIZIO_ZEROCONF_SERVICE_TYPE}" ZEROCONF_HOST = HOST.split(":")[0] ZEROCONF_PORT = HOST.split(":")[1] -MOCK_ZEROCONF_SERVICE_INFO = { - CONF_TYPE: VIZIO_ZEROCONF_SERVICE_TYPE, - CONF_NAME: ZEROCONF_NAME, - CONF_HOST: ZEROCONF_HOST, - CONF_PORT: ZEROCONF_PORT, - "properties": {"name": "SB4031-D5"}, -} +MOCK_ZEROCONF_SERVICE_INFO = zeroconf.ZeroconfServiceInfo( + host=ZEROCONF_HOST, + hostname="mock_hostname", + name=ZEROCONF_NAME, + port=ZEROCONF_PORT, + properties={"name": "SB4031-D5"}, + type=VIZIO_ZEROCONF_SERVICE_TYPE, +) diff --git a/tests/components/vizio/test_config_flow.py b/tests/components/vizio/test_config_flow.py index 544ad2b38cd..d02008c6d3d 100644 --- a/tests/components/vizio/test_config_flow.py +++ b/tests/components/vizio/test_config_flow.py @@ -1,8 +1,11 @@ """Tests for Vizio config flow.""" +import dataclasses + import pytest import voluptuous as vol from homeassistant import data_entry_flow +from homeassistant.components import zeroconf from homeassistant.components.media_player import DEVICE_CLASS_SPEAKER, DEVICE_CLASS_TV from homeassistant.components.vizio.config_flow import _get_config_schema from homeassistant.components.vizio.const import ( @@ -27,7 +30,6 @@ from homeassistant.const import ( CONF_HOST, CONF_NAME, CONF_PIN, - CONF_PORT, ) from homeassistant.core import HomeAssistant @@ -728,7 +730,7 @@ async def test_zeroconf_flow( vizio_guess_device_type: pytest.fixture, ) -> None: """Test zeroconf config flow.""" - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -739,7 +741,15 @@ async def test_zeroconf_flow( # Apply discovery updates to entry to mimic when user hits submit without changing # defaults which were set from discovery parameters - user_input = result["data_schema"](discovery_info) + user_input = result["data_schema"]( + { + CONF_HOST: f"{discovery_info[zeroconf.ATTR_HOST]}:{discovery_info[zeroconf.ATTR_PORT]}", + CONF_NAME: discovery_info[zeroconf.ATTR_NAME][ + : -(len(discovery_info[zeroconf.ATTR_TYPE]) + 1) + ], + CONF_DEVICE_CLASS: "speaker", + } + ) result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input=user_input @@ -768,7 +778,7 @@ async def test_zeroconf_flow_already_configured( entry.add_to_hass(hass) # Try rediscovering same device - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -794,10 +804,8 @@ async def test_zeroconf_flow_with_port_in_host( entry.add_to_hass(hass) # Try rediscovering same device, this time with port already in host - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() - discovery_info[ - CONF_HOST - ] = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}" + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) + discovery_info.host = f"{discovery_info.host}:{discovery_info.port}" result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -814,7 +822,7 @@ async def test_zeroconf_dupe_fail( vizio_guess_device_type: pytest.fixture, ) -> None: """Test zeroconf config flow when device gets discovered multiple times.""" - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -823,7 +831,7 @@ async def test_zeroconf_dupe_fail( assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -848,7 +856,7 @@ async def test_zeroconf_ignore( ) entry.add_to_hass(hass) - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -863,7 +871,7 @@ async def test_zeroconf_no_unique_id( ) -> None: """Test zeroconf discovery aborts when unique_id is None.""" - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -888,7 +896,7 @@ async def test_zeroconf_abort_when_ignored( ) entry.add_to_hass(hass) - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info ) @@ -916,7 +924,7 @@ async def test_zeroconf_flow_already_configured_hostname( entry.add_to_hass(hass) # Try rediscovering same device - discovery_info = MOCK_ZEROCONF_SERVICE_INFO.copy() + discovery_info = dataclasses.replace(MOCK_ZEROCONF_SERVICE_INFO) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info )