Remove Aladdin Connect integration (#120980)

This commit is contained in:
Joost Lekkerkerker 2024-07-02 13:51:44 +02:00 committed by Franck Nijhof
parent 24afbde79e
commit 98a2e46d4a
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
20 changed files with 89 additions and 738 deletions

View File

@ -58,11 +58,6 @@ omit =
homeassistant/components/airvisual/sensor.py
homeassistant/components/airvisual_pro/__init__.py
homeassistant/components/airvisual_pro/sensor.py
homeassistant/components/aladdin_connect/__init__.py
homeassistant/components/aladdin_connect/api.py
homeassistant/components/aladdin_connect/application_credentials.py
homeassistant/components/aladdin_connect/cover.py
homeassistant/components/aladdin_connect/sensor.py
homeassistant/components/alarmdecoder/__init__.py
homeassistant/components/alarmdecoder/alarm_control_panel.py
homeassistant/components/alarmdecoder/binary_sensor.py

View File

@ -80,8 +80,6 @@ build.json @home-assistant/supervisor
/tests/components/airzone/ @Noltari
/homeassistant/components/airzone_cloud/ @Noltari
/tests/components/airzone_cloud/ @Noltari
/homeassistant/components/aladdin_connect/ @swcloudgenie
/tests/components/aladdin_connect/ @swcloudgenie
/homeassistant/components/alarm_control_panel/ @home-assistant/core
/tests/components/alarm_control_panel/ @home-assistant/core
/homeassistant/components/alert/ @home-assistant/core @frenck

View File

@ -1,94 +1,38 @@
"""The Aladdin Connect Genie integration."""
# mypy: ignore-errors
from __future__ import annotations
# from genie_partner_sdk.client import AladdinConnectClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.config_entry_oauth2_flow import (
OAuth2Session,
async_get_config_entry_implementation,
)
from homeassistant.helpers import issue_registry as ir
from .api import AsyncConfigEntryAuth
from .const import DOMAIN
from .coordinator import AladdinConnectCoordinator
PLATFORMS: list[Platform] = [Platform.COVER, Platform.SENSOR]
type AladdinConnectConfigEntry = ConfigEntry[AladdinConnectCoordinator]
DOMAIN = "aladdin_connect"
async def async_setup_entry(
hass: HomeAssistant, entry: AladdinConnectConfigEntry
) -> bool:
"""Set up Aladdin Connect Genie from a config entry."""
implementation = await async_get_config_entry_implementation(hass, entry)
session = OAuth2Session(hass, entry, implementation)
auth = AsyncConfigEntryAuth(async_get_clientsession(hass), session)
coordinator = AladdinConnectCoordinator(hass, AladdinConnectClient(auth))
await coordinator.async_setup()
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async_remove_stale_devices(hass, entry)
return True
async def async_unload_entry(
hass: HomeAssistant, entry: AladdinConnectConfigEntry
) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_entry(
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
) -> bool:
"""Migrate old config."""
if config_entry.version < 2:
config_entry.async_start_reauth(hass)
hass.config_entries.async_update_entry(
config_entry,
version=2,
minor_version=1,
)
return True
def async_remove_stale_devices(
hass: HomeAssistant, config_entry: AladdinConnectConfigEntry
) -> None:
"""Remove stale devices from device registry."""
device_registry = dr.async_get(hass)
device_entries = dr.async_entries_for_config_entry(
device_registry, config_entry.entry_id
async def async_setup_entry(hass: HomeAssistant, _: ConfigEntry) -> bool:
"""Set up Aladdin Connect from a config entry."""
ir.async_create_issue(
hass,
DOMAIN,
DOMAIN,
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="integration_removed",
translation_placeholders={
"entries": "/config/integrations/integration/aladdin_connect",
},
)
all_device_ids = {door.unique_id for door in config_entry.runtime_data.doors}
for device_entry in device_entries:
device_id: str | None = None
return True
for identifier in device_entry.identifiers:
if identifier[0] == DOMAIN:
device_id = identifier[1]
break
if device_id is None or device_id not in all_device_ids:
# If device_id is None an invalid device entry was found for this config entry.
# If the device_id is not in existing device ids it's a stale device entry.
# Remove config entry from this device entry in either case.
device_registry.async_update_device(
device_entry.id, remove_config_entry_id=config_entry.entry_id
)
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if all(
config_entry.state is ConfigEntryState.NOT_LOADED
for config_entry in hass.config_entries.async_entries(DOMAIN)
if config_entry.entry_id != entry.entry_id
):
ir.async_delete_issue(hass, DOMAIN, DOMAIN)
return True

View File

@ -1,33 +0,0 @@
"""API for Aladdin Connect Genie bound to Home Assistant OAuth."""
# mypy: ignore-errors
from typing import cast
from aiohttp import ClientSession
# from genie_partner_sdk.auth import Auth
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
API_URL = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1"
API_KEY = "k6QaiQmcTm2zfaNns5L1Z8duBtJmhDOW8JawlCC3"
class AsyncConfigEntryAuth(Auth): # type: ignore[misc]
"""Provide Aladdin Connect Genie authentication tied to an OAuth2 based config entry."""
def __init__(
self,
websession: ClientSession,
oauth_session: OAuth2Session,
) -> None:
"""Initialize Aladdin Connect Genie auth."""
super().__init__(
websession, API_URL, oauth_session.token["access_token"], API_KEY
)
self._oauth_session = oauth_session
async def async_get_access_token(self) -> str:
"""Return a valid access token."""
await self._oauth_session.async_ensure_token_valid()
return cast(str, self._oauth_session.token["access_token"])

View File

@ -1,14 +0,0 @@
"""application_credentials platform the Aladdin Connect Genie integration."""
from homeassistant.components.application_credentials import AuthorizationServer
from homeassistant.core import HomeAssistant
from .const import OAUTH2_AUTHORIZE, OAUTH2_TOKEN
async def async_get_authorization_server(hass: HomeAssistant) -> AuthorizationServer:
"""Return authorization server."""
return AuthorizationServer(
authorize_url=OAUTH2_AUTHORIZE,
token_url=OAUTH2_TOKEN,
)

View File

@ -1,70 +1,11 @@
"""Config flow for Aladdin Connect Genie."""
"""Config flow for Aladdin Connect integration."""
from collections.abc import Mapping
import logging
from typing import Any
from homeassistant.config_entries import ConfigFlow
import jwt
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
from .const import DOMAIN
from . import DOMAIN
class AladdinConnectOAuth2FlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
"""Config flow to handle Aladdin Connect Genie OAuth2 authentication."""
class AladdinConnectConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Aladdin Connect."""
DOMAIN = DOMAIN
VERSION = 2
MINOR_VERSION = 1
reauth_entry: ConfigEntry | None = None
async def async_step_reauth(
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon API auth error or upgrade from v1 to v2."""
self.reauth_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: Mapping[str, Any] | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
if user_input is None:
return self.async_show_form(step_id="reauth_confirm")
return await self.async_step_user()
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
"""Create an oauth config entry or update existing entry for reauth."""
token_payload = jwt.decode(
data[CONF_TOKEN][CONF_ACCESS_TOKEN], options={"verify_signature": False}
)
if not self.reauth_entry:
await self.async_set_unique_id(token_payload["sub"])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=token_payload["username"],
data=data,
)
if self.reauth_entry.unique_id == token_payload["username"]:
return self.async_update_reload_and_abort(
self.reauth_entry,
data=data,
unique_id=token_payload["sub"],
)
if self.reauth_entry.unique_id == token_payload["sub"]:
return self.async_update_reload_and_abort(self.reauth_entry, data=data)
return self.async_abort(reason="wrong_account")
@property
def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)
VERSION = 1

