diff --git a/homeassistant/const.py b/homeassistant/const.py index 74095a2583b6..2f1cc75e4a55 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -184,6 +184,7 @@ EVENT_CORE_CONFIG_UPDATE = "core_config_updated" EVENT_HOMEASSISTANT_CLOSE = "homeassistant_close" EVENT_HOMEASSISTANT_START = "homeassistant_start" EVENT_HOMEASSISTANT_STOP = "homeassistant_stop" +EVENT_HOMEASSISTANT_FINAL_WRITE = "homeassistant_final_write" EVENT_LOGBOOK_ENTRY = "logbook_entry" EVENT_PLATFORM_DISCOVERED = "platform_discovered" EVENT_SCRIPT_STARTED = "script_started" diff --git a/homeassistant/core.py b/homeassistant/core.py index fd894fd6c054..9265c57bbf34 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -47,6 +47,7 @@ from homeassistant.const import ( EVENT_CALL_SERVICE, EVENT_CORE_CONFIG_UPDATE, EVENT_HOMEASSISTANT_CLOSE, + EVENT_HOMEASSISTANT_FINAL_WRITE, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, EVENT_SERVICE_REGISTERED, @@ -151,6 +152,7 @@ class CoreState(enum.Enum): starting = "STARTING" running = "RUNNING" stopping = "STOPPING" + writing_data = "WRITING_DATA" def __str__(self) -> str: """Return the event.""" @@ -412,7 +414,7 @@ class HomeAssistant: # regardless of the state of the loop. if self.state == CoreState.not_running: # just ignore return - if self.state == CoreState.stopping: + if self.state == CoreState.stopping or self.state == CoreState.writing_data: _LOGGER.info("async_stop called twice: ignored") return if self.state == CoreState.starting: @@ -426,6 +428,11 @@ class HomeAssistant: await self.async_block_till_done() # stage 2 + self.state = CoreState.writing_data + self.bus.async_fire(EVENT_HOMEASSISTANT_FINAL_WRITE) + await self.async_block_till_done() + + # stage 3 self.state = CoreState.not_running self.bus.async_fire(EVENT_HOMEASSISTANT_CLOSE) await self.async_block_till_done() diff --git a/homeassistant/helpers/restore_state.py b/homeassistant/helpers/restore_state.py index d57d3ad99207..0757770d2f78 100644 --- a/homeassistant/helpers/restore_state.py +++ b/homeassistant/helpers/restore_state.py @@ -4,7 +4,10 @@ from datetime import datetime, timedelta import logging from typing import Any, Awaitable, Dict, List, Optional, Set, cast -from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP +from homeassistant.const import ( + EVENT_HOMEASSISTANT_FINAL_WRITE, + EVENT_HOMEASSISTANT_START, +) from homeassistant.core import ( CoreState, HomeAssistant, @@ -184,7 +187,9 @@ class RestoreStateData: async_track_time_interval(self.hass, _async_dump_states, STATE_DUMP_INTERVAL) # Dump states when stopping hass - self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_dump_states) + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_FINAL_WRITE, _async_dump_states + ) @callback def async_restore_entity_added(self, entity_id: str) -> None: diff --git a/homeassistant/helpers/storage.py b/homeassistant/helpers/storage.py index 1cad8eec473f..5885aa01e6fb 100644 --- a/homeassistant/helpers/storage.py +++ b/homeassistant/helpers/storage.py @@ -5,7 +5,7 @@ import logging import os from typing import Any, Callable, Dict, List, Optional, Type, Union -from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers.event import async_call_later from homeassistant.loader import bind_hass @@ -153,7 +153,7 @@ class Store: """Ensure that we write if we quit before delay has passed.""" if self._unsub_stop_listener is None: self._unsub_stop_listener = self.hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_STOP, self._async_callback_stop_write + EVENT_HOMEASSISTANT_FINAL_WRITE, self._async_callback_stop_write ) @callback diff --git a/tests/helpers/test_storage.py b/tests/helpers/test_storage.py index 8c8d370e4b42..dcadd4d4369f 100644 --- a/tests/helpers/test_storage.py +++ b/tests/helpers/test_storage.py @@ -6,7 +6,7 @@ from unittest.mock import Mock, patch import pytest -from homeassistant.const import EVENT_HOMEASSISTANT_STOP +from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE from homeassistant.helpers import storage from homeassistant.util import dt @@ -85,7 +85,7 @@ async def test_saving_on_stop(hass, hass_storage): store.async_delay_save(lambda: MOCK_DATA, 1) assert store.key not in hass_storage - hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP) + hass.bus.async_fire(EVENT_HOMEASSISTANT_FINAL_WRITE) await hass.async_block_till_done() assert hass_storage[store.key] == { "version": MOCK_VERSION, diff --git a/tests/test_core.py b/tests/test_core.py index f5a6f4718cd7..5e6bb0908211 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -21,6 +21,7 @@ from homeassistant.const import ( EVENT_CALL_SERVICE, EVENT_CORE_CONFIG_UPDATE, EVENT_HOMEASSISTANT_CLOSE, + EVENT_HOMEASSISTANT_FINAL_WRITE, EVENT_HOMEASSISTANT_STOP, EVENT_SERVICE_REGISTERED, EVENT_SERVICE_REMOVED, @@ -151,10 +152,14 @@ def test_stage_shutdown(): """Simulate a shutdown, test calling stuff.""" hass = get_test_home_assistant() test_stop = [] + test_final_write = [] test_close = [] test_all = [] hass.bus.listen(EVENT_HOMEASSISTANT_STOP, lambda event: test_stop.append(event)) + hass.bus.listen( + EVENT_HOMEASSISTANT_FINAL_WRITE, lambda event: test_final_write.append(event) + ) hass.bus.listen(EVENT_HOMEASSISTANT_CLOSE, lambda event: test_close.append(event)) hass.bus.listen("*", lambda event: test_all.append(event)) @@ -162,7 +167,8 @@ def test_stage_shutdown(): assert len(test_stop) == 1 assert len(test_close) == 1 - assert len(test_all) == 1 + assert len(test_final_write) == 1 + assert len(test_all) == 2 class TestHomeAssistant(unittest.TestCase):