Understanding Home Assistant’s event-driven architecture and event bus communication
The Event System is the backbone of Home Assistant’s architecture, enabling loose coupling between components through an event-driven communication model. All significant actions in Home Assistant trigger events that other components can listen to and react upon.
The EventBus manages event distribution across the entire system:
homeassistant/core.py
class EventBus: """Allow the firing of and listening for events.""" def __init__(self, hass: HomeAssistant) -> None: """Initialize a new event bus.""" self._listeners: defaultdict[ EventType[Any] | str, list[_FilterableJobType[Any]] ] = defaultdict(list) self._match_all_listeners: list[_FilterableJobType[Any]] = [] self._listeners[MATCH_ALL] = self._match_all_listeners self._hass = hass
The EventBus uses a dictionary to map event types to listener lists, enabling O(1) lookup for event dispatch. The MATCH_ALL special key allows listeners to receive all events.
from homeassistant.core import callback, Event@callbackdef handle_my_event(event: Event) -> None: """Handle my custom event.""" data = event.data _LOGGER.info("Received event with data: %s", data)# Register the listenerremove_listener = hass.bus.async_listen( "my_custom_event", handle_my_event)# Later, to unregister:remove_listener()
Always decorate event listeners with @callback to indicate they’re safe to run in the event loop without creating a task. This improves performance significantly.
State changes trigger the most common events in Home Assistant:
homeassistant/core.py
class EventStateChangedData(EventStateEventData): """EVENT_STATE_CHANGED data. A state changed event is fired when on state write the state is changed. """ entity_id: str new_state: State | None old_state: State | None
For better performance when tracking specific entities, use the event helpers:
from homeassistant.helpers.event import async_track_state_change_event@callbackdef handle_light_change(event: Event[EventStateChangedData]) -> None: """Handle changes to specific light.""" new_state = event.data["new_state"] if new_state and new_state.state == "on": _LOGGER.info("Light turned on")# This is much faster than listening to all state changesremove_listener = async_track_state_change_event( hass, ["light.living_room", "light.bedroom"], handle_light_change)
async_track_state_change_event uses an optimized index that routes events directly to listeners interested in specific entities, avoiding the need to process all state change events.
Listen to every event (use sparingly for debugging/monitoring):
from homeassistant.const import MATCH_ALL@callbackdef log_all_events(event: Event) -> None: """Log every event for debugging.""" _LOGGER.debug( "Event: %s - %s", event.event_type, event.data )remove_listener = hass.bus.async_listen(MATCH_ALL, log_all_events)
MATCH_ALL listeners receive every single event, which can significantly impact performance. Use only for temporary debugging or specialized monitoring tools.
# Check who triggered an event@callbackdef handle_light_change(event: Event[EventStateChangedData]) -> None: if event.context.user_id: user = await hass.auth.async_get_user(event.context.user_id) _LOGGER.info("Light changed by user: %s", user.name) else: _LOGGER.info("Light changed by automation or integration")# Propagate context in automation chainscontext = Context(parent_id=trigger_event.context.id)hass.bus.async_fire("my_event", {}, context=context)
Events can originate locally or from remote systems:
homeassistant/core.py
class EventOrigin(enum.Enum): """Represent the origin of an event.""" local = "LOCAL" # Event originated on this Home Assistant instance remote = "REMOTE" # Event came from a remote instance
This is primarily used in multi-instance setups or when integrating with external systems.
Use @callback: Always decorate listeners with @callback when possible Specific Events: Listen to specific events rather than MATCH_ALL Event Filters: Use filters to reduce unnecessary job creation Fast Execution: Keep listener code fast; offload heavy work to tasks