mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 07:37:34 +00:00
Move imports to top (#24108)
This commit is contained in:
parent
25505dc1d4
commit
0194905e97
@ -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)
|
||||||
|
@ -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():
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user