Home Assistant Unofficial Reference 2024.12.1
helpers.py
Go to the documentation of this file.
1 """Helpers to make instant statistics about your history."""
2 
3 from __future__ import annotations
4 
5 import datetime
6 import logging
7 import math
8 
9 from homeassistant.core import callback
10 from homeassistant.exceptions import TemplateError
11 from homeassistant.helpers.template import Template
12 import homeassistant.util.dt as dt_util
13 
14 _LOGGER = logging.getLogger(__name__)
15 
16 
17 DURATION_START = "start"
18 DURATION_END = "end"
19 
20 
21 @callback
23  duration: datetime.timedelta | None,
24  start_template: Template | None,
25  end_template: Template | None,
26 ) -> tuple[datetime.datetime, datetime.datetime]:
27  """Parse the templates and return the period."""
28  bounds: dict[str, datetime.datetime | None] = {
29  DURATION_START: None,
30  DURATION_END: None,
31  }
32  for bound, template in (
33  (DURATION_START, start_template),
34  (DURATION_END, end_template),
35  ):
36  # Parse start
37  if template is None:
38  continue
39  try:
40  rendered = template.async_render()
41  except (TemplateError, TypeError) as ex:
42  if ex.args and not ex.args[0].startswith(
43  "UndefinedError: 'None' has no attribute"
44  ):
45  _LOGGER.error("Error parsing template for field %s", bound, exc_info=ex)
46  raise
47  if isinstance(rendered, str):
48  bounds[bound] = dt_util.parse_datetime(rendered)
49  if bounds[bound] is not None:
50  continue
51  try:
52  bounds[bound] = dt_util.as_local(
53  dt_util.utc_from_timestamp(math.floor(float(rendered)))
54  )
55  except ValueError as ex:
56  raise ValueError(
57  f"Parsing error: {bound} must be a datetime or a timestamp: {ex}"
58  ) from ex
59 
60  start = bounds[DURATION_START]
61  end = bounds[DURATION_END]
62 
63  # Calculate start or end using the duration
64  if start is None:
65  assert end is not None
66  assert duration is not None
67  start = end - duration
68  if end is None:
69  assert start is not None
70  assert duration is not None
71  end = start + duration
72 
73  return start, end
74 
75 
77  value: float, period: tuple[datetime.datetime, datetime.datetime]
78 ) -> float:
79  """Format the ratio of value / period duration."""
80  if len(period) != 2 or period[0] == period[1]:
81  return 0.0
82 
83  ratio = 100 * value / (period[1] - period[0]).total_seconds()
84  return round(ratio, 1)
85 
86 
87 def floored_timestamp(incoming_dt: datetime.datetime) -> float:
88  """Calculate the floored value of a timestamp."""
89  return math.floor(dt_util.as_timestamp(incoming_dt))
float pretty_ratio(float value, tuple[datetime.datetime, datetime.datetime] period)
Definition: helpers.py:78
tuple[datetime.datetime, datetime.datetime] async_calculate_period(datetime.timedelta|None duration, Template|None start_template, Template|None end_template)
Definition: helpers.py:26
float floored_timestamp(datetime.datetime incoming_dt)
Definition: helpers.py:87