Skip to content

State Manager

Type stub file for States resource.

This stub provides static type hints for all known domain properties on the States class, enabling IDE autocomplete and type checking. At runtime, these properties are handled by getattr in states.py, which queries the state registry dynamically.

For custom domains not listed here, users should use:

    app.states[CustomStateClass]

As this will ensure proper type annotations of the return type.

DomainStates

Bases: Generic[StateT]

DomainStates provides access to all states within a specific domain, with automatic type validation and caching.

This class accesses the StateProxy under the hood to provide access to the current states from HomeAssistant, without needing to make direct calls to the Home Assistant API.

Accessed states are automatically validated against the provided model and cached for efficient repeated access.

Examples:

    # if you know the entity exists
    light_state = self.states.light["bedroom"]

    # to safely access an entity that may not exist
    light_state = self.states.light.get("bedroom")
    if light_state is not None:
        self.logger.info("Light state: %s", light_state.value)

    # or you can check existence ahead of time
    if "bedroom" in self.states.light:
        light_state = self.states.light["bedroom"]
        self.logger.info("Light state: %s", light_state.value)

    # if you are working with a state that isn't defined in Hassette
    custom_states = self.states[CustomStateClass]
    for entity_id, state in custom_states:
        self.logger.info("%s: %s", entity_id, state.value)
