Home Assistant Unofficial Reference 2024.12.1
helpers.py
Go to the documentation of this file.
1 """Support for (EMEA/EU-based) Honeywell TCC systems."""
2 
3 from __future__ import annotations
4 
5 from datetime import datetime, timedelta
6 from http import HTTPStatus
7 import logging
8 import re
9 from typing import Any
10 
11 import evohomeasync2 as evo
12 
13 from homeassistant.const import CONF_SCAN_INTERVAL
14 import homeassistant.util.dt as dt_util
15 
16 _LOGGER = logging.getLogger(__name__)
17 
18 
19 def dt_local_to_aware(dt_naive: datetime) -> datetime:
20  """Convert a local/naive datetime to TZ-aware."""
21  dt_aware = dt_util.now() + (dt_naive - datetime.now())
22  if dt_aware.microsecond >= 500000:
23  dt_aware += timedelta(seconds=1)
24  return dt_aware.replace(microsecond=0)
25 
26 
27 def dt_aware_to_naive(dt_aware: datetime) -> datetime:
28  """Convert a TZ-aware datetime to naive/local."""
29  dt_naive = datetime.now() + (dt_aware - dt_util.now())
30  if dt_naive.microsecond >= 500000:
31  dt_naive += timedelta(seconds=1)
32  return dt_naive.replace(microsecond=0)
33 
34 
35 def convert_until(status_dict: dict, until_key: str) -> None:
36  """Reformat a dt str from "%Y-%m-%dT%H:%M:%SZ" as local/aware/isoformat."""
37  if until_key in status_dict and ( # only present for certain modes
38  dt_utc_naive := dt_util.parse_datetime(status_dict[until_key])
39  ):
40  status_dict[until_key] = dt_util.as_local(dt_utc_naive).isoformat()
41 
42 
43 def convert_dict(dictionary: dict[str, Any]) -> dict[str, Any]:
44  """Recursively convert a dict's keys to snake_case."""
45 
46  def convert_key(key: str) -> str:
47  """Convert a string to snake_case."""
48  string = re.sub(r"[\-\.\s]", "_", str(key))
49  return (
50  (string[0]).lower()
51  + re.sub(
52  r"[A-Z]",
53  lambda matched: f"_{matched.group(0).lower()}", # type:ignore[str-bytes-safe]
54  string[1:],
55  )
56  )
57 
58  return {
59  (convert_key(k) if isinstance(k, str) else k): (
60  convert_dict(v) if isinstance(v, dict) else v
61  )
62  for k, v in dictionary.items()
63  }
64 
65 
66 def handle_evo_exception(err: evo.RequestFailed) -> None:
67  """Return False if the exception can't be ignored."""
68 
69  try:
70  raise err
71 
72  except evo.AuthenticationFailed:
73  _LOGGER.error(
74  (
75  "Failed to authenticate with the vendor's server. Check your username"
76  " and password. NB: Some special password characters that work"
77  " correctly via the website will not work via the web API. Message"
78  " is: %s"
79  ),
80  err,
81  )
82 
83  except evo.RequestFailed:
84  if err.status is None:
85  _LOGGER.warning(
86  (
87  "Unable to connect with the vendor's server. "
88  "Check your network and the vendor's service status page. "
89  "Message is: %s"
90  ),
91  err,
92  )
93 
94  elif err.status == HTTPStatus.SERVICE_UNAVAILABLE:
95  _LOGGER.warning(
96  "The vendor says their server is currently unavailable. "
97  "Check the vendor's service status page"
98  )
99 
100  elif err.status == HTTPStatus.TOO_MANY_REQUESTS:
101  _LOGGER.warning(
102  (
103  "The vendor's API rate limit has been exceeded. "
104  "If this message persists, consider increasing the %s"
105  ),
106  CONF_SCAN_INTERVAL,
107  )
108 
109  else:
110  raise # we don't expect/handle any other Exceptions
None handle_evo_exception(evo.RequestFailed err)
Definition: helpers.py:66
datetime dt_local_to_aware(datetime dt_naive)
Definition: helpers.py:19
datetime dt_aware_to_naive(datetime dt_aware)
Definition: helpers.py:27
dict[str, Any] convert_dict(dict[str, Any] dictionary)
Definition: helpers.py:43
None convert_until(dict status_dict, str until_key)
Definition: helpers.py:35