Skip to content

Bus

The event bus delivers Home Assistant events (state changes, service calls, component loads) to any app handler that subscribes. It also delivers Hassette-internal events.

self.bus is available on every App instance. Hassette creates it at startup.

Subscribing to Events

Bus provides typed subscription methods for common event types. Each returns a Subscription handle — call sub.cancel() to unregister the handler.

from hassette import App, AppConfig, D, states


class DoorApp(App[AppConfig]):
    async def on_initialize(self):
        await self.bus.on_state_change(
            "binary_sensor.front_door",
            handler=self.on_door_change,
            name="front_door",
        )

    async def on_door_change(self, new: D.StateNew[states.BinarySensorState]):
        if new.value is True:
            self.logger.info("Front door opened")

D is hassette.event_handling.dependencies, a module of type annotations that tell Hassette what to extract from each event. states is hassette.models.states, typed state classes for each Home Assistant domain. D.StateNew[states.BinarySensorState] extracts the new state and passes it as a typed BinarySensorState. The handler receives clean, typed data instead of a raw event dictionary. Dependency Injection covers the full annotation reference.

name= is required on every subscription — it identifies the listener in logs and the monitoring UI. Omitting it raises ListenerNameRequiredError at call time.

Subscription Methods covers each method, its parameters, and compatible DI annotations.

Matching Multiple Entities

Subscription methods accept glob patterns for entity matching.

# Any light
await self.bus.on_state_change("light.*", handler=self.on_any_light, name="any_light")

# Sensors in bedroom
await self.bus.on_state_change("sensor.bedroom_*", handler=self.on_bedroom_sensor, name="bedroom_sensors")

# Specific service calls
await self.bus.on_call_service(
    domain="light",
    service="turn_*",
    handler=self.on_light_service,
    name="light_turn_service",
)

"light.*" matches any entity in the light domain. "sensor.bedroom_*" matches sensors with a bedroom_ prefix. The same patterns work for domain and service parameters on on_call_service.

Glob patterns match identifiers only

Glob patterns do not match attribute names or data values. Predicates (functions that decide whether to run the handler — see Filtering) handle those cases.

Synchronous usage (AppSync only)

self.bus.sync exposes a BusSyncFacade that mirrors all subscription methods as blocking calls. It exists for AppSync lifecycle hooks, which run in a worker thread outside the async event loop. The Apps page covers the AppSync pattern.

Verify It's Working

Run hassette listener --app <key> to see registered listeners and invocation counts, where <key> is the app identifier from hassette.toml (e.g., motion_lights). Run hassette log --app <key> --since 5m to see handler log output. The monitoring UI's Handlers tab shows invocation history and last-seen timestamps.

Next Steps