> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/home-assistant/core/llms.txt
> Use this file to discover all available pages before exploring further.

# State Machine

> Centralized management and storage of all entity states in Home Assistant

The State Machine is Home Assistant's central storage system for all entity states. It maintains a complete picture of your home automation system's current state and efficiently tracks changes over time.

## StateMachine Overview

The `StateMachine` class provides thread-safe access to entity states:

```python homeassistant/core.py theme={null}
class StateMachine:
    """Helper class that tracks the state of different entities."""
    
    def __init__(self, bus: EventBus, loop: asyncio.events.AbstractEventLoop) -> None:
        """Initialize state machine."""
        self._states = States()
        self._states_data = self._states.data
        self._reservations: set[str] = set()
        self._bus = bus
        self._loop = loop
```

<Info>
  The StateMachine maintains two references to state storage: `_states` (the States container) and `_states_data` (direct dict access) for performance optimization.
</Info>

## State Object Structure

Each entity's state is represented by a `State` object:

```python homeassistant/core.py theme={null}
class State:
    """Object to represent a state within the state machine."""
    
    entity_id: str                    # Entity identifier (e.g., "light.living_room")
    domain: str                       # Domain portion (e.g., "light")
    object_id: str                    # Object ID portion (e.g., "living_room")
    state: str                        # Current state value (e.g., "on", "23.5", "home")
    attributes: ReadOnlyDict[str, Any]  # Additional state information
    last_changed: datetime.datetime   # When state value last changed
    last_reported: datetime.datetime  # When state was last reported
    last_updated: datetime.datetime   # When state or attributes last changed
    context: Context                  # Context that created this state
```

### State Components

<CardGroup cols={2}>
  <Card title="entity_id" icon="fingerprint">
    Unique identifier in format `domain.object_id`
  </Card>

  <Card title="state" icon="circle-dot">
    String representing the current state value
  </Card>

  <Card title="attributes" icon="table">
    Dictionary of additional state metadata
  </Card>

  <Card title="timestamps" icon="clock">
    Three timestamps tracking different types of changes
  </Card>
</CardGroup>

## Setting States

### Basic State Setting

```python theme={null}
# Async context (preferred)
hass.states.async_set(
    "sensor.temperature",
    "23.5",
    {"unit_of_measurement": "°C", "friendly_name": "Temperature"}
)

# Synchronous context (blocks until complete)
hass.states.set(
    "sensor.temperature",
    "23.5",
    {"unit_of_measurement": "°C", "friendly_name": "Temperature"}
)
```

### Advanced State Setting

```python homeassistant/core.py theme={null}
@callback
def async_set(
    self,
    entity_id: str,
    new_state: str,
    attributes: Mapping[str, Any] | None = None,
    force_update: bool = False,
    context: Context | None = None,
    state_info: StateInfo | None = None,
    timestamp: float | None = None,
) -> None:
    """Set the state of an entity, add entity if it does not exist.
    
    If you just update the attributes and not the state, last_changed
    will not be affected.
    """
    state = str(new_state)
    validate_state(state)
    self.async_set_internal(
        entity_id.lower(),
        state,
        attributes or {},
        force_update,
        context,
        state_info,
        timestamp or time.time(),
    )
```

#### Parameters

* **entity\_id**: Entity identifier (automatically lowercased)
* **new\_state**: New state value (converted to string)
* **attributes**: Optional dictionary of attributes
* **force\_update**: Force `last_changed` update even if state unchanged
* **context**: Context tracking the change origin
* **timestamp**: Override the timestamp (for replay scenarios)

<Steps>
  <Step title="Entity ID Normalized">
    The entity\_id is converted to lowercase for consistency.
  </Step>

  <Step title="State Validated">
    The state string length is validated (max 255 characters).
  </Step>

  <Step title="Old State Retrieved">
    The existing state is retrieved to compare changes.
  </Step>

  <Step title="State Object Created">
    A new State object is created with the updated values.
  </Step>

  <Step title="Event Fired">
    Either `EVENT_STATE_CHANGED` or `EVENT_STATE_REPORTED` is fired.
  </Step>
</Steps>

## State Change Detection

The state machine intelligently detects different types of state changes:

