The event bus
How the event bus works, how to fire your own events, and how to listen for events without leaking listeners.
This page explains how the event bus works underneath. To fire events from your integration, or to react to what happens in Home Assistant with the event helpers, see events in integrations.
Home Assistant is driven by events. A state changes: that is an event. An action is called: event. Home Assistant starts or stops: event. The event bus on hass.bus carries all of them, and any code can listen.
In practice, you rarely listen to the raw bus. Home Assistant has helpers for the common cases, like tracking a state change or running something every minute. Use those when they fit. This page explains what they are built on.
An event is an Event object from homeassistant.core with:
event_type: a string, at most 64 characters. Custom events are prefixed with the domain of your integration, likemy_integration_button_pressed.data: a dictionary. Keep it JSON serializable, because events are stored by the recorder and sent over the WebSocket API.context: theContextthat caused the event. It links the event to the user, automation, or action that started it.origin:EventOrigin.localfor events from this instance,EventOrigin.remotefor events that came in from outside, like through the REST API.time_fired: when the event was fired.
Home Assistant itself fires events like state_changed, call_service, homeassistant_start, homeassistant_started, and homeassistant_stop. The constants live in homeassistant/const.py, like EVENT_STATE_CHANGED. The event reference on home-assistant.io lists the events users see.
Events are recorded in the database. Think about the size and shape of your event data before you fire events often. The data science portal explains how events are stored.
From the event loop, use async_fire:
from homeassistant.core import HomeAssistant
DOMAIN = "my_integration"
async def async_press(hass: HomeAssistant, button_id: str) -> None:
"""Tell the rest of Home Assistant a button was pressed."""
hass.bus.async_fire(
f"{DOMAIN}_button_pressed",
{"button_id": button_id},
)From another thread, use hass.bus.fire instead. It schedules the event on the event loop for you. See thread safety.
When you fire an event because a user did something, pass their context, so the logbook and the permissions system can attribute it. Inside an action handler, that is call.context.
Integrations that represent a physical button or remote often want an event entity instead of a raw event. See make device events available.
async_listen registers a listener and returns a function that removes it. Always keep that function and call it when you are done, or your listener lives forever.
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import Event, HomeAssistant, callback
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry
) -> bool:
"""Set up my integration."""
@callback
def handle_stop(event: Event) -> None:
"""Close the connection when Home Assistant stops."""
entry.runtime_data.close()
entry.async_on_unload(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, handle_stop)
)
return Trueentry.async_on_unload calls the remove function when the config entry is unloaded. In an entity, use self.async_on_remove the same way.
A few things to know:
- How your listener runs depends on what it is. A function decorated with
@callbackruns directly in the event loop. A coroutine function runs as a task in the event loop. A plain function without@callbackruns in a thread of the executor. Decorate small, non-blocking listeners with@callback: it is the fastest option. - Listen to one event type. You can pass
MATCH_ALLto get every event, but that is expensive and rarely what you want. - Filter early.
async_listentakes an optionalevent_filter: a@callbackfunction that gets the event data and returnsTrueif the listener should run. It runs before your listener is scheduled, which saves work on a busy bus. async_listen_onceremoves the listener after the first event.
To type the event data, core uses EventType from homeassistant.util.event_type. EVENT_STATE_CHANGED is an EventType[EventStateChangedData], so a listener gets Event[EventStateChangedData] and a type checker knows what is in event.data.
homeassistant.helpers.event builds on async_listen with helpers for what you usually want, like following the state of a few entities or running something at a time of day. They filter for you, and return a function to remove the listener, just like async_listen. The full list, with examples, is on events in integrations.
Events go over the bus, get recorded, and can be seen by anyone, including automations and the WebSocket API. For signals that only matter inside your integration, like "new data arrived for device 12", use the dispatcher in homeassistant.helpers.dispatcher instead. async_dispatcher_send and async_dispatcher_connect work like events, but stay in memory and are not recorded.