View File

@ -1,6 +0,0 @@
"""Constants for the Aladdin Connect Genie integration."""
DOMAIN = "aladdin_connect"
OAUTH2_AUTHORIZE = "https://app.aladdinconnect.net/login.html"
OAUTH2_TOKEN = "https://twdvzuefzh.execute-api.us-east-2.amazonaws.com/v1/oauth2/token"

View File

@ -1,38 +0,0 @@
"""Define an object to coordinate fetching Aladdin Connect data."""
# mypy: ignore-errors
from datetime import timedelta
import logging
# from genie_partner_sdk.client import AladdinConnectClient
# from genie_partner_sdk.model import GarageDoor
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class AladdinConnectCoordinator(DataUpdateCoordinator[None]):
"""Aladdin Connect Data Update Coordinator."""
def __init__(self, hass: HomeAssistant, acc: AladdinConnectClient) -> None:
"""Initialize."""
super().__init__(
hass,
logger=_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=15),
)
self.acc = acc
self.doors: list[GarageDoor] = []
async def async_setup(self) -> None:
"""Fetch initial data."""
self.doors = await self.acc.get_doors()
async def _async_update_data(self) -> None:
"""Fetch data from API endpoint."""
for door in self.doors:
await self.acc.update_door(door.device_id, door.door_number)

