Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support to serve the Home Assistant API as WSGI application."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 from collections.abc import Collection
7 from dataclasses import dataclass
8 import datetime
9 from functools import partial
10 from ipaddress import IPv4Network, IPv6Network, ip_network
11 import logging
12 import os
13 import socket
14 import ssl
15 from tempfile import NamedTemporaryFile
16 from typing import Any, Final, TypedDict, cast
17 
18 from aiohttp import web
19 from aiohttp.abc import AbstractStreamWriter
20 from aiohttp.http_parser import RawRequestMessage
21 from aiohttp.streams import StreamReader
22 from aiohttp.typedefs import JSONDecoder, StrOrURL
23 from aiohttp.web_exceptions import HTTPMovedPermanently, HTTPRedirection
24 from aiohttp.web_protocol import RequestHandler
25 from cryptography import x509
26 from cryptography.hazmat.primitives import hashes, serialization
27 from cryptography.hazmat.primitives.asymmetric import rsa
28 from cryptography.x509.oid import NameOID
29 import voluptuous as vol
30 from yarl import URL
31 
32 from homeassistant.components.network import async_get_source_ip
33 from homeassistant.const import (
34  EVENT_HOMEASSISTANT_START,
35  EVENT_HOMEASSISTANT_STOP,
36  SERVER_PORT,
37 )
38 from homeassistant.core import Event, HomeAssistant, callback
39 from homeassistant.exceptions import HomeAssistantError
40 from homeassistant.helpers import frame, issue_registry as ir, storage
42 from homeassistant.helpers.http import (
43  KEY_ALLOW_CONFIGURED_CORS,
44  KEY_AUTHENTICATED, # noqa: F401
45  KEY_HASS,
46  HomeAssistantView,
47  current_request,
48 )
49 from homeassistant.helpers.importlib import async_import_module
50 from homeassistant.helpers.network import NoURLAvailableError, get_url
51 from homeassistant.helpers.typing import ConfigType
52 from homeassistant.loader import bind_hass
53 from homeassistant.setup import (
54  SetupPhases,
55  async_start_setup,
56  async_when_setup_or_start,
57 )
58 from homeassistant.util import dt as dt_util, ssl as ssl_util
59 from homeassistant.util.async_ import create_eager_task
60 from homeassistant.util.json import json_loads
61 
62 from .auth import async_setup_auth
63 from .ban import setup_bans
64 from .const import DOMAIN, KEY_HASS_REFRESH_TOKEN_ID, KEY_HASS_USER # noqa: F401
65 from .cors import setup_cors
66 from .decorators import require_admin # noqa: F401
67 from .forwarded import async_setup_forwarded
68 from .headers import setup_headers
69 from .request_context import setup_request_context
70 from .security_filter import setup_security_filter
71 from .static import CACHE_HEADERS, CachingStaticResource
72 from .web_runner import HomeAssistantTCPSite
73 
74 CONF_SERVER_HOST: Final = "server_host"
75 CONF_SERVER_PORT: Final = "server_port"
76 CONF_BASE_URL: Final = "base_url"
77 CONF_SSL_CERTIFICATE: Final = "ssl_certificate"
78 CONF_SSL_PEER_CERTIFICATE: Final = "ssl_peer_certificate"
79 CONF_SSL_KEY: Final = "ssl_key"
80 CONF_CORS_ORIGINS: Final = "cors_allowed_origins"
81 CONF_USE_X_FORWARDED_FOR: Final = "use_x_forwarded_for"
82 CONF_USE_X_FRAME_OPTIONS: Final = "use_x_frame_options"
83 CONF_TRUSTED_PROXIES: Final = "trusted_proxies"
84 CONF_LOGIN_ATTEMPTS_THRESHOLD: Final = "login_attempts_threshold"
85 CONF_IP_BAN_ENABLED: Final = "ip_ban_enabled"
86 CONF_SSL_PROFILE: Final = "ssl_profile"
87 
88 SSL_MODERN: Final = "modern"
89 SSL_INTERMEDIATE: Final = "intermediate"
90 
91 _LOGGER: Final = logging.getLogger(__name__)
92 
93 DEFAULT_DEVELOPMENT: Final = "0"
94 # Cast to be able to load custom cards.
95 # My to be able to check url and version info.
96 DEFAULT_CORS: Final[list[str]] = ["https://cast.home-assistant.io"]
97 NO_LOGIN_ATTEMPT_THRESHOLD: Final = -1
98 
99 MAX_CLIENT_SIZE: Final = 1024**2 * 16
100 MAX_LINE_SIZE: Final = 24570
101 
102 STORAGE_KEY: Final = DOMAIN
103 STORAGE_VERSION: Final = 1
104 SAVE_DELAY: Final = 180
105 
106 _HAS_IPV6 = hasattr(socket, "AF_INET6")
107 _DEFAULT_BIND = ["0.0.0.0", "::"] if _HAS_IPV6 else ["0.0.0.0"]
108 
109 HTTP_SCHEMA: Final = vol.All(
110  cv.deprecated(CONF_BASE_URL),
111  vol.Schema(
112  {
113  vol.Optional(CONF_SERVER_HOST, default=_DEFAULT_BIND): vol.All(
114  cv.ensure_list, vol.Length(min=1), [cv.string]
115  ),
116  vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port,
117  vol.Optional(CONF_BASE_URL): cv.string,
118  vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
119  vol.Optional(CONF_SSL_PEER_CERTIFICATE): cv.isfile,
120  vol.Optional(CONF_SSL_KEY): cv.isfile,
121  vol.Optional(CONF_CORS_ORIGINS, default=DEFAULT_CORS): vol.All(
122  cv.ensure_list, [cv.string]
123  ),
124  vol.Inclusive(CONF_USE_X_FORWARDED_FOR, "proxy"): cv.boolean,
125  vol.Inclusive(CONF_TRUSTED_PROXIES, "proxy"): vol.All(
126  cv.ensure_list, [ip_network]
127  ),
128  vol.Optional(
129  CONF_LOGIN_ATTEMPTS_THRESHOLD, default=NO_LOGIN_ATTEMPT_THRESHOLD
130  ): vol.Any(cv.positive_int, NO_LOGIN_ATTEMPT_THRESHOLD),
131  vol.Optional(CONF_IP_BAN_ENABLED, default=True): cv.boolean,
132  vol.Optional(CONF_SSL_PROFILE, default=SSL_MODERN): vol.In(
133  [SSL_INTERMEDIATE, SSL_MODERN]
134  ),
135  vol.Optional(CONF_USE_X_FRAME_OPTIONS, default=True): cv.boolean,
136  }
137  ),
138 )
139 
140 CONFIG_SCHEMA: Final = vol.Schema({DOMAIN: HTTP_SCHEMA}, extra=vol.ALLOW_EXTRA)
141 
142 
143 @dataclass(slots=True)
145  """Configuration for a static path."""
146 
147  url_path: str
148  path: str
149  cache_headers: bool = True
150 
151 
152 _STATIC_CLASSES = {
153  True: CachingStaticResource,
154  False: web.StaticResource,
155 }
156 
157 
158 class ConfData(TypedDict, total=False):
159  """Typed dict for config data."""
160 
161  server_host: list[str]
162  server_port: int
163  base_url: str
164  ssl_certificate: str
165  ssl_peer_certificate: str
166  ssl_key: str
167  cors_allowed_origins: list[str]
168  use_x_forwarded_for: bool
169  use_x_frame_options: bool
170  trusted_proxies: list[IPv4Network | IPv6Network]
171  login_attempts_threshold: int
172  ip_ban_enabled: bool
173  ssl_profile: str
174 
175 
176 @bind_hass
177 async def async_get_last_config(hass: HomeAssistant) -> dict[str, Any] | None:
178  """Return the last known working config."""
179  store = storage.Store[dict[str, Any]](hass, STORAGE_VERSION, STORAGE_KEY)
180  return await store.async_load()
181 
182 
183 class ApiConfig:
184  """Configuration settings for API server."""
185 
186  def __init__(
187  self,
188  local_ip: str,
189  host: str,
190  port: int,
191  use_ssl: bool,
192  ) -> None:
193  """Initialize a new API config object."""
194  self.local_iplocal_ip = local_ip
195  self.hosthost = host
196  self.portport = port
197  self.use_ssluse_ssl = use_ssl
198 
199 
200 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
201  """Set up the HTTP API and debug interface."""
202  # Late import to ensure isal is updated before
203  # we import aiohttp_fast_zlib
204  (await async_import_module(hass, "aiohttp_fast_zlib")).enable()
205 
206  conf: ConfData | None = config.get(DOMAIN)
207 
208  if conf is None:
209  conf = cast(ConfData, HTTP_SCHEMA({}))
210 
211  server_host = conf[CONF_SERVER_HOST]
212  server_port = conf[CONF_SERVER_PORT]
213  ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
214  ssl_peer_certificate = conf.get(CONF_SSL_PEER_CERTIFICATE)
215  ssl_key = conf.get(CONF_SSL_KEY)
216  cors_origins = conf[CONF_CORS_ORIGINS]
217  use_x_forwarded_for = conf.get(CONF_USE_X_FORWARDED_FOR, False)
218  use_x_frame_options = conf[CONF_USE_X_FRAME_OPTIONS]
219  trusted_proxies = conf.get(CONF_TRUSTED_PROXIES) or []
220  is_ban_enabled = conf[CONF_IP_BAN_ENABLED]
221  login_threshold = conf[CONF_LOGIN_ATTEMPTS_THRESHOLD]
222  ssl_profile = conf[CONF_SSL_PROFILE]
223 
224  source_ip_task = create_eager_task(async_get_source_ip(hass))
225 
226  server = HomeAssistantHTTP(
227  hass,
228  server_host=server_host,
229  server_port=server_port,
230  ssl_certificate=ssl_certificate,
231  ssl_peer_certificate=ssl_peer_certificate,
232  ssl_key=ssl_key,
233  trusted_proxies=trusted_proxies,
234  ssl_profile=ssl_profile,
235  )
236  await server.async_initialize(
237  cors_origins=cors_origins,
238  use_x_forwarded_for=use_x_forwarded_for,
239  login_threshold=login_threshold,
240  is_ban_enabled=is_ban_enabled,
241  use_x_frame_options=use_x_frame_options,
242  )
243 
244  async def stop_server(event: Event) -> None:
245  """Stop the server."""
246  await server.stop()
247 
248  async def start_server(*_: Any) -> None:
249  """Start the server."""
250  with async_start_setup(hass, integration="http", phase=SetupPhases.SETUP):
251  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_server)
252  # We already checked it's not None.
253  assert conf is not None
254  await start_http_server_and_save_config(hass, dict(conf), server)
255 
256  async_when_setup_or_start(hass, "frontend", start_server)
257 
258  hass.http = server
259 
260  local_ip = await source_ip_task
261 
262  host = local_ip
263  if server_host is not None:
264  # Assume the first server host name provided as API host
265  host = server_host[0]
266 
267  hass.config.api = ApiConfig(
268  local_ip, host, server_port, ssl_certificate is not None
269  )
270 
271  @callback
272  def _async_check_ssl_issue(_: Event) -> None:
273  if (
274  ssl_certificate is not None
275  and (hass.config.external_url or hass.config.internal_url) is None
276  ):
277  # pylint: disable-next=import-outside-toplevel
278  from homeassistant.components.cloud import (
279  CloudNotAvailable,
280  async_remote_ui_url,
281  )
282 
283  try:
284  async_remote_ui_url(hass)
285  except CloudNotAvailable:
286  ir.async_create_issue(
287  hass,
288  DOMAIN,
289  "ssl_configured_without_configured_urls",
290  is_fixable=False,
291  severity=ir.IssueSeverity.ERROR,
292  translation_key="ssl_configured_without_configured_urls",
293  )
294 
295  hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _async_check_ssl_issue)
296 
297  return True
298 
299 
300 class HomeAssistantRequest(web.Request):
301  """Home Assistant request object."""
302 
303  async def json(self, *, loads: JSONDecoder = json_loads) -> Any:
304  """Return body as JSON."""
305  # json_loads is a wrapper around orjson.loads that handles
306  # bytes and str. We can pass the bytes directly to json_loads.
307  return json_loads(await self.read())
308 
309 
310 class HomeAssistantApplication(web.Application):
311  """Home Assistant application."""
312 
314  self,
315  message: RawRequestMessage,
316  payload: StreamReader,
317  protocol: RequestHandler,
318  writer: AbstractStreamWriter,
319  task: asyncio.Task[None],
320  _cls: type[web.Request] = HomeAssistantRequest,
321  ) -> web.Request:
322  """Create request instance."""
323  return _cls(
324  message,
325  payload,
326  protocol,
327  writer,
328  task,
329  # loop will never be None when called from aiohttp
330  loop=self._loop, # type: ignore[arg-type]
331  client_max_size=self._client_max_size,
332  )
333 
334 
336  path: str, request: web.Request
337 ) -> web.FileResponse:
338  return web.FileResponse(path, headers=CACHE_HEADERS)
339 
340 
341 async def _serve_file(path: str, request: web.Request) -> web.FileResponse:
342  return web.FileResponse(path)
343 
344 
346  """HTTP server for Home Assistant."""
347 
348  def __init__(
349  self,
350  hass: HomeAssistant,
351  ssl_certificate: str | None,
352  ssl_peer_certificate: str | None,
353  ssl_key: str | None,
354  server_host: list[str] | None,
355  server_port: int,
356  trusted_proxies: list[IPv4Network | IPv6Network],
357  ssl_profile: str,
358  ) -> None:
359  """Initialize the HTTP Home Assistant server."""
361  middlewares=[],
362  client_max_size=MAX_CLIENT_SIZE,
363  handler_args={
364  "max_line_size": MAX_LINE_SIZE,
365  "max_field_size": MAX_LINE_SIZE,
366  },
367  )
368  self.hasshass = hass
369  self.ssl_certificatessl_certificate = ssl_certificate
370  self.ssl_peer_certificatessl_peer_certificate = ssl_peer_certificate
371  self.ssl_keyssl_key = ssl_key
372  self.server_hostserver_host = server_host
373  self.server_portserver_port = server_port
374  self.trusted_proxiestrusted_proxies = trusted_proxies
375  self.ssl_profilessl_profile = ssl_profile
376  self.runnerrunner: web.AppRunner | None = None
377  self.sitesite: HomeAssistantTCPSite | None = None
378  self.contextcontext: ssl.SSLContext | None = None
379 
380  async def async_initialize(
381  self,
382  *,
383  cors_origins: list[str],
384  use_x_forwarded_for: bool,
385  login_threshold: int,
386  is_ban_enabled: bool,
387  use_x_frame_options: bool,
388  ) -> None:
389  """Initialize the server."""
390  self.appapp[KEY_HASS] = self.hasshass
391  self.appapp["hass"] = self.hasshass # For backwards compatibility
392 
393  # Order matters, security filters middleware needs to go first,
394  # forwarded middleware needs to go second.
395  setup_security_filter(self.appapp)
396 
397  async_setup_forwarded(self.appapp, use_x_forwarded_for, self.trusted_proxiestrusted_proxies)
398 
399  setup_request_context(self.appapp, current_request)
400 
401  if is_ban_enabled:
402  setup_bans(self.hasshass, self.appapp, login_threshold)
403 
404  await async_setup_auth(self.hasshass, self.appapp)
405 
406  setup_headers(self.appapp, use_x_frame_options)
407  setup_cors(self.appapp, cors_origins)
408 
409  if self.ssl_certificatessl_certificate:
410  self.contextcontext = await self.hasshass.async_add_executor_job(
411  self._create_ssl_context_create_ssl_context
412  )
413 
414  def register_view(self, view: HomeAssistantView | type[HomeAssistantView]) -> None:
415  """Register a view with the WSGI server.
416 
417  The view argument must be a class that inherits from HomeAssistantView.
418  It is optional to instantiate it before registering; this method will
419  handle it either way.
420  """
421  if isinstance(view, type):
422  # Instantiate the view, if needed
423  view = view()
424 
425  if not hasattr(view, "url"):
426  class_name = view.__class__.__name__
427  raise AttributeError(f'{class_name} missing required attribute "url"')
428 
429  if not hasattr(view, "name"):
430  class_name = view.__class__.__name__
431  raise AttributeError(f'{class_name} missing required attribute "name"')
432 
433  view.register(self.hasshass, self.appapp, self.appapp.router)
434 
436  self,
437  url: str,
438  redirect_to: StrOrURL,
439  *,
440  redirect_exc: type[HTTPRedirection] = HTTPMovedPermanently,
441  ) -> None:
442  """Register a redirect with the server.
443 
444  If given this must be either a string or callable. In case of a
445  callable it's called with the url adapter that triggered the match and
446  the values of the URL as keyword arguments and has to return the target
447  for the redirect, otherwise it has to be a string with placeholders in
448  rule syntax.
449  """
450 
451  async def redirect(request: web.Request) -> web.StreamResponse:
452  """Redirect to location."""
453  # Should be instance of aiohttp.web_exceptions._HTTPMove.
454  raise redirect_exc(redirect_to) # type: ignore[arg-type,misc]
455 
456  self.appapp[KEY_ALLOW_CONFIGURED_CORS](
457  self.appapp.router.add_route("GET", url, redirect)
458  )
459 
461  self, configs: Collection[StaticPathConfig]
462  ) -> dict[str, CachingStaticResource | web.StaticResource | None]:
463  """Create a list of static resources."""
464  return {
465  config.url_path: _STATIC_CLASSES[config.cache_headers](
466  config.url_path, config.path
467  )
468  if os.path.isdir(config.path)
469  else None
470  for config in configs
471  }
472 
474  self, configs: Collection[StaticPathConfig]
475  ) -> None:
476  """Register a folder or file to serve as a static path."""
477  resources = await self.hasshass.async_add_executor_job(
478  self._make_static_resources_make_static_resources, configs
479  )
480  self._async_register_static_paths_async_register_static_paths(configs, resources)
481 
482  @callback
484  self,
485  configs: Collection[StaticPathConfig],
486  resources: dict[str, CachingStaticResource | web.StaticResource | None],
487  ) -> None:
488  """Register a folders or files to serve as a static path."""
489  app = self.appapp
490  allow_cors = app[KEY_ALLOW_CONFIGURED_CORS]
491  for config in configs:
492  if resource := resources[config.url_path]:
493  app.router.register_resource(resource)
494  allow_cors(resource)
495 
496  target = (
497  _serve_file_with_cache_headers if config.cache_headers else _serve_file
498  )
499  allow_cors(
500  self.appapp.router.add_route(
501  "GET", config.url_path, partial(target, config.path)
502  )
503  )
504 
506  self, url_path: str, path: str, cache_headers: bool = True
507  ) -> None:
508  """Register a folder or file to serve as a static path."""
509  frame.report_usage(
510  "calls hass.http.register_static_path which is deprecated because "
511  "it does blocking I/O in the event loop, instead "
512  "call `await hass.http.async_register_static_paths("
513  f'[StaticPathConfig("{url_path}", "{path}", {cache_headers})])`',
514  exclude_integrations={"http"},
515  core_behavior=frame.ReportBehavior.LOG,
516  breaks_in_ha_version="2025.7",
517  )
518  configs = [StaticPathConfig(url_path, path, cache_headers)]
519  resources = self._make_static_resources_make_static_resources(configs)
520  self._async_register_static_paths_async_register_static_paths(configs, resources)
521 
522  def _create_ssl_context(self) -> ssl.SSLContext | None:
523  context: ssl.SSLContext | None = None
524  assert self.ssl_certificatessl_certificate is not None
525  try:
526  if self.ssl_profilessl_profile == SSL_INTERMEDIATE:
527  context = ssl_util.server_context_intermediate()
528  else:
529  context = ssl_util.server_context_modern()
530  context.load_cert_chain(self.ssl_certificatessl_certificate, self.ssl_keyssl_key)
531  except OSError as error:
532  if not self.hasshass.config.recovery_mode:
533  raise HomeAssistantError(
534  f"Could not use SSL certificate from {self.ssl_certificate}:"
535  f" {error}"
536  ) from error
537  _LOGGER.error(
538  "Could not read SSL certificate from %s: %s",
539  self.ssl_certificatessl_certificate,
540  error,
541  )
542  try:
543  context = self._create_emergency_ssl_context_create_emergency_ssl_context()
544  except OSError as error2:
545  _LOGGER.error(
546  "Could not create an emergency self signed ssl certificate: %s",
547  error2,
548  )
549  context = None
550  else:
551  _LOGGER.critical(
552  "Home Assistant is running in recovery mode with an emergency self"
553  " signed ssl certificate because the configured SSL certificate was"
554  " not usable"
555  )
556  return context
557 
558  if self.ssl_peer_certificatessl_peer_certificate:
559  if context is None:
560  raise HomeAssistantError(
561  "Failed to create ssl context, no fallback available because a peer"
562  " certificate is required."
563  )
564 
565  context.verify_mode = ssl.CERT_REQUIRED
566  context.load_verify_locations(self.ssl_peer_certificatessl_peer_certificate)
567 
568  return context
569 
570  def _create_emergency_ssl_context(self) -> ssl.SSLContext:
571  """Create an emergency ssl certificate so we can still startup."""
572  context = ssl_util.server_context_modern()
573  host: str
574  try:
575  host = cast(str, URL(get_url(self.hasshass, prefer_external=True)).host)
576  except NoURLAvailableError:
577  host = "homeassistant.local"
578  key = rsa.generate_private_key(
579  public_exponent=65537,
580  key_size=2048,
581  )
582  subject = issuer = x509.Name(
583  [
584  x509.NameAttribute(
585  NameOID.ORGANIZATION_NAME, "Home Assistant Emergency Certificate"
586  ),
587  x509.NameAttribute(NameOID.COMMON_NAME, host),
588  ]
589  )
590  now = dt_util.utcnow()
591  cert = (
592  x509.CertificateBuilder()
593  .subject_name(subject)
594  .issuer_name(issuer)
595  .public_key(key.public_key())
596  .serial_number(x509.random_serial_number())
597  .not_valid_before(now)
598  .not_valid_after(now + datetime.timedelta(days=30))
599  .add_extension(
600  x509.SubjectAlternativeName([x509.DNSName(host)]),
601  critical=False,
602  )
603  .sign(key, hashes.SHA256())
604  )
605  with NamedTemporaryFile() as cert_pem, NamedTemporaryFile() as key_pem:
606  cert_pem.write(cert.public_bytes(serialization.Encoding.PEM))
607  key_pem.write(
608  key.private_bytes(
609  serialization.Encoding.PEM,
610  format=serialization.PrivateFormat.TraditionalOpenSSL,
611  encryption_algorithm=serialization.NoEncryption(),
612  )
613  )
614  cert_pem.flush()
615  key_pem.flush()
616  context.load_cert_chain(cert_pem.name, key_pem.name)
617  return context
618 
619  async def start(self) -> None:
620  """Start the aiohttp server."""
621  # Aiohttp freezes apps after start so that no changes can be made.
622  # However in Home Assistant components can be discovered after boot.
623  # This will now raise a RunTimeError.
624  # To work around this we now prevent the router from getting frozen
625  self.appapp._router.freeze = lambda: None # type: ignore[method-assign] # noqa: SLF001
626 
627  self.runnerrunner = web.AppRunner(
628  self.appapp, handler_cancellation=True, shutdown_timeout=10
629  )
630  await self.runnerrunner.setup()
631 
633  self.runnerrunner, self.server_hostserver_host, self.server_portserver_port, ssl_context=self.contextcontext
634  )
635  try:
636  await self.sitesite.start()
637  except OSError as error:
638  _LOGGER.error(
639  "Failed to create HTTP server at port %d: %s", self.server_portserver_port, error
640  )
641 
642  _LOGGER.info("Now listening on port %d", self.server_portserver_port)
643 
644  async def stop(self) -> None:
645  """Stop the aiohttp server."""
646  if self.sitesite is not None:
647  await self.sitesite.stop()
648  if self.runnerrunner is not None:
649  await self.runnerrunner.cleanup()
650 
651 
653  hass: HomeAssistant, conf: dict, server: HomeAssistantHTTP
654 ) -> None:
655  """Startup the http server and save the config."""
656  await server.start()
657 
658  # If we are set up successful, we store the HTTP settings for recovery mode.
659  store: storage.Store[dict[str, Any]] = storage.Store(
660  hass, STORAGE_VERSION, STORAGE_KEY
661  )
662 
663  if CONF_TRUSTED_PROXIES in conf:
664  conf[CONF_TRUSTED_PROXIES] = [
665  str(cast(IPv4Network | IPv6Network, ip).network_address)
666  for ip in conf[CONF_TRUSTED_PROXIES]
667  ]
668 
669  store.async_delay_save(lambda: conf, SAVE_DELAY)
None __init__(self, str local_ip, str host, int port, bool use_ssl)
Definition: __init__.py:192
web.Request _make_request(self, RawRequestMessage message, StreamReader payload, RequestHandler protocol, AbstractStreamWriter writer, asyncio.Task[None] task, type[web.Request] _cls=HomeAssistantRequest)
Definition: __init__.py:321
None async_initialize(self, *list[str] cors_origins, bool use_x_forwarded_for, int login_threshold, bool is_ban_enabled, bool use_x_frame_options)
Definition: __init__.py:388
None async_register_static_paths(self, Collection[StaticPathConfig] configs)
Definition: __init__.py:475
ssl.SSLContext _create_emergency_ssl_context(self)
Definition: __init__.py:570
ssl.SSLContext|None _create_ssl_context(self)
Definition: __init__.py:522
None __init__(self, HomeAssistant hass, str|None ssl_certificate, str|None ssl_peer_certificate, str|None ssl_key, list[str]|None server_host, int server_port, list[IPv4Network|IPv6Network] trusted_proxies, str ssl_profile)
Definition: __init__.py:358
None register_view(self, HomeAssistantView|type[HomeAssistantView] view)
Definition: __init__.py:414
dict[str, CachingStaticResource|web.StaticResource|None] _make_static_resources(self, Collection[StaticPathConfig] configs)
Definition: __init__.py:462
None _async_register_static_paths(self, Collection[StaticPathConfig] configs, dict[str, CachingStaticResource|web.StaticResource|None] resources)
Definition: __init__.py:487
None register_static_path(self, str url_path, str path, bool cache_headers=True)
Definition: __init__.py:507
None register_redirect(self, str url, StrOrURL redirect_to, *type[HTTPRedirection] redirect_exc=HTTPMovedPermanently)
Definition: __init__.py:441
Any json(self, *JSONDecoder loads=json_loads)
Definition: __init__.py:303
str async_remote_ui_url(HomeAssistant hass)
Definition: __init__.py:227
None async_setup_auth(HomeAssistant hass, Application app)
Definition: auth.py:123
None setup_bans(HomeAssistant hass, Application app, int login_threshold)
Definition: ban.py:56
None setup_cors(Application app, list[str] origins)
Definition: cors.py:37
None async_setup_forwarded(Application app, bool|None use_x_forwarded_for, list[IPv4Network|IPv6Network] trusted_proxies)
Definition: forwarded.py:23
None setup_headers(Application app, bool use_x_frame_options)
Definition: headers.py:14
None setup_request_context(Application app, ContextVar[Request|None] context)
dict[str, Any]|None async_get_last_config(HomeAssistant hass)
Definition: __init__.py:177
web.FileResponse _serve_file_with_cache_headers(str path, web.Request request)
Definition: __init__.py:337
web.FileResponse _serve_file(str path, web.Request request)
Definition: __init__.py:341
None start_http_server_and_save_config(HomeAssistant hass, dict conf, HomeAssistantHTTP server)
Definition: __init__.py:654
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:200
str async_get_source_ip(HomeAssistant hass, str|UndefinedType target_ip=UNDEFINED)
Definition: __init__.py:40
bool setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:72
ModuleType async_import_module(HomeAssistant hass, str name)
Definition: importlib.py:30
str get_url(HomeAssistant hass, *bool require_current_request=False, bool require_ssl=False, bool require_standard_port=False, bool require_cloud=False, bool allow_internal=True, bool allow_external=True, bool allow_cloud=True, bool|None allow_ip=None, bool|None prefer_external=None, bool prefer_cloud=False)
Definition: network.py:131
None async_when_setup_or_start(core.HomeAssistant hass, str component, Callable[[core.HomeAssistant, str], Awaitable[None]] when_setup_cb)
Definition: setup.py:597
Generator[None] async_start_setup(core.HomeAssistant hass, str integration, SetupPhases phase, str|None group=None)
Definition: setup.py:739