Clean up ElkM1 lib (#42319)

This commit is contained in:
Glenn Waters 2020-10-25 00:46:22 -04:00 committed by GitHub
parent f8d87bff16
commit cc77621477
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 51 deletions

View file

@ -249,18 +249,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
for keypad in elk.keypads: # pylint: disable=no-member for keypad in elk.keypads: # pylint: disable=no-member
keypad.add_callback(_element_changed) keypad.add_callback(_element_changed)
if not await async_wait_for_elk_to_sync(elk, SYNC_TIMEOUT): try:
_LOGGER.error( if not await async_wait_for_elk_to_sync(elk, SYNC_TIMEOUT, conf[CONF_HOST]):
"Timed out after %d seconds while trying to sync with ElkM1 at %s", return False
SYNC_TIMEOUT, except asyncio.TimeoutError as exc:
conf[CONF_HOST], raise ConfigEntryNotReady from exc
)
elk.disconnect()
raise ConfigEntryNotReady
if elk.invalid_auth:
_LOGGER.error("Authentication failed for ElkM1")
return False
hass.data[DOMAIN][entry.entry_id] = { hass.data[DOMAIN][entry.entry_id] = {
"elk": elk, "elk": elk,
@ -312,16 +305,40 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
return unload_ok return unload_ok
async def async_wait_for_elk_to_sync(elk, timeout): async def async_wait_for_elk_to_sync(elk, timeout, conf_host):
"""Wait until the elk system has finished sync.""" """Wait until the elk has finished sync. Can fail login or timeout."""
def login_status(succeeded):
nonlocal success
success = succeeded
if succeeded:
_LOGGER.info("ElkM1 login succeeded.")
else:
elk.disconnect()
_LOGGER.error("ElkM1 login failed; invalid username or password.")
event.set()
def sync_complete():
event.set()
success = True
event = asyncio.Event()
elk.add_handler("login", login_status)
elk.add_handler("sync_complete", sync_complete)
try: try:
with async_timeout.timeout(timeout): with async_timeout.timeout(timeout):
await elk.sync_complete() await event.wait()
return True
except asyncio.TimeoutError: except asyncio.TimeoutError:
_LOGGER.error(
"Timed out after %d seconds while trying to sync with ElkM1 at %s",
SYNC_TIMEOUT,
conf_host,
)
elk.disconnect() elk.disconnect()
raise
return False return success
def _create_elk_services(hass): def _create_elk_services(hass):

View file

@ -1,4 +1,5 @@
"""Config flow for Elk-M1 Control integration.""" """Config flow for Elk-M1 Control integration."""
import asyncio
import logging import logging
from urllib.parse import urlparse from urllib.parse import urlparse
@ -65,20 +66,7 @@ async def validate_input(data):
) )
elk.connect() elk.connect()
timed_out = False if not await async_wait_for_elk_to_sync(elk, VALIDATE_TIMEOUT, url):
if not await async_wait_for_elk_to_sync(elk, VALIDATE_TIMEOUT):
_LOGGER.error(
"Timed out after %d seconds while trying to sync with ElkM1 at %s",
VALIDATE_TIMEOUT,
url,
)
timed_out = True
elk.disconnect()
if timed_out:
raise CannotConnect
if elk.invalid_auth:
raise InvalidAuth raise InvalidAuth
device_name = data[CONF_PREFIX] if data[CONF_PREFIX] else "ElkM1" device_name = data[CONF_PREFIX] if data[CONF_PREFIX] else "ElkM1"
@ -116,7 +104,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
try: try:
info = await validate_input(user_input) info = await validate_input(user_input)
except CannotConnect: except asyncio.TimeoutError:
errors["base"] = "cannot_connect" errors["base"] = "cannot_connect"
except InvalidAuth: except InvalidAuth:
errors["base"] = "invalid_auth" errors["base"] = "invalid_auth"
@ -161,9 +149,5 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return urlparse(url).hostname in existing_hosts return urlparse(url).hostname in existing_hosts
class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""
class InvalidAuth(exceptions.HomeAssistantError): class InvalidAuth(exceptions.HomeAssistantError):
"""Error to indicate there is invalid auth.""" """Error to indicate there is invalid auth."""

View file

@ -2,7 +2,7 @@
"domain": "elkm1", "domain": "elkm1",
"name": "Elk-M1 Control", "name": "Elk-M1 Control",
"documentation": "https://www.home-assistant.io/integrations/elkm1", "documentation": "https://www.home-assistant.io/integrations/elkm1",
"requirements": ["elkm1-lib==0.8.4"], "requirements": ["elkm1-lib==0.8.5"],
"codeowners": ["@gwww", "@bdraco"], "codeowners": ["@gwww", "@bdraco"],
"config_flow": true "config_flow": true
} }

View file

@ -535,7 +535,7 @@ elgato==0.2.0
eliqonline==1.2.2 eliqonline==1.2.2
# homeassistant.components.elkm1 # homeassistant.components.elkm1
elkm1-lib==0.8.4 elkm1-lib==0.8.5
# homeassistant.components.mobile_app # homeassistant.components.mobile_app
emoji==0.5.4 emoji==0.5.4

