Skip to main content

Entity Platforms

Entity platforms are the core building blocks that represent controllable or observable elements in Home Assistant. This guide covers implementing different entity types for your integration.

Platform Architecture

Platforms are separate Python modules within your integration, one for each entity type you support:
homeassistant/components/your_integration/
├── __init__.py
├── manifest.json
├── light.py          # Light platform
├── sensor.py         # Sensor platform
├── switch.py         # Switch platform
└── climate.py        # Climate platform

Available Platforms

From homeassistant.const.Platform:
  • light - Lights
  • switch - Switches
  • sensor - Sensors
  • binary_sensor - Binary sensors
  • climate - Climate devices (thermostats)
  • cover - Covers (blinds, garage doors)
  • fan - Fans
  • lock - Locks
  • media_player - Media players
  • camera - Cameras
  • vacuum - Vacuum cleaners
  • water_heater - Water heaters
  • And many more…

Basic Platform Structure

Every platform follows the same pattern:
"""Platform implementation."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the platform from a config entry."""
    # Get data from main integration
    # Create entities
    # Add entities to Home Assistant
    pass

Entity Base Class

All entities inherit from Entity (from homeassistant/helpers/entity.py):
from homeassistant.helpers.entity import Entity

class MyEntity(Entity):
    """Representation of my entity."""
    
    _attr_has_entity_name = True
    _attr_name = None
    
    def __init__(self) -> None:
        """Initialize the entity."""
        self._attr_unique_id = "unique_id_here"
Using _attr_* attributes is the modern approach. They’re automatically exposed as properties.

Light Platform

Basic Light Implementation

light.py
"""Light platform for My Integration."""
from __future__ import annotations

from typing import Any

