Move to async for aladdin connect integration (#73954)
* Moved to AIOAladdinConnect API * Added callback logic for door status * close unused connections * Close connection after verification * Matched to current version * Matched __init__.py to current release * Matched cover.py to existing version * added missing awaits * Moved callback * Bumped AIOAladdinConnect to 0.1.3 * Removed await from callback config * Finished tests * Added callback test * Bumped AIOAladdinConnect to 0.1.4 * Finished tests * Callback correct call to update HA * Modified calls to state machine * Modified update path * Removed unused status * Bumped AIOAladdinConnect to 0.1.7 * Revised test_cover cover tests and bumped AIOAladdinConnect to 0.1.10 * Bumped AIOAladdinConnect to 0.1.11 * Bumped AIOAladdinConenct to 0.1.12 * Bumped AIOAladdinConnect to 0.1.13 * Bumped AIOAladdinConnect to 0.1.14 * Added ability to handle multiple doors * Added timout errors to config flow * asyncio timout error added to setup retry * Cleanup added to hass proceedure * Bumped AIOAladdinConnect to 0.1.16 * Bumped AIOAladdinConnect to 0.1.18 * Bumped AIOAladdinConnect to 0.1.19 * Bumped AIOAladdinConnect to 0.1.20 * Addressed recommended changes: SCAN_INTERVAL and spelling * Moved to async_get_clientsession and bumped AIOAladdinConnect to 0.1.21 * Missing test for new code structure * removed extra call to write_ha_state, callback decorator, cleaned up tests * Update tests/components/aladdin_connect/test_init.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Removed extra_attributes. * Added typing to variable acc Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
7d74301045
commit
c3a2fce5cc
10 changed files with 458 additions and 209 deletions
|
@ -1,13 +1,16 @@
|
|||
"""The aladdin_connect component."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Final
|
||||
|
||||
from aladdin_connect import AladdinConnectClient
|
||||
from AIOAladdinConnect import AladdinConnectClient
|
||||
from aiohttp import ClientConnectionError
|
||||
|
||||
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
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
@ -20,9 +23,13 @@ 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)
|
||||
if not await hass.async_add_executor_job(acc.login):
|
||||
raise ConfigEntryAuthFailed("Incorrect Password")
|
||||
acc = AladdinConnectClient(username, password, async_get_clientsession(hass))
|
||||
try:
|
||||
if not await acc.login():
|
||||
raise ConfigEntryAuthFailed("Incorrect Password")
|
||||
except (ClientConnectionError, asyncio.TimeoutError) as ex:
|
||||
raise ConfigEntryNotReady("Can not connect to host") from ex
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = acc
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
"""Config flow for Aladdin Connect cover integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aladdin_connect import AladdinConnectClient
|
||||
from AIOAladdinConnect import AladdinConnectClient
|
||||
from aiohttp import ClientError
|
||||
from aiohttp.client_exceptions import ClientConnectionError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
@ -13,6 +16,7 @@ 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 homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
|
@ -33,8 +37,11 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
|
|||
|
||||
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
|
||||
"""
|
||||
acc = AladdinConnectClient(data[CONF_USERNAME], data[CONF_PASSWORD])
|
||||
login = await hass.async_add_executor_job(acc.login)
|
||||
acc = AladdinConnectClient(
|
||||
data[CONF_USERNAME], data[CONF_PASSWORD], async_get_clientsession(hass)
|
||||
)
|
||||
login = await acc.login()
|
||||
await acc.close()
|
||||
if not login:
|
||||
raise InvalidAuth
|
||||
|
||||
|
@ -67,8 +74,13 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
try:
|
||||
await validate_input(self.hass, data)
|
||||
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
|
||||
except (ClientConnectionError, asyncio.TimeoutError, ClientError):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
else:
|
||||
|
||||
self.hass.config_entries.async_update_entry(
|
||||
|
@ -103,6 +115,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
|
||||
except (ClientConnectionError, asyncio.TimeoutError, ClientError):
|
||||
errors["base"] = "cannot_connect"
|
||||
|
||||
else:
|
||||
await self.async_set_unique_id(
|
||||
user_input["username"].lower(), raise_on_progress=False
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
"""Platform for the Aladdin Connect cover component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any, Final
|
||||
|
||||
from aladdin_connect import AladdinConnectClient
|
||||
from AIOAladdinConnect import AladdinConnectClient
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
|
@ -34,6 +35,7 @@ _LOGGER: Final = logging.getLogger(__name__)
|
|||
PLATFORM_SCHEMA: Final = BASE_PLATFORM_SCHEMA.extend(
|
||||
{vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
|
||||
)
|
||||
SCAN_INTERVAL = timedelta(seconds=300)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
|
@ -62,14 +64,12 @@ async def async_setup_entry(
|
|||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the Aladdin Connect platform."""
|
||||
acc = hass.data[DOMAIN][config_entry.entry_id]
|
||||
doors = await hass.async_add_executor_job(acc.get_doors)
|
||||
|
||||
acc: AladdinConnectClient = hass.data[DOMAIN][config_entry.entry_id]
|
||||
doors = await acc.get_doors()
|
||||
if doors is None:
|
||||
raise PlatformNotReady("Error from Aladdin Connect getting doors")
|
||||
async_add_entities(
|
||||
(AladdinDevice(acc, door) for door in doors),
|
||||
update_before_add=True,
|
||||
(AladdinDevice(acc, door, config_entry) for door in doors),
|
||||
)
|
||||
|
||||
|
||||
|
@ -79,27 +79,63 @@ class AladdinDevice(CoverEntity):
|
|||
_attr_device_class = CoverDeviceClass.GARAGE
|
||||
_attr_supported_features = SUPPORTED_FEATURES
|
||||
|
||||
def __init__(self, acc: AladdinConnectClient, device: DoorDevice) -> None:
|
||||
def __init__(
|
||||
self, acc: AladdinConnectClient, device: DoorDevice, entry: ConfigEntry
|
||||
) -> None:
|
||||
"""Initialize the Aladdin Connect cover."""
|
||||
self._acc = acc
|
||||
|
||||
self._device_id = device["device_id"]
|
||||
self._number = device["door_number"]
|
||||
self._attr_name = device["name"]
|
||||
self._attr_unique_id = f"{self._device_id}-{self._number}"
|
||||
|
||||
def close_cover(self, **kwargs: Any) -> None:
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Connect Aladdin Connect to the cloud."""
|
||||
|
||||
async def update_callback() -> None:
|
||||
"""Schedule a state update."""
|
||||
self.async_write_ha_state()
|
||||
|
||||
self._acc.register_callback(update_callback, self._number)
|
||||
await self._acc.get_doors(self._number)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Close Aladdin Connect before removing."""
|
||||
await self._acc.close()
|
||||
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Issue close command to cover."""
|
||||
self._acc.close_door(self._device_id, self._number)
|
||||
await self._acc.close_door(self._device_id, self._number)
|
||||
|
||||
def open_cover(self, **kwargs: Any) -> None:
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Issue open command to cover."""
|
||||
self._acc.open_door(self._device_id, self._number)
|
||||
await self._acc.open_door(self._device_id, self._number)
|
||||
|
||||
def update(self) -> None:
|
||||
async def async_update(self) -> None:
|
||||
"""Update status of cover."""
|
||||
status = STATES_MAP.get(
|
||||
self._acc.get_door_status(self._device_id, self._number)
|
||||
await self._acc.get_doors(self._number)
|
||||
|
||||
@property
|
||||
def is_closed(self) -> bool | None:
|
||||
"""Update is closed attribute."""
|
||||
value = STATES_MAP.get(self._acc.get_door_status(self._device_id, self._number))
|
||||
if value is None:
|
||||
return None
|
||||
return value == STATE_CLOSED
|
||||
|
||||
@property
|
||||
def is_closing(self) -> bool:
|
||||
"""Update is closing attribute."""
|
||||
return (
|
||||
STATES_MAP.get(self._acc.get_door_status(self._device_id, self._number))
|
||||
== STATE_CLOSING
|
||||
)
|
||||
|
||||
@property
|
||||
def is_opening(self) -> bool:
|
||||
"""Update is opening attribute."""
|
||||
return (
|
||||
STATES_MAP.get(self._acc.get_door_status(self._device_id, self._number))
|
||||
== STATE_OPENING
|
||||
)
|
||||
self._attr_is_opening = status == STATE_OPENING
|
||||
self._attr_is_closing = status == STATE_CLOSING
|
||||
self._attr_is_closed = None if status is None else status == STATE_CLOSED
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"domain": "aladdin_connect",
|
||||
"name": "Aladdin Connect",
|
||||
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
|
||||
"requirements": ["aladdin_connect==0.4"],
|
||||
"requirements": ["AIOAladdinConnect==0.1.21"],
|
||||
"codeowners": ["@mkmer"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aladdin_connect"],
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
# homeassistant.components.aemet
|
||||
AEMET-OpenData==0.2.1
|
||||
|
||||
# homeassistant.components.aladdin_connect
|
||||
AIOAladdinConnect==0.1.21
|
||||
|
||||
# homeassistant.components.adax
|
||||
Adax-local==0.1.4
|
||||
|
||||
|
@ -282,9 +285,6 @@ airthings_cloud==0.1.0
|
|||
# homeassistant.components.airtouch4
|
||||
airtouch4pyapi==1.0.5
|
||||
|
||||
# homeassistant.components.aladdin_connect
|
||||
aladdin_connect==0.4
|
||||
|
||||
# homeassistant.components.alpha_vantage
|
||||
alpha_vantage==2.3.1
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
# homeassistant.components.aemet
|
||||
AEMET-OpenData==0.2.1
|
||||
|
||||
# homeassistant.components.aladdin_connect
|
||||
AIOAladdinConnect==0.1.21
|
||||
|
||||
# homeassistant.components.adax
|
||||
Adax-local==0.1.4
|
||||
|
||||
|
@ -251,9 +254,6 @@ 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
|
||||
|
||||
|
|
39
tests/components/aladdin_connect/conftest.py
Normal file
39
tests/components/aladdin_connect/conftest.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
"""Fixtures for the Aladdin Connect integration tests."""
|
||||
from unittest import mock
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
DEVICE_CONFIG_OPEN = {
|
||||
"device_id": 533255,
|
||||
"door_number": 1,
|
||||
"name": "home",
|
||||
"status": "open",
|
||||
"link_status": "Connected",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_aladdinconnect_api")
|
||||
def fixture_mock_aladdinconnect_api():
|
||||
"""Set up aladdin connect API fixture."""
|
||||
with mock.patch(
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient"
|
||||
) as mock_opener:
|
||||
mock_opener.login = AsyncMock(return_value=True)
|
||||
mock_opener.close = AsyncMock(return_value=True)
|
||||
|
||||
mock_opener.async_get_door_status = AsyncMock(return_value="open")
|
||||
mock_opener.get_door_status.return_value = "open"
|
||||
mock_opener.async_get_door_link_status = AsyncMock(return_value="connected")
|
||||
mock_opener.get_door_link_status.return_value = "connected"
|
||||
mock_opener.async_get_battery_status = AsyncMock(return_value="99")
|
||||
mock_opener.get_battery_status.return_value = "99"
|
||||
mock_opener.async_get_rssi_status = AsyncMock(return_value="-55")
|
||||
mock_opener.get_rssi_status.return_value = "-55"
|
||||
mock_opener.get_doors = AsyncMock(return_value=[DEVICE_CONFIG_OPEN])
|
||||
|
||||
mock_opener.register_callback = mock.Mock(return_value=True)
|
||||
mock_opener.open_door = AsyncMock(return_value=True)
|
||||
mock_opener.close_door = AsyncMock(return_value=True)
|
||||
|
||||
yield mock_opener
|
|
@ -1,5 +1,7 @@
|
|||
"""Test the Aladdin Connect config flow."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from aiohttp.client_exceptions import ClientConnectionError
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.aladdin_connect.const import DOMAIN
|
||||
|
@ -14,8 +16,9 @@ from homeassistant.data_entry_flow import (
|
|||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
async def test_form(hass: HomeAssistant, mock_aladdinconnect_api: MagicMock) -> None:
|
||||
"""Test we get the form."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
@ -23,11 +26,10 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.async_setup_entry",
|
||||
return_value=True,
|
||||
"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"],
|
||||
|
@ -44,33 +46,21 @@ async def test_form(hass: HomeAssistant) -> None:
|
|||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_failed_auth(hass: HomeAssistant) -> None:
|
||||
async def test_form_failed_auth(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test we handle failed authentication error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
mock_aladdinconnect_api.login.return_value = False
|
||||
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"}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=False,
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -84,7 +74,33 @@ async def test_form_failed_auth(hass: HomeAssistant) -> None:
|
|||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_form_already_configured(hass):
|
||||
async def test_form_connection_timeout(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test we handle http timeout error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
mock_aladdinconnect_api.login.side_effect = ClientConnectionError
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
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"}
|
||||
|
||||
|
||||
async def test_form_already_configured(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
):
|
||||
"""Test we handle already configured error."""
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -101,8 +117,8 @@ async def test_form_already_configured(hass):
|
|||
assert result["step_id"] == config_entries.SOURCE_USER
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -117,18 +133,15 @@ async def test_form_already_configured(hass):
|
|||
assert result2["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_import_flow_success(hass: HomeAssistant) -> None:
|
||||
async def test_import_flow_success(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test a successful import of yaml."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_platform",
|
||||
return_value=True,
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_entry",
|
||||
return_value=True,
|
||||
"homeassistant.components.aladdin_connect.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
|
@ -149,7 +162,9 @@ async def test_import_flow_success(hass: HomeAssistant) -> None:
|
|||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_reauth_flow(hass: HomeAssistant) -> None:
|
||||
async def test_reauth_flow(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test a successful reauth flow."""
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
|
@ -174,14 +189,11 @@ async def test_reauth_flow(hass: HomeAssistant) -> None:
|
|||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_platform",
|
||||
"homeassistant.components.aladdin_connect.async_setup_entry",
|
||||
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,
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -197,7 +209,9 @@ async def test_reauth_flow(hass: HomeAssistant) -> None:
|
|||
}
|
||||
|
||||
|
||||
async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None:
|
||||
async def test_reauth_flow_auth_error(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test an authorization error reauth flow."""
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
|
@ -220,13 +234,13 @@ async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None:
|
|||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
mock_aladdinconnect_api.login.return_value = False
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_platform",
|
||||
return_value=True,
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient.login",
|
||||
return_value=False,
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_entry",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.async_setup_entry",
|
||||
return_value=True,
|
||||
|
@ -239,3 +253,44 @@ async def test_reauth_flow_auth_error(hass: HomeAssistant) -> None:
|
|||
|
||||
assert result2["type"] == RESULT_TYPE_FORM
|
||||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_reauth_flow_connnection_error(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test a connection error 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"] == {}
|
||||
mock_aladdinconnect_api.login.side_effect = ClientConnectionError
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
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"}
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
"""Test the Aladdin Connect Cover."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.aladdin_connect.const import DOMAIN
|
||||
from homeassistant.components.aladdin_connect.cover import SCAN_INTERVAL
|
||||
from homeassistant.components.cover import DOMAIN as COVER_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
SERVICE_CLOSE_COVER,
|
||||
SERVICE_OPEN_COVER,
|
||||
STATE_CLOSED,
|
||||
STATE_CLOSING,
|
||||
STATE_OPEN,
|
||||
STATE_OPENING,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
YAML_CONFIG = {"username": "test-user", "password": "test-password"}
|
||||
|
||||
|
@ -76,63 +82,11 @@ DEVICE_CONFIG_BAD_NO_DOOR = {
|
|||
}
|
||||
|
||||
|
||||
async def test_setup_get_doors_errors(hass: HomeAssistant) -> 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",
|
||||
return_value=None,
|
||||
):
|
||||
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
|
||||
|
||||
|
||||
async def test_setup_login_error(hass: HomeAssistant) -> 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",
|
||||
return_value=False,
|
||||
):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is False
|
||||
|
||||
|
||||
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."""
|
||||
async def test_cover_operation(
|
||||
hass: HomeAssistant,
|
||||
mock_aladdinconnect_api: MagicMock,
|
||||
) -> None:
|
||||
"""Test Cover Operation states (open,close,opening,closing) cover."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
|
@ -142,92 +96,116 @@ async def test_cover_operation(hass: HomeAssistant) -> None:
|
|||
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_aladdinconnect_api.async_get_door_status = AsyncMock(return_value=STATE_OPEN)
|
||||
mock_aladdinconnect_api.get_door_status.return_value = STATE_OPEN
|
||||
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],
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
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,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_OPEN],
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"cover", "open_cover", {"entity_id": "cover.home"}, blocking=True
|
||||
)
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_OPEN_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.home"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get("cover.home").state == STATE_OPEN
|
||||
|
||||
mock_aladdinconnect_api.async_get_door_status = AsyncMock(return_value=STATE_CLOSED)
|
||||
mock_aladdinconnect_api.get_door_status.return_value = STATE_CLOSED
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.close_door",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_CLOSED],
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
|
||||
await hass.services.async_call(
|
||||
"cover", "close_cover", {"entity_id": "cover.home"}, blocking=True
|
||||
COVER_DOMAIN,
|
||||
SERVICE_CLOSE_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.home"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
utcnow() + SCAN_INTERVAL,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("cover.home").state == STATE_CLOSED
|
||||
|
||||
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
|
||||
mock_aladdinconnect_api.async_get_door_status = AsyncMock(
|
||||
return_value=STATE_CLOSING
|
||||
)
|
||||
mock_aladdinconnect_api.get_door_status.return_value = STATE_CLOSING
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_CLOSING],
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
utcnow() + SCAN_INTERVAL,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
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
|
||||
mock_aladdinconnect_api.async_get_door_status = AsyncMock(
|
||||
return_value=STATE_OPENING
|
||||
)
|
||||
mock_aladdinconnect_api.get_door_status.return_value = STATE_OPENING
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=[DEVICE_CONFIG_BAD_NO_DOOR],
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
await hass.services.async_call(
|
||||
"homeassistant", "update_entity", {"entity_id": "cover.home"}, blocking=True
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
utcnow() + SCAN_INTERVAL,
|
||||
)
|
||||
assert hass.states.get("cover.home").state
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get("cover.home").state == STATE_OPENING
|
||||
|
||||
mock_aladdinconnect_api.async_get_door_status = AsyncMock(return_value=None)
|
||||
mock_aladdinconnect_api.get_door_status.return_value = None
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_CLOSE_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.home"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
utcnow() + SCAN_INTERVAL,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("cover.home").state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_yaml_import(hass: HomeAssistant, caplog: pytest.LogCaptureFixture):
|
||||
async def test_yaml_import(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
mock_aladdinconnect_api: MagicMock,
|
||||
):
|
||||
"""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],
|
||||
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
await async_setup_component(
|
||||
hass,
|
||||
|
@ -248,3 +226,37 @@ async def test_yaml_import(hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
|||
config_data = hass.config_entries.async_entries(DOMAIN)[0].data
|
||||
assert config_data[CONF_USERNAME] == "test-user"
|
||||
assert config_data[CONF_PASSWORD] == "test-password"
|
||||
|
||||
|
||||
async def test_callback(
|
||||
hass: HomeAssistant,
|
||||
mock_aladdinconnect_api: MagicMock,
|
||||
):
|
||||
"""Test callback from Aladdin Connect API."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_aladdinconnect_api.async_get_door_status.return_value = STATE_CLOSING
|
||||
mock_aladdinconnect_api.get_door_status.return_value = STATE_CLOSING
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient._call_back",
|
||||
AsyncMock(),
|
||||
):
|
||||
callback = mock_aladdinconnect_api.register_callback.call_args[0][0]
|
||||
await callback()
|
||||
assert hass.states.get("cover.home").state == STATE_CLOSING
|
||||
|
|
|
@ -1,35 +1,77 @@
|
|||
"""Test for Aladdin Connect init logic."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from aiohttp import ClientConnectionError
|
||||
|
||||
from homeassistant.components.aladdin_connect.const import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import AsyncMock, MockConfigEntry
|
||||
|
||||
YAML_CONFIG = {"username": "test-user", "password": "test-password"}
|
||||
|
||||
|
||||
async def test_entry_password_fail(hass: HomeAssistant):
|
||||
"""Test password fail during entry."""
|
||||
entry = MockConfigEntry(
|
||||
async def test_setup_get_doors_errors(hass: HomeAssistant) -> None:
|
||||
"""Test component setup Get Doors Errors."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-user", "password": "test-password"},
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.login",
|
||||
return_value=False,
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient.get_doors",
|
||||
return_value=None,
|
||||
):
|
||||
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
|
||||
|
||||
|
||||
async def test_setup_login_error(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test component setup Login Errors."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
mock_aladdinconnect_api.login.return_value = False
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.cover.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is False
|
||||
|
||||
|
||||
async def test_load_and_unload(hass: HomeAssistant) -> None:
|
||||
"""Test loading and unloading Aladdin Connect entry."""
|
||||
async def test_setup_connection_error(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> None:
|
||||
"""Test component setup Login Errors."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
unique_id="test-id",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
mock_aladdinconnect_api.login.side_effect = ClientConnectionError
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is False
|
||||
|
||||
|
||||
async def test_setup_component_no_error(hass: HomeAssistant) -> None:
|
||||
"""Test component setup No Error."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=YAML_CONFIG,
|
||||
|
@ -41,6 +83,49 @@ async def test_load_and_unload(hass: HomeAssistant) -> None:
|
|||
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_entry_password_fail(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
):
|
||||
"""Test password fail during entry."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={"username": "test-user", "password": "test-password"},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
mock_aladdinconnect_api.login = AsyncMock(return_value=False)
|
||||
with patch(
|
||||
"homeassistant.components.aladdin_connect.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||
|
||||
|
||||
async def test_load_and_unload(
|
||||
hass: HomeAssistant, mock_aladdinconnect_api: MagicMock
|
||||
) -> 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.AladdinConnectClient",
|
||||
return_value=mock_aladdinconnect_api,
|
||||
):
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue