diff --git a/homeassistant/components/mullvad/__init__.py b/homeassistant/components/mullvad/__init__.py index 8d63ffd2221..20a7092d58a 100644 --- a/homeassistant/components/mullvad/__init__.py +++ b/homeassistant/components/mullvad/__init__.py @@ -8,6 +8,7 @@ from mullvad_api import MullvadAPI from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import update_coordinator from .const import DOMAIN @@ -17,7 +18,6 @@ PLATFORMS = ["binary_sensor"] async def async_setup(hass: HomeAssistant, config: dict): """Set up the Mullvad VPN integration.""" - return True @@ -29,14 +29,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: dict): api = await hass.async_add_executor_job(MullvadAPI) return api.data - hass.data[DOMAIN] = update_coordinator.DataUpdateCoordinator( + coordinator = update_coordinator.DataUpdateCoordinator( hass, logging.getLogger(__name__), name=DOMAIN, update_method=async_get_mullvad_api_data, update_interval=timedelta(minutes=1), ) - await hass.data[DOMAIN].async_refresh() + await coordinator.async_refresh() + + if not coordinator.last_update_success: + raise ConfigEntryNotReady + + hass.data[DOMAIN] = coordinator for component in PLATFORMS: hass.async_create_task( diff --git a/homeassistant/components/mullvad/binary_sensor.py b/homeassistant/components/mullvad/binary_sensor.py index 40b9ae5b1a8..f85820cd7d0 100644 --- a/homeassistant/components/mullvad/binary_sensor.py +++ b/homeassistant/components/mullvad/binary_sensor.py @@ -47,6 +47,6 @@ class MullvadBinarySensor(CoordinatorEntity, BinarySensorEntity): return self._name @property - def state(self): + def is_on(self): """Return the state for this binary sensor.""" return self.coordinator.data[self.id] diff --git a/homeassistant/components/mullvad/config_flow.py b/homeassistant/components/mullvad/config_flow.py index d7b6f92c445..674308c1d1a 100644 --- a/homeassistant/components/mullvad/config_flow.py +++ b/homeassistant/components/mullvad/config_flow.py @@ -1,6 +1,8 @@ """Config flow for Mullvad VPN integration.""" import logging +from mullvad_api import MullvadAPI, MullvadAPIError + from homeassistant import config_entries from .const import DOMAIN # pylint:disable=unused-import @@ -19,7 +21,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if self.hass.config_entries.async_entries(DOMAIN): return self.async_abort(reason="already_configured") + errors = {} if user_input is not None: - return self.async_create_entry(title="Mullvad VPN", data=user_input) + try: + await self.hass.async_add_executor_job(MullvadAPI) + except MullvadAPIError: + errors["base"] = "cannot_connect" + except Exception: # pylint: disable=broad-except + errors["base"] = "unknown" + else: + return self.async_create_entry(title="Mullvad VPN", data=user_input) - return self.async_show_form(step_id="user") + return self.async_show_form(step_id="user", errors=errors) diff --git a/homeassistant/components/mullvad/strings.json b/homeassistant/components/mullvad/strings.json index f522c12871f..7910a40ec35 100644 --- a/homeassistant/components/mullvad/strings.json +++ b/homeassistant/components/mullvad/strings.json @@ -5,17 +5,11 @@ }, "error": { "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%]" }, "step": { "user": { - "description": "Set up the Mullvad VPN integration?", - "data": { - "host": "[%key:common::config_flow::data::host%]", - "password": "[%key:common::config_flow::data::password%]", - "username": "[%key:common::config_flow::data::username%]" - } + "description": "Set up the Mullvad VPN integration?" } } } diff --git a/homeassistant/components/mullvad/translations/en.json b/homeassistant/components/mullvad/translations/en.json index fcfa89ef082..45664554aed 100644 --- a/homeassistant/components/mullvad/translations/en.json +++ b/homeassistant/components/mullvad/translations/en.json @@ -5,16 +5,10 @@ }, "error": { "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication", "unknown": "Unexpected error" }, "step": { "user": { - "data": { - "host": "Host", - "password": "Password", - "username": "Username" - }, "description": "Set up the Mullvad VPN integration?" } } diff --git a/tests/components/mullvad/test_config_flow.py b/tests/components/mullvad/test_config_flow.py index 01485da60a0..c101e5a7246 100644 --- a/tests/components/mullvad/test_config_flow.py +++ b/tests/components/mullvad/test_config_flow.py @@ -1,8 +1,11 @@ """Test the Mullvad config flow.""" from unittest.mock import patch +from mullvad_api import MullvadAPIError + from homeassistant import config_entries, setup from homeassistant.components.mullvad.const import DOMAIN +from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_FORM from tests.common import MockConfigEntry @@ -13,15 +16,17 @@ async def test_form_user(hass): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == "form" - assert result["errors"] is None + assert result["type"] == RESULT_TYPE_FORM + assert not result["errors"] with patch( "homeassistant.components.mullvad.async_setup", return_value=True ) as mock_setup, patch( "homeassistant.components.mullvad.async_setup_entry", return_value=True, - ) as mock_setup_entry: + ) as mock_setup_entry, patch( + "homeassistant.components.mullvad.config_flow.MullvadAPI" + ) as mock_mullvad_api: result2 = await hass.config_entries.flow.async_configure( result["flow_id"], {}, @@ -33,6 +38,7 @@ async def test_form_user(hass): assert result2["data"] == {} assert len(mock_setup.mock_calls) == 0 assert len(mock_setup_entry.mock_calls) == 1 + assert len(mock_mullvad_api.mock_calls) == 1 async def test_form_user_only_once(hass): @@ -42,5 +48,47 @@ async def test_form_user_only_once(hass): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["type"] == "abort" + assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "already_configured" + + +async def test_connection_error(hass): + """Test we show an error when we have trouble connecting.""" + await setup.async_setup_component(hass, DOMAIN, {}) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.mullvad.config_flow.MullvadAPI", + side_effect=MullvadAPIError, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {}, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "cannot_connect"} + + +async def test_unknown_error(hass): + """Test we show an error when an unknown error occurs.""" + await setup.async_setup_component(hass, DOMAIN, {}) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.mullvad.config_flow.MullvadAPI", + side_effect=Exception, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {}, + ) + await hass.async_block_till_done() + + assert result2["type"] == RESULT_TYPE_FORM + assert result2["errors"] == {"base": "unknown"}