diff --git a/.coveragerc b/.coveragerc
index 923642733d4b15eb6ceb2fd2405b36fde2c2236b..02cd35bcd603f577fc3a6954ffc8cc0fa5747914 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -374,6 +374,7 @@ omit =
     homeassistant/components/greenwave/light.py
     homeassistant/components/group/notify.py
     homeassistant/components/growatt_server/sensor.py
+    homeassistant/components/growatt_server/__init__.py
     homeassistant/components/gstreamer/media_player.py
     homeassistant/components/gtfs/sensor.py
     homeassistant/components/guardian/__init__.py
diff --git a/homeassistant/components/growatt_server/__init__.py b/homeassistant/components/growatt_server/__init__.py
index 14205e8d9ba3cee2501418c9d4aba76645843464..8fcc7c3f34d113b5012a6fe75530580ec1550743 100644
--- a/homeassistant/components/growatt_server/__init__.py
+++ b/homeassistant/components/growatt_server/__init__.py
@@ -1 +1,19 @@
 """The Growatt server PV inverter sensor integration."""
+from homeassistant import config_entries
+from homeassistant.core import HomeAssistant
+
+from .const import PLATFORMS
+
+
+async def async_setup_entry(
+    hass: HomeAssistant, entry: config_entries.ConfigEntry
+) -> bool:
+    """Load the saved entities."""
+
+    hass.config_entries.async_setup_platforms(entry, PLATFORMS)
+    return True
+
+
+async def async_unload_entry(hass, entry):
+    """Unload a config entry."""
+    return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
diff --git a/homeassistant/components/growatt_server/config_flow.py b/homeassistant/components/growatt_server/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..300c96746e797da80f87e612b0cd8bdfc5930233
--- /dev/null
+++ b/homeassistant/components/growatt_server/config_flow.py
@@ -0,0 +1,78 @@
+"""Config flow for growatt server integration."""
+import growattServer
+import voluptuous as vol
+
+from homeassistant import config_entries
+from homeassistant.const import CONF_NAME, CONF_PASSWORD, CONF_USERNAME
+from homeassistant.core import callback
+
+from .const import CONF_PLANT_ID, DOMAIN
+
+
+class GrowattServerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
+    """Config flow class."""
+
+    VERSION = 1
+    CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
+
+    def __init__(self):
+        """Initialise growatt server flow."""
+        self.api = growattServer.GrowattApi()
+        self.user_id = None
+        self.data = {}
+
+    @callback
+    def _async_show_user_form(self, errors=None):
+        """Show the form to the user."""
+        data_schema = vol.Schema(
+            {vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
+        )
+
+        return self.async_show_form(
+            step_id="user", data_schema=data_schema, errors=errors
+        )
+
+    async def async_step_user(self, user_input=None):
+        """Handle the start of the config flow."""
+        if not user_input:
+            return self._async_show_user_form()
+
+        login_response = await self.hass.async_add_executor_job(
+            self.api.login, user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
+        )
+
+        if not login_response["success"] and login_response["errCode"] == "102":
+            return self._async_show_user_form({"base": "invalid_auth"})
+        self.user_id = login_response["userId"]
+
+        self.data = user_input
+        return await self.async_step_plant()
+
+    async def async_step_plant(self, user_input=None):
+        """Handle adding a "plant" to Home Assistant."""
+        plant_info = await self.hass.async_add_executor_job(
+            self.api.plant_list, self.user_id
+        )
+
+        if not plant_info["data"]:
+            return self.async_abort(reason="no_plants")
+
+        plants = {plant["plantId"]: plant["plantName"] for plant in plant_info["data"]}
+
+        if user_input is None and len(plant_info["data"]) > 1:
+            data_schema = vol.Schema({vol.Required(CONF_PLANT_ID): vol.In(plants)})
+
+            return self.async_show_form(step_id="plant", data_schema=data_schema)
+
+        if user_input is None and len(plant_info["data"]) == 1:
+            user_input = {CONF_PLANT_ID: plant_info["data"][0]["plantId"]}
+
+        user_input[CONF_NAME] = plants[user_input[CONF_PLANT_ID]]
+        await self.async_set_unique_id(user_input[CONF_PLANT_ID])
+        self._abort_if_unique_id_configured()
+        self.data.update(user_input)
+        return self.async_create_entry(title=self.data[CONF_NAME], data=self.data)
+
+    async def async_step_import(self, import_data):
+        """Migrate old yaml config to config flow."""
+        return await self.async_step_user(import_data)
diff --git a/homeassistant/components/growatt_server/const.py b/homeassistant/components/growatt_server/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..4dc09988e6f97fda774f378ae595f36a2d394a54
--- /dev/null
+++ b/homeassistant/components/growatt_server/const.py
@@ -0,0 +1,10 @@
+"""Define constants for the Growatt Server component."""
+CONF_PLANT_ID = "plant_id"
+
+DEFAULT_PLANT_ID = "0"
+
+DEFAULT_NAME = "Growatt"
+
+DOMAIN = "growatt_server"
+
+PLATFORMS = ["sensor"]
diff --git a/homeassistant/components/growatt_server/manifest.json b/homeassistant/components/growatt_server/manifest.json
index f3376ba4ae2d6cf82320e986ac217a42df60fdc1..94fc293b8d710f802f2ce090f8d2026919d75d98 100644
--- a/homeassistant/components/growatt_server/manifest.json
+++ b/homeassistant/components/growatt_server/manifest.json
@@ -1,6 +1,7 @@
 {
   "domain": "growatt_server",
   "name": "Growatt",
+  "config_flow": true,
   "documentation": "https://www.home-assistant.io/integrations/growatt_server/",
   "requirements": ["growattServer==1.0.0"],
   "codeowners": ["@indykoning", "@muppet3000"],
diff --git a/homeassistant/components/growatt_server/sensor.py b/homeassistant/components/growatt_server/sensor.py
index 6464dee6729dd37f07230662b828aab4b9393386..0ccdc9425f6495d089d1c9794a386a054c206d34 100644
--- a/homeassistant/components/growatt_server/sensor.py
+++ b/homeassistant/components/growatt_server/sensor.py
@@ -8,6 +8,7 @@ import growattServer
 import voluptuous as vol
 
 from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
+from homeassistant.config_entries import SOURCE_IMPORT
 from homeassistant.const import (
     CONF_NAME,
     CONF_PASSWORD,
@@ -32,11 +33,10 @@ from homeassistant.const import (
 import homeassistant.helpers.config_validation as cv
 from homeassistant.util import Throttle, dt
 
+from .const import CONF_PLANT_ID, DEFAULT_NAME, DEFAULT_PLANT_ID, DOMAIN
+
 _LOGGER = logging.getLogger(__name__)
 
-CONF_PLANT_ID = "plant_id"
-DEFAULT_PLANT_ID = "0"
-DEFAULT_NAME = "Growatt"
 SCAN_INTERVAL = datetime.timedelta(minutes=1)
 
 # Sensor type order is: Sensor name, Unit of measurement, api data name, additional options
@@ -558,17 +558,26 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
 )
 
 
-def setup_platform(hass, config, add_entities, discovery_info=None):
-    """Set up the Growatt sensor."""
-    username = config[CONF_USERNAME]
-    password = config[CONF_PASSWORD]
-    plant_id = config[CONF_PLANT_ID]
-    name = config[CONF_NAME]
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
+    """Set up growatt server from yaml."""
+    if not hass.config_entries.async_entries(DOMAIN):
+        _LOGGER.warning(
+            "Loading Growatt via platform setup is deprecated."
+            "Please remove it from your configuration"
+        )
+        hass.async_create_task(
+            hass.config_entries.flow.async_init(
+                DOMAIN, context={"source": SOURCE_IMPORT}, data=config
+            )
+        )
 
-    api = growattServer.GrowattApi()
+
+def get_device_list(api, config):
+    """Retrieve the device list for the selected plant."""
+    plant_id = config[CONF_PLANT_ID]
 
     # Log in to api and fetch first plant if no plant id is defined.
-    login_response = api.login(username, password)
+    login_response = api.login(config[CONF_USERNAME], config[CONF_PASSWORD])
     if not login_response["success"] and login_response["errCode"] == "102":
         _LOGGER.error("Username or Password may be incorrect!")
         return
@@ -579,6 +588,20 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
 
     # Get a list of devices for specified plant to add sensors for.
     devices = api.device_list(plant_id)
+    return [devices, plant_id]
+
+
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Growatt sensor."""
+    config = config_entry.data
+    username = config[CONF_USERNAME]
+    password = config[CONF_PASSWORD]
+    name = config[CONF_NAME]
+
+    api = growattServer.GrowattApi()
+
+    devices, plant_id = await hass.async_add_executor_job(get_device_list, api, config)
+
     entities = []
     probe = GrowattData(api, username, password, plant_id, "total")
     for sensor in TOTAL_SENSOR_TYPES:
@@ -616,7 +639,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
                 )
             )
 
-    add_entities(entities, True)
+    async_add_entities(entities, True)
 
 
 class GrowattInverter(SensorEntity):
diff --git a/homeassistant/components/growatt_server/strings.json b/homeassistant/components/growatt_server/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..903ba400a6f99c9aa655f59b984f8c6059fe12d8
--- /dev/null
+++ b/homeassistant/components/growatt_server/strings.json
@@ -0,0 +1,27 @@
+{
+    "config": {
+        "abort": {
+            "no_plants": "No plants have been found on this account"
+        },
+        "error": {
+            "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
+        },
+        "step": {
+            "plant": {
+                "data": {
+                    "plant_id": "Plant"
+                },
+                "title": "Select your plant"
+            },
+            "user": {
+                "data": {
+                    "name": "[%key:common::config_flow::data::name%]",
+                    "password": "[%key:common::config_flow::data::name%]",
+                    "username": "[%key:common::config_flow::data::username%]"
+                },
+                "title": "Enter your Growatt information"
+            }
+        }
+    },
+    "title": "Growatt Server"
+}
diff --git a/homeassistant/components/growatt_server/translations/en.json b/homeassistant/components/growatt_server/translations/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..11bb6a64b32237852b5d0dd739ead88cb6e9ab1e
--- /dev/null
+++ b/homeassistant/components/growatt_server/translations/en.json
@@ -0,0 +1,27 @@
+{
+    "config": {
+        "abort": {
+            "no_plants": "No plants have been found on this account"
+        },
+        "error": {
+            "invalid_auth": "Invalid authentication"
+        },
+        "step": {
+            "plant": {
+                "data": {
+                    "plant_id": "Plant"
+                },
+                "title": "Select your plant"
+            },
+            "user": {
+                "data": {
+                    "name": "Name",
+                    "password": "Password",
+                    "username": "Username"
+                },
+                "title": "Enter your Growatt information"
+            }
+        }
+    },
+    "title": "Growatt Server"
+}
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index bb346ff5b0f9b78df899dfc5380b76a43e59092b..fe62725cc82b4e82132d228d8ab7c4f05ce591e8 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -92,6 +92,7 @@ FLOWS = [
     "google_travel_time",
     "gpslogger",
     "gree",
+    "growatt_server",
     "guardian",
     "habitica",
     "hangouts",
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index f91b249c4779a6ad0a7f335a3a42bd91276b26a2..c7f63ab3692c20afa71ebc509893d75a5d0a21e7 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -386,6 +386,9 @@ googlemaps==2.5.1
 # homeassistant.components.gree
 greeclimate==0.11.4
 
+# homeassistant.components.growatt_server
+growattServer==1.0.0
+
 # homeassistant.components.profiler
 guppy3==3.1.0
 
diff --git a/tests/components/growatt_server/__init__.py b/tests/components/growatt_server/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..999e1782a9ffcd0db6ae8ba932471fbd4bf2bee0
--- /dev/null
+++ b/tests/components/growatt_server/__init__.py
@@ -0,0 +1 @@
+"""Tests for the growatt_server component."""
diff --git a/tests/components/growatt_server/test_config_flow.py b/tests/components/growatt_server/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc11c2f8bf2d273807c27cff593d3ad6bc697d25
--- /dev/null
+++ b/tests/components/growatt_server/test_config_flow.py
@@ -0,0 +1,188 @@
+"""Tests for the Growatt server config flow."""
+from copy import deepcopy
+from unittest.mock import patch
+
+from homeassistant import config_entries, data_entry_flow
+from homeassistant.components.growatt_server.const import CONF_PLANT_ID, DOMAIN
+from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
+
+from tests.common import MockConfigEntry
+
+FIXTURE_USER_INPUT = {CONF_USERNAME: "username", CONF_PASSWORD: "password"}
+
+GROWATT_PLANT_LIST_RESPONSE = {
+    "data": [
+        {
+            "plantMoneyText": "474.9 (€)",
+            "plantName": "Plant name",
+            "plantId": "123456",
+            "isHaveStorage": "false",
+            "todayEnergy": "2.6 kWh",
+            "totalEnergy": "2.37 MWh",
+            "currentPower": "628.8 W",
+        }
+    ],
+    "totalData": {
+        "currentPowerSum": "628.8 W",
+        "CO2Sum": "2.37 KT",
+        "isHaveStorage": "false",
+        "eTotalMoneyText": "474.9 (€)",
+        "todayEnergySum": "2.6 kWh",
+        "totalEnergySum": "2.37 MWh",
+    },
+    "success": True,
+}
+GROWATT_LOGIN_RESPONSE = {"userId": 123456, "userLevel": 1, "success": True}
+
+
+async def test_show_authenticate_form(hass):
+    """Test that the setup form is served."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+    assert result["step_id"] == "user"
+
+
+async def test_incorrect_username(hass):
+    """Test that it shows the appropriate error when an incorrect username is entered."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    with patch(
+        "growattServer.GrowattApi.login",
+        return_value={"errCode": "102", "success": False},
+    ):
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"], FIXTURE_USER_INPUT
+        )
+
+    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+    assert result["step_id"] == "user"
+    assert result["errors"] == {"base": "invalid_auth"}
+
+
+async def test_no_plants_on_account(hass):
+    """Test registering an integration and finishing flow with an entered plant_id."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    user_input = FIXTURE_USER_INPUT.copy()
+    plant_list = deepcopy(GROWATT_PLANT_LIST_RESPONSE)
+    plant_list["data"] = []
+
+    with patch(
+        "growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE
+    ), patch("growattServer.GrowattApi.plant_list", return_value=plant_list):
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"], user_input
+        )
+
+    assert result["type"] == "abort"
+    assert result["reason"] == "no_plants"
+
+
+async def test_multiple_plant_ids(hass):
+    """Test registering an integration and finishing flow with an entered plant_id."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    user_input = FIXTURE_USER_INPUT.copy()
+    plant_list = deepcopy(GROWATT_PLANT_LIST_RESPONSE)
+    plant_list["data"].append(plant_list["data"][0])
+
+    with patch(
+        "growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE
+    ), patch("growattServer.GrowattApi.plant_list", return_value=plant_list), patch(
+        "homeassistant.components.growatt_server.async_setup_entry", return_value=True
+    ):
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"], user_input
+        )
+        assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+        assert result["step_id"] == "plant"
+
+        user_input = {CONF_PLANT_ID: "123456"}
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"], user_input
+        )
+        await hass.async_block_till_done()
+
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT[CONF_USERNAME]
+    assert result["data"][CONF_PASSWORD] == FIXTURE_USER_INPUT[CONF_PASSWORD]
+    assert result["data"][CONF_PLANT_ID] == "123456"
+
+
+async def test_one_plant_on_account(hass):
+    """Test registering an integration and finishing flow with an entered plant_id."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    user_input = FIXTURE_USER_INPUT.copy()
+
+    with patch(
+        "growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE
+    ), patch(
+        "growattServer.GrowattApi.plant_list",
+        return_value=GROWATT_PLANT_LIST_RESPONSE,
+    ), patch(
+        "homeassistant.components.growatt_server.async_setup_entry", return_value=True
+    ):
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"], user_input
+        )
+
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT[CONF_USERNAME]
+    assert result["data"][CONF_PASSWORD] == FIXTURE_USER_INPUT[CONF_PASSWORD]
+    assert result["data"][CONF_PLANT_ID] == "123456"
+
+
+async def test_import_one_plant(hass):
+    """Test import step with a single plant."""
+    import_data = FIXTURE_USER_INPUT.copy()
+
+    with patch(
+        "growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE
+    ), patch(
+        "growattServer.GrowattApi.plant_list",
+        return_value=GROWATT_PLANT_LIST_RESPONSE,
+    ), patch(
+        "homeassistant.components.growatt_server.async_setup_entry", return_value=True
+    ):
+        result = await hass.config_entries.flow.async_init(
+            DOMAIN,
+            context={"source": config_entries.SOURCE_IMPORT},
+            data=import_data,
+        )
+
+    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+    assert result["data"][CONF_USERNAME] == FIXTURE_USER_INPUT[CONF_USERNAME]
+    assert result["data"][CONF_PASSWORD] == FIXTURE_USER_INPUT[CONF_PASSWORD]
+    assert result["data"][CONF_PLANT_ID] == "123456"
+
+
+async def test_existing_plant_configured(hass):
+    """Test entering an existing plant_id."""
+    entry = MockConfigEntry(domain=DOMAIN, unique_id="123456")
+    entry.add_to_hass(hass)
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    user_input = FIXTURE_USER_INPUT.copy()
+
+    with patch(
+        "growattServer.GrowattApi.login", return_value=GROWATT_LOGIN_RESPONSE
+    ), patch(
+        "growattServer.GrowattApi.plant_list",
+        return_value=GROWATT_PLANT_LIST_RESPONSE,
+    ):
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"], user_input
+        )
+
+    assert result["type"] == "abort"
+    assert result["reason"] == "already_configured"