From 15e6278a2e40b839142a702d40751c1fd651ecad Mon Sep 17 00:00:00 2001
From: Bram Kragten <mail@bramkragten.nl>
Date: Wed, 13 Nov 2019 16:37:31 +0100
Subject: [PATCH] Add config entry and device support to Demo (#28702)

* Add config entry and device support to Demo

* Some more devices

* Fix tests using demo

* Review comments

* Update config_flow.py

* Revert

* Disable pylint
---
 homeassistant/components/demo/__init__.py     | 41 +++++++++++++-----
 homeassistant/components/demo/air_quality.py  |  9 +++-
 .../components/demo/alarm_control_panel.py    |  5 +++
 .../components/demo/binary_sensor.py          | 33 ++++++++++++---
 homeassistant/components/demo/camera.py       |  5 +++
 homeassistant/components/demo/climate.py      | 33 ++++++++++++++-
 homeassistant/components/demo/config_flow.py  | 16 +++++++
 homeassistant/components/demo/cover.py        | 35 +++++++++++++---
 homeassistant/components/demo/fan.py          |  9 +++-
 homeassistant/components/demo/light.py        | 32 +++++++++++---
 homeassistant/components/demo/lock.py         |  9 +++-
 homeassistant/components/demo/media_player.py |  9 +++-
 homeassistant/components/demo/remote.py       |  5 +++
 homeassistant/components/demo/sensor.py       | 42 ++++++++++++++++---
 homeassistant/components/demo/strings.json    |  5 +++
 homeassistant/components/demo/switch.py       | 33 ++++++++++++---
 homeassistant/components/demo/vacuum.py       |  5 +++
 homeassistant/components/demo/water_heater.py |  9 +++-
 homeassistant/components/demo/weather.py      |  5 +++
 .../google_assistant/test_smart_home.py       |  7 +++-
 tests/components/prometheus/test_init.py      | 10 +++--
 21 files changed, 304 insertions(+), 53 deletions(-)
 create mode 100644 homeassistant/components/demo/config_flow.py
 create mode 100644 homeassistant/components/demo/strings.json

diff --git a/homeassistant/components/demo/__init__.py b/homeassistant/components/demo/__init__.py
index 93a34794366..05febfad603 100644
--- a/homeassistant/components/demo/__init__.py
+++ b/homeassistant/components/demo/__init__.py
@@ -3,33 +3,37 @@ import asyncio
 import logging
 import time
 
-from homeassistant import bootstrap
+from homeassistant import bootstrap, config_entries
 import homeassistant.core as ha
 from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_START
 
 DOMAIN = "demo"
 _LOGGER = logging.getLogger(__name__)
-COMPONENTS_WITH_DEMO_PLATFORM = [
+
+COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM = [
     "air_quality",
     "alarm_control_panel",
     "binary_sensor",
-    "calendar",
     "camera",
     "climate",
     "cover",
-    "device_tracker",
     "fan",
-    "image_processing",
     "light",
     "lock",
     "media_player",
-    "notify",
     "sensor",
-    "stt",
     "switch",
+    "water_heater",
+]
+
+COMPONENTS_WITH_DEMO_PLATFORM = [
     "tts",
+    "stt",
     "mailbox",
-    "water_heater",
+    "notify",
+    "image_processing",
+    "calendar",
+    "device_tracker",
 ]
 
 
@@ -38,8 +42,12 @@ async def async_setup(hass, config):
     if DOMAIN not in config:
         return True
 
-    config.setdefault(ha.DOMAIN, {})
-    config.setdefault(DOMAIN, {})
+    if not hass.config_entries.async_entries(DOMAIN):
+        hass.async_create_task(
+            hass.config_entries.flow.async_init(
+                DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={}
+            )
+        )
 
     # Set up demo platforms
     for component in COMPONENTS_WITH_DEMO_PLATFORM:
@@ -47,6 +55,9 @@ async def async_setup(hass, config):
             hass.helpers.discovery.async_load_platform(component, DOMAIN, {}, config)
         )
 
+    config.setdefault(ha.DOMAIN, {})
+    config.setdefault(DOMAIN, {})
+
     # Set up sun
     if not hass.config.latitude:
         hass.config.latitude = 32.87336
@@ -176,6 +187,16 @@ async def async_setup(hass, config):
     return True
 
 
+async def async_setup_entry(hass, config_entry):
+    """Set the config entry up."""
+    # Set up demo platforms with config entry
+    for component in COMPONENTS_WITH_CONFIG_ENTRY_DEMO_PLATFORM:
+        hass.async_create_task(
+            hass.config_entries.async_forward_entry_setup(config_entry, component)
+        )
+    return True
+
+
 async def finish_setup(hass, config):
     """Finish set up once demo platforms are set up."""
     switches = None
diff --git a/homeassistant/components/demo/air_quality.py b/homeassistant/components/demo/air_quality.py
index 0b41d9c87e9..9fe0f675d9d 100644
--- a/homeassistant/components/demo/air_quality.py
+++ b/homeassistant/components/demo/air_quality.py
@@ -2,13 +2,18 @@
 from homeassistant.components.air_quality import AirQualityEntity
 
 
-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 the Air Quality."""
-    add_entities(
+    async_add_entities(
         [DemoAirQuality("Home", 14, 23, 100), DemoAirQuality("Office", 4, 16, None)]
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoAirQuality(AirQualityEntity):
     """Representation of Air Quality data."""
 
diff --git a/homeassistant/components/demo/alarm_control_panel.py b/homeassistant/components/demo/alarm_control_panel.py
index 378ba3b18dd..d82edadf161 100644
--- a/homeassistant/components/demo/alarm_control_panel.py
+++ b/homeassistant/components/demo/alarm_control_panel.py
@@ -57,3 +57,8 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
             )
         ]
     )
+
+
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
diff --git a/homeassistant/components/demo/binary_sensor.py b/homeassistant/components/demo/binary_sensor.py
index 96c2b66fa5b..c1e42807f6d 100644
--- a/homeassistant/components/demo/binary_sensor.py
+++ b/homeassistant/components/demo/binary_sensor.py
@@ -1,26 +1,49 @@
 """Demo platform that has two fake binary sensors."""
 from homeassistant.components.binary_sensor import BinarySensorDevice
+from . import DOMAIN
 
 
-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 the Demo binary sensor platform."""
-    add_entities(
+    async_add_entities(
         [
-            DemoBinarySensor("Basement Floor Wet", False, "moisture"),
-            DemoBinarySensor("Movement Backyard", True, "motion"),
+            DemoBinarySensor("binary_1", "Basement Floor Wet", False, "moisture"),
+            DemoBinarySensor("binary_2", "Movement Backyard", True, "motion"),
         ]
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoBinarySensor(BinarySensorDevice):
     """representation of a Demo binary sensor."""
 
-    def __init__(self, name, state, device_class):
+    def __init__(self, unique_id, name, state, device_class):
         """Initialize the demo sensor."""
+        self._unique_id = unique_id
         self._name = name
         self._state = state
         self._sensor_type = device_class
 
+    @property
+    def device_info(self):
+        """Return device info."""
+        return {
+            "identifiers": {
+                # Serial numbers are unique identifiers within a specific domain
+                (DOMAIN, self.unique_id)
+            },
+            "name": self.name,
+        }
+
+    @property
+    def unique_id(self):
+        """Return the unique id."""
+        return self._unique_id
+
     @property
     def device_class(self):
         """Return the class of this sensor."""
diff --git a/homeassistant/components/demo/camera.py b/homeassistant/components/demo/camera.py
index 0cd77b6112e..f639dae9757 100644
--- a/homeassistant/components/demo/camera.py
+++ b/homeassistant/components/demo/camera.py
@@ -12,6 +12,11 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
     async_add_entities([DemoCamera("Demo camera")])
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoCamera(Camera):
     """The representation of a Demo camera."""
 
diff --git a/homeassistant/components/demo/climate.py b/homeassistant/components/demo/climate.py
index f5117b7986c..f4affed7ced 100644
--- a/homeassistant/components/demo/climate.py
+++ b/homeassistant/components/demo/climate.py
@@ -1,4 +1,5 @@
 """Demo platform that offers a fake climate device."""
+import logging
 from homeassistant.components.climate import ClimateDevice
 from homeassistant.components.climate.const import (
     ATTR_TARGET_TEMP_HIGH,
@@ -20,15 +21,18 @@ from homeassistant.components.climate.const import (
     HVAC_MODE_AUTO,
 )
 from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
+from . import DOMAIN
 
 SUPPORT_FLAGS = 0
+_LOGGER = logging.getLogger(__name__)
 
 
-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 the Demo climate devices."""
-    add_entities(
+    async_add_entities(
         [
             DemoClimate(
+                unique_id="climate_1",
                 name="HeatPump",
                 target_temperature=68,
                 unit_of_measurement=TEMP_FAHRENHEIT,
@@ -46,6 +50,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
                 hvac_modes=[HVAC_MODE_HEAT, HVAC_MODE_OFF],
             ),
             DemoClimate(
+                unique_id="climate_2",
                 name="Hvac",
                 target_temperature=21,
                 unit_of_measurement=TEMP_CELSIUS,
@@ -63,6 +68,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
                 hvac_modes=[mode for mode in HVAC_MODES if mode != HVAC_MODE_HEAT_COOL],
             ),
             DemoClimate(
+                unique_id="climate_3",
                 name="Ecobee",
                 target_temperature=None,
                 unit_of_measurement=TEMP_CELSIUS,
@@ -84,11 +90,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo climate devices config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoClimate(ClimateDevice):
     """Representation of a demo climate device."""
 
     def __init__(
         self,
+        unique_id,
         name,
         target_temperature,
         unit_of_measurement,
@@ -107,6 +119,7 @@ class DemoClimate(ClimateDevice):
         preset_modes=None,
     ):
         """Initialize the climate device."""
+        self._unique_id = unique_id
         self._name = name
         self._support_flags = SUPPORT_FLAGS
         if target_temperature is not None:
@@ -143,6 +156,22 @@ class DemoClimate(ClimateDevice):
         self._target_temperature_high = target_temp_high
         self._target_temperature_low = target_temp_low
 
+    @property
+    def device_info(self):
+        """Return device info."""
+        return {
+            "identifiers": {
+                # Serial numbers are unique identifiers within a specific domain
+                (DOMAIN, self.unique_id)
+            },
+            "name": self.name,
+        }
+
+    @property
+    def unique_id(self):
+        """Return the unique id."""
+        return self._unique_id
+
     @property
     def supported_features(self):
         """Return the list of supported features."""
diff --git a/homeassistant/components/demo/config_flow.py b/homeassistant/components/demo/config_flow.py
new file mode 100644
index 00000000000..e6b275920c8
--- /dev/null
+++ b/homeassistant/components/demo/config_flow.py
@@ -0,0 +1,16 @@
+"""Config flow to configure demo component."""
+
+from homeassistant import config_entries
+
+# pylint: disable=unused-import
+from . import DOMAIN
+
+
+class DemoConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
+    """Demo configuration flow."""
+
+    VERSION = 1
+
+    async def async_step_import(self, import_info):
+        """Set the config entry up from yaml."""
+        return self.async_create_entry(title="Demo", data={})
diff --git a/homeassistant/components/demo/cover.py b/homeassistant/components/demo/cover.py
index 180312eefa3..20a8747aaa5 100644
--- a/homeassistant/components/demo/cover.py
+++ b/homeassistant/components/demo/cover.py
@@ -8,17 +8,19 @@ from homeassistant.components.cover import (
     SUPPORT_OPEN,
     CoverDevice,
 )
+from . import DOMAIN
 
 
-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 the Demo covers."""
-    add_entities(
+    async_add_entities(
         [
-            DemoCover(hass, "Kitchen Window"),
-            DemoCover(hass, "Hall Window", 10),
-            DemoCover(hass, "Living Room Window", 70, 50),
+            DemoCover(hass, "cover_1", "Kitchen Window"),
+            DemoCover(hass, "cover_2", "Hall Window", 10),
+            DemoCover(hass, "cover_3", "Living Room Window", 70, 50),
             DemoCover(
                 hass,
+                "cover_4",
                 "Garage Door",
                 device_class="garage",
                 supported_features=(SUPPORT_OPEN | SUPPORT_CLOSE),
@@ -27,12 +29,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoCover(CoverDevice):
     """Representation of a demo cover."""
 
     def __init__(
         self,
         hass,
+        unique_id,
         name,
         position=None,
         tilt_position=None,
@@ -41,6 +49,7 @@ class DemoCover(CoverDevice):
     ):
         """Initialize the cover."""
         self.hass = hass
+        self._unique_id = unique_id
         self._name = name
         self._position = position
         self._device_class = device_class
@@ -59,6 +68,22 @@ class DemoCover(CoverDevice):
         else:
             self._closed = self.current_cover_position <= 0
 
+    @property
+    def device_info(self):
+        """Return device info."""
+        return {
+            "identifiers": {
+                # Serial numbers are unique identifiers within a specific domain
+                (DOMAIN, self.unique_id)
+            },
+            "name": self.name,
+        }
+
+    @property
+    def unique_id(self):
+        """Return unique ID for cover."""
+        return self._unique_id
+
     @property
     def name(self):
         """Return the name of the cover."""
diff --git a/homeassistant/components/demo/fan.py b/homeassistant/components/demo/fan.py
index ab8a6f3fae9..500d5f6a5ce 100644
--- a/homeassistant/components/demo/fan.py
+++ b/homeassistant/components/demo/fan.py
@@ -15,9 +15,9 @@ FULL_SUPPORT = SUPPORT_SET_SPEED | SUPPORT_OSCILLATE | SUPPORT_DIRECTION
 LIMITED_SUPPORT = SUPPORT_SET_SPEED
 
 
-def setup_platform(hass, config, add_entities_callback, discovery_info=None):
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
     """Set up the demo fan platform."""
-    add_entities_callback(
+    async_add_entities(
         [
             DemoFan(hass, "Living Room Fan", FULL_SUPPORT),
             DemoFan(hass, "Ceiling Fan", LIMITED_SUPPORT),
@@ -25,6 +25,11 @@ def setup_platform(hass, config, add_entities_callback, discovery_info=None):
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoFan(FanEntity):
     """A demonstration fan component."""
 
diff --git a/homeassistant/components/demo/light.py b/homeassistant/components/demo/light.py
index f8b1b511511..a2c06b72986 100644
--- a/homeassistant/components/demo/light.py
+++ b/homeassistant/components/demo/light.py
@@ -15,6 +15,8 @@ from homeassistant.components.light import (
     Light,
 )
 
+from . import DOMAIN
+
 LIGHT_COLORS = [(56, 86), (345, 75)]
 
 LIGHT_EFFECT_LIST = ["rainbow", "none"]
@@ -30,24 +32,33 @@ SUPPORT_DEMO = (
 )
 
 
-def setup_platform(hass, config, add_entities_callback, discovery_info=None):
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
     """Set up the demo light platform."""
-    add_entities_callback(
+    async_add_entities(
         [
             DemoLight(
-                1,
+                "light_1",
                 "Bed Light",
                 False,
                 True,
                 effect_list=LIGHT_EFFECT_LIST,
                 effect=LIGHT_EFFECT_LIST[0],
             ),
-            DemoLight(2, "Ceiling Lights", True, True, LIGHT_COLORS[0], LIGHT_TEMPS[1]),
-            DemoLight(3, "Kitchen Lights", True, True, LIGHT_COLORS[1], LIGHT_TEMPS[0]),
+            DemoLight(
+                "light_2", "Ceiling Lights", True, True, LIGHT_COLORS[0], LIGHT_TEMPS[1]
+            ),
+            DemoLight(
+                "light_3", "Kitchen Lights", True, True, LIGHT_COLORS[1], LIGHT_TEMPS[0]
+            ),
         ]
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoLight(Light):
     """Representation of a demo light."""
 
@@ -76,6 +87,17 @@ class DemoLight(Light):
         self._effect = effect
         self._available = True
 
+    @property
+    def device_info(self):
+        """Return device info."""
+        return {
+            "identifiers": {
+                # Serial numbers are unique identifiers within a specific domain
+                (DOMAIN, self.unique_id)
+            },
+            "name": self.name,
+        }
+
     @property
     def should_poll(self) -> bool:
         """No polling needed for a demo light."""
diff --git a/homeassistant/components/demo/lock.py b/homeassistant/components/demo/lock.py
index d8fb244b9bc..923469f045c 100644
--- a/homeassistant/components/demo/lock.py
+++ b/homeassistant/components/demo/lock.py
@@ -4,9 +4,9 @@ from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED
 from homeassistant.components.lock import SUPPORT_OPEN, LockDevice
 
 
-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 the Demo lock platform."""
-    add_entities(
+    async_add_entities(
         [
             DemoLock("Front Door", STATE_LOCKED),
             DemoLock("Kitchen Door", STATE_UNLOCKED),
@@ -15,6 +15,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoLock(LockDevice):
     """Representation of a Demo lock."""
 
diff --git a/homeassistant/components/demo/media_player.py b/homeassistant/components/demo/media_player.py
index fb64f8015c0..9d7c3892af8 100644
--- a/homeassistant/components/demo/media_player.py
+++ b/homeassistant/components/demo/media_player.py
@@ -24,9 +24,9 @@ from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING
 import homeassistant.util.dt as dt_util
 
 
-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 the media player demo platform."""
-    add_entities(
+    async_add_entities(
         [
             DemoYoutubePlayer(
                 "Living Room",
@@ -43,6 +43,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 YOUTUBE_COVER_URL_FORMAT = "https://img.youtube.com/vi/{}/hqdefault.jpg"
 SOUND_MODE_LIST = ["Dummy Music", "Dummy Movie"]
 DEFAULT_SOUND_MODE = "Dummy Music"
diff --git a/homeassistant/components/demo/remote.py b/homeassistant/components/demo/remote.py
index 4a363781e2e..70e0d3c8b6e 100644
--- a/homeassistant/components/demo/remote.py
+++ b/homeassistant/components/demo/remote.py
@@ -3,6 +3,11 @@ from homeassistant.components.remote import RemoteDevice
 from homeassistant.const import DEVICE_DEFAULT_NAME
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    setup_platform(hass, {}, async_add_entities)
+
+
 def setup_platform(hass, config, add_entities_callback, discovery_info=None):
     """Set up the demo remotes."""
     add_entities_callback(
diff --git a/homeassistant/components/demo/sensor.py b/homeassistant/components/demo/sensor.py
index 78bd88b42cf..bf5df94e74c 100644
--- a/homeassistant/components/demo/sensor.py
+++ b/homeassistant/components/demo/sensor.py
@@ -6,31 +6,63 @@ from homeassistant.const import (
     DEVICE_CLASS_TEMPERATURE,
 )
 from homeassistant.helpers.entity import Entity
+from . import DOMAIN
 
 
-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 the Demo sensors."""
-    add_entities(
+    async_add_entities(
         [
             DemoSensor(
-                "Outside Temperature", 15.6, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, 12
+                "sensor_1",
+                "Outside Temperature",
+                15.6,
+                DEVICE_CLASS_TEMPERATURE,
+                TEMP_CELSIUS,
+                12,
+            ),
+            DemoSensor(
+                "sensor_2", "Outside Humidity", 54, DEVICE_CLASS_HUMIDITY, "%", None
             ),
-            DemoSensor("Outside Humidity", 54, DEVICE_CLASS_HUMIDITY, "%", None),
         ]
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoSensor(Entity):
     """Representation of a Demo sensor."""
 
-    def __init__(self, name, state, device_class, unit_of_measurement, battery):
+    def __init__(
+        self, unique_id, name, state, device_class, unit_of_measurement, battery
+    ):
         """Initialize the sensor."""
+        self._unique_id = unique_id
         self._name = name
         self._state = state
         self._device_class = device_class
         self._unit_of_measurement = unit_of_measurement
         self._battery = battery
 
+    @property
+    def device_info(self):
+        """Return device info."""
+        return {
+            "identifiers": {
+                # Serial numbers are unique identifiers within a specific domain
+                (DOMAIN, self.unique_id)
+            },
+            "name": self.name,
+        }
+
+    @property
+    def unique_id(self):
+        """Return the unique id."""
+        return self._unique_id
+
     @property
     def should_poll(self):
         """No polling needed for a demo sensor."""
diff --git a/homeassistant/components/demo/strings.json b/homeassistant/components/demo/strings.json
new file mode 100644
index 00000000000..a2c0103280b
--- /dev/null
+++ b/homeassistant/components/demo/strings.json
@@ -0,0 +1,5 @@
+{
+  "config": {
+    "title": "Demo"
+  }
+}
diff --git a/homeassistant/components/demo/switch.py b/homeassistant/components/demo/switch.py
index 65860867ed6..23006cff875 100644
--- a/homeassistant/components/demo/switch.py
+++ b/homeassistant/components/demo/switch.py
@@ -1,29 +1,52 @@
 """Demo platform that has two fake switches."""
 from homeassistant.components.switch import SwitchDevice
 from homeassistant.const import DEVICE_DEFAULT_NAME
+from . import DOMAIN
 
 
-def setup_platform(hass, config, add_entities_callback, discovery_info=None):
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
     """Set up the demo switches."""
-    add_entities_callback(
+    async_add_entities(
         [
-            DemoSwitch("Decorative Lights", True, None, True),
-            DemoSwitch("AC", False, "mdi:air-conditioner", False),
+            DemoSwitch("swith1", "Decorative Lights", True, None, True),
+            DemoSwitch("swith2", "AC", False, "mdi:air-conditioner", False),
         ]
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoSwitch(SwitchDevice):
     """Representation of a demo switch."""
 
-    def __init__(self, name, state, icon, assumed, device_class=None):
+    def __init__(self, unique_id, name, state, icon, assumed, device_class=None):
         """Initialize the Demo switch."""
+        self._unique_id = unique_id
         self._name = name or DEVICE_DEFAULT_NAME
         self._state = state
         self._icon = icon
         self._assumed = assumed
         self._device_class = device_class
 
+    @property
+    def device_info(self):
+        """Return device info."""
+        return {
+            "identifiers": {
+                # Serial numbers are unique identifiers within a specific domain
+                (DOMAIN, self.unique_id)
+            },
+            "name": self.name,
+        }
+
+    @property
+    def unique_id(self):
+        """Return the unique id."""
+        return self._unique_id
+
     @property
     def should_poll(self):
         """No polling needed for a demo switch."""
diff --git a/homeassistant/components/demo/vacuum.py b/homeassistant/components/demo/vacuum.py
index 2ba704d3925..fb64f17a452 100644
--- a/homeassistant/components/demo/vacuum.py
+++ b/homeassistant/components/demo/vacuum.py
@@ -76,6 +76,11 @@ DEMO_VACUUM_NONE = "4_Fourth_floor"
 DEMO_VACUUM_STATE = "5_Fifth_floor"
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    setup_platform(hass, {}, async_add_entities)
+
+
 def setup_platform(hass, config, add_entities, discovery_info=None):
     """Set up the Demo vacuums."""
     add_entities(
diff --git a/homeassistant/components/demo/water_heater.py b/homeassistant/components/demo/water_heater.py
index d1d53e058c6..c3fff26c992 100644
--- a/homeassistant/components/demo/water_heater.py
+++ b/homeassistant/components/demo/water_heater.py
@@ -13,9 +13,9 @@ SUPPORT_FLAGS_HEATER = (
 )
 
 
-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 the Demo water_heater devices."""
-    add_entities(
+    async_add_entities(
         [
             DemoWaterHeater("Demo Water Heater", 119, TEMP_FAHRENHEIT, False, "eco"),
             DemoWaterHeater("Demo Water Heater Celsius", 45, TEMP_CELSIUS, True, "eco"),
@@ -23,6 +23,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
     )
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    await async_setup_platform(hass, {}, async_add_entities)
+
+
 class DemoWaterHeater(WaterHeaterDevice):
     """Representation of a demo water_heater device."""
 
diff --git a/homeassistant/components/demo/weather.py b/homeassistant/components/demo/weather.py
index 2253f261ad2..8cc1b2f95fd 100644
--- a/homeassistant/components/demo/weather.py
+++ b/homeassistant/components/demo/weather.py
@@ -30,6 +30,11 @@ CONDITION_CLASSES = {
 }
 
 
+async def async_setup_entry(hass, config_entry, async_add_entities):
+    """Set up the Demo config entry."""
+    setup_platform(hass, {}, async_add_entities)
+
+
 def setup_platform(hass, config, add_entities, discovery_info=None):
     """Set up the Demo weather."""
     add_entities(
diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py
index 3c3801b35c6..250d611b602 100644
--- a/tests/components/google_assistant/test_smart_home.py
+++ b/tests/components/google_assistant/test_smart_home.py
@@ -498,6 +498,7 @@ async def test_unavailable_state_doesnt_sync(hass):
 async def test_device_class_switch(hass, device_class, google_type):
     """Test that a cover entity syncs to the correct device type."""
     sensor = DemoSwitch(
+        None,
         "Demo Sensor",
         state=False,
         icon="mdi:switch",
@@ -545,7 +546,9 @@ async def test_device_class_switch(hass, device_class, google_type):
 )
 async def test_device_class_binary_sensor(hass, device_class, google_type):
     """Test that a binary entity syncs to the correct device type."""
-    sensor = DemoBinarySensor("Demo Sensor", state=False, device_class=device_class)
+    sensor = DemoBinarySensor(
+        None, "Demo Sensor", state=False, device_class=device_class
+    )
     sensor.hass = hass
     sensor.entity_id = "binary_sensor.demo_sensor"
     await sensor.async_update_ha_state()
@@ -585,7 +588,7 @@ async def test_device_class_binary_sensor(hass, device_class, google_type):
 )
 async def test_device_class_cover(hass, device_class, google_type):
     """Test that a binary entity syncs to the correct device type."""
-    sensor = DemoCover(hass, "Demo Sensor", device_class=device_class)
+    sensor = DemoCover(None, hass, "Demo Sensor", device_class=device_class)
     sensor.hass = hass
     sensor.entity_id = "cover.demo_sensor"
     await sensor.async_update_ha_state()
diff --git a/tests/components/prometheus/test_init.py b/tests/components/prometheus/test_init.py
index 4ec40731c5d..cf1ff7489f6 100644
--- a/tests/components/prometheus/test_init.py
+++ b/tests/components/prometheus/test_init.py
@@ -24,24 +24,26 @@ async def prometheus_client(loop, hass, hass_client):
         hass, climate.DOMAIN, {"climate": [{"platform": "demo"}]}
     )
 
-    sensor1 = DemoSensor("Television Energy", 74, None, ENERGY_KILO_WATT_HOUR, None)
+    sensor1 = DemoSensor(
+        None, "Television Energy", 74, None, ENERGY_KILO_WATT_HOUR, None
+    )
     sensor1.hass = hass
     sensor1.entity_id = "sensor.television_energy"
     await sensor1.async_update_ha_state()
 
     sensor2 = DemoSensor(
-        "Radio Energy", 14, DEVICE_CLASS_POWER, ENERGY_KILO_WATT_HOUR, None
+        None, "Radio Energy", 14, DEVICE_CLASS_POWER, ENERGY_KILO_WATT_HOUR, None
     )
     sensor2.hass = hass
     sensor2.entity_id = "sensor.radio_energy"
     await sensor2.async_update_ha_state()
 
-    sensor3 = DemoSensor("Electricity price", 0.123, None, "SEK/kWh", None)
+    sensor3 = DemoSensor(None, "Electricity price", 0.123, None, "SEK/kWh", None)
     sensor3.hass = hass
     sensor3.entity_id = "sensor.electricity_price"
     await sensor3.async_update_ha_state()
 
-    sensor4 = DemoSensor("Wind Direction", 25, None, "°", None)
+    sensor4 = DemoSensor(None, "Wind Direction", 25, None, "°", None)
     sensor4.hass = hass
     sensor4.entity_id = "sensor.wind_direction"
     await sensor4.async_update_ha_state()
-- 
GitLab