diff --git a/.coveragerc b/.coveragerc
index 1d861d69c1dfe1083b4831636c35c583f7422276..02d59b55f5f2a1f35ebf0584557baf80cd32ed40 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -93,6 +93,7 @@ omit =
     homeassistant/components/canary/camera.py
     homeassistant/components/cast/*
     homeassistant/components/cert_expiry/sensor.py
+    homeassistant/components/cert_expiry/helper.py
     homeassistant/components/channels/media_player.py
     homeassistant/components/cisco_ios/device_tracker.py
     homeassistant/components/cisco_mobility_express/device_tracker.py
diff --git a/CODEOWNERS b/CODEOWNERS
index 3ede39518c18742181e1ced6132b4599ffde0c7f..d51031486ef7cc866adcb0e848315616fd9371a2 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -46,6 +46,7 @@ homeassistant/components/broadlink/* @danielhiversen
 homeassistant/components/brunt/* @eavanvalkenburg
 homeassistant/components/bt_smarthub/* @jxwolstenholme
 homeassistant/components/buienradar/* @mjj4791 @ties
+homeassistant/components/cert_expiry/* @cereal2nd
 homeassistant/components/cisco_ios/* @fbradyirl
 homeassistant/components/cisco_mobility_express/* @fbradyirl
 homeassistant/components/cisco_webex_teams/* @fbradyirl
diff --git a/homeassistant/components/cert_expiry/.translations/en.json b/homeassistant/components/cert_expiry/.translations/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..b6aa1cefb02aedb827ed169a74a55bd9a9cc6b36
--- /dev/null
+++ b/homeassistant/components/cert_expiry/.translations/en.json
@@ -0,0 +1,24 @@
+{
+    "config": {
+        "abort": {
+            "host_port_exists": "This host and port combination is already configured"
+        },
+        "error": {
+            "certificate_fetch_failed": "Can not fetch certificate from this host and port combination",
+            "connection_timeout": "Timeout whemn connecting to this host",
+            "host_port_exists": "This host and port combination is already configured",
+            "resolve_failed": "This host can not be resolved"
+        },
+        "step": {
+            "user": {
+                "data": {
+                    "host": "The hostname of the certificate",
+                    "name": "The name of the certificate",
+                    "port": "The port of the certificate"
+                },
+                "title": "Define the certificate to test"
+            }
+        },
+        "title": "Certificate Expiry"
+    }
+}
\ No newline at end of file
diff --git a/homeassistant/components/cert_expiry/__init__.py b/homeassistant/components/cert_expiry/__init__.py
index 78ceb60dd404b72d8d42a25be4d5a34ccad95519..ab68d5ba08bc4387336f6a07ba5c91d6b96afd4c 100644
--- a/homeassistant/components/cert_expiry/__init__.py
+++ b/homeassistant/components/cert_expiry/__init__.py
@@ -1 +1,25 @@
 """The cert_expiry component."""
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import EVENT_HOMEASSISTANT_START
+from homeassistant.core import callback
+from homeassistant.helpers.typing import HomeAssistantType
+
+
+async def async_setup(hass, config):
+    """Platform setup, do nothing."""
+    return True
+
+
+async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
+    """Load the saved entities."""
+
+    @callback
+    def async_start(_):
+        """Load the entry after the start event."""
+        hass.async_create_task(
+            hass.config_entries.async_forward_entry_setup(entry, "sensor")
+        )
+
+    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, async_start)
+
+    return True
diff --git a/homeassistant/components/cert_expiry/config_flow.py b/homeassistant/components/cert_expiry/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd3463fff95c4ee6830c2f07f512e13015680e76
--- /dev/null
+++ b/homeassistant/components/cert_expiry/config_flow.py
@@ -0,0 +1,98 @@
+"""Config flow for the Cert Expiry platform."""
+import socket
+import voluptuous as vol
+
+from homeassistant import config_entries
+from homeassistant.const import CONF_PORT, CONF_NAME, CONF_HOST
+from homeassistant.core import HomeAssistant, callback
+from homeassistant.util import slugify
+
+from .const import DOMAIN, DEFAULT_PORT, DEFAULT_NAME
+from .helper import get_cert
+
+
+@callback
+def certexpiry_entries(hass: HomeAssistant):
+    """Return the host,port tuples for the domain."""
+    return set(
+        (entry.data[CONF_HOST], entry.data[CONF_PORT])
+        for entry in hass.config_entries.async_entries(DOMAIN)
+    )
+
+
+class CertexpiryConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
+    """Handle a config flow."""
+
+    VERSION = 1
+    CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
+
+    def __init__(self) -> None:
+        """Initialize the config flow."""
+        self._errors = {}
+
+    def _prt_in_configuration_exists(self, user_input) -> bool:
+        """Return True if host, port combination exists in configuration."""
+        host = user_input[CONF_HOST]
+        port = user_input.get(CONF_PORT, DEFAULT_PORT)
+        if (host, port) in certexpiry_entries(self.hass):
+            return True
+        return False
+
+    def _test_connection(self, user_input=None):
+        """Test connection to the server and try to get the certtificate."""
+        try:
+            get_cert(user_input[CONF_HOST], user_input.get(CONF_PORT, DEFAULT_PORT))
+            return True
+        except socket.gaierror:
+            self._errors[CONF_HOST] = "resolve_failed"
+        except socket.timeout:
+            self._errors[CONF_HOST] = "connection_timeout"
+        except OSError:
+            self._errors[CONF_HOST] = "certificate_fetch_failed"
+        return False
+
+    async def async_step_user(self, user_input=None):
+        """Step when user intializes a integration."""
+        self._errors = {}
+        if user_input is not None:
+            # set some defaults in case we need to return to the form
+            if self._prt_in_configuration_exists(user_input):
+                self._errors[CONF_HOST] = "host_port_exists"
+            else:
+                if self._test_connection(user_input):
+                    host = user_input[CONF_HOST]
+                    name = slugify(user_input.get(CONF_NAME, DEFAULT_NAME))
+                    prt = user_input.get(CONF_PORT, DEFAULT_PORT)
+                    return self.async_create_entry(
+                        title=name, data={CONF_HOST: host, CONF_PORT: prt}
+                    )
+        else:
+            user_input = {}
+            user_input[CONF_NAME] = DEFAULT_NAME
+            user_input[CONF_HOST] = ""
+            user_input[CONF_PORT] = DEFAULT_PORT
+
+        return self.async_show_form(
+            step_id="user",
+            data_schema=vol.Schema(
+                {
+                    vol.Required(
+                        CONF_NAME, default=user_input.get(CONF_NAME, DEFAULT_NAME)
+                    ): str,
+                    vol.Required(CONF_HOST, default=user_input[CONF_HOST]): str,
+                    vol.Required(
+                        CONF_PORT, default=user_input.get(CONF_PORT, DEFAULT_PORT)
+                    ): int,
+                }
+            ),
+            errors=self._errors,
+        )
+
+    async def async_step_import(self, user_input=None):
+        """Import a config entry.
+
+        Only host was required in the yaml file all other fields are optional
+        """
+        if self._prt_in_configuration_exists(user_input):
+            return self.async_abort(reason="host_port_exists")
+        return await self.async_step_user(user_input)
diff --git a/homeassistant/components/cert_expiry/const.py b/homeassistant/components/cert_expiry/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..4129781f2a0fb2718e8f8599d8cf49995249c2b5
--- /dev/null
+++ b/homeassistant/components/cert_expiry/const.py
@@ -0,0 +1,6 @@
+"""Const for Cert Expiry."""
+
+DOMAIN = "cert_expiry"
+DEFAULT_NAME = "SSL Certificate Expiry"
+DEFAULT_PORT = 443
+TIMEOUT = 10.0
diff --git a/homeassistant/components/cert_expiry/helper.py b/homeassistant/components/cert_expiry/helper.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c10887293adbd5e8325b121c53a74aaf8b8ac4c
--- /dev/null
+++ b/homeassistant/components/cert_expiry/helper.py
@@ -0,0 +1,15 @@
+"""Helper functions for the Cert Expiry platform."""
+import socket
+import ssl
+
+from .const import TIMEOUT
+
+
+def get_cert(host, port):
+    """Get the ssl certificate for the host and port combination."""
+    ctx = ssl.create_default_context()
+    address = (host, port)
+    with socket.create_connection(address, timeout=TIMEOUT) as sock:
+        with ctx.wrap_socket(sock, server_hostname=address[0]) as ssock:
+            cert = ssock.getpeercert()
+            return cert
diff --git a/homeassistant/components/cert_expiry/manifest.json b/homeassistant/components/cert_expiry/manifest.json
index 7ef2e0b7d105d6827afd5d9619c5efc5d6992fe6..781f27afb5f115f74a8a775fdcbe3e2892c2c4d9 100644
--- a/homeassistant/components/cert_expiry/manifest.json
+++ b/homeassistant/components/cert_expiry/manifest.json
@@ -1,8 +1,9 @@
 {
-  "domain": "cert_expiry",
-  "name": "Cert expiry",
-  "documentation": "https://www.home-assistant.io/components/cert_expiry",
-  "requirements": [],
-  "dependencies": [],
-  "codeowners": []
+    "domain": "cert_expiry",
+    "name": "Cert expiry",
+    "documentation": "https://www.home-assistant.io/components/cert_expiry",
+    "requirements": [],
+    "config_flow": true,
+    "dependencies": [],
+    "codeowners": ["@cereal2nd"]
 }
diff --git a/homeassistant/components/cert_expiry/sensor.py b/homeassistant/components/cert_expiry/sensor.py
index b1e0d819358e0d9fb81c53c6f9b62c67b6138a7e..fccfb295c0fff2c6160d0c13e78b1efef35ab374 100644
--- a/homeassistant/components/cert_expiry/sensor.py
+++ b/homeassistant/components/cert_expiry/sensor.py
@@ -7,24 +7,18 @@ from datetime import datetime, timedelta
 import voluptuous as vol
 
 import homeassistant.helpers.config_validation as cv
+from homeassistant.config_entries import SOURCE_IMPORT
 from homeassistant.components.sensor import PLATFORM_SCHEMA
-from homeassistant.const import (
-    CONF_NAME,
-    CONF_HOST,
-    CONF_PORT,
-    EVENT_HOMEASSISTANT_START,
-)
+from homeassistant.const import CONF_NAME, CONF_HOST, CONF_PORT
 from homeassistant.helpers.entity import Entity
 
-_LOGGER = logging.getLogger(__name__)
+from .const import DOMAIN, DEFAULT_NAME, DEFAULT_PORT
+from .helper import get_cert
 
-DEFAULT_NAME = "SSL Certificate Expiry"
-DEFAULT_PORT = 443
+_LOGGER = logging.getLogger(__name__)
 
 SCAN_INTERVAL = timedelta(hours=12)
 
-TIMEOUT = 10.0
-
 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
     {
         vol.Required(CONF_HOST): cv.string,
@@ -34,22 +28,22 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
 )
 
 
-def setup_platform(hass, config, add_entities, discovery_info=None):
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
     """Set up certificate expiry sensor."""
+    hass.async_create_task(
+        hass.config_entries.flow.async_init(
+            DOMAIN, context={"source": SOURCE_IMPORT}, data=dict(config)
+        )
+    )
 
-    def run_setup(event):
-        """Wait until Home Assistant is fully initialized before creating.
 
-        Delay the setup until Home Assistant is fully initialized.
-        """
-        server_name = config.get(CONF_HOST)
-        server_port = config.get(CONF_PORT)
-        sensor_name = config.get(CONF_NAME)
-
-        add_entities([SSLCertificate(sensor_name, server_name, server_port)], True)
-
-    # To allow checking of the HA certificate we must first be running.
-    hass.bus.listen_once(EVENT_HOMEASSISTANT_START, run_setup)
+async def async_setup_entry(hass, entry, async_add_entities):
+    """Add cert-expiry entry."""
+    async_add_entities(
+        [SSLCertificate(entry.title, entry.data[CONF_HOST], entry.data[CONF_PORT])],
+        True,
+    )
+    return True
 
 
 class SSLCertificate(Entity):
@@ -90,13 +84,8 @@ class SSLCertificate(Entity):
 
     def update(self):
         """Fetch the certificate information."""
-        ctx = ssl.create_default_context()
         try:
-            address = (self.server_name, self.server_port)
-            with socket.create_connection(address, timeout=TIMEOUT) as sock:
-                with ctx.wrap_socket(sock, server_hostname=address[0]) as ssock:
-                    cert = ssock.getpeercert()
-
+            cert = get_cert(self.server_name, self.server_port)
         except socket.gaierror:
             _LOGGER.error("Cannot resolve hostname: %s", self.server_name)
             self._available = False
diff --git a/homeassistant/components/cert_expiry/strings.json b/homeassistant/components/cert_expiry/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..8943643e8b392e2f22519ea7ed80b4abcaf08d6d
--- /dev/null
+++ b/homeassistant/components/cert_expiry/strings.json
@@ -0,0 +1,24 @@
+{
+    "config": {
+        "title": "Certificate Expiry",
+        "step": {
+            "user": {
+                "title": "Define the certificate to test",
+                "data": {
+                    "name": "The name of the certificate",
+                    "host": "The hostname of the certificate",
+                    "port": "The port of the certificate"
+                }
+            }
+        },
+        "error": {
+            "host_port_exists": "This host and port combination is already configured",
+            "resolve_failed": "This host can not be resolved",
+            "connection_timeout": "Timeout whemn connecting to this host",
+            "certificate_fetch_failed": "Can not fetch certificate from this host and port combination"
+        },
+        "abort": {
+            "host_port_exists": "This host and port combination is already configured"
+        }
+    }
+}
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index de665ecf5a6e44d9b771d8b2493ff8c6373ca2c4..082e0f853f87ceec38b417bd4ef5b0ee75919d59 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -10,6 +10,7 @@ FLOWS = [
     "ambient_station",
     "axis",
     "cast",
+    "cert_expiry",
     "daikin",
     "deconz",
     "dialogflow",
diff --git a/tests/components/cert_expiry/__init__.py b/tests/components/cert_expiry/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ef5adee2e22911c8aa888e9dd67c746c2d8f5ce
--- /dev/null
+++ b/tests/components/cert_expiry/__init__.py
@@ -0,0 +1 @@
+"""Tests for the Cert Expiry component."""
diff --git a/tests/components/cert_expiry/test_config_flow.py b/tests/components/cert_expiry/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8c99496a563eb55e1e010c31501394282bd9fc1
--- /dev/null
+++ b/tests/components/cert_expiry/test_config_flow.py
@@ -0,0 +1,137 @@
+"""Tests for the Cert Expiry config flow."""
+import pytest
+import socket
+from unittest.mock import patch
+
+from homeassistant import data_entry_flow
+from homeassistant.components.cert_expiry import config_flow
+from homeassistant.components.cert_expiry.const import DEFAULT_PORT
+from homeassistant.const import CONF_PORT, CONF_NAME, CONF_HOST
+
+from tests.common import MockConfigEntry
+
+NAME = "Cert Expiry test 1 2 3"
+PORT = 443
+HOST = "example.com"
+
+
+@pytest.fixture(name="test_connect")
+def mock_controller():
+    """Mock a successfull _prt_in_configuration_exists."""
+    with patch(
+        "homeassistant.components.cert_expiry.config_flow.CertexpiryConfigFlow._test_connection",
+        return_value=True,
+    ):
+        yield
+
+
+def init_config_flow(hass):
+    """Init a configuration flow."""
+    flow = config_flow.CertexpiryConfigFlow()
+    flow.hass = hass
+    return flow
+
+
+async def test_user(hass, test_connect):
+    """Test user config."""
+    flow = init_config_flow(hass)
+
+    result = await flow.async_step_user()
+    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+    assert result["step_id"] == "user"
+
+    # tets with all provided
+    result = await flow.async_step_user(
+        {CONF_NAME: NAME, CONF_HOST: HOST, CONF_PORT: PORT}
+    )
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["title"] == "cert_expiry_test_1_2_3"
+    assert result["data"][CONF_HOST] == HOST
+    assert result["data"][CONF_PORT] == PORT
+
+
+async def test_import(hass, test_connect):
+    """Test import step."""
+    flow = init_config_flow(hass)
+
+    # import with only host
+    result = await flow.async_step_import({CONF_HOST: HOST})
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["title"] == "ssl_certificate_expiry"
+    assert result["data"][CONF_HOST] == HOST
+    assert result["data"][CONF_PORT] == DEFAULT_PORT
+
+    # import with host and name
+    result = await flow.async_step_import({CONF_HOST: HOST, CONF_NAME: NAME})
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["title"] == "cert_expiry_test_1_2_3"
+    assert result["data"][CONF_HOST] == HOST
+    assert result["data"][CONF_PORT] == DEFAULT_PORT
+
+    # improt with host and port
+    result = await flow.async_step_import({CONF_HOST: HOST, CONF_PORT: PORT})
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["title"] == "ssl_certificate_expiry"
+    assert result["data"][CONF_HOST] == HOST
+    assert result["data"][CONF_PORT] == PORT
+
+    # import with all
+    result = await flow.async_step_import(
+        {CONF_HOST: HOST, CONF_PORT: PORT, CONF_NAME: NAME}
+    )
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["title"] == "cert_expiry_test_1_2_3"
+    assert result["data"][CONF_HOST] == HOST
+    assert result["data"][CONF_PORT] == PORT
+
+
+async def test_abort_if_already_setup(hass, test_connect):
+    """Test we abort if the cert is already setup."""
+    flow = init_config_flow(hass)
+    MockConfigEntry(
+        domain="cert_expiry",
+        data={CONF_PORT: DEFAULT_PORT, CONF_NAME: NAME, CONF_HOST: HOST},
+    ).add_to_hass(hass)
+
+    # Should fail, same HOST and PORT (default)
+    result = await flow.async_step_import(
+        {CONF_HOST: HOST, CONF_NAME: NAME, CONF_PORT: DEFAULT_PORT}
+    )
+    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
+    assert result["reason"] == "host_port_exists"
+
+    # Should be the same HOST and PORT (default)
+    result = await flow.async_step_user(
+        {CONF_HOST: HOST, CONF_NAME: NAME, CONF_PORT: DEFAULT_PORT}
+    )
+    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+    assert result["errors"] == {CONF_HOST: "host_port_exists"}
+
+    # SHOULD pass, same Host diff PORT
+    result = await flow.async_step_import(
+        {CONF_HOST: HOST, CONF_NAME: NAME, CONF_PORT: 888}
+    )
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["title"] == "cert_expiry_test_1_2_3"
+    assert result["data"][CONF_HOST] == HOST
+    assert result["data"][CONF_PORT] == 888
+
+
+async def test_abort_on_socket_failed(hass):
+    """Test we abort of we have errors during socket creation."""
+    flow = init_config_flow(hass)
+
+    with patch("socket.create_connection", side_effect=socket.gaierror()):
+        result = await flow.async_step_user({CONF_HOST: HOST})
+        assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+        assert result["errors"] == {CONF_HOST: "resolve_failed"}
+
+    with patch("socket.create_connection", side_effect=socket.timeout()):
+        result = await flow.async_step_user({CONF_HOST: HOST})
+        assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+        assert result["errors"] == {CONF_HOST: "connection_timeout"}
+
+    with patch("socket.create_connection", side_effect=OSError()):
+        result = await flow.async_step_user({CONF_HOST: HOST})
+        assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+        assert result["errors"] == {CONF_HOST: "certificate_fetch_failed"}