mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Set the nest configuration title to a user friendly name (#62886)
* Set the nest configuration title to a user friendly name Set the config entry title name to be the name of the Google Home or Nest Home that was authorized. In case more than one home was authorized, they are all listed since they are all accessed over a single shared home. * Fix pylint errors * Resolve pylint errors
This commit is contained in:
parent
5c44c27088
commit
224f960050
@ -27,6 +27,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections import OrderedDict
|
||||
from collections.abc import Iterable
|
||||
from enum import Enum
|
||||
import logging
|
||||
import os
|
||||
@ -34,10 +35,12 @@ from typing import Any
|
||||
|
||||
import async_timeout
|
||||
from google_nest_sdm.exceptions import (
|
||||
ApiException,
|
||||
AuthException,
|
||||
ConfigurationException,
|
||||
SubscriberException,
|
||||
)
|
||||
from google_nest_sdm.structure import InfoTrait, Structure
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -128,6 +131,17 @@ class UnexpectedStateError(HomeAssistantError):
|
||||
"""Raised when the config flow is invoked in a 'should not happen' case."""
|
||||
|
||||
|
||||
def generate_config_title(structures: Iterable[Structure]) -> str | None:
|
||||
"""Pick a user friendly config title based on the Google Home name(s)."""
|
||||
names: list[str] = []
|
||||
for structure in structures:
|
||||
if (trait := structure.traits.get(InfoTrait.NAME)) and trait.custom_name:
|
||||
names.append(trait.custom_name)
|
||||
if not names:
|
||||
return None
|
||||
return ", ".join(names)
|
||||
|
||||
|
||||
class NestFlowHandler(
|
||||
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
|
||||
):
|
||||
@ -141,6 +155,8 @@ class NestFlowHandler(
|
||||
super().__init__()
|
||||
self._reauth = False
|
||||
self._data: dict[str, Any] = {DATA_SDM: {}}
|
||||
# Possible name to use for config entry based on the Google Home name
|
||||
self._structure_config_title: str | None = None
|
||||
|
||||
@property
|
||||
def config_mode(self) -> ConfigMode:
|
||||
@ -298,8 +314,18 @@ class NestFlowHandler(
|
||||
except SubscriberException as err:
|
||||
_LOGGER.error("Error creating subscription: %s", err)
|
||||
errors[CONF_CLOUD_PROJECT_ID] = "subscriber_error"
|
||||
|
||||
if not errors:
|
||||
|
||||
try:
|
||||
device_manager = await subscriber.async_get_device_manager()
|
||||
except ApiException as err:
|
||||
# Generating a user friendly home name is best effort
|
||||
_LOGGER.debug("Error fetching structures: %s", err)
|
||||
else:
|
||||
self._structure_config_title = generate_config_title(
|
||||
device_manager.structures.values()
|
||||
)
|
||||
|
||||
self._data.update(
|
||||
{
|
||||
CONF_SUBSCRIBER_ID: subscriber_id,
|
||||
@ -339,7 +365,10 @@ class NestFlowHandler(
|
||||
)
|
||||
await self.hass.config_entries.async_reload(entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
return await super().async_oauth_create_entry(self._data)
|
||||
title = self.flow_impl.name
|
||||
if self._structure_config_title:
|
||||
title = self._structure_config_title
|
||||
return self.async_create_entry(title=title, data=self._data)
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
|
@ -1,13 +1,14 @@
|
||||
"""Test the Google Nest Device Access config flow."""
|
||||
|
||||
import copy
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from google_nest_sdm.exceptions import (
|
||||
AuthException,
|
||||
ConfigurationException,
|
||||
SubscriberException,
|
||||
)
|
||||
from google_nest_sdm.structure import Structure
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries, setup
|
||||
@ -562,3 +563,163 @@ async def test_pubsub_subscriber_config_entry_reauth(hass, oauth, subscriber):
|
||||
assert entry.data["auth_implementation"] == APP_AUTH_DOMAIN
|
||||
assert entry.data["subscriber_id"] == SUBSCRIBER_ID
|
||||
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
|
||||
|
||||
|
||||
async def test_config_entry_title_from_home(hass, oauth, subscriber):
|
||||
"""Test that the Google Home name is used for the config entry title."""
|
||||
|
||||
device_manager = await subscriber.async_get_device_manager()
|
||||
device_manager.add_structure(
|
||||
Structure.MakeStructure(
|
||||
{
|
||||
"name": f"enterprise/{PROJECT_ID}/structures/some-structure-id",
|
||||
"traits": {
|
||||
"sdm.structures.traits.Info": {
|
||||
"customName": "Example Home",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
assert await async_setup_configflow(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
|
||||
await oauth.async_oauth_app_flow(result)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nest.api.GoogleNestSubscriber",
|
||||
return_value=subscriber,
|
||||
):
|
||||
result = await oauth.async_configure(result, {"code": "1234"})
|
||||
await oauth.async_pubsub_flow(result)
|
||||
entry = await oauth.async_finish_setup(
|
||||
result, {"cloud_project_id": CLOUD_PROJECT_ID}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.title == "Example Home"
|
||||
assert "token" in entry.data
|
||||
assert "subscriber_id" in entry.data
|
||||
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
|
||||
|
||||
|
||||
async def test_config_entry_title_multiple_homes(hass, oauth, subscriber):
|
||||
"""Test handling of multiple Google Homes authorized."""
|
||||
|
||||
device_manager = await subscriber.async_get_device_manager()
|
||||
device_manager.add_structure(
|
||||
Structure.MakeStructure(
|
||||
{
|
||||
"name": f"enterprise/{PROJECT_ID}/structures/id-1",
|
||||
"traits": {
|
||||
"sdm.structures.traits.Info": {
|
||||
"customName": "Example Home #1",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
device_manager.add_structure(
|
||||
Structure.MakeStructure(
|
||||
{
|
||||
"name": f"enterprise/{PROJECT_ID}/structures/id-2",
|
||||
"traits": {
|
||||
"sdm.structures.traits.Info": {
|
||||
"customName": "Example Home #2",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
assert await async_setup_configflow(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
|
||||
await oauth.async_oauth_app_flow(result)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nest.api.GoogleNestSubscriber",
|
||||
return_value=subscriber,
|
||||
):
|
||||
result = await oauth.async_configure(result, {"code": "1234"})
|
||||
await oauth.async_pubsub_flow(result)
|
||||
entry = await oauth.async_finish_setup(
|
||||
result, {"cloud_project_id": CLOUD_PROJECT_ID}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.title == "Example Home #1, Example Home #2"
|
||||
|
||||
|
||||
async def test_title_failure_fallback(hass, oauth):
|
||||
"""Test exception handling when determining the structure names."""
|
||||
assert await async_setup_configflow(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
|
||||
await oauth.async_oauth_app_flow(result)
|
||||
|
||||
mock_subscriber = AsyncMock(FakeSubscriber)
|
||||
mock_subscriber.async_get_device_manager.side_effect = AuthException()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nest.api.GoogleNestSubscriber",
|
||||
return_value=mock_subscriber,
|
||||
):
|
||||
result = await oauth.async_configure(result, {"code": "1234"})
|
||||
await oauth.async_pubsub_flow(result)
|
||||
entry = await oauth.async_finish_setup(
|
||||
result, {"cloud_project_id": CLOUD_PROJECT_ID}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.title == "OAuth for Apps"
|
||||
assert "token" in entry.data
|
||||
assert "subscriber_id" in entry.data
|
||||
assert entry.data["cloud_project_id"] == CLOUD_PROJECT_ID
|
||||
|
||||
|
||||
async def test_structure_missing_trait(hass, oauth, subscriber):
|
||||
"""Test handling the case where a structure has no name set."""
|
||||
|
||||
device_manager = await subscriber.async_get_device_manager()
|
||||
device_manager.add_structure(
|
||||
Structure.MakeStructure(
|
||||
{
|
||||
"name": f"enterprise/{PROJECT_ID}/structures/id-1",
|
||||
# Missing Info trait
|
||||
"traits": {},
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
assert await async_setup_configflow(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
result = await oauth.async_pick_flow(result, APP_AUTH_DOMAIN)
|
||||
await oauth.async_oauth_app_flow(result)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nest.api.GoogleNestSubscriber",
|
||||
return_value=subscriber,
|
||||
):
|
||||
result = await oauth.async_configure(result, {"code": "1234"})
|
||||
await oauth.async_pubsub_flow(result)
|
||||
entry = await oauth.async_finish_setup(
|
||||
result, {"cloud_project_id": CLOUD_PROJECT_ID}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Fallback to default name
|
||||
assert entry.title == "OAuth for Apps"
|
||||
|
Loading…
x
Reference in New Issue
Block a user