# What Are Event Types in Home Assistant? Architecture and Usage Guide

> Discover Home Assistant event types how they define and categorize events. Learn about the EventBus architecture and usage for streamlined smart home automation.

- Repository: [Home Assistant/core](https://github.com/home-assistant/core)
- Tags: architecture
- Published: 2026-02-28

---

**Event types are string identifiers that categorize every event flowing through Home Assistant's internal event bus, defined as typed constants in [`homeassistant/const.py`](https://github.com/home-assistant/core/blob/main/homeassistant/const.py) and used by the core `EventBus` class to fire events and register listeners.**

Home Assistant's event-driven architecture relies on a robust publish/subscribe system to coordinate state changes, service calls, and lifecycle hooks across core components and integrations. Understanding how **event types** function is essential for developing custom automations or custom components. This guide examines the implementation details from the `home-assistant/core` repository, covering the generic `EventType` class, the `EventBus` dispatch mechanism, and practical usage patterns for firing and listening to events.

## The EventType Class: Generic Type Safety for Event Strings

At the foundation of Home Assistant's event system lies the **`EventType`** class, defined in [`homeassistant/util/event_type.py`](https://github.com/home-assistant/core/blob/main/homeassistant/util/event_type.py). This is not a simple string constant but a generic subclass of `str` that carries compile-time type information about the event's data payload.

```python

# homeassistant/util/event_type.py

class EventType[_DataT: Mapping[str, Any] = Mapping[str, Any]](str):
    """Custom type for Event.event_type.

    At runtime this is a generic subclass of str.
    """

```

The generic parameter `_DataT` describes the shape of the dictionary passed as `event_data`. While this generic typing is used only by static type checkers like MyPy (at runtime the object behaves exactly like a string), it enables IDE autocomplete and type checking for event handlers. By default, `_DataT` accepts any `Mapping[str, Any]`, but specific event types constrain this to precise `TypedDict` structures.

## Built-in Event Type Constants in homeassistant/const.py

Home Assistant pre-declares all core event identifiers in [`homeassistant/const.py`](https://github.com/home-assistant/core/blob/main/homeassistant/const.py). These constants serve as the contract between the core event bus and integration code, ensuring consistent naming across the codebase.

The repository uses two patterns for these constants:

**Typed constants** using `EventType` with specific payload shapes:

```python

# homeassistant/const.py

EVENT_STATE_CHANGED: EventType[EventStateChangedData] = EventType("state_changed")
EVENT_HOMEASSISTANT_START: EventType[NoEventData] = EventType("homeassistant_start")

```

**Untyped string constants** for events without standardized payloads:

```python

# homeassistant/const.py

EVENT_CALL_SERVICE: Final = "call_service"

```

The typed constants enable static analysis. For example, when listening to `EVENT_STATE_CHANGED`, type checkers know the `event.data` dictionary contains `old_state` and `new_state` keys conforming to the `EventStateChangedData` interface defined in [`homeassistant/helpers/event.py`](https://github.com/home-assistant/core/blob/main/homeassistant/helpers/event.py).

## How the EventBus Uses Event Types for Dispatch

The **`EventBus`** class in [`homeassistant/core.py`](https://github.com/home-assistant/core/blob/main/homeassistant/core.py) (around line 1422) manages the routing of events from publishers to subscribers. It validates event type strings, creates `Event` objects, and maintains the listener registry.

### Firing Events with Type Validation

When firing an event, the bus validates the event type string length before processing:

```python

# homeassistant/core.py (excerpt)

def async_fire(self, event_type, event_data=None, ...):
    """Fire an event. Must be called from the event loop."""
    _verify_event_type_length_or_raise(event_type)          # length guard

    return self.async_fire_internal(event_type, event_data, ...)

```

The `_verify_event_type_length_or_raise` helper ensures event type identifiers do not exceed internal limits. The `async_fire` method then creates an `Event` object containing the `event_type`, `data`, `origin` (whether the event came from local code or a remote source), and timestamp.

### Registering Listeners by Event Type

Components subscribe to specific event types using `async_listen`, which supports optional filtering and execution control:

```python

# homeassistant/core.py (excerpt)

def async_listen(self, event_type, listener, event_filter=None, ...):
    """Register a listener for a specific event type (or MATCH_ALL)."""
    filterable_job = (HassJob(listener, f"listen {event_type}"), event_filter)
    self._listeners[event_type].append(filterable_job)

```

Key features of the listener registration:

- **Specific or global listening**: Pass an `EventType` constant to listen for specific events, or use `MATCH_ALL` to receive every event on the bus.
- **Event filtering**: The optional `event_filter` parameter accepts a callable that inspects the `Event` object; the listener only triggers if the filter returns `True`.
- **Immediate execution**: The `run_immediately` parameter controls whether the listener runs synchronously during event firing or is scheduled as a job.

## Practical Usage: Firing and Listening for Events

### Firing a Built-in Event

Integrations signal system state changes by firing pre-defined event types:

```python
from homeassistant.const import EVENT_HOMEASSISTANT_START

# Inside an async function with access to the hass instance

hass.bus.async_fire(EVENT_HOMEASSISTANT_START)

```

Because `EVENT_HOMEASSISTANT_START` is typed as `EventType[NoEventData]`, static analysis confirms no payload should be passed.

### Listening for Typed Events with Callbacks

To react to state changes, register a listener with the `EVENT_STATE_CHANGED` constant:

```python
from homeassistant.const import EVENT_STATE_CHANGED
from homeassistant.core import HomeAssistant, Event

async def async_state_changed(event: Event) -> None:
    """Callback invoked for every state change."""
    data = event.data          # Typed as EventStateChangedData

    old_state = data["old_state"]
    new_state = data["new_state"]
    # React to the state transition...

hass.bus.async_listen(EVENT_STATE_CHANGED, async_state_changed)

```

The `Event` class stores the `event_type` as a string attribute, while the `data` attribute contains the payload dictionary. Type inference ensures `data` is treated as `EventStateChangedData` when the typed constant is used.

### Creating Custom Event Types

Integrations can define custom event types with typed payloads for internal communication:

```python
from homeassistant.core import HomeAssistant, Event
from homeassistant.util.event_type import EventType
from typing import TypedDict

class MyData(TypedDict):
    value: int
    source: str

MY_EVENT = EventType[MyData]("my_custom_event")

async def handle_my(event: Event[MyData]) -> None:
    print(event.data["value"], event.data["source"])

hass.bus.async_listen(MY_EVENT, handle_my)
hass.bus.async_fire(MY_EVENT, {"value": 42, "source": "sensor"})

```

This pattern provides the same type safety as core events, allowing IDEs to validate that `event.data` contains the expected `value` and `source` keys.

## Summary

- **Event types** are specialized strings (instances of `EventType`) that categorize events in Home Assistant's publish/subscribe system.
- The **`EventType`** generic class in [`homeassistant/util/event_type.py`](https://github.com/home-assistant/core/blob/main/homeassistant/util/event_type.py) enables static type checking of event payloads without runtime overhead.
- Built-in constants like `EVENT_STATE_CHANGED` and `EVENT_HOMEASSISTANT_START` are defined in [`homeassistant/const.py`](https://github.com/home-assistant/core/blob/main/homeassistant/const.py) with specific payload types.
- The **`EventBus`** in [`homeassistant/core.py`](https://github.com/home-assistant/core/blob/main/homeassistant/core.py) validates event type lengths, creates `Event` objects, and dispatches them to registered listeners via `async_fire` and `async_listen`.
- Listeners can filter events by type, apply custom event filters, or use `MATCH_ALL` to receive every event on the bus.

## Frequently Asked Questions

### What is the difference between EventType and a regular string constant?

**`EventType`** is a generic subclass of `str` defined in [`homeassistant/util/event_type.py`](https://github.com/home-assistant/core/blob/main/homeassistant/util/event_type.py) that carries type information about the event's payload structure. While it behaves exactly like a string at runtime, it allows MyPy and IDEs to validate that event listeners handle the correct data shape. Regular string constants (like `EVENT_CALL_SERVICE`) lack this compile-time type safety.

### How do I listen for all events regardless of type?

Pass the special constant **`MATCH_ALL`** (defined in [`homeassistant/core.py`](https://github.com/home-assistant/core/blob/main/homeassistant/core.py)) as the event type when calling `hass.bus.async_listen()`. This registers the listener to receive every event fired on the bus. Be cautious with this approach, as it executes your callback for every state change, service call, and system event, which can impact performance.

### Why does Home Assistant validate event type length?

The **`_verify_event_type_length_or_raise`** function in [`homeassistant/core.py`](https://github.com/home-assistant/core/blob/main/homeassistant/core.py) enforces a maximum length on event type strings to prevent memory issues and ensure consistent behavior in the internal listener registry dictionary. This validation occurs inside `async_fire` before the event object is created.

### Can I use custom event types in my custom integration?

Yes. Create an `EventType` instance with a `TypedDict` defining your payload structure, as shown in the custom event example. Fire it using `hass.bus.async_fire()` and listen for it using `hass.bus.async_listen()`. Using `EventType` rather than plain strings ensures your integration benefits from static type checking and clearly documents the expected event data schema.