Skip to content

Hass

HassEvent: TypeAlias = Event[HassPayload[Any]] module-attribute

Alias for Home Assistant events.

CallServicePayload dataclass

Payload for a call_service event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
20
21
22
23
24
25
26
27
@dataclass(slots=True, frozen=True)
class CallServicePayload:
    """Payload for a call_service event in Home Assistant."""

    domain: str
    service: str
    service_data: dict[str, Any] = field(default_factory=dict)
    service_call_id: str | None = None  # have never seen this but the docs say it exists

ComponentLoadedPayload dataclass

Payload for a component_loaded event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
30
31
32
33
34
@dataclass(slots=True, frozen=True)
class ComponentLoadedPayload:
    """Payload for a component_loaded event in Home Assistant."""

    component: str

ServiceRegisteredPayload dataclass

Payload for a service_registered event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
37
38
39
40
41
42
@dataclass(slots=True, frozen=True)
class ServiceRegisteredPayload:
    """Payload for a service_registered event in Home Assistant."""

    domain: str
    service: str

ServiceRemovedPayload dataclass

Payload for a service_removed event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
45
46
47
48
49
50
@dataclass(slots=True, frozen=True)
class ServiceRemovedPayload:
    """Payload for a service_removed event in Home Assistant."""

    domain: str
    service: str

LogbookEntryPayload dataclass

Payload for a logbook_entry event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
53
54
55
56
57
58
59
60
@dataclass(slots=True, frozen=True)
class LogbookEntryPayload:
    """Payload for a logbook_entry event in Home Assistant."""

    name: str
    message: str
    domain: str | None = None
    entity_id: str | None = None

UserAddedPayload dataclass

Payload for a user_added event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
63
64
65
66
67
@dataclass(slots=True, frozen=True)
class UserAddedPayload:
    """Payload for a user_added event in Home Assistant."""

    user_id: str

UserRemovedPayload dataclass

Payload for a user_removed event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
70
71
72
73
74
@dataclass(slots=True, frozen=True)
class UserRemovedPayload:
    """Payload for a user_removed event in Home Assistant."""

    user_id: str

AutomationTriggeredPayload dataclass

Payload for an automation_triggered event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
77
78
79
80
81
82
83
@dataclass(slots=True, frozen=True)
class AutomationTriggeredPayload:
    """Payload for an automation_triggered event in Home Assistant."""

    name: str
    entity_id: str
    source: str | None = None  # this one isn't on the docs page but is included apparently

ScriptStartedPayload dataclass

Payload for a script_started event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
87
88
89
90
91
92
@dataclass(slots=True, frozen=True)
class ScriptStartedPayload:
    """Payload for a script_started event in Home Assistant."""

    name: str
    entity_id: str

EntityRegistryUpdatedPayload dataclass

Payload for an entity_registry_updated event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
 95
 96
 97
 98
 99
100
101
102
@dataclass(slots=True, frozen=True)
class EntityRegistryUpdatedPayload:
    """Payload for an entity_registry_updated event in Home Assistant."""

    action: Literal["create", "update", "remove"]
    entity_id: str
    changes: dict[str, Any] | None = None  # Required with action == "update"
    old_entity_id: str | None = None  # Present when action="update" and entity_id changed

RawStateChangePayload dataclass

Payload for a state_changed event in Home Assistant.

Source code in src/hassette/events/hass/hass.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
@dataclass(slots=True, frozen=True)
class RawStateChangePayload:
    """Payload for a state_changed event in Home Assistant."""

    entity_id: str
    """"The entity ID of the entity that changed state."""

    old_state: HassStateDict | None
    """The previous state of the entity before it changed. Omitted if the state is set for the first time."""

    new_state: HassStateDict | None
    """The new state of the entity. Omitted if the state has been removed."""

    @property
    def state_value_has_changed(self) -> bool:
        """Check if the state value has changed between old and new states.

        Appropriately handles cases where either state may be None.

        Returns:
            True if the state value has changed, False otherwise.
        """
        return self.old_state_value != self.new_state_value

    @property
    def new_state_value(self) -> "Any | FalseySentinel":
        """Return the value of the new state, or MISSING_VALUE if not present."""
        return self.new_state.get("state") if self.new_state is not None else MISSING_VALUE

    @property
    def old_state_value(self) -> "Any | FalseySentinel":
        """Return the value of the old state, or MISSING_VALUE if not present."""
        return self.old_state.get("state") if self.old_state is not None else MISSING_VALUE

    @property
    def has_new_state(self) -> bool:
        """Check if the new state is not None - not a TypeGuard."""
        return self.new_state is not None

    @property
    def has_old_state(self) -> bool:
        """Check if the old state is not None - not a TypeGuard."""
        return self.old_state is not None

    @property
    def domain(self) -> str:
        """Extract the domain from the entity_id."""
        return self.entity_id.split(".", 1)[0]

