Add Aladdin connect config flow (#68304)
* Adding flow and async * Fixes to init * Lint and type * Fixed coveragerc file * Added Test Coverage * Added Update Listener and removed unused code * Wrong integration name in init. * Nothing * Added yaml import flow * Added YAML import functionality * Added back aladdin_connect files to coverage rc * Removed commented code * Clean up error message * Update homeassistant/components/aladdin_connect/__init__.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update homeassistant/components/aladdin_connect/__init__.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update homeassistant/components/aladdin_connect/config_flow.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Updated Documentation errors * recommended change broke cover.py - backed out * Cleaned up unused defenitions * implimented recommended changes from gjohansson * Dev environment cleanup * Raised errors for better recovery, replaced removed update files, utilized PLATFORM vars to init platform * Added back removal * Added Code Owner * Fixed more comment errors and import duplicates * Added test coverage and formated code * Added test coverage for model and init * Added test_cover for full testing coverage * Added await to async call * Added missing asserts to failure tests * Updated tranlsation * Fixed wording in yaml import function, white space in const.py, return from validate_input. * Update homeassistant/components/aladdin_connect/config_flow.py Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com> * "too much" whitespace * Added back mising strings.json errors * Added ConfigFlowReconfig and tests * Finished up reauth config flow and associated tests * Added reauth to strings, removed username from reauth * recommended changes, ran script.translations, added auth test to reauth * put back self.entry.data unpack. * Cleanup for error message, fixed missing "asserts" in tests * Added yaml import assertions * Fixed documentation errors in test_cover. * remove unused string. * revised tests and wording for yaml import * Documentation cleanup. * Changed sideeffect names Co-authored-by: G Johansson <goran.johansson@shiftit.se> Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>pull/71818/head^2
parent
663f6f8340
commit
f6600bbc20
|
@ -1,6 +1,5 @@
|
|||
[run]
|
||||
source = homeassistant
|
||||
|
||||
omit =
|
||||
homeassistant/__main__.py
|
||||
homeassistant/helpers/signal.py
|
||||
|
@ -42,7 +41,6 @@ omit =
|
|||
homeassistant/components/airtouch4/const.py
|
||||
homeassistant/components/airvisual/__init__.py
|
||||
homeassistant/components/airvisual/sensor.py
|
||||
homeassistant/components/aladdin_connect/*
|
||||
homeassistant/components/alarmdecoder/__init__.py
|
||||
homeassistant/components/alarmdecoder/alarm_control_panel.py
|
||||
homeassistant/components/alarmdecoder/binary_sensor.py
|
||||
|
|
|
@ -50,6 +50,8 @@ build.json @home-assistant/supervisor
|
|||
/tests/components/airvisual/ @bachya
|
||||
/homeassistant/components/airzone/ @Noltari
|
||||
/tests/components/airzone/ @Noltari
|
||||
/homeassistant/components/aladdin_connect/ @mkmer
|
||||
/tests/components/aladdin_connect/ @mkmer
|
||||
/homeassistant/components/alarm_control_panel/ @home-assistant/core
|
||||
/tests/components/alarm_control_panel/ @home-assistant/core
|
||||
/homeassistant/components/alert/ @home-assistant/core
|
||||
|
|
|
@ -1 +1,38 @@
|
|||
"""The aladdin_connect component."""
|
||||
import logging
|
||||
from typing import Final
|
||||
|
||||
from aladdin_connect import AladdinConnectClient
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER: Final = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.COVER]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up platform from a ConfigEntry."""
|
||||
username = entry.data[CONF_USERNAME]
|
||||
password = entry.data[CONF_PASSWORD]
|
||||
acc = AladdinConnectClient(username, password)
|
||||
try:
|
||||
if not await hass.async_add_executor_job(acc.login):
|
||||
raise ConfigEntryAuthFailed("Incorrect Password")
|
||||
except (TypeError, KeyError, NameError, ValueError) as ex:
|
||||
_LOGGER.error("%s", ex)
|
||||
raise ConfigEntryNotReady from ex
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = acc
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
"""Config flow for Aladdin Connect cover integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aladdin_connect import AladdinConnectClient
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
}
|
||||
)
|
||||
|
||||
REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str})
|
||||
|
||||
|
||||
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
|
||||
"""
|
||||
acc = AladdinConnectClient(data[CONF_USERNAME], data[CONF_PASSWORD])
|
||||
try:
|
||||
login = await hass.async_add_executor_job(acc.login)
|
||||
except (TypeError, KeyError, NameError, ValueError) as ex:
|
||||
raise ConnectionError from ex
|
||||
else:
|
||||
if not login:
|
||||
raise InvalidAuth
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Aladdin Connect."""
|
||||
|
||||
VERSION = 1
|
||||
entry: config_entries.ConfigEntry | None
|
||||
|
||||
async def async_step_reauth(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle re-authentication with Aladdin Connect."""
|
||||
|
||||
self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
|
||||
return await self.async_step_reauth_confirm()
|
||||
|
||||
async def async_step_reauth_confirm(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Confirm re-authentication with Aladdin Connect."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input:
|
||||
assert self.entry is not None
|
||||
password = user_input[CONF_PASSWORD]
|
||||
data = {
|
||||
CONF_USERNAME: self.entry.data[CONF_USERNAME],
|
||||
CONF_PASSWORD: password,
|
||||
}
|
||||
|
||||
try:
|
||||
await validate_input(self.hass, data)
|
||||
except ConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
else:
|
||||
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self.entry,
|
||||
data={
|
||||
**self.entry.data,
|
||||
CONF_PASSWORD: password,
|
||||
},
|
||||
)
|
||||
await self.hass.config_entries.async_reload(self.entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm",
|
||||
data_schema=REAUTH_SCHEMA,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the initial step."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
||||
)
|
||||
|
||||
errors = {}
|
||||
|
||||
try:
|
||||
await validate_input(self.hass, user_input)
|
||||
except ConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
|
||||
else:
|
||||
await self.async_set_unique_id(
|
||||
user_input["username"].lower(), raise_on_progress=False
|
||||
)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(title="Aladdin Connect", data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||
)
|
||||
|
||||
async def async_step_import(
|
||||
self, import_data: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Import Aladin Connect config from configuration.yaml."""
|
||||
return await self.async_step_user(import_data)
|
||||
|
||||
|
||||
class InvalidAuth(HomeAssistantError):
|
||||
"""Error to indicate there is invalid auth."""
|
|
@ -16,4 +16,5 @@ STATES_MAP: Final[dict[str, str]] = {
|
|||
"closing": STATE_CLOSING,
|
||||
}
|
||||
|
||||
DOMAIN = "aladdin_connect"
|
||||
SUPPORTED_FEATURES: Final = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
|
||||
|
|
|
@ -7,12 +7,12 @@ from typing import Any, Final
|
|||
from aladdin_connect import AladdinConnectClient
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.components.cover import (
|
||||
PLATFORM_SCHEMA as BASE_PLATFORM_SCHEMA,
|
||||
CoverDeviceClass,
|
||||
CoverEntity,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
|
@ -21,11 +21,12 @@ from homeassistant.const import (
|
|||
STATE_OPENING,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .const import NOTIFICATION_ID, NOTIFICATION_TITLE, STATES_MAP, SUPPORTED_FEATURES
|
||||
from .const import DOMAIN, STATES_MAP, SUPPORTED_FEATURES
|
||||
from .model import DoorDevice
|
||||
|
||||
_LOGGER: Final = logging.getLogger(__name__)
|
||||
|
@ -35,33 +36,44 @@ PLATFORM_SCHEMA: Final = BASE_PLATFORM_SCHEMA.extend(
|
|||
)
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Aladdin Connect platform."""
|
||||
|
||||
username: str = config[CONF_USERNAME]
|
||||
password: str = config[CONF_PASSWORD]
|
||||
acc = AladdinConnectClient(username, password)
|
||||
|
||||
try:
|
||||
if not acc.login():
|
||||
raise ValueError("Username or Password is incorrect")
|
||||
add_entities(
|
||||
(AladdinDevice(acc, door) for door in acc.get_doors()),
|
||||
update_before_add=True,
|
||||
"""Set up Aladdin Connect devices yaml depreciated."""
|
||||
_LOGGER.warning(
|
||||
"Configuring Aladdin Connect through yaml is deprecated"
|
||||
"Please remove it from your configuration as it has already been imported to a config entry"
|
||||
)
|
||||
await hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=config,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Aladdin Connect platform."""
|
||||
acc = hass.data[DOMAIN][config_entry.entry_id]
|
||||
try:
|
||||
doors = await hass.async_add_executor_job(acc.get_doors)
|
||||
|
||||
except (TypeError, KeyError, NameError, ValueError) as ex:
|
||||
_LOGGER.error("%s", ex)
|
||||
persistent_notification.create(
|
||||
hass,
|
||||
f"Error: {ex}<br />You will need to restart hass after fixing.",
|
||||
title=NOTIFICATION_TITLE,
|
||||
notification_id=NOTIFICATION_ID,
|
||||
)
|
||||
raise ConfigEntryNotReady from ex
|
||||
|
||||
async_add_entities(
|
||||
(AladdinDevice(acc, door) for door in doors),
|
||||
update_before_add=True,
|
||||
)
|
||||
|
||||
|
||||
class AladdinDevice(CoverEntity):
|
||||
|
@ -71,7 +83,7 @@ class AladdinDevice(CoverEntity):
|
|||
_attr_supported_features = SUPPORTED_FEATURES
|
||||
|
||||
def __init__(self, acc: AladdinConnectClient, device: DoorDevice) -> None:
|
||||
"""Initialize the cover."""
|
||||
"""Initialize the Aladdin Connect cover."""
|
||||
self._acc = acc
|
||||
self._device_id = device["device_id"]
|
||||
self._number = device["door_number"]
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"name": "Aladdin Connect",
|
||||
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
|
||||
"requirements": ["aladdin_connect==0.4"],
|
||||
"codeowners": [],
|
||||
"codeowners": ["@mkmer"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aladdin_connect"]
|
||||
"loggers": ["aladdin_connect"],
|
||||
"config_flow": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"title": "[%key:common::config_flow::title::reauth%]",
|
||||
"description": "The Aladdin Connect integration needs to re-authenticate your account",
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
},
|
||||
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured",
|
||||
"reauth_successful": "Re-authentication was successful"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"invalid_auth": "Invalid authentication"
|
||||
},
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"password": "Password"
|
||||
},
|
||||
"description": "The Aladdin Connect integration needs to re-authenticate your account",
|
||||
"title": "Reauthenticate Integration"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Password",
|
||||
"username": "Username"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ FLOWS = {
|
|||
"airtouch4",
|
||||
"airvisual",
|
||||
"airzone",
|
||||
"aladdin_connect",
|
||||
"alarmdecoder",
|
||||
"almond",
|
||||
"ambee",
|
||||
|
|
|
@ -248,6 +248,9 @@ airthings_cloud==0.1.0
|
|||
# homeassistant.components.airtouch4
|
||||
airtouch4pyapi==1.0.5
|
||||
|
||||
# homeassistant.components.aladdin_connect
|
||||
aladdin_connect==0.4
|
||||
|
||||
# homeassistant.components.ambee
|
||||
ambee==0.4.0
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
"""The tests for Aladdin Connect platforms."""
|
|
@ -0,0 +1,323 @@
|
|||
"""Test the Aladdin Connect config flow."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.aladdin_connect.config_flow import InvalidAuth
|
||||
from homeassistant.components.aladdin_connect.const import DOMAIN
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
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
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "Aladdin Connect"
|
||||
assert result2["data"] == {
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_invalid_auth(hass: HomeAssistant) -> None:
|
||||
"""Test we handle invalid auth."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
side_effect=InvalidAuth,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
||||
"""Test we handle cannot connect error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
side_effect=ConnectionError,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
side_effect=TypeError,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=False,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_form_already_configured(hass):
|
||||
"""Test we handle already configured error."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"},
|
||||
unique_id="test-username",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == config_entries.SOURCE_USER
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_import_flow_success(hass: HomeAssistant) -> None:
|
||||
"""Test a successful import of yaml."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_platform",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_USERNAME: "test-user",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "Aladdin Connect"
|
||||
assert result2["data"] == {
|
||||
CONF_USERNAME: "test-user",
|
||||
CONF_PASSWORD: "test-password",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_reauth_flow(hass: HomeAssistant) -> None:
|
||||
"""Test a successful reauth flow."""
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-username", "password": "test-password"},
|
||||
unique_id="test-username",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": config_entries.SOURCE_REAUTH,
|
||||
"unique_id": mock_entry.unique_id,
|
||||
"entry_id": mock_entry.entry_id,
|
||||
},
|
||||
data={"username": "test-username", "password": "new-password"},
|
||||
)
|
||||
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_platform",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "new-password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_ABORT
|
||||
assert result2["reason"] == "reauth_successful"
|
||||
assert mock_entry.data == {
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "new-password",
|
||||
}
|
||||
|
||||
|
||||
async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None:
|
||||
"""Test a successful reauth flow."""
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-username", "password": "test-password"},
|
||||
unique_id="test-username",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": config_entries.SOURCE_REAUTH,
|
||||
"unique_id": mock_entry.unique_id,
|
||||
"entry_id": mock_entry.entry_id,
|
||||
},
|
||||
data={"username": "test-username", "password": "new-password"},
|
||||
)
|
||||
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_platform",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=False,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "new-password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_reauth_flow_other_error(hass: HomeAssistant) -> None:
|
||||
"""Test an unsuccessful reauth flow."""
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-username", "password": "test-password"},
|
||||
unique_id="test-username",
|
||||
)
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={
|
||||
"source": config_entries.SOURCE_REAUTH,
|
||||
"unique_id": mock_entry.unique_id,
|
||||
"entry_id": mock_entry.entry_id,
|
||||
},
|
||||
data={"username": "test-username", "password": "new-password"},
|
||||
)
|
||||
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_platform",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
side_effect=ValueError,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_entry",
|
||||
return_value=True,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: "new-password"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
|
@ -0,0 +1,272 @@
|
|||
"""Test the Aladdin Connect Cover."""
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.aladdin_connect.const import DOMAIN
|
||||
import homeassistant.components.aladdin_connect.cover as cover
|
||||
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
STATE_CLOSED,
|
||||
STATE_CLOSING,
|
||||
STATE_OPEN,
|
||||
STATE_OPENING,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
YAML_CONFIG = {"username": "test-user", "password": "test-password"}
|
||||
|
||||
DEVICE_CONFIG_OPEN = {
|
||||
"device_id": 533255,
|
||||
"door_number": 1,
|
||||
"name": "home",
|
||||
"status": "open",
|
||||
"link_status": "Connected",
|
||||
}
|
||||
|
||||
DEVICE_CONFIG_OPENING = {
|
||||
"device_id": 533255,
|
||||
"door_number": 1,
|
||||
"name": "home",
|
||||
"status": "opening",
|
||||
"link_status": "Connected",
|
||||
}
|
||||
|
||||
DEVICE_CONFIG_CLOSED = {
|
||||
"device_id": 533255,
|
||||
"door_number": 1,
|
||||
"name": "home",
|
||||
"status": "closed",
|
||||
"link_status": "Connected",
|
||||
}
|
||||
|
||||
DEVICE_CONFIG_CLOSING = {
|
||||
"device_id": 533255,
|
||||
"door_number": 1,
|
||||
"name": "home",
|
||||
"status": "closing",
|
||||
"link_status": "Connected",
|
||||
}
|
||||
|
||||
DEVICE_CONFIG_DISCONNECTED = {
|
||||
"device_id": 533255,
|
||||
"door_number": 1,
|
||||
"name": "home",
|
||||
"status": "open",
|
||||
"link_status": "Disconnected",
|
||||
}
|
||||
|
||||
DEVICE_CONFIG_BAD = {
|
||||
"device_id": 533255,
|
||||
"door_number": 1,
|
||||
"name": "home",
|
||||
"status": "open",
|
||||
}
|
||||
DEVICE_CONFIG_BAD_NO_DOOR = {
|
||||
"device_id": 533255,
|
||||
"door_number": 2,
|
||||
"name": "home",
|
||||
"status": "open",
|
||||
"link_status": "Disconnected",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"side_effect",
|
||||
[
|
||||
(TypeError),
|
||||
(KeyError),
|
||||
(NameError),
|
||||
(ValueError),
|
||||
],
|
||||
)
|
||||
async def test_setup_get_doors_errors(
|
||||
hass: HomeAssistant, side_effect: Exception
|
||||
) -> None:
|
||||
"""Test component setup Get Doors Errors."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
side_effect=side_effect,
|
||||
):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"side_effect",
|
||||
[
|
||||
(TypeError),
|
||||
(KeyError),
|
||||
(NameError),
|
||||
(ValueError),
|
||||
],
|
||||
)
|
||||
async def test_setup_login_error(hass: HomeAssistant, side_effect: Exception) -> None:
|
||||
"""Test component setup Login Errors."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
side_effect=side_effect,
|
||||
):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is False
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_setup_component_noerror(hass: HomeAssistant) -> None:
|
||||
"""Test component setup No Error."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
):
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state == ConfigEntryState.LOADED
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
|
||||
|
||||
async def test_cover_operation(hass: HomeAssistant) -> None:
|
||||
"""Test component setup open cover, close cover."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_OPEN],
|
||||
):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state == ConfigEntryState.LOADED
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert COVER_DOMAIN in hass.config.components
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.open_door",
|
||||
return_value=True,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"cover", "open_cover", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.close_door",
|
||||
return_value=True,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"cover", "close_cover", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_CLOSED],
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
assert hass.states.get("cover.home").state == STATE_CLOSED
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_OPEN],
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
assert hass.states.get("cover.home").state == STATE_OPEN
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_OPENING],
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
assert hass.states.get("cover.home").state == STATE_OPENING
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_CLOSING],
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
assert hass.states.get("cover.home").state == STATE_CLOSING
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_BAD],
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
assert hass.states.get("cover.home").state
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_BAD_NO_DOOR],
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
assert hass.states.get("cover.home").state
|
||||
|
||||
|
||||
async def test_yaml_import(hass: HomeAssistant, caplog: pytest.LogCaptureFixture):
|
||||
"""Test setup YAML import."""
|
||||
assert COVER_DOMAIN not in hass.config.components
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_CLOSED],
|
||||
):
|
||||
await cover.async_setup_platform(hass, YAML_CONFIG, None)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Configuring Aladdin Connect through yaml is deprecated" in caplog.text
|
||||
|
||||
assert hass.config_entries.async_entries(DOMAIN)
|
||||
config_data = hass.config_entries.async_entries(DOMAIN)[0].data
|
||||
assert config_data[CONF_USERNAME] == "test-user"
|
||||
assert config_data[CONF_PASSWORD] == "test-password"
|
|
@ -0,0 +1,76 @@
|
|||
"""Test for Aladdin Connect init logic."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.aladdin_connect.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
YAML_CONFIG = {"username": "test-user", "password": "test-password"}
|
||||
|
||||
|
||||
async def test_unload_entry(hass: HomeAssistant):
|
||||
"""Test successful unload of entry."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-user", "password": "test-password"},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
):
|
||||
|
||||
assert (await async_setup_component(hass, DOMAIN, entry)) is True
|
||||
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_entry_password_fail(hass: HomeAssistant):
|
||||
"""Test successful unload of entry."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-user", "password": "test-password"},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=False,
|
||||
):
|
||||
|
||||
assert (await async_setup_component(hass, DOMAIN, entry)) is True
|
||||
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||
|
||||
|
||||
async def test_load_and_unload(hass: HomeAssistant) -> None:
|
||||
"""Test loading and unloading Aladdin Connect entry."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
):
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state == ConfigEntryState.LOADED
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
|
||||
assert await config_entry.async_unload(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state == ConfigEntryState.NOT_LOADED
|
|
@ -0,0 +1,18 @@
|
|||
"""Test the Aladdin Connect model class."""
|
||||
from homeassistant.components.aladdin_connect.model import DoorDevice
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
async def test_model(hass: HomeAssistant) -> None:
|
||||
"""Test model for Aladdin Connect Model."""
|
||||
test_values = {
|
||||
"device_id": "1",
|
||||
"door_number": "2",
|
||||
"name": "my door",
|
||||
"status": "good",
|
||||
}
|
||||
result2 = DoorDevice(test_values)
|
||||
assert result2["device_id"] == "1"
|
||||
assert result2["door_number"] == "2"
|
||||
assert result2["name"] == "my door"
|
||||
assert result2["status"] == "good"
|
Loading…
Reference in New Issue