mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 02:37:08 +00:00
Add enable and disable services for recorder (#45778)
This commit is contained in:
parent
470121e5b0
commit
44293a3738
@ -48,6 +48,8 @@ from .util import (
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SERVICE_PURGE = "purge"
|
SERVICE_PURGE = "purge"
|
||||||
|
SERVICE_ENABLE = "enable"
|
||||||
|
SERVICE_DISABLE = "disable"
|
||||||
|
|
||||||
ATTR_KEEP_DAYS = "keep_days"
|
ATTR_KEEP_DAYS = "keep_days"
|
||||||
ATTR_REPACK = "repack"
|
ATTR_REPACK = "repack"
|
||||||
@ -58,6 +60,8 @@ SERVICE_PURGE_SCHEMA = vol.Schema(
|
|||||||
vol.Optional(ATTR_REPACK, default=False): cv.boolean,
|
vol.Optional(ATTR_REPACK, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
SERVICE_ENABLE_SCHEMA = vol.Schema({})
|
||||||
|
SERVICE_DISABLE_SCHEMA = vol.Schema({})
|
||||||
|
|
||||||
DEFAULT_URL = "sqlite:///{hass_config_path}"
|
DEFAULT_URL = "sqlite:///{hass_config_path}"
|
||||||
DEFAULT_DB_FILE = "home-assistant_v2.db"
|
DEFAULT_DB_FILE = "home-assistant_v2.db"
|
||||||
@ -199,6 +203,23 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
DOMAIN, SERVICE_PURGE, async_handle_purge_service, schema=SERVICE_PURGE_SCHEMA
|
DOMAIN, SERVICE_PURGE, async_handle_purge_service, schema=SERVICE_PURGE_SCHEMA
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_handle_enable_sevice(service):
|
||||||
|
instance.set_enable(True)
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN, SERVICE_ENABLE, async_handle_enable_sevice, schema=SERVICE_ENABLE_SCHEMA
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_handle_disable_service(service):
|
||||||
|
instance.set_enable(False)
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_DISABLE,
|
||||||
|
async_handle_disable_service,
|
||||||
|
schema=SERVICE_DISABLE_SCHEMA,
|
||||||
|
)
|
||||||
|
|
||||||
return await instance.async_db_ready
|
return await instance.async_db_ready
|
||||||
|
|
||||||
|
|
||||||
@ -255,6 +276,12 @@ class Recorder(threading.Thread):
|
|||||||
self.get_session = None
|
self.get_session = None
|
||||||
self._completed_database_setup = None
|
self._completed_database_setup = None
|
||||||
|
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
|
def set_enable(self, enable):
|
||||||
|
"""Enable or disable recording events and states."""
|
||||||
|
self.enabled = enable
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_initialize(self):
|
def async_initialize(self):
|
||||||
"""Initialize the recorder."""
|
"""Initialize the recorder."""
|
||||||
@ -413,6 +440,9 @@ class Recorder(threading.Thread):
|
|||||||
self._commit_event_session_or_recover()
|
self._commit_event_session_or_recover()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not self.enabled:
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if event.event_type == EVENT_STATE_CHANGED:
|
if event.event_type == EVENT_STATE_CHANGED:
|
||||||
dbevent = Events.from_event(event, event_data="{}")
|
dbevent = Events.from_event(event, event_data="{}")
|
||||||
|
@ -24,3 +24,9 @@ purge:
|
|||||||
default: false
|
default: false
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
|
|
||||||
|
disable:
|
||||||
|
description: Stop the recording of events and state changes
|
||||||
|
|
||||||
|
enabled:
|
||||||
|
description: Start the recording of events and state changes
|
||||||
|
@ -8,13 +8,17 @@ from sqlalchemy.exc import OperationalError
|
|||||||
from homeassistant.components.recorder import (
|
from homeassistant.components.recorder import (
|
||||||
CONF_DB_URL,
|
CONF_DB_URL,
|
||||||
CONFIG_SCHEMA,
|
CONFIG_SCHEMA,
|
||||||
|
DATA_INSTANCE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
SERVICE_DISABLE,
|
||||||
|
SERVICE_ENABLE,
|
||||||
|
SERVICE_PURGE,
|
||||||
|
SQLITE_URL_PREFIX,
|
||||||
Recorder,
|
Recorder,
|
||||||
run_information,
|
run_information,
|
||||||
run_information_from_instance,
|
run_information_from_instance,
|
||||||
run_information_with_session,
|
run_information_with_session,
|
||||||
)
|
)
|
||||||
from homeassistant.components.recorder.const import DATA_INSTANCE, SQLITE_URL_PREFIX
|
|
||||||
from homeassistant.components.recorder.models import Events, RecorderRuns, States
|
from homeassistant.components.recorder.models import Events, RecorderRuns, States
|
||||||
from homeassistant.components.recorder.util import session_scope
|
from homeassistant.components.recorder.util import session_scope
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
@ -24,7 +28,7 @@ from homeassistant.const import (
|
|||||||
STATE_UNLOCKED,
|
STATE_UNLOCKED,
|
||||||
)
|
)
|
||||||
from homeassistant.core import Context, CoreState, callback
|
from homeassistant.core import Context, CoreState, callback
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component, setup_component
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from .common import async_wait_recording_done, corrupt_db_file, wait_recording_done
|
from .common import async_wait_recording_done, corrupt_db_file, wait_recording_done
|
||||||
@ -518,6 +522,155 @@ def test_run_information(hass_recorder):
|
|||||||
assert run_info.closed_incorrect is False
|
assert run_info.closed_incorrect is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_has_services(hass_recorder):
|
||||||
|
"""Test the services exist."""
|
||||||
|
hass = hass_recorder()
|
||||||
|
|
||||||
|
assert hass.services.has_service(DOMAIN, SERVICE_DISABLE)
|
||||||
|
assert hass.services.has_service(DOMAIN, SERVICE_ENABLE)
|
||||||
|
assert hass.services.has_service(DOMAIN, SERVICE_PURGE)
|
||||||
|
|
||||||
|
|
||||||
|
def test_service_disable_events_not_recording(hass, hass_recorder):
|
||||||
|
"""Test that events are not recorded when recorder is disabled using service."""
|
||||||
|
hass = hass_recorder()
|
||||||
|
|
||||||
|
assert hass.services.call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_DISABLE,
|
||||||
|
{},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
event_type = "EVENT_TEST"
|
||||||
|
|
||||||
|
events = []
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def event_listener(event):
|
||||||
|
"""Record events from eventbus."""
|
||||||
|
if event.event_type == event_type:
|
||||||
|
events.append(event)
|
||||||
|
|
||||||
|
hass.bus.listen(MATCH_ALL, event_listener)
|
||||||
|
|
||||||
|
event_data1 = {"test_attr": 5, "test_attr_10": "nice"}
|
||||||
|
hass.bus.fire(event_type, event_data1)
|
||||||
|
wait_recording_done(hass)
|
||||||
|
|
||||||
|
assert len(events) == 1
|
||||||
|
event = events[0]
|
||||||
|
|
||||||
|
with session_scope(hass=hass) as session:
|
||||||
|
db_events = list(session.query(Events).filter_by(event_type=event_type))
|
||||||
|
assert len(db_events) == 0
|
||||||
|
|
||||||
|
assert hass.services.call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ENABLE,
|
||||||
|
{},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
event_data2 = {"attr_one": 5, "attr_two": "nice"}
|
||||||
|
hass.bus.fire(event_type, event_data2)
|
||||||
|
wait_recording_done(hass)
|
||||||
|
|
||||||
|
assert len(events) == 2
|
||||||
|
assert events[0] != events[1]
|
||||||
|
assert events[0].data != events[1].data
|
||||||
|
|
||||||
|
with session_scope(hass=hass) as session:
|
||||||
|
db_events = list(session.query(Events).filter_by(event_type=event_type))
|
||||||
|
assert len(db_events) == 1
|
||||||
|
db_event = db_events[0].to_native()
|
||||||
|
|
||||||
|
event = events[1]
|
||||||
|
|
||||||
|
assert event.event_type == db_event.event_type
|
||||||
|
assert event.data == db_event.data
|
||||||
|
assert event.origin == db_event.origin
|
||||||
|
assert event.time_fired.replace(microsecond=0) == db_event.time_fired.replace(
|
||||||
|
microsecond=0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_service_disable_states_not_recording(hass, hass_recorder):
|
||||||
|
"""Test that state changes are not recorded when recorder is disabled using service."""
|
||||||
|
hass = hass_recorder()
|
||||||
|
|
||||||
|
assert hass.services.call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_DISABLE,
|
||||||
|
{},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.set("test.one", "on", {})
|
||||||
|
wait_recording_done(hass)
|
||||||
|
|
||||||
|
with session_scope(hass=hass) as session:
|
||||||
|
assert len(list(session.query(States))) == 0
|
||||||
|
|
||||||
|
assert hass.services.call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ENABLE,
|
||||||
|
{},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.set("test.two", "off", {})
|
||||||
|
wait_recording_done(hass)
|
||||||
|
|
||||||
|
with session_scope(hass=hass) as session:
|
||||||
|
db_states = list(session.query(States))
|
||||||
|
assert len(db_states) == 1
|
||||||
|
assert db_states[0].event_id > 0
|
||||||
|
assert db_states[0].to_native() == _state_empty_context(hass, "test.two")
|
||||||
|
|
||||||
|
|
||||||
|
def test_service_disable_run_information_recorded(tmpdir):
|
||||||
|
"""Test that runs are still recorded when recorder is disabled."""
|
||||||
|
test_db_file = tmpdir.mkdir("sqlite").join("test_run_info.db")
|
||||||
|
dburl = f"{SQLITE_URL_PREFIX}//{test_db_file}"
|
||||||
|
|
||||||
|
hass = get_test_home_assistant()
|
||||||
|
setup_component(hass, DOMAIN, {DOMAIN: {CONF_DB_URL: dburl}})
|
||||||
|
hass.start()
|
||||||
|
wait_recording_done(hass)
|
||||||
|
|
||||||
|
with session_scope(hass=hass) as session:
|
||||||
|
db_run_info = list(session.query(RecorderRuns))
|
||||||
|
assert len(db_run_info) == 1
|
||||||
|
assert db_run_info[0].start is not None
|
||||||
|
assert db_run_info[0].end is None
|
||||||
|
|
||||||
|
assert hass.services.call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_DISABLE,
|
||||||
|
{},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
wait_recording_done(hass)
|
||||||
|
hass.stop()
|
||||||
|
|
||||||
|
hass = get_test_home_assistant()
|
||||||
|
setup_component(hass, DOMAIN, {DOMAIN: {CONF_DB_URL: dburl}})
|
||||||
|
hass.start()
|
||||||
|
wait_recording_done(hass)
|
||||||
|
|
||||||
|
with session_scope(hass=hass) as session:
|
||||||
|
db_run_info = list(session.query(RecorderRuns))
|
||||||
|
assert len(db_run_info) == 2
|
||||||
|
assert db_run_info[0].start is not None
|
||||||
|
assert db_run_info[0].end is not None
|
||||||
|
assert db_run_info[1].start is not None
|
||||||
|
assert db_run_info[1].end is None
|
||||||
|
|
||||||
|
hass.stop()
|
||||||
|
|
||||||
|
|
||||||
class CannotSerializeMe:
|
class CannotSerializeMe:
|
||||||
"""A class that the JSONEncoder cannot serialize."""
|
"""A class that the JSONEncoder cannot serialize."""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user