```python homeassistant/core.py theme={null}
@callback
def async_set_internal(
    self,
    entity_id: str,
    new_state: str,
    attributes: Mapping[str, Any] | None,
    force_update: bool,
    context: Context | None,
    state_info: StateInfo | None,
    timestamp: float,
) -> None:
    """Set the state of an entity."""
    old_state: State | None
    try:
        old_state = self._states_data[entity_id]
    except KeyError:
        old_state = None
        same_state = False
        same_attr = False
        last_changed = None
    else:
        same_state = old_state.state == new_state and not force_update
        same_attr = old_state.attributes == attributes
        last_changed = old_state.last_changed if same_state else None
    
    if same_state and same_attr:
        # State unchanged - update last_reported and fire STATE_REPORTED
        old_state.last_reported = now
        self._bus.async_fire_internal(
            EVENT_STATE_REPORTED,
            {
                "entity_id": entity_id,
                "last_reported": now,
                "old_last_reported": old_last_reported,
                "new_state": old_state,
            },
            context=context,
        )
        return
    
    # State or attributes changed - create new state and fire STATE_CHANGED
    state = State(
        entity_id,
        new_state,
        attributes,
        last_changed,
        now,
        now,
        context,
        old_state is None,
        state_info,
        timestamp,
    )
    self._states[entity_id] = state
    self._bus.async_fire_internal(
        EVENT_STATE_CHANGED,
        {
            "entity_id": entity_id,
            "old_state": old_state,
            "new_state": state,
        },
        context=context,
    )
```

### Event Types

<CodeGroup>
  ```python STATE_CHANGED Event theme={null}
  # Fired when state value or attributes change
  {
      "entity_id": "light.living_room",
      "old_state": State(entity_id="light.living_room", state="off", ...),
      "new_state": State(entity_id="light.living_room", state="on", ...)
  }
  ```

  ```python STATE_REPORTED Event theme={null}
  # Fired when state is reported but unchanged
  {
      "entity_id": "sensor.temperature",
      "new_state": State(entity_id="sensor.temperature", state="23.5", ...),
      "last_reported": datetime(...),
      "old_last_reported": datetime(...)
  }
  ```
</CodeGroup>

<Note>
  `EVENT_STATE_REPORTED` was introduced to allow tracking of entity updates even when the state value doesn't change. This is useful for detecting that a device is still communicating.
</Note>

## Reading States

### Getting a Single State

```python theme={null}
# Get state (returns None if not found)
state = hass.states.get("light.living_room")

if state:
    print(f"State: {state.state}")
    print(f"Attributes: {state.attributes}")
    print(f"Last changed: {state.last_changed}")
```

### Checking State Value

```python theme={null}
# Check if entity is in a specific state
if hass.states.is_state("light.living_room", "on"):
    print("Light is on")
```

### Getting All States

```python theme={null}
# Get all states (async context)
all_states = hass.states.async_all()

# Get states for a specific domain
light_states = hass.states.async_all("light")

# Get states for multiple domains  
states = hass.states.async_all(["light", "switch"])
```

### Getting Entity IDs

```python theme={null}
# Get all entity IDs
all_entity_ids = hass.states.async_entity_ids()

# Get entity IDs for a domain
light_entity_ids = hass.states.async_entity_ids("light")

# Get count of entities
total_entities = hass.states.async_entity_ids_count()
light_count = hass.states.async_entity_ids_count("light")
```

## Removing States

Remove an entity from the state machine:

```python theme={null}
# Async context
removed = hass.states.async_remove("sensor.old_sensor")

if removed:
    print("Entity removed")
else:
    print("Entity did not exist")

# Synchronous context
removed = hass.states.remove("sensor.old_sensor")
```

<Warning>
  Removing a state fires a `STATE_CHANGED` event with `new_state=None`. This signals to listeners that the entity no longer exists.
</Warning>

## State Reservations

Reserve an entity\_id before creating it to prevent race conditions:

```python theme={null}
from homeassistant.exceptions import HomeAssistantError

try:
    # Reserve the entity_id
    hass.states.async_reserve("sensor.new_sensor")
    
    # Perform setup that might take time
    await setup_sensor()
    
    # Set the initial state
    hass.states.async_set(
        "sensor.new_sensor",
        "initializing",
        {"friendly_name": "New Sensor"}
    )
except HomeAssistantError:
    _LOGGER.error("Entity ID already in use")
```

<Info>
  Reservations prevent multiple components from trying to create the same entity\_id simultaneously, which could cause data corruption or unexpected behavior.
</Info>

## States Container

The `States` class provides efficient domain-based indexing:

