1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Set xuid as unique_id and gamertag as title in Xbox config flow (#154693)

This commit is contained in:
Manu
2025-10-18 23:25:44 +02:00
committed by GitHub
parent d607323731
commit 5b1e3ef574
4 changed files with 132 additions and 10 deletions
+26
View File
@@ -54,6 +54,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: XboxConfigEntry) -> bool
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
await async_migrate_unique_id(hass, entry)
return True
@@ -61,3 +62,28 @@ async def async_unload_entry(hass: HomeAssistant, entry: XboxConfigEntry) -> boo
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_unique_id(hass: HomeAssistant, entry: XboxConfigEntry) -> bool:
"""Migrate config entry.
Migration requires runtime data
"""
if entry.version == 1 and entry.minor_version < 2:
# Migrate unique_id from `xbox` to account xuid and
# change generic entry name to user's gamertag
return hass.config_entries.async_update_entry(
entry,
unique_id=entry.runtime_data.client.xuid,
title=(
entry.runtime_data.data.presence[
entry.runtime_data.client.xuid
].gamertag
if entry.title == "Home Assistant Cloud"
else entry.title
),
minor_version=2,
)
return True
+22 -1
View File
@@ -3,6 +3,11 @@
import logging
from typing import Any
from xbox.webapi.api.client import XboxLiveClient
from xbox.webapi.authentication.manager import AuthenticationManager
from xbox.webapi.authentication.models import OAuth2TokenResponse
from xbox.webapi.common.signed_session import SignedSession
from homeassistant.config_entries import ConfigFlowResult
from homeassistant.helpers import config_entry_oauth2_flow
@@ -16,6 +21,8 @@ class OAuth2FlowHandler(
DOMAIN = DOMAIN
MINOR_VERSION = 2
@property
def logger(self) -> logging.Logger:
"""Return logger."""
@@ -31,9 +38,23 @@ class OAuth2FlowHandler(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow start."""
await self.async_set_unique_id(DOMAIN)
if self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
return await super().async_step_user(user_input)
async def async_oauth_create_entry(self, data: dict) -> ConfigFlowResult:
"""Create an entry for the flow."""
async with SignedSession() as session:
auth = AuthenticationManager(session, "", "", "")
auth.oauth = OAuth2TokenResponse(**data["token"])
await auth.refresh_tokens()
client = XboxLiveClient(auth)
me = await client.people.get_friends_own_batch([client.xuid])
await self.async_set_unique_id(client.xuid)
return self.async_create_entry(title=me.people[0].gamertag, data=data)
+34 -7
View File
@@ -65,17 +65,36 @@ def mock_config_entry() -> MockConfigEntry:
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
},
},
unique_id="xbox",
)
@pytest.fixture(name="authentication_manager")
def mock_authentication_manager() -> Generator[AsyncMock]:
"""Mock xbox-webapi AuthenticationManager."""
with (
patch(
"homeassistant.components.xbox.config_flow.AuthenticationManager",
autospec=True,
) as mock_client,
):
client = mock_client.return_value
yield client
@pytest.fixture(name="signed_session")
def mock_signed_session() -> Generator[AsyncMock]:
"""Mock xbox-webapi SignedSession."""
with patch(
"homeassistant.components.xbox.SignedSession", autospec=True
) as mock_client:
with (
patch(
"homeassistant.components.xbox.SignedSession", autospec=True
) as mock_client,
patch(
"homeassistant.components.xbox.config_flow.SignedSession", new=mock_client
),
):
client = mock_client.return_value
yield client
@@ -85,9 +104,14 @@ def mock_signed_session() -> Generator[AsyncMock]:
def mock_xbox_live_client(signed_session) -> Generator[AsyncMock]:
"""Mock xbox-webapi XboxLiveClient."""
with patch(
"homeassistant.components.xbox.XboxLiveClient", autospec=True
) as mock_client:
with (
patch(
"homeassistant.components.xbox.XboxLiveClient", autospec=True
) as mock_client,
patch(
"homeassistant.components.xbox.config_flow.XboxLiveClient", new=mock_client
),
):
client = mock_client.return_value
client.smartglass = AsyncMock()
@@ -110,4 +134,7 @@ def mock_xbox_live_client(signed_session) -> Generator[AsyncMock]:
client.people.get_friends_own.return_value = PeopleResponse(
**load_json_object_fixture("people_friends_own.json", DOMAIN)
)
client.xuid = "271958441785640"
yield client
+50 -2
View File
@@ -29,7 +29,11 @@ async def test_abort_if_existing_entry(hass: HomeAssistant) -> None:
assert result["reason"] == "single_instance_allowed"
@pytest.mark.usefixtures("current_request_with_host")
@pytest.mark.usefixtures(
"current_request_with_host",
"xbox_live_client",
"authentication_manager",
)
async def test_full_flow(
hass: HomeAssistant,
hass_client_no_auth: ClientSessionGenerator,
@@ -68,13 +72,57 @@ async def test_full_flow(
"access_token": "mock-access-token",
"type": "Bearer",
"expires_in": 60,
"scope": "XboxLive.signin XboxLive.offline_access",
"service": "xbox",
"token_type": "bearer",
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
},
)
with patch(
"homeassistant.components.xbox.async_setup_entry", return_value=True
) as mock_setup:
await hass.config_entries.flow.async_configure(result["flow_id"])
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["result"].unique_id == "271958441785640"
assert result["result"].title == "GSR Ae"
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert len(mock_setup.mock_calls) == 1
@pytest.mark.usefixtures("xbox_live_client")
async def test_unique_id_migration(
hass: HomeAssistant,
) -> None:
"""Test config entry unique_id migration."""
config_entry = MockConfigEntry(
domain=DOMAIN,
title="Home Assistant Cloud",
data={
"auth_implementation": "cloud",
"token": {
"access_token": "1234567890",
"expires_at": 1760697327.7298331,
"expires_in": 3600,
"refresh_token": "0987654321",
"scope": "XboxLive.signin XboxLive.offline_access",
"service": "xbox",
"token_type": "bearer",
"user_id": "AAAAAAAAAAAAAAAAAAAAA",
},
},
unique_id=DOMAIN,
version=1,
minor_version=1,
)
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is config_entries.ConfigEntryState.LOADED
assert config_entry.version == 1
assert config_entry.minor_version == 2
assert config_entry.unique_id == "271958441785640"
assert config_entry.title == "GSR Ae"