View File

@ -1,84 +0,0 @@
"""Cover Entity for Genie Garage Door."""
# mypy: ignore-errors
from typing import Any
# from genie_partner_sdk.model import GarageDoor
from homeassistant.components.cover import (
CoverDeviceClass,
CoverEntity,
CoverEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AladdinConnectConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Aladdin Connect platform."""
coordinator = config_entry.runtime_data
async_add_entities(AladdinDevice(coordinator, door) for door in coordinator.doors)
class AladdinDevice(AladdinConnectEntity, CoverEntity):
"""Representation of Aladdin Connect cover."""
_attr_device_class = CoverDeviceClass.GARAGE
_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
_attr_name = None
def __init__(
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
) -> None:
"""Initialize the Aladdin Connect cover."""
super().__init__(coordinator, device)
self._attr_unique_id = device.unique_id
async def async_open_cover(self, **kwargs: Any) -> None:
"""Issue open command to cover."""
await self.coordinator.acc.open_door(
self._device.device_id, self._device.door_number
)
async def async_close_cover(self, **kwargs: Any) -> None:
"""Issue close command to cover."""
await self.coordinator.acc.close_door(
self._device.device_id, self._device.door_number
)
@property
def is_closed(self) -> bool | None:
"""Update is closed attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "closed")
@property
def is_closing(self) -> bool | None:
"""Update is closing attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "closing")
@property
def is_opening(self) -> bool | None:
"""Update is opening attribute."""
value = self.coordinator.acc.get_door_status(
self._device.device_id, self._device.door_number
)
if value is None:
return None
return bool(value == "opening")

View File

@ -1,27 +0,0 @@
"""Defines a base Aladdin Connect entity."""
# mypy: ignore-errors
# from genie_partner_sdk.model import GarageDoor
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import AladdinConnectCoordinator
class AladdinConnectEntity(CoordinatorEntity[AladdinConnectCoordinator]):
"""Defines a base Aladdin Connect entity."""
_attr_has_entity_name = True
def __init__(
self, coordinator: AladdinConnectCoordinator, device: GarageDoor
) -> None:
"""Initialize the entity."""
super().__init__(coordinator)
self._device = device
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, device.unique_id)},
name=device.name,
manufacturer="Overhead Door",
)

View File

@ -1,11 +1,9 @@
{
"domain": "aladdin_connect",
"name": "Aladdin Connect",
"codeowners": ["@swcloudgenie"],
"config_flow": true,
"dependencies": ["application_credentials"],
"disabled": "This integration is disabled because it uses non-open source code to operate.",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
"integration_type": "system",
"iot_class": "cloud_polling",
"requirements": ["genie-partner-sdk==1.0.2"]
"requirements": []
}

View File

@ -1,5 +0,0 @@
extend = "../../../pyproject.toml"
lint.extend-ignore = [
"F821"
]

View File

@ -1,80 +0,0 @@
"""Support for Aladdin Connect Garage Door sensors."""
# mypy: ignore-errors
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
# from genie_partner_sdk.client import AladdinConnectClient
# from genie_partner_sdk.model import GarageDoor
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AladdinConnectConfigEntry, AladdinConnectCoordinator
from .entity import AladdinConnectEntity
@dataclass(frozen=True, kw_only=True)
class AccSensorEntityDescription(SensorEntityDescription):
"""Describes AladdinConnect sensor entity."""
value_fn: Callable[[AladdinConnectClient, str, int], float | None]
SENSORS: tuple[AccSensorEntityDescription, ...] = (
AccSensorEntityDescription(
key="battery_level",
device_class=SensorDeviceClass.BATTERY,
entity_registry_enabled_default=False,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=AladdinConnectClient.get_battery_status,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AladdinConnectConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Aladdin Connect sensor devices."""
coordinator = entry.runtime_data
async_add_entities(
AladdinConnectSensor(coordinator, door, description)
for description in SENSORS
for door in coordinator.doors
)
class AladdinConnectSensor(AladdinConnectEntity, SensorEntity):
"""A sensor implementation for Aladdin Connect devices."""
entity_description: AccSensorEntityDescription
def __init__(
self,
coordinator: AladdinConnectCoordinator,
device: GarageDoor,
description: AccSensorEntityDescription,
) -> None:
"""Initialize a sensor for an Aladdin Connect device."""
super().__init__(coordinator, device)
self.entity_description = description
self._attr_unique_id = f"{device.unique_id}-{description.key}"
@property
def native_value(self) -> float | None:
"""Return the state of the sensor."""
return self.entity_description.value_fn(
self.coordinator.acc, self._device.device_id, self._device.door_number
)

