Update pyheos to 0.6.0 (#26191)

This commit is contained in:
Andrew Sayre 2019-08-25 13:57:43 -05:00 committed by Pascal Vizeli
parent d4bd5a180c
commit 7bfb365f62
11 changed files with 66 additions and 92 deletions

View file

@ -4,7 +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 from pyheos import Heos, HeosError, const as heos_const
import voluptuous as vol import voluptuous as vol
from homeassistant.components.media_player.const import DOMAIN as MEDIA_PLAYER_DOMAIN from homeassistant.components.media_player.const import DOMAIN as MEDIA_PLAYER_DOMAIN
@ -68,7 +68,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
try: try:
await controller.connect(auto_reconnect=True) await controller.connect(auto_reconnect=True)
# Auto reconnect only operates if initial connection was successful. # Auto reconnect only operates if initial connection was successful.
except (asyncio.TimeoutError, ConnectionError, CommandError) as error: except HeosError as error:
await controller.disconnect() await controller.disconnect()
_LOGGER.debug("Unable to connect to controller %s: %s", host, error) _LOGGER.debug("Unable to connect to controller %s: %s", host, error)
raise ConfigEntryNotReady raise ConfigEntryNotReady
@ -93,13 +93,9 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
host, host,
) )
inputs = await controller.get_input_sources() inputs = await controller.get_input_sources()
except (asyncio.TimeoutError, ConnectionError, CommandError) as error: except HeosError as error:
await controller.disconnect() await controller.disconnect()
_LOGGER.debug( _LOGGER.debug("Unable to retrieve players and sources: %s", error)
"Unable to retrieve players and sources: %s",
error,
exc_info=isinstance(error, CommandError),
)
raise ConfigEntryNotReady raise ConfigEntryNotReady
controller_manager = ControllerManager(hass, controller) controller_manager = ControllerManager(hass, controller)
@ -187,7 +183,7 @@ class ControllerManager:
# 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[heos_const.DATA_MAPPED_IDS]) self.update_ids(data[heos_const.DATA_MAPPED_IDS])
except (CommandError, asyncio.TimeoutError, ConnectionError) as ex: except HeosError as ex:
_LOGGER.error("Unable to refresh players: %s", ex) _LOGGER.error("Unable to refresh players: %s", ex)
# Update players # Update players
self._hass.helpers.dispatcher.async_dispatcher_send(SIGNAL_HEOS_UPDATED) self._hass.helpers.dispatcher.async_dispatcher_send(SIGNAL_HEOS_UPDATED)
@ -312,21 +308,15 @@ class SourceManager:
favorites = await controller.get_favorites() favorites = await controller.get_favorites()
inputs = await controller.get_input_sources() inputs = await controller.get_input_sources()
return favorites, inputs return favorites, inputs
except (asyncio.TimeoutError, ConnectionError, CommandError) as error: except HeosError as error:
if retry_attempts < self.max_retry_attempts: if retry_attempts < self.max_retry_attempts:
retry_attempts += 1 retry_attempts += 1
_LOGGER.debug( _LOGGER.debug(
"Error retrieving sources and will " "retry: %s", "Error retrieving sources and will " "retry: %s", error
error,
exc_info=isinstance(error, CommandError),
) )
await asyncio.sleep(self.retry_delay) await asyncio.sleep(self.retry_delay)
else: else:
_LOGGER.error( _LOGGER.error("Unable to update sources: %s", error)
"Unable to update sources: %s",
error,
exc_info=isinstance(error, CommandError),
)
return return
async def update_sources(event, data=None): async def update_sources(event, data=None):

View file

@ -1,7 +1,5 @@
"""Config flow to configure Heos.""" """Config flow to configure Heos."""
import asyncio from pyheos import Heos, HeosError
from pyheos import Heos
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
@ -59,7 +57,7 @@ class HeosFlowHandler(config_entries.ConfigFlow):
await heos.connect() await heos.connect()
self.hass.data.pop(DATA_DISCOVERED_HOSTS) self.hass.data.pop(DATA_DISCOVERED_HOSTS)
return await self.async_step_import({CONF_HOST: host}) return await self.async_step_import({CONF_HOST: host})
except (asyncio.TimeoutError, ConnectionError): except HeosError:
errors[CONF_HOST] = "connection_failure" errors[CONF_HOST] = "connection_failure"
finally: finally:
await heos.disconnect() await heos.disconnect()

