Move imports to top (#24108)

This commit is contained in:
Andrew Sayre 2019-05-26 06:47:11 -05:00 committed by Pascal Vizeli
parent 25505dc1d4
commit 0194905e97
4 changed files with 54 additions and 58 deletions

View File

@ -4,6 +4,7 @@ from datetime import timedelta
import logging import logging
from typing import Dict from typing import Dict
from pyheos import CommandError, Heos, const as heos_const
import voluptuous as vol import voluptuous as vol
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
@ -57,7 +58,6 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType):
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
"""Initialize config entry which represents the HEOS controller.""" """Initialize config entry which represents the HEOS controller."""
from pyheos import Heos, CommandError
host = entry.data[CONF_HOST] host = entry.data[CONF_HOST]
# Setting all_progress_events=False ensures that we only receive a # Setting all_progress_events=False ensures that we only receive a
# media position update upon start of playback or when media changes # media position update upon start of playback or when media changes
@ -137,16 +137,15 @@ class ControllerManager:
async def connect_listeners(self): async def connect_listeners(self):
"""Subscribe to events of interest.""" """Subscribe to events of interest."""
from pyheos import const
self._device_registry, self._entity_registry = await asyncio.gather( self._device_registry, self._entity_registry = await asyncio.gather(
self._hass.helpers.device_registry.async_get_registry(), self._hass.helpers.device_registry.async_get_registry(),
self._hass.helpers.entity_registry.async_get_registry()) self._hass.helpers.entity_registry.async_get_registry())
# Handle controller events # Handle controller events
self._signals.append(self.controller.dispatcher.connect( self._signals.append(self.controller.dispatcher.connect(
const.SIGNAL_CONTROLLER_EVENT, self._controller_event)) heos_const.SIGNAL_CONTROLLER_EVENT, self._controller_event))
# Handle connection-related events # Handle connection-related events
self._signals.append(self.controller.dispatcher.connect( self._signals.append(self.controller.dispatcher.connect(
const.SIGNAL_HEOS_EVENT, self._heos_event)) heos_const.SIGNAL_HEOS_EVENT, self._heos_event))
async def disconnect(self): async def disconnect(self):
"""Disconnect subscriptions.""" """Disconnect subscriptions."""
@ -158,21 +157,19 @@ class ControllerManager:
async def _controller_event(self, event, data): async def _controller_event(self, event, data):
"""Handle controller event.""" """Handle controller event."""
from pyheos import const if event == heos_const.EVENT_PLAYERS_CHANGED:
if event == const.EVENT_PLAYERS_CHANGED: self.update_ids(data[heos_const.DATA_MAPPED_IDS])
self.update_ids(data[const.DATA_MAPPED_IDS])
# Update players # Update players
self._hass.helpers.dispatcher.async_dispatcher_send( self._hass.helpers.dispatcher.async_dispatcher_send(
SIGNAL_HEOS_UPDATED) SIGNAL_HEOS_UPDATED)
async def _heos_event(self, event): async def _heos_event(self, event):
"""Handle connection event.""" """Handle connection event."""
from pyheos import CommandError, const if event == heos_const.EVENT_CONNECTED:
if event == const.EVENT_CONNECTED:
try: try:
# Retrieve latest players and refresh status # Retrieve latest players and refresh status
data = await self.controller.load_players() data = await self.controller.load_players()
self.update_ids(data[const.DATA_MAPPED_IDS]) self.update_ids(data[heos_const.DATA_MAPPED_IDS])
except (CommandError, asyncio.TimeoutError, ConnectionError) as ex: except (CommandError, asyncio.TimeoutError, ConnectionError) as ex:
_LOGGER.error("Unable to refresh players: %s", ex) _LOGGER.error("Unable to refresh players: %s", ex)
# Update players # Update players
@ -241,9 +238,8 @@ class SourceManager:
def get_current_source(self, now_playing_media): def get_current_source(self, now_playing_media):
"""Determine current source from now playing media.""" """Determine current source from now playing media."""
from pyheos import const
# Match input by input_name:media_id # Match input by input_name:media_id
if now_playing_media.source_id == const.MUSIC_SOURCE_AUX_INPUT: if now_playing_media.source_id == heos_const.MUSIC_SOURCE_AUX_INPUT:
return next((input_source.name for input_source in self.inputs return next((input_source.name for input_source in self.inputs
if input_source.input_name == if input_source.input_name ==
now_playing_media.media_id), None) now_playing_media.media_id), None)
@ -260,8 +256,6 @@ class SourceManager:
physical event therefore throttle it. Retrieving sources immediately physical event therefore throttle it. Retrieving sources immediately
after the event may fail so retry. after the event may fail so retry.
""" """
from pyheos import CommandError, const
@Throttle(MIN_UPDATE_SOURCES) @Throttle(MIN_UPDATE_SOURCES)
async def get_sources(): async def get_sources():
retry_attempts = 0 retry_attempts = 0
@ -286,9 +280,9 @@ class SourceManager:
return return
async def update_sources(event, data=None): async def update_sources(event, data=None):
if event in (const.EVENT_SOURCES_CHANGED, if event in (heos_const.EVENT_SOURCES_CHANGED,
const.EVENT_USER_CHANGED, heos_const.EVENT_USER_CHANGED,
const.EVENT_CONNECTED): heos_const.EVENT_CONNECTED):
sources = await get_sources() sources = await get_sources()
# If throttled, it will return None # If throttled, it will return None
if sources: if sources:
@ -300,6 +294,6 @@ class SourceManager:
SIGNAL_HEOS_UPDATED) SIGNAL_HEOS_UPDATED)
controller.dispatcher.connect( controller.dispatcher.connect(
const.SIGNAL_CONTROLLER_EVENT, update_sources) heos_const.SIGNAL_CONTROLLER_EVENT, update_sources)
controller.dispatcher.connect( controller.dispatcher.connect(
const.SIGNAL_HEOS_EVENT, update_sources) heos_const.SIGNAL_HEOS_EVENT, update_sources)

