From 2fa08ae6abd920bb6b2b7008d537cfeafaff6d74 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sat, 16 Oct 2021 23:53:57 +0200 Subject: [PATCH] Add hassio discovery to VLC telnet (#57815) --- .../components/vlc_telnet/config_flow.py | 38 +++++ .../components/vlc_telnet/strings.json | 8 +- .../vlc_telnet/translations/en.json | 8 +- .../components/vlc_telnet/test_config_flow.py | 152 +++++++++++++++--- 4 files changed, 178 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/vlc_telnet/config_flow.py b/homeassistant/components/vlc_telnet/config_flow.py index 0044995c7db..e86ab635517 100644 --- a/homeassistant/components/vlc_telnet/config_flow.py +++ b/homeassistant/components/vlc_telnet/config_flow.py @@ -70,6 +70,7 @@ class VLCTelnetConfigFlow(ConfigFlow, domain=DOMAIN): VERSION = 1 entry: ConfigEntry | None = None + hassio_discovery: dict[str, Any] | None = None async def async_step_user( self, user_input: dict[str, Any] | None = None @@ -150,6 +151,43 @@ class VLCTelnetConfigFlow(ConfigFlow, domain=DOMAIN): errors=errors, ) + async def async_step_hassio(self, discovery_info: dict[str, Any]) -> FlowResult: + """Handle the discovery step via hassio.""" + await self.async_set_unique_id("hassio") + self._abort_if_unique_id_configured(discovery_info) + + self.hassio_discovery = discovery_info + self.context["title_placeholders"] = {"host": discovery_info[CONF_HOST]} + return await self.async_step_hassio_confirm() + + async def async_step_hassio_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm Supervisor discovery.""" + assert self.hassio_discovery + if user_input is None: + return self.async_show_form( + step_id="hassio_confirm", + data_schema=vol.Schema({}), + description_placeholders={"addon": self.hassio_discovery["addon"]}, + ) + + self.hassio_discovery.pop("addon") + + try: + info = await validate_input(self.hass, self.hassio_discovery) + except CannotConnect: + return self.async_abort(reason="cannot_connect") + except InvalidAuth: + return self.async_abort(reason="invalid_auth") + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + return self.async_abort(reason="unknown") + else: + return self.async_create_entry( + title=info["title"], data=self.hassio_discovery + ) + class CannotConnect(exceptions.HomeAssistantError): """Error to indicate we cannot connect.""" diff --git a/homeassistant/components/vlc_telnet/strings.json b/homeassistant/components/vlc_telnet/strings.json index dbdae9755ea..3a22bd06602 100644 --- a/homeassistant/components/vlc_telnet/strings.json +++ b/homeassistant/components/vlc_telnet/strings.json @@ -15,11 +15,17 @@ "password": "[%key:common::config_flow::data::password%]", "name": "[%key:common::config_flow::data::name%]" } + }, + "hassio_confirm": { + "description": "Do you want to connect to add-on {addon}?" } }, "abort": { "already_configured": "[%key:common::config_flow::abort::already_configured_service%]", - "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]" + "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]", + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", + "unknown": "[%key:common::config_flow::error::unknown%]" }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", diff --git a/homeassistant/components/vlc_telnet/translations/en.json b/homeassistant/components/vlc_telnet/translations/en.json index 3f7cbadb4b7..7802768a2c6 100644 --- a/homeassistant/components/vlc_telnet/translations/en.json +++ b/homeassistant/components/vlc_telnet/translations/en.json @@ -2,7 +2,10 @@ "config": { "abort": { "already_configured": "Service is already configured", - "reauth_successful": "Re-authentication was successful" + "cannot_connect": "Failed to connect", + "invalid_auth": "Invalid authentication", + "reauth_successful": "Re-authentication was successful", + "unknown": "Unexpected error" }, "error": { "cannot_connect": "Failed to connect", @@ -11,6 +14,9 @@ }, "flow_title": "{host}", "step": { + "hassio_confirm": { + "description": "Do you want to connect to add-on {addon}?" + }, "reauth_confirm": { "data": { "password": "Password" diff --git a/tests/components/vlc_telnet/test_config_flow.py b/tests/components/vlc_telnet/test_config_flow.py index 7865648d565..5ef8b6c0400 100644 --- a/tests/components/vlc_telnet/test_config_flow.py +++ b/tests/components/vlc_telnet/test_config_flow.py @@ -10,6 +10,11 @@ import pytest from homeassistant import config_entries from homeassistant.components.vlc_telnet.const import DOMAIN from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import ( + RESULT_TYPE_ABORT, + RESULT_TYPE_CREATE_ENTRY, + RESULT_TYPE_FORM, +) from tests.common import MockConfigEntry @@ -50,7 +55,7 @@ async def test_user_flow( result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == "form" + assert result["type"] == RESULT_TYPE_FORM assert result["errors"] is None with patch("homeassistant.components.vlc_telnet.config_flow.Client.connect"), patch( @@ -67,7 +72,7 @@ async def test_user_flow( ) await hass.async_block_till_done() - assert result["type"] == "create_entry" + assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["title"] == entry_data["host"] assert result["data"] == entry_data assert len(mock_setup_entry.mock_calls) == 1 @@ -75,6 +80,12 @@ async def test_user_flow( async def test_import_flow(hass: HomeAssistant) -> None: """Test successful import flow.""" + test_data = { + "password": "test-password", + "host": "1.1.1.1", + "port": 8888, + "name": "custom name", + } with patch("homeassistant.components.vlc_telnet.config_flow.Client.connect"), patch( "homeassistant.components.vlc_telnet.config_flow.Client.login" ), patch( @@ -86,23 +97,13 @@ async def test_import_flow(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, - data={ - "password": "test-password", - "host": "1.1.1.1", - "port": 8888, - "name": "custom name", - }, + data=test_data, ) await hass.async_block_till_done() - assert result["type"] == "create_entry" - assert result["title"] == "custom name" - assert result["data"] == { - "password": "test-password", - "host": "1.1.1.1", - "port": 8888, - "name": "custom name", - } + assert result["type"] == RESULT_TYPE_CREATE_ENTRY + assert result["title"] == test_data["name"] + assert result["data"] == test_data assert len(mock_setup_entry.mock_calls) == 1 @@ -127,7 +128,7 @@ async def test_abort_already_configured(hass: HomeAssistant, source: str) -> Non data=entry_data, ) - assert result["type"] == "abort" + assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "already_configured" @@ -168,7 +169,7 @@ async def test_errors( {"password": "test-password"}, ) - assert result2["type"] == "form" + assert result2["type"] == RESULT_TYPE_FORM assert result2["errors"] == {"base": error} @@ -208,15 +209,10 @@ async def test_reauth_flow(hass: HomeAssistant) -> None: ) await hass.async_block_till_done() - assert result["type"] == "abort" + assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "reauth_successful" assert len(mock_setup_entry.mock_calls) == 1 - assert dict(entry.data) == { - "password": "new-password", - "host": "1.1.1.1", - "port": 8888, - "name": "custom name", - } + assert dict(entry.data) == {**entry_data, "password": "new-password"} @pytest.mark.parametrize( @@ -268,5 +264,109 @@ async def test_reauth_errors( {"password": "test-password"}, ) - assert result2["type"] == "form" + assert result2["type"] == RESULT_TYPE_FORM assert result2["errors"] == {"base": error} + + +async def test_hassio_flow(hass: HomeAssistant) -> None: + """Test successful hassio flow.""" + with patch("homeassistant.components.vlc_telnet.config_flow.Client.connect"), patch( + "homeassistant.components.vlc_telnet.config_flow.Client.login" + ), patch( + "homeassistant.components.vlc_telnet.config_flow.Client.disconnect" + ), patch( + "homeassistant.components.vlc_telnet.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + test_data = { + "password": "test-password", + "host": "1.1.1.1", + "port": 8888, + "name": "custom name", + "addon": "vlc", + } + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=test_data, + ) + await hass.async_block_till_done() + + assert result["type"] == RESULT_TYPE_FORM + + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert result2["type"] == RESULT_TYPE_CREATE_ENTRY + assert result2["title"] == test_data["name"] + assert result2["data"] == test_data + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_hassio_already_configured(hass: HomeAssistant) -> None: + """Test successful hassio flow.""" + + entry_data = { + "password": "test-password", + "host": "1.1.1.1", + "port": 8888, + "name": "custom name", + "addon": "vlc", + } + + entry = MockConfigEntry(domain=DOMAIN, data=entry_data, unique_id="hassio") + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data=entry_data, + ) + await hass.async_block_till_done() + + assert result["type"] == RESULT_TYPE_ABORT + + +@pytest.mark.parametrize( + "error, connect_side_effect, login_side_effect", + [ + ("invalid_auth", None, AuthError), + ("cannot_connect", ConnectError, None), + ("unknown", Exception, None), + ], +) +async def test_hassio_errors( + hass: HomeAssistant, + error: str, + connect_side_effect: Exception | None, + login_side_effect: Exception | None, +) -> None: + """Test we handle hassio errors.""" + with patch( + "homeassistant.components.vlc_telnet.config_flow.Client.connect", + side_effect=connect_side_effect, + ), patch( + "homeassistant.components.vlc_telnet.config_flow.Client.login", + side_effect=login_side_effect, + ), patch( + "homeassistant.components.vlc_telnet.config_flow.Client.disconnect" + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_HASSIO}, + data={ + "password": "test-password", + "host": "1.1.1.1", + "port": 8888, + "name": "custom name", + "addon": "vlc", + }, + ) + await hass.async_block_till_done() + + assert result["type"] == RESULT_TYPE_FORM + + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + assert result2["type"] == RESULT_TYPE_ABORT + assert result2["reason"] == error