Move recorder services to services.py (#71249)

This commit is contained in:
J. Nick Koston 2022-05-03 12:38:44 -05:00 committed by GitHub
parent 0580803b7d
commit e1be6dd34f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 147 additions and 132 deletions

View file

@ -188,6 +188,7 @@ homeassistant.components.recorder.pool
homeassistant.components.recorder.purge
homeassistant.components.recorder.repack
homeassistant.components.recorder.run_history
homeassistant.components.recorder.services
homeassistant.components.recorder.statistics
homeassistant.components.recorder.system_health
homeassistant.components.recorder.tasks

View file

@ -7,7 +7,7 @@ from typing import Any
import voluptuous as vol
from homeassistant.const import CONF_EXCLUDE, EVENT_STATE_CHANGED
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entityfilter import (
INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA,
@ -17,15 +17,11 @@ from homeassistant.helpers.entityfilter import (
from homeassistant.helpers.integration_platform import (
async_process_integration_platforms,
)
from homeassistant.helpers.service import async_extract_entity_ids
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
from . import history, statistics, websocket_api
from .const import (
ATTR_APPLY_FILTER,
ATTR_KEEP_DAYS,
ATTR_REPACK,
CONF_DB_INTEGRITY_CHECK,
DATA_INSTANCE,
DOMAIN,
@ -33,39 +29,12 @@ from .const import (
SQLITE_URL_PREFIX,
)
from .core import Recorder
from .services import async_register_services
from .tasks import AddRecorderPlatformTask
_LOGGER = logging.getLogger(__name__)
SERVICE_PURGE = "purge"
SERVICE_PURGE_ENTITIES = "purge_entities"
SERVICE_ENABLE = "enable"
SERVICE_DISABLE = "disable"
SERVICE_PURGE_SCHEMA = vol.Schema(
{
vol.Optional(ATTR_KEEP_DAYS): cv.positive_int,
vol.Optional(ATTR_REPACK, default=False): cv.boolean,
vol.Optional(ATTR_APPLY_FILTER, default=False): cv.boolean,
}
)
ATTR_DOMAINS = "domains"
ATTR_ENTITY_GLOBS = "entity_globs"
SERVICE_PURGE_ENTITIES_SCHEMA = vol.Schema(
{
vol.Optional(ATTR_DOMAINS, default=[]): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_ENTITY_GLOBS, default=[]): vol.All(
cv.ensure_list, [cv.string]
),
}
).extend(cv.ENTITY_SERVICE_FIELDS)
SERVICE_ENABLE_SCHEMA = vol.Schema({})
SERVICE_DISABLE_SCHEMA = vol.Schema({})
DEFAULT_URL = "sqlite:///{hass_config_path}"
DEFAULT_DB_FILE = "home-assistant_v2.db"
DEFAULT_DB_INTEGRITY_CHECK = True
@ -196,7 +165,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
instance.async_initialize()
instance.async_register()
instance.start()
_async_register_services(hass, instance)
async_register_services(hass, instance)
history.async_setup(hass)
statistics.async_setup(hass)
websocket_api.async_setup(hass)
@ -211,51 +180,3 @@ async def _process_recorder_platform(
"""Process a recorder platform."""
instance: Recorder = hass.data[DATA_INSTANCE]
instance.queue.put(AddRecorderPlatformTask(domain, platform))
@callback
def _async_register_services(hass: HomeAssistant, instance: Recorder) -> None:
"""Register recorder services."""
async def async_handle_purge_service(service: ServiceCall) -> None:
"""Handle calls to the purge service."""
instance.do_adhoc_purge(**service.data)
hass.services.async_register(
DOMAIN, SERVICE_PURGE, async_handle_purge_service, schema=SERVICE_PURGE_SCHEMA
)
async def async_handle_purge_entities_service(service: ServiceCall) -> None:
"""Handle calls to the purge entities service."""
entity_ids = await async_extract_entity_ids(hass, service)
domains = service.data.get(ATTR_DOMAINS, [])
entity_globs = service.data.get(ATTR_ENTITY_GLOBS, [])
instance.do_adhoc_purge_entities(entity_ids, domains, entity_globs)
hass.services.async_register(
DOMAIN,
SERVICE_PURGE_ENTITIES,
async_handle_purge_entities_service,
schema=SERVICE_PURGE_ENTITIES_SCHEMA,
)
async def async_handle_enable_service(service: ServiceCall) -> None:
instance.set_enable(True)
hass.services.async_register(
DOMAIN,
SERVICE_ENABLE,
async_handle_enable_service,
schema=SERVICE_ENABLE_SCHEMA,
)
async def async_handle_disable_service(service: ServiceCall) -> None:
instance.set_enable(False)
hass.services.async_register(
DOMAIN,
SERVICE_DISABLE,
async_handle_disable_service,
schema=SERVICE_DISABLE_SCHEMA,
)

View file

@ -0,0 +1,106 @@
"""Support for recorder services."""
from __future__ import annotations
import voluptuous as vol
from homeassistant.core import HomeAssistant, ServiceCall, callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.service import async_extract_entity_ids
from .const import ATTR_APPLY_FILTER, ATTR_KEEP_DAYS, ATTR_REPACK, DOMAIN
from .core import Recorder
SERVICE_PURGE = "purge"
SERVICE_PURGE_ENTITIES = "purge_entities"
SERVICE_ENABLE = "enable"
SERVICE_DISABLE = "disable"
SERVICE_PURGE_SCHEMA = vol.Schema(
{
vol.Optional(ATTR_KEEP_DAYS): cv.positive_int,
vol.Optional(ATTR_REPACK, default=False): cv.boolean,
vol.Optional(ATTR_APPLY_FILTER, default=False): cv.boolean,
}
)
ATTR_DOMAINS = "domains"
ATTR_ENTITY_GLOBS = "entity_globs"
SERVICE_PURGE_ENTITIES_SCHEMA = vol.Schema(
{
vol.Optional(ATTR_DOMAINS, default=[]): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_ENTITY_GLOBS, default=[]): vol.All(
cv.ensure_list, [cv.string]
),
}
).extend(cv.ENTITY_SERVICE_FIELDS)
SERVICE_ENABLE_SCHEMA = vol.Schema({})
SERVICE_DISABLE_SCHEMA = vol.Schema({})
@callback
def _async_register_purge_service(hass: HomeAssistant, instance: Recorder) -> None:
async def async_handle_purge_service(service: ServiceCall) -> None:
"""Handle calls to the purge service."""
instance.do_adhoc_purge(**service.data)
hass.services.async_register(
DOMAIN, SERVICE_PURGE, async_handle_purge_service, schema=SERVICE_PURGE_SCHEMA
)
@callback
def _async_register_purge_entities_service(
hass: HomeAssistant, instance: Recorder
) -> None:
async def async_handle_purge_entities_service(service: ServiceCall) -> None:
"""Handle calls to the purge entities service."""
entity_ids = await async_extract_entity_ids(hass, service)
domains = service.data.get(ATTR_DOMAINS, [])
entity_globs = service.data.get(ATTR_ENTITY_GLOBS, [])
instance.do_adhoc_purge_entities(entity_ids, domains, entity_globs)
hass.services.async_register(
DOMAIN,
SERVICE_PURGE_ENTITIES,
async_handle_purge_entities_service,
schema=SERVICE_PURGE_ENTITIES_SCHEMA,
)
@callback
def _async_register_enable_service(hass: HomeAssistant, instance: Recorder) -> None:
async def async_handle_enable_service(service: ServiceCall) -> None:
instance.set_enable(True)
hass.services.async_register(
DOMAIN,
SERVICE_ENABLE,
async_handle_enable_service,
schema=SERVICE_ENABLE_SCHEMA,
)
@callback
def _async_register_disable_service(hass: HomeAssistant, instance: Recorder) -> None:
async def async_handle_disable_service(service: ServiceCall) -> None:
instance.set_enable(False)
hass.services.async_register(
DOMAIN,
SERVICE_DISABLE,
async_handle_disable_service,
schema=SERVICE_DISABLE_SCHEMA,
)
@callback
def async_register_services(hass: HomeAssistant, instance: Recorder) -> None:
"""Register recorder services."""
_async_register_purge_service(hass, instance)
_async_register_purge_entities_service(hass, instance)
_async_register_enable_service(hass, instance)
_async_register_disable_service(hass, instance)

View file

@ -1831,6 +1831,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.recorder.services]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.recorder.statistics]
check_untyped_defs = true
disallow_incomplete_defs = true

View file

@ -20,10 +20,6 @@ from homeassistant.components.recorder import (
CONF_DB_URL,
CONFIG_SCHEMA,
DOMAIN,
SERVICE_DISABLE,
SERVICE_ENABLE,
SERVICE_PURGE,
SERVICE_PURGE_ENTITIES,
SQLITE_URL_PREFIX,
Recorder,
get_instance,
@ -38,6 +34,12 @@ from homeassistant.components.recorder.models import (
StatisticsRuns,
process_timestamp,
)
from homeassistant.components.recorder.services import (
SERVICE_DISABLE,
SERVICE_ENABLE,
SERVICE_PURGE,
SERVICE_PURGE_ENTITIES,
)
from homeassistant.components.recorder.util import session_scope
from homeassistant.const import (
EVENT_HOMEASSISTANT_FINAL_WRITE,

View file

@ -19,6 +19,10 @@ from homeassistant.components.recorder.models import (
StatisticsShortTerm,
)
from homeassistant.components.recorder.purge import purge_old_data
from homeassistant.components.recorder.services import (
SERVICE_PURGE,
SERVICE_PURGE_ENTITIES,
)
from homeassistant.components.recorder.tasks import PurgeTask
from homeassistant.components.recorder.util import session_scope
from homeassistant.const import EVENT_STATE_CHANGED, STATE_ON
@ -133,9 +137,7 @@ async def test_purge_old_states_encouters_database_corruption(
"homeassistant.components.recorder.purge.purge_old_data",
side_effect=sqlite3_exception,
):
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, {"keep_days": 0}
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
@ -169,9 +171,7 @@ async def test_purge_old_states_encounters_temporary_mysql_error(
), patch.object(
instance.engine.dialect, "name", "mysql"
):
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, {"keep_days": 0}
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
await async_wait_recording_done(hass)
@ -197,9 +197,7 @@ async def test_purge_old_states_encounters_operational_error(
"homeassistant.components.recorder.purge._purge_old_recorder_runs",
side_effect=exception,
):
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, {"keep_days": 0}
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, {"keep_days": 0})
await hass.async_block_till_done()
await async_wait_recording_done(hass)
await async_wait_recording_done(hass)
@ -452,9 +450,7 @@ async def test_purge_edge_case(
events = session.query(Events).filter(Events.event_type == "EVENT_TEST_PURGE")
assert events.count() == 1
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -722,9 +718,7 @@ async def test_purge_filtered_states(
assert events_keep.count() == 1
# Normal purge doesn't remove excluded entities
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -742,9 +736,7 @@ async def test_purge_filtered_states(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -780,9 +772,7 @@ async def test_purge_filtered_states(
assert session.query(StateAttributes).count() == 11
# Do it again to make sure nothing changes
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -794,9 +784,7 @@ async def test_purge_filtered_states(
assert session.query(StateAttributes).count() == 11
service_data = {"keep_days": 0}
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -844,9 +832,7 @@ async def test_purge_filtered_states_to_empty(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -858,9 +844,7 @@ async def test_purge_filtered_states_to_empty(
# Do it again to make sure nothing changes
# Why do we do this? Should we check the end result?
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -914,9 +898,7 @@ async def test_purge_without_state_attributes_filtered_states_to_empty(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -928,9 +910,7 @@ async def test_purge_without_state_attributes_filtered_states_to_empty(
# Do it again to make sure nothing changes
# Why do we do this? Should we check the end result?
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await async_recorder_block_till_done(hass)
await async_wait_purge_done(hass)
@ -985,9 +965,7 @@ async def test_purge_filtered_events(
assert states.count() == 10
# Normal purge doesn't remove excluded events
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -1005,9 +983,7 @@ async def test_purge_filtered_events(
# Test with 'apply_filter' = True
service_data["apply_filter"] = True
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -1105,9 +1081,7 @@ async def test_purge_filtered_events_state_changed(
assert events_purge.count() == 60
assert states.count() == 63
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE, service_data
)
await hass.services.async_call(recorder.DOMAIN, SERVICE_PURGE, service_data)
await hass.async_block_till_done()
await async_recorder_block_till_done(hass)
@ -1146,7 +1120,7 @@ async def test_purge_entities(
}
await hass.services.async_call(
recorder.DOMAIN, recorder.SERVICE_PURGE_ENTITIES, service_data
recorder.DOMAIN, SERVICE_PURGE_ENTITIES, service_data
)
await hass.async_block_till_done()