Skip to main content
The switch platform represents entities that can be turned on or off. Switches are used for outlets, wall switches, and other binary controllable devices.

Base Class

All switch entities inherit from SwitchEntity, which extends ToggleEntity. The class is defined in homeassistant.components.switch.
from homeassistant.components.switch import SwitchEntity

class MySwitch(SwitchEntity):
    """Representation of a switch."""

Entity Description

Switch entities can optionally use SwitchEntityDescription to define static metadata:
from homeassistant.components.switch import (
    SwitchEntity,
    SwitchEntityDescription,
    SwitchDeviceClass,
)

SWITCH_TYPES = [
    SwitchEntityDescription(
        key="outlet",
        name="Power Outlet",
        device_class=SwitchDeviceClass.OUTLET,
    ),
]

Required Methods

turn_on

Turn the switch on.
def turn_on(self, **kwargs: Any) -> None:
    """Turn the switch on."""
    # Your implementation here

turn_off

Turn the switch off.
def turn_off(self, **kwargs: Any) -> None:
    """Turn the switch off."""
    # Your implementation here

Optional Properties

is_on

Return the current state of the switch.
@property
def is_on(self) -> bool | None:
    """Return true if switch is on."""
    return self._attr_is_on

device_class

Indicates the type of switch.
@cached_property
def device_class(self) -> SwitchDeviceClass | None:
    """Return the class of this entity."""
    return self._attr_device_class
Available device classes:
  • OUTLET - Power outlet
  • SWITCH - Generic switch

Services

The switch platform automatically registers these services:
  • switch.turn_on - Turn the switch on
  • switch.turn_off - Turn the switch off
  • switch.toggle - Toggle the switch state

Example Implementation

from typing import Any

from homeassistant.components.switch import (
    SwitchEntity,
    SwitchDeviceClass,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

class PowerSwitch(SwitchEntity):
    """Representation of a power switch."""

    _attr_device_class = SwitchDeviceClass.OUTLET

    def __init__(self, name: str, device_id: str) -> None:
        """Initialize the switch."""
        self._attr_name = name
        self._attr_unique_id = f"{device_id}_switch"
        self._attr_is_on = False

    def turn_on(self, **kwargs: Any) -> None:
        """Turn the switch on."""
        # Send command to device
        self._device.set_power(True)
        self._attr_is_on = True
        self.schedule_update_ha_state()

    def turn_off(self, **kwargs: Any) -> None:
        """Turn the switch off."""
        # Send command to device
        self._device.set_power(False)
        self._attr_is_on = False
        self.schedule_update_ha_state()

Async Implementation

For async devices, override the async methods:
class AsyncSwitch(SwitchEntity):
    """Async switch implementation."""

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the switch on asynchronously."""
        await self._device.async_set_power(True)
        self._attr_is_on = True
        self.async_write_ha_state()

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the switch off asynchronously."""
        await self._device.async_set_power(False)
        self._attr_is_on = False
        self.async_write_ha_state()

Real-World Example

From the demo integration (homeassistant/components/demo/switch.py):
class DemoSwitch(SwitchEntity):
    """Representation of a demo switch."""

    _attr_has_entity_name = True
    _attr_name = None
    _attr_should_poll = False

    def __init__(
        self,
        unique_id: str,
        device_name: str,
        state: bool,
        assumed: bool,
        translation_key: str | None = None,
        device_class: SwitchDeviceClass | None = None,
    ) -> None:
        """Initialize the Demo switch."""
        self._attr_assumed_state = assumed
        self._attr_device_class = device_class
        self._attr_translation_key = translation_key
        self._attr_is_on = state
        self._attr_unique_id = unique_id
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, unique_id)},
            name=device_name,
        )

    def turn_on(self, **kwargs: Any) -> None:
        """Turn the switch on."""
        self._attr_is_on = True
        self.schedule_update_ha_state()

    def turn_off(self, **kwargs: Any) -> None:
        """Turn the device off."""
        self._attr_is_on = False
        self.schedule_update_ha_state()

Toggle Support

Switches automatically support toggling via the inherited ToggleEntity base class:
# The toggle method is automatically available
async def async_toggle(self, **kwargs: Any) -> None:
    """Toggle the entity."""
    if self.is_on:
        await self.async_turn_off(**kwargs)
    else:
        await self.async_turn_on(**kwargs)

Assumed State

For devices where the state cannot be read:
class UnconfirmedSwitch(SwitchEntity):
    """Switch with assumed state."""

    _attr_assumed_state = True

    def turn_on(self, **kwargs: Any) -> None:
        """Turn on (send command but can't confirm)."""
        self._device.send_on_command()
        self._attr_is_on = True  # Assume it worked
        self.schedule_update_ha_state()

Optimistic Updates

For fast UI response while waiting for device confirmation:
class OptimisticSwitch(SwitchEntity):
    """Switch with optimistic updates."""

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the switch on."""
        # Update state immediately for UI
        self._attr_is_on = True
        self.async_write_ha_state()
        
        # Send command to device
        await self._device.async_set_power(True)
        
        # Refresh state from device to confirm
        await self.async_update()

Important Notes

  • Always call schedule_update_ha_state() (sync) or async_write_ha_state() (async) after changing state
  • Switches inherit from ToggleEntity, which provides the toggle functionality
  • Set _attr_should_poll = False for switches that receive push updates
  • Use _attr_assumed_state = True when you can’t read back the actual state
  • The turn_on and turn_off methods should update _attr_is_on to reflect the new state

State Management

Switches can manage state in different ways:

Polling (Default)

class PollingSwitch(SwitchEntity):
    """Switch that polls for state."""

    async def async_update(self) -> None:
        """Fetch state from device."""
        self._attr_is_on = await self._device.get_state()

Push Updates

class PushSwitch(SwitchEntity):
    """Switch that receives push updates."""

    _attr_should_poll = False

    async def async_added_to_hass(self) -> None:
        """Subscribe to updates."""
        await super().async_added_to_hass()
        self._device.subscribe(self._handle_update)

    def _handle_update(self, state: bool) -> None:
        """Handle state update from device."""
        self._attr_is_on = state
        self.async_write_ha_state()

See Also

Build docs developers (and LLMs) love