Source code in src/hassette/state_manager/state_manager.py
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
class DomainStates(Generic[StateT]):
    """DomainStates provides access to all states within a specific domain, with automatic type validation and caching.

    This class accesses the StateProxy under the hood to provide access to the current states from HomeAssistant,
    without needing to make direct calls to the Home Assistant API.

    Accessed states are automatically validated against the provided model and cached for efficient repeated access.

    Examples:
    ```python
        # if you know the entity exists
        light_state = self.states.light["bedroom"]

        # to safely access an entity that may not exist
        light_state = self.states.light.get("bedroom")
        if light_state is not None:
            self.logger.info("Light state: %s", light_state.value)

        # or you can check existence ahead of time
        if "bedroom" in self.states.light:
            light_state = self.states.light["bedroom"]
            self.logger.info("Light state: %s", light_state.value)

        # if you are working with a state that isn't defined in Hassette
        custom_states = self.states[CustomStateClass]
        for entity_id, state in custom_states:
            self.logger.info("%s: %s", entity_id, state.value)
    ```

    """

    def __init__(self, state_proxy: "StateProxy", model: type[StateT]) -> None:
        if not issubclass(model, BaseState):
            raise TypeError(f"Expected a subclass of BaseState, got {model!r}")

        self._state_proxy = state_proxy
        self._model = model
        self._domain = model.get_domain()
        self._cache: dict[str, CacheValue[StateT]] = {}

    def _validate_or_return_from_cache(self, entity_id: str, state: "HassStateDict") -> StateT:
        context_id: str | None = state.get("context", {}).get("id")

        cached = self._cache.get(entity_id)

        # first check if the context ID matches
        if cached is not None and context_id is not None and cached.context_id == context_id:  # pyright: ignore[reportUnnecessaryComparison]
            return cached.model

        # if not then use deepfreeze and see if frozen states match
        frozen_state = deepfreeze(state)
        if cached is not None and cached.frozen_state == frozen_state:
            return cached.model

        validated = self._model.model_validate(state)
        self._cache[entity_id] = CacheValue(context_id, frozen_state, validated)
        return validated

    def get(self, entity_id: str) -> StateT | None:
        """Get a specific entity state by ID.

        Args:
            entity_id: The full entity ID (e.g., "light.bedroom") or just the entity name (e.g., "bedroom").

        Returns:
            The typed state if found and matches domain, None otherwise.

        Raises:
            ValueError: If the entity ID does not belong to this domain.
        """
        entity_id = make_entity_id(entity_id, self._domain)

        state = self._state_proxy.get_state(entity_id)
        if state is None:
            return None

        return self._validate_or_return_from_cache(entity_id, state)

    def items(self) -> Iterator[tuple[str, StateT]]:
        """Iterate (entity_id, typed state) pairs lazily."""
        return iter(self)

    def keys(self) -> list[str]:
        """Return a list of entity IDs for this domain."""
        return [entity_id for entity_id, _ in self]

    def iterkeys(self) -> Iterator[str]:
        """Returns an iterator over entity IDs for this domain."""
        for entity_id, _ in self:
            yield entity_id

    def values(self) -> list[StateT]:
        """Return a list of typed states for this domain.

        This returns an eagerly evaluated list of all typed states in this domain.

        Note:
            This method will iterate over all states in the domain and validate them,
            which may be expensive for large domains. Consider using the iterator
            returned by `__iter__` for lazy evaluation if performance is a concern.
        """
        return [value for _, value in self]

    def itervalues(self) -> Iterator[StateT]:
        """Returns an iterator over typed states for this domain."""
        for _, value in self:
            yield value

    def to_dict(self) -> dict[str, StateT]:
        """Return a dictionary of entity_id to typed state for this domain.

        This returns an eagerly evaluated dictionary of all typed states in this domain.

        Note:
            This method will iterate over all states in the domain and validate them,
            which may be expensive for large domains. Consider using the iterator
            returned by `__iter__` for lazy evaluation if performance is a concern.
        """
        return {entity_id: value for entity_id, value in self}

    def __iter__(self) -> typing.Generator[tuple[str, StateT], None, None]:
        """Iterate over all states in this domain."""
        for entity_id, state in self._state_proxy.yield_domain_states(self._domain):
            try:
                yield entity_id, self._validate_or_return_from_cache(entity_id, state)
            except Exception as e:
                LOGGER.error(
                    "Error validating state for entity_id '%s' as type %s: %s", entity_id, self._model.__name__, e
                )
                continue

    def __len__(self) -> int:
        """Return the number of entities in this domain."""
        return self._state_proxy.num_domain_states(self._domain)

    def __contains__(self, entity_id: str) -> bool:
        """Check if a specific entity ID exists in this domain."""
        try:
            entity_id = make_entity_id(entity_id, self._domain)
            return entity_id in self._state_proxy
        except ValueError:
            return False

    def __getitem__(self, entity_id: str) -> StateT:
        """Get a specific entity state by ID, raising if not found.

        Args:
            entity_id: The full entity ID (e.g., "light.bedroom") or just the entity name (e.g., "bedroom").

        Raises:
            EntityNotFoundError: If the entity is not found.

        Returns:
            The typed state.
        """
        value = self.get(entity_id)
        if value is None:
            raise KeyError(f"State for entity_id '{entity_id}' not found in domain '{self._domain}'")
        return value

    def __repr__(self) -> str:
        """Return a string representation of the DomainStates container."""
        return f"DomainStates(domain='{self._domain}', count={len(self)})"

    def __bool__(self) -> bool:
        """Return True if there are any entities in this domain."""
        return len(self) > 0

get(entity_id: str) -> StateT | None

Get a specific entity state by ID.

Parameters:

Name Type Description Default
entity_id str

The full entity ID (e.g., "light.bedroom") or just the entity name (e.g., "bedroom").

required

Returns:

Type Description
StateT | None

The typed state if found and matches domain, None otherwise.

Raises:

Type Description
ValueError

If the entity ID does not belong to this domain.

Source code in src/hassette/state_manager/state_manager.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def get(self, entity_id: str) -> StateT | None:
    """Get a specific entity state by ID.

    Args:
        entity_id: The full entity ID (e.g., "light.bedroom") or just the entity name (e.g., "bedroom").

    Returns:
        The typed state if found and matches domain, None otherwise.

    Raises:
        ValueError: If the entity ID does not belong to this domain.
    """
    entity_id = make_entity_id(entity_id, self._domain)

    state = self._state_proxy.get_state(entity_id)
    if state is None:
        return None

    return self._validate_or_return_from_cache(entity_id, state)

items() -> Iterator[tuple[str, StateT]]

Iterate (entity_id, typed state) pairs lazily.

