Add Save Persistent States service (#53881)

pull/54365/head
Richard T. Schaefer 2021-08-09 17:38:56 -05:00 committed by GitHub
parent 33c33d844e
commit 3184f0697f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 1 deletions

View File

@ -14,13 +14,14 @@ from homeassistant.const import (
RESTART_EXIT_CODE,
SERVICE_HOMEASSISTANT_RESTART,
SERVICE_HOMEASSISTANT_STOP,
SERVICE_SAVE_PERSISTENT_STATES,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
import homeassistant.core as ha
from homeassistant.exceptions import HomeAssistantError, Unauthorized, UnknownUser
from homeassistant.helpers import config_validation as cv, recorder
from homeassistant.helpers import config_validation as cv, recorder, restore_state
from homeassistant.helpers.service import (
async_extract_config_entry_ids,
async_extract_referenced_entity_ids,
@ -53,6 +54,10 @@ SHUTDOWN_SERVICES = (SERVICE_HOMEASSISTANT_STOP, SERVICE_HOMEASSISTANT_RESTART)
async def async_setup(hass: ha.HomeAssistant, config: dict) -> bool: # noqa: C901
"""Set up general services related to Home Assistant."""
async def async_save_persistent_states(service):
"""Handle calls to homeassistant.save_persistent_states."""
await restore_state.RestoreStateData.async_save_persistent_states(hass)
async def async_handle_turn_service(service):
"""Handle calls to homeassistant.turn_on/off."""
referenced = await async_extract_referenced_entity_ids(hass, service)
@ -114,6 +119,10 @@ async def async_setup(hass: ha.HomeAssistant, config: dict) -> bool: # noqa: C9
if tasks:
await asyncio.gather(*tasks)
hass.services.async_register(
ha.DOMAIN, SERVICE_SAVE_PERSISTENT_STATES, async_save_persistent_states
)
service_schema = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids}, extra=vol.ALLOW_EXTRA)
hass.services.async_register(

View File

@ -74,3 +74,9 @@ reload_config_entry:
example: 8955375327824e14ba89e4b29cc3ec9a
selector:
text:
save_persistent_states:
name: Save Persistent States
description:
Save the persistent states (for entities derived from RestoreEntity) immediately.
Maintain the normal periodic saving interval.

View File

@ -612,6 +612,7 @@ SERVICE_CLOSE_COVER: Final = "close_cover"
SERVICE_CLOSE_COVER_TILT: Final = "close_cover_tilt"
SERVICE_OPEN_COVER: Final = "open_cover"
SERVICE_OPEN_COVER_TILT: Final = "open_cover_tilt"
SERVICE_SAVE_PERSISTENT_STATES: Final = "save_persistent_states"
SERVICE_SET_COVER_POSITION: Final = "set_cover_position"
SERVICE_SET_COVER_TILT_POSITION: Final = "set_cover_tilt_position"
SERVICE_STOP_COVER: Final = "stop_cover"

View File

@ -100,6 +100,12 @@ class RestoreStateData:
return cast(RestoreStateData, await load_instance(hass))
@classmethod
async def async_save_persistent_states(cls, hass: HomeAssistant) -> None:
"""Dump states now."""
data = await cls.async_get_instance(hass)
await data.async_dump_states()
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the restore state data class."""
self.hass: HomeAssistant = hass

View File

@ -23,6 +23,7 @@ from homeassistant.const import (
EVENT_CORE_CONFIG_UPDATE,
SERVICE_HOMEASSISTANT_RESTART,
SERVICE_HOMEASSISTANT_STOP,
SERVICE_SAVE_PERSISTENT_STATES,
SERVICE_TOGGLE,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
@ -543,3 +544,18 @@ async def test_stop_homeassistant(hass):
assert not mock_check.called
await hass.async_block_till_done()
assert mock_restart.called
async def test_save_persistent_states(hass):
"""Test we can call save_persistent_states."""
await async_setup_component(hass, "homeassistant", {})
with patch(
"homeassistant.helpers.restore_state.RestoreStateData.async_save_persistent_states",
return_value=None,
) as mock_save:
await hass.services.async_call(
"homeassistant",
SERVICE_SAVE_PERSISTENT_STATES,
blocking=True,
)
assert mock_save.called

View File

@ -98,6 +98,62 @@ async def test_periodic_write(hass):
assert not mock_write_data.called
async def test_save_persistent_states(hass):
"""Test that we cancel the currently running job, save the data, and verify the perdiodic job continues."""
data = await RestoreStateData.async_get_instance(hass)
await hass.async_block_till_done()
await data.store.async_save([])
# Emulate a fresh load
hass.data[DATA_RESTORE_STATE_TASK] = None
entity = RestoreEntity()
entity.hass = hass
entity.entity_id = "input_boolean.b1"
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
await entity.async_get_last_state()
await hass.async_block_till_done()
# Startup Save
assert mock_write_data.called
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=10))
await hass.async_block_till_done()
# Not quite the first interval
assert not mock_write_data.called
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
await RestoreStateData.async_save_persistent_states(hass)
await hass.async_block_till_done()
assert mock_write_data.called
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=20))
await hass.async_block_till_done()
# Verify still saving
assert mock_write_data.called
with patch(
"homeassistant.helpers.restore_state.Store.async_save"
) as mock_write_data:
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
# Verify normal shutdown
assert mock_write_data.called
async def test_hass_starting(hass):
"""Test that we cache data."""
hass.state = CoreState.starting