Skip to main content
The binary sensor platform represents entities with two states: on/off, detected/not detected, open/closed, etc. Binary sensors are used for things like motion detection, door/window sensors, and occupancy detection.

Base Class

All binary sensor entities inherit from BinarySensorEntity, which is defined in homeassistant.components.binary_sensor.
from homeassistant.components.binary_sensor import BinarySensorEntity

class MyBinarySensor(BinarySensorEntity):
    """Representation of a binary sensor."""

Entity Description

Binary sensor entities can optionally use BinarySensorEntityDescription to define static metadata:
from homeassistant.components.binary_sensor import (
    BinarySensorEntity,
    BinarySensorEntityDescription,
    BinarySensorDeviceClass,
)

BINARY_SENSOR_TYPES = [
    BinarySensorEntityDescription(
        key="motion",
        name="Motion",
        device_class=BinarySensorDeviceClass.MOTION,
    ),
]

Required Properties

is_on

Returns True if the binary sensor is on, False if off, or None if unknown.
@cached_property
def is_on(self) -> bool | None:
    """Return true if the binary sensor is on."""
    return self._attr_is_on

Optional Properties

device_class

Indicates the type of binary sensor. This affects the icon displayed in the UI and what the on/off states represent.
@cached_property
def device_class(self) -> BinarySensorDeviceClass | None:
    """Return the class of this entity."""
    return self._attr_device_class
Common device classes:
  • BATTERY - On = low, Off = normal
  • BATTERY_CHARGING - On = charging, Off = not charging
  • DOOR - On = open, Off = closed
  • GARAGE_DOOR - On = open, Off = closed
  • MOTION - On = motion detected, Off = no motion
  • OCCUPANCY - On = occupied, Off = not occupied
  • OPENING - On = open, Off = closed
  • PLUG - On = plugged in, Off = unplugged
  • PRESENCE - On = home, Off = away
  • PROBLEM - On = problem detected, Off = OK
  • SMOKE - On = smoke detected, Off = clear
  • WINDOW - On = open, Off = closed

State Property

The state property is final and automatically returns “on” or “off” based on is_on:
@final
@property
def state(self) -> Literal["on", "off"] | None:
    """Return the state of the binary sensor."""
    if (is_on := self.is_on) is None:
        return None
    return STATE_ON if is_on else STATE_OFF

Example Implementation

from homeassistant.components.binary_sensor import (
    BinarySensorEntity,
    BinarySensorDeviceClass,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

class MotionSensor(BinarySensorEntity):
    """Representation of a motion sensor."""

    _attr_device_class = BinarySensorDeviceClass.MOTION

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

    async def async_update(self) -> None:
        """Fetch new state data for the sensor."""
        # Fetch data from your device/API
        self._attr_is_on = await self._check_motion()

    async def _check_motion(self) -> bool:
        """Check if motion is detected."""
        # Your implementation here
        return False

Push vs. Poll

For binary sensors that receive push updates (like webhooks or event-based systems):
class PushBinarySensor(BinarySensorEntity):
    """Binary sensor that receives push updates."""

    _attr_should_poll = False

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

    async def async_added_to_hass(self) -> None:
        """Subscribe to updates when added to Home Assistant."""
        await super().async_added_to_hass()
        # Subscribe to your device's events
        self._unsubscribe = await self._device.subscribe(self._handle_event)

    async def async_will_remove_from_hass(self) -> None:
        """Unsubscribe when removed."""
        if self._unsubscribe:
            self._unsubscribe()

    def _handle_event(self, event) -> None:
        """Handle event from device."""
        self._attr_is_on = event.state
        self.async_write_ha_state()

Real-World Example

From the demo integration (homeassistant/components/demo/binary_sensor.py):
class DemoBinarySensor(BinarySensorEntity):
    """Representation of a Demo binary sensor."""

    _attr_has_entity_name = True
    _attr_name = None
    _attr_should_poll = False

    def __init__(
        self,
        unique_id: str,
        device_name: str,
        state: bool,
        device_class: BinarySensorDeviceClass,
    ) -> None:
        """Initialize the demo sensor."""
        self._unique_id = unique_id
        self._state = state
        self._attr_device_class = device_class
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, self.unique_id)},
            name=device_name,
        )

    @property
    def unique_id(self) -> str:
        """Return the unique id."""
        return self._unique_id

    @property
    def is_on(self) -> bool:
        """Return true if the binary sensor is on."""
        return self._state

Entity Attributes

Binary sensors typically don’t have additional state attributes, but you can add them if needed:
@property
def extra_state_attributes(self) -> dict[str, Any]:
    """Return additional attributes."""
    return {
        "last_triggered": self._last_triggered,
        "sensitivity": self._sensitivity,
    }

Important Notes

  • Binary sensors should set _attr_is_on rather than implementing state directly
  • The state property is final and automatically converts is_on to “on”/“off”
  • Set _attr_should_poll = False for sensors that receive push updates
  • Binary sensors with EntityCategory.CONFIG cannot be added (will raise HomeAssistantError)
  • Always set a device_class to get the appropriate icon and on/off semantics

Device Class Meanings

Each device class has specific semantics for what “on” and “off” mean:
Device ClassOn StateOff State
batteryLowNormal
battery_chargingChargingNot charging
carbon_monoxideDetectedClear
doorOpenClosed
garage_doorOpenClosed
gasDetectedClear
moistureWetDry
motionDetectedClear
occupancyOccupiedClear
openingOpenClosed
plugPlugged inUnplugged
powerDetectedNo power
presenceHomeAway
problemProblemOK
smokeDetectedClear
windowOpenClosed

See Also

Build docs developers (and LLMs) love