Source code in src/hassette/state_manager/state_manager.py
110
111
112
def items(self) -> Iterator[tuple[str, StateT]]:
    """Iterate (entity_id, typed state) pairs lazily."""
    return iter(self)

keys() -> list[str]

Return a list of entity IDs for this domain.

Source code in src/hassette/state_manager/state_manager.py
114
115
116
def keys(self) -> list[str]:
    """Return a list of entity IDs for this domain."""
    return [entity_id for entity_id, _ in self]

iterkeys() -> Iterator[str]

Returns an iterator over entity IDs for this domain.

Source code in src/hassette/state_manager/state_manager.py
118
119
120
121
def iterkeys(self) -> Iterator[str]:
    """Returns an iterator over entity IDs for this domain."""
    for entity_id, _ in self:
        yield entity_id

values() -> list[StateT]

Return a list of typed states for this domain.

This returns an eagerly evaluated list of all typed states in this domain.

Note

This method will iterate over all states in the domain and validate them, which may be expensive for large domains. Consider using the iterator returned by __iter__ for lazy evaluation if performance is a concern.

Source code in src/hassette/state_manager/state_manager.py
123
124
125
126
127
128
129
130
131
132
133
def values(self) -> list[StateT]:
    """Return a list of typed states for this domain.

    This returns an eagerly evaluated list of all typed states in this domain.

    Note:
        This method will iterate over all states in the domain and validate them,
        which may be expensive for large domains. Consider using the iterator
        returned by `__iter__` for lazy evaluation if performance is a concern.
    """
    return [value for _, value in self]

itervalues() -> Iterator[StateT]

Returns an iterator over typed states for this domain.

Source code in src/hassette/state_manager/state_manager.py
135
136
137
138
def itervalues(self) -> Iterator[StateT]:
    """Returns an iterator over typed states for this domain."""
    for _, value in self:
        yield value

to_dict() -> dict[str, StateT]

Return a dictionary of entity_id to typed state for this domain.

This returns an eagerly evaluated dictionary of all typed states in this domain.

Note

This method will iterate over all states in the domain and validate them, which may be expensive for large domains. Consider using the iterator returned by __iter__ for lazy evaluation if performance is a concern.

Source code in src/hassette/state_manager/state_manager.py
140
141
142
143
144
145
146
147
148
149
150
def to_dict(self) -> dict[str, StateT]:
    """Return a dictionary of entity_id to typed state for this domain.

    This returns an eagerly evaluated dictionary of all typed states in this domain.

    Note:
        This method will iterate over all states in the domain and validate them,
        which may be expensive for large domains. Consider using the iterator
        returned by `__iter__` for lazy evaluation if performance is a concern.
    """
    return {entity_id: value for entity_id, value in self}

__iter__() -> typing.Generator[tuple[str, StateT], typing.Any]

Iterate over all states in this domain.

Source code in src/hassette/state_manager/state_manager.py
152
153
154
155
156
157
158
159
160
161
def __iter__(self) -> typing.Generator[tuple[str, StateT], None, None]:
    """Iterate over all states in this domain."""
    for entity_id, state in self._state_proxy.yield_domain_states(self._domain):
        try:
            yield entity_id, self._validate_or_return_from_cache(entity_id, state)
        except Exception as e:
            LOGGER.error(
                "Error validating state for entity_id '%s' as type %s: %s", entity_id, self._model.__name__, e
            )
            continue

__len__() -> int

Return the number of entities in this domain.

Source code in src/hassette/state_manager/state_manager.py
163
164
165
def __len__(self) -> int:
    """Return the number of entities in this domain."""
    return self._state_proxy.num_domain_states(self._domain)

__contains__(entity_id: str) -> bool

Check if a specific entity ID exists in this domain.

Source code in src/hassette/state_manager/state_manager.py
167
168
169
170
171
172
173
def __contains__(self, entity_id: str) -> bool:
    """Check if a specific entity ID exists in this domain."""
    try:
        entity_id = make_entity_id(entity_id, self._domain)
        return entity_id in self._state_proxy
    except ValueError:
        return False

__getitem__(entity_id: str) -> StateT