```python homeassistant/core.py theme={null}
class States(UserDict[str, State]):
    """Container for states, maps entity_id -> State.
    
    Maintains an additional index:
    - domain -> dict[str, State]
    """
    
    def __init__(self) -> None:
        super().__init__()
        self._domain_index: defaultdict[str, dict[str, State]] = defaultdict(dict)
    
    def __setitem__(self, key: str, entry: State) -> None:
        """Add an item."""
        self.data[key] = entry
        self._domain_index[entry.domain][entry.entity_id] = entry
    
    def domain_entity_ids(self, key: str) -> KeysView[str] | tuple[()]:
        """Get all entity_ids for a domain."""
        if key not in self._domain_index:
            return ()
        return self._domain_index[key].keys()
```

<Tip>
  The domain index enables O(1) lookups when retrieving all entities of a specific domain, making operations like `hass.states.async_all("light")` very fast even with thousands of entities.
</Tip>

## State Attributes

Attributes provide additional context about an entity's state:

```python theme={null}
# Common attributes
attributes = {
    "friendly_name": "Living Room Temperature",
    "unit_of_measurement": "°C",
    "device_class": "temperature",
    "icon": "mdi:thermometer",
    
    # Location attributes
    "latitude": 37.7749,
    "longitude": -122.4194,
    
    # Device information
    "manufacturer": "Acme Corp",
    "model": "TH-100",
    "sw_version": "1.2.3",
}

hass.states.async_set(
    "sensor.temperature",
    "23.5",
    attributes
)
```

### Attribute Best Practices

<Note>
  * Use standard attribute names defined in `homeassistant.const`
  * Keep attribute values JSON-serializable
  * Don't store large data structures in attributes
  * Use ReadOnlyDict to prevent accidental modification
</Note>

## Timestamp Semantics

The State object maintains three timestamps:

### last\_changed

Updated when the **state value** changes:

```python theme={null}
# Initial state
hass.states.async_set("sensor.temp", "20", {"unit": "°C"})
# last_changed = now

# Update attributes only
hass.states.async_set("sensor.temp", "20", {"unit": "°C", "battery": 90})
# last_changed = unchanged

# Update state value
hass.states.async_set("sensor.temp", "21", {"unit": "°C", "battery": 90})
# last_changed = now
```

### last\_updated

Updated when the state value **or attributes** change:

```python theme={null}
# Update attributes
hass.states.async_set("sensor.temp", "20", {"battery": 90})
# last_updated = now, last_changed = unchanged
```

### last\_reported

Updated every time `async_set` is called, even if nothing changed:

```python theme={null}
# Report same state
hass.states.async_set("sensor.temp", "20", {"unit": "°C"})
# last_reported = now
# Fires EVENT_STATE_REPORTED (not EVENT_STATE_CHANGED)
```

<Tip>
  Use `last_reported` to detect if a device is still communicating, even when its state hasn't changed. This is particularly useful for detecting unavailable devices.
</Tip>

## State Serialization

States can be serialized for storage or API responses:

```python theme={null}
# Get as dictionary
state_dict = state.as_dict()
# Returns ReadOnlyDict with ISO-formatted timestamps

# Get as JSON bytes
state_json = state.as_dict_json

# Get compressed state (for WebSocket API)
compressed = state.as_compressed_state
# Omits last_updated if equal to last_changed
# Compresses context if simple
```

### Compressed State Format

```python homeassistant/core.py theme={null}
class CompressedState(TypedDict):
    """Compressed dict of a state."""
    
    s: str                         # state
    a: ReadOnlyDict[str, Any]      # attributes  
    c: str | dict[str, Any]        # context (string if simple)
    lc: float                      # last_changed timestamp
    lu: NotRequired[float]         # last_updated (omitted if == lc)
```

## Performance Optimizations

The state machine includes several optimizations:

<Tip>
  **Direct dict access**: Internal code uses `_states_data` to bypass container overhead\
  **Cached properties**: Expensive computations are cached with `@cached_property`\
  **Domain indexing**: O(1) lookups for domain-filtered queries\
  **Timestamp caching**: Timestamp conversions are cached on State objects
</Tip>

## Best Practices

1. **Use lowercase entity\_ids**: The state machine automatically lowercases, but it's faster if you do it yourself
2. **Preserve contexts**: Pass contexts through automation chains for proper tracking
3. **Validate before setting**: Check entity\_id format before calling `async_set`
4. **Use async methods**: Always prefer `async_set` over `set` when in the event loop
5. **Handle None states**: When listening to state changes, old\_state or new\_state may be None