from homeassistant.components.light import (
    ATTR_BRIGHTNESS,
    ColorMode,
    LightEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import DOMAIN


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up light platform."""
    # Get stored data
    data = hass.data[DOMAIN][entry.entry_id]
    
    # Create light entities
    async_add_entities([
        MyLight("Living Room", "light_1"),
        MyLight("Bedroom", "light_2"),
    ])


class MyLight(LightEntity):
    """Representation of a Light."""
    
    _attr_has_entity_name = True
    _attr_color_mode = ColorMode.BRIGHTNESS
    _attr_supported_color_modes = {ColorMode.BRIGHTNESS}
    
    def __init__(self, name: str, unique_id: str) -> None:
        """Initialize the light."""
        self._attr_name = name
        self._attr_unique_id = unique_id
        self._attr_is_on = False
        self._attr_brightness = 255
    
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the light on."""
        if ATTR_BRIGHTNESS in kwargs:
            self._attr_brightness = kwargs[ATTR_BRIGHTNESS]
        
        self._attr_is_on = True
        self.async_write_ha_state()
    
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the light off."""
        self._attr_is_on = False
        self.async_write_ha_state()

Color Support

For RGB/RGBW lights:
from homeassistant.components.light import (
    ATTR_HS_COLOR,
    ATTR_RGBW_COLOR,
    ColorMode,
    LightEntity,
)

class MyRGBLight(LightEntity):
    """RGB light."""
    
    _attr_color_mode = ColorMode.HS
    _attr_supported_color_modes = {ColorMode.HS}
    
    def __init__(self) -> None:
        """Initialize."""
        self._attr_hs_color = (0, 0)
    
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn on."""
        if ATTR_HS_COLOR in kwargs:
            self._attr_hs_color = kwargs[ATTR_HS_COLOR]
        
        self._attr_is_on = True
        self.async_write_ha_state()
Real example from Demo integration (homeassistant/components/demo/light.py:94):
class DemoLight(LightEntity):
    """Representation of a demo light."""
    
    _attr_has_entity_name = True
    _attr_name = None
    _attr_should_poll = False

Sensor Platform

Basic Sensor

sensor.py
"""Sensor platform for My Integration."""
from __future__ import annotations

from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import DOMAIN


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up sensor platform."""
    async_add_entities([
        MyTemperatureSensor("Living Room", "temp_1"),
        MyHumiditySensor("Living Room", "humidity_1"),
    ])


class MyTemperatureSensor(SensorEntity):
    """Temperature sensor."""
    
    _attr_has_entity_name = True
    _attr_device_class = SensorDeviceClass.TEMPERATURE
    _attr_state_class = SensorStateClass.MEASUREMENT
    _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
    
    def __init__(self, name: str, unique_id: str) -> None:
        """Initialize the sensor."""
        self._attr_name = name
        self._attr_unique_id = unique_id
        self._attr_native_value = None
    
    async def async_update(self) -> None:
        """Fetch new state data."""
        # Fetch from API
        self._attr_native_value = 22.5
Device Class: Indicates the type of sensor (temperature, humidity, etc.)State Class: For statistics and long-term data (measurement, total, total_increasing)Native Value: The sensor’s value in its native unit

Statistics and Units

class MyEnergySensor(SensorEntity):
    """Energy sensor with statistics."""
    
    _attr_device_class = SensorDeviceClass.ENERGY
    _attr_state_class = SensorStateClass.TOTAL_INCREASING
    _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
    _attr_suggested_display_precision = 2
Home Assistant automatically handles:
  • Unit conversion
  • Statistics collection
  • Long-term storage
  • Graphing

Switch Platform

switch.py
"""Switch platform for My Integration."""
from __future__ import annotations

from typing import Any

from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import DOMAIN


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up switch platform."""
    async_add_entities([
        MySwitch("Outlet 1", "switch_1"),
    ])


class MySwitch(SwitchEntity):
    """Representation of a Switch."""
    
    _attr_has_entity_name = True
    
    def __init__(self, name: str, unique_id: str) -> None:
        """Initialize the switch."""
        self._attr_name = name
        self._attr_unique_id = unique_id
        self._attr_is_on = False
    
    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the switch on."""
        self._attr_is_on = True
        self.async_write_ha_state()
    
    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the switch off."""
        self._attr_is_on = False
        self.async_write_ha_state()

Binary Sensor Platform

binary_sensor.py
"""Binary sensor platform for My Integration."""
from __future__ import annotations

from homeassistant.components.binary_sensor import (
    BinarySensorDeviceClass,
    BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import DOMAIN


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up binary sensor platform."""
    async_add_entities([
        MyMotionSensor("Living Room", "motion_1"),
        MyDoorSensor("Front Door", "door_1"),
    ])


class MyMotionSensor(BinarySensorEntity):
    """Motion sensor."""
    
    _attr_has_entity_name = True
    _attr_device_class = BinarySensorDeviceClass.MOTION
    
    def __init__(self, name: str, unique_id: str) -> None:
        """Initialize the sensor."""
        self._attr_name = name
        self._attr_unique_id = unique_id
        self._attr_is_on = False
    
    async def async_update(self) -> None:
        """Update the sensor."""
        # Fetch from API
        self._attr_is_on = False  # No motion


class MyDoorSensor(BinarySensorEntity):
    """Door sensor."""
    
    _attr_has_entity_name = True
    _attr_device_class = BinarySensorDeviceClass.DOOR
    
    def __init__(self, name: str, unique_id: str) -> None:
        """Initialize the sensor."""
        self._attr_name = name
        self._attr_unique_id = unique_id
        self._attr_is_on = False  # Closed

Climate Platform

climate.py
"""Climate platform for My Integration."""
from __future__ import annotations

from typing import Any

from homeassistant.components.climate import (
    ATTR_TEMPERATURE,
    ClimateEntity,
    ClimateEntityFeature,
    HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import DOMAIN


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up climate platform."""
    async_add_entities([
        MyThermostat("Living Room", "thermostat_1"),
    ])


class MyThermostat(ClimateEntity):
    """Thermostat entity."""
    
    _attr_has_entity_name = True
    _attr_temperature_unit = UnitOfTemperature.CELSIUS
    _attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT, HVACMode.COOL, HVACMode.AUTO]
    _attr_supported_features = (
        ClimateEntityFeature.TARGET_TEMPERATURE
        | ClimateEntityFeature.TURN_ON
        | ClimateEntityFeature.TURN_OFF
    )
    
    def __init__(self, name: str, unique_id: str) -> None:
        """Initialize the thermostat."""
        self._attr_name = name
        self._attr_unique_id = unique_id
        self._attr_hvac_mode = HVACMode.OFF
        self._attr_current_temperature = 20.0
        self._attr_target_temperature = 22.0
    
    async def async_set_temperature(self, **kwargs: Any) -> None:
        """Set new target temperature."""
        if (temperature := kwargs.get(ATTR_TEMPERATURE)) is not None:
            self._attr_target_temperature = temperature
            self.async_write_ha_state()
    
    async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
        """Set new target hvac mode."""
        self._attr_hvac_mode = hvac_mode
        self.async_write_ha_state()

Entity Features

Unique ID

Every entity should have a unique ID:
def __init__(self) -> None:
    """Initialize."""
    self._attr_unique_id = f"{device_id}_{entity_type}"
Unique IDs must be stable across restarts and unique across all integrations.

Device Info

Group entities under devices:
from homeassistant.helpers.device_registry import DeviceInfo

class MyEntity(SensorEntity):
    """Entity with device info."""
    
    def __init__(self) -> None:
        """Initialize."""
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, "device_id")},
            name="My Device",
            manufacturer="My Company",
            model="Model X",
            sw_version="1.0.0",
        )

