diff --git a/homeassistant/components/media_player/cast.py b/homeassistant/components/media_player/cast.py index a05653cd11554..67d8ea0b419a8 100644 --- a/homeassistant/components/media_player/cast.py +++ b/homeassistant/components/media_player/cast.py @@ -61,10 +61,6 @@ vol.All(cv.ensure_list, [cv.string]), }) -CONNECTION_RETRY = 3 -CONNECTION_RETRY_WAIT = 2 -CONNECTION_TIMEOUT = 10 - @attr.s(slots=True, frozen=True) class ChromecastInfo: @@ -216,9 +212,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities): if not isinstance(config, list): config = [config] - await asyncio.wait([ + # no pending task + done, _ = await asyncio.wait([ _async_setup_platform(hass, cfg, async_add_entities, None) for cfg in config]) + if any([task.exception() for task in done]): + raise PlatformNotReady async def _async_setup_platform(hass: HomeAssistantType, config: ConfigType, @@ -250,8 +249,8 @@ def async_cast_discovered(discover: ChromecastInfo) -> None: if cast_device is not None: async_add_entities([cast_device]) - async_dispatcher_connect(hass, SIGNAL_CAST_DISCOVERED, - async_cast_discovered) + remove_handler = async_dispatcher_connect( + hass, SIGNAL_CAST_DISCOVERED, async_cast_discovered) # Re-play the callback for all past chromecasts, store the objects in # a list to avoid concurrent modification resulting in exception. for chromecast in list(hass.data[KNOWN_CHROMECAST_INFO_KEY]): @@ -265,8 +264,11 @@ def async_cast_discovered(discover: ChromecastInfo) -> None: info = await hass.async_add_job(_fill_out_missing_chromecast_info, info) if info.friendly_name is None: - # HTTP dial failed, so we won't be able to connect. + _LOGGER.debug("Cannot retrieve detail information for chromecast" + " %s, the device may not be online", info) + remove_handler() raise PlatformNotReady + hass.async_add_job(_discover_chromecast, hass, info) @@ -379,7 +381,7 @@ async def async_set_cast_info(self, cast_info): pychromecast._get_chromecast_from_host, ( cast_info.host, cast_info.port, cast_info.uuid, cast_info.model_name, cast_info.friendly_name - ), CONNECTION_RETRY, CONNECTION_RETRY_WAIT, CONNECTION_TIMEOUT) + )) self._chromecast = chromecast self._status_listener = CastStatusListener(self, chromecast) # Initialise connection status as connected because we can only diff --git a/tests/common.py b/tests/common.py index 287f1e24ee50b..0cb15d683b566 100644 --- a/tests/common.py +++ b/tests/common.py @@ -609,16 +609,18 @@ def mock_open_f(fname, **_): return patch.object(yaml, 'open', mock_open_f, create=True) -def mock_coro(return_value=None): - """Return a coro that returns a value.""" - return mock_coro_func(return_value)() +def mock_coro(return_value=None, exception=None): + """Return a coro that returns a value or raise an exception.""" + return mock_coro_func(return_value, exception)() -def mock_coro_func(return_value=None): +def mock_coro_func(return_value=None, exception=None): """Return a method to create a coro function that returns a value.""" @asyncio.coroutine def coro(*args, **kwargs): """Fake coroutine.""" + if exception: + raise exception return return_value return coro diff --git a/tests/components/media_player/test_cast.py b/tests/components/media_player/test_cast.py index 7345fd0c1582a..8fd1ae1884118 100644 --- a/tests/components/media_player/test_cast.py +++ b/tests/components/media_player/test_cast.py @@ -415,3 +415,23 @@ async def test_entry_setup_list_config(hass: HomeAssistantType): assert len(mock_setup.mock_calls) == 2 assert mock_setup.mock_calls[0][1][1] == {'host': 'bla'} assert mock_setup.mock_calls[1][1][1] == {'host': 'blu'} + + +async def test_entry_setup_platform_not_ready(hass: HomeAssistantType): + """Test failed setting up entry will raise PlatformNotReady.""" + await async_setup_component(hass, 'cast', { + 'cast': { + 'media_player': { + 'host': 'bla' + } + } + }) + + with patch( + 'homeassistant.components.media_player.cast._async_setup_platform', + return_value=mock_coro(exception=Exception)) as mock_setup: + with pytest.raises(PlatformNotReady): + await cast.async_setup_entry(hass, MockConfigEntry(), None) + + assert len(mock_setup.mock_calls) == 1 + assert mock_setup.mock_calls[0][1][1] == {'host': 'bla'}