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
This commit is contained in:
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" hass: HomeAssistant, host: str, port: int = DEFAULT_PORT, client_addr: str = "0"
) -> dict: ) -> dict:
"""Retrieve a DIRECTV instance, locations list, and version info for the receiver device.""" """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() locations = dtv.get_locations()
version_info = dtv.get_version() 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. Data has the keys from DATA_SCHEMA with values provided by the user.
""" """
# directpy does IO in constructor. dtv = DIRECTV(data["host"], DEFAULT_PORT, determine_state=False)
dtv = DIRECTV(data["host"], DEFAULT_PORT)
version_info = dtv.get_version() version_info = dtv.get_version()
return { return {
@ -76,8 +75,7 @@ class DirecTVConfigFlow(ConfigFlow, domain=DOMAIN):
return self._show_form(errors) return self._show_form(errors)
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
_LOGGER.exception("Unexpected exception") _LOGGER.exception("Unexpected exception")
errors["base"] = ERROR_UNKNOWN return self.async_abort(reason=ERROR_UNKNOWN)
return self._show_form(errors)
await self.async_set_unique_id(info["receiver_id"]) await self.async_set_unique_id(info["receiver_id"])
self._abort_if_unique_id_configured() 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( async def async_setup_entry(
hass: HomeAssistantType, hass: HomeAssistantType,
entry: ConfigEntry, entry: ConfigEntry,
@ -114,16 +98,15 @@ async def async_setup_entry(
continue continue
if loc["clientAddr"] != "0": if loc["clientAddr"] != "0":
# directpy does IO in constructor. dtv = DIRECTV(
dtv = await hass.async_add_executor_job( entry.data[CONF_HOST],
get_dtv_instance, entry.data[CONF_HOST], DEFAULT_PORT, loc["clientAddr"] DEFAULT_PORT,
loc["clientAddr"],
determine_state=False,
) )
else: else:
dtv = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] dtv = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT]
if not dtv:
continue
entities.append( entities.append(
DirecTvDevice( DirecTvDevice(
str.title(loc["locationName"]), loc["clientAddr"], dtv, version_info, str.title(loc["locationName"]), loc["clientAddr"], dtv, version_info,

View File

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

View File

@ -114,9 +114,7 @@ async def test_form_cannot_connect(hass: HomeAssistantType) -> None:
) )
with patch( with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, "tests.components.directv.test_config_flow.MockDirectvClass.get_version",
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
side_effect=RequestException, side_effect=RequestException,
) as mock_validate_input: ) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {CONF_HOST: HOST},) 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( with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, "tests.components.directv.test_config_flow.MockDirectvClass.get_version",
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
side_effect=Exception, side_effect=Exception,
) as mock_validate_input: ) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {CONF_HOST: HOST},) result = await async_configure_flow(hass, result["flow_id"], {CONF_HOST: HOST},)
assert result["type"] == RESULT_TYPE_FORM assert result["type"] == RESULT_TYPE_ABORT
assert result["errors"] == {"base": "unknown"} assert result["reason"] == "unknown"
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(mock_validate_input.mock_calls) == 1 assert len(mock_validate_input.mock_calls) == 1
@ -205,9 +201,7 @@ async def test_ssdp_discovery_confirm_abort(hass: HomeAssistantType) -> None:
) )
with patch( with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, "tests.components.directv.test_config_flow.MockDirectvClass.get_version",
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
side_effect=RequestException, side_effect=RequestException,
) as mock_validate_input: ) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {}) 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( with patch(
"homeassistant.components.directv.config_flow.DIRECTV", new=MockDirectvClass, "tests.components.directv.test_config_flow.MockDirectvClass.get_version",
), patch(
"homeassistant.components.directv.config_flow.DIRECTV.get_version",
side_effect=Exception, side_effect=Exception,
) as mock_validate_input: ) as mock_validate_input:
result = await async_configure_flow(hass, result["flow_id"], {}) 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.common import MockConfigEntry, async_fire_time_changed
from tests.components.directv import ( from tests.components.directv import (
CLIENT_ADDRESS,
DOMAIN, DOMAIN,
HOST,
MOCK_GET_LOCATIONS_MULTIPLE, MOCK_GET_LOCATIONS_MULTIPLE,
RECORDING, RECORDING,
MockDirectvClass, MockDirectvClass,
@ -70,15 +68,6 @@ MAIN_ENTITY_ID = f"{MP_DOMAIN}.main_dvr"
# pylint: disable=redefined-outer-name # 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 @fixture
def mock_now() -> datetime: def mock_now() -> datetime:
"""Fixture for dtutil.now.""" """Fixture for dtutil.now."""
@ -93,34 +82,19 @@ async def setup_directv(hass: HomeAssistantType) -> MockConfigEntry:
return await setup_integration(hass) 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.""" """Set up mock DirecTV integration."""
with patch( with patch(
"homeassistant.components.directv.DIRECTV", new=MockDirectvClass, "tests.components.directv.test_media_player.MockDirectvClass.get_locations",
), patch(
"homeassistant.components.directv.DIRECTV.get_locations",
return_value=MOCK_GET_LOCATIONS_MULTIPLE, return_value=MOCK_GET_LOCATIONS_MULTIPLE,
), patch(
"homeassistant.components.directv.media_player.get_dtv_instance",
return_value=None,
): ):
return await setup_integration(hass) with patch(
"homeassistant.components.directv.DIRECTV", new=MockDirectvClass,
), patch(
async def setup_directv_with_locations( "homeassistant.components.directv.media_player.DIRECTV",
hass: HomeAssistantType, client_dtv: MockDirectvClass, new=MockDirectvClass,
) -> MockConfigEntry: ):
"""Set up mock DirecTV integration.""" return await setup_integration(hass)
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)
async def async_turn_on( async def async_turn_on(
@ -204,27 +178,17 @@ async def test_setup(hass: HomeAssistantType) -> None:
assert hass.states.get(MAIN_ENTITY_ID) assert hass.states.get(MAIN_ENTITY_ID)
async def test_setup_with_multiple_locations( async def test_setup_with_multiple_locations(hass: HomeAssistantType) -> None:
hass: HomeAssistantType, client_dtv: MockDirectvClass
) -> None:
"""Test setup with basic config with client location.""" """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(MAIN_ENTITY_ID)
assert hass.states.get(CLIENT_ENTITY_ID) assert hass.states.get(CLIENT_ENTITY_ID)
async def test_setup_with_instance_error(hass: HomeAssistantType) -> None: async def test_unique_id(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:
"""Test unique id.""" """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() 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" assert client.unique_id == "2CA17D1CD30X"
async def test_supported_features( async def test_supported_features(hass: HomeAssistantType) -> None:
hass: HomeAssistantType, client_dtv: MockDirectvClass
) -> None:
"""Test supported features.""" """Test supported features."""
await setup_directv_with_locations(hass, client_dtv) await setup_directv_with_locations(hass)
# Features supported for main DVR # Features supported for main DVR
state = hass.states.get(MAIN_ENTITY_ID) state = hass.states.get(MAIN_ENTITY_ID)
@ -269,10 +231,10 @@ async def test_supported_features(
async def test_check_attributes( 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: ) -> None:
"""Test attributes.""" """Test attributes."""
await setup_directv_with_locations(hass, client_dtv) await setup_directv_with_locations(hass)
next_update = mock_now + timedelta(minutes=5) next_update = mock_now + timedelta(minutes=5)
with patch("homeassistant.util.dt.utcnow", return_value=next_update): with patch("homeassistant.util.dt.utcnow", return_value=next_update):
@ -321,10 +283,10 @@ async def test_check_attributes(
async def test_main_services( 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: ) -> None:
"""Test the different services.""" """Test the different services."""
await setup_directv_with_locations(hass, client_dtv) await setup_directv(hass)
next_update = mock_now + timedelta(minutes=5) next_update = mock_now + timedelta(minutes=5)
with patch("homeassistant.util.dt.utcnow", return_value=next_update): with patch("homeassistant.util.dt.utcnow", return_value=next_update):
@ -373,10 +335,10 @@ async def test_main_services(
async def test_available( async def test_available(
hass: HomeAssistantType, mock_now: dt_util.dt.datetime, client_dtv: MockDirectvClass hass: HomeAssistantType, mock_now: dt_util.dt.datetime
) -> None: ) -> None:
"""Test available status.""" """Test available status."""
entry = await setup_directv_with_locations(hass, client_dtv) entry = await setup_directv(hass)
next_update = mock_now + timedelta(minutes=5) next_update = mock_now + timedelta(minutes=5)
with patch("homeassistant.util.dt.utcnow", return_value=next_update): with patch("homeassistant.util.dt.utcnow", return_value=next_update):