Availability

Indicate when entities are unavailable:
async def async_update(self) -> None:
    """Update the entity."""
    try:
        data = await self.api.get_data()
        self._attr_native_value = data
        self._attr_available = True
    except ConnectionError:
        self._attr_available = False

Entity Category

Categorize entities:
from homeassistant.const import EntityCategory

class MyDiagnosticSensor(SensorEntity):
    """Diagnostic sensor."""
    
    _attr_entity_category = EntityCategory.DIAGNOSTIC
Categories:
  • CONFIG - Configuration entities
  • DIAGNOSTIC - Diagnostic information
  • None - Regular entities (default)

State Updates

1
Push Updates
2
For entities that receive push updates:
3
class MyPushEntity(SensorEntity):
    """Entity with push updates."""
    
    _attr_should_poll = False
    
    async def async_added_to_hass(self) -> None:
        """Register callbacks."""
        self.api.register_callback(self._handle_update)
    
    def _handle_update(self, data: Any) -> None:
        """Handle push update."""
        self._attr_native_value = data
        self.async_write_ha_state()
4
Polling Updates
5
For entities that need polling:
6
class MyPollingEntity(SensorEntity):
    """Entity with polling."""
    
    _attr_should_poll = True
    
    async def async_update(self) -> None:
        """Poll for updates."""
        self._attr_native_value = await self.api.get_value()

Platform Setup Methods

From homeassistant/helpers/entity_platform.py:96:
class EntityPlatformModule(Protocol):
    """Protocol type for entity platform modules."""
    
    async def async_setup_platform(
        self,
        hass: HomeAssistant,
        config: ConfigType,
        async_add_entities: AddEntitiesCallback,
        discovery_info: DiscoveryInfoType | None = None,
    ) -> None:
        """Set up an integration platform async."""
    
    async def async_setup_entry(
        self,
        hass: HomeAssistant,
        entry: ConfigEntry,
        async_add_entities: AddConfigEntryEntitiesCallback,
    ) -> None:
        """Set up an integration platform from a config entry."""
Use async_setup_entry for modern config-entry-based integrations.Use async_setup_platform only for legacy YAML-based platforms.

Best Practices

Use Async Methods

# Good
async def async_turn_on(self) -> None:
    await self.api.turn_on()

# Bad
def turn_on(self) -> None:
    self.api.turn_on()  # Blocking!

Batch Entity Creation

async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up platform."""
    # Good - add all at once
    entities = [
        MyEntity(device)
        for device in await api.get_devices()
    ]
    async_add_entities(entities)
    
    # Bad - multiple calls
    for device in await api.get_devices():
        async_add_entities([MyEntity(device)])

Handle Missing Data

@property
def native_value(self) -> float | None:
    """Return the value."""
    if self._value is None:
        return None
    return float(self._value)

Next Steps

Integration Overview

Understand the full integration architecture

Config Flow

Add UI configuration to your integration

Build docs developers (and LLMs) love