Clean up ElkM1 lib (#42319)
This commit is contained in:
parent
f8d87bff16
commit
cc77621477
6 changed files with 64 additions and 51 deletions
|
@ -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):
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Reference in a new issue