entity_id: str instance-attribute

"The entity ID of the entity that changed state.

old_state: HassStateDict | None instance-attribute

The previous state of the entity before it changed. Omitted if the state is set for the first time.

new_state: HassStateDict | None instance-attribute

The new state of the entity. Omitted if the state has been removed.

state_value_has_changed: bool property

Check if the state value has changed between old and new states.

Appropriately handles cases where either state may be None.

Returns:

Type Description
bool

True if the state value has changed, False otherwise.

new_state_value: Any | FalseySentinel property

Return the value of the new state, or MISSING_VALUE if not present.

old_state_value: Any | FalseySentinel property

Return the value of the old state, or MISSING_VALUE if not present.

has_new_state: bool property

Check if the new state is not None - not a TypeGuard.

has_old_state: bool property

Check if the old state is not None - not a TypeGuard.

domain: str property

Extract the domain from the entity_id.

TypedStateChangePayload dataclass

Bases: Generic[StateT]

Payload for a state_changed event in Home Assistant, with typed state data.

Source code in src/hassette/events/hass/hass.py
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
@dataclass(slots=True, frozen=True)
class TypedStateChangePayload(Generic[StateT]):
    """Payload for a state_changed event in Home Assistant, with typed state data."""

    entity_id: str
    """The entity ID of the entity that changed state."""

    old_state: StateT | None
    """The previous state of the entity before it changed. Omitted if the state is set for the first time."""

    new_state: StateT | None
    """The new state of the entity. Omitted if the state has been removed."""

    @property
    def domain(self) -> str:
        """Extract the domain from the entity_id."""
        return self.entity_id.split(".", 1)[0]

entity_id: str instance-attribute

The entity ID of the entity that changed state.

old_state: StateT | None instance-attribute

The previous state of the entity before it changed. Omitted if the state is set for the first time.

new_state: StateT | None instance-attribute

The new state of the entity. Omitted if the state has been removed.

domain: str property

Extract the domain from the entity_id.

RawStateChangeEvent dataclass

Bases: Event[HassPayload[RawStateChangePayload]]

Event representing a state change in Home Assistant, with raw state data.

Source code in src/hassette/events/hass/hass.py
174
175
class RawStateChangeEvent(Event[HassPayload[RawStateChangePayload]]):
    """Event representing a state change in Home Assistant, with raw state data."""

CallServiceEvent dataclass

Bases: Event[HassPayload[CallServicePayload]]

Event representing a call service in Home Assistant.

Source code in src/hassette/events/hass/hass.py
178
179
class CallServiceEvent(Event[HassPayload[CallServicePayload]]):
    """Event representing a call service in Home Assistant."""

ComponentLoadedEvent dataclass

Bases: Event[HassPayload[ComponentLoadedPayload]]

Event representing a component loaded in Home Assistant.

Source code in src/hassette/events/hass/hass.py
182
183
class ComponentLoadedEvent(Event[HassPayload[ComponentLoadedPayload]]):
    """Event representing a component loaded in Home Assistant."""

ServiceRegisteredEvent dataclass

Bases: Event[HassPayload[ServiceRegisteredPayload]]

Event representing a service registered in Home Assistant.

Source code in src/hassette/events/hass/hass.py
186
187
class ServiceRegisteredEvent(Event[HassPayload[ServiceRegisteredPayload]]):
    """Event representing a service registered in Home Assistant."""

ServiceRemovedEvent dataclass

Bases: Event[HassPayload[ServiceRemovedPayload]]

Event representing a service removed in Home Assistant.

Source code in src/hassette/events/hass/hass.py
190
191
class ServiceRemovedEvent(Event[HassPayload[ServiceRemovedPayload]]):
    """Event representing a service removed in Home Assistant."""

LogbookEntryEvent dataclass

Bases: Event[HassPayload[LogbookEntryPayload]]

Event representing a logbook entry in Home Assistant.

Source code in src/hassette/events/hass/hass.py
194
195
class LogbookEntryEvent(Event[HassPayload[LogbookEntryPayload]]):
    """Event representing a logbook entry in Home Assistant."""

UserAddedEvent dataclass

Bases: Event[HassPayload[UserAddedPayload]]

Event representing a user added in Home Assistant.

Source code in src/hassette/events/hass/hass.py
198
199
class UserAddedEvent(Event[HassPayload[UserAddedPayload]]):
    """Event representing a user added in Home Assistant."""

UserRemovedEvent dataclass

Bases: Event[HassPayload[UserRemovedPayload]]

