diff --git a/.coveragerc b/.coveragerc index c3ab7f1006f..65a4c1bfc31 100644 --- a/.coveragerc +++ b/.coveragerc @@ -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 diff --git a/CODEOWNERS b/CODEOWNERS index 355985b6d4c..14f8a7996bc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/homeassistant/components/aladdin_connect/__init__.py b/homeassistant/components/aladdin_connect/__init__.py index ed284c0e6bb..6d3f1d642b5 100644 --- a/homeassistant/components/aladdin_connect/__init__.py +++ b/homeassistant/components/aladdin_connect/__init__.py @@ -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 diff --git a/homeassistant/components/aladdin_connect/api.py b/homeassistant/components/aladdin_connect/api.py deleted file mode 100644 index 4377fc8fbcb..00000000000 --- a/homeassistant/components/aladdin_connect/api.py +++ /dev/null @@ -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"]) diff --git a/homeassistant/components/aladdin_connect/application_credentials.py b/homeassistant/components/aladdin_connect/application_credentials.py deleted file mode 100644 index e8e959f1fa3..00000000000 --- a/homeassistant/components/aladdin_connect/application_credentials.py +++ /dev/null @@ -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, - ) diff --git a/homeassistant/components/aladdin_connect/config_flow.py b/homeassistant/components/aladdin_connect/config_flow.py index 507085fa27f..a508ff89c68 100644 --- a/homeassistant/components/aladdin_connect/config_flow.py +++ b/homeassistant/components/aladdin_connect/config_flow.py @@ -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 diff --git a/homeassistant/components/aladdin_connect/const.py b/homeassistant/components/aladdin_connect/const.py deleted file mode 100644 index a87147c8f09..00000000000 --- a/homeassistant/components/aladdin_connect/const.py +++ /dev/null @@ -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" diff --git a/homeassistant/components/aladdin_connect/coordinator.py b/homeassistant/components/aladdin_connect/coordinator.py deleted file mode 100644 index 9af3e330409..00000000000 --- a/homeassistant/components/aladdin_connect/coordinator.py +++ /dev/null @@ -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) diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py deleted file mode 100644 index 1be41e6b516..00000000000 --- a/homeassistant/components/aladdin_connect/cover.py +++ /dev/null @@ -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") diff --git a/homeassistant/components/aladdin_connect/entity.py b/homeassistant/components/aladdin_connect/entity.py deleted file mode 100644 index 2615cbc636e..00000000000 --- a/homeassistant/components/aladdin_connect/entity.py +++ /dev/null @@ -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", - ) diff --git a/homeassistant/components/aladdin_connect/manifest.json b/homeassistant/components/aladdin_connect/manifest.json index dce95492272..adf0d9c9b5b 100644 --- a/homeassistant/components/aladdin_connect/manifest.json +++ b/homeassistant/components/aladdin_connect/manifest.json @@ -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": [] } diff --git a/homeassistant/components/aladdin_connect/ruff.toml b/homeassistant/components/aladdin_connect/ruff.toml deleted file mode 100644 index 38f6f586aef..00000000000 --- a/homeassistant/components/aladdin_connect/ruff.toml +++ /dev/null @@ -1,5 +0,0 @@ -extend = "../../../pyproject.toml" - -lint.extend-ignore = [ - "F821" -] diff --git a/homeassistant/components/aladdin_connect/sensor.py b/homeassistant/components/aladdin_connect/sensor.py deleted file mode 100644 index cd1fff12c97..00000000000 --- a/homeassistant/components/aladdin_connect/sensor.py +++ /dev/null @@ -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 - ) diff --git a/homeassistant/components/aladdin_connect/strings.json b/homeassistant/components/aladdin_connect/strings.json index 48f9b299a1d..f62e68de64e 100644 --- a/homeassistant/components/aladdin_connect/strings.json +++ b/homeassistant/components/aladdin_connect/strings.json @@ -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})." } } } diff --git a/homeassistant/generated/application_credentials.py b/homeassistant/generated/application_credentials.py index bc6b29e4c23..c576f242e30 100644 --- a/homeassistant/generated/application_credentials.py +++ b/homeassistant/generated/application_credentials.py @@ -4,7 +4,6 @@ To update, run python3 -m script.hassfest """ APPLICATION_CREDENTIALS = [ - "aladdin_connect", "electric_kiwi", "fitbit", "geocaching", diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 23a13bcbfd8..463a38feb9f 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -42,7 +42,6 @@ FLOWS = { "airvisual_pro", "airzone", "airzone_cloud", - "aladdin_connect", "alarmdecoder", "amberelectric", "ambient_network", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 3371c8de0fa..0ad8ac09c9e 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -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", diff --git a/tests/components/aladdin_connect/conftest.py b/tests/components/aladdin_connect/conftest.py deleted file mode 100644 index 8399269b30d..00000000000 --- a/tests/components/aladdin_connect/conftest.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Test fixtures for the Aladdin Connect Garage Door integration.""" - -from collections.abc import Generator -from unittest.mock import AsyncMock, patch - -import pytest - -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, - ) diff --git a/tests/components/aladdin_connect/test_config_flow.py b/tests/components/aladdin_connect/test_config_flow.py deleted file mode 100644 index 7154c53b9f6..00000000000 --- a/tests/components/aladdin_connect/test_config_flow.py +++ /dev/null @@ -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" diff --git a/tests/components/aladdin_connect/test_init.py b/tests/components/aladdin_connect/test_init.py new file mode 100644 index 00000000000..b01af287b7b --- /dev/null +++ b/tests/components/aladdin_connect/test_init.py @@ -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