Get a specific entity state by ID, raising if not found.

Parameters:

Name Type Description Default
entity_id str

The full entity ID (e.g., "light.bedroom") or just the entity name (e.g., "bedroom").

required

Raises:

Type Description
EntityNotFoundError

If the entity is not found.

Returns:

Type Description
StateT

The typed state.

Source code in src/hassette/state_manager/state_manager.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def __getitem__(self, entity_id: str) -> StateT:
    """Get a specific entity state by ID, raising if not found.

    Args:
        entity_id: The full entity ID (e.g., "light.bedroom") or just the entity name (e.g., "bedroom").

    Raises:
        EntityNotFoundError: If the entity is not found.

    Returns:
        The typed state.
    """
    value = self.get(entity_id)
    if value is None:
        raise KeyError(f"State for entity_id '{entity_id}' not found in domain '{self._domain}'")
    return value

__repr__() -> str

Return a string representation of the DomainStates container.

Source code in src/hassette/state_manager/state_manager.py
192
193
194
def __repr__(self) -> str:
    """Return a string representation of the DomainStates container."""
    return f"DomainStates(domain='{self._domain}', count={len(self)})"

__bool__() -> bool

Return True if there are any entities in this domain.

Source code in src/hassette/state_manager/state_manager.py
196
197
198
def __bool__(self) -> bool:
    """Return True if there are any entities in this domain."""
    return len(self) > 0

StateManager

Bases: Resource

Resource for managing Home Assistant states.

Provides typed access to entity states by domain through dynamic properties.

Examples:

