diff --git a/homeassistant/components/discovery.py b/homeassistant/components/discovery.py index 5d8102e3056..f87395520bb 100644 --- a/homeassistant/components/discovery.py +++ b/homeassistant/components/discovery.py @@ -21,7 +21,7 @@ from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.helpers.discovery import async_load_platform, async_discover import homeassistant.util.dt as dt_util -REQUIREMENTS = ['netdisco==2.2.0'] +REQUIREMENTS = ['netdisco==2.3.0'] DOMAIN = 'discovery' @@ -51,6 +51,7 @@ SERVICE_DLNA_DMR = 'dlna_dmr' CONFIG_ENTRY_HANDLERS = { SERVICE_DAIKIN: 'daikin', SERVICE_DECONZ: 'deconz', + 'esphome': 'esphome', 'google_cast': 'cast', SERVICE_HUE: 'hue', SERVICE_TELLDUSLIVE: 'tellduslive', diff --git a/homeassistant/components/esphome/config_flow.py b/homeassistant/components/esphome/config_flow.py index d6abf03cf5d..1f71d8d66b5 100644 --- a/homeassistant/components/esphome/config_flow.py +++ b/homeassistant/components/esphome/config_flow.py @@ -53,6 +53,24 @@ class EsphomeFlowHandler(config_entries.ConfigFlow): errors=errors ) + async def async_step_discovery(self, user_input: ConfigType): + """Handle discovery.""" + # mDNS hostname has additional '.' at end + hostname = user_input['hostname'][:-1] + hosts = (hostname, user_input['host']) + for entry in self._async_current_entries(): + if entry.data['host'] in hosts: + return self.async_abort( + reason='already_configured' + ) + + # Prefer .local addresses (mDNS is available after all, otherwise + # we wouldn't have received the discovery message) + return await self.async_step_user(user_input={ + 'host': hostname, + 'port': user_input['port'], + }) + def _async_get_entry(self): return self.async_create_entry( title=self._name, diff --git a/requirements_all.txt b/requirements_all.txt index 6a0eb4db397..fc8240c263b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -705,7 +705,7 @@ nessclient==0.9.9 netdata==0.1.2 # homeassistant.components.discovery -netdisco==2.2.0 +netdisco==2.3.0 # homeassistant.components.sensor.neurio_energy neurio==0.3.1 diff --git a/tests/components/esphome/test_config_flow.py b/tests/components/esphome/test_config_flow.py index 07398dce8be..1291aa53123 100644 --- a/tests/components/esphome/test_config_flow.py +++ b/tests/components/esphome/test_config_flow.py @@ -1,11 +1,11 @@ """Test config flow.""" from collections import namedtuple -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch import pytest from homeassistant.components.esphome import config_flow -from tests.common import mock_coro +from tests.common import mock_coro, MockConfigEntry MockDeviceInfo = namedtuple("DeviceInfo", ["uses_password", "name"]) @@ -186,3 +186,64 @@ async def test_user_invalid_password(hass, mock_api_connection_error, assert result['errors'] == { 'base': 'invalid_password' } + + +async def test_discovery_initiation(hass, mock_client): + """Test discovery importing works.""" + flow = config_flow.EsphomeFlowHandler() + flow.hass = hass + service_info = { + 'host': '192.168.43.183', + 'port': 6053, + 'hostname': 'test8266.local.', + 'properties': {} + } + + mock_client.device_info.return_value = mock_coro( + MockDeviceInfo(False, "test8266")) + + result = await flow.async_step_discovery(user_input=service_info) + assert result['type'] == 'create_entry' + assert result['title'] == 'test8266' + assert result['data']['host'] == 'test8266.local' + assert result['data']['port'] == 6053 + + +async def test_discovery_already_configured_hostname(hass, mock_client): + """Test discovery aborts if already configured via hostname.""" + MockConfigEntry( + domain='esphome', + data={'host': 'test8266.local', 'port': 6053, 'password': ''} + ).add_to_hass(hass) + + flow = config_flow.EsphomeFlowHandler() + flow.hass = hass + service_info = { + 'host': '192.168.43.183', + 'port': 6053, + 'hostname': 'test8266.local.', + 'properties': {} + } + result = await flow.async_step_discovery(user_input=service_info) + assert result['type'] == 'abort' + assert result['reason'] == 'already_configured' + + +async def test_discovery_already_configured_ip(hass, mock_client): + """Test discovery aborts if already configured via static IP.""" + MockConfigEntry( + domain='esphome', + data={'host': '192.168.43.183', 'port': 6053, 'password': ''} + ).add_to_hass(hass) + + flow = config_flow.EsphomeFlowHandler() + flow.hass = hass + service_info = { + 'host': '192.168.43.183', + 'port': 6053, + 'hostname': 'test8266.local.', + 'properties': {} + } + result = await flow.async_step_discovery(user_input=service_info) + assert result['type'] == 'abort' + assert result['reason'] == 'already_configured'