diff --git a/CODEOWNERS b/CODEOWNERS
index 62e6f0d8e6683b35c039b23ae1b27f2cbd144741..cbd4ae11c248589ad8f45aa8f58d34085dac15a7 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -99,6 +99,7 @@ homeassistant/components/edl21/* @mtdcr
 homeassistant/components/egardia/* @jeroenterheerdt
 homeassistant/components/eight_sleep/* @mezz64
 homeassistant/components/elgato/* @frenck
+homeassistant/components/elkm1/* @bdraco
 homeassistant/components/elv/* @majuss
 homeassistant/components/emby/* @mezz64
 homeassistant/components/emoncms/* @borpin
diff --git a/homeassistant/components/elkm1/.translations/en.json b/homeassistant/components/elkm1/.translations/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..a5246a004c3ee2684cbecfd3b24c66c726a32a51
--- /dev/null
+++ b/homeassistant/components/elkm1/.translations/en.json
@@ -0,0 +1,28 @@
+{
+  "config": {
+    "title": "Elk-M1 Control",
+    "step": {
+      "user": {
+        "title": "Connect to Elk-M1 Control",
+        "description": "The address string must be in the form 'address[:port]' for 'secure' and 'non-secure'. Example: '192.168.1.1'. The port is optional and defaults to 2101 for 'non-secure' and 2601 for 'secure'. For the serial protocol, the address must be in the form 'tty[:baud]'. Example: '/dev/ttyS1'. The baud is optional and defaults to 115200.",
+        "data": {
+          "protocol": "Protocol",
+          "address": "The IP address or domain or serial port if connecting via serial.",
+          "username": "Username (secure only).",
+          "password": "Password (secure only).",
+          "prefix": "A unique prefix (leave blank if you only have one ElkM1).",
+          "temperature_unit": "The temperature unit ElkM1 uses."
+        }
+      }
+    },
+    "error": {
+      "cannot_connect": "Failed to connect, please try again",
+      "invalid_auth": "Invalid authentication",
+      "unknown": "Unexpected error"
+    },
+    "abort": {
+      "already_configured": "An ElkM1 with this prefix is already configured",
+      "address_already_configured": "An ElkM1 with this address is already configured"
+    }
+  }
+}
\ No newline at end of file
diff --git a/homeassistant/components/elkm1/__init__.py b/homeassistant/components/elkm1/__init__.py
index 2acb8030cf1d2fc638d9addc3aa727bf0d9df025..2f08b046d9c06509d19644afaf18cf26b7d7a156 100644
--- a/homeassistant/components/elkm1/__init__.py
+++ b/homeassistant/components/elkm1/__init__.py
@@ -1,11 +1,13 @@
 """Support the ElkM1 Gold and ElkM1 EZ8 alarm/integration panels."""
+import asyncio
 import logging
 import re
 
+import async_timeout
 import elkm1_lib as elkm1
-from elkm1_lib.const import Max
 import voluptuous as vol
 
+from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
 from homeassistant.const import (
     CONF_EXCLUDE,
     CONF_HOST,
@@ -15,23 +17,29 @@ from homeassistant.const import (
     CONF_USERNAME,
 )
 from homeassistant.core import HomeAssistant, callback
-from homeassistant.helpers import config_validation as cv, discovery
+from homeassistant.exceptions import ConfigEntryNotReady
+from homeassistant.helpers import config_validation as cv
 from homeassistant.helpers.entity import Entity
 from homeassistant.helpers.typing import ConfigType
 
-DOMAIN = "elkm1"
+from .const import (
+    CONF_AREA,
+    CONF_AUTO_CONFIGURE,
+    CONF_COUNTER,
+    CONF_ENABLED,
+    CONF_KEYPAD,
+    CONF_OUTPUT,
+    CONF_PLC,
+    CONF_PREFIX,
+    CONF_SETTING,
+    CONF_TASK,
+    CONF_THERMOSTAT,
+    CONF_ZONE,
+    DOMAIN,
+    ELK_ELEMENTS,
+)
 
-CONF_AREA = "area"
-CONF_COUNTER = "counter"
-CONF_ENABLED = "enabled"
-CONF_KEYPAD = "keypad"
-CONF_OUTPUT = "output"
-CONF_PLC = "plc"
-CONF_SETTING = "setting"
-CONF_TASK = "task"
-CONF_THERMOSTAT = "thermostat"
-CONF_ZONE = "zone"
-CONF_PREFIX = "prefix"
+SYNC_TIMEOUT = 55
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -110,6 +118,7 @@ DEVICE_SCHEMA = vol.Schema(
         vol.Optional(CONF_PREFIX, default=""): vol.All(cv.string, vol.Lower),
         vol.Optional(CONF_USERNAME, default=""): cv.string,
         vol.Optional(CONF_PASSWORD, default=""): cv.string,
+        vol.Optional(CONF_AUTO_CONFIGURE, default=False): cv.boolean,
         vol.Optional(CONF_TEMPERATURE_UNIT, default="F"): cv.temperature_unit,
         vol.Optional(CONF_AREA, default={}): DEVICE_SCHEMA_SUBDOMAIN,
         vol.Optional(CONF_COUNTER, default={}): DEVICE_SCHEMA_SUBDOMAIN,
@@ -132,34 +141,53 @@ CONFIG_SCHEMA = vol.Schema(
 
 async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool:
     """Set up the Elk M1 platform."""
-    devices = {}
-    elk_datas = {}
-
-    configs = {
-        CONF_AREA: Max.AREAS.value,
-        CONF_COUNTER: Max.COUNTERS.value,
-        CONF_KEYPAD: Max.KEYPADS.value,
-        CONF_OUTPUT: Max.OUTPUTS.value,
-        CONF_PLC: Max.LIGHTS.value,
-        CONF_SETTING: Max.SETTINGS.value,
-        CONF_TASK: Max.TASKS.value,
-        CONF_THERMOSTAT: Max.THERMOSTATS.value,
-        CONF_ZONE: Max.ZONES.value,
-    }
+    hass.data.setdefault(DOMAIN, {})
+    _create_elk_services(hass)
 
-    def _included(ranges, set_to, values):
-        for rng in ranges:
-            if not rng[0] <= rng[1] <= len(values):
-                raise vol.Invalid(f"Invalid range {rng}")
-            values[rng[0] - 1 : rng[1]] = [set_to] * (rng[1] - rng[0] + 1)
+    if DOMAIN not in hass_config:
+        return True
 
     for index, conf in enumerate(hass_config[DOMAIN]):
-        _LOGGER.debug("Setting up elkm1 #%d - %s", index, conf["host"])
+        _LOGGER.debug("Importing elkm1 #%d - %s", index, conf[CONF_HOST])
+        current_config_entry = _async_find_matching_config_entry(
+            hass, conf[CONF_PREFIX]
+        )
+        if current_config_entry:
+            # If they alter the yaml config we import the changes
+            # since there currently is no practical way to do an options flow
+            # with the large amount of include/exclude/enabled options that elkm1 has.
+            hass.config_entries.async_update_entry(current_config_entry, data=conf)
+            continue
 
-        config = {"temperature_unit": conf[CONF_TEMPERATURE_UNIT]}
-        config["panel"] = {"enabled": True, "included": [True]}
+        hass.async_create_task(
+            hass.config_entries.flow.async_init(
+                DOMAIN, context={"source": SOURCE_IMPORT}, data=conf,
+            )
+        )
+
+    return True
+
+
+@callback
+def _async_find_matching_config_entry(hass, prefix):
+    for entry in hass.config_entries.async_entries(DOMAIN):
+        if entry.unique_id == prefix:
+            return entry
+
+
+async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
+    """Set up Elk-M1 Control from a config entry."""
+
+    conf = entry.data
 
-        for item, max_ in configs.items():
+    _LOGGER.debug("Setting up elkm1 %s", conf["host"])
+
+    config = {"temperature_unit": conf[CONF_TEMPERATURE_UNIT]}
+
+    if not conf[CONF_AUTO_CONFIGURE]:
+        # With elkm1-lib==0.7.16 and later auto configure is available
+        config["panel"] = {"enabled": True, "included": [True]}
+        for item, max_ in ELK_ELEMENTS.items():
             config[item] = {
                 "enabled": conf[item][CONF_ENABLED],
                 "included": [not conf[item]["include"]] * max_,
@@ -171,39 +199,92 @@ async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool:
                 _LOGGER.error("Config item: %s; %s", item, err)
                 return False
 
-        prefix = conf[CONF_PREFIX]
-        elk = elkm1.Elk(
-            {
-                "url": conf[CONF_HOST],
-                "userid": conf[CONF_USERNAME],
-                "password": conf[CONF_PASSWORD],
-            }
-        )
-        elk.connect()
-
-        devices[prefix] = elk
-        elk_datas[prefix] = {
-            "elk": elk,
-            "prefix": prefix,
-            "config": config,
-            "keypads": {},
+    elk = elkm1.Elk(
+        {
+            "url": conf[CONF_HOST],
+            "userid": conf[CONF_USERNAME],
+            "password": conf[CONF_PASSWORD],
         }
+    )
+    elk.connect()
 
-    _create_elk_services(hass, devices)
+    if not await async_wait_for_elk_to_sync(elk, SYNC_TIMEOUT):
+        _LOGGER.error(
+            "Timed out after %d seconds while trying to sync with ElkM1", SYNC_TIMEOUT,
+        )
+        elk.disconnect()
+        raise ConfigEntryNotReady
+
+    if elk.invalid_auth:
+        _LOGGER.error("Authentication failed for ElkM1")
+        return False
+
+    hass.data[DOMAIN][entry.entry_id] = {
+        "elk": elk,
+        "prefix": conf[CONF_PREFIX],
+        "auto_configure": conf[CONF_AUTO_CONFIGURE],
+        "config": config,
+        "keypads": {},
+    }
 
-    hass.data[DOMAIN] = elk_datas
     for component in SUPPORTED_DOMAINS:
         hass.async_create_task(
-            discovery.async_load_platform(hass, component, DOMAIN, {}, hass_config)
+            hass.config_entries.async_forward_entry_setup(entry, component)
         )
 
     return True
 
 
-def _create_elk_services(hass, elks):
+def _included(ranges, set_to, values):
+    for rng in ranges:
+        if not rng[0] <= rng[1] <= len(values):
+            raise vol.Invalid(f"Invalid range {rng}")
+        values[rng[0] - 1 : rng[1]] = [set_to] * (rng[1] - rng[0] + 1)
+
+
+def _find_elk_by_prefix(hass, prefix):
+    """Search all config entries for a given prefix."""
+    for entry_id in hass.data[DOMAIN]:
+        if hass.data[DOMAIN][entry_id]["prefix"] == prefix:
+            return hass.data[DOMAIN][entry_id]["elk"]
+
+
+async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
+    """Unload a config entry."""
+    unload_ok = all(
+        await asyncio.gather(
+            *[
+                hass.config_entries.async_forward_entry_unload(entry, component)
+                for component in SUPPORTED_DOMAINS
+            ]
+        )
+    )
+
+    # disconnect cleanly
+    hass.data[DOMAIN][entry.entry_id]["elk"].disconnect()
+
+    if unload_ok:
+        hass.data[DOMAIN].pop(entry.entry_id)
+
+    return unload_ok
+
+
+async def async_wait_for_elk_to_sync(elk, timeout):
+    """Wait until the elk system has finished sync."""
+    try:
+        with async_timeout.timeout(timeout):
+            await elk.sync_complete()
+            return True
+    except asyncio.TimeoutError:
+        elk.disconnect()
+
+    return False
+
+
+def _create_elk_services(hass):
     def _speak_word_service(service):
         prefix = service.data["prefix"]
-        elk = elks.get(prefix)
+        elk = _find_elk_by_prefix(hass, prefix)
         if elk is None:
             _LOGGER.error("No elk m1 with prefix for speak_word: '%s'", prefix)
             return
@@ -211,7 +292,7 @@ def _create_elk_services(hass, elks):
 
     def _speak_phrase_service(service):
         prefix = service.data["prefix"]
-        elk = elks.get(prefix)
+        elk = _find_elk_by_prefix(hass, prefix)
         if elk is None:
             _LOGGER.error("No elk m1 with prefix for speak_phrase: '%s'", prefix)
             return
@@ -227,12 +308,23 @@ def _create_elk_services(hass, elks):
 
 def create_elk_entities(elk_data, elk_elements, element_type, class_, entities):
     """Create the ElkM1 devices of a particular class."""
-    if elk_data["config"][element_type]["enabled"]:
-        elk = elk_data["elk"]
-        _LOGGER.debug("Creating elk entities for %s", elk)
-        for element in elk_elements:
-            if elk_data["config"][element_type]["included"][element.index]:
-                entities.append(class_(element, elk, elk_data))
+    auto_configure = elk_data["auto_configure"]
+
+    if not auto_configure and not elk_data["config"][element_type]["enabled"]:
+        return
+
+    elk = elk_data["elk"]
+    _LOGGER.debug("Creating elk entities for %s", elk)
+
+    for element in elk_elements:
+        if auto_configure:
+            if not element.configured:
+                continue
+        # Only check the included list if auto configure is not
+        elif not elk_data["config"][element_type]["included"][element.index]:
+            continue
+
+        entities.append(class_(element, elk, elk_data))
     return entities
 
 
@@ -297,9 +389,34 @@ class ElkEntity(Entity):
     def _element_callback(self, element, changeset):
         """Handle callback from an Elk element that has changed."""
         self._element_changed(element, changeset)
-        self.async_schedule_update_ha_state(True)
+        self.async_write_ha_state()
 
     async def async_added_to_hass(self):
         """Register callback for ElkM1 changes and update entity state."""
         self._element.add_callback(self._element_callback)
         self._element_callback(self._element, {})
+
+    @property
+    def device_info(self):
+        """Device info connecting via the ElkM1 system."""
+        return {
+            "via_device": (DOMAIN, f"{self._prefix}_system"),
+        }
+
+
+class ElkAttachedEntity(ElkEntity):
+    """An elk entity that is attached to the elk system."""
+
+    @property
+    def device_info(self):
+        """Device info for the underlying ElkM1 system."""
+        device_name = "ElkM1"
+        if self._prefix:
+            device_name += f" {self._prefix}"
+        return {
+            "name": device_name,
+            "identifiers": {(DOMAIN, f"{self._prefix}_system")},
+            "sw_version": self._elk.panel.elkm1_version,
+            "manufacturer": "ELK Products, Inc.",
+            "model": "M1",
+        }
diff --git a/homeassistant/components/elkm1/alarm_control_panel.py b/homeassistant/components/elkm1/alarm_control_panel.py
index de1cb62234c78985aaa36c58d01f945fa12af979..d7cd5cf2ad09792f62962c055ca53b5f9487efab 100644
--- a/homeassistant/components/elkm1/alarm_control_panel.py
+++ b/homeassistant/components/elkm1/alarm_control_panel.py
@@ -1,4 +1,6 @@
 """Each ElkM1 area will be created as a separate alarm_control_panel."""
+import logging
+
 from elkm1_lib.const import AlarmState, ArmedStatus, ArmLevel, ArmUpState
 import voluptuous as vol
 
@@ -22,24 +24,18 @@ from homeassistant.const import (
     STATE_ALARM_PENDING,
     STATE_ALARM_TRIGGERED,
 )
+from homeassistant.helpers import entity_platform
 import homeassistant.helpers.config_validation as cv
-from homeassistant.helpers.dispatcher import (
-    async_dispatcher_connect,
-    async_dispatcher_send,
-)
 
 from . import (
-    DOMAIN,
     SERVICE_ALARM_ARM_HOME_INSTANT,
     SERVICE_ALARM_ARM_NIGHT_INSTANT,
     SERVICE_ALARM_ARM_VACATION,
     SERVICE_ALARM_DISPLAY_MESSAGE,
-    ElkEntity,
+    ElkAttachedEntity,
     create_elk_entities,
 )
-
-SIGNAL_ARM_ENTITY = "elkm1_arm"
-SIGNAL_DISPLAY_MESSAGE = "elkm1_display_message"
+from .const import DOMAIN
 
 ELK_ALARM_SERVICE_SCHEMA = vol.Schema(
     {
@@ -61,69 +57,57 @@ DISPLAY_MESSAGE_SERVICE_SCHEMA = vol.Schema(
     }
 )
 
+_LOGGER = logging.getLogger(__name__)
 
-async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
-    """Set up the ElkM1 alarm platform."""
-    if discovery_info is None:
-        return
 
-    elk_datas = hass.data[DOMAIN]
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the ElkM1 alarm platform."""
+    elk_data = hass.data[DOMAIN][config_entry.entry_id]
     entities = []
-    for elk_data in elk_datas.values():
-        elk = elk_data["elk"]
-        entities = create_elk_entities(elk_data, elk.areas, "area", ElkArea, entities)
+
+    elk = elk_data["elk"]
+    areas_with_keypad = set()
+    for keypad in elk.keypads:
+        areas_with_keypad.add(keypad.area)
+
+    areas = []
+    for area in elk.areas:
+        if area.index in areas_with_keypad or elk_data["auto_configure"] is False:
+            areas.append(area)
+    create_elk_entities(elk_data, areas, "area", ElkArea, entities)
     async_add_entities(entities, True)
 
-    def _dispatch(signal, entity_ids, *args):
-        for entity_id in entity_ids:
-            async_dispatcher_send(hass, f"{signal}_{entity_id}", *args)
-
-    def _arm_service(service):
-        entity_ids = service.data.get(ATTR_ENTITY_ID, [])
-        arm_level = _arm_services().get(service.service)
-        args = (arm_level, service.data.get(ATTR_CODE))
-        _dispatch(SIGNAL_ARM_ENTITY, entity_ids, *args)
-
-    for service in _arm_services():
-        hass.services.async_register(
-            DOMAIN, service, _arm_service, ELK_ALARM_SERVICE_SCHEMA
-        )
-
-    def _display_message_service(service):
-        entity_ids = service.data.get(ATTR_ENTITY_ID, [])
-        data = service.data
-        args = (
-            data["clear"],
-            data["beep"],
-            data["timeout"],
-            data["line1"],
-            data["line2"],
-        )
-        _dispatch(SIGNAL_DISPLAY_MESSAGE, entity_ids, *args)
-
-    hass.services.async_register(
-        DOMAIN,
+    platform = entity_platform.current_platform.get()
+
+    platform.async_register_entity_service(
+        SERVICE_ALARM_ARM_VACATION,
+        ELK_ALARM_SERVICE_SCHEMA,
+        "async_alarm_arm_vacation",
+    )
+    platform.async_register_entity_service(
+        SERVICE_ALARM_ARM_HOME_INSTANT,
+        ELK_ALARM_SERVICE_SCHEMA,
+        "async_alarm_arm_home_instant",
+    )
+    platform.async_register_entity_service(
+        SERVICE_ALARM_ARM_NIGHT_INSTANT,
+        ELK_ALARM_SERVICE_SCHEMA,
+        "async_alarm_arm_night_instant",
+    )
+    platform.async_register_entity_service(
         SERVICE_ALARM_DISPLAY_MESSAGE,
-        _display_message_service,
         DISPLAY_MESSAGE_SERVICE_SCHEMA,
+        "async_display_message",
     )
 
 
-def _arm_services():
-    return {
-        SERVICE_ALARM_ARM_VACATION: ArmLevel.ARMED_VACATION.value,
-        SERVICE_ALARM_ARM_HOME_INSTANT: ArmLevel.ARMED_STAY_INSTANT.value,
-        SERVICE_ALARM_ARM_NIGHT_INSTANT: ArmLevel.ARMED_NIGHT_INSTANT.value,
-    }
-
-
-class ElkArea(ElkEntity, AlarmControlPanel):
+class ElkArea(ElkAttachedEntity, AlarmControlPanel):
     """Representation of an Area / Partition within the ElkM1 alarm panel."""
 
     def __init__(self, element, elk, elk_data):
         """Initialize Area as Alarm Control Panel."""
         super().__init__(element, elk, elk_data)
-        self._changed_by_entity_id = ""
+        self._changed_by_keypad = None
         self._state = None
 
     async def async_added_to_hass(self):
@@ -131,23 +115,13 @@ class ElkArea(ElkEntity, AlarmControlPanel):
         await super().async_added_to_hass()
         for keypad in self._elk.keypads:
             keypad.add_callback(self._watch_keypad)
-        async_dispatcher_connect(
-            self.hass, f"{SIGNAL_ARM_ENTITY}_{self.entity_id}", self._arm_service
-        )
-        async_dispatcher_connect(
-            self.hass,
-            f"{SIGNAL_DISPLAY_MESSAGE}_{self.entity_id}",
-            self._display_message,
-        )
 
     def _watch_keypad(self, keypad, changeset):
         if keypad.area != self._element.index:
             return
         if changeset.get("last_user") is not None:
-            self._changed_by_entity_id = self.hass.data[DOMAIN][self._prefix][
-                "keypads"
-            ].get(keypad.index, "")
-            self.async_schedule_update_ha_state(True)
+            self._changed_by_keypad = keypad.name
+            self.async_write_ha_state()
 
     @property
     def code_format(self):
@@ -178,7 +152,7 @@ class ElkArea(ElkEntity, AlarmControlPanel):
             attrs["arm_up_state"] = ArmUpState(elmt.arm_up_state).name.lower()
         if elmt.alarm_state is not None:
             attrs["alarm_state"] = AlarmState(elmt.alarm_state).name.lower()
-        attrs["changed_by_entity_id"] = self._changed_by_entity_id
+        attrs["changed_by_keypad"] = self._changed_by_keypad
         return attrs
 
     def _element_changed(self, element, changeset):
@@ -225,9 +199,18 @@ class ElkArea(ElkEntity, AlarmControlPanel):
         """Send arm night command."""
         self._element.arm(ArmLevel.ARMED_NIGHT.value, int(code))
 
-    async def _arm_service(self, arm_level, code):
-        self._element.arm(arm_level, code)
+    async def async_alarm_arm_home_instant(self, code=None):
+        """Send arm stay instant command."""
+        self._element.arm(ArmLevel.ARMED_STAY_INSTANT.value, int(code))
+
+    async def async_alarm_arm_night_instant(self, code=None):
+        """Send arm night instant command."""
+        self._element.arm(ArmLevel.ARMED_NIGHT_INSTANT.value, int(code))
+
+    async def async_alarm_arm_vacation(self, code=None):
+        """Send arm vacation command."""
+        self._element.arm(ArmLevel.ARMED_VACATION.value, int(code))
 
-    async def _display_message(self, clear, beep, timeout, line1, line2):
+    async def async_display_message(self, clear, beep, timeout, line1, line2):
         """Display a message on all keypads for the area."""
         self._element.display_message(clear, beep, timeout, line1, line2)
diff --git a/homeassistant/components/elkm1/climate.py b/homeassistant/components/elkm1/climate.py
index abc9dc0933c57e427c2f024c97286e567f23cf80..3c5c70b2bd02d884b93167085c9bf228676d5d91 100644
--- a/homeassistant/components/elkm1/climate.py
+++ b/homeassistant/components/elkm1/climate.py
@@ -14,9 +14,10 @@ from homeassistant.components.climate.const import (
     SUPPORT_FAN_MODE,
     SUPPORT_TARGET_TEMPERATURE_RANGE,
 )
-from homeassistant.const import PRECISION_WHOLE, STATE_ON
+from homeassistant.const import PRECISION_WHOLE, STATE_ON, TEMP_CELSIUS, TEMP_FAHRENHEIT
 
-from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities
+from . import ElkEntity, create_elk_entities
+from .const import DOMAIN
 
 SUPPORT_HVAC = [
     HVAC_MODE_OFF,
@@ -27,18 +28,14 @@ SUPPORT_HVAC = [
 ]
 
 
-async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
+async def async_setup_entry(hass, config_entry, async_add_entities):
     """Create the Elk-M1 thermostat platform."""
-    if discovery_info is None:
-        return
-
-    elk_datas = hass.data[ELK_DOMAIN]
+    elk_data = hass.data[DOMAIN][config_entry.entry_id]
     entities = []
-    for elk_data in elk_datas.values():
-        elk = elk_data["elk"]
-        entities = create_elk_entities(
-            elk_data, elk.thermostats, "thermostat", ElkThermostat, entities
-        )
+    elk = elk_data["elk"]
+    create_elk_entities(
+        elk_data, elk.thermostats, "thermostat", ElkThermostat, entities
+    )
     async_add_entities(entities, True)
 
 
@@ -58,7 +55,7 @@ class ElkThermostat(ElkEntity, ClimateDevice):
     @property
     def temperature_unit(self):
         """Return the temperature unit."""
-        return self._temperature_unit
+        return TEMP_FAHRENHEIT if self._temperature_unit == "F" else TEMP_CELSIUS
 
     @property
     def current_temperature(self):
diff --git a/homeassistant/components/elkm1/config_flow.py b/homeassistant/components/elkm1/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..cad3ecac42a72a663d1d996af246fb88e91c913a
--- /dev/null
+++ b/homeassistant/components/elkm1/config_flow.py
@@ -0,0 +1,164 @@
+"""Config flow for Elk-M1 Control integration."""
+import logging
+from urllib.parse import urlparse
+
+import elkm1_lib as elkm1
+import voluptuous as vol
+
+from homeassistant import config_entries, exceptions
+from homeassistant.const import (
+    CONF_ADDRESS,
+    CONF_HOST,
+    CONF_PASSWORD,
+    CONF_PROTOCOL,
+    CONF_TEMPERATURE_UNIT,
+    CONF_USERNAME,
+)
+from homeassistant.util import slugify
+
+from . import async_wait_for_elk_to_sync
+from .const import CONF_AUTO_CONFIGURE, CONF_PREFIX
+from .const import DOMAIN  # pylint:disable=unused-import
+
+_LOGGER = logging.getLogger(__name__)
+
+PROTOCOL_MAP = {"secure": "elks://", "non-secure": "elk://", "serial": "serial://"}
+
+DATA_SCHEMA = vol.Schema(
+    {
+        vol.Required(CONF_PROTOCOL, default="secure"): vol.In(
+            ["secure", "non-secure", "serial"]
+        ),
+        vol.Required(CONF_ADDRESS): str,
+        vol.Optional(CONF_USERNAME, default=""): str,
+        vol.Optional(CONF_PASSWORD, default=""): str,
+        vol.Optional(CONF_PREFIX, default=""): str,
+        vol.Optional(CONF_TEMPERATURE_UNIT, default="F"): vol.In(["F", "C"]),
+    }
+)
+
+VALIDATE_TIMEOUT = 35
+
+
+async def validate_input(data):
+    """Validate the user input allows us to connect.
+
+    Data has the keys from DATA_SCHEMA with values provided by the user.
+    """
+
+    userid = data.get(CONF_USERNAME)
+    password = data.get(CONF_PASSWORD)
+
+    prefix = data[CONF_PREFIX]
+    url = _make_url_from_data(data)
+    requires_password = url.startswith("elks://")
+
+    if requires_password and (not userid or not password):
+        raise InvalidAuth
+
+    elk = elkm1.Elk(
+        {"url": url, "userid": userid, "password": password, "element_list": ["panel"]}
+    )
+    elk.connect()
+
+    timed_out = False
+    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",
+            VALIDATE_TIMEOUT,
+        )
+        timed_out = True
+
+    elk.disconnect()
+
+    if timed_out:
+        raise CannotConnect
+    if elk.invalid_auth:
+        raise InvalidAuth
+
+    device_name = data[CONF_PREFIX] if data[CONF_PREFIX] else "ElkM1"
+    # Return info that you want to store in the config entry.
+    return {"title": device_name, CONF_HOST: url, CONF_PREFIX: slugify(prefix)}
+
+
+def _make_url_from_data(data):
+    host = data.get(CONF_HOST)
+    if host:
+        return host
+
+    protocol = PROTOCOL_MAP[data[CONF_PROTOCOL]]
+    address = data[CONF_ADDRESS]
+    return f"{protocol}{address}"
+
+
+class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
+    """Handle a config flow for Elk-M1 Control."""
+
+    VERSION = 1
+    CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
+
+    def __init__(self):
+        """Initialize the elkm1 config flow."""
+        self.importing = False
+
+    async def async_step_user(self, user_input=None):
+        """Handle the initial step."""
+        errors = {}
+        if user_input is not None:
+            if self._url_already_configured(_make_url_from_data(user_input)):
+                return self.async_abort(reason="address_already_configured")
+
+            try:
+                info = await validate_input(user_input)
+
+            except CannotConnect:
+                errors["base"] = "cannot_connect"
+            except InvalidAuth:
+                errors["base"] = "invalid_auth"
+            except Exception:  # pylint: disable=broad-except
+                _LOGGER.exception("Unexpected exception")
+                errors["base"] = "unknown"
+
+            if "base" not in errors:
+                await self.async_set_unique_id(user_input[CONF_PREFIX])
+                self._abort_if_unique_id_configured()
+
+                if self.importing:
+                    return self.async_create_entry(title=info["title"], data=user_input)
+
+                return self.async_create_entry(
+                    title=info["title"],
+                    data={
+                        CONF_HOST: info[CONF_HOST],
+                        CONF_USERNAME: user_input[CONF_USERNAME],
+                        CONF_PASSWORD: user_input[CONF_PASSWORD],
+                        CONF_AUTO_CONFIGURE: True,
+                        CONF_TEMPERATURE_UNIT: user_input[CONF_TEMPERATURE_UNIT],
+                        CONF_PREFIX: info[CONF_PREFIX],
+                    },
+                )
+
+        return self.async_show_form(
+            step_id="user", data_schema=DATA_SCHEMA, errors=errors
+        )
+
+    async def async_step_import(self, user_input):
+        """Handle import."""
+        self.importing = True
+        return await self.async_step_user(user_input)
+
+    def _url_already_configured(self, url):
+        """See if we already have a elkm1 matching user input configured."""
+        existing_hosts = {
+            urlparse(entry.data[CONF_HOST]).hostname
+            for entry in self._async_current_entries()
+        }
+        return urlparse(url).hostname in existing_hosts
+
+
+class CannotConnect(exceptions.HomeAssistantError):
+    """Error to indicate we cannot connect."""
+
+
+class InvalidAuth(exceptions.HomeAssistantError):
+    """Error to indicate there is invalid auth."""
diff --git a/homeassistant/components/elkm1/const.py b/homeassistant/components/elkm1/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..bad6d7fbcf1a646286f8ebc4d87cb2e8cb312106
--- /dev/null
+++ b/homeassistant/components/elkm1/const.py
@@ -0,0 +1,31 @@
+"""Support the ElkM1 Gold and ElkM1 EZ8 alarm/integration panels."""
+
+from elkm1_lib.const import Max
+
+DOMAIN = "elkm1"
+
+CONF_AUTO_CONFIGURE = "auto_configure"
+CONF_AREA = "area"
+CONF_COUNTER = "counter"
+CONF_ENABLED = "enabled"
+CONF_KEYPAD = "keypad"
+CONF_OUTPUT = "output"
+CONF_PLC = "plc"
+CONF_SETTING = "setting"
+CONF_TASK = "task"
+CONF_THERMOSTAT = "thermostat"
+CONF_ZONE = "zone"
+CONF_PREFIX = "prefix"
+
+
+ELK_ELEMENTS = {
+    CONF_AREA: Max.AREAS.value,
+    CONF_COUNTER: Max.COUNTERS.value,
+    CONF_KEYPAD: Max.KEYPADS.value,
+    CONF_OUTPUT: Max.OUTPUTS.value,
+    CONF_PLC: Max.LIGHTS.value,
+    CONF_SETTING: Max.SETTINGS.value,
+    CONF_TASK: Max.TASKS.value,
+    CONF_THERMOSTAT: Max.THERMOSTATS.value,
+    CONF_ZONE: Max.ZONES.value,
+}
diff --git a/homeassistant/components/elkm1/light.py b/homeassistant/components/elkm1/light.py
index 10a9ae1b931e10091b58add7032b7a9f789208ec..b7cfe20dfd8eab73c10a076ee17cdb4ab7fce5ae 100644
--- a/homeassistant/components/elkm1/light.py
+++ b/homeassistant/components/elkm1/light.py
@@ -1,18 +1,17 @@
 """Support for control of ElkM1 lighting (X10, UPB, etc)."""
+
 from homeassistant.components.light import ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light
 
-from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities
+from . import ElkEntity, create_elk_entities
+from .const import DOMAIN
 
 
-async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
+async def async_setup_entry(hass, config_entry, async_add_entities):
     """Set up the Elk light platform."""
-    if discovery_info is None:
-        return
-    elk_datas = hass.data[ELK_DOMAIN]
+    elk_data = hass.data[DOMAIN][config_entry.entry_id]
     entities = []
-    for elk_data in elk_datas.values():
-        elk = elk_data["elk"]
-        create_elk_entities(elk_data, elk.lights, "plc", ElkLight, entities)
+    elk = elk_data["elk"]
+    create_elk_entities(elk_data, elk.lights, "plc", ElkLight, entities)
     async_add_entities(entities, True)
 
 
diff --git a/homeassistant/components/elkm1/manifest.json b/homeassistant/components/elkm1/manifest.json
index c75da1ef039174d6bb57ab22f8392e5f3b9502fc..17b016fcb8b1157847de8e656f06a8360926cffe 100644
--- a/homeassistant/components/elkm1/manifest.json
+++ b/homeassistant/components/elkm1/manifest.json
@@ -2,7 +2,12 @@
   "domain": "elkm1",
   "name": "Elk-M1 Control",
   "documentation": "https://www.home-assistant.io/integrations/elkm1",
-  "requirements": ["elkm1-lib==0.7.15"],
+  "requirements": [
+    "elkm1-lib==0.7.17"
+  ],
   "dependencies": [],
-  "codeowners": []
+  "codeowners": [
+    "@bdraco"
+  ],
+  "config_flow": true
 }
diff --git a/homeassistant/components/elkm1/scene.py b/homeassistant/components/elkm1/scene.py
index dc5ea39d15478a9137a2f7fa840b042aa86b457a..1f894cc76814f12c6cbd88d6d916daf1e2240b85 100644
--- a/homeassistant/components/elkm1/scene.py
+++ b/homeassistant/components/elkm1/scene.py
@@ -1,22 +1,20 @@
 """Support for control of ElkM1 tasks ("macros")."""
 from homeassistant.components.scene import Scene
 
-from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities
+from . import ElkAttachedEntity, create_elk_entities
+from .const import DOMAIN
 
 
-async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
+async def async_setup_entry(hass, config_entry, async_add_entities):
     """Create the Elk-M1 scene platform."""
-    if discovery_info is None:
-        return
-    elk_datas = hass.data[ELK_DOMAIN]
+    elk_data = hass.data[DOMAIN][config_entry.entry_id]
     entities = []
-    for elk_data in elk_datas.values():
-        elk = elk_data["elk"]
-        entities = create_elk_entities(elk_data, elk.tasks, "task", ElkTask, entities)
+    elk = elk_data["elk"]
+    create_elk_entities(elk_data, elk.tasks, "task", ElkTask, entities)
     async_add_entities(entities, True)
 
 
-class ElkTask(ElkEntity, Scene):
+class ElkTask(ElkAttachedEntity, Scene):
     """Elk-M1 task as scene."""
 
     async def async_activate(self):
diff --git a/homeassistant/components/elkm1/sensor.py b/homeassistant/components/elkm1/sensor.py
index df29e1cda7ee375e43b1f02c5aa699201a91de30..79987d806a1fef7390ff10bd2459799d34f9ebb6 100644
--- a/homeassistant/components/elkm1/sensor.py
+++ b/homeassistant/components/elkm1/sensor.py
@@ -7,31 +7,22 @@ from elkm1_lib.const import (
 )
 from elkm1_lib.util import pretty_const, username
 
-from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities
+from . import ElkAttachedEntity, create_elk_entities
+from .const import DOMAIN
 
+UNDEFINED_TEMPATURE = -40
 
-async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
-    """Create the Elk-M1 sensor platform."""
-    if discovery_info is None:
-        return
 
-    elk_datas = hass.data[ELK_DOMAIN]
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Create the Elk-M1 sensor platform."""
+    elk_data = hass.data[DOMAIN][config_entry.entry_id]
     entities = []
-    for elk_data in elk_datas.values():
-        elk = elk_data["elk"]
-        entities = create_elk_entities(
-            elk_data, elk.counters, "counter", ElkCounter, entities
-        )
-        entities = create_elk_entities(
-            elk_data, elk.keypads, "keypad", ElkKeypad, entities
-        )
-        entities = create_elk_entities(
-            elk_data, [elk.panel], "panel", ElkPanel, entities
-        )
-        entities = create_elk_entities(
-            elk_data, elk.settings, "setting", ElkSetting, entities
-        )
-        entities = create_elk_entities(elk_data, elk.zones, "zone", ElkZone, entities)
+    elk = elk_data["elk"]
+    create_elk_entities(elk_data, elk.counters, "counter", ElkCounter, entities)
+    create_elk_entities(elk_data, elk.keypads, "keypad", ElkKeypad, entities)
+    create_elk_entities(elk_data, [elk.panel], "panel", ElkPanel, entities)
+    create_elk_entities(elk_data, elk.settings, "setting", ElkSetting, entities)
+    create_elk_entities(elk_data, elk.zones, "zone", ElkZone, entities)
     async_add_entities(entities, True)
 
 
@@ -40,7 +31,7 @@ def temperature_to_state(temperature, undefined_temperature):
     return temperature if temperature > undefined_temperature else None
 
 
-class ElkSensor(ElkEntity):
+class ElkSensor(ElkAttachedEntity):
     """Base representation of Elk-M1 sensor."""
 
     def __init__(self, element, elk, elk_data):
@@ -89,7 +80,7 @@ class ElkKeypad(ElkSensor):
         """Attributes of the sensor."""
         attrs = self.initial_attrs()
         attrs["area"] = self._element.area + 1
-        attrs["temperature"] = self._element.temperature
+        attrs["temperature"] = self._state
         attrs["last_user_time"] = self._element.last_user_time.isoformat()
         attrs["last_user"] = self._element.last_user + 1
         attrs["code"] = self._element.code
@@ -98,14 +89,9 @@ class ElkKeypad(ElkSensor):
         return attrs
 
     def _element_changed(self, element, changeset):
-        self._state = temperature_to_state(self._element.temperature, -40)
-
-    async def async_added_to_hass(self):
-        """Register callback for ElkM1 changes and update entity state."""
-        await super().async_added_to_hass()
-        elk_datas = self.hass.data[ELK_DOMAIN]
-        for elk_data in elk_datas.values():
-            elk_data["keypads"][self._element.index] = self.entity_id
+        self._state = temperature_to_state(
+            self._element.temperature, UNDEFINED_TEMPATURE
+        )
 
 
 class ElkPanel(ElkSensor):
@@ -214,7 +200,9 @@ class ElkZone(ElkSensor):
 
     def _element_changed(self, element, changeset):
         if self._element.definition == ZoneType.TEMPERATURE.value:
-            self._state = temperature_to_state(self._element.temperature, -60)
+            self._state = temperature_to_state(
+                self._element.temperature, UNDEFINED_TEMPATURE
+            )
         elif self._element.definition == ZoneType.ANALOG_ZONE.value:
             self._state = self._element.voltage
         else:
diff --git a/homeassistant/components/elkm1/strings.json b/homeassistant/components/elkm1/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..a5246a004c3ee2684cbecfd3b24c66c726a32a51
--- /dev/null
+++ b/homeassistant/components/elkm1/strings.json
@@ -0,0 +1,28 @@
+{
+  "config": {
+    "title": "Elk-M1 Control",
+    "step": {
+      "user": {
+        "title": "Connect to Elk-M1 Control",
+        "description": "The address string must be in the form 'address[:port]' for 'secure' and 'non-secure'. Example: '192.168.1.1'. The port is optional and defaults to 2101 for 'non-secure' and 2601 for 'secure'. For the serial protocol, the address must be in the form 'tty[:baud]'. Example: '/dev/ttyS1'. The baud is optional and defaults to 115200.",
+        "data": {
+          "protocol": "Protocol",
+          "address": "The IP address or domain or serial port if connecting via serial.",
+          "username": "Username (secure only).",
+          "password": "Password (secure only).",
+          "prefix": "A unique prefix (leave blank if you only have one ElkM1).",
+          "temperature_unit": "The temperature unit ElkM1 uses."
+        }
+      }
+    },
+    "error": {
+      "cannot_connect": "Failed to connect, please try again",
+      "invalid_auth": "Invalid authentication",
+      "unknown": "Unexpected error"
+    },
+    "abort": {
+      "already_configured": "An ElkM1 with this prefix is already configured",
+      "address_already_configured": "An ElkM1 with this address is already configured"
+    }
+  }
+}
\ No newline at end of file
diff --git a/homeassistant/components/elkm1/switch.py b/homeassistant/components/elkm1/switch.py
index e6dd82dc0ac13651de8a46bf28180ff1640b0258..af32e81bc4c4b85b11193640236998730181eb78 100644
--- a/homeassistant/components/elkm1/switch.py
+++ b/homeassistant/components/elkm1/switch.py
@@ -1,24 +1,20 @@
 """Support for control of ElkM1 outputs (relays)."""
 from homeassistant.components.switch import SwitchDevice
 
-from . import DOMAIN as ELK_DOMAIN, ElkEntity, create_elk_entities
+from . import ElkAttachedEntity, create_elk_entities
+from .const import DOMAIN
 
 
-async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
+async def async_setup_entry(hass, config_entry, async_add_entities):
     """Create the Elk-M1 switch platform."""
-    if discovery_info is None:
-        return
-    elk_datas = hass.data[ELK_DOMAIN]
+    elk_data = hass.data[DOMAIN][config_entry.entry_id]
     entities = []
-    for elk_data in elk_datas.values():
-        elk = elk_data["elk"]
-        entities = create_elk_entities(
-            elk_data, elk.outputs, "output", ElkOutput, entities
-        )
+    elk = elk_data["elk"]
+    create_elk_entities(elk_data, elk.outputs, "output", ElkOutput, entities)
     async_add_entities(entities, True)
 
 
-class ElkOutput(ElkEntity, SwitchDevice):
+class ElkOutput(ElkAttachedEntity, SwitchDevice):
     """Elk output as switch."""
 
     @property
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index 8c03702e8f95a23530765c8ebfef521461ae19dd..05bc4a7ba4a306232693e547f80a4a055bb8e3dd 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -28,6 +28,7 @@ FLOWS = [
     "dynalite",
     "ecobee",
     "elgato",
+    "elkm1",
     "emulated_roku",
     "esphome",
     "freebox",
diff --git a/requirements_all.txt b/requirements_all.txt
index f0ba017f5740ae59572d0c4c3d6911ed07360625..8d377e3602d746ecfdbb8ea96a1aacbff81cd7a7 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -495,7 +495,7 @@ elgato==0.2.0
 eliqonline==1.2.2
 
 # homeassistant.components.elkm1
-elkm1-lib==0.7.15
+elkm1-lib==0.7.17
 
 # homeassistant.components.emulated_roku
 emulated_roku==0.2.1
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index f94245b6df502ac2eee9f2e53f42f838f6be55f8..7bded1c1d217ecbd728996e89c534ed821c9d211 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -198,6 +198,9 @@ eebrightbox==0.0.4
 # homeassistant.components.elgato
 elgato==0.2.0
 
+# homeassistant.components.elkm1
+elkm1-lib==0.7.17
+
 # homeassistant.components.emulated_roku
 emulated_roku==0.2.1
 
diff --git a/tests/components/elkm1/__init__.py b/tests/components/elkm1/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ae7f6d7b49eb14349d391615fd8c5dea1f0a33e
--- /dev/null
+++ b/tests/components/elkm1/__init__.py
@@ -0,0 +1 @@
+"""Tests for the Elk-M1 Control integration."""
diff --git a/tests/components/elkm1/test_config_flow.py b/tests/components/elkm1/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..466005f3d43c4f8d1e47e319382a443157575e84
--- /dev/null
+++ b/tests/components/elkm1/test_config_flow.py
@@ -0,0 +1,269 @@
+"""Test the Elk-M1 Control config flow."""
+
+from asynctest import CoroutineMock, MagicMock, PropertyMock, patch
+
+from homeassistant import config_entries, setup
+from homeassistant.components.elkm1.const import DOMAIN
+
+
+def mock_elk(invalid_auth=None, sync_complete=None):
+    """Mock m1lib Elk."""
+    mocked_elk = MagicMock()
+    type(mocked_elk).invalid_auth = PropertyMock(return_value=invalid_auth)
+    type(mocked_elk).sync_complete = CoroutineMock()
+    return mocked_elk
+
+
+async def test_form_user_with_secure_elk(hass):
+    """Test we can setup a secure elk."""
+    await setup.async_setup_component(hass, "persistent_notification", {})
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    assert result["type"] == "form"
+    assert result["errors"] == {}
+
+    mocked_elk = mock_elk(invalid_auth=False)
+
+    with patch(
+        "homeassistant.components.elkm1.config_flow.elkm1.Elk", return_value=mocked_elk,
+    ), patch(
+        "homeassistant.components.elkm1.async_setup", return_value=True
+    ) as mock_setup, patch(
+        "homeassistant.components.elkm1.async_setup_entry", return_value=True,
+    ) as mock_setup_entry:
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                "protocol": "secure",
+                "address": "1.2.3.4",
+                "username": "test-username",
+                "password": "test-password",
+                "temperature_unit": "F",
+                "prefix": "",
+            },
+        )
+
+    assert result2["type"] == "create_entry"
+    assert result2["title"] == "ElkM1"
+    assert result2["data"] == {
+        "auto_configure": True,
+        "host": "elks://1.2.3.4",
+        "password": "test-password",
+        "prefix": "",
+        "temperature_unit": "F",
+        "username": "test-username",
+    }
+    await hass.async_block_till_done()
+    assert len(mock_setup.mock_calls) == 1
+    assert len(mock_setup_entry.mock_calls) == 1
+
+
+async def test_form_user_with_non_secure_elk(hass):
+    """Test we can setup a non-secure elk."""
+    await setup.async_setup_component(hass, "persistent_notification", {})
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    assert result["type"] == "form"
+    assert result["errors"] == {}
+
+    mocked_elk = mock_elk(invalid_auth=False)
+
+    with patch(
+        "homeassistant.components.elkm1.config_flow.elkm1.Elk", return_value=mocked_elk,
+    ), patch(
+        "homeassistant.components.elkm1.async_setup", return_value=True
+    ) as mock_setup, patch(
+        "homeassistant.components.elkm1.async_setup_entry", return_value=True,
+    ) as mock_setup_entry:
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                "protocol": "non-secure",
+                "address": "1.2.3.4",
+                "temperature_unit": "F",
+                "prefix": "guest_house",
+            },
+        )
+
+    assert result2["type"] == "create_entry"
+    assert result2["title"] == "guest_house"
+    assert result2["data"] == {
+        "auto_configure": True,
+        "host": "elk://1.2.3.4",
+        "prefix": "guest_house",
+        "username": "",
+        "password": "",
+        "temperature_unit": "F",
+    }
+    await hass.async_block_till_done()
+    assert len(mock_setup.mock_calls) == 1
+    assert len(mock_setup_entry.mock_calls) == 1
+
+
+async def test_form_user_with_serial_elk(hass):
+    """Test we can setup a serial elk."""
+    await setup.async_setup_component(hass, "persistent_notification", {})
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    assert result["type"] == "form"
+    assert result["errors"] == {}
+
+    mocked_elk = mock_elk(invalid_auth=False)
+
+    with patch(
+        "homeassistant.components.elkm1.config_flow.elkm1.Elk", return_value=mocked_elk,
+    ), patch(
+        "homeassistant.components.elkm1.async_setup", return_value=True
+    ) as mock_setup, patch(
+        "homeassistant.components.elkm1.async_setup_entry", return_value=True,
+    ) as mock_setup_entry:
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                "protocol": "serial",
+                "address": "/dev/ttyS0:115200",
+                "temperature_unit": "F",
+                "prefix": "",
+            },
+        )
+
+    assert result2["type"] == "create_entry"
+    assert result2["title"] == "ElkM1"
+    assert result2["data"] == {
+        "auto_configure": True,
+        "host": "serial:///dev/ttyS0:115200",
+        "prefix": "",
+        "username": "",
+        "password": "",
+        "temperature_unit": "F",
+    }
+    await hass.async_block_till_done()
+    assert len(mock_setup.mock_calls) == 1
+    assert len(mock_setup_entry.mock_calls) == 1
+
+
+async def test_form_cannot_connect(hass):
+    """Test we handle cannot connect error."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    mocked_elk = mock_elk(invalid_auth=False)
+
+    with patch(
+        "homeassistant.components.elkm1.config_flow.elkm1.Elk", return_value=mocked_elk,
+    ), patch(
+        "homeassistant.components.elkm1.config_flow.async_wait_for_elk_to_sync",
+        return_value=False,
+    ):  # async_wait_for_elk_to_sync is being patched to avoid making the test wait 45s
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                "protocol": "secure",
+                "address": "1.2.3.4",
+                "username": "test-username",
+                "password": "test-password",
+                "temperature_unit": "F",
+                "prefix": "",
+            },
+        )
+
+    assert result2["type"] == "form"
+    assert result2["errors"] == {"base": "cannot_connect"}
+
+
+async def test_form_invalid_auth(hass):
+    """Test we handle cannot connect error."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    mocked_elk = mock_elk(invalid_auth=True)
+
+    with patch(
+        "homeassistant.components.elkm1.config_flow.elkm1.Elk", return_value=mocked_elk,
+    ):
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                "protocol": "secure",
+                "address": "1.2.3.4",
+                "username": "test-username",
+                "password": "test-password",
+                "temperature_unit": "F",
+                "prefix": "",
+            },
+        )
+
+    assert result2["type"] == "form"
+    assert result2["errors"] == {"base": "invalid_auth"}
+
+
+async def test_form_import(hass):
+    """Test we get the form with import source."""
+    await setup.async_setup_component(hass, "persistent_notification", {})
+
+    mocked_elk = mock_elk(invalid_auth=False)
+    with patch(
+        "homeassistant.components.elkm1.config_flow.elkm1.Elk", return_value=mocked_elk,
+    ), patch(
+        "homeassistant.components.elkm1.async_setup", return_value=True
+    ) as mock_setup, patch(
+        "homeassistant.components.elkm1.async_setup_entry", return_value=True,
+    ) as mock_setup_entry:
+        result = await hass.config_entries.flow.async_init(
+            DOMAIN,
+            context={"source": config_entries.SOURCE_IMPORT},
+            data={
+                "host": "elks://1.2.3.4",
+                "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": [],
+                },
+            },
+        )
+
+    assert result["type"] == "create_entry"
+    assert result["title"] == "ohana"
+
+    assert result["data"] == {
+        "auto_configure": False,
+        "host": "elks://1.2.3.4",
+        "keypad": {"enabled": True, "exclude": [], "include": [[1, 1], [2, 2], [3, 3]]},
+        "output": {"enabled": False, "exclude": [], "include": []},
+        "password": "love",
+        "plc": {"enabled": False, "exclude": [], "include": []},
+        "prefix": "ohana",
+        "setting": {"enabled": False, "exclude": [], "include": []},
+        "area": {"enabled": False, "exclude": [], "include": []},
+        "counter": {"enabled": False, "exclude": [], "include": []},
+        "task": {"enabled": False, "exclude": [], "include": []},
+        "temperature_unit": "C",
+        "thermostat": {"enabled": False, "exclude": [], "include": []},
+        "username": "friend",
+        "zone": {"enabled": True, "exclude": [[15, 15], [28, 208]], "include": []},
+    }
+    await hass.async_block_till_done()
+    assert len(mock_setup.mock_calls) == 1
+    assert len(mock_setup_entry.mock_calls) == 1