Event representing a user removed in Home Assistant.

Source code in src/hassette/events/hass/hass.py
202
203
class UserRemovedEvent(Event[HassPayload[UserRemovedPayload]]):
    """Event representing a user removed in Home Assistant."""

AutomationTriggeredEvent dataclass

Bases: Event[HassPayload[AutomationTriggeredPayload]]

Event representing an automation triggered in Home Assistant.

Source code in src/hassette/events/hass/hass.py
206
207
class AutomationTriggeredEvent(Event[HassPayload[AutomationTriggeredPayload]]):
    """Event representing an automation triggered in Home Assistant."""

ScriptStartedEvent dataclass

Bases: Event[HassPayload[ScriptStartedPayload]]

Event representing a script started in Home Assistant.

Source code in src/hassette/events/hass/hass.py
210
211
class ScriptStartedEvent(Event[HassPayload[ScriptStartedPayload]]):
    """Event representing a script started in Home Assistant."""

TypedStateChangeEvent dataclass

Bases: Event[HassPayload[TypedStateChangePayload[StateT]]]

Event representing a state change in Home Assistant, with typed state data.

This is not used directly; use the TypedStateChangeEvent annotation in dependencies instead.

Source code in src/hassette/events/hass/hass.py
289
290
291
292
293
class TypedStateChangeEvent(Event[HassPayload[TypedStateChangePayload[StateT]]]):
    """Event representing a state change in Home Assistant, with typed state data.

    This is not used directly; use the TypedStateChangeEvent annotation in dependencies instead.
    """

create_event_from_hass(data: HassEventEnvelopeDict) -> Event

Create an Event from a dictionary.

Source code in src/hassette/events/hass/hass.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
def create_event_from_hass(data: HassEventEnvelopeDict) -> Event:
    """Create an Event from a dictionary."""

    event = data.get("event", {})
    event_type = event.get("event_type")
    if not event_type:
        raise ValueError("Event data must contain 'event_type' key")

    event_data = event.get("data", {}) or {}
    event_payload = {
        "event_type": event_type,
        "origin": event["origin"],
        "context": HassContext(**event["context"]),
        "time_fired": convert_datetime_str_to_system_tz(event["time_fired"]),
    }

    match event_type:
        case "state_changed":
            return RawStateChangeEvent(
                topic=Topic.HASS_EVENT_STATE_CHANGED,
                payload=HassPayload(**event_payload, data=RawStateChangePayload(**event_data)),
            )

        case "call_service":
            return CallServiceEvent(
                topic=Topic.HASS_EVENT_CALL_SERVICE,
                payload=HassPayload(**event_payload, data=CallServicePayload(**event_data)),
            )
        case "component_loaded":
            return ComponentLoadedEvent(
                topic=Topic.HASS_EVENT_COMPONENT_LOADED,
                payload=HassPayload(**event_payload, data=ComponentLoadedPayload(**event_data)),
            )
        case "service_registered":
            return ServiceRegisteredEvent(
                topic=Topic.HASS_EVENT_SERVICE_REGISTERED,
                payload=HassPayload(**event_payload, data=ServiceRegisteredPayload(**event_data)),
            )
        case "service_removed":
            return ServiceRemovedEvent(
                topic=Topic.HASS_EVENT_SERVICE_REMOVED,
                payload=HassPayload(**event_payload, data=ServiceRemovedPayload(**event_data)),
            )
        case "logbook_entry":
            return LogbookEntryEvent(
                topic=Topic.HASS_EVENT_LOGBOOK_ENTRY,
                payload=HassPayload(**event_payload, data=LogbookEntryPayload(**event_data)),
            )
        case "user_added":
            return UserAddedEvent(
                topic=Topic.HASS_EVENT_USER_ADDED,
                payload=HassPayload(**event_payload, data=UserAddedPayload(**event_data)),
            )
        case "user_removed":
            return UserRemovedEvent(
                topic=Topic.HASS_EVENT_USER_REMOVED,
                payload=HassPayload(**event_payload, data=UserRemovedPayload(**event_data)),
            )
        case "automation_triggered":
            return AutomationTriggeredEvent(
                topic=Topic.HASS_EVENT_AUTOMATION_TRIGGERED,
                payload=HassPayload(**event_payload, data=AutomationTriggeredPayload(**event_data)),
            )
        case "script_started":
            return ScriptStartedEvent(
                topic=Topic.HASS_EVENT_SCRIPT_STARTED,
                payload=HassPayload(**event_payload, data=ScriptStartedPayload(**event_data)),
            )
        case _:
            pass

    # fallback to generic event
    return Event(topic=f"hass.event.{event_type}", payload=HassPayload(**event_payload, data=event_data))