Optimize directv client initialization (#32706)

* Optimize directv client initialization.

* Update config_flow.py

* Update media_player.py

* Update media_player.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update test_media_player.py

* Update __init__.py

* Update media_player.py

* Update test_media_player.py

* Update media_player.py

* Update test_media_player.py

* Update config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update __init__.py

* Update test_config_flow.py

* Update test_config_flow.py

* Update test_media_player.py

* Update test_media_player.py

* Update __init__.py

* Update __init__.py

* Update __init__.py
pull/32789/head
Chris Talkington 2020-03-13 23:55:21 -05:00 committed by GitHub
parent 750ed2facd
commit 5dd031af17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 50 additions and 108 deletions

View File

@ -32,7 +32,7 @@ def get_dtv_data(
hass: HomeAssistant, host: str, port: int = DEFAULT_PORT, client_addr: str = "0"
) -> dict:
"""Retrieve a DIRECTV instance, locations list, and version info for the receiver device."""
dtv = DIRECTV(host, port, client_addr)
dtv = DIRECTV(host, port, client_addr, determine_state=False)
locations = dtv.get_locations()
version_info = dtv.get_version()

View File

@ -29,8 +29,7 @@ def validate_input(data: Dict) -> Dict:
Data has the keys from DATA_SCHEMA with values provided by the user.
"""
# directpy does IO in constructor.
dtv = DIRECTV(data["host"], DEFAULT_PORT)
dtv = DIRECTV(data["host"], DEFAULT_PORT, determine_state=False)
version_info = dtv.get_version()
return {
@ -76,8 +75,7 @@ class DirecTVConfigFlow(ConfigFlow, domain=DOMAIN):
return self._show_form(errors)
except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception")
errors["base"] = ERROR_UNKNOWN
return self._show_form(errors)
return self.async_abort(reason=ERROR_UNKNOWN)
await self.async_set_unique_id(info["receiver_id"])
self._abort_if_unique_id_configured()

View File

@ -83,22 +83,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
)
def get_dtv_instance(
host: str, port: int = DEFAULT_PORT, client_addr: str = "0"
) -> DIRECTV:
"""Retrieve a DIRECTV instance for the receiver or client device."""
try:
return DIRECTV(host, port, client_addr)
except RequestException as exception:
_LOGGER.debug(
"Request exception %s trying to retrieve DIRECTV instance for client address %s on device %s",
exception,
client_addr,
host,
)
return None
async def async_setup_entry(
hass: HomeAssistantType,
entry: ConfigEntry,
@ -114,16 +98,15 @@ async def async_setup_entry(
continue
if loc["clientAddr"] != "0":
# directpy does IO in constructor.
dtv = await hass.async_add_executor_job(
get_dtv_instance, entry.data[CONF_HOST], DEFAULT_PORT, loc["clientAddr"]
dtv = DIRECTV(
entry.data[CONF_HOST],
DEFAULT_PORT,
loc["clientAddr"],
determine_state=False,
)
else:
dtv = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT]
if not dtv:
continue
entities.append(
DirecTvDevice(
str.title(loc["locationName"]), loc["clientAddr"], dtv, version_info,

View File

@ -1,4 +1,6 @@
"""Tests for the DirecTV component."""
from DirectPy import DIRECTV
from homeassistant.components.directv.const import DOMAIN
from homeassistant.const import CONF_HOST
from homeassistant.helpers.typing import HomeAssistantType
@ -94,18 +96,23 @@ MOCK_GET_VERSION = {
}
class MockDirectvClass:
class MockDirectvClass(DIRECTV):
"""A fake DirecTV DVR device."""
def __init__(self, ip, port=8080, clientAddr="0"):
def __init__(self, ip, port=8080, clientAddr="0", determine_state=False):
"""Initialize the fake DirecTV device."""
self._host = ip
self._port = port
self._device = clientAddr
self._standby = True
self._play = False
super().__init__(
ip=ip, port=port, clientAddr=clientAddr, determine_state=determine_state,
)
self.attributes = LIVE
self._play = False
self._standby = True
if self.clientAddr == CLIENT_ADDRESS:
self.attributes = RECORDING
self._standby = False
else:
self.attributes = LIVE
def get_locations(self):
"""Mock for get_locations method."""

View File

@ -114,9 +114,7 @@ async def test_form_cannot_connect(hass: HomeAssistantType) -> None:
)
with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass,
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
"tests.components.directv.test_config_flow.MockDirectvClass.get_version",
side_effect=RequestException,
) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {CONF_HOST: HOST},)
@ -135,15 +133,13 @@ async def test_form_unknown_error(hass: HomeAssistantType) -> None:
)
with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass,
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
"tests.components.directv.test_config_flow.MockDirectvClass.get_version",
side_effect=Exception,
) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {CONF_HOST: HOST},)
assert result["type"] == RESULT_TYPE_FORM
assert result["errors"] == {"base": "unknown"}
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "unknown"
await hass.async_block_till_done()
assert len(mock_validate_input.mock_calls) == 1
@ -205,9 +201,7 @@ async def test_ssdp_discovery_confirm_abort(hass: HomeAssistantType) -> None:
)
with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass,
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
"tests.components.directv.test_config_flow.MockDirectvClass.get_version",
side_effect=RequestException,
) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {})
@ -227,9 +221,7 @@ async def test_ssdp_discovery_confirm_unknown_error(hass: HomeAssistantType) ->
)
with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass,
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
"tests.components.directv.test_config_flow.MockDirectvClass.get_version",
side_effect=Exception,
) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {})

View File

@ -54,9 +54,7 @@ from homeassistant.util import dt as dt_util
from tests.common import MockConfigEntry, async_fire_time_changed
from tests.components.directv import (
CLIENT_ADDRESS,
DOMAIN,
HOST,
MOCK_GET_LOCATIONS_MULTIPLE,
RECORDING,
MockDirectvClass,
@ -70,15 +68,6 @@ MAIN_ENTITY_ID = f"{MP_DOMAIN}.main_dvr"
# pylint: disable=redefined-outer-name
@fixture
def client_dtv() -> MockDirectvClass:
"""Fixture for a client device."""
mocked_dtv = MockDirectvClass(HOST, clientAddr=CLIENT_ADDRESS)
mocked_dtv.attributes = RECORDING
mocked_dtv._standby = False # pylint: disable=protected-access
return mocked_dtv
@fixture
def mock_now() -> datetime:
"""Fixture for dtutil.now."""
@ -93,34 +82,19 @@ async def setup_directv(hass: HomeAssistantType) -> MockConfigEntry:
return await setup_integration(hass)
async def setup_directv_with_instance_error(hass: HomeAssistantType) -> MockConfigEntry:
async def setup_directv_with_locations(hass: HomeAssistantType) -> MockConfigEntry:
"""Set up mock DirecTV integration."""
with patch(
"homeassistant.components.directv.DIRECTV", new=MockDirectvClass,
), patch(
"homeassistant.components.directv.DIRECTV.get_locations",
"tests.components.directv.test_media_player.MockDirectvClass.get_locations",
return_value=MOCK_GET_LOCATIONS_MULTIPLE,
), patch(
"homeassistant.components.directv.media_player.get_dtv_instance",
return_value=None,
):
return await setup_integration(hass)
async def setup_directv_with_locations(
hass: HomeAssistantType, client_dtv: MockDirectvClass,
) -> MockConfigEntry:
"""Set up mock DirecTV integration."""
with patch(
"homeassistant.components.directv.DIRECTV", new=MockDirectvClass,
), patch(
"homeassistant.components.directv.DIRECTV.get_locations",
return_value=MOCK_GET_LOCATIONS_MULTIPLE,
), patch(
"homeassistant.components.directv.media_player.get_dtv_instance",
return_value=client_dtv,
):
return await setup_integration(hass)
with patch(
"homeassistant.components.directv.DIRECTV", new=MockDirectvClass,
), patch(
"homeassistant.components.directv.media_player.DIRECTV",
new=MockDirectvClass,
):
return await setup_integration(hass)
async def async_turn_on(
@ -204,27 +178,17 @@ async def test_setup(hass: HomeAssistantType) -> None:
assert hass.states.get(MAIN_ENTITY_ID)
async def test_setup_with_multiple_locations(
hass: HomeAssistantType, client_dtv: MockDirectvClass
) -> None:
async def test_setup_with_multiple_locations(hass: HomeAssistantType) -> None:
"""Test setup with basic config with client location."""
await setup_directv_with_locations(hass, client_dtv)
await setup_directv_with_locations(hass)
assert hass.states.get(MAIN_ENTITY_ID)
assert hass.states.get(CLIENT_ENTITY_ID)
async def test_setup_with_instance_error(hass: HomeAssistantType) -> None:
"""Test setup with basic config with client location that results in instance error."""
await setup_directv_with_instance_error(hass)
assert hass.states.get(MAIN_ENTITY_ID)
assert hass.states.async_entity_ids(MP_DOMAIN) == [MAIN_ENTITY_ID]
async def test_unique_id(hass: HomeAssistantType, client_dtv: MockDirectvClass) -> None:
async def test_unique_id(hass: HomeAssistantType) -> None:
"""Test unique id."""
await setup_directv_with_locations(hass, client_dtv)
await setup_directv_with_locations(hass)
entity_registry = await hass.helpers.entity_registry.async_get_registry()
@ -235,11 +199,9 @@ async def test_unique_id(hass: HomeAssistantType, client_dtv: MockDirectvClass)
assert client.unique_id == "2CA17D1CD30X"
async def test_supported_features(
hass: HomeAssistantType, client_dtv: MockDirectvClass
) -> None:
async def test_supported_features(hass: HomeAssistantType) -> None:
"""Test supported features."""
await setup_directv_with_locations(hass, client_dtv)
await setup_directv_with_locations(hass)
# Features supported for main DVR
state = hass.states.get(MAIN_ENTITY_ID)
@ -269,10 +231,10 @@ async def test_supported_features(
async def test_check_attributes(
hass: HomeAssistantType, mock_now: dt_util.dt.datetime, client_dtv: MockDirectvClass
hass: HomeAssistantType, mock_now: dt_util.dt.datetime
) -> None:
"""Test attributes."""
await setup_directv_with_locations(hass, client_dtv)
await setup_directv_with_locations(hass)
next_update = mock_now + timedelta(minutes=5)
with patch("homeassistant.util.dt.utcnow", return_value=next_update):
@ -321,10 +283,10 @@ async def test_check_attributes(
async def test_main_services(
hass: HomeAssistantType, mock_now: dt_util.dt.datetime, client_dtv: MockDirectvClass
hass: HomeAssistantType, mock_now: dt_util.dt.datetime
) -> None:
"""Test the different services."""
await setup_directv_with_locations(hass, client_dtv)
await setup_directv(hass)
next_update = mock_now + timedelta(minutes=5)
with patch("homeassistant.util.dt.utcnow", return_value=next_update):
@ -373,10 +335,10 @@ async def test_main_services(
async def test_available(
hass: HomeAssistantType, mock_now: dt_util.dt.datetime, client_dtv: MockDirectvClass
hass: HomeAssistantType, mock_now: dt_util.dt.datetime
) -> None:
"""Test available status."""
entry = await setup_directv_with_locations(hass, client_dtv)
entry = await setup_directv(hass)
next_update = mock_now + timedelta(minutes=5)
with patch("homeassistant.util.dt.utcnow", return_value=next_update):