diff --git a/.coveragerc b/.coveragerc
index 70d8f867e0e42db724c88ad35c4832b45bdc5f81..10d56c4701d09d15b2bff2cd39b8b11db214170f 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -258,6 +258,9 @@ omit =
     homeassistant/components/geniushub/*
     homeassistant/components/gearbest/sensor.py
     homeassistant/components/geizhals/sensor.py
+    homeassistant/components/gios/__init__.py
+    homeassistant/components/gios/air_quality.py
+    homeassistant/components/gios/consts.py
     homeassistant/components/github/sensor.py
     homeassistant/components/gitlab_ci/sensor.py
     homeassistant/components/gitter/sensor.py
diff --git a/CODEOWNERS b/CODEOWNERS
index 1df0d2741cdfdee25be504514a9f812ea9d875ad..f5357d1348c2cf6982880df7466979d38495d9f0 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -119,6 +119,7 @@ homeassistant/components/geniushub/* @zxdavb
 homeassistant/components/geo_rss_events/* @exxamalte
 homeassistant/components/geonetnz_quakes/* @exxamalte
 homeassistant/components/geonetnz_volcano/* @exxamalte
+homeassistant/components/gios/* @bieniu
 homeassistant/components/gitter/* @fabaff
 homeassistant/components/glances/* @fabaff @engrbm87
 homeassistant/components/gntp/* @robbiet480
diff --git a/homeassistant/components/gios/__init__.py b/homeassistant/components/gios/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..981de6395deaed481cc91af7e0e02992b78f60cc
--- /dev/null
+++ b/homeassistant/components/gios/__init__.py
@@ -0,0 +1,78 @@
+"""The GIOS component."""
+import asyncio
+import logging
+
+from aiohttp.client_exceptions import ClientConnectorError
+from async_timeout import timeout
+from gios import ApiError, Gios, NoStationError
+
+from homeassistant.core import Config, HomeAssistant
+from homeassistant.helpers.aiohttp_client import async_get_clientsession
+from homeassistant.util import Throttle
+
+from .const import CONF_STATION_ID, DATA_CLIENT, DEFAULT_SCAN_INTERVAL, DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+
+async def async_setup(hass: HomeAssistant, config: Config) -> bool:
+    """Set up configured GIOS."""
+    hass.data[DOMAIN] = {}
+    hass.data[DOMAIN][DATA_CLIENT] = {}
+    return True
+
+
+async def async_setup_entry(hass, config_entry):
+    """Set up GIOS as config entry."""
+    station_id = config_entry.data[CONF_STATION_ID]
+    _LOGGER.debug("Using station_id: %s", station_id)
+
+    websession = async_get_clientsession(hass)
+
+    gios = GiosData(websession, station_id)
+
+    await gios.async_update()
+
+    hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = gios
+
+    hass.async_create_task(
+        hass.config_entries.async_forward_entry_setup(config_entry, "air_quality")
+    )
+    return True
+
+
+async def async_unload_entry(hass, config_entry):
+    """Unload a config entry."""
+    hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
+    await hass.config_entries.async_forward_entry_unload(config_entry, "air_quality")
+    return True
+
+
+class GiosData:
+    """Define an object to hold GIOS data."""
+
+    def __init__(self, session, station_id):
+        """Initialize."""
+        self._gios = Gios(station_id, session)
+        self.station_id = station_id
+        self.sensors = {}
+        self.latitude = None
+        self.longitude = None
+        self.station_name = None
+        self.available = True
+
+    @Throttle(DEFAULT_SCAN_INTERVAL)
+    async def async_update(self):
+        """Update GIOS data."""
+        try:
+            with timeout(30):
+                await self._gios.update()
+        except asyncio.TimeoutError:
+            _LOGGER.error("Asyncio Timeout Error")
+        except (ApiError, NoStationError, ClientConnectorError) as error:
+            _LOGGER.error("GIOS data update failed: %s", error)
+        self.available = self._gios.available
+        self.latitude = self._gios.latitude
+        self.longitude = self._gios.longitude
+        self.station_name = self._gios.station_name
+        self.sensors = self._gios.data
diff --git a/homeassistant/components/gios/air_quality.py b/homeassistant/components/gios/air_quality.py
new file mode 100644
index 0000000000000000000000000000000000000000..f7285c8cc5a851eaea8d9ca6598fc0794283e7ff
--- /dev/null
+++ b/homeassistant/components/gios/air_quality.py
@@ -0,0 +1,158 @@
+"""Support for the GIOS service."""
+from homeassistant.components.air_quality import (
+    ATTR_CO,
+    ATTR_NO2,
+    ATTR_OZONE,
+    ATTR_PM_2_5,
+    ATTR_PM_10,
+    ATTR_SO2,
+    AirQualityEntity,
+)
+from homeassistant.const import CONF_NAME
+
+from .const import ATTR_STATION, DATA_CLIENT, DEFAULT_SCAN_INTERVAL, DOMAIN, ICONS_MAP
+
+ATTRIBUTION = "Data provided by GIOÅš"
+SCAN_INTERVAL = DEFAULT_SCAN_INTERVAL
+
+
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Add a GIOS entities from a config_entry."""
+    name = config_entry.data[CONF_NAME]
+
+    data = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id]
+
+    async_add_entities([GiosAirQuality(data, name)], True)
+
+
+def round_state(func):
+    """Round state."""
+
+    def _decorator(self):
+        res = func(self)
+        if isinstance(res, float):
+            return round(res)
+        return res
+
+    return _decorator
+
+
+class GiosAirQuality(AirQualityEntity):
+    """Define an GIOS sensor."""
+
+    def __init__(self, gios, name):
+        """Initialize."""
+        self.gios = gios
+        self._name = name
+        self._aqi = None
+        self._co = None
+        self._no2 = None
+        self._o3 = None
+        self._pm_2_5 = None
+        self._pm_10 = None
+        self._so2 = None
+        self._attrs = {}
+
+    @property
+    def name(self):
+        """Return the name."""
+        return self._name
+
+    @property
+    def icon(self):
+        """Return the icon."""
+        if self._aqi in ICONS_MAP:
+            return ICONS_MAP[self._aqi]
+        return "mdi:blur"
+
+    @property
+    def air_quality_index(self):
+        """Return the air quality index."""
+        return self._aqi
+
+    @property
+    @round_state
+    def particulate_matter_2_5(self):
+        """Return the particulate matter 2.5 level."""
+        return self._pm_2_5
+
+    @property
+    @round_state
+    def particulate_matter_10(self):
+        """Return the particulate matter 10 level."""
+        return self._pm_10
+
+    @property
+    @round_state
+    def ozone(self):
+        """Return the O3 (ozone) level."""
+        return self._o3
+
+    @property
+    @round_state
+    def carbon_monoxide(self):
+        """Return the CO (carbon monoxide) level."""
+        return self._co
+
+    @property
+    @round_state
+    def sulphur_dioxide(self):
+        """Return the SO2 (sulphur dioxide) level."""
+        return self._so2
+
+    @property
+    @round_state
+    def nitrogen_dioxide(self):
+        """Return the NO2 (nitrogen dioxide) level."""
+        return self._no2
+
+    @property
+    def attribution(self):
+        """Return the attribution."""
+        return ATTRIBUTION
+
+    @property
+    def unique_id(self):
+        """Return a unique_id for this entity."""
+        return self.gios.station_id
+
+    @property
+    def available(self):
+        """Return True if entity is available."""
+        return self.gios.available
+
+    @property
+    def device_state_attributes(self):
+        """Return the state attributes."""
+        self._attrs[ATTR_STATION] = self.gios.station_name
+        return self._attrs
+
+    async def async_update(self):
+        """Get the data from GIOS."""
+        await self.gios.async_update()
+
+        if self.gios.available:
+            # Different measuring stations have different sets of sensors. We don't know
+            # what data we will get.
+            if "AQI" in self.gios.sensors:
+                self._aqi = self.gios.sensors["AQI"]["value"]
+            if "CO" in self.gios.sensors:
+                self._co = self.gios.sensors["CO"]["value"]
+                self._attrs[f"{ATTR_CO}_index"] = self.gios.sensors["CO"]["index"]
+            if "NO2" in self.gios.sensors:
+                self._no2 = self.gios.sensors["NO2"]["value"]
+                self._attrs[f"{ATTR_NO2}_index"] = self.gios.sensors["NO2"]["index"]
+            if "O3" in self.gios.sensors:
+                self._o3 = self.gios.sensors["O3"]["value"]
+                self._attrs[f"{ATTR_OZONE}_index"] = self.gios.sensors["O3"]["index"]
+            if "PM2.5" in self.gios.sensors:
+                self._pm_2_5 = self.gios.sensors["PM2.5"]["value"]
+                self._attrs[f"{ATTR_PM_2_5}_index"] = self.gios.sensors["PM2.5"][
+                    "index"
+                ]
+            if "PM10" in self.gios.sensors:
+                self._pm_10 = self.gios.sensors["PM10"]["value"]
+                self._attrs[f"{ATTR_PM_10}_index"] = self.gios.sensors["PM10"]["index"]
+            if "SO2" in self.gios.sensors:
+                self._so2 = self.gios.sensors["SO2"]["value"]
+                self._attrs[f"{ATTR_SO2}_index"] = self.gios.sensors["SO2"]["index"]
diff --git a/homeassistant/components/gios/config_flow.py b/homeassistant/components/gios/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..368d610c22640e28eb4cd683ded5ed2a71964233
--- /dev/null
+++ b/homeassistant/components/gios/config_flow.py
@@ -0,0 +1,65 @@
+"""Adds config flow for GIOS."""
+import asyncio
+
+from aiohttp.client_exceptions import ClientConnectorError
+from async_timeout import timeout
+from gios import ApiError, Gios, NoStationError
+import voluptuous as vol
+
+from homeassistant import config_entries, exceptions
+from homeassistant.const import CONF_NAME
+from homeassistant.helpers.aiohttp_client import async_get_clientsession
+
+from .const import CONF_STATION_ID, DEFAULT_NAME, DOMAIN  # pylint:disable=unused-import
+
+DATA_SCHEMA = vol.Schema(
+    {
+        vol.Required(CONF_STATION_ID): int,
+        vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
+    }
+)
+
+
+class GiosFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
+    """Config flow for GIOS."""
+
+    VERSION = 1
+    CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
+
+    async def async_step_user(self, user_input=None):
+        """Handle a flow initialized by the user."""
+        errors = {}
+
+        if user_input is not None:
+            try:
+                await self.async_set_unique_id(
+                    user_input[CONF_STATION_ID], raise_on_progress=False
+                )
+                self._abort_if_unique_id_configured()
+
+                websession = async_get_clientsession(self.hass)
+
+                with timeout(30):
+                    gios = Gios(user_input[CONF_STATION_ID], websession)
+                    await gios.update()
+
+                if not gios.available:
+                    raise InvalidSensorsData()
+
+                return self.async_create_entry(
+                    title=user_input[CONF_STATION_ID], data=user_input,
+                )
+            except (ApiError, ClientConnectorError, asyncio.TimeoutError):
+                errors["base"] = "cannot_connect"
+            except NoStationError:
+                errors[CONF_STATION_ID] = "wrong_station_id"
+            except InvalidSensorsData:
+                errors[CONF_STATION_ID] = "invalid_sensors_data"
+
+        return self.async_show_form(
+            step_id="user", data_schema=DATA_SCHEMA, errors=errors
+        )
+
+
+class InvalidSensorsData(exceptions.HomeAssistantError):
+    """Error to indicate invalid sensors data."""
diff --git a/homeassistant/components/gios/const.py b/homeassistant/components/gios/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..3588b5e8dfcf0678a66e3b6ea3f3f73eb138922b
--- /dev/null
+++ b/homeassistant/components/gios/const.py
@@ -0,0 +1,25 @@
+"""Constants for GIOS integration."""
+from datetime import timedelta
+
+ATTR_NAME = "name"
+ATTR_STATION = "station"
+CONF_STATION_ID = "station_id"
+DATA_CLIENT = "client"
+DEFAULT_NAME = "GIOÅš"
+# Term of service GIOÅš allow downloading data no more than twice an hour.
+DEFAULT_SCAN_INTERVAL = timedelta(minutes=30)
+DOMAIN = "gios"
+
+AQI_GOOD = "dobry"
+AQI_MODERATE = "umiarkowany"
+AQI_POOR = "dostateczny"
+AQI_VERY_GOOD = "bardzo dobry"
+AQI_VERY_POOR = "zły"
+
+ICONS_MAP = {
+    AQI_VERY_GOOD: "mdi:emoticon-excited",
+    AQI_GOOD: "mdi:emoticon-happy",
+    AQI_MODERATE: "mdi:emoticon-neutral",
+    AQI_POOR: "mdi:emoticon-sad",
+    AQI_VERY_POOR: "mdi:emoticon-dead",
+}
diff --git a/homeassistant/components/gios/manifest.json b/homeassistant/components/gios/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..b3d125d8ab6424fe380c82380726d6df3f965433
--- /dev/null
+++ b/homeassistant/components/gios/manifest.json
@@ -0,0 +1,9 @@
+{
+  "domain": "gios",
+  "name": "GIOÅš",
+  "documentation": "https://www.home-assistant.io/integrations/gios",
+  "dependencies": [],
+  "codeowners": ["@bieniu"],
+  "requirements": ["gios==0.0.3"],
+  "config_flow": true
+}
diff --git a/homeassistant/components/gios/strings.json b/homeassistant/components/gios/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..cc05a471b4a1387b32151c1a7e29449f1c77ad83
--- /dev/null
+++ b/homeassistant/components/gios/strings.json
@@ -0,0 +1,20 @@
+{
+  "config": {
+    "title": "GIOÅš",
+    "step": {
+      "user": {
+        "title": "GIOÅš (Polish Chief Inspectorate Of Environmental Protection)",
+        "description": "Set up GIOÅš (Polish Chief Inspectorate Of Environmental Protection) air quality integration. If you need help with the configuration have a look here: https://www.home-assistant.io/integrations/gios",
+        "data": {
+          "name": "Name of the integration",
+          "station_id": "ID of the measuring station"
+        }
+      }
+    },
+    "error": {
+      "wrong_station_id": "ID of the measuring station is not correct.",
+      "invalid_sensors_data": "Invalid sensors' data for this measuring station.",
+      "cannot_connect": "Cannot connect to the GIOÅš server."
+    }
+  }
+}
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index 88ff92a57b05930ed01ba188d25507f74e877fc5..55a4d76fdcd21aea3b7317bf0fd7ccfe17bd7830 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -26,6 +26,7 @@ FLOWS = [
     "geofency",
     "geonetnz_quakes",
     "geonetnz_volcano",
+    "gios",
     "glances",
     "gpslogger",
     "hangouts",
diff --git a/requirements_all.txt b/requirements_all.txt
index ed6a433616a031f8413e03a610ca53a374bde326..d84815a42f312d7ace525fa33b6b7c6f55c4d351 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -578,6 +578,9 @@ georss_qld_bushfire_alert_client==0.3
 # homeassistant.components.nmap_tracker
 getmac==0.8.1
 
+# homeassistant.components.gios
+gios==0.0.3
+
 # homeassistant.components.gitter
 gitterpy==0.1.7
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index cf62fd652888f66a59652b3d72a9a77b05c31cff..d197ed0196afa311ab583fd8400bf2ddb75d243c 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -197,6 +197,9 @@ georss_qld_bushfire_alert_client==0.3
 # homeassistant.components.nmap_tracker
 getmac==0.8.1
 
+# homeassistant.components.gios
+gios==0.0.3
+
 # homeassistant.components.glances
 glances_api==0.2.0
 
diff --git a/tests/components/gios/__init__.py b/tests/components/gios/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..98528fda9f91b923cf8e91a1f44ed89d79ae593b
--- /dev/null
+++ b/tests/components/gios/__init__.py
@@ -0,0 +1 @@
+"""Tests for GIOS."""
diff --git a/tests/components/gios/test_config_flow.py b/tests/components/gios/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a4aff6d9ad6950c30b402465ba52d9bf05c0a9c
--- /dev/null
+++ b/tests/components/gios/test_config_flow.py
@@ -0,0 +1,104 @@
+"""Define tests for the GIOS config flow."""
+from asynctest import patch
+from gios import ApiError
+
+from homeassistant import data_entry_flow
+from homeassistant.components.gios import config_flow
+from homeassistant.components.gios.const import CONF_STATION_ID
+from homeassistant.const import CONF_NAME
+
+CONFIG = {
+    CONF_NAME: "Foo",
+    CONF_STATION_ID: 123,
+}
+
+VALID_STATIONS = [
+    {"id": 123, "stationName": "Test Name 1", "gegrLat": "99.99", "gegrLon": "88.88"},
+    {"id": 321, "stationName": "Test Name 2", "gegrLat": "77.77", "gegrLon": "66.66"},
+]
+
+VALID_STATION = [
+    {"id": 3764, "param": {"paramName": "particulate matter PM10", "paramCode": "PM10"}}
+]
+
+VALID_INDEXES = {
+    "stIndexLevel": {"id": 1, "indexLevelName": "Good"},
+    "pm10IndexLevel": {"id": 0, "indexLevelName": "Very good"},
+}
+
+VALID_SENSOR = {"key": "PM10", "values": [{"value": 11.11}]}
+
+
+async def test_show_form(hass):
+    """Test that the form is served with no input."""
+    flow = config_flow.GiosFlowHandler()
+    flow.hass = hass
+
+    result = await flow.async_step_user(user_input=None)
+
+    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+    assert result["step_id"] == "user"
+
+
+async def test_invalid_station_id(hass):
+    """Test that errors are shown when measuring station ID is invalid."""
+    with patch("gios.Gios._get_stations", return_value=VALID_STATIONS):
+        flow = config_flow.GiosFlowHandler()
+        flow.hass = hass
+        flow.context = {}
+
+        result = await flow.async_step_user(
+            user_input={CONF_NAME: "Foo", CONF_STATION_ID: 0}
+        )
+
+        assert result["errors"] == {CONF_STATION_ID: "wrong_station_id"}
+
+
+async def test_invalid_sensor_data(hass):
+    """Test that errors are shown when sensor data is invalid."""
+    with patch("gios.Gios._get_stations", return_value=VALID_STATIONS), patch(
+        "gios.Gios._get_station", return_value=VALID_STATION
+    ), patch("gios.Gios._get_station", return_value=VALID_STATION), patch(
+        "gios.Gios._get_sensor", return_value={}
+    ):
+        flow = config_flow.GiosFlowHandler()
+        flow.hass = hass
+        flow.context = {}
+
+        result = await flow.async_step_user(user_input=CONFIG)
+
+        assert result["errors"] == {CONF_STATION_ID: "invalid_sensors_data"}
+
+
+async def test_cannot_connect(hass):
+    """Test that errors are shown when cannot connect to GIOS server."""
+    with patch("gios.Gios._async_get", side_effect=ApiError("error")):
+        flow = config_flow.GiosFlowHandler()
+        flow.hass = hass
+        flow.context = {}
+
+        result = await flow.async_step_user(user_input=CONFIG)
+
+        assert result["errors"] == {"base": "cannot_connect"}
+
+
+async def test_create_entry(hass):
+    """Test that the user step works."""
+    with patch("gios.Gios._get_stations", return_value=VALID_STATIONS), patch(
+        "gios.Gios._get_station", return_value=VALID_STATION
+    ), patch("gios.Gios._get_station", return_value=VALID_STATION), patch(
+        "gios.Gios._get_sensor", return_value=VALID_SENSOR
+    ), patch(
+        "gios.Gios._get_indexes", return_value=VALID_INDEXES
+    ):
+        flow = config_flow.GiosFlowHandler()
+        flow.hass = hass
+        flow.context = {}
+
+        result = await flow.async_step_user(user_input=CONFIG)
+
+        assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
+        assert result["title"] == CONFIG[CONF_STATION_ID]
+        assert result["data"][CONF_STATION_ID] == CONFIG[CONF_STATION_ID]
+
+        assert flow.context["unique_id"] == CONFIG[CONF_STATION_ID]