diff --git a/CODEOWNERS b/CODEOWNERS index a9a77d12267..5df8a86a876 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1805,6 +1805,8 @@ build.json @home-assistant/supervisor /tests/components/weatherflow_cloud/ @jeeftor /homeassistant/components/weatherkit/ @tjhorner /tests/components/weatherkit/ @tjhorner +/homeassistant/components/web_rtc/ @home-assistant/core +/tests/components/web_rtc/ @home-assistant/core /homeassistant/components/webdav/ @jpbede /tests/components/webdav/ @jpbede /homeassistant/components/webhook/ @home-assistant/core diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 3e91e94db4e..9362faa1093 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -20,7 +20,7 @@ from aiohttp import hdrs, web import attr from propcache.api import cached_property, under_cached_property import voluptuous as vol -from webrtc_models import RTCIceCandidateInit, RTCIceServer +from webrtc_models import RTCIceCandidateInit from homeassistant.components import websocket_api from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView @@ -37,6 +37,7 @@ from homeassistant.components.stream import ( Stream, create_stream, ) +from homeassistant.components.web_rtc import async_get_ice_servers from homeassistant.components.websocket_api import ActiveConnection from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -84,7 +85,6 @@ from .prefs import ( get_dynamic_camera_stream_settings, ) from .webrtc import ( - DATA_ICE_SERVERS, CameraWebRTCProvider, WebRTCAnswer, # noqa: F401 WebRTCCandidate, # noqa: F401 @@ -93,7 +93,6 @@ from .webrtc import ( WebRTCMessage, # noqa: F401 WebRTCSendMessage, async_get_supported_provider, - async_register_ice_servers, async_register_webrtc_provider, # noqa: F401 async_register_ws, ) @@ -400,20 +399,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: SERVICE_RECORD, CAMERA_SERVICE_RECORD, async_handle_record_service ) - @callback - def get_ice_servers() -> list[RTCIceServer]: - if hass.config.webrtc.ice_servers: - return hass.config.webrtc.ice_servers - return [ - RTCIceServer( - urls=[ - "stun:stun.home-assistant.io:3478", - "stun:stun.home-assistant.io:80", - ] - ), - ] - - async_register_ice_servers(hass, get_ice_servers) return True @@ -731,11 +716,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): """Return the WebRTC client configuration and extend it with the registered ice servers.""" config = self._async_get_webrtc_client_configuration() - ice_servers = [ - server - for servers in self.hass.data.get(DATA_ICE_SERVERS, []) - for server in servers() - ] + ice_servers = async_get_ice_servers(self.hass) config.configuration.ice_servers.extend(ice_servers) return config diff --git a/homeassistant/components/camera/manifest.json b/homeassistant/components/camera/manifest.json index fa279a9b205..72ccfd5b02e 100644 --- a/homeassistant/components/camera/manifest.json +++ b/homeassistant/components/camera/manifest.json @@ -3,7 +3,7 @@ "name": "Camera", "after_dependencies": ["media_player"], "codeowners": ["@home-assistant/core"], - "dependencies": ["http"], + "dependencies": ["http", "web_rtc"], "documentation": "https://www.home-assistant.io/integrations/camera", "integration_type": "entity", "quality_scale": "internal", diff --git a/homeassistant/components/camera/webrtc.py b/homeassistant/components/camera/webrtc.py index c2de5eac0a0..796a5160c07 100644 --- a/homeassistant/components/camera/webrtc.py +++ b/homeassistant/components/camera/webrtc.py @@ -4,7 +4,7 @@ from __future__ import annotations from abc import ABC, abstractmethod import asyncio -from collections.abc import Awaitable, Callable, Iterable +from collections.abc import Awaitable, Callable from dataclasses import asdict, dataclass, field from functools import cache, partial, wraps import logging @@ -12,12 +12,7 @@ from typing import TYPE_CHECKING, Any from mashumaro import MissingField import voluptuous as vol -from webrtc_models import ( - RTCConfiguration, - RTCIceCandidate, - RTCIceCandidateInit, - RTCIceServer, -) +from webrtc_models import RTCConfiguration, RTCIceCandidate, RTCIceCandidateInit from homeassistant.components import websocket_api from homeassistant.core import HomeAssistant, callback @@ -38,9 +33,6 @@ _LOGGER = logging.getLogger(__name__) DATA_WEBRTC_PROVIDERS: HassKey[set[CameraWebRTCProvider]] = HassKey( "camera_webrtc_providers" ) -DATA_ICE_SERVERS: HassKey[list[Callable[[], Iterable[RTCIceServer]]]] = HassKey( - "camera_webrtc_ice_servers" -) _WEBRTC = "WebRTC" @@ -367,21 +359,3 @@ async def async_get_supported_provider( return provider return None - - -@callback -def async_register_ice_servers( - hass: HomeAssistant, - get_ice_server_fn: Callable[[], Iterable[RTCIceServer]], -) -> Callable[[], None]: - """Register a ICE server. - - The registering integration is responsible to implement caching if needed. - """ - servers = hass.data.setdefault(DATA_ICE_SERVERS, []) - - def remove() -> None: - servers.remove(get_ice_server_fn) - - servers.append(get_ice_server_fn) - return remove diff --git a/homeassistant/components/cloud/client.py b/homeassistant/components/cloud/client.py index 04353d875fc..550bb9fc8d7 100644 --- a/homeassistant/components/cloud/client.py +++ b/homeassistant/components/cloud/client.py @@ -19,8 +19,8 @@ from homeassistant.components.alexa import ( errors as alexa_errors, smart_home as alexa_smart_home, ) -from homeassistant.components.camera import async_register_ice_servers from homeassistant.components.google_assistant import smart_home as ga +from homeassistant.components.web_rtc import async_register_ice_servers from homeassistant.const import __version__ as HA_VERSION from homeassistant.core import Context, HassJob, HomeAssistant, callback from homeassistant.helpers.aiohttp_client import SERVER_SOFTWARE diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index b148c7957bf..bcf2d015808 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -8,7 +8,7 @@ "google_assistant" ], "codeowners": ["@home-assistant/cloud"], - "dependencies": ["auth", "http", "repairs", "webhook"], + "dependencies": ["auth", "http", "repairs", "webhook", "web_rtc"], "documentation": "https://www.home-assistant.io/integrations/cloud", "integration_type": "system", "iot_class": "cloud_push", diff --git a/homeassistant/components/web_rtc/__init__.py b/homeassistant/components/web_rtc/__init__.py new file mode 100644 index 00000000000..8b684cbda3c --- /dev/null +++ b/homeassistant/components/web_rtc/__init__.py @@ -0,0 +1,138 @@ +"""The WebRTC integration.""" + +from __future__ import annotations + +from collections.abc import Callable, Iterable +from typing import Any + +import voluptuous as vol +from webrtc_models import RTCIceServer + +from homeassistant.components import websocket_api +from homeassistant.const import CONF_URL, CONF_USERNAME +from homeassistant.core import HomeAssistant, callback +from homeassistant.core_config import ( + CONF_CREDENTIAL, + CONF_ICE_SERVERS, + validate_stun_or_turn_url, +) +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.typing import ConfigType +from homeassistant.util.hass_dict import HassKey + +__all__ = [ + "async_get_ice_servers", + "async_register_ice_servers", +] + +DOMAIN = "web_rtc" + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_ICE_SERVERS): vol.All( + cv.ensure_list, + [ + vol.Schema( + { + vol.Required(CONF_URL): vol.All( + cv.ensure_list, [validate_stun_or_turn_url] + ), + vol.Optional(CONF_USERNAME): cv.string, + vol.Optional(CONF_CREDENTIAL): cv.string, + } + ) + ], + ) + } + ) + }, + extra=vol.ALLOW_EXTRA, +) + +DATA_ICE_SERVERS_USER: HassKey[Iterable[RTCIceServer]] = HassKey( + "web_rtc_ice_servers_user" +) +DATA_ICE_SERVERS: HassKey[list[Callable[[], Iterable[RTCIceServer]]]] = HassKey( + "web_rtc_ice_servers" +) + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the WebRTC integration.""" + servers = [ + RTCIceServer( + server[CONF_URL], + server.get(CONF_USERNAME), + server.get(CONF_CREDENTIAL), + ) + for server in config.get(DOMAIN, {}).get(CONF_ICE_SERVERS, []) + ] + if servers: + hass.data[DATA_ICE_SERVERS_USER] = servers + + hass.data[DATA_ICE_SERVERS] = [] + websocket_api.async_register_command(hass, ws_ice_servers) + return True + + +@callback +def async_register_ice_servers( + hass: HomeAssistant, + get_ice_server_fn: Callable[[], Iterable[RTCIceServer]], +) -> Callable[[], None]: + """Register an ICE server. + + The registering integration is responsible to implement caching if needed. + """ + servers = hass.data[DATA_ICE_SERVERS] + + def remove() -> None: + servers.remove(get_ice_server_fn) + + servers.append(get_ice_server_fn) + return remove + + +@callback +def async_get_ice_servers(hass: HomeAssistant) -> list[RTCIceServer]: + """Return all registered ICE servers.""" + servers: list[RTCIceServer] = [] + + if hass.config.webrtc.ice_servers: + servers.extend(hass.config.webrtc.ice_servers) + + if DATA_ICE_SERVERS_USER in hass.data: + servers.extend(hass.data[DATA_ICE_SERVERS_USER]) + + if not servers: + servers = [ + RTCIceServer( + urls=[ + "stun:stun.home-assistant.io:3478", + "stun:stun.home-assistant.io:80", + ] + ), + ] + + for gen_servers in hass.data[DATA_ICE_SERVERS]: + servers.extend(gen_servers()) + + return servers + + +@websocket_api.websocket_command( + { + "type": "web_rtc/ice_servers", + } +) +@callback +def ws_ice_servers( + hass: HomeAssistant, + connection: websocket_api.ActiveConnection, + msg: dict[str, Any], +) -> None: + """Handle get WebRTC ICE servers websocket command.""" + ice_servers = [server.to_dict() for server in async_get_ice_servers(hass)] + connection.send_result(msg["id"], ice_servers) diff --git a/homeassistant/components/web_rtc/manifest.json b/homeassistant/components/web_rtc/manifest.json new file mode 100644 index 00000000000..7944cd7e704 --- /dev/null +++ b/homeassistant/components/web_rtc/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "web_rtc", + "name": "WebRTC", + "codeowners": ["@home-assistant/core"], + "documentation": "https://www.home-assistant.io/integrations/web_rtc", + "integration_type": "system", + "quality_scale": "internal" +} diff --git a/homeassistant/core_config.py b/homeassistant/core_config.py index ce28618e455..bb1386d235e 100644 --- a/homeassistant/core_config.py +++ b/homeassistant/core_config.py @@ -249,7 +249,7 @@ def _validate_currency(data: Any) -> Any: raise -def _validate_stun_or_turn_url(value: Any) -> str: +def validate_stun_or_turn_url(value: Any) -> str: """Validate an URL.""" url_in = str(value) url = urlparse(url_in) @@ -331,7 +331,7 @@ CORE_CONFIG_SCHEMA = vol.All( vol.Schema( { vol.Required(CONF_URL): vol.All( - cv.ensure_list, [_validate_stun_or_turn_url] + cv.ensure_list, [validate_stun_or_turn_url] ), vol.Optional(CONF_USERNAME): cv.string, vol.Optional(CONF_CREDENTIAL): cv.string, diff --git a/script/hassfest/manifest.py b/script/hassfest/manifest.py index 5828ed329bc..05f89f250f6 100644 --- a/script/hassfest/manifest.py +++ b/script/hassfest/manifest.py @@ -115,6 +115,7 @@ NO_IOT_CLASS = [ "tag", "timer", "trace", + "web_rtc", "webhook", "websocket_api", "zone", diff --git a/script/hassfest/quality_scale.py b/script/hassfest/quality_scale.py index e5aca4ef685..cf88614128f 100644 --- a/script/hassfest/quality_scale.py +++ b/script/hassfest/quality_scale.py @@ -2196,6 +2196,7 @@ NO_QUALITY_SCALE = [ "timer", "trace", "usage_prediction", + "web_rtc", "webhook", "websocket_api", "zone", diff --git a/tests/components/camera/test_webrtc.py b/tests/components/camera/test_webrtc.py index 8ddc03dc628..b43c043fdeb 100644 --- a/tests/components/camera/test_webrtc.py +++ b/tests/components/camera/test_webrtc.py @@ -8,7 +8,6 @@ import pytest from webrtc_models import RTCIceCandidate, RTCIceCandidateInit, RTCIceServer from homeassistant.components.camera import ( - DATA_ICE_SERVERS, Camera, CameraWebRTCProvider, StreamType, @@ -17,10 +16,10 @@ from homeassistant.components.camera import ( WebRTCError, WebRTCMessage, WebRTCSendMessage, - async_register_ice_servers, async_register_webrtc_provider, get_camera_from_entity_id, ) +from homeassistant.components.web_rtc import async_register_ice_servers from homeassistant.components.websocket_api import TYPE_RESULT from homeassistant.core import HomeAssistant, callback from homeassistant.core_config import async_process_ha_core_config @@ -101,89 +100,6 @@ async def test_async_register_webrtc_provider_camera_not_loaded( async_register_webrtc_provider(hass, SomeTestProvider()) -@pytest.mark.usefixtures("mock_test_webrtc_cameras") -async def test_async_register_ice_server( - hass: HomeAssistant, -) -> None: - """Test registering an ICE server.""" - # Clear any existing ICE servers - hass.data[DATA_ICE_SERVERS].clear() - - called = 0 - - @callback - def get_ice_servers() -> list[RTCIceServer]: - nonlocal called - called += 1 - return [ - RTCIceServer(urls="stun:example.com"), - RTCIceServer(urls="turn:example.com"), - ] - - unregister = async_register_ice_servers(hass, get_ice_servers) - assert not called - - camera = get_camera_from_entity_id(hass, "camera.async") - config = camera.async_get_webrtc_client_configuration() - - assert config.configuration.ice_servers == [ - RTCIceServer(urls="stun:example.com"), - RTCIceServer(urls="turn:example.com"), - ] - assert called == 1 - - # register another ICE server - called_2 = 0 - - @callback - def get_ice_servers_2() -> list[RTCIceServer]: - nonlocal called_2 - called_2 += 1 - return [ - RTCIceServer( - urls=["stun:example2.com", "turn:example2.com"], - username="user", - credential="pass", - ) - ] - - unregister_2 = async_register_ice_servers(hass, get_ice_servers_2) - - config = camera.async_get_webrtc_client_configuration() - assert config.configuration.ice_servers == [ - RTCIceServer(urls="stun:example.com"), - RTCIceServer(urls="turn:example.com"), - RTCIceServer( - urls=["stun:example2.com", "turn:example2.com"], - username="user", - credential="pass", - ), - ] - assert called == 2 - assert called_2 == 1 - - # unregister the first ICE server - - unregister() - - config = camera.async_get_webrtc_client_configuration() - assert config.configuration.ice_servers == [ - RTCIceServer( - urls=["stun:example2.com", "turn:example2.com"], - username="user", - credential="pass", - ), - ] - assert called == 2 - assert called_2 == 2 - - # unregister the second ICE server - unregister_2() - - config = camera.async_get_webrtc_client_configuration() - assert config.configuration.ice_servers == [] - - @pytest.mark.usefixtures("mock_test_webrtc_cameras") async def test_ws_get_client_config( hass: HomeAssistant, hass_ws_client: WebSocketGenerator diff --git a/tests/components/cloud/snapshots/test_http_api.ambr b/tests/components/cloud/snapshots/test_http_api.ambr index 2876ba20eb8..2249cc6f9fe 100644 --- a/tests/components/cloud/snapshots/test_http_api.ambr +++ b/tests/components/cloud/snapshots/test_http_api.ambr @@ -21,7 +21,7 @@ ## Active Integrations - Built-in integrations: 21 + Built-in integrations: 22 Custom integrations: 1
Built-in integrations @@ -48,6 +48,7 @@ stt | Speech-to-text (STT) system_health | System Health tts | Text-to-speech (TTS) + web_rtc | WebRTC webhook | Webhook
@@ -122,7 +123,7 @@ ## Active Integrations - Built-in integrations: 21 + Built-in integrations: 22 Custom integrations: 0
Built-in integrations @@ -149,6 +150,7 @@ stt | Speech-to-text (STT) system_health | System Health tts | Text-to-speech (TTS) + web_rtc | WebRTC webhook | Webhook
diff --git a/tests/components/web_rtc/__init__.py b/tests/components/web_rtc/__init__.py new file mode 100644 index 00000000000..ff0593f07e9 --- /dev/null +++ b/tests/components/web_rtc/__init__.py @@ -0,0 +1 @@ +"""Tests for the WebRTC integration.""" diff --git a/tests/components/web_rtc/test_init.py b/tests/components/web_rtc/test_init.py new file mode 100644 index 00000000000..01f45977854 --- /dev/null +++ b/tests/components/web_rtc/test_init.py @@ -0,0 +1,250 @@ +"""Test the WebRTC integration.""" + +from webrtc_models import RTCIceServer + +from homeassistant.components.web_rtc import ( + async_get_ice_servers, + async_register_ice_servers, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.core_config import async_process_ha_core_config +from homeassistant.setup import async_setup_component + +from tests.typing import WebSocketGenerator + + +async def test_async_setup(hass: HomeAssistant) -> None: + """Test setting up the web_rtc integration.""" + assert await async_setup_component(hass, "web_rtc", {}) + await hass.async_block_till_done() + + # Verify default ICE servers are registered + ice_servers = async_get_ice_servers(hass) + assert len(ice_servers) == 1 + assert ice_servers[0].urls == [ + "stun:stun.home-assistant.io:3478", + "stun:stun.home-assistant.io:80", + ] + + +async def test_async_setup_custom_ice_servers_core(hass: HomeAssistant) -> None: + """Test setting up web_rtc with custom ICE servers in config.""" + await async_process_ha_core_config( + hass, + {"webrtc": {"ice_servers": [{"url": "stun:custom_stun_server:3478"}]}}, + ) + + assert await async_setup_component(hass, "web_rtc", {}) + await hass.async_block_till_done() + + ice_servers = async_get_ice_servers(hass) + assert len(ice_servers) == 1 + assert ice_servers[0].urls == ["stun:custom_stun_server:3478"] + + +async def test_async_setup_custom_ice_servers_integration(hass: HomeAssistant) -> None: + """Test setting up web_rtc with custom ICE servers in config.""" + assert await async_setup_component( + hass, + "web_rtc", + { + "web_rtc": { + "ice_servers": [ + {"url": "stun:custom_stun_server:3478"}, + { + "url": "stun:custom_stun_server:3478", + "credential": "mock-credential", + }, + { + "url": "stun:custom_stun_server:3478", + "username": "mock-username", + }, + { + "url": "stun:custom_stun_server:3478", + "credential": "mock-credential", + "username": "mock-username", + }, + ] + } + }, + ) + await hass.async_block_till_done() + + ice_servers = async_get_ice_servers(hass) + assert ice_servers == [ + RTCIceServer( + urls=["stun:custom_stun_server:3478"], + ), + RTCIceServer( + urls=["stun:custom_stun_server:3478"], + credential="mock-credential", + ), + RTCIceServer( + urls=["stun:custom_stun_server:3478"], + username="mock-username", + ), + RTCIceServer( + urls=["stun:custom_stun_server:3478"], + username="mock-username", + credential="mock-credential", + ), + ] + + +async def test_async_setup_custom_ice_servers_core_and_integration( + hass: HomeAssistant, +) -> None: + """Test setting up web_rtc with custom ICE servers in config.""" + await async_process_ha_core_config( + hass, + {"webrtc": {"ice_servers": [{"url": "stun:custom_stun_server_core:3478"}]}}, + ) + + assert await async_setup_component( + hass, + "web_rtc", + { + "web_rtc": { + "ice_servers": [{"url": "stun:custom_stun_server_integration:3478"}] + } + }, + ) + await hass.async_block_till_done() + + ice_servers = async_get_ice_servers(hass) + assert ice_servers == [ + RTCIceServer( + urls=["stun:custom_stun_server_core:3478"], + ), + RTCIceServer( + urls=["stun:custom_stun_server_integration:3478"], + ), + ] + + +async def test_async_register_ice_servers(hass: HomeAssistant) -> None: + """Test registering ICE servers.""" + assert await async_setup_component(hass, "web_rtc", {}) + await hass.async_block_till_done() + default_servers = async_get_ice_servers(hass) + + called = 0 + + @callback + def get_ice_servers() -> list[RTCIceServer]: + nonlocal called + called += 1 + return [ + RTCIceServer(urls="stun:example.com"), + RTCIceServer(urls="turn:example.com"), + ] + + unregister = async_register_ice_servers(hass, get_ice_servers) + assert called == 0 + + # Getting ice servers should call the callback + ice_servers = async_get_ice_servers(hass) + assert called == 1 + assert ice_servers == [ + *default_servers, + RTCIceServer(urls="stun:example.com"), + RTCIceServer(urls="turn:example.com"), + ] + + # Unregister and verify servers are removed + unregister() + ice_servers = async_get_ice_servers(hass) + assert ice_servers == default_servers + + +async def test_multiple_ice_server_registrations(hass: HomeAssistant) -> None: + """Test registering multiple ICE server providers.""" + assert await async_setup_component(hass, "web_rtc", {}) + await hass.async_block_till_done() + default_servers = async_get_ice_servers(hass) + + @callback + def get_ice_servers_1() -> list[RTCIceServer]: + return [RTCIceServer(urls="stun:server1.com")] + + @callback + def get_ice_servers_2() -> list[RTCIceServer]: + return [ + RTCIceServer( + urls=["stun:server2.com", "turn:server2.com"], + username="user", + credential="pass", + ) + ] + + unregister_1 = async_register_ice_servers(hass, get_ice_servers_1) + unregister_2 = async_register_ice_servers(hass, get_ice_servers_2) + + ice_servers = async_get_ice_servers(hass) + assert ice_servers == [ + *default_servers, + RTCIceServer(urls="stun:server1.com"), + RTCIceServer( + urls=["stun:server2.com", "turn:server2.com"], + username="user", + credential="pass", + ), + ] + + # Unregister first provider + unregister_1() + ice_servers = async_get_ice_servers(hass) + assert ice_servers == [ + *default_servers, + RTCIceServer( + urls=["stun:server2.com", "turn:server2.com"], + username="user", + credential="pass", + ), + ] + + # Unregister second provider + unregister_2() + ice_servers = async_get_ice_servers(hass) + assert ice_servers == default_servers + + +async def test_ws_ice_servers_with_registered_servers( + hass: HomeAssistant, hass_ws_client: WebSocketGenerator +) -> None: + """Test WebSocket ICE servers endpoint with registered servers.""" + assert await async_setup_component(hass, "web_rtc", {}) + await hass.async_block_till_done() + + @callback + def get_ice_server() -> list[RTCIceServer]: + return [ + RTCIceServer( + urls=["stun:example2.com", "turn:example2.com"], + username="user", + credential="pass", + ) + ] + + async_register_ice_servers(hass, get_ice_server) + + client = await hass_ws_client(hass) + await client.send_json_auto_id({"type": "web_rtc/ice_servers"}) + msg = await client.receive_json() + + # Assert WebSocket response includes registered ICE servers + assert msg["type"] == "result" + assert msg["success"] + assert msg["result"] == [ + { + "urls": [ + "stun:stun.home-assistant.io:3478", + "stun:stun.home-assistant.io:80", + ] + }, + { + "urls": ["stun:example2.com", "turn:example2.com"], + "username": "user", + "credential": "pass", + }, + ] diff --git a/tests/test_core_config.py b/tests/test_core_config.py index b20503121fc..ab099cc2aab 100644 --- a/tests/test_core_config.py +++ b/tests/test_core_config.py @@ -34,8 +34,8 @@ from homeassistant.core_config import ( DATA_CUSTOMIZE, Config, ConfigSource, - _validate_stun_or_turn_url, async_process_ha_core_config, + validate_stun_or_turn_url, ) from homeassistant.helpers import issue_registry as ir from homeassistant.helpers.entity import Entity, EntityPlatformState @@ -175,8 +175,8 @@ def test_webrtc_schema() -> None: assert validated["webrtc"] == validated_webrtc -def test_validate_stun_or_turn_url() -> None: - """Test _validate_stun_or_turn_url.""" +def testvalidate_stun_or_turn_url() -> None: + """Test validate_stun_or_turn_url.""" invalid_urls = ( "custom_stun_server", "custom_stun_server:3478", @@ -203,10 +203,10 @@ def test_validate_stun_or_turn_url() -> None: for url in invalid_urls: with pytest.raises(Invalid): - _validate_stun_or_turn_url(url) + validate_stun_or_turn_url(url) for url in valid_urls: - assert _validate_stun_or_turn_url(url) == url + assert validate_stun_or_turn_url(url) == url def test_customize_glob_is_ordered() -> None: