Add support for when device is not logged in to HEOS (#22913)

This commit is contained in:
Andrew Sayre 2019-04-09 11:21:00 -05:00 committed by Martin Hjelmare
parent 0d2646ba25
commit 4110bd0acf
5 changed files with 82 additions and 17 deletions

View file

@ -75,12 +75,16 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
await controller.disconnect() await controller.disconnect()
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect_controller) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect_controller)
# Get players and sources
try: try:
players, favorites, inputs = await asyncio.gather( players = await controller.get_players()
controller.get_players(), favorites = {}
controller.get_favorites(), if controller.is_signed_in:
controller.get_input_sources() favorites = await controller.get_favorites()
) else:
_LOGGER.warning("%s is not logged in to your HEOS account and will"
" be unable to retrieve your favorites", host)
inputs = await controller.get_input_sources()
except (asyncio.TimeoutError, ConnectionError, CommandError) as error: except (asyncio.TimeoutError, ConnectionError, CommandError) as error:
await controller.disconnect() await controller.disconnect()
_LOGGER.debug("Unable to retrieve players and sources: %s", error, _LOGGER.debug("Unable to retrieve players and sources: %s", error,
@ -175,9 +179,11 @@ class SourceManager:
retry_attempts = 0 retry_attempts = 0
while True: while True:
try: try:
return await asyncio.gather( favorites = {}
controller.get_favorites(), if controller.is_signed_in:
controller.get_input_sources()) favorites = await controller.get_favorites()
inputs = await controller.get_input_sources()
return favorites, inputs
except (asyncio.TimeoutError, ConnectionError, CommandError) \ except (asyncio.TimeoutError, ConnectionError, CommandError) \
as error: as error:
if retry_attempts < self.max_retry_attempts: if retry_attempts < self.max_retry_attempts:
@ -192,7 +198,8 @@ class SourceManager:
return return
async def update_sources(event): async def update_sources(event):
if event in const.EVENT_SOURCES_CHANGED: if event in (const.EVENT_SOURCES_CHANGED,
const.EVENT_USER_CHANGED):
sources = await get_sources() sources = await get_sources()
# If throttled, it will return None # If throttled, it will return None
if sources: if sources:

View file

@ -141,7 +141,7 @@ class HeosMediaPlayer(MediaPlayerDevice):
async def async_set_volume_level(self, volume): async def async_set_volume_level(self, volume):
"""Set volume level, range 0..1.""" """Set volume level, range 0..1."""
await self._player.set_volume(volume * 100) await self._player.set_volume(int(volume * 100))
async def async_update(self): async def async_update(self):
"""Update supported features of the player.""" """Update supported features of the player."""

View file

@ -28,6 +28,8 @@ def controller_fixture(players, favorites, input_sources, dispatcher):
mock_heos.players = players mock_heos.players = players
mock_heos.get_favorites.return_value = favorites mock_heos.get_favorites.return_value = favorites
mock_heos.get_input_sources.return_value = input_sources mock_heos.get_input_sources.return_value = input_sources
mock_heos.is_signed_in = True
mock_heos.signed_in_username = "user@user.com"
yield mock_heos yield mock_heos

View file

@ -5,8 +5,7 @@ from asynctest import patch
from pyheos import CommandError, const from pyheos import CommandError, const
import pytest import pytest
from homeassistant.components.heos import ( from homeassistant.components.heos import async_setup_entry, async_unload_entry
SourceManager, async_setup_entry, async_unload_entry)
from homeassistant.components.heos.const import ( from homeassistant.components.heos.const import (
DATA_CONTROLLER, DATA_SOURCE_MANAGER, DOMAIN) DATA_CONTROLLER, DATA_SOURCE_MANAGER, DOMAIN)
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
@ -61,7 +60,7 @@ async def test_async_setup_no_config_returns_true(hass, config_entry):
async def test_async_setup_entry_loads_platforms( async def test_async_setup_entry_loads_platforms(
hass, config_entry, controller): hass, config_entry, controller, input_sources, favorites):
"""Test load connects to heos, retrieves players, and loads platforms.""" """Test load connects to heos, retrieves players, and loads platforms."""
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
with patch.object( with patch.object(
@ -71,10 +70,39 @@ async def test_async_setup_entry_loads_platforms(
await hass.async_block_till_done() await hass.async_block_till_done()
assert forward_mock.call_count == 1 assert forward_mock.call_count == 1
assert controller.connect.call_count == 1 assert controller.connect.call_count == 1
assert controller.get_players.call_count == 1
assert controller.get_favorites.call_count == 1
assert controller.get_input_sources.call_count == 1
controller.disconnect.assert_not_called() controller.disconnect.assert_not_called()
assert hass.data[DOMAIN][DATA_CONTROLLER] == controller assert hass.data[DOMAIN][DATA_CONTROLLER] == controller
assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players
assert isinstance(hass.data[DOMAIN][DATA_SOURCE_MANAGER], SourceManager) assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].favorites == favorites
assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].inputs == input_sources
async def test_async_setup_entry_not_signed_in_loads_platforms(
hass, config_entry, controller, input_sources, caplog):
"""Test setup does not retrieve favorites when not logged in."""
config_entry.add_to_hass(hass)
controller.is_signed_in = False
controller.signed_in_username = None
with patch.object(
hass.config_entries, 'async_forward_entry_setup') as forward_mock:
assert await async_setup_entry(hass, config_entry)
# Assert platforms loaded
await hass.async_block_till_done()
assert forward_mock.call_count == 1
assert controller.connect.call_count == 1
assert controller.get_players.call_count == 1
assert controller.get_favorites.call_count == 0
assert controller.get_input_sources.call_count == 1
controller.disconnect.assert_not_called()
assert hass.data[DOMAIN][DATA_CONTROLLER] == controller
assert hass.data[DOMAIN][MEDIA_PLAYER_DOMAIN] == controller.players
assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].favorites == {}
assert hass.data[DOMAIN][DATA_SOURCE_MANAGER].inputs == input_sources
assert "127.0.0.1 is not logged in to your HEOS account and will be " \
"unable to retrieve your favorites" in caplog.text
async def test_async_setup_entry_connect_failure( async def test_async_setup_entry_connect_failure(
@ -138,4 +166,3 @@ async def test_update_sources_retry(hass, config_entry, config, controller,
while "Unable to update sources" not in caplog.text: while "Unable to update sources" not in caplog.text:
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
assert controller.get_favorites.call_count == 2 assert controller.get_favorites.call_count == 2
assert controller.get_input_sources.call_count == 2

View file

@ -116,7 +116,12 @@ async def test_updates_start_from_signals(
state = hass.states.get('media_player.test_player') state = hass.states.get('media_player.test_player')
assert state.state == STATE_PLAYING assert state.state == STATE_PLAYING
# Test sources event update
async def test_updates_from_sources_updated(
hass, config_entry, config, controller, input_sources):
"""Tests player updates from changes in sources list."""
await setup_platform(hass, config_entry, config)
player = controller.players[1]
event = asyncio.Event() event = asyncio.Event()
async def set_signal(): async def set_signal():
@ -124,11 +129,34 @@ async def test_updates_start_from_signals(
hass.helpers.dispatcher.async_dispatcher_connect( hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_HEOS_SOURCES_UPDATED, set_signal) SIGNAL_HEOS_SOURCES_UPDATED, set_signal)
favorites.clear() input_sources.clear()
player.heos.dispatcher.send( player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED) const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED)
await event.wait() await event.wait()
source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list
assert len(source_list) == 2
state = hass.states.get('media_player.test_player')
assert state.attributes[ATTR_INPUT_SOURCE_LIST] == source_list
async def test_updates_from_user_changed(
hass, config_entry, config, controller):
"""Tests player updates from changes in user."""
await setup_platform(hass, config_entry, config)
player = controller.players[1]
event = asyncio.Event()
async def set_signal():
event.set()
hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_HEOS_SOURCES_UPDATED, set_signal)
controller.is_signed_in = False
controller.signed_in_username = None
player.heos.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_USER_CHANGED)
await event.wait()
source_list = hass.data[DOMAIN][DATA_SOURCE_MANAGER].source_list
assert len(source_list) == 1 assert len(source_list) == 1
state = hass.states.get('media_player.test_player') state = hass.states.get('media_player.test_player')
assert state.attributes[ATTR_INPUT_SOURCE_LIST] == source_list assert state.attributes[ATTR_INPUT_SOURCE_LIST] == source_list
@ -194,6 +222,7 @@ async def test_services(hass, config_entry, config, controller):
{ATTR_ENTITY_ID: 'media_player.test_player', {ATTR_ENTITY_ID: 'media_player.test_player',
ATTR_MEDIA_VOLUME_LEVEL: 1}, blocking=True) ATTR_MEDIA_VOLUME_LEVEL: 1}, blocking=True)
player.set_volume.assert_called_once_with(100) player.set_volume.assert_called_once_with(100)
assert isinstance(player.set_volume.call_args[0][0], int)
async def test_select_favorite( async def test_select_favorite(