View File

@ -1,29 +1,8 @@
{
"config": {
"step": {
"pick_implementation": {
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
},
"reauth_confirm": {
"title": "[%key:common::config_flow::title::reauth%]",
"description": "Aladdin Connect needs to re-authenticate your account"
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
},
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"
"issues": {
"integration_removed": {
"title": "The Aladdin Connect integration has been removed",
"description": "The Aladdin Connect integration has been removed from Home Assistant.\n\nTo resolve this issue, please remove the (now defunct) integration entries from your Home Assistant setup. [Click here to see your existing Aladdin Connect integration entries]({entries})."
}
}
}

View File

@ -4,7 +4,6 @@ To update, run python3 -m script.hassfest
"""
APPLICATION_CREDENTIALS = [
"aladdin_connect",
"electric_kiwi",
"fitbit",
"geocaching",

View File

@ -42,7 +42,6 @@ FLOWS = {
"airvisual_pro",
"airzone",
"airzone_cloud",
"aladdin_connect",
"alarmdecoder",
"amberelectric",
"ambient_network",

View File

@ -180,12 +180,6 @@
}
}
},
"aladdin_connect": {
"name": "Aladdin Connect",
"integration_type": "hub",
"config_flow": true,
"iot_class": "cloud_polling"
},
"alarmdecoder": {
"name": "AlarmDecoder",
"integration_type": "device",

View File

@ -1,29 +0,0 @@
"""Test fixtures for the Aladdin Connect Garage Door integration."""
from unittest.mock import AsyncMock, patch
import pytest
from typing_extensions import Generator
from tests.common import MockConfigEntry
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.aladdin_connect.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return an Aladdin Connect config entry."""
return MockConfigEntry(
domain="aladdin_connect",
data={},
title="test@test.com",
unique_id="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
version=2,
)

View File

@ -1,230 +0,0 @@
"""Test the Aladdin Connect Garage Door config flow."""
# from unittest.mock import AsyncMock
#
# import pytest
#
# from homeassistant.components.aladdin_connect.const import (
# DOMAIN,
# OAUTH2_AUTHORIZE,
# OAUTH2_TOKEN,
# )
# from homeassistant.components.application_credentials import (
# ClientCredential,
# async_import_client_credential,
# )
# from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER, ConfigFlowResult
# from homeassistant.core import HomeAssistant
# from homeassistant.data_entry_flow import FlowResultType
# from homeassistant.helpers import config_entry_oauth2_flow
# from homeassistant.setup import async_setup_component
#
# from tests.common import MockConfigEntry
# from tests.test_util.aiohttp import AiohttpClientMocker
# from tests.typing import ClientSessionGenerator
#
# CLIENT_ID = "1234"
# CLIENT_SECRET = "5678"
#
# EXAMPLE_TOKEN = (
# "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYWFhYWFhYS1iYmJiLWNjY2MtZGRk"
# "ZC1lZWVlZWVlZWVlZWUiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsInVzZXJuYW"
# "1lIjoidGVzdEB0ZXN0LmNvbSJ9.CTU1YItIrUl8nSM3koJxlFJr5CjLghgc9gS6h45D8dE"
# )
#
#
# @pytest.fixture
# async def setup_credentials(hass: HomeAssistant) -> None:
# """Fixture to setup credentials."""
# assert await async_setup_component(hass, "application_credentials", {})
# await async_import_client_credential(
# hass,
# DOMAIN,
# ClientCredential(CLIENT_ID, CLIENT_SECRET),
# )
#
#
# async def _oauth_actions(
# hass: HomeAssistant,
# result: ConfigFlowResult,
# hass_client_no_auth: ClientSessionGenerator,
# aioclient_mock: AiohttpClientMocker,
# ) -> None:
# state = config_entry_oauth2_flow._encode_jwt(
# hass,
# {
# "flow_id": result["flow_id"],
# "redirect_uri": "https://example.com/auth/external/callback",
# },
# )
#
# assert result["url"] == (
# f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
# "&redirect_uri=https://example.com/auth/external/callback"
# f"&state={state}"
# )
#
# client = await hass_client_no_auth()
# resp = await client.get(f"/auth/external/callback?code=abcd&state={state}")
# assert resp.status == 200
# assert resp.headers["content-type"] == "text/html; charset=utf-8"
#
# aioclient_mock.post(
# OAUTH2_TOKEN,
# json={
# "refresh_token": "mock-refresh-token",
# "access_token": EXAMPLE_TOKEN,
# "type": "Bearer",
# "expires_in": 60,
# },
# )
#
#
# @pytest.mark.skip(reason="Integration disabled")
# @pytest.mark.usefixtures("current_request_with_host")
# async def test_full_flow(
# hass: HomeAssistant,
# hass_client_no_auth: ClientSessionGenerator,
# aioclient_mock: AiohttpClientMocker,
# setup_credentials: None,
# mock_setup_entry: AsyncMock,
# ) -> None:
# """Check full flow."""
# result = await hass.config_entries.flow.async_init(
# DOMAIN, context={"source": SOURCE_USER}
# )
# await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
#
# result = await hass.config_entries.flow.async_configure(result["flow_id"])
# assert result["type"] is FlowResultType.CREATE_ENTRY
# assert result["title"] == "test@test.com"
# assert result["data"]["token"]["access_token"] == EXAMPLE_TOKEN
# assert result["data"]["token"]["refresh_token"] == "mock-refresh-token"
# assert result["result"].unique_id == "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
#
# assert len(hass.config_entries.async_entries(DOMAIN)) == 1
# assert len(mock_setup_entry.mock_calls) == 1
#
#
# @pytest.mark.skip(reason="Integration disabled")
# @pytest.mark.usefixtures("current_request_with_host")
# async def test_duplicate_entry(
# hass: HomeAssistant,
# hass_client_no_auth: ClientSessionGenerator,
# aioclient_mock: AiohttpClientMocker,
# setup_credentials: None,
# mock_config_entry: MockConfigEntry,
# ) -> None:
# """Test we abort with duplicate entry."""
# mock_config_entry.add_to_hass(hass)
# result = await hass.config_entries.flow.async_init(
# DOMAIN, context={"source": SOURCE_USER}
# )
# await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
#
# result = await hass.config_entries.flow.async_configure(result["flow_id"])
# assert result["type"] is FlowResultType.ABORT
# assert result["reason"] == "already_configured"
#
#
# @pytest.mark.skip(reason="Integration disabled")
# @pytest.mark.usefixtures("current_request_with_host")
# async def test_reauth(
# hass: HomeAssistant,
# hass_client_no_auth: ClientSessionGenerator,
# aioclient_mock: AiohttpClientMocker,
# setup_credentials: None,
# mock_config_entry: MockConfigEntry,
# mock_setup_entry: AsyncMock,
# ) -> None:
# """Test reauthentication."""
# mock_config_entry.add_to_hass(hass)
# result = await hass.config_entries.flow.async_init(
# DOMAIN,
# context={
# "source": SOURCE_REAUTH,
# "entry_id": mock_config_entry.entry_id,
# },
# data=mock_config_entry.data,
# )
# assert result["type"] is FlowResultType.FORM
# assert result["step_id"] == "reauth_confirm"
# result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
# await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
#
# result = await hass.config_entries.flow.async_configure(result["flow_id"])
# assert result["type"] is FlowResultType.ABORT
# assert result["reason"] == "reauth_successful"
#
#
# @pytest.mark.skip(reason="Integration disabled")
# @pytest.mark.usefixtures("current_request_with_host")
# async def test_reauth_wrong_account(
# hass: HomeAssistant,
# hass_client_no_auth: ClientSessionGenerator,
# aioclient_mock: AiohttpClientMocker,
# setup_credentials: None,
# mock_setup_entry: AsyncMock,
# ) -> None:
# """Test reauthentication with wrong account."""
# config_entry = MockConfigEntry(
# domain=DOMAIN,
# data={},
# title="test@test.com",
# unique_id="aaaaaaaa-bbbb-ffff-dddd-eeeeeeeeeeee",
# version=2,
# )
# config_entry.add_to_hass(hass)
# result = await hass.config_entries.flow.async_init(
# DOMAIN,
# context={
# "source": SOURCE_REAUTH,
# "entry_id": config_entry.entry_id,
# },
# data=config_entry.data,
# )
# assert result["type"] is FlowResultType.FORM
# assert result["step_id"] == "reauth_confirm"
# result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
# await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
#
# result = await hass.config_entries.flow.async_configure(result["flow_id"])
# assert result["type"] is FlowResultType.ABORT
# assert result["reason"] == "wrong_account"
#
#
# @pytest.mark.skip(reason="Integration disabled")
# @pytest.mark.usefixtures("current_request_with_host")
# async def test_reauth_old_account(
# hass: HomeAssistant,
# hass_client_no_auth: ClientSessionGenerator,
# aioclient_mock: AiohttpClientMocker,
# setup_credentials: None,
# mock_setup_entry: AsyncMock,
# ) -> None:
# """Test reauthentication with old account."""
# config_entry = MockConfigEntry(
# domain=DOMAIN,
# data={},
# title="test@test.com",
# unique_id="test@test.com",
# version=2,
# )
# config_entry.add_to_hass(hass)
# result = await hass.config_entries.flow.async_init(
# DOMAIN,
# context={
# "source": SOURCE_REAUTH,
# "entry_id": config_entry.entry_id,
# },
# data=config_entry.data,
# )
# assert result["type"] is FlowResultType.FORM
# assert result["step_id"] == "reauth_confirm"
# result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
# await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
#
# result = await hass.config_entries.flow.async_configure(result["flow_id"])
# assert result["type"] is FlowResultType.ABORT
# assert result["reason"] == "reauth_successful"
# assert config_entry.unique_id == "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"

View File

@ -0,0 +1,50 @@
"""Tests for the Aladdin Connect integration."""
from homeassistant.components.aladdin_connect import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
from tests.common import MockConfigEntry
async def test_aladdin_connect_repair_issue(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test the Aladdin Connect configuration entry loading/unloading handles the repair."""
config_entry_1 = MockConfigEntry(
title="Example 1",
domain=DOMAIN,
)
config_entry_1.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry_1.entry_id)
await hass.async_block_till_done()
assert config_entry_1.state is ConfigEntryState.LOADED
# Add a second one
config_entry_2 = MockConfigEntry(
title="Example 2",
domain=DOMAIN,
)
config_entry_2.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry_2.entry_id)
await hass.async_block_till_done()
assert config_entry_2.state is ConfigEntryState.LOADED
assert issue_registry.async_get_issue(DOMAIN, DOMAIN)
# Remove the first one
await hass.config_entries.async_remove(config_entry_1.entry_id)
await hass.async_block_till_done()
assert config_entry_1.state is ConfigEntryState.NOT_LOADED
assert config_entry_2.state is ConfigEntryState.LOADED
assert issue_registry.async_get_issue(DOMAIN, DOMAIN)
# Remove the second one
await hass.config_entries.async_remove(config_entry_2.entry_id)
await hass.async_block_till_done()
assert config_entry_1.state is ConfigEntryState.NOT_LOADED
assert config_entry_2.state is ConfigEntryState.NOT_LOADED
assert issue_registry.async_get_issue(DOMAIN, DOMAIN) is None