Add SharkIQ EU region support (#89349)
* SharkIQ Dep & Codeowner Update * Update code owners * Add EU Region Support * Update Config Flow Tests * Standardize Region Comparison Strings * Add Translation Support to Region Selector * Fix Validation Tests
This commit is contained in:
parent
db6f0827aa
commit
38f3b9f165
6 changed files with 80 additions and 18 deletions
|
@ -13,11 +13,11 @@ from sharkiq import (
|
|||
|
||||
from homeassistant import exceptions
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import API_TIMEOUT, DOMAIN, LOGGER, PLATFORMS
|
||||
from .const import API_TIMEOUT, DOMAIN, LOGGER, PLATFORMS, SHARKIQ_REGION_EUROPE
|
||||
from .update_coordinator import SharkIqUpdateCoordinator
|
||||
|
||||
|
||||
|
@ -47,6 +47,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
|||
username=config_entry.data[CONF_USERNAME],
|
||||
password=config_entry.data[CONF_PASSWORD],
|
||||
websession=async_get_clientsession(hass),
|
||||
europe=(config_entry.data[CONF_REGION] == SHARKIQ_REGION_EUROPE),
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
|
@ -11,14 +11,31 @@ from sharkiq import SharkIqAuthError, get_ayla_api
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, core, exceptions
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers import selector
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
SHARKIQ_REGION_DEFAULT,
|
||||
SHARKIQ_REGION_EUROPE,
|
||||
SHARKIQ_REGION_OPTIONS,
|
||||
)
|
||||
|
||||
SHARKIQ_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
|
||||
{
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Required(
|
||||
CONF_REGION, default=SHARKIQ_REGION_DEFAULT
|
||||
): selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=SHARKIQ_REGION_OPTIONS, translation_key="region"
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
@ -30,16 +47,29 @@ async def _validate_input(
|
|||
username=data[CONF_USERNAME],
|
||||
password=data[CONF_PASSWORD],
|
||||
websession=async_get_clientsession(hass),
|
||||
europe=(data[CONF_REGION] == SHARKIQ_REGION_EUROPE),
|
||||
)
|
||||
|
||||
try:
|
||||
async with async_timeout.timeout(10):
|
||||
LOGGER.debug("Initialize connection to Ayla networks API")
|
||||
await ayla_api.async_sign_in()
|
||||
except (asyncio.TimeoutError, aiohttp.ClientError) as errors:
|
||||
raise CannotConnect from errors
|
||||
except (asyncio.TimeoutError, aiohttp.ClientError, TypeError) as error:
|
||||
LOGGER.error(error)
|
||||
raise CannotConnect(
|
||||
"Unable to connect to SharkIQ services. Check your region settings."
|
||||
) from error
|
||||
except SharkIqAuthError as error:
|
||||
raise InvalidAuth from error
|
||||
LOGGER.error(error)
|
||||
raise InvalidAuth(
|
||||
"Username or password incorrect. Please check your credentials."
|
||||
) from error
|
||||
except Exception as error:
|
||||
LOGGER.exception("Unexpected exception")
|
||||
LOGGER.error(error)
|
||||
raise UnknownAuth(
|
||||
"An unknown error occurred. Check your region settings and open an issue on Github if the issue persists."
|
||||
) from error
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return {"title": data[CONF_USERNAME]}
|
||||
|
@ -64,8 +94,7 @@ class SharkIqConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
errors["base"] = "cannot_connect"
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
LOGGER.exception("Unexpected exception")
|
||||
except UnknownAuth: # pylint: disable=broad-except
|
||||
errors["base"] = "unknown"
|
||||
return info, errors
|
||||
|
||||
|
@ -114,3 +143,7 @@ class CannotConnect(exceptions.HomeAssistantError):
|
|||
|
||||
class InvalidAuth(exceptions.HomeAssistantError):
|
||||
"""Error to indicate there is invalid auth."""
|
||||
|
||||
|
||||
class UnknownAuth(exceptions.HomeAssistantError):
|
||||
"""Error to indicate there is an uncaught auth error."""
|
||||
|
|
|
@ -11,3 +11,8 @@ PLATFORMS = [Platform.VACUUM]
|
|||
DOMAIN = "sharkiq"
|
||||
SHARK = "Shark"
|
||||
UPDATE_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
SHARKIQ_REGION_EUROPE = "europe"
|
||||
SHARKIQ_REGION_ELSEWHERE = "elsewhere"
|
||||
SHARKIQ_REGION_DEFAULT = SHARKIQ_REGION_ELSEWHERE
|
||||
SHARKIQ_REGION_OPTIONS = [SHARKIQ_REGION_EUROPE, SHARKIQ_REGION_ELSEWHERE]
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
{
|
||||
"config": {
|
||||
"flow_title": "Add Shark IQ Account",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Sign into your Shark Clean account to control your devices.",
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"region": "Region"
|
||||
},
|
||||
"data_description": {
|
||||
"region": "Shark IQ uses different services in the EU. Select your region to connect to the correct service for your account."
|
||||
}
|
||||
},
|
||||
"reauth": {
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"region": "Region"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -25,5 +32,13 @@
|
|||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"region": {
|
||||
"options": {
|
||||
"europe": "Europe",
|
||||
"elsewhere": "Everywhere Else"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Constants used in shark iq tests."""
|
||||
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME
|
||||
|
||||
# Dummy device dict of the form returned by AylaApi.list_devices()
|
||||
SHARK_DEVICE_DICT = {
|
||||
|
@ -69,6 +69,11 @@ SHARK_PROPERTIES_DICT = {
|
|||
|
||||
TEST_USERNAME = "test-username"
|
||||
TEST_PASSWORD = "test-password"
|
||||
TEST_REGION = "elsewhere"
|
||||
UNIQUE_ID = "foo@bar.com"
|
||||
CONFIG = {CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD}
|
||||
CONFIG = {
|
||||
CONF_USERNAME: TEST_USERNAME,
|
||||
CONF_PASSWORD: TEST_PASSWORD,
|
||||
CONF_REGION: TEST_REGION,
|
||||
}
|
||||
ENTRY_ID = "0123456789abcdef0123456789abcdef"
|
||||
|
|
|
@ -3,13 +3,13 @@ from unittest.mock import patch
|
|||
|
||||
import aiohttp
|
||||
import pytest
|
||||
from sharkiq import AylaApi, SharkIqAuthError
|
||||
from sharkiq import AylaApi, SharkIqAuthError, SharkIqError
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.sharkiq.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONFIG, TEST_PASSWORD, TEST_USERNAME, UNIQUE_ID
|
||||
from .const import CONFIG, TEST_PASSWORD, TEST_REGION, TEST_USERNAME, UNIQUE_ID
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
@ -37,6 +37,7 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||
assert result2["data"] == {
|
||||
"username": TEST_USERNAME,
|
||||
"password": TEST_PASSWORD,
|
||||
"region": TEST_REGION,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
mock_setup_entry.assert_called_once()
|
||||
|
@ -47,7 +48,8 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||
[
|
||||
(SharkIqAuthError, "invalid_auth"),
|
||||
(aiohttp.ClientError, "cannot_connect"),
|
||||
(TypeError, "unknown"),
|
||||
(TypeError, "cannot_connect"),
|
||||
(SharkIqError, "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_form_error(hass: HomeAssistant, exc: Exception, base_error: str) -> None:
|
||||
|
@ -87,7 +89,8 @@ async def test_reauth_success(hass: HomeAssistant) -> None:
|
|||
[
|
||||
(SharkIqAuthError, "form", "errors", "invalid_auth"),
|
||||
(aiohttp.ClientError, "abort", "reason", "cannot_connect"),
|
||||
(TypeError, "abort", "reason", "unknown"),
|
||||
(TypeError, "abort", "reason", "cannot_connect"),
|
||||
(SharkIqError, "abort", "reason", "unknown"),
|
||||
],
|
||||
)
|
||||
async def test_reauth(
|
||||
|
|
Loading…
Add table
Reference in a new issue