Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """RTSPtoWebRTC integration with an external RTSPToWebRTC Server.
2 
3 WebRTC uses a direct communication from the client (e.g. a web browser) to a
4 camera device. Home Assistant acts as the signal path for initial set up,
5 passing through the client offer and returning a camera answer, then the client
6 and camera communicate directly.
7 
8 However, not all cameras natively support WebRTC. This integration is a shim
9 for camera devices that support RTSP streams only, relying on an external
10 server RTSPToWebRTC that is a proxy. Home Assistant does not participate in
11 the offer/answer SDP protocol, other than as a signal path pass through.
12 
13 Other integrations may use this integration with these steps:
14 - Check if this integration is loaded
15 - Call is_supported_stream_source for compatibility
16 - Call async_offer_for_stream_source to get back an answer for a client offer
17 """
18 
19 from __future__ import annotations
20 
21 import asyncio
22 import logging
23 
24 from rtsp_to_webrtc.client import get_adaptive_client
25 from rtsp_to_webrtc.exceptions import ClientError, ResponseError
26 from rtsp_to_webrtc.interface import WebRTCClientInterface
27 from webrtc_models import RTCIceServer
28 
29 from homeassistant.components import camera
30 from homeassistant.config_entries import ConfigEntry
31 from homeassistant.core import HomeAssistant, callback
32 from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
33 from homeassistant.helpers import issue_registry as ir
34 from homeassistant.helpers.aiohttp_client import async_get_clientsession
35 
36 _LOGGER = logging.getLogger(__name__)
37 
38 DOMAIN = "rtsp_to_webrtc"
39 DATA_SERVER_URL = "server_url"
40 DATA_UNSUB = "unsub"
41 TIMEOUT = 10
42 CONF_STUN_SERVER = "stun_server"
43 
44 _DEPRECATED = "deprecated"
45 
46 
47 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
48  """Set up RTSPtoWebRTC from a config entry."""
49  hass.data.setdefault(DOMAIN, {})
50  ir.async_create_issue(
51  hass,
52  DOMAIN,
53  _DEPRECATED,
54  breaks_in_ha_version="2025.6.0",
55  is_fixable=False,
56  severity=ir.IssueSeverity.WARNING,
57  translation_key=_DEPRECATED,
58  translation_placeholders={
59  "go2rtc": "[go2rtc](https://www.home-assistant.io/integrations/go2rtc/)",
60  },
61  )
62 
63  client: WebRTCClientInterface
64  try:
65  async with asyncio.timeout(TIMEOUT):
66  client = await get_adaptive_client(
67  async_get_clientsession(hass), entry.data[DATA_SERVER_URL]
68  )
69  except ResponseError as err:
70  raise ConfigEntryNotReady from err
71  except (TimeoutError, ClientError) as err:
72  raise ConfigEntryNotReady from err
73 
74  hass.data[DOMAIN][CONF_STUN_SERVER] = entry.options.get(CONF_STUN_SERVER)
75  if server := entry.options.get(CONF_STUN_SERVER):
76 
77  @callback
78  def get_servers() -> list[RTCIceServer]:
79  return [RTCIceServer(urls=[server])]
80 
81  entry.async_on_unload(camera.async_register_ice_servers(hass, get_servers))
82 
83  async def async_offer_for_stream_source(
84  stream_source: str,
85  offer_sdp: str,
86  stream_id: str,
87  ) -> str:
88  """Handle the signal path for a WebRTC stream.
89 
90  This signal path is used to route the offer created by the client to the
91  proxy server that translates a stream to WebRTC. The communication for
92  the stream itself happens directly between the client and proxy.
93  """
94  try:
95  async with asyncio.timeout(TIMEOUT):
96  return await client.offer_stream_id(stream_id, offer_sdp, stream_source)
97  except TimeoutError as err:
98  raise HomeAssistantError("Timeout talking to RTSPtoWebRTC server") from err
99  except ClientError as err:
100  raise HomeAssistantError(str(err)) from err
101 
102  entry.async_on_unload(
103  camera.async_register_rtsp_to_web_rtc_provider(
104  hass, DOMAIN, async_offer_for_stream_source
105  )
106  )
107  entry.async_on_unload(entry.add_update_listener(async_reload_entry))
108 
109  return True
110 
111 
112 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
113  """Unload a config entry."""
114  if DOMAIN in hass.data:
115  del hass.data[DOMAIN]
116  ir.async_delete_issue(hass, DOMAIN, _DEPRECATED)
117  return True
118 
119 
120 async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
121  """Reload config entry when options change."""
122  if hass.data[DOMAIN][CONF_STUN_SERVER] != entry.options.get(CONF_STUN_SERVER):
123  await hass.config_entries.async_reload(entry.entry_id)
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:112
None async_reload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:120
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:47
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)