mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Fix ElkM1 systems that do not use password authentication (#67194)
This commit is contained in:
parent
4dc6aab17e
commit
00c6e30988
@ -228,7 +228,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
_LOGGER.debug("Setting up elkm1 %s", conf["host"])
|
_LOGGER.debug("Setting up elkm1 %s", conf["host"])
|
||||||
|
|
||||||
if not entry.unique_id or ":" not in entry.unique_id and is_ip_address(host):
|
if (not entry.unique_id or ":" not in entry.unique_id) and is_ip_address(host):
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Unique id for %s is missing during setup, trying to fill from discovery",
|
||||||
|
host,
|
||||||
|
)
|
||||||
if device := await async_discover_device(hass, host):
|
if device := await async_discover_device(hass, host):
|
||||||
async_update_entry_from_discovery(hass, entry, device)
|
async_update_entry_from_discovery(hass, entry, device)
|
||||||
|
|
||||||
@ -276,7 +280,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if not await async_wait_for_elk_to_sync(
|
if not await async_wait_for_elk_to_sync(
|
||||||
elk, LOGIN_TIMEOUT, SYNC_TIMEOUT, conf[CONF_HOST]
|
elk, LOGIN_TIMEOUT, SYNC_TIMEOUT, bool(conf[CONF_USERNAME])
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
except asyncio.TimeoutError as exc:
|
except asyncio.TimeoutError as exc:
|
||||||
@ -327,7 +331,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
async def async_wait_for_elk_to_sync(
|
async def async_wait_for_elk_to_sync(
|
||||||
elk: elkm1.Elk, login_timeout: int, sync_timeout: int, conf_host: str
|
elk: elkm1.Elk,
|
||||||
|
login_timeout: int,
|
||||||
|
sync_timeout: int,
|
||||||
|
password_auth: bool,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Wait until the elk has finished sync. Can fail login or timeout."""
|
"""Wait until the elk has finished sync. Can fail login or timeout."""
|
||||||
|
|
||||||
@ -353,15 +360,21 @@ async def async_wait_for_elk_to_sync(
|
|||||||
success = True
|
success = True
|
||||||
elk.add_handler("login", login_status)
|
elk.add_handler("login", login_status)
|
||||||
elk.add_handler("sync_complete", sync_complete)
|
elk.add_handler("sync_complete", sync_complete)
|
||||||
events = ((login_event, login_timeout), (sync_event, sync_timeout))
|
events = []
|
||||||
|
if password_auth:
|
||||||
|
events.append(("login", login_event, login_timeout))
|
||||||
|
events.append(("sync_complete", sync_event, sync_timeout))
|
||||||
|
|
||||||
for event, timeout in events:
|
for name, event, timeout in events:
|
||||||
|
_LOGGER.debug("Waiting for %s event for %s seconds", name, timeout)
|
||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(timeout):
|
async with async_timeout.timeout(timeout):
|
||||||
await event.wait()
|
await event.wait()
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
|
_LOGGER.debug("Timed out waiting for %s event", name)
|
||||||
elk.disconnect()
|
elk.disconnect()
|
||||||
raise
|
raise
|
||||||
|
_LOGGER.debug("Received %s event", name)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from homeassistant.data_entry_flow import FlowResult
|
|||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
from homeassistant.util.network import is_ip_address
|
||||||
|
|
||||||
from . import async_wait_for_elk_to_sync
|
from . import async_wait_for_elk_to_sync
|
||||||
from .const import CONF_AUTO_CONFIGURE, DISCOVER_SCAN_TIMEOUT, DOMAIN, LOGIN_TIMEOUT
|
from .const import CONF_AUTO_CONFIGURE, DISCOVER_SCAN_TIMEOUT, DOMAIN, LOGIN_TIMEOUT
|
||||||
@ -80,7 +81,9 @@ async def validate_input(data: dict[str, str], mac: str | None) -> dict[str, str
|
|||||||
)
|
)
|
||||||
elk.connect()
|
elk.connect()
|
||||||
|
|
||||||
if not await async_wait_for_elk_to_sync(elk, LOGIN_TIMEOUT, VALIDATE_TIMEOUT, url):
|
if not await async_wait_for_elk_to_sync(
|
||||||
|
elk, LOGIN_TIMEOUT, VALIDATE_TIMEOUT, bool(userid)
|
||||||
|
):
|
||||||
raise InvalidAuth
|
raise InvalidAuth
|
||||||
|
|
||||||
short_mac = _short_mac(mac) if mac else None
|
short_mac = _short_mac(mac) if mac else None
|
||||||
@ -124,6 +127,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self._discovered_device = ElkSystem(
|
self._discovered_device = ElkSystem(
|
||||||
discovery_info.macaddress, discovery_info.ip, 0
|
discovery_info.macaddress, discovery_info.ip, 0
|
||||||
)
|
)
|
||||||
|
_LOGGER.debug("Elk discovered from dhcp: %s", self._discovered_device)
|
||||||
return await self._async_handle_discovery()
|
return await self._async_handle_discovery()
|
||||||
|
|
||||||
async def async_step_integration_discovery(
|
async def async_step_integration_discovery(
|
||||||
@ -135,6 +139,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
discovery_info["ip_address"],
|
discovery_info["ip_address"],
|
||||||
discovery_info["port"],
|
discovery_info["port"],
|
||||||
)
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Elk discovered from integration discovery: %s", self._discovered_device
|
||||||
|
)
|
||||||
return await self._async_handle_discovery()
|
return await self._async_handle_discovery()
|
||||||
|
|
||||||
async def _async_handle_discovery(self) -> FlowResult:
|
async def _async_handle_discovery(self) -> FlowResult:
|
||||||
@ -304,11 +311,22 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
async def async_step_import(self, user_input):
|
async def async_step_import(self, user_input):
|
||||||
"""Handle import."""
|
"""Handle import."""
|
||||||
if device := await async_discover_device(
|
_LOGGER.debug("Elk is importing from yaml")
|
||||||
self.hass, urlparse(user_input[CONF_HOST]).hostname
|
url = _make_url_from_data(user_input)
|
||||||
|
|
||||||
|
if self._url_already_configured(url):
|
||||||
|
return self.async_abort(reason="address_already_configured")
|
||||||
|
|
||||||
|
host = urlparse(url).hostname
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Importing is trying to fill unique id from discovery for %s", host
|
||||||
|
)
|
||||||
|
if is_ip_address(host) and (
|
||||||
|
device := await async_discover_device(self.hass, host)
|
||||||
):
|
):
|
||||||
await self.async_set_unique_id(dr.format_mac(device.mac_address))
|
await self.async_set_unique_id(dr.format_mac(device.mac_address))
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
return (await self._async_create_or_error(user_input, True))[1]
|
return (await self._async_create_or_error(user_input, True))[1]
|
||||||
|
|
||||||
def _url_already_configured(self, url):
|
def _url_already_configured(self, url):
|
||||||
|
@ -9,7 +9,7 @@ from homeassistant.const import ATTR_CODE, CONF_ZONE
|
|||||||
|
|
||||||
DOMAIN = "elkm1"
|
DOMAIN = "elkm1"
|
||||||
|
|
||||||
LOGIN_TIMEOUT = 15
|
LOGIN_TIMEOUT = 20
|
||||||
|
|
||||||
CONF_AUTO_CONFIGURE = "auto_configure"
|
CONF_AUTO_CONFIGURE = "auto_configure"
|
||||||
CONF_AREA = "area"
|
CONF_AREA = "area"
|
||||||
|
@ -29,9 +29,11 @@ def async_update_entry_from_discovery(
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
"""Update a config entry from a discovery."""
|
"""Update a config entry from a discovery."""
|
||||||
if not entry.unique_id or ":" not in entry.unique_id:
|
if not entry.unique_id or ":" not in entry.unique_id:
|
||||||
|
_LOGGER.debug("Adding unique id from discovery: %s", device)
|
||||||
return hass.config_entries.async_update_entry(
|
return hass.config_entries.async_update_entry(
|
||||||
entry, unique_id=dr.format_mac(device.mac_address)
|
entry, unique_id=dr.format_mac(device.mac_address)
|
||||||
)
|
)
|
||||||
|
_LOGGER.debug("Unique id is already present from discovery: %s", device)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -652,6 +652,50 @@ async def test_form_import_device_discovered(hass):
|
|||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_import_existing(hass):
|
||||||
|
"""Test we abort on existing import."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={CONF_HOST: f"elks://{MOCK_IP_ADDRESS}"},
|
||||||
|
unique_id="cc:cc:cc:cc:cc:cc",
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": config_entries.SOURCE_IMPORT},
|
||||||
|
data={
|
||||||
|
"host": f"elks://{MOCK_IP_ADDRESS}",
|
||||||
|
"username": "friend",
|
||||||
|
"password": "love",
|
||||||
|
"temperature_unit": "C",
|
||||||
|
"auto_configure": False,
|
||||||
|
"keypad": {
|
||||||
|
"enabled": True,
|
||||||
|
"exclude": [],
|
||||||
|
"include": [[1, 1], [2, 2], [3, 3]],
|
||||||
|
},
|
||||||
|
"output": {"enabled": False, "exclude": [], "include": []},
|
||||||
|
"counter": {"enabled": False, "exclude": [], "include": []},
|
||||||
|
"plc": {"enabled": False, "exclude": [], "include": []},
|
||||||
|
"prefix": "ohana",
|
||||||
|
"setting": {"enabled": False, "exclude": [], "include": []},
|
||||||
|
"area": {"enabled": False, "exclude": [], "include": []},
|
||||||
|
"task": {"enabled": False, "exclude": [], "include": []},
|
||||||
|
"thermostat": {"enabled": False, "exclude": [], "include": []},
|
||||||
|
"zone": {
|
||||||
|
"enabled": True,
|
||||||
|
"exclude": [[15, 15], [28, 208]],
|
||||||
|
"include": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "address_already_configured"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"source, data",
|
"source, data",
|
||||||
[
|
[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user