```python # Iterate over all lights for entity_id, light_state in self.states.light: self.logger.info("%s: %s", entity_id, light_state.value)

# Get specific entity
bedroom_light = self.states.light.get("light.bedroom")
if bedroom_light and bedroom_light.attributes.brightness:
    self.logger.info("Brightness: %s", bedroom_light.attributes.brightness)

# Check count
self.logger.info("Total lights: %d", len(self.states.light))
Source code in src/hassette/state_manager/state_manager.py
201
202
203
204
205
206
207
208
209
210
211
212
213
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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
class StateManager(Resource):
    """Resource for managing Home Assistant states.

    Provides typed access to entity states by domain through dynamic properties.

    Examples:
    ```python
        # Iterate over all lights
        for entity_id, light_state in self.states.light:
            self.logger.info("%s: %s", entity_id, light_state.value)

        # Get specific entity
        bedroom_light = self.states.light.get("light.bedroom")
        if bedroom_light and bedroom_light.attributes.brightness:
            self.logger.info("Brightness: %s", bedroom_light.attributes.brightness)

        # Check count
        self.logger.info("Total lights: %d", len(self.states.light))
    """

    _domain_states_cache: dict[type[BaseState], DomainStates[BaseState]]

    def __init__(self, hassette: "Hassette", *, parent: "Resource | None" = None) -> None:
        super().__init__(hassette, parent=parent)
        self._domain_states_cache = {}

    async def after_initialize(self) -> None:
        self.mark_ready(reason="StateManager initialized")

    @property
    def config_log_level(self) -> LOG_LEVEL_TYPE:
        """Return the log level from the config for this resource."""
        return self.hassette.config.logging.state_proxy

    @property
    def _state_proxy(self) -> StateProxy:
        """Access the underlying StateProxy instance."""
        assert self.hassette._state_proxy is not None
        return self.hassette._state_proxy

    def __getattr__(self, domain: str) -> "DomainStates[BaseState]":
        """Dynamically access domain states by property name.

        This method provides dynamic access to domain states at runtime while
        maintaining type safety through the companion .pyi stub file. For known
        domains (defined in the stub), IDEs will provide full type hints. For
        custom/unknown domains, use `get_states(CustomStateClass)` directly.

        Args:
            domain: The domain name (e.g., "light", "switch", "custom_domain").

        Returns:
            DomainStates container for the requested domain.

        Raises:
            AttributeError: If the attribute name matches a reserved name or if the domain is not registered in the
                state registry.

        Example:
            ```python
            # Known domain (typed via .pyi stub)
            for entity_id, light in self.states.light:
                print(light.attributes.brightness)

            # Custom domain (fallback to BaseState at runtime)
            custom_states = self.states.custom_domain
            for entity_id, state in custom_states:
                print(state.value)
            ```
        """
        # Avoid recursion for internal attributes
        if domain.startswith("_") or domain in ("hassette", "parent", "name"):
            raise AttributeError(f"'{type(self).__name__}' object has no attribute '{domain}'")

        try:
            state_class = self.hassette.state_registry.resolve(domain=domain)
        except RegistryNotReadyError:
            raise AttributeError(
                f"State registry not initialized. Cannot access domain '{domain}'. "
                "Ensure state modules are imported before accessing States properties."
            ) from None

        if state_class in self._domain_states_cache:
            return self._domain_states_cache[state_class]

        if state_class is None:
            raise AttributeError(
                f"Domain '{domain}' is not registered in the state registry. Use `states[<state_class>]` "
                "if you have a custom state class for this domain."
            )

        # Domain is registered, use its specific class
        self._domain_states_cache[state_class] = self[state_class]
        return self._domain_states_cache[state_class]

    def __getitem__(self, model: type[StateT]) -> DomainStates[StateT]:
        """Access domain states using the indexing syntax. This is required if you need
        to access domain states for a state model class that is not known by the StateRegistry.

        Args:
            model: The state model class representing the domain.

        Returns:
            DomainStates container for the specified domain.

        Example:
            ```python
            my_state_instance = self.states[MyStateClass].get("custom_entity")
            ```
        """
        return DomainStates[StateT](self._state_proxy, model)

    def get(self, entity_id: str) -> BaseState | None:
        """Get a state by entity ID, returning the most specific type available.

        This method provides generic access to any entity state, regardless of whether
        a domain-specific state class is registered. If a specific class is registered
        (e.g., LightState for domain "light"), it will be used. Otherwise, the state
        is returned as a BaseState instance.

        Args:
            entity_id: Full entity ID (e.g., "light.bedroom" or "test.test_entity")

        Returns:
            Typed state object (domain-specific or BaseState), or None if not found.

        Examples:
            ```python
            # Get a registered domain (returns LightState)
            light = self.states.get("light.bedroom")

            # Get an unregistered domain (returns BaseState)
            test_entity = self.states.get("test.test_entity")
            if test_entity:
                print(f"Domain: {test_entity.domain}, Value: {test_entity.value}")
            ```
        """
        # Get raw state dict from proxy
        state_dict = self._state_proxy.get_state(entity_id)
        if state_dict is None:
            return None

        # Use StateRegistry's try_convert_state which handles fallback to BaseState
        try:
            return self.hassette.state_registry.try_convert_state(state_dict, entity_id)
        except Exception as e:
            LOGGER.error(
                "Failed to convert state for entity '%s': %s",
                entity_id,
                e,
                stacklevel=2,
            )
            return None

    def __contains__(self, model: type[StateT]) -> bool:
        """Check the global STATE_REGISTRY, not this proxy's cached instances."""
        return model in STATE_REGISTRY

    def __iter__(self) -> Iterator[tuple[StateKey, DomainStates[states.BaseState]]]:
        """Iterate over all registered state classes with their keys."""
        for key, state_class in STATE_REGISTRY.items():
            yield key, self[state_class]

    def items(self) -> Iterator[tuple[StateKey, DomainStates[states.BaseState]]]:
        """Iterate over all registered state classes with their keys."""
        return iter(self)

    def values(self) -> Iterator[DomainStates[states.BaseState]]:
        """Iterate over all registered state classes.

        Returns:
            An iterator over all registered DomainStates instances.
        """
        for state_class in STATE_REGISTRY.values():
            yield self[state_class]

    def keys(self) -> Iterator[StateKey]:
        """Iterate over all registered state keys.

        Returns:
            An iterator over all registered state keys.
        """
        return iter(STATE_REGISTRY.keys())

config_log_level: LOG_LEVEL_TYPE property

Return the log level from the config for this resource.

