Add support for Add-on discovery in rtsp_to_webrtc (#63211)

Co-authored-by: Franck Nijhof <git@frenck.dev>
pull/63389/head
Allen Porter 2022-01-04 08:58:08 -08:00 committed by GitHub
parent 46e61008ea
commit d7b080b285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 163 additions and 12 deletions

View File

@ -9,6 +9,8 @@ import rtsp_to_webrtc
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -22,6 +24,8 @@ DATA_SCHEMA = vol.Schema({vol.Required(DATA_SERVER_URL): str})
class RTSPToWebRTCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""RTSPtoWebRTC config flow."""
_hassio_discovery: dict[str, Any]
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
@ -41,21 +45,11 @@ class RTSPToWebRTCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors={DATA_SERVER_URL: "invalid_url"},
)
errors = {}
client = rtsp_to_webrtc.client.Client(async_get_clientsession(self.hass), url)
try:
await client.heartbeat()
except rtsp_to_webrtc.exceptions.ResponseError as err:
_LOGGER.error("RTSPtoWebRTC server failure: %s", str(err))
errors["base"] = "server_failure"
except rtsp_to_webrtc.exceptions.ClientError as err:
_LOGGER.error("RTSPtoWebRTC communication failure: %s", str(err))
errors["base"] = "server_unreachable"
if errors:
if error_code := await self._test_connection(url):
return self.async_show_form(
step_id="user",
data_schema=DATA_SCHEMA,
errors=errors,
errors={"base": error_code},
)
await self.async_set_unique_id(DOMAIN)
@ -63,3 +57,51 @@ class RTSPToWebRTCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
title=url,
data={DATA_SERVER_URL: url},
)
async def _test_connection(self, url: str) -> str | None:
"""Test the connection and return any relevant errors."""
client = rtsp_to_webrtc.client.Client(async_get_clientsession(self.hass), url)
try:
await client.heartbeat()
except rtsp_to_webrtc.exceptions.ResponseError as err:
_LOGGER.error("RTSPtoWebRTC server failure: %s", str(err))
return "server_failure"
except rtsp_to_webrtc.exceptions.ClientError as err:
_LOGGER.error("RTSPtoWebRTC communication failure: %s", str(err))
return "server_unreachable"
return None
async def async_step_hassio(self, discovery_info: HassioServiceInfo) -> FlowResult:
"""Prepare confiugration for the RTSPtoWebRTC server add-on discovery."""
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
self._hassio_discovery = discovery_info.config
return await self.async_step_hassio_confirm()
async def async_step_hassio_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Confirm Add-on discovery."""
errors = None
if user_input is not None:
# Validate server connection once user has confirmed
host = self._hassio_discovery[CONF_HOST]
port = self._hassio_discovery[CONF_PORT]
url = f"http://{host}:{port}"
if error_code := await self._test_connection(url):
errors = {"base": error_code}
if user_input is None or errors:
# Show initial confirmation or errors from server validation
return self.async_show_form(
step_id="hassio_confirm",
description_placeholders={"addon": self._hassio_discovery["addon"]},
data_schema=vol.Schema({}),
errors=errors,
)
return self.async_create_entry(
title=self._hassio_discovery["addon"],
data={DATA_SERVER_URL: url},
)

View File

@ -7,6 +7,10 @@
"data": {
"server_url": "RTSPtoWebRTC server URL e.g. https://example.com"
}
},
"hassio_confirm": {
"title": "RTSPtoWebRTC via Home Assistant add-on",
"description": "Do you want to configure Home Assistant to connect to the RTSPtoWebRTC server provided by the add-on: {addon}?"
}
},
"error": {

View File

@ -7,6 +7,7 @@ from unittest.mock import patch
import rtsp_to_webrtc
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.rtsp_to_webrtc import DOMAIN
from homeassistant.core import HomeAssistant
@ -31,6 +32,7 @@ async def test_web_full_flow(hass: HomeAssistant) -> None:
result["flow_id"], {"server_url": "https://example.com"}
)
assert result.get("type") == "create_entry"
assert result.get("title") == "https://example.com"
assert "result" in result
assert result["result"].data == {"server_url": "https://example.com"}
@ -108,3 +110,106 @@ async def test_server_failure(hass: HomeAssistant) -> None:
assert result.get("type") == "form"
assert result.get("step_id") == "user"
assert result.get("errors") == {"base": "server_failure"}
async def test_hassio_discovery(hass):
"""Test supervisor add-on discovery."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
data=HassioServiceInfo(
config={
"addon": "RTSPtoWebRTC",
"host": "fake-server",
"port": 8083,
}
),
context={"source": config_entries.SOURCE_HASSIO},
)
assert result.get("type") == "form"
assert result.get("step_id") == "hassio_confirm"
assert result.get("description_placeholders") == {"addon": "RTSPtoWebRTC"}
with patch("rtsp_to_webrtc.client.Client.heartbeat"), patch(
"homeassistant.components.rtsp_to_webrtc.async_setup_entry",
return_value=True,
) as mock_setup:
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
await hass.async_block_till_done()
assert result.get("type") == "create_entry"
assert result.get("title") == "RTSPtoWebRTC"
assert "result" in result
assert result["result"].data == {"server_url": "http://fake-server:8083"}
assert len(mock_setup.mock_calls) == 1
async def test_hassio_single_config_entry(hass: HomeAssistant) -> None:
"""Test supervisor add-on discovery only allows a single entry."""
old_entry = MockConfigEntry(domain=DOMAIN, data={"example": True})
old_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
data=HassioServiceInfo(
config={
"addon": "RTSPtoWebRTC",
"host": "fake-server",
"port": 8083,
}
),
context={"source": config_entries.SOURCE_HASSIO},
)
assert result.get("type") == "abort"
assert result.get("reason") == "single_instance_allowed"
async def test_hassio_ignored(hass: HomeAssistant) -> None:
"""Test ignoring superversor add-on discovery."""
old_entry = MockConfigEntry(domain=DOMAIN, source=config_entries.SOURCE_IGNORE)
old_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
data=HassioServiceInfo(
config={
"addon": "RTSPtoWebRTC",
"host": "fake-server",
"port": 8083,
}
),
context={"source": config_entries.SOURCE_HASSIO},
)
assert result.get("type") == "abort"
assert result.get("reason") == "single_instance_allowed"
async def test_hassio_discovery_server_failure(hass: HomeAssistant) -> None:
"""Test server failure during supvervisor add-on discovery shows an error."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
data=HassioServiceInfo(
config={
"addon": "RTSPtoWebRTC",
"host": "fake-server",
"port": 8083,
}
),
context={"source": config_entries.SOURCE_HASSIO},
)
assert result.get("type") == "form"
assert result.get("step_id") == "hassio_confirm"
assert not result.get("errors")
assert "flow_id" in result
with patch(
"rtsp_to_webrtc.client.Client.heartbeat",
side_effect=rtsp_to_webrtc.exceptions.ResponseError(),
):
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
assert result.get("type") == "form"
assert result.get("step_id") == "hassio_confirm"
assert result.get("errors") == {"base": "server_failure"}