Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for recording details."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 import voluptuous as vol
9 
10 from homeassistant.const import (
11  CONF_EXCLUDE,
12  EVENT_RECORDER_5MIN_STATISTICS_GENERATED, # noqa: F401
13  EVENT_RECORDER_HOURLY_STATISTICS_GENERATED, # noqa: F401
14  EVENT_STATE_CHANGED,
15 )
16 from homeassistant.core import HomeAssistant, callback
19  INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA,
20  INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER,
21  convert_include_exclude_filter,
22 )
24  async_process_integration_platforms,
25 )
26 from homeassistant.helpers.recorder import DATA_INSTANCE
27 from homeassistant.helpers.typing import ConfigType
28 from homeassistant.loader import bind_hass
29 from homeassistant.util.event_type import EventType
30 
31 from . import entity_registry, websocket_api
32 from .const import ( # noqa: F401
33  CONF_DB_INTEGRITY_CHECK,
34  DOMAIN,
35  INTEGRATION_PLATFORM_COMPILE_STATISTICS,
36  INTEGRATION_PLATFORM_METHODS,
37  SQLITE_URL_PREFIX,
38  SupportedDialect,
39 )
40 from .core import Recorder
41 from .services import async_register_services
42 from .tasks import AddRecorderPlatformTask
43 from .util import get_instance
44 
45 _LOGGER = logging.getLogger(__name__)
46 
47 
48 DEFAULT_URL = "sqlite:///{hass_config_path}"
49 DEFAULT_DB_FILE = "home-assistant_v2.db"
50 DEFAULT_DB_INTEGRITY_CHECK = True
51 DEFAULT_DB_MAX_RETRIES = 10
52 DEFAULT_DB_RETRY_WAIT = 3
53 DEFAULT_COMMIT_INTERVAL = 5
54 
55 CONF_AUTO_PURGE = "auto_purge"
56 CONF_AUTO_REPACK = "auto_repack"
57 CONF_DB_URL = "db_url"
58 CONF_DB_MAX_RETRIES = "db_max_retries"
59 CONF_DB_RETRY_WAIT = "db_retry_wait"
60 CONF_PURGE_KEEP_DAYS = "purge_keep_days"
61 CONF_PURGE_INTERVAL = "purge_interval"
62 CONF_EVENT_TYPES = "event_types"
63 CONF_COMMIT_INTERVAL = "commit_interval"
64 
65 
66 EXCLUDE_SCHEMA = INCLUDE_EXCLUDE_FILTER_SCHEMA_INNER.extend(
67  {vol.Optional(CONF_EVENT_TYPES): vol.All(cv.ensure_list, [cv.string])}
68 )
69 
70 FILTER_SCHEMA = INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA.extend(
71  {vol.Optional(CONF_EXCLUDE, default=EXCLUDE_SCHEMA({})): EXCLUDE_SCHEMA}
72 )
73 
74 
75 ALLOW_IN_MEMORY_DB = False
76 
77 
78 def validate_db_url(db_url: str) -> Any:
79  """Validate database URL."""
80  # Don't allow on-memory sqlite databases
81  if (
82  db_url == SQLITE_URL_PREFIX
83  or (db_url.startswith(SQLITE_URL_PREFIX) and ":memory:" in db_url)
84  ) and not ALLOW_IN_MEMORY_DB:
85  raise vol.Invalid("In-memory SQLite database is not supported")
86 
87  return db_url
88 
89 
90 CONFIG_SCHEMA = vol.Schema(
91  {
92  vol.Optional(DOMAIN, default=dict): vol.All(
93  cv.deprecated(CONF_PURGE_INTERVAL),
94  cv.deprecated(CONF_DB_INTEGRITY_CHECK),
95  FILTER_SCHEMA.extend(
96  {
97  vol.Optional(CONF_AUTO_PURGE, default=True): cv.boolean,
98  vol.Optional(CONF_AUTO_REPACK, default=True): cv.boolean,
99  vol.Optional(CONF_PURGE_KEEP_DAYS, default=10): vol.All(
100  vol.Coerce(int), vol.Range(min=1)
101  ),
102  vol.Optional(CONF_PURGE_INTERVAL, default=1): cv.positive_int,
103  vol.Optional(CONF_DB_URL): vol.All(cv.string, validate_db_url),
104  vol.Optional(
105  CONF_COMMIT_INTERVAL, default=DEFAULT_COMMIT_INTERVAL
106  ): cv.positive_int,
107  vol.Optional(
108  CONF_DB_MAX_RETRIES, default=DEFAULT_DB_MAX_RETRIES
109  ): cv.positive_int,
110  vol.Optional(
111  CONF_DB_RETRY_WAIT, default=DEFAULT_DB_RETRY_WAIT
112  ): cv.positive_int,
113  vol.Optional(
114  CONF_DB_INTEGRITY_CHECK, default=DEFAULT_DB_INTEGRITY_CHECK
115  ): cv.boolean,
116  }
117  ),
118  )
119  },
120  extra=vol.ALLOW_EXTRA,
121 )
122 
123 
124 @bind_hass
125 def is_entity_recorded(hass: HomeAssistant, entity_id: str) -> bool:
126  """Check if an entity is being recorded.
127 
128  Async friendly.
129  """
130  instance = get_instance(hass)
131  return instance.entity_filter is None or instance.entity_filter(entity_id)
132 
133 
134 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
135  """Set up the recorder."""
136  conf = config[DOMAIN]
137  _filter = convert_include_exclude_filter(conf)
138  entity_filter = None if _filter.empty_filter else _filter.get_filter()
139  auto_purge = conf[CONF_AUTO_PURGE]
140  auto_repack = conf[CONF_AUTO_REPACK]
141  keep_days = conf[CONF_PURGE_KEEP_DAYS]
142  commit_interval = conf[CONF_COMMIT_INTERVAL]
143  db_max_retries = conf[CONF_DB_MAX_RETRIES]
144  db_retry_wait = conf[CONF_DB_RETRY_WAIT]
145  db_url = conf.get(CONF_DB_URL) or DEFAULT_URL.format(
146  hass_config_path=hass.config.path(DEFAULT_DB_FILE)
147  )
148  exclude = conf[CONF_EXCLUDE]
149  exclude_event_types: set[EventType[Any] | str] = set(
150  exclude.get(CONF_EVENT_TYPES, [])
151  )
152  if EVENT_STATE_CHANGED in exclude_event_types:
153  _LOGGER.error("State change events cannot be excluded, use a filter instead")
154  exclude_event_types.remove(EVENT_STATE_CHANGED)
155  instance = hass.data[DATA_INSTANCE] = Recorder(
156  hass=hass,
157  auto_purge=auto_purge,
158  auto_repack=auto_repack,
159  keep_days=keep_days,
160  commit_interval=commit_interval,
161  uri=db_url,
162  db_max_retries=db_max_retries,
163  db_retry_wait=db_retry_wait,
164  entity_filter=entity_filter,
165  exclude_event_types=exclude_event_types,
166  )
167  get_instance.cache_clear()
168  instance.async_initialize()
169  instance.async_register()
170  instance.start()
171  async_register_services(hass, instance)
172  websocket_api.async_setup(hass)
173  entity_registry.async_setup(hass)
174 
175  await _async_setup_integration_platform(hass, instance)
176 
177  return await instance.async_db_ready
178 
179 
181  hass: HomeAssistant, instance: Recorder
182 ) -> None:
183  """Set up a recorder integration platform."""
184 
185  @callback
186  def _process_recorder_platform(
187  hass: HomeAssistant, domain: str, platform: Any
188  ) -> None:
189  """Process a recorder platform."""
190  # If the platform has a compile_statistics method, we need to
191  # add it to the recorder queue to be processed.
192  if any(hasattr(platform, _attr) for _attr in INTEGRATION_PLATFORM_METHODS):
193  instance.queue_task(AddRecorderPlatformTask(domain, platform))
194 
195  await async_process_integration_platforms(hass, DOMAIN, _process_recorder_platform)
None async_register_services(HomeAssistant hass)
Definition: services.py:80
Any validate_db_url(str db_url)
Definition: __init__.py:78
None _async_setup_integration_platform(HomeAssistant hass, Recorder instance)
Definition: __init__.py:182
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:134
bool is_entity_recorded(HomeAssistant hass, str entity_id)
Definition: __init__.py:125
EntityFilter convert_include_exclude_filter(dict[str, dict[str, list[str]]] config)
None async_process_integration_platforms(HomeAssistant hass, str platform_name, Callable[[HomeAssistant, str, Any], Awaitable[None]|None] process_platform, bool wait_for_platforms=False)
Recorder get_instance(HomeAssistant hass)
Definition: recorder.py:74