__getattr__(domain: str) -> DomainStates[BaseState]

Dynamically access domain states by property name.

This method provides dynamic access to domain states at runtime while maintaining type safety through the companion .pyi stub file. For known domains (defined in the stub), IDEs will provide full type hints. For custom/unknown domains, use get_states(CustomStateClass) directly.

Parameters:

Name Type Description Default
domain str

The domain name (e.g., "light", "switch", "custom_domain").

required

Returns:

Type Description
DomainStates[BaseState]

DomainStates container for the requested domain.

Raises:

Type Description
AttributeError

If the attribute name matches a reserved name or if the domain is not registered in the state registry.

Example
# Known domain (typed via .pyi stub)
for entity_id, light in self.states.light:
    print(light.attributes.brightness)

# Custom domain (fallback to BaseState at runtime)
custom_states = self.states.custom_domain
for entity_id, state in custom_states:
    print(state.value)
Source code in src/hassette/state_manager/state_manager.py
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
287
288
289
290
291
292
293
294
def __getattr__(self, domain: str) -> "DomainStates[BaseState]":
    """Dynamically access domain states by property name.

    This method provides dynamic access to domain states at runtime while
    maintaining type safety through the companion .pyi stub file. For known
    domains (defined in the stub), IDEs will provide full type hints. For
    custom/unknown domains, use `get_states(CustomStateClass)` directly.

    Args:
        domain: The domain name (e.g., "light", "switch", "custom_domain").

    Returns:
        DomainStates container for the requested domain.

    Raises:
        AttributeError: If the attribute name matches a reserved name or if the domain is not registered in the
            state registry.

    Example:
        ```python
        # Known domain (typed via .pyi stub)
        for entity_id, light in self.states.light:
            print(light.attributes.brightness)

        # Custom domain (fallback to BaseState at runtime)
        custom_states = self.states.custom_domain
        for entity_id, state in custom_states:
            print(state.value)
        ```
    """
    # Avoid recursion for internal attributes
    if domain.startswith("_") or domain in ("hassette", "parent", "name"):
        raise AttributeError(f"'{type(self).__name__}' object has no attribute '{domain}'")

    try:
        state_class = self.hassette.state_registry.resolve(domain=domain)
    except RegistryNotReadyError:
        raise AttributeError(
            f"State registry not initialized. Cannot access domain '{domain}'. "
            "Ensure state modules are imported before accessing States properties."
        ) from None

    if state_class in self._domain_states_cache:
        return self._domain_states_cache[state_class]

    if state_class is None:
        raise AttributeError(
            f"Domain '{domain}' is not registered in the state registry. Use `states[<state_class>]` "
            "if you have a custom state class for this domain."
        )

    # Domain is registered, use its specific class
    self._domain_states_cache[state_class] = self[state_class]
    return self._domain_states_cache[state_class]

__getitem__(model: type[StateT]) -> DomainStates[StateT]

Access domain states using the indexing syntax. This is required if you need to access domain states for a state model class that is not known by the StateRegistry.

Parameters:

Name Type Description Default
model type[StateT]

The state model class representing the domain.

required

Returns:

Type Description
DomainStates[StateT]

DomainStates container for the specified domain.

Example
my_state_instance = self.states[MyStateClass].get("custom_entity")
Source code in src/hassette/state_manager/state_manager.py
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
def __getitem__(self, model: type[StateT]) -> DomainStates[StateT]:
    """Access domain states using the indexing syntax. This is required if you need
    to access domain states for a state model class that is not known by the StateRegistry.

    Args:
        model: The state model class representing the domain.

    Returns:
        DomainStates container for the specified domain.

    Example:
        ```python
        my_state_instance = self.states[MyStateClass].get("custom_entity")
        ```
    """
    return DomainStates[StateT](self._state_proxy, model)

get(entity_id: str) -> states.BaseState | None

Get a state by entity ID, returning the most specific type available.

This method provides generic access to any entity state, regardless of whether a domain-specific state class is registered. If a specific class is registered (e.g., LightState for domain "light"), it will be used. Otherwise, the state is returned as a BaseState instance.