View file

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/components/heos", "documentation": "https://www.home-assistant.io/components/heos",
"requirements": [ "requirements": [
"pyheos==0.5.2" "pyheos==0.6.0"
], ],
"ssdp": { "ssdp": {
"st": [ "st": [
@ -15,4 +15,4 @@
"codeowners": [ "codeowners": [
"@andrewsayre" "@andrewsayre"
] ]
} }

View file

@ -1,11 +1,10 @@
"""Denon HEOS Media Player.""" """Denon HEOS Media Player."""
import asyncio
from functools import reduce, wraps from functools import reduce, wraps
import logging 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 pyheos import HeosError, 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 (
@ -83,12 +82,7 @@ def log_command_error(command: str):
async def wrapper(*args, **kwargs): async def wrapper(*args, **kwargs):
try: try:
await func(*args, **kwargs) await func(*args, **kwargs)
except ( except (HeosError, ValueError) as ex:
CommandError,
asyncio.TimeoutError,
ConnectionError,
ValueError,
) as ex:
_LOGGER.error("Unable to %s: %s", command, ex) _LOGGER.error("Unable to %s: %s", command, ex)
return wrapper return wrapper

View file

@ -1,9 +1,8 @@
"""Services for the HEOS integration.""" """Services for the HEOS integration."""
import asyncio
import functools import functools
import logging import logging
from pyheos import CommandError, Heos, const from pyheos import CommandFailedError, Heos, HeosError, const
import voluptuous as vol import voluptuous as vol
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
@ -57,9 +56,9 @@ async def _sign_in_handler(controller, service):
password = service.data[ATTR_PASSWORD] password = service.data[ATTR_PASSWORD]
try: try:
await controller.sign_in(username, password) await controller.sign_in(username, password)
except CommandError as err: except CommandFailedError as err:
_LOGGER.error("Sign in failed: %s", err) _LOGGER.error("Sign in failed: %s", err)
except (asyncio.TimeoutError, ConnectionError) as err: except HeosError as err:
_LOGGER.error("Unable to sign in: %s", err) _LOGGER.error("Unable to sign in: %s", err)
@ -70,5 +69,5 @@ async def _sign_out_handler(controller, service):
return return
try: try:
await controller.sign_out() await controller.sign_out()
except (asyncio.TimeoutError, ConnectionError, CommandError) as err: except HeosError as err:
_LOGGER.error("Unable to sign out: %s", err) _LOGGER.error("Unable to sign out: %s", err)

View file

@ -1186,7 +1186,7 @@ pygtt==1.1.2
pyhaversion==3.0.2 pyhaversion==3.0.2
# homeassistant.components.heos # homeassistant.components.heos
pyheos==0.5.2 pyheos==0.6.0
# homeassistant.components.hikvision # homeassistant.components.hikvision
pyhik==0.2.3 pyhik==0.2.3

View file

@ -277,7 +277,7 @@ pydeconz==62
pydispatcher==2.0.5 pydispatcher==2.0.5
# homeassistant.components.heos # homeassistant.components.heos
pyheos==0.5.2 pyheos==0.6.0
# homeassistant.components.homematic # homeassistant.components.homematic
pyhomematic==0.1.60 pyhomematic==0.1.60

View file

@ -1,5 +1,5 @@
"""Tests for the Heos config flow module.""" """Tests for the Heos config flow module."""
import asyncio from pyheos import HeosError
from homeassistant import data_entry_flow from homeassistant import data_entry_flow
from homeassistant.components.heos.config_flow import HeosFlowHandler from homeassistant.components.heos.config_flow import HeosFlowHandler
@ -31,18 +31,15 @@ async def test_cannot_connect_shows_error_form(hass, controller):
"""Test form is shown with error when cannot connect.""" """Test form is shown with error when cannot connect."""
flow = HeosFlowHandler() flow = HeosFlowHandler()
flow.hass = hass flow.hass = hass
controller.connect.side_effect = HeosError()
errors = [ConnectionError, asyncio.TimeoutError] result = await flow.async_step_user({CONF_HOST: "127.0.0.1"})
for error in errors: assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
controller.connect.side_effect = error assert result["step_id"] == "user"
result = await flow.async_step_user({CONF_HOST: "127.0.0.1"}) assert result["errors"][CONF_HOST] == "connection_failure"
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert controller.connect.call_count == 1
assert result["step_id"] == "user" assert controller.disconnect.call_count == 1
assert result["errors"][CONF_HOST] == "connection_failure" controller.connect.reset_mock()
assert controller.connect.call_count == 1 controller.disconnect.reset_mock()
assert controller.disconnect.call_count == 1
controller.connect.reset_mock()
controller.disconnect.reset_mock()
async def test_create_entry_when_host_valid(hass, controller): async def test_create_entry_when_host_valid(hass, controller):

View file

@ -2,7 +2,7 @@
import asyncio import asyncio
from asynctest import Mock, patch from asynctest import Mock, patch
from pyheos import CommandError, const from pyheos import CommandFailedError, HeosError, const
import pytest import pytest
from homeassistant.components.heos import ( from homeassistant.components.heos import (
@ -117,31 +117,27 @@ async def test_async_setup_entry_not_signed_in_loads_platforms(
async def test_async_setup_entry_connect_failure(hass, config_entry, controller): async def test_async_setup_entry_connect_failure(hass, config_entry, controller):
"""Connection failure raises ConfigEntryNotReady.""" """Connection failure raises ConfigEntryNotReady."""
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
errors = [ConnectionError, asyncio.TimeoutError] controller.connect.side_effect = HeosError()
for error in errors: with pytest.raises(ConfigEntryNotReady):
controller.connect.side_effect = error await async_setup_entry(hass, config_entry)
with pytest.raises(ConfigEntryNotReady): await hass.async_block_till_done()
await async_setup_entry(hass, config_entry) assert controller.connect.call_count == 1
await hass.async_block_till_done() assert controller.disconnect.call_count == 1
assert controller.connect.call_count == 1 controller.connect.reset_mock()
assert controller.disconnect.call_count == 1 controller.disconnect.reset_mock()
controller.connect.reset_mock()
controller.disconnect.reset_mock()
async def test_async_setup_entry_player_failure(hass, config_entry, controller): async def test_async_setup_entry_player_failure(hass, config_entry, controller):
"""Failure to retrieve players/sources raises ConfigEntryNotReady.""" """Failure to retrieve players/sources raises ConfigEntryNotReady."""
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
errors = [ConnectionError, asyncio.TimeoutError] controller.get_players.side_effect = HeosError()
for error in errors: with pytest.raises(ConfigEntryNotReady):
controller.get_players.side_effect = error await async_setup_entry(hass, config_entry)
with pytest.raises(ConfigEntryNotReady): await hass.async_block_till_done()
await async_setup_entry(hass, config_entry) assert controller.connect.call_count == 1
await hass.async_block_till_done() assert controller.disconnect.call_count == 1
assert controller.connect.call_count == 1 controller.connect.reset_mock()
assert controller.disconnect.call_count == 1 controller.disconnect.reset_mock()
controller.connect.reset_mock()
controller.disconnect.reset_mock()
async def test_unload_entry(hass, config_entry, controller): async def test_unload_entry(hass, config_entry, controller):
@ -167,7 +163,7 @@ async def test_update_sources_retry(hass, config_entry, config, controller, capl
source_manager = hass.data[DOMAIN][DATA_SOURCE_MANAGER] source_manager = hass.data[DOMAIN][DATA_SOURCE_MANAGER]
source_manager.retry_delay = 0 source_manager.retry_delay = 0
source_manager.max_retry_attempts = 1 source_manager.max_retry_attempts = 1
controller.get_favorites.side_effect = CommandError("Test", "test", 0) controller.get_favorites.side_effect = CommandFailedError("Test", "test", 0)
controller.dispatcher.send( controller.dispatcher.send(
const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED, {} const.SIGNAL_CONTROLLER_EVENT, const.EVENT_SOURCES_CHANGED, {}
) )

View file

@ -1,7 +1,7 @@
"""Tests for the Heos Media Player platform.""" """Tests for the Heos Media Player platform."""
import asyncio import asyncio
from pyheos import CommandError, const from pyheos import CommandFailedError, const
from homeassistant.components.heos import media_player from homeassistant.components.heos import media_player
from homeassistant.components.heos.const import ( from homeassistant.components.heos.const import (
@ -179,7 +179,7 @@ async def test_updates_from_connection_event(
event.clear() event.clear()
player.reset_mock() player.reset_mock()
controller.load_players.reset_mock() controller.load_players.reset_mock()
controller.load_players.side_effect = CommandError(None, "Failure", 1) controller.load_players.side_effect = CommandFailedError(None, "Failure", 1)
player.available = True player.available = True
player.heos.dispatcher.send(const.SIGNAL_HEOS_EVENT, const.EVENT_CONNECTED) player.heos.dispatcher.send(const.SIGNAL_HEOS_EVENT, const.EVENT_CONNECTED)
await event.wait() await event.wait()
@ -313,7 +313,7 @@ async def test_clear_playlist(hass, config_entry, config, controller, caplog):
) )
assert player.clear_queue.call_count == 1 assert player.clear_queue.call_count == 1
player.clear_queue.reset_mock() player.clear_queue.reset_mock()
player.clear_queue.side_effect = CommandError(None, "Failure", 1) player.clear_queue.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to clear playlist: Failure (1)" in caplog.text assert "Unable to clear playlist: Failure (1)" in caplog.text
@ -331,7 +331,7 @@ async def test_pause(hass, config_entry, config, controller, caplog):
) )
assert player.pause.call_count == 1 assert player.pause.call_count == 1
player.pause.reset_mock() player.pause.reset_mock()
player.pause.side_effect = CommandError(None, "Failure", 1) player.pause.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to pause: Failure (1)" in caplog.text assert "Unable to pause: Failure (1)" in caplog.text
@ -349,7 +349,7 @@ async def test_play(hass, config_entry, config, controller, caplog):
) )
assert player.play.call_count == 1 assert player.play.call_count == 1
player.play.reset_mock() player.play.reset_mock()
player.play.side_effect = CommandError(None, "Failure", 1) player.play.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to play: Failure (1)" in caplog.text assert "Unable to play: Failure (1)" in caplog.text
@ -367,7 +367,7 @@ async def test_previous_track(hass, config_entry, config, controller, caplog):
) )
assert player.play_previous.call_count == 1 assert player.play_previous.call_count == 1
player.play_previous.reset_mock() player.play_previous.reset_mock()
player.play_previous.side_effect = CommandError(None, "Failure", 1) player.play_previous.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to move to previous track: Failure (1)" in caplog.text assert "Unable to move to previous track: Failure (1)" in caplog.text
@ -385,7 +385,7 @@ async def test_next_track(hass, config_entry, config, controller, caplog):
) )
assert player.play_next.call_count == 1 assert player.play_next.call_count == 1
player.play_next.reset_mock() player.play_next.reset_mock()
player.play_next.side_effect = CommandError(None, "Failure", 1) player.play_next.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to move to next track: Failure (1)" in caplog.text assert "Unable to move to next track: Failure (1)" in caplog.text
@ -403,7 +403,7 @@ async def test_stop(hass, config_entry, config, controller, caplog):
) )
assert player.stop.call_count == 1 assert player.stop.call_count == 1
player.stop.reset_mock() player.stop.reset_mock()
player.stop.side_effect = CommandError(None, "Failure", 1) player.stop.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to stop: Failure (1)" in caplog.text assert "Unable to stop: Failure (1)" in caplog.text
@ -421,7 +421,7 @@ async def test_volume_mute(hass, config_entry, config, controller, caplog):
) )
assert player.set_mute.call_count == 1 assert player.set_mute.call_count == 1
player.set_mute.reset_mock() player.set_mute.reset_mock()
player.set_mute.side_effect = CommandError(None, "Failure", 1) player.set_mute.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to set mute: Failure (1)" in caplog.text assert "Unable to set mute: Failure (1)" in caplog.text
@ -439,7 +439,7 @@ async def test_shuffle_set(hass, config_entry, config, controller, caplog):
) )
player.set_play_mode.assert_called_once_with(player.repeat, True) player.set_play_mode.assert_called_once_with(player.repeat, True)
player.set_play_mode.reset_mock() player.set_play_mode.reset_mock()
player.set_play_mode.side_effect = CommandError(None, "Failure", 1) player.set_play_mode.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to set shuffle: Failure (1)" in caplog.text assert "Unable to set shuffle: Failure (1)" in caplog.text
@ -457,7 +457,7 @@ async def test_volume_set(hass, config_entry, config, controller, caplog):
) )
player.set_volume.assert_called_once_with(100) player.set_volume.assert_called_once_with(100)
player.set_volume.reset_mock() player.set_volume.reset_mock()
player.set_volume.side_effect = CommandError(None, "Failure", 1) player.set_volume.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to set volume level: Failure (1)" in caplog.text assert "Unable to set volume level: Failure (1)" in caplog.text
@ -516,7 +516,7 @@ async def test_select_radio_favorite_command_error(
player = controller.players[1] player = controller.players[1]
# Test set radio preset # Test set radio preset
favorite = favorites[2] favorite = favorites[2]
player.play_favorite.side_effect = CommandError(None, "Failure", 1) player.play_favorite.side_effect = CommandFailedError(None, "Failure", 1)
await hass.services.async_call( await hass.services.async_call(
MEDIA_PLAYER_DOMAIN, MEDIA_PLAYER_DOMAIN,
SERVICE_SELECT_SOURCE, SERVICE_SELECT_SOURCE,
@ -575,7 +575,7 @@ async def test_select_input_command_error(
await setup_platform(hass, config_entry, config) await setup_platform(hass, config_entry, config)
player = controller.players[1] player = controller.players[1]
input_source = input_sources[0] input_source = input_sources[0]
player.play_input_source.side_effect = CommandError(None, "Failure", 1) player.play_input_source.side_effect = CommandFailedError(None, "Failure", 1)
await hass.services.async_call( await hass.services.async_call(
MEDIA_PLAYER_DOMAIN, MEDIA_PLAYER_DOMAIN,
SERVICE_SELECT_SOURCE, SERVICE_SELECT_SOURCE,
@ -615,7 +615,7 @@ async def test_play_media_url(hass, config_entry, config, controller, caplog):
) )
player.play_url.assert_called_once_with(url) player.play_url.assert_called_once_with(url)
player.play_url.reset_mock() player.play_url.reset_mock()
player.play_url.side_effect = CommandError(None, "Failure", 1) player.play_url.side_effect = CommandFailedError(None, "Failure", 1)
assert "Unable to play media: Failure (1)" in caplog.text assert "Unable to play media: Failure (1)" in caplog.text

View file

@ -1,5 +1,5 @@
"""Tests for the services module.""" """Tests for the services module."""
from pyheos import CommandError, const from pyheos import CommandFailedError, HeosError, const
from homeassistant.components.heos.const import ( from homeassistant.components.heos.const import (
ATTR_PASSWORD, ATTR_PASSWORD,
@ -51,7 +51,7 @@ async def test_sign_in_not_connected(hass, config_entry, controller, caplog):
async def test_sign_in_failed(hass, config_entry, controller, caplog): async def test_sign_in_failed(hass, config_entry, controller, caplog):
"""Test sign-in service logs error when not connected.""" """Test sign-in service logs error when not connected."""
await setup_component(hass, config_entry) await setup_component(hass, config_entry)
controller.sign_in.side_effect = CommandError("", "Invalid credentials", 6) controller.sign_in.side_effect = CommandFailedError("", "Invalid credentials", 6)
await hass.services.async_call( await hass.services.async_call(
DOMAIN, DOMAIN,
@ -67,7 +67,7 @@ async def test_sign_in_failed(hass, config_entry, controller, caplog):
async def test_sign_in_unknown_error(hass, config_entry, controller, caplog): async def test_sign_in_unknown_error(hass, config_entry, controller, caplog):
"""Test sign-in service logs error for failure.""" """Test sign-in service logs error for failure."""
await setup_component(hass, config_entry) await setup_component(hass, config_entry)
controller.sign_in.side_effect = ConnectionError controller.sign_in.side_effect = HeosError()
await hass.services.async_call( await hass.services.async_call(
DOMAIN, DOMAIN,
@ -103,7 +103,7 @@ async def test_sign_out_not_connected(hass, config_entry, controller, caplog):
async def test_sign_out_unknown_error(hass, config_entry, controller, caplog): async def test_sign_out_unknown_error(hass, config_entry, controller, caplog):
"""Test the sign-out service.""" """Test the sign-out service."""
await setup_component(hass, config_entry) await setup_component(hass, config_entry)
controller.sign_out.side_effect = ConnectionError controller.sign_out.side_effect = HeosError()
await hass.services.async_call(DOMAIN, SERVICE_SIGN_OUT, {}, blocking=True) await hass.services.async_call(DOMAIN, SERVICE_SIGN_OUT, {}, blocking=True)