Add support for Add-on discovery in rtsp_to_webrtc (#63211)
Co-authored-by: Franck Nijhof <git@frenck.dev>pull/63389/head
parent
46e61008ea
commit
d7b080b285
|
@ -9,6 +9,8 @@ import rtsp_to_webrtc
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
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.data_entry_flow import FlowResult
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
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):
|
class RTSPToWebRTCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""RTSPtoWebRTC config flow."""
|
"""RTSPtoWebRTC config flow."""
|
||||||
|
|
||||||
|
_hassio_discovery: dict[str, Any]
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
|
@ -41,21 +45,11 @@ class RTSPToWebRTCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
errors={DATA_SERVER_URL: "invalid_url"},
|
errors={DATA_SERVER_URL: "invalid_url"},
|
||||||
)
|
)
|
||||||
|
|
||||||
errors = {}
|
if error_code := await self._test_connection(url):
|
||||||
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:
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
data_schema=DATA_SCHEMA,
|
data_schema=DATA_SCHEMA,
|
||||||
errors=errors,
|
errors={"base": error_code},
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.async_set_unique_id(DOMAIN)
|
await self.async_set_unique_id(DOMAIN)
|
||||||
|
@ -63,3 +57,51 @@ class RTSPToWebRTCConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
title=url,
|
title=url,
|
||||||
data={DATA_SERVER_URL: 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},
|
||||||
|
)
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
"data": {
|
"data": {
|
||||||
"server_url": "RTSPtoWebRTC server URL e.g. https://example.com"
|
"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": {
|
"error": {
|
||||||
|
|
|
@ -7,6 +7,7 @@ from unittest.mock import patch
|
||||||
import rtsp_to_webrtc
|
import rtsp_to_webrtc
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components.hassio import HassioServiceInfo
|
||||||
from homeassistant.components.rtsp_to_webrtc import DOMAIN
|
from homeassistant.components.rtsp_to_webrtc import DOMAIN
|
||||||
from homeassistant.core import HomeAssistant
|
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"}
|
result["flow_id"], {"server_url": "https://example.com"}
|
||||||
)
|
)
|
||||||
assert result.get("type") == "create_entry"
|
assert result.get("type") == "create_entry"
|
||||||
|
assert result.get("title") == "https://example.com"
|
||||||
assert "result" in result
|
assert "result" in result
|
||||||
assert result["result"].data == {"server_url": "https://example.com"}
|
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("type") == "form"
|
||||||
assert result.get("step_id") == "user"
|
assert result.get("step_id") == "user"
|
||||||
assert result.get("errors") == {"base": "server_failure"}
|
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"}
|
||||||
|
|
Loading…
Reference in New Issue