Parameters:

Name Type Description Default
entity_id str

Full entity ID (e.g., "light.bedroom" or "test.test_entity")

required

Returns:

Type Description
BaseState | None

Typed state object (domain-specific or BaseState), or None if not found.

Examples:

# Get a registered domain (returns LightState)
light = self.states.get("light.bedroom")

# Get an unregistered domain (returns BaseState)
test_entity = self.states.get("test.test_entity")
if test_entity:
    print(f"Domain: {test_entity.domain}, Value: {test_entity.value}")
Source code in src/hassette/state_manager/state_manager.py
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
def get(self, entity_id: str) -> BaseState | None:
    """Get a state by entity ID, returning the most specific type available.

    This method provides generic access to any entity state, regardless of whether
    a domain-specific state class is registered. If a specific class is registered
    (e.g., LightState for domain "light"), it will be used. Otherwise, the state
    is returned as a BaseState instance.

    Args:
        entity_id: Full entity ID (e.g., "light.bedroom" or "test.test_entity")

    Returns:
        Typed state object (domain-specific or BaseState), or None if not found.

    Examples:
        ```python
        # Get a registered domain (returns LightState)
        light = self.states.get("light.bedroom")

        # Get an unregistered domain (returns BaseState)
        test_entity = self.states.get("test.test_entity")
        if test_entity:
            print(f"Domain: {test_entity.domain}, Value: {test_entity.value}")
        ```
    """
    # Get raw state dict from proxy
    state_dict = self._state_proxy.get_state(entity_id)
    if state_dict is None:
        return None

    # Use StateRegistry's try_convert_state which handles fallback to BaseState
    try:
        return self.hassette.state_registry.try_convert_state(state_dict, entity_id)
    except Exception as e:
        LOGGER.error(
            "Failed to convert state for entity '%s': %s",
            entity_id,
            e,
            stacklevel=2,
        )
        return None

__contains__(model: type[StateT]) -> bool

Check the global STATE_REGISTRY, not this proxy's cached instances.

Source code in src/hassette/state_manager/state_manager.py
355
356
357
def __contains__(self, model: type[StateT]) -> bool:
    """Check the global STATE_REGISTRY, not this proxy's cached instances."""
    return model in STATE_REGISTRY

__iter__() -> Iterator[tuple[StateKey, DomainStates[states.BaseState]]]

Iterate over all registered state classes with their keys.

Source code in src/hassette/state_manager/state_manager.py
359
360
361
362
def __iter__(self) -> Iterator[tuple[StateKey, DomainStates[states.BaseState]]]:
    """Iterate over all registered state classes with their keys."""
    for key, state_class in STATE_REGISTRY.items():
        yield key, self[state_class]

items() -> Iterator[tuple[StateKey, DomainStates[states.BaseState]]]

Iterate over all registered state classes with their keys.

Source code in src/hassette/state_manager/state_manager.py
364
365
366
def items(self) -> Iterator[tuple[StateKey, DomainStates[states.BaseState]]]:
    """Iterate over all registered state classes with their keys."""
    return iter(self)

values() -> Iterator[DomainStates[states.BaseState]]

Iterate over all registered state classes.

Returns:

Type Description
Iterator[DomainStates[BaseState]]

An iterator over all registered DomainStates instances.

Source code in src/hassette/state_manager/state_manager.py
368
369
370
371
372
373
374
375
def values(self) -> Iterator[DomainStates[states.BaseState]]:
    """Iterate over all registered state classes.

    Returns:
        An iterator over all registered DomainStates instances.
    """
    for state_class in STATE_REGISTRY.values():
        yield self[state_class]

keys() -> Iterator[StateKey]

Iterate over all registered state keys.

Returns:

Type Description
Iterator[StateKey]

An iterator over all registered state keys.

Source code in src/hassette/state_manager/state_manager.py
377
378
379
380
381
382
383
def keys(self) -> Iterator[StateKey]:
    """Iterate over all registered state keys.

    Returns:
        An iterator over all registered state keys.
    """
    return iter(STATE_REGISTRY.keys())