Avoid setting up harmony websocket from discovery (#57589)

This commit is contained in:
J. Nick Koston 2021-10-17 17:32:02 -10:00 committed by GitHub
parent 147febb18a
commit cac0c04a91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 36 deletions

View File

@ -1,12 +1,10 @@
"""The Logitech Harmony Hub integration.""" """The Logitech Harmony Hub integration."""
import asyncio
import logging import logging
from homeassistant.components.remote import ATTR_ACTIVITY, ATTR_DELAY_SECS from homeassistant.components.remote import ATTR_ACTIVITY, ATTR_DELAY_SECS
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STOP from homeassistant.const import CONF_HOST, CONF_NAME, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import entity_registry from homeassistant.helpers import entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
@ -34,13 +32,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
address = entry.data[CONF_HOST] address = entry.data[CONF_HOST]
name = entry.data[CONF_NAME] name = entry.data[CONF_NAME]
data = HarmonyData(hass, address, name, entry.unique_id) data = HarmonyData(hass, address, name, entry.unique_id)
try: await data.connect()
connected_ok = await data.connect()
except (asyncio.TimeoutError, ValueError, AttributeError) as err:
raise ConfigEntryNotReady from err
if not connected_ok:
raise ConfigEntryNotReady
await _migrate_old_unique_ids(hass, entry.entry_id, data) await _migrate_old_unique_ids(hass, entry.entry_id, data)
@ -51,8 +43,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
cancel_stop = hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, _async_on_stop) cancel_stop = hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, _async_on_stop)
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
hass.data[DOMAIN][entry.entry_id] = {
HARMONY_DATA: data, HARMONY_DATA: data,
CANCEL_LISTENER: cancel_listener, CANCEL_LISTENER: cancel_listener,
CANCEL_STOP: cancel_stop, CANCEL_STOP: cancel_stop,

View File

@ -1,7 +1,10 @@
"""Config flow for Logitech Harmony Hub integration.""" """Config flow for Logitech Harmony Hub integration."""
import asyncio
import logging import logging
from urllib.parse import urlparse from urllib.parse import urlparse
from aioharmony.hubconnector_websocket import HubConnector
import aiohttp
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, exceptions from homeassistant import config_entries, exceptions
@ -94,16 +97,20 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
CONF_NAME: friendly_name, CONF_NAME: friendly_name,
} }
harmony = await get_harmony_client_if_available(parsed_url.hostname) connector = HubConnector(parsed_url.hostname, asyncio.Queue())
try:
remote_id = await connector.get_remote_id()
except aiohttp.ClientError:
return self.async_abort(reason="cannot_connect")
finally:
await connector.async_close_session()
if harmony: unique_id = str(remote_id)
unique_id = find_unique_id_for_remote(harmony) await self.async_set_unique_id(str(unique_id))
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured( self._abort_if_unique_id_configured(
updates={CONF_HOST: self.harmony_config[CONF_HOST]} updates={CONF_HOST: self.harmony_config[CONF_HOST]}
) )
self.harmony_config[UNIQUE_ID] = unique_id self.harmony_config[UNIQUE_ID] = unique_id
return await self.async_step_link() return await self.async_step_link()
async def async_step_link(self, user_input=None): async def async_step_link(self, user_input=None):

View File

@ -1,6 +1,7 @@
"""Harmony data object which contains the Harmony Client.""" """Harmony data object which contains the Harmony Client."""
from __future__ import annotations from __future__ import annotations
import asyncio
from collections.abc import Iterable from collections.abc import Iterable
import logging import logging
@ -8,6 +9,8 @@ from aioharmony.const import ClientCallbackType, SendCommandDevice
import aioharmony.exceptions as aioexc import aioharmony.exceptions as aioexc
from aioharmony.harmonyapi import HarmonyAPI as HarmonyClient from aioharmony.harmonyapi import HarmonyAPI as HarmonyClient
from homeassistant.exceptions import ConfigEntryNotReady
from .const import ACTIVITY_POWER_OFF from .const import ACTIVITY_POWER_OFF
from .subscriber import HarmonySubscriberMixin from .subscriber import HarmonySubscriberMixin
@ -109,16 +112,24 @@ class HarmonyData(HarmonySubscriberMixin):
ip_address=self._address, callbacks=ClientCallbackType(**callbacks) ip_address=self._address, callbacks=ClientCallbackType(**callbacks)
) )
connected = False
try: try:
if not await self._client.connect(): connected = await self._client.connect()
_LOGGER.warning("%s: Unable to connect to HUB", self._name) except (asyncio.TimeoutError, aioexc.TimeOut) as err:
await self._client.close() await self._client.close()
return False raise ConfigEntryNotReady(
except aioexc.TimeOut: f"{self._name}: Connection timed-out to {self._address}:8088"
_LOGGER.warning("%s: Connection timed-out", self._name) ) from err
return False except (ValueError, AttributeError) as err:
await self._client.close()
return True raise ConfigEntryNotReady(
f"{self._name}: Error {err} while connected HUB at: {self._address}:8088"
) from err
if not connected:
await self._client.close()
raise ConfigEntryNotReady(
f"{self._name}: Unable to connect to HUB at: {self._address}:8088"
)
async def shutdown(self): async def shutdown(self):
"""Close connection on shutdown.""" """Close connection on shutdown."""