View file

@ -278,7 +278,7 @@ eebrightbox==0.0.4
elgato==0.2.0 elgato==0.2.0
# homeassistant.components.elkm1 # homeassistant.components.elkm1
elkm1-lib==0.8.4 elkm1-lib==0.8.5
# homeassistant.components.mobile_app # homeassistant.components.mobile_app
emoji==0.5.4 emoji==0.5.4

View file

@ -3,14 +3,24 @@
from homeassistant import config_entries, setup from homeassistant import config_entries, setup
from homeassistant.components.elkm1.const import DOMAIN from homeassistant.components.elkm1.const import DOMAIN
from tests.async_mock import AsyncMock, MagicMock, PropertyMock, patch from tests.async_mock import MagicMock, patch
def mock_elk(invalid_auth=None, sync_complete=None): def mock_elk(invalid_auth=None, sync_complete=None):
"""Mock m1lib Elk.""" """Mock m1lib Elk."""
def handler_callbacks(type_, callback):
nonlocal invalid_auth, sync_complete
if type_ == "login":
if invalid_auth is not None:
callback(not invalid_auth)
elif type_ == "sync_complete":
if sync_complete:
callback()
mocked_elk = MagicMock() mocked_elk = MagicMock()
type(mocked_elk).invalid_auth = PropertyMock(return_value=invalid_auth) mocked_elk.add_handler.side_effect = handler_callbacks
type(mocked_elk).sync_complete = AsyncMock()
return mocked_elk return mocked_elk
@ -23,7 +33,7 @@ async def test_form_user_with_secure_elk(hass):
assert result["type"] == "form" assert result["type"] == "form"
assert result["errors"] == {} assert result["errors"] == {}
mocked_elk = mock_elk(invalid_auth=False) mocked_elk = mock_elk(invalid_auth=False, sync_complete=True)
with patch( with patch(
"homeassistant.components.elkm1.config_flow.elkm1.Elk", "homeassistant.components.elkm1.config_flow.elkm1.Elk",
@ -70,7 +80,7 @@ async def test_form_user_with_non_secure_elk(hass):
assert result["type"] == "form" assert result["type"] == "form"
assert result["errors"] == {} assert result["errors"] == {}
mocked_elk = mock_elk(invalid_auth=False) mocked_elk = mock_elk(invalid_auth=None, sync_complete=True)
with patch( with patch(
"homeassistant.components.elkm1.config_flow.elkm1.Elk", "homeassistant.components.elkm1.config_flow.elkm1.Elk",
@ -115,7 +125,7 @@ async def test_form_user_with_serial_elk(hass):
assert result["type"] == "form" assert result["type"] == "form"
assert result["errors"] == {} assert result["errors"] == {}
mocked_elk = mock_elk(invalid_auth=False) mocked_elk = mock_elk(invalid_auth=None, sync_complete=True)
with patch( with patch(
"homeassistant.components.elkm1.config_flow.elkm1.Elk", "homeassistant.components.elkm1.config_flow.elkm1.Elk",
@ -153,19 +163,21 @@ async def test_form_user_with_serial_elk(hass):
async def test_form_cannot_connect(hass): async def test_form_cannot_connect(hass):
"""Test we handle cannot connect error.""" """Test we handle cannot connect error."""
from asyncio import TimeoutError
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
mocked_elk = mock_elk(invalid_auth=False) mocked_elk = mock_elk(invalid_auth=None, sync_complete=None)
with patch( with patch(
"homeassistant.components.elkm1.config_flow.elkm1.Elk", "homeassistant.components.elkm1.config_flow.elkm1.Elk",
return_value=mocked_elk, return_value=mocked_elk,
), patch( ), patch(
"homeassistant.components.elkm1.config_flow.async_wait_for_elk_to_sync", "homeassistant.components.elkm1.async_timeout.timeout",
return_value=False, side_effect=TimeoutError,
): # async_wait_for_elk_to_sync is being patched to avoid making the test wait 45s ):
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{ {
@ -188,7 +200,7 @@ async def test_form_invalid_auth(hass):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
mocked_elk = mock_elk(invalid_auth=True) mocked_elk = mock_elk(invalid_auth=True, sync_complete=True)
with patch( with patch(
"homeassistant.components.elkm1.config_flow.elkm1.Elk", "homeassistant.components.elkm1.config_flow.elkm1.Elk",
@ -214,7 +226,7 @@ async def test_form_import(hass):
"""Test we get the form with import source.""" """Test we get the form with import source."""
await setup.async_setup_component(hass, "persistent_notification", {}) await setup.async_setup_component(hass, "persistent_notification", {})
mocked_elk = mock_elk(invalid_auth=False) mocked_elk = mock_elk(invalid_auth=False, sync_complete=True)
with patch( with patch(
"homeassistant.components.elkm1.config_flow.elkm1.Elk", "homeassistant.components.elkm1.config_flow.elkm1.Elk",
return_value=mocked_elk, return_value=mocked_elk,