Example of an integration that creates and registers a device

I have a very basic question. I am creating a custom component. I have no problem creating entities. However, I am unable to create a device and attach entities to the device. Does anyone have a very simple example of this. For example, creating a device that has two sensors connected to it. I’m surprised but I can’t find a simple example anywhere. I must be very bad at searching as this seems so basic. Thank you very much.

Here is my code so far - so the question is how do I create a device that will contain the sensor, and appear in the UI as a device…

__init__.py:
"""Example Load Platform integration."""
from __future__ import annotations

from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType

DOMAIN = 'example_load_platform'

def setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Your controller/hub specific code."""
    # Data that you want to share with your platforms
    hass.data[DOMAIN] = {
        'temperature': 23
    }

    hass.helpers.discovery.load_platform('sensor', DOMAIN, {}, config)

    return True


sensor.py

"""Platform for sensor integration."""
from __future__ import annotations

from homeassistant.components.sensor import SensorEntity
from homeassistant.const import TEMP_CELSIUS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.entity import generate_entity_id

from . import DOMAIN

import logging
LOGGER: logging.Logger = logging.getLogger(DOMAIN)
LOGGER.setLevel(logging.INFO)
LOGGER.warn('Loading, logging has been initiated')

def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None
) -> None:
    """Set up the sensor platform."""
    LOGGER.info('Setting up the Sensor Platform')

    # We only want this platform to be set up via discovery.
    if discovery_info is None:
        return
    add_entities([ExampleSensor()])
    LOGGER.info('Entities have been added')

class ExampleSensor(SensorEntity):
    """Representation of a sensor."""

    def __init__(self) -> None:
        """Initialize the sensor."""
        self._state = None

    @property
    def name(self) -> str:
        """Return the name of the sensor."""
        return 'Example Temperature Sensor'

    @property
    def state(self):
        """Return the state of the sensor."""
        return self._state

    @property
    def unit_of_measurement(self) -> str:
        """Return the unit of measurement."""
        return TEMP_CELSIUS

    def update(self) -> None:
        """Fetch new state data for the sensor.

        This is the only method that should fetch new data for Home Assistant.
        """
        self._state = self.hass.data[DOMAIN]['temperature']

Have a look at other integrations?

It’s been a while since I wrote mine for my PelletStove

Or pick any other official integration → core/homeassistant/components at dev · home-assistant/core · GitHub

Have a look at this example integration i created. See device_info in sensor.py

Thank you - I have looked at code and examples but nothing I have tried has actually created the device. I see in your code that you are creating entities but not a containing device. My question is not about creating the entities, which my code does correctly, but adding the entities to a new device that I also want to create.

Does this help Device registry | Home Assistant Developer Docs

Thank you - it does help, but still code is not working: The entity for the sensor is created, but not the device. The device_info method for the sensor is never called.

__init__.py:

"""Example Load Platform integration."""
from __future__ import annotations

from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.discovery import load_platform
from homeassistant.config_entries import ConfigEntry

DOMAIN = 'example_load_platform_v2'

import logging
LOGGER: logging.Logger = logging.getLogger(DOMAIN)
LOGGER.setLevel(logging.INFO)
LOGGER.warn('Loading, logging has been initiated')

async def async_setup(hass: HomeAssistant, config: dict) -> bool:
    """Set up the custom component from configuration.yaml."""

    LOGGER.info('Setting up the SensorV2 platform from init...')

    load_platform(hass, 'sensor', DOMAIN, {}, config)

    # Data that you want to share with you__uir platforms
    hass.data[DOMAIN] = {
        'temperature': 23
    }

    return True

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up custom component from a config entry."""

    LOGGER.info('Setting up the entry from init...')

    hass.data[DOMAIN][entry.entry_id] = entry.data
    hass.async_create_task(
        hass.config_entries.async_forward_entry_setup(entry, "sensor")
    )

    return True

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload a config entry."""

    LOGGER.info('Unloading the entry from init')

    await hass.config_entries.async_forward_entry_unload(entry, "sensor")
    hass.data[DOMAIN].pop(entry.entry_id)

    return True

sensor.py:

"""Platform for sensor integration."""
from __future__ import annotations

from homeassistant.components.sensor import SensorEntity
from homeassistant.const import UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.device_registry import DeviceInfo

from . import DOMAIN

import logging
LOGGER: logging.Logger = logging.getLogger(DOMAIN)
LOGGER.setLevel(logging.INFO)
LOGGER.warn('Loading, logging has been initiated')

async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: Optgional[DiscoveryInfoType] = None
) -> None:
    """Set up the sensor platform."""
    LOGGER.info('Setting up the SensorV2 Platform...')

    async_add_entities([ExampleSensorV2()])

class ExampleSensorV2(SensorEntity):
    """Representation of a sensor."""

    def __init__(self) -> None:
        """Initialize the sensor."""
        LOGGER.warn('Running init for the entity...')
        self._state = None
        self._entry_id = "foobarbaz"

    @property
    def name(self) -> str:
        """Return the name of the sensor."""
        LOGGER.warn('Generating sensor name...')
        return 'Example Temperature Sensor V2'

    @property
    def unique_id(self) -> str:
        """Return a unique ID for the sensor."""
        LOGGER.warn('Generating sensor id...')
        return "foobarbaz123"

    @property
    def state(self):
        """Return the state of the sensor."""
        return self._state

    @property
    def device_info(self) -> DeviceInfo:
        """Return device information."""
        LOGGER.warn('Generating device info...')
        return DeviceInfo(
            name="Example Sensor Device V2",
            manufacturer="Example Sensor Manufacturer V2",
            model="Example Sensor Model V2",
            identifiers={
                (DOMAIN, self._entry_id)
            },
        )

    def update(self) -> None:
        """Fetch new state data for the sensor.

        This is the only method that should fetch new data for Home Assistant.
        """
        LOGGER.warn('Fetching new data...')

        self._state = self.hass.data[DOMAIN]['temperature']

Are you using config.yaml to set it up or a config flow? It will only create devices if using config flow. See the tip box in the link from @nickrout under Defining Devices.

1 Like

Ah - that was the issue!!! Thank you. I was using yaml not config flow. Making that change solved the problem. Thank you very much.

1 Like

Wait so does this mean that it is completely impossible for an integration to create a device if the integration is set up via config.yaml?
I understand the push towards config flows but to completely deprecate this function from the config.yaml is a bad idea. Why not allow for advanced users to keep using the config.yaml?

If developing, look at the MQTT integration, it is the only integration I know that can create devices in yaml.

1 Like

AFAIK devices have never been able to be create devices (except mqtt as @francisp says)