1 """Support for restoring entity states on startup."""
3 from __future__
import annotations
5 from abc
import ABC, abstractmethod
6 from datetime
import datetime, timedelta
8 from typing
import Any, Self, cast
18 from .entity
import Entity
19 from .event
import async_track_time_interval
20 from .json
import JSONEncoder
21 from .singleton
import singleton
22 from .storage
import Store
24 DATA_RESTORE_STATE: HassKey[RestoreStateData] =
HassKey(
"restore_state")
26 _LOGGER = logging.getLogger(__name__)
28 STORAGE_KEY =
"core.restore_state"
39 """Object to hold extra stored data."""
43 """Return a dict representation of the extra data.
45 Must be serializable by Home Assistant's JSONEncoder.
49 class RestoredExtraData(ExtraStoredData):
50 """Object to hold extra stored data loaded from storage."""
52 def __init__(self, json_dict: dict[str, Any]) ->
None:
53 """Object to hold extra stored data."""
57 """Return a dict representation of the extra data."""
62 """Object to represent a stored state."""
67 extra_data: ExtraStoredData |
None,
70 """Initialize a new stored state."""
76 """Return a dict representation of the stored state to be JSON serialized."""
78 "state": self.
statestate.json_fragment,
85 """Initialize a stored state from a dict."""
86 extra_data_dict = json_dict.get(
"extra_data")
88 last_seen = json_dict[
"last_seen"]
90 if isinstance(last_seen, str):
91 last_seen = dt_util.parse_datetime(last_seen)
94 cast(State, State.from_dict(json_dict[
"state"])), extra_data, last_seen
99 """Load the restore state task."""
104 @singleton(DATA_RESTORE_STATE)
106 """Get the restore state data helper."""
111 """Helper class for managing the helper saved data."""
115 """Dump states now."""
119 """Initialize the restore state data class."""
120 self.hass: HomeAssistant = hass
121 self.
storestore = Store[list[dict[str, Any]]](
122 hass, STORAGE_VERSION, STORAGE_KEY, encoder=JSONEncoder
124 self.
last_stateslast_states: dict[str, StoredState] = {}
125 self.entities: dict[str, RestoreEntity] = {}
128 """Set up up the instance of this data helper."""
132 def hass_start(hass: HomeAssistant) ->
None:
133 """Start the restore state task."""
136 start.async_at_start(self.hass, hass_start)
139 """Load the instance of this data helper."""
142 except HomeAssistantError
as exc:
143 _LOGGER.error(
"Error loading last states", exc_info=exc)
146 if stored_states
is None:
147 _LOGGER.debug(
"Not creating cache - no saved states found")
151 item[
"state"][
"entity_id"]: StoredState.from_dict(item)
152 for item
in stored_states
155 _LOGGER.debug(
"Created cache with %s",
list(self.
last_stateslast_states))
159 """Get the set of states which should be stored.
161 This includes the states of all registered entities, as well as the
162 stored states from the previous run, which have not been created as
163 entities on this run, and have not expired.
165 now = dt_util.utcnow()
166 all_states = self.hass.states.async_all()
168 current_states_by_entity_id = {
169 state.entity_id: state
170 for state
in all_states
171 if not state.attributes.get(ATTR_RESTORED)
177 current_states_by_entity_id[entity_id],
178 entity.extra_restore_state_data,
181 for entity_id, entity
in self.entities.items()
182 if entity_id
in current_states_by_entity_id
184 expiration_time = now - STATE_EXPIRATION
186 for entity_id, stored_state
in self.
last_stateslast_states.items():
190 if entity_id
in current_states_by_entity_id:
194 if stored_state.last_seen < expiration_time:
197 stored_states.append(stored_state)
202 """Save the current state machine to storage."""
203 _LOGGER.debug(
"Dumping states")
207 stored_state.as_dict()
211 except HomeAssistantError
as exc:
212 _LOGGER.error(
"Error saving current states", exc_info=exc)
216 """Set up the restore state listeners."""
218 async
def _async_dump_states(*_: Any) ->
None:
224 self.hass.async_create_task_internal(
225 _async_dump_states(),
"RestoreStateData dump"
233 name=
"RestoreStateData dump states",
236 async
def _async_dump_states_at_stop(*_: Any) ->
None:
241 self.hass.bus.async_listen_once(
242 EVENT_HOMEASSISTANT_STOP, _async_dump_states_at_stop
247 """Store this entity's state when hass is shutdown."""
248 self.entities[entity.entity_id] = entity
252 self, entity_id: str, extra_data: ExtraStoredData |
None
254 """Unregister this entity from saving state."""
258 state = self.hass.states.get(entity_id)
261 if state
is not None:
262 state = State.from_dict(
json_loads(state.as_dict_json))
263 if state
is not None:
265 state, extra_data, dt_util.utcnow()
268 del self.entities[entity_id]
272 """Mixin class for restoring previous entity state."""
275 """Register this entity as a restorable entity."""
280 """Run when entity will be removed from hass."""
288 """Get data stored for an entity, if any."""
292 "Cannot get last state. Entity not added to hass"
298 """Get the entity state from the previous run."""
301 return stored_state.state
304 """Get the entity specific state data from the previous run."""
307 return stored_state.extra_data
311 """Return entity specific state data to be restored.
313 Implemented by platform classes.
None async_internal_added_to_hass(self)
ExtraStoredData|None extra_restore_state_data(self)
State|None async_get_last_state(self)
None async_internal_will_remove_from_hass(self)
ExtraStoredData|None async_get_last_extra_data(self)
StoredState|None _async_get_restored_data(self)
None async_restore_entity_added(self, RestoreEntity entity)
None async_setup_dump(self, *Any args)
None async_dump_states(self)
None __init__(self, HomeAssistant hass)
list[StoredState] async_get_stored_states(self)
None async_restore_entity_removed(self, str entity_id, ExtraStoredData|None extra_data)
None async_save_persistent_states(cls, HomeAssistant hass)
dict[str, Any] as_dict(self)
None __init__(self, State state, ExtraStoredData|None extra_data, datetime last_seen)
Self from_dict(cls, dict json_dict)
bool async_setup(HomeAssistant hass, ConfigType config)
bool valid_entity_id(str entity_id)
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)
RestoreStateData async_get(HomeAssistant hass)
None async_load(HomeAssistant hass)
None async_save(self, _T data)