View File

@ -1,6 +1,7 @@
"""Config flow to configure Heos.""" """Config flow to configure Heos."""
import asyncio import asyncio
from pyheos import Heos
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
@ -44,7 +45,6 @@ class HeosFlowHandler(config_entries.ConfigFlow):
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Obtain host and validate connection.""" """Obtain host and validate connection."""
from pyheos import Heos
self.hass.data.setdefault(DATA_DISCOVERED_HOSTS, {}) self.hass.data.setdefault(DATA_DISCOVERED_HOSTS, {})
# Only a single entry is needed for all devices # Only a single entry is needed for all devices
if self._async_current_entries(): if self._async_current_entries():

View File

@ -5,6 +5,8 @@ import logging
from operator import ior from operator import ior
from typing import Sequence from typing import Sequence
from pyheos import CommandError, const as heos_const
from homeassistant.components.media_player import MediaPlayerDevice from homeassistant.components.media_player import MediaPlayerDevice
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
ATTR_MEDIA_ENQUEUE, DOMAIN, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, ATTR_MEDIA_ENQUEUE, DOMAIN, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST,
@ -25,6 +27,20 @@ BASE_SUPPORTED_FEATURES = SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \
SUPPORT_SHUFFLE_SET | SUPPORT_SELECT_SOURCE | \ SUPPORT_SHUFFLE_SET | SUPPORT_SELECT_SOURCE | \
SUPPORT_PLAY_MEDIA SUPPORT_PLAY_MEDIA
PLAY_STATE_TO_STATE = {
heos_const.PLAY_STATE_PLAY: STATE_PLAYING,
heos_const.PLAY_STATE_STOP: STATE_IDLE,
heos_const.PLAY_STATE_PAUSE: STATE_PAUSED
}
CONTROL_TO_SUPPORT = {
heos_const.CONTROL_PLAY: SUPPORT_PLAY,
heos_const.CONTROL_PAUSE: SUPPORT_PAUSE,
heos_const.CONTROL_STOP: SUPPORT_STOP,
heos_const.CONTROL_PLAY_PREVIOUS: SUPPORT_PREVIOUS_TRACK,
heos_const.CONTROL_PLAY_NEXT: SUPPORT_NEXT_TRACK
}
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -47,7 +63,6 @@ def log_command_error(command: str):
def decorator(func): def decorator(func):
@wraps(func) @wraps(func)
async def wrapper(*args, **kwargs): async def wrapper(*args, **kwargs):
from pyheos import CommandError
try: try:
await func(*args, **kwargs) await func(*args, **kwargs)
except (CommandError, asyncio.TimeoutError, ConnectionError, except (CommandError, asyncio.TimeoutError, ConnectionError,
@ -62,31 +77,17 @@ class HeosMediaPlayer(MediaPlayerDevice):
def __init__(self, player): def __init__(self, player):
"""Initialize.""" """Initialize."""
from pyheos import const
self._media_position_updated_at = None self._media_position_updated_at = None
self._player = player self._player = player
self._signals = [] self._signals = []
self._supported_features = BASE_SUPPORTED_FEATURES self._supported_features = BASE_SUPPORTED_FEATURES
self._source_manager = None self._source_manager = None
self._play_state_to_state = {
const.PLAY_STATE_PLAY: STATE_PLAYING,
const.PLAY_STATE_STOP: STATE_IDLE,
const.PLAY_STATE_PAUSE: STATE_PAUSED
}
self._control_to_support = {
const.CONTROL_PLAY: SUPPORT_PLAY,
const.CONTROL_PAUSE: SUPPORT_PAUSE,
const.CONTROL_STOP: SUPPORT_STOP,
const.CONTROL_PLAY_PREVIOUS: SUPPORT_PREVIOUS_TRACK,
const.CONTROL_PLAY_NEXT: SUPPORT_NEXT_TRACK
}
async def _player_update(self, player_id, event): async def _player_update(self, player_id, event):
"""Handle player attribute updated.""" """Handle player attribute updated."""
from pyheos import const
if self._player.player_id != player_id: if self._player.player_id != player_id:
return return
if event == const.EVENT_PLAYER_NOW_PLAYING_PROGRESS: if event == heos_const.EVENT_PLAYER_NOW_PLAYING_PROGRESS:
self._media_position_updated_at = utcnow() self._media_position_updated_at = utcnow()
await self.async_update_ha_state(True) await self.async_update_ha_state(True)
@ -96,11 +97,10 @@ class HeosMediaPlayer(MediaPlayerDevice):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Device added to hass.""" """Device added to hass."""
from pyheos import const
self._source_manager = self.hass.data[HEOS_DOMAIN][DATA_SOURCE_MANAGER] self._source_manager = self.hass.data[HEOS_DOMAIN][DATA_SOURCE_MANAGER]
# Update state when attributes of the player change # Update state when attributes of the player change
self._signals.append(self._player.heos.dispatcher.connect( self._signals.append(self._player.heos.dispatcher.connect(
const.SIGNAL_PLAYER_EVENT, self._player_update)) heos_const.SIGNAL_PLAYER_EVENT, self._player_update))
# Update state when heos changes # Update state when heos changes
self._signals.append( self._signals.append(
self.hass.helpers.dispatcher.async_dispatcher_connect( self.hass.helpers.dispatcher.async_dispatcher_connect(
@ -163,14 +163,13 @@ class HeosMediaPlayer(MediaPlayerDevice):
return return
if media_type == MEDIA_TYPE_PLAYLIST: if media_type == MEDIA_TYPE_PLAYLIST:
from pyheos import const
playlists = await self._player.heos.get_playlists() playlists = await self._player.heos.get_playlists()
playlist = next((p for p in playlists if p.name == media_id), None) playlist = next((p for p in playlists if p.name == media_id), None)
if not playlist: if not playlist:
raise ValueError("Invalid playlist '{}'".format(media_id)) raise ValueError("Invalid playlist '{}'".format(media_id))
add_queue_option = const.ADD_QUEUE_ADD_TO_END \ add_queue_option = heos_const.ADD_QUEUE_ADD_TO_END \
if kwargs.get(ATTR_MEDIA_ENQUEUE) \ if kwargs.get(ATTR_MEDIA_ENQUEUE) \
else const.ADD_QUEUE_REPLACE_AND_PLAY else heos_const.ADD_QUEUE_REPLACE_AND_PLAY
await self._player.add_to_queue(playlist, add_queue_option) await self._player.add_to_queue(playlist, add_queue_option)
return return
@ -208,7 +207,7 @@ class HeosMediaPlayer(MediaPlayerDevice):
async def async_update(self): async def async_update(self):
"""Update supported features of the player.""" """Update supported features of the player."""
controls = self._player.now_playing_media.supported_controls controls = self._player.now_playing_media.supported_controls
current_support = [self._control_to_support[control] current_support = [CONTROL_TO_SUPPORT[control]
for control in controls] for control in controls]
self._supported_features = reduce(ior, current_support, self._supported_features = reduce(ior, current_support,
BASE_SUPPORTED_FEATURES) BASE_SUPPORTED_FEATURES)
@ -343,7 +342,7 @@ class HeosMediaPlayer(MediaPlayerDevice):
@property @property
def state(self) -> str: def state(self) -> str:
"""State of the player.""" """State of the player."""
return self._play_state_to_state[self._player.state] return PLAY_STATE_TO_STATE[self._player.state]
@property @property
def supported_features(self) -> int: def supported_features(self) -> int:

View File

@ -2,7 +2,7 @@
from typing import Dict, Sequence from typing import Dict, Sequence
from asynctest.mock import Mock, patch as patch from asynctest.mock import Mock, patch as patch
from pyheos import Dispatcher, HeosPlayer, HeosSource, InputSource, const from pyheos import Dispatcher, Heos, HeosPlayer, HeosSource, InputSource, const
import pytest import pytest
from homeassistant.components.heos import DOMAIN from homeassistant.components.heos import DOMAIN
@ -22,8 +22,7 @@ def config_entry_fixture():
def controller_fixture( def controller_fixture(
players, favorites, input_sources, playlists, change_data, dispatcher): players, favorites, input_sources, playlists, change_data, dispatcher):
"""Create a mock Heos controller fixture.""" """Create a mock Heos controller fixture."""
with patch("pyheos.Heos", autospec=True) as mock: mock_heos = Mock(Heos)
mock_heos = mock.return_value
for player in players.values(): for player in players.values():
player.heos = mock_heos player.heos = mock_heos
mock_heos.dispatcher = dispatcher mock_heos.dispatcher = dispatcher
@ -36,6 +35,10 @@ def controller_fixture(
mock_heos.is_signed_in = True mock_heos.is_signed_in = True
mock_heos.signed_in_username = "user@user.com" mock_heos.signed_in_username = "user@user.com"
mock_heos.connection_state = const.STATE_CONNECTED mock_heos.connection_state = const.STATE_CONNECTED
mock = Mock(return_value=mock_heos)
with patch("homeassistant.components.heos.Heos", new=mock), \
patch("homeassistant.components.heos.config_flow.Heos", new=mock):
yield mock_heos yield mock_heos