Home Assistant Unofficial Reference 2024.12.1
data.py
Go to the documentation of this file.
1 """Support for RESTful API."""
2 
3 from __future__ import annotations
4 
5 import logging
6 import ssl
7 
8 import httpx
9 import xmltodict
10 
11 from homeassistant.core import HomeAssistant
12 from homeassistant.helpers import template
13 from homeassistant.helpers.httpx_client import create_async_httpx_client
14 from homeassistant.helpers.json import json_dumps
15 from homeassistant.util.ssl import SSLCipherList
16 
17 from .const import XML_MIME_TYPES
18 
19 DEFAULT_TIMEOUT = 10
20 
21 _LOGGER = logging.getLogger(__name__)
22 
23 
24 class RestData:
25  """Class for handling the data retrieval."""
26 
27  def __init__(
28  self,
29  hass: HomeAssistant,
30  method: str,
31  resource: str,
32  encoding: str,
33  auth: httpx.DigestAuth | tuple[str, str] | None,
34  headers: dict[str, str] | None,
35  params: dict[str, str] | None,
36  data: str | None,
37  verify_ssl: bool,
38  ssl_cipher_list: str,
39  timeout: int = DEFAULT_TIMEOUT,
40  ) -> None:
41  """Initialize the data object."""
42  self._hass_hass = hass
43  self._method_method = method
44  self._resource_resource = resource
45  self._encoding_encoding = encoding
46  self._auth_auth = auth
47  self._headers_headers = headers
48  self._params_params = params
49  self._request_data_request_data = data
50  self._timeout_timeout = timeout
51  self._verify_ssl_verify_ssl = verify_ssl
52  self._ssl_cipher_list_ssl_cipher_list = SSLCipherList(ssl_cipher_list)
53  self._async_client_async_client: httpx.AsyncClient | None = None
54  self.datadata: str | None = None
55  self.last_exceptionlast_exception: Exception | None = None
56  self.headersheaders: httpx.Headers | None = None
57 
58  def set_payload(self, payload: str) -> None:
59  """Set request data."""
60  self._request_data_request_data = payload
61 
62  @property
63  def url(self) -> str:
64  """Get url."""
65  return self._resource_resource
66 
67  def set_url(self, url: str) -> None:
68  """Set url."""
69  self._resource_resource = url
70 
71  def data_without_xml(self) -> str | None:
72  """If the data is an XML string, convert it to a JSON string."""
73  _LOGGER.debug("Data fetched from resource: %s", self.datadata)
74  if (
75  (value := self.datadata) is not None
76  # If the http request failed, headers will be None
77  and (headers := self.headersheaders) is not None
78  and (content_type := headers.get("content-type"))
79  and content_type.startswith(XML_MIME_TYPES)
80  ):
81  value = json_dumps(xmltodict.parse(value))
82  _LOGGER.debug("JSON converted from XML: %s", value)
83  return value
84 
85  async def async_update(self, log_errors: bool = True) -> None:
86  """Get the latest data from REST service with provided method."""
87  if not self._async_client_async_client:
89  self._hass_hass,
90  verify_ssl=self._verify_ssl_verify_ssl,
91  default_encoding=self._encoding_encoding,
92  ssl_cipher_list=self._ssl_cipher_list_ssl_cipher_list,
93  )
94 
95  rendered_headers = template.render_complex(self._headers_headers, parse_result=False)
96  rendered_params = template.render_complex(self._params_params)
97 
98  _LOGGER.debug("Updating from %s", self._resource_resource)
99  try:
100  response = await self._async_client_async_client.request(
101  self._method_method,
102  self._resource_resource,
103  headers=rendered_headers,
104  params=rendered_params,
105  auth=self._auth_auth,
106  content=self._request_data_request_data,
107  timeout=self._timeout_timeout,
108  follow_redirects=True,
109  )
110  self.datadata = response.text
111  self.headersheaders = response.headers
112  except httpx.TimeoutException as ex:
113  if log_errors:
114  _LOGGER.error("Timeout while fetching data: %s", self._resource_resource)
115  self.last_exceptionlast_exception = ex
116  self.datadata = None
117  self.headersheaders = None
118  except httpx.RequestError as ex:
119  if log_errors:
120  _LOGGER.error(
121  "Error fetching data: %s failed with %s", self._resource_resource, ex
122  )
123  self.last_exceptionlast_exception = ex
124  self.datadata = None
125  self.headersheaders = None
126  except ssl.SSLError as ex:
127  if log_errors:
128  _LOGGER.error(
129  "Error connecting to %s failed with %s", self._resource_resource, ex
130  )
131  self.last_exceptionlast_exception = ex
132  self.datadata = None
133  self.headersheaders = None
None __init__(self, HomeAssistant hass, str method, str resource, str encoding, httpx.DigestAuth|tuple[str, str]|None auth, dict[str, str]|None headers, dict[str, str]|None params, str|None data, bool verify_ssl, str ssl_cipher_list, int timeout=DEFAULT_TIMEOUT)
Definition: data.py:40
None set_payload(self, str payload)
Definition: data.py:58
None async_update(self, bool log_errors=True)
Definition: data.py:85
httpx.AsyncClient create_async_httpx_client(HomeAssistant hass, bool verify_ssl=True, bool auto_cleanup=True, SSLCipherList ssl_cipher_list=SSLCipherList.PYTHON_DEFAULT, **Any kwargs)
Definition: httpx_client.py:72
str json_dumps(Any data)
Definition: json.py:149