Skip to content

Commit fc9bdb3

Browse files
JamieMageejoostlek
andauthored
Bring aladdin_connect to Bronze quality scale (home-assistant#163221)
Co-authored-by: Joostlek <joostlek@outlook.com>
1 parent bb1956c commit fc9bdb3

File tree

9 files changed

+322
-166
lines changed

9 files changed

+322
-166
lines changed

homeassistant/components/aladdin_connect/__init__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
from __future__ import annotations
44

5+
import aiohttp
56
from genie_partner_sdk.client import AladdinConnectClient
67

78
from homeassistant.const import Platform
89
from homeassistant.core import HomeAssistant
10+
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
911
from homeassistant.helpers import (
1012
aiohttp_client,
1113
config_entry_oauth2_flow,
@@ -31,11 +33,27 @@ async def async_setup_entry(
3133

3234
session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
3335

36+
try:
37+
await session.async_ensure_token_valid()
38+
except aiohttp.ClientResponseError as err:
39+
if 400 <= err.status < 500:
40+
raise ConfigEntryAuthFailed(err) from err
41+
raise ConfigEntryNotReady from err
42+
except aiohttp.ClientError as err:
43+
raise ConfigEntryNotReady from err
44+
3445
client = AladdinConnectClient(
3546
api.AsyncConfigEntryAuth(aiohttp_client.async_get_clientsession(hass), session)
3647
)
3748

38-
doors = await client.get_doors()
49+
try:
50+
doors = await client.get_doors()
51+
except aiohttp.ClientResponseError as err:
52+
if 400 <= err.status < 500:
53+
raise ConfigEntryAuthFailed(err) from err
54+
raise ConfigEntryNotReady from err
55+
except aiohttp.ClientError as err:
56+
raise ConfigEntryNotReady from err
3957

4058
entry.runtime_data = {
4159
door.unique_id: AladdinConnectCoordinator(hass, entry, client, door)

homeassistant/components/aladdin_connect/api.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111
API_KEY = "k6QaiQmcTm2zfaNns5L1Z8duBtJmhDOW8JawlCC3"
1212

1313

14+
class AsyncConfigFlowAuth(Auth):
15+
"""Provide Aladdin Connect Genie authentication for config flow validation."""
16+
17+
def __init__(self, websession: ClientSession, access_token: str) -> None:
18+
"""Initialize Aladdin Connect Genie auth."""
19+
super().__init__(websession, API_URL, access_token, API_KEY)
20+
21+
async def async_get_access_token(self) -> str:
22+
"""Return the access token."""
23+
return self.access_token
24+
25+
1426
class AsyncConfigEntryAuth(Auth):
1527
"""Provide Aladdin Connect Genie authentication tied to an OAuth2 based config entry."""
1628

homeassistant/components/aladdin_connect/config_flow.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import logging
55
from typing import Any
66

7+
from genie_partner_sdk.client import AladdinConnectClient
78
import jwt
89
import voluptuous as vol
910

1011
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
11-
from homeassistant.helpers import config_entry_oauth2_flow
12+
from homeassistant.helpers import aiohttp_client, config_entry_oauth2_flow
1213

14+
from .api import AsyncConfigFlowAuth
1315
from .const import CONFIG_FLOW_MINOR_VERSION, CONFIG_FLOW_VERSION, DOMAIN
1416

1517

@@ -52,11 +54,25 @@ async def async_step_reauth_confirm(
5254

5355
async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult:
5456
"""Create an oauth config entry or update existing entry for reauth."""
55-
# Extract the user ID from the JWT token's 'sub' field
56-
token = jwt.decode(
57-
data["token"]["access_token"], options={"verify_signature": False}
57+
try:
58+
token = jwt.decode(
59+
data["token"]["access_token"], options={"verify_signature": False}
60+
)
61+
user_id = token["sub"]
62+
except jwt.DecodeError, KeyError:
63+
return self.async_abort(reason="oauth_error")
64+
65+
client = AladdinConnectClient(
66+
AsyncConfigFlowAuth(
67+
aiohttp_client.async_get_clientsession(self.hass),
68+
data["token"]["access_token"],
69+
)
5870
)
59-
user_id = token["sub"]
71+
try:
72+
await client.get_doors()
73+
except Exception: # noqa: BLE001
74+
return self.async_abort(reason="cannot_connect")
75+
6076
await self.async_set_unique_id(user_id)
6177

6278
if self.source == SOURCE_REAUTH:

homeassistant/components/aladdin_connect/quality_scale.yaml

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,31 @@ rules:
77
brands: done
88
common-modules: done
99
config-flow: done
10-
config-flow-test-coverage: todo
10+
config-flow-test-coverage: done
1111
dependency-transparency: done
1212
docs-actions:
1313
status: exempt
1414
comment: Integration does not register any service actions.
1515
docs-high-level-description: done
16-
docs-installation-instructions:
17-
status: todo
18-
comment: Documentation needs to be created.
19-
docs-removal-instructions:
20-
status: todo
21-
comment: Documentation needs to be created.
16+
docs-installation-instructions: done
17+
docs-removal-instructions: done
2218
entity-event-setup:
2319
status: exempt
2420
comment: Integration does not subscribe to external events.
2521
entity-unique-id: done
2622
has-entity-name: done
2723
runtime-data: done
28-
test-before-configure:
29-
status: todo
30-
comment: Config flow does not currently test connection during setup.
31-
test-before-setup: todo
24+
test-before-configure: done
25+
test-before-setup: done
3226
unique-config-entry: done
3327

3428
# Silver
3529
action-exceptions: todo
3630
config-entry-unloading: done
3731
docs-configuration-parameters:
38-
status: todo
39-
comment: Documentation needs to be created.
40-
docs-installation-parameters:
41-
status: todo
42-
comment: Documentation needs to be created.
32+
status: exempt
33+
comment: Integration does not have an options flow.
34+
docs-installation-parameters: done
4335
entity-unavailable: todo
4436
integration-owner: done
4537
log-when-unavailable: todo
@@ -52,29 +44,17 @@ rules:
5244
# Gold
5345
devices: done
5446
diagnostics: todo
55-
discovery: todo
56-
discovery-update-info: todo
57-
docs-data-update:
58-
status: todo
59-
comment: Documentation needs to be created.
60-
docs-examples:
61-
status: todo
62-
comment: Documentation needs to be created.
63-
docs-known-limitations:
64-
status: todo
65-
comment: Documentation needs to be created.
66-
docs-supported-devices:
67-
status: todo
68-
comment: Documentation needs to be created.
69-
docs-supported-functions:
70-
status: todo
71-
comment: Documentation needs to be created.
72-
docs-troubleshooting:
73-
status: todo
74-
comment: Documentation needs to be created.
75-
docs-use-cases:
76-
status: todo
77-
comment: Documentation needs to be created.
47+
discovery: done
48+
discovery-update-info:
49+
status: exempt
50+
comment: Integration connects via the cloud and not locally.
51+
docs-data-update: done
52+
docs-examples: done
53+
docs-known-limitations: done
54+
docs-supported-devices: done
55+
docs-supported-functions: done
56+
docs-troubleshooting: done
57+
docs-use-cases: done
7858
dynamic-devices: todo
7959
entity-category: done
8060
entity-device-class: done
@@ -86,7 +66,7 @@ rules:
8666
repair-issues: todo
8767
stale-devices:
8868
status: todo
89-
comment: Stale devices can be done dynamically
69+
comment: We can automatically remove removed devices
9070

9171
# Platinum
9272
async-dependency: todo

homeassistant/components/aladdin_connect/strings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
55
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
66
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
7+
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
78
"cloud_not_enabled": "Please make sure you run Home Assistant with `{default_config}` enabled in your configuration.yaml.",
89
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
910
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,12 @@
11
"""Tests for the Aladdin Connect Garage Door integration."""
2+
3+
from homeassistant.core import HomeAssistant
4+
5+
from tests.common import MockConfigEntry
6+
7+
8+
async def init_integration(hass: HomeAssistant, entry: MockConfigEntry) -> None:
9+
"""Set up the Aladdin Connect integration for testing."""
10+
entry.add_to_hass(hass)
11+
await hass.config_entries.async_setup(entry.entry_id)
12+
await hass.async_block_till_done()

tests/components/aladdin_connect/conftest.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
"""Fixtures for aladdin_connect tests."""
22

3+
from collections.abc import Generator
4+
from time import time
5+
from unittest.mock import AsyncMock, patch
6+
37
import pytest
48

59
from homeassistant.components.aladdin_connect import DOMAIN
@@ -27,6 +31,42 @@ async def setup_credentials(hass: HomeAssistant) -> None:
2731
)
2832

2933

34+
@pytest.fixture(autouse=True)
35+
def mock_aladdin_connect_api() -> Generator[AsyncMock]:
36+
"""Mock the AladdinConnectClient."""
37+
mock_door = AsyncMock()
38+
mock_door.device_id = "test_device_id"
39+
mock_door.door_number = 1
40+
mock_door.name = "Test Door"
41+
mock_door.status = "closed"
42+
mock_door.link_status = "connected"
43+
mock_door.battery_level = 100
44+
mock_door.unique_id = f"{mock_door.device_id}-{mock_door.door_number}"
45+
46+
with (
47+
patch(
48+
"homeassistant.components.aladdin_connect.AladdinConnectClient",
49+
autospec=True,
50+
) as mock_client,
51+
patch(
52+
"homeassistant.components.aladdin_connect.config_flow.AladdinConnectClient",
53+
new=mock_client,
54+
),
55+
):
56+
client = mock_client.return_value
57+
client.get_doors.return_value = [mock_door]
58+
yield client
59+
60+
61+
@pytest.fixture
62+
def mock_setup_entry() -> AsyncMock:
63+
"""Fixture to mock setup entry."""
64+
with patch(
65+
"homeassistant.components.aladdin_connect.async_setup_entry", return_value=True
66+
):
67+
yield
68+
69+
3070
@pytest.fixture
3171
def mock_config_entry() -> MockConfigEntry:
3272
"""Define a mock config entry fixture."""
@@ -41,7 +81,7 @@ def mock_config_entry() -> MockConfigEntry:
4181
"access_token": "old-token",
4282
"refresh_token": "old-refresh-token",
4383
"expires_in": 3600,
44-
"expires_at": 1234567890,
84+
"expires_at": time() + 3600,
4585
},
4686
},
4787
source="user",

0 commit comments

Comments
 (0)