Rework chromecast fix (#16804)

* Revert changes of #16471, and fix the platform setup issue

* Fix unit test

* Fix

* Fix comment

* Fix test

* Address review comment

* Address review comment
This commit is contained in:
Jason Hu 2018-09-23 14:35:07 -07:00 committed by Paulus Schoutsen
parent 329d9dfc06
commit 564ad7e22a
3 changed files with 37 additions and 13 deletions

View File

@ -61,10 +61,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.All(cv.ensure_list, [cv.string]), vol.All(cv.ensure_list, [cv.string]),
}) })
CONNECTION_RETRY = 3
CONNECTION_RETRY_WAIT = 2
CONNECTION_TIMEOUT = 10
@attr.s(slots=True, frozen=True) @attr.s(slots=True, frozen=True)
class ChromecastInfo: class ChromecastInfo:
@ -216,9 +212,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
if not isinstance(config, list): if not isinstance(config, list):
config = [config] config = [config]
await asyncio.wait([ # no pending task
done, _ = await asyncio.wait([
_async_setup_platform(hass, cfg, async_add_entities, None) _async_setup_platform(hass, cfg, async_add_entities, None)
for cfg in config]) for cfg in config])
if any([task.exception() for task in done]):
raise PlatformNotReady
async def _async_setup_platform(hass: HomeAssistantType, config: ConfigType, async def _async_setup_platform(hass: HomeAssistantType, config: ConfigType,
@ -250,8 +249,8 @@ async def _async_setup_platform(hass: HomeAssistantType, config: ConfigType,
if cast_device is not None: if cast_device is not None:
async_add_entities([cast_device]) async_add_entities([cast_device])
async_dispatcher_connect(hass, SIGNAL_CAST_DISCOVERED, remove_handler = async_dispatcher_connect(
async_cast_discovered) hass, SIGNAL_CAST_DISCOVERED, async_cast_discovered)
# Re-play the callback for all past chromecasts, store the objects in # Re-play the callback for all past chromecasts, store the objects in
# a list to avoid concurrent modification resulting in exception. # a list to avoid concurrent modification resulting in exception.
for chromecast in list(hass.data[KNOWN_CHROMECAST_INFO_KEY]): for chromecast in list(hass.data[KNOWN_CHROMECAST_INFO_KEY]):
@ -265,8 +264,11 @@ async def _async_setup_platform(hass: HomeAssistantType, config: ConfigType,
info = await hass.async_add_job(_fill_out_missing_chromecast_info, info = await hass.async_add_job(_fill_out_missing_chromecast_info,
info) info)
if info.friendly_name is None: 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 raise PlatformNotReady
hass.async_add_job(_discover_chromecast, hass, info) hass.async_add_job(_discover_chromecast, hass, info)
@ -379,7 +381,7 @@ class CastDevice(MediaPlayerDevice):
pychromecast._get_chromecast_from_host, ( pychromecast._get_chromecast_from_host, (
cast_info.host, cast_info.port, cast_info.uuid, cast_info.host, cast_info.port, cast_info.uuid,
cast_info.model_name, cast_info.friendly_name cast_info.model_name, cast_info.friendly_name
), CONNECTION_RETRY, CONNECTION_RETRY_WAIT, CONNECTION_TIMEOUT) ))
self._chromecast = chromecast self._chromecast = chromecast
self._status_listener = CastStatusListener(self, chromecast) self._status_listener = CastStatusListener(self, chromecast)
# Initialise connection status as connected because we can only # Initialise connection status as connected because we can only

View File

@ -609,16 +609,18 @@ def patch_yaml_files(files_dict, endswith=True):
return patch.object(yaml, 'open', mock_open_f, create=True) return patch.object(yaml, 'open', mock_open_f, create=True)
def mock_coro(return_value=None): def mock_coro(return_value=None, exception=None):
"""Return a coro that returns a value.""" """Return a coro that returns a value or raise an exception."""
return mock_coro_func(return_value)() 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.""" """Return a method to create a coro function that returns a value."""
@asyncio.coroutine @asyncio.coroutine
def coro(*args, **kwargs): def coro(*args, **kwargs):
"""Fake coroutine.""" """Fake coroutine."""
if exception:
raise exception
return return_value return return_value
return coro return coro

View File

@ -415,3 +415,23 @@ async def test_entry_setup_list_config(hass: HomeAssistantType):
assert len(mock_setup.mock_calls) == 2 assert len(mock_setup.mock_calls) == 2
assert mock_setup.mock_calls[0][1][1] == {'host': 'bla'} assert mock_setup.mock_calls[0][1][1] == {'host': 'bla'}
assert mock_setup.mock_calls[1][1][1] == {'host': 'blu'} 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'}