View File

@ -2,7 +2,7 @@
"domain": "harmony", "domain": "harmony",
"name": "Logitech Harmony Hub", "name": "Logitech Harmony Hub",
"documentation": "https://www.home-assistant.io/integrations/harmony", "documentation": "https://www.home-assistant.io/integrations/harmony",
"requirements": ["aioharmony==0.2.7"], "requirements": ["aioharmony==0.2.8"],
"codeowners": [ "codeowners": [
"@ehendrix23", "@ehendrix23",
"@bramkragten", "@bramkragten",

View File

@ -176,7 +176,7 @@ aiogithubapi==21.8.0
aioguardian==1.0.8 aioguardian==1.0.8
# homeassistant.components.harmony # homeassistant.components.harmony
aioharmony==0.2.7 aioharmony==0.2.8
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==0.6.3 aiohomekit==0.6.3

View File

@ -118,7 +118,7 @@ aioflo==0.4.1
aioguardian==1.0.8 aioguardian==1.0.8
# homeassistant.components.harmony # homeassistant.components.harmony
aioharmony==0.2.7 aioharmony==0.2.8
# homeassistant.components.homekit_controller # homeassistant.components.homekit_controller
aiohomekit==0.6.3 aiohomekit==0.6.3

View File

@ -1,6 +1,8 @@
"""Test the Logitech Harmony Hub config flow.""" """Test the Logitech Harmony Hub config flow."""
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
import aiohttp
from homeassistant import config_entries, data_entry_flow from homeassistant import config_entries, data_entry_flow
from homeassistant.components.harmony.config_flow import CannotConnect from homeassistant.components.harmony.config_flow import CannotConnect
from homeassistant.components.harmony.const import DOMAIN, PREVIOUS_ACTIVE_ACTIVITY from homeassistant.components.harmony.const import DOMAIN, PREVIOUS_ACTIVE_ACTIVITY
@ -49,11 +51,9 @@ async def test_user_form(hass):
async def test_form_ssdp(hass): async def test_form_ssdp(hass):
"""Test we get the form with ssdp source.""" """Test we get the form with ssdp source."""
harmonyapi = _get_mock_harmonyapi(connect=True)
with patch( with patch(
"homeassistant.components.harmony.util.HarmonyAPI", "homeassistant.components.harmony.config_flow.HubConnector.get_remote_id",
return_value=harmonyapi, return_value=1234,
): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
@ -75,6 +75,8 @@ async def test_form_ssdp(hass):
assert progress[0]["flow_id"] == result["flow_id"] assert progress[0]["flow_id"] == result["flow_id"]
assert progress[0]["context"]["confirm_only"] is True assert progress[0]["context"]["confirm_only"] is True
harmonyapi = _get_mock_harmonyapi(connect=True)
with patch( with patch(
"homeassistant.components.harmony.util.HarmonyAPI", "homeassistant.components.harmony.util.HarmonyAPI",
return_value=harmonyapi, return_value=harmonyapi,
@ -94,6 +96,25 @@ async def test_form_ssdp(hass):
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
async def test_form_ssdp_fails_to_get_remote_id(hass):
"""Test we abort if we cannot get the remote id."""
with patch(
"homeassistant.components.harmony.config_flow.HubConnector.get_remote_id",
side_effect=aiohttp.ClientError,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data={
"friendlyName": "Harmony Hub",
"ssdp_location": "http://192.168.1.12:8088/description",
},
)
assert result["type"] == "abort"
assert result["reason"] == "cannot_connect"
async def test_form_ssdp_aborts_before_checking_remoteid_if_host_known(hass): async def test_form_ssdp_aborts_before_checking_remoteid_if_host_known(hass):
"""Test we abort without connecting if the host is already known.""" """Test